Compare commits
6 Commits
nilmdb-1.4
...
nilmdb-1.4
Author | SHA1 | Date | |
---|---|---|---|
ae9fe89759 | |||
04def60021 | |||
9ce0f69dff | |||
90c3be91c4 | |||
ebccfb3531 | |||
e006f1d02e |
@@ -6,6 +6,7 @@ import nilmdb.utils
|
|||||||
import nilmdb.client.httpclient
|
import nilmdb.client.httpclient
|
||||||
from nilmdb.client.errors import ClientError
|
from nilmdb.client.errors import ClientError
|
||||||
|
|
||||||
|
import re
|
||||||
import time
|
import time
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
import contextlib
|
import contextlib
|
||||||
@@ -65,7 +66,12 @@ class Client(object):
|
|||||||
params["layout"] = layout
|
params["layout"] = layout
|
||||||
if extended:
|
if extended:
|
||||||
params["extended"] = 1
|
params["extended"] = 1
|
||||||
return self.http.get("stream/list", params)
|
def sort_streams_nicely(x):
|
||||||
|
"""Human-friendly sort (/stream/2 before /stream/10)"""
|
||||||
|
num = lambda t: int(t) if t.isdigit() else t
|
||||||
|
key = lambda k: [ num(c) for c in re.split('([0-9]+)', k[0]) ]
|
||||||
|
return sorted(x, key = key)
|
||||||
|
return sort_streams_nicely(self.http.get("stream/list", params))
|
||||||
|
|
||||||
def stream_get_metadata(self, path, keys = None):
|
def stream_get_metadata(self, path, keys = None):
|
||||||
params = { "path": path }
|
params = { "path": path }
|
||||||
|
@@ -81,7 +81,7 @@ class Cmdline(object):
|
|||||||
def __init__(self, argv = None):
|
def __init__(self, argv = None):
|
||||||
self.argv = argv or sys.argv[1:]
|
self.argv = argv or sys.argv[1:]
|
||||||
self.client = None
|
self.client = None
|
||||||
self.def_url = os.environ.get("NILMDB_URL", "http://localhost:12380")
|
self.def_url = os.environ.get("NILMDB_URL", "http://localhost/nilmdb/")
|
||||||
self.subcmd = {}
|
self.subcmd = {}
|
||||||
self.complete = Complete()
|
self.complete = Complete()
|
||||||
|
|
||||||
|
@@ -9,7 +9,8 @@ def setup(self, sub):
|
|||||||
a stream.
|
a stream.
|
||||||
""",
|
""",
|
||||||
usage="%(prog)s path [-g [key ...] | "
|
usage="%(prog)s path [-g [key ...] | "
|
||||||
"-s key=value [...] | -u key=value [...]]")
|
"-s key=value [...] | -u key=value [...]] | "
|
||||||
|
"-d [key ...]")
|
||||||
cmd.set_defaults(handler = cmd_metadata)
|
cmd.set_defaults(handler = cmd_metadata)
|
||||||
|
|
||||||
group = cmd.add_argument_group("Required arguments")
|
group = cmd.add_argument_group("Required arguments")
|
||||||
@@ -30,6 +31,9 @@ def setup(self, sub):
|
|||||||
help="Update metadata using provided "
|
help="Update metadata using provided "
|
||||||
"key=value pairs",
|
"key=value pairs",
|
||||||
).completer = self.complete.meta_keyval
|
).completer = self.complete.meta_keyval
|
||||||
|
exc.add_argument("-d", "--delete", nargs="*", metavar="key",
|
||||||
|
help="Delete metadata for specified keys (default all)",
|
||||||
|
).completer = self.complete.meta_key
|
||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
def cmd_metadata(self):
|
def cmd_metadata(self):
|
||||||
@@ -56,6 +60,16 @@ def cmd_metadata(self):
|
|||||||
handler(self.args.path, data)
|
handler(self.args.path, data)
|
||||||
except nilmdb.client.ClientError as e:
|
except nilmdb.client.ClientError as e:
|
||||||
self.die("error setting/updating metadata: %s", str(e))
|
self.die("error setting/updating metadata: %s", str(e))
|
||||||
|
elif self.args.delete is not None:
|
||||||
|
# Delete (by setting values to empty strings)
|
||||||
|
keys = self.args.delete or None
|
||||||
|
try:
|
||||||
|
data = self.client.stream_get_metadata(self.args.path, keys)
|
||||||
|
for key in data:
|
||||||
|
data[key] = ""
|
||||||
|
self.client.stream_update_metadata(self.args.path, data)
|
||||||
|
except nilmdb.client.ClientError as e:
|
||||||
|
self.die("error deleting metadata: %s", str(e))
|
||||||
else:
|
else:
|
||||||
# Get (or unspecified)
|
# Get (or unspecified)
|
||||||
keys = self.args.get or None
|
keys = self.args.get or None
|
||||||
@@ -64,7 +78,7 @@ def cmd_metadata(self):
|
|||||||
except nilmdb.client.ClientError as e:
|
except nilmdb.client.ClientError as e:
|
||||||
self.die("error getting metadata: %s", str(e))
|
self.die("error getting metadata: %s", str(e))
|
||||||
for key, value in sorted(data.items()):
|
for key, value in sorted(data.items()):
|
||||||
# Omit nonexistant keys
|
# Print nonexistant keys as having empty value
|
||||||
if value is None:
|
if value is None:
|
||||||
value = ""
|
value = ""
|
||||||
printf("%s=%s\n", key, value)
|
printf("%s=%s\n", key, value)
|
||||||
|
@@ -79,7 +79,12 @@ class BulkData(object):
|
|||||||
if Table.exists(ospath):
|
if Table.exists(ospath):
|
||||||
raise ValueError("stream already exists at this path")
|
raise ValueError("stream already exists at this path")
|
||||||
if os.path.isdir(ospath):
|
if os.path.isdir(ospath):
|
||||||
raise ValueError("subdirs of this path already exist")
|
# Look for any files in subdirectories. Fully empty subdirectories
|
||||||
|
# are OK; they might be there during a rename
|
||||||
|
for (root, dirs, files) in os.walk(ospath):
|
||||||
|
if len(files):
|
||||||
|
raise ValueError(
|
||||||
|
"non-empty subdirs of this path already exist")
|
||||||
|
|
||||||
def _create_parents(self, unicodepath):
|
def _create_parents(self, unicodepath):
|
||||||
"""Verify the path name, and create parent directories if they
|
"""Verify the path name, and create parent directories if they
|
||||||
@@ -188,7 +193,6 @@ class BulkData(object):
|
|||||||
# Basic checks
|
# Basic checks
|
||||||
if oldospath == newospath:
|
if oldospath == newospath:
|
||||||
raise ValueError("old and new paths are the same")
|
raise ValueError("old and new paths are the same")
|
||||||
self._create_check_ospath(newospath)
|
|
||||||
|
|
||||||
# Move the table to a temporary location
|
# Move the table to a temporary location
|
||||||
tmpdir = tempfile.mkdtemp(prefix = "rename-", dir = self.root)
|
tmpdir = tempfile.mkdtemp(prefix = "rename-", dir = self.root)
|
||||||
@@ -196,6 +200,9 @@ class BulkData(object):
|
|||||||
os.rename(oldospath, tmppath)
|
os.rename(oldospath, tmppath)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Check destination path
|
||||||
|
self._create_check_ospath(newospath)
|
||||||
|
|
||||||
# Create parent dirs for new location
|
# Create parent dirs for new location
|
||||||
self._create_parents(newunicodepath)
|
self._create_parents(newunicodepath)
|
||||||
|
|
||||||
|
@@ -317,9 +317,8 @@ class Stream(NilmApp):
|
|||||||
body = cherrypy.request.body.read()
|
body = cherrypy.request.body.read()
|
||||||
|
|
||||||
# Check path and get layout
|
# Check path and get layout
|
||||||
streams = self.db.stream_list(path = path)
|
if len(self.db.stream_list(path = path)) != 1:
|
||||||
if len(streams) != 1:
|
raise cherrypy.HTTPError("404", "No such stream: " + path)
|
||||||
raise cherrypy.HTTPError("404 Not Found", "No such stream")
|
|
||||||
|
|
||||||
# Check limits
|
# Check limits
|
||||||
(start, end) = self._get_times(start, end)
|
(start, end) = self._get_times(start, end)
|
||||||
@@ -416,9 +415,8 @@ class Stream(NilmApp):
|
|||||||
(start, end) = self._get_times(start, end)
|
(start, end) = self._get_times(start, end)
|
||||||
|
|
||||||
# Check path and get layout
|
# Check path and get layout
|
||||||
streams = self.db.stream_list(path = path)
|
if len(self.db.stream_list(path = path)) != 1:
|
||||||
if len(streams) != 1:
|
raise cherrypy.HTTPError("404", "No such stream: " + path)
|
||||||
raise cherrypy.HTTPError("404 Not Found", "No such stream")
|
|
||||||
|
|
||||||
@workaround_cp_bug_1200
|
@workaround_cp_bug_1200
|
||||||
def content(start, end):
|
def content(start, end):
|
||||||
|
@@ -65,6 +65,14 @@ def parse_time(toparse):
|
|||||||
if toparse == "max":
|
if toparse == "max":
|
||||||
return max_timestamp
|
return max_timestamp
|
||||||
|
|
||||||
|
# If it starts with @, treat it as a NILM timestamp
|
||||||
|
# (integer microseconds since epoch)
|
||||||
|
try:
|
||||||
|
if toparse[0] == '@':
|
||||||
|
return int(toparse[1:])
|
||||||
|
except (ValueError, KeyError):
|
||||||
|
pass
|
||||||
|
|
||||||
# If string isn't "now" and doesn't contain at least 4 digits,
|
# If string isn't "now" and doesn't contain at least 4 digits,
|
||||||
# consider it invalid. smartparse might otherwise accept
|
# consider it invalid. smartparse might otherwise accept
|
||||||
# empty strings and strings with just separators.
|
# empty strings and strings with just separators.
|
||||||
@@ -78,14 +86,6 @@ def parse_time(toparse):
|
|||||||
except (ValueError, OverflowError):
|
except (ValueError, OverflowError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# If it starts with @, treat it as a NILM timestamp
|
|
||||||
# (integer microseconds since epoch)
|
|
||||||
try:
|
|
||||||
if toparse[0] == '@':
|
|
||||||
return int(toparse[1:])
|
|
||||||
except (ValueError, KeyError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# If it's parseable as a float, treat it as a Unix or NILM
|
# If it's parseable as a float, treat it as a Unix or NILM
|
||||||
# timestamp based on its range.
|
# timestamp based on its range.
|
||||||
try:
|
try:
|
||||||
|
@@ -369,6 +369,8 @@ class TestCmdline(object):
|
|||||||
self.contain("No stream at path")
|
self.contain("No stream at path")
|
||||||
self.fail("metadata /newton/nosuchstream --set foo=bar")
|
self.fail("metadata /newton/nosuchstream --set foo=bar")
|
||||||
self.contain("No stream at path")
|
self.contain("No stream at path")
|
||||||
|
self.fail("metadata /newton/nosuchstream --delete")
|
||||||
|
self.contain("No stream at path")
|
||||||
|
|
||||||
self.ok("metadata /newton/prep")
|
self.ok("metadata /newton/prep")
|
||||||
self.match("description=The Data\nv_scale=1.234\n")
|
self.match("description=The Data\nv_scale=1.234\n")
|
||||||
@@ -394,6 +396,19 @@ class TestCmdline(object):
|
|||||||
self.fail("metadata /newton/nosuchpath")
|
self.fail("metadata /newton/nosuchpath")
|
||||||
self.contain("No stream at path /newton/nosuchpath")
|
self.contain("No stream at path /newton/nosuchpath")
|
||||||
|
|
||||||
|
self.ok("metadata /newton/prep --delete")
|
||||||
|
self.ok("metadata /newton/prep --get")
|
||||||
|
self.match("")
|
||||||
|
self.ok("metadata /newton/prep --set "
|
||||||
|
"'description=The Data' "
|
||||||
|
"v_scale=1.234")
|
||||||
|
self.ok("metadata /newton/prep --delete v_scale")
|
||||||
|
self.ok("metadata /newton/prep --get")
|
||||||
|
self.match("description=The Data\n")
|
||||||
|
self.ok("metadata /newton/prep --set description=")
|
||||||
|
self.ok("metadata /newton/prep --get")
|
||||||
|
self.match("")
|
||||||
|
|
||||||
def test_06_insert(self):
|
def test_06_insert(self):
|
||||||
self.ok("insert --help")
|
self.ok("insert --help")
|
||||||
|
|
||||||
@@ -1038,10 +1053,12 @@ class TestCmdline(object):
|
|||||||
self.contain("old and new paths are the same")
|
self.contain("old and new paths are the same")
|
||||||
check_path("newton", "prep")
|
check_path("newton", "prep")
|
||||||
self.fail("rename /newton/prep /newton")
|
self.fail("rename /newton/prep /newton")
|
||||||
self.contain("subdirs of this path already exist")
|
self.contain("path must contain at least one folder")
|
||||||
self.fail("rename /newton/prep /newton/prep/")
|
self.fail("rename /newton/prep /newton/prep/")
|
||||||
self.contain("invalid path")
|
self.contain("invalid path")
|
||||||
self.ok("rename /newton/prep /newton/foo")
|
self.ok("rename /newton/prep /newton/foo/1")
|
||||||
|
check_path("newton", "foo", "1")
|
||||||
|
self.ok("rename /newton/foo/1 /newton/foo")
|
||||||
check_path("newton", "foo")
|
check_path("newton", "foo")
|
||||||
self.ok("rename /newton/foo /totally/different/thing")
|
self.ok("rename /newton/foo /totally/different/thing")
|
||||||
check_path("totally", "different", "thing")
|
check_path("totally", "different", "thing")
|
||||||
|
Reference in New Issue
Block a user