From 50c006a3cea93cf64beb74a45cef9ec77dcf7009 Mon Sep 17 00:00:00 2001 From: John-Mark Gurney Date: Sun, 29 Sep 2024 20:16:28 -0700 Subject: [PATCH] implement searching by metadata modified date... --- ui/fixtures/cmd.search.json | 24 ++++++++++++++++-- ui/medashare/cli.py | 50 +++++++++++++++++++++++++++++++------ 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/ui/fixtures/cmd.search.json b/ui/fixtures/cmd.search.json index 41acd7f..64c6b63 100644 --- a/ui/fixtures/cmd.search.json +++ b/ui/fixtures/cmd.search.json @@ -24,6 +24,9 @@ "title": "add tag a", "cmd": [ "modify", "+tag=foo", "+dc:creator=John-Mark Gurney", "newfile.txt" ] }, +{ + "special": "store now" +}, { "title": "add tag b", "cmd": [ "modify", "+tag=bar", "+dc:creator=John-Mark Gurney", "+other=baz", "test.txt" ] @@ -34,8 +37,25 @@ "count": 6 }, { - "title": "search tag foo", - "cmd": [ "search", "file", "+tag=foo" ], + "title": "invalid meta:", + "cmd": [ "search", "file", "+meta:modified" ], + "stderr": "invalid meta: specification: 'meta:modified'\n", + "exit": 1 +}, +{ + "special": "format next cmd" +}, +{ + "title": "search on meta:modified", + "cmd": [ "search", "file", "+meta:modified>{nowiso}" ], + "stdout_re": "/test.txt\n$" +}, +{ + "special": "format next cmd" +}, +{ + "title": "search on meta:modified", + "cmd": [ "search", "file", "+meta:modified<{nowiso}" ], "stdout_re": "/newfile.txt\n$" }, { diff --git a/ui/medashare/cli.py b/ui/medashare/cli.py index 0420c82..0895c5c 100644 --- a/ui/medashare/cli.py +++ b/ui/medashare/cli.py @@ -50,6 +50,7 @@ import itertools import json import libarchive import magic +import operator import os.path import pathlib import pasn1 @@ -96,6 +97,16 @@ def _iterdictlist(obj, **kwargs): else: yield k, v +_metaopre = re.compile('meta:(?P[a-zA-Z]+)(?P=|<|>|!=|<=|>=)(?P.*)') +_metaoplookup = { + '=': operator.eq, '!=': operator.ne, + '<': operator.lt, '>': operator.gt, + '<=': operator.le, '>=': operator.ge +} +_metavalueconv = { + 'modified': datetime.datetime.fromisoformat, +} + from .utils import _makeuuid, _makedatetime, _asn1coder from .mdb import MDBase @@ -1631,19 +1642,37 @@ def cmd_search(options, persona, objstr, cache): skeymap = aliased(orm.StringTable) svaluemap = aliased(orm.StringTable) + origvalue = '='.join(i[1:]) + try: op, key, value = i except ValueError: op, key = i value = None - subq = select(propmapsub.obj).where( - # that match the key - propmapsub.keyid == skeymap.id, skeymap.str == key) + # handle meta tree + if key.startswith('meta:'): + mat = _metaopre.match(origvalue) + if not mat: + print('invalid meta: specification: %s' % repr(origvalue), + file=sys.stderr) + sys.exit(1) + + #print(repr(mat), repr(mat.groups()), file=_real_stderr) + + mdo = aliased(orm.MetaDataObject) + metavalue = _metavalueconv[mat.group('key')](mat.group('val')) + subq = select(propmapsub.obj).where(propmapsub.obj == mdo.uuid, + _metaoplookup[mat.group('op')](mdo.modified, metavalue)) + + else: + subq = select(propmapsub.obj).where( + # that match the key + propmapsub.keyid == skeymap.id, skeymap.str == key) - if value is not None: - subq = subq.where(propmapsub.valueid == svaluemap.id, - svaluemap.str == value) + if value is not None: + subq = subq.where(propmapsub.valueid == svaluemap.id, + svaluemap.str == value) #subq = subq.subquery() @@ -1719,6 +1748,7 @@ def cmd_search(options, persona, objstr, cache): sel = sel.execution_options(yield_per=10) #_debprint('sel:', _getquery(sel, objstr)) + #print('sel:', _getquery(sel, objstr), file=_real_stderr) with objstr._ses() as session: r = ( x[0] for x in session.execute(sel) ) @@ -2508,7 +2538,7 @@ class _TestCases(unittest.TestCase): patches = [] - for cmd in cmds: + for idx, cmd in enumerate(cmds): try: if cmd['skip']: # pragma: no cover continue @@ -2583,6 +2613,12 @@ class _TestCases(unittest.TestCase): elif special == 'setup tar file': shutil.copy(self.fixtures / 'testfile.tar.gz', self.tempdir) + elif special == 'store now': + storednow = datetime.datetime.now(datetime.timezone.utc) + elif special == 'format next cmd': + modcmd = cmds[idx + 1] + formatkwargs = dict(nowiso=storednow.isoformat()) + modcmd['cmd'] = [ x.format(**formatkwargs) for x in modcmd['cmd'] ] else: # pragma: no cover raise ValueError('unhandled special: %s' % repr(special))