Exemple #1
0
    def _search(self):
        error = None

        query = self._input.text()

        if len(query.split()) == 1 and all(o not in query.strip()
                                           for o in '=<>'):
            objectid = query.strip()
            try:
                entries = [self.store.get(objectid)]
            except KeyError:
                error = _("objectid '{oid}' not found", oid=objectid)

        else:
            try:
                conditions = parse_expression(query)
            except tdparser.Error as e:
                error = e.args[0]
            else:
                conditions = self._alter_search_conditions(conditions)
                entries = self.store.query(conditions, limit=self.MAX_RESULTS)

        self._result_tree.clear()

        if error is not None:
            w = QtWidgets.QTreeWidgetItem([error])
            w.setForeground(0, QtGui.QColor(255, 0, 0))
            self._result_tree.addTopLevelItem(w)
            self._result_tree.setFirstItemColumnSpanned(w, True)
        else:
            for i, entry in enumerate(entries):
                file_item = FileItem(entry)
                f = file_item.font(0)
                f.setBold(True)
                file_item.setFont(0, f)
                self._result_tree.addTopLevelItem(file_item)
                self._result_tree.setFirstItemColumnSpanned(file_item, True)
                for k, v in entry.metadata.items():
                    file_item.addChild(MetadataItem(entry, k, v))

                if i + 1 == self.MAX_RESULTS:
                    last_item = QtWidgets.QTreeWidgetItem([
                        _("... stripped after {nb} results...",
                          nb=self.MAX_RESULTS)
                    ])
                    f = last_item.font(0)
                    f.setBold(True)
                    f.setItalic(True)
                    last_item.setFont(0, f)
                    self._result_tree.addTopLevelItem(last_item)
                    self._result_tree.setFirstItemColumnSpanned(
                        last_item, True)
                    break
            if self._result_tree.topLevelItemCount() == 0:
                w = QtWidgets.QTreeWidgetItem([_("No matches")])
                self._result_tree.addTopLevelItem(w)
                self._result_tree.setFirstItemColumnSpanned(w, True)
            self._result_tree.expandAll()

        self._set_needs_refresh(False)
Exemple #2
0
    def _create_buttons(self):
        buttons = []

        # Open button; uses the system to choose the program to open with
        # (on Windows, might ask you what to use every time because of filename
        # scheme)
        open_button = QtWidgets.QPushButton(_("Open"))
        if openfile is not None:
            open_button.clicked.connect(self._openfile)
            buttons.append(('single', open_button))
        else:
            open_button.setEnabled(False)

        # Copy hash button
        copy_button = QtWidgets.QPushButton(_("Copy ID"))
        copy_button.clicked.connect(self._copy_objectid)
        buttons.append(('single', copy_button))

        # Edit metadata button
        edit_button = QtWidgets.QPushButton(_("Edit metadata..."))
        edit_button.clicked.connect(self._edit_metadata)
        buttons.append(('single', edit_button))

        # Delete button, removes what's selected (with confirmation)
        remove_button = QtWidgets.QPushButton(_("Delete"))
        remove_button.clicked.connect(self._delete)
        buttons.append(('multi', remove_button))

        return buttons
Exemple #3
0
def parse_new_metadata(args):
    """Parses a list of key=value or key=type:value arguments.
    """
    metadata = {}
    for a in args:
        k = a.split('=', 1)
        if len(k) != 2:
            sys.stderr.write(
                _("Metadata should have format key=value or "
                  "key=type:value (eg. age=int:23)\n"))
            sys.exit(1)
        k, v = k
        if ':' in v:
            t, v = v.split(':', 1)
        else:
            t = 'str'
        if k in metadata:
            sys.stderr.write("Multiple values for key %s\n" % k)
            sys.exit(1)
        if t == 'int':
            v = int(v)
        elif t == 'str':
            if isinstance(v, bytes):
                v = v.decode(locale.getpreferredencoding())
        else:
            sys.stderr.write(
                _(
                    "Metadata has unknown type '{t}'! Only 'str' "
                    "and 'int' are supported.\n"
                    "If you meant a string with a ':', use "
                    "'str:mystring'",
                    t=t))
            sys.exit(1)
        metadata[k] = {'type': t, 'value': v}
    return metadata
Exemple #4
0
def parse_new_metadata(args):
    """Parses a list of key=value or key=type:value arguments.
    """
    metadata = {}
    for a in args:
        k = a.split('=', 1)
        if len(k) != 2:
            sys.stderr.write(_("Metadata should have format key=value or "
                               "key=type:value (eg. age=int:23)\n"))
            sys.exit(1)
        k, v = k
        if ':' in v:
            t, v = v.split(':', 1)
        else:
            t = 'str'
        if k in metadata:
            sys.stderr.write("Multiple values for key %s\n" % k)
            sys.exit(1)
        if t == 'int':
            v = int(v)
        elif t == 'str':
            if isinstance(v, bytes):
                v = v.decode(locale.getpreferredencoding())
        else:
            sys.stderr.write(_("Metadata has unknown type '{t}'! Only 'str' "
                               "and 'int' are supported.\n"
                               "If you meant a string with a ':', use "
                               "'str:mystring'", t=t))
            sys.exit(1)
        metadata[k] = {'type': t, 'value': v}
    return metadata
Exemple #5
0
def cmd_print(store, args):
    """Print command.

    print [-m] [-t] <filehash> [...]
    print [-m] [-t] [key1=value1] [...]
    """
    meta = False
    types = False
    while args and args[0][0] == '-':
        if args[0] == '-m':
            meta = True
        elif args[0] == '-t':
            types = True
        elif args[0] == '--':
            del args[0]
            break
        else:
            sys.stderr.write(_("Unknown option: {opt}\n", opt=args[0]))
            sys.exit(1)
        del args[0]
    h, metadata = parse_query_metadata(args)
    if h is not None:
        try:
            entry = store.get(h)
        except KeyError:
            sys.stderr.write(_("Objectid not found\n"))
            sys.exit(2)
    else:
        entries = store.query(metadata)
        try:
            entry = next(entries)
        except StopIteration:
            sys.stderr.write(_("No match found\n"))
            sys.exit(2)
        try:
            next(entries)
        except StopIteration:
            pass
        else:
            sys.stderr.write(_("Warning: more matching files exist\n"))
    if meta:
        for k, v in entry.metadata.items():
            if k == 'hash':
                continue
            if types:
                if isinstance(v, int_types):
                    v = 'int:%d' % v
                else:  # isinstance(v, string_types):
                    v = 'str:%s' % v
            sys.stdout.write("%s\t%s\n" % (k, v))
    else:
        if os.path.isdir(entry.filename):
            sys.stderr.write(_("Error: match found but is a directory\n"))
            sys.exit(2)
        fp = entry.open()
        try:
            for chunk in BufferedReader(fp):
                sys.stdout.write(chunk)
        finally:
            fp.close()
Exemple #6
0
def cmd_print(store, args):
    """Print command.

    print [-m] [-t] <filehash> [...]
    print [-m] [-t] [key1=value1] [...]
    """
    meta = False
    types = False
    while args and args[0][0] == '-':
        if args[0] == '-m':
            meta = True
        elif args[0] == '-t':
            types = True
        elif args[0] == '--':
            del args[0]
            break
        else:
            sys.stderr.write(_("Unknown option: {opt}\n", opt=args[0]))
            sys.exit(1)
        del args[0]
    h, metadata = parse_query_metadata(args)
    if h is not None:
        try:
            entry = store.get(h)
        except KeyError:
            sys.stderr.write(_("Objectid not found\n"))
            sys.exit(2)
    else:
        entries = store.query(metadata)
        try:
            entry = next(entries)
        except StopIteration:
            sys.stderr.write(_("No match found\n"))
            sys.exit(2)
        try:
            next(entries)
        except StopIteration:
            pass
        else:
            sys.stderr.write(_("Warning: more matching files exist\n"))
    if meta:
        for k, v in entry.metadata.items():
            if k == 'hash':
                continue
            if types:
                if isinstance(v, int_types):
                    v = 'int:%d' % v
                else:  # isinstance(v, string_types):
                    v = 'str:%s' % v
            sys.stdout.write("%s\t%s\n" % (k, v))
    else:
        if os.path.isdir(entry.filename):
            sys.stderr.write(_("Error: match found but is a directory\n"))
            sys.exit(2)
        fp = entry.open()
        try:
            for chunk in BufferedReader(fp):
                sys.stdout.write(chunk)
        finally:
            fp.close()
Exemple #7
0
    def _create_buttons(self):
        buttons = []

        # Open button; uses the system to choose the program to open with
        # (on Windows, might ask you what to use every time because of filename
        # scheme)
        open_button = QtWidgets.QPushButton(_("Open"))
        if openfile is not None:
            open_button.clicked.connect(self._openfile)
            buttons.append(('single', open_button))
        else:
            open_button.setEnabled(False)

        # Copy hash button
        copy_button = QtWidgets.QPushButton(_("Copy ID"))
        copy_button.clicked.connect(self._copy_objectid)
        buttons.append(('single', copy_button))

        # Edit metadata button
        edit_button = QtWidgets.QPushButton(_("Edit metadata..."))
        edit_button.clicked.connect(self._edit_metadata)
        buttons.append(('single', edit_button))

        # Delete button, removes what's selected (with confirmation)
        remove_button = QtWidgets.QPushButton(_("Delete"))
        remove_button.clicked.connect(self._delete)
        buttons.append(('multi', remove_button))

        return buttons
Exemple #8
0
    def _search(self):
        error = None

        query = self._input.text()

        if len(query.split()) == 1 and all(o not in query.strip()
                                           for o in '=<>'):
            objectid = query.strip()
            try:
                entries = [self.store.get(objectid)]
            except KeyError:
                error = _("objectid '{oid}' not found", oid=objectid)

        else:
            try:
                conditions = parse_expression(query)
            except tdparser.Error as e:
                error = e.args[0]
            else:
                conditions = self._alter_search_conditions(conditions)
                entries = self.store.query(conditions, limit=self.MAX_RESULTS)

        self._result_tree.clear()

        if error is not None:
            w = QtWidgets.QTreeWidgetItem([error])
            w.setForeground(0, QtGui.QColor(255, 0, 0))
            self._result_tree.addTopLevelItem(w)
            self._result_tree.setFirstItemColumnSpanned(w, True)
        else:
            for i, entry in enumerate(entries):
                file_item = FileItem(entry)
                f = file_item.font(0)
                f.setBold(True)
                file_item.setFont(0, f)
                self._result_tree.addTopLevelItem(file_item)
                self._result_tree.setFirstItemColumnSpanned(file_item, True)
                for k, v in entry.metadata.items():
                    file_item.addChild(MetadataItem(entry, k, v))

                if i + 1 == self.MAX_RESULTS:
                    last_item = QtWidgets.QTreeWidgetItem(
                        [_("... stripped after {nb} results...",
                           nb=self.MAX_RESULTS)])
                    f = last_item.font(0)
                    f.setBold(True)
                    f.setItalic(True)
                    last_item.setFont(0, f)
                    self._result_tree.addTopLevelItem(last_item)
                    self._result_tree.setFirstItemColumnSpanned(last_item,
                                                                True)
                    break
            if self._result_tree.topLevelItemCount() == 0:
                w = QtWidgets.QTreeWidgetItem([_("No matches")])
                self._result_tree.addTopLevelItem(w)
                self._result_tree.setFirstItemColumnSpanned(w, True)
            self._result_tree.expandAll()

        self._set_needs_refresh(False)
Exemple #9
0
def parse_query_metadata(args):
    """Parses a list of key=value arguments or a hash value.

    Returns (hash:str, metadata:dict)
    """
    if len(args) == 1 and '=' not in args[0]:
        return args[0], None
    else:
        metadata = {}
        for a in args:
            k = a.split('=', 1)
            if len(k) != 2:
                sys.stderr.write(_("Metadata should have format key=value, "
                                   "key=type:value (eg. age=int:23) or "
                                   "key=type:req (eg. age=int:>21)\n"))
                sys.exit(1)
            k, v = k
            if ':' in v:
                t, v = v.split(':', 1)
                if t == 'int':
                    if v[0] == '>':
                        req, v = 'gt', v[1:]
                    elif v[0] == '<':
                        req, v = 'lt', v[1:]
                    else:
                        req = 'equal'
                    v = int(v)
                elif t == 'str':
                    req = 'equal'
                else:
                    sys.stderr.write(_("Metadata has unknown type '{t}'! "
                                       "Only 'str' and 'int' are supported.\n"
                                       "If you meant a string with a ':', "
                                       "use 'str:mystring'", t=t))
                    sys.exit(1)
            else:
                t = 'str'
                req = 'equal'
            if t == 'str' and isinstance(v, bytes):
                v = v.decode(locale.getpreferredencoding())
            if k in metadata:
                if t != metadata[k]['type']:
                    sys.stderr.write(_("Differing types for conditions on "
                                       "key {k}: {t1}, {t2}\n",
                                       k=k, t1=metadata[k]['type'], t2=t))
                    sys.exit(1)
                if req in metadata[k]:
                    sys.stderr.write(_("Multiple conditions {cond} on key "
                                       "{k}\n",
                                       cond=req, k=k))
                    sys.exit(1)
                metadata[k][req] = v
            else:
                metadata[k] = {'type': t, req: v}
        return None, metadata
Exemple #10
0
def main(args):
    warnings.filterwarnings('always', category=UsageWarning)

    usage = _(
        "usage: {bin} <store> create\n"
        "   or: {bin} <store> add <filename> [key1=value1] [...]\n"
        "   or: {bin} <store> write [key1=value1] [...]\n"
        "   or: {bin} <store> query [-d] [-t] [key1=value1] [...]\n"
        "   or: {bin} <store> print [-m] [-t] <filehash> [...]\n"
        "   or: {bin} <store> print [-m] [-t] [key1=value1] [...]\n"
        "   or: {bin} <store> remove [-f] <filehash>\n"
        "   or: {bin} <store> remove [-f] <key1=value1> [...]\n"
        "   or: {bin} <store> verify\n"
        "   or: {bin} <store> view\n",
        bin='file_archive')

    if len(args) < 2:
        sys.stderr.write(usage)
        sys.exit(1)

    store = args[0]
    command = args[1]

    if command == 'create':
        try:
            FileStore.create_store(store)
        except Exception as e:
            sys.stderr.write(_("Can't create store: {err}\n", err=e.args[0]))
            sys.exit(3)
        sys.exit(0)

    try:
        store = FileStore(store)
    except Exception as e:
        sys.stderr.write(_("Invalid store: {err}\n", err=e.args[0]))
        sys.exit(3)

    try:
        try:
            func = commands[command]
        except KeyError:
            sys.stderr.write(usage)
            sys.exit(1)
        try:
            func(store, args[2:])
        except Exception:
            import traceback
            traceback.print_exc()
            sys.exit(3)
    finally:
        store.close()

    sys.exit(0)
Exemple #11
0
def main(args):
    warnings.filterwarnings('always', category=UsageWarning)

    usage = _(
        "usage: {bin} <store> create\n"
        "   or: {bin} <store> add <filename> [key1=value1] [...]\n"
        "   or: {bin} <store> write [key1=value1] [...]\n"
        "   or: {bin} <store> query [-d] [-t] [key1=value1] [...]\n"
        "   or: {bin} <store> print [-m] [-t] <filehash> [...]\n"
        "   or: {bin} <store> print [-m] [-t] [key1=value1] [...]\n"
        "   or: {bin} <store> remove [-f] <filehash>\n"
        "   or: {bin} <store> remove [-f] <key1=value1> [...]\n"
        "   or: {bin} <store> verify\n"
        "   or: {bin} <store> view\n",
        bin='file_archive')

    if len(args) < 2:
        sys.stderr.write(usage)
        sys.exit(1)

    store = args[0]
    command = args[1]

    if command == 'create':
        try:
            FileStore.create_store(store)
        except Exception as e:
            sys.stderr.write(_("Can't create store: {err}\n", err=e.args[0]))
            sys.exit(3)
        sys.exit(0)

    try:
        store = FileStore(store)
    except Exception as e:
        sys.stderr.write(_("Invalid store: {err}\n", err=e.args[0]))
        sys.exit(3)

    try:
        try:
            func = commands[command]
        except KeyError:
            sys.stderr.write(usage)
            sys.exit(1)
        try:
            func(store, args[2:])
        except Exception:
            import traceback
            traceback.print_exc()
            sys.exit(3)
    finally:
        store.close()

    sys.exit(0)
Exemple #12
0
    def __init__(self, store):
        QtWidgets.QMainWindow.__init__(self)
        self.setWindowTitle(self.WINDOW_TITLE)

        self.store = store

        searchbar = QtWidgets.QHBoxLayout()

        self._needs_refresh = False

        # Input line for the query
        self._input = QtWidgets.QLineEdit()
        self._input.setPlaceholderText(_("Enter query here"))
        self._input.returnPressed.connect(self._search)
        self._input.textEdited.connect(lambda t: self._set_needs_refresh())
        searchbar.addWidget(self._input)

        # Search button
        self._searchbutton = QtWidgets.QPushButton(_("Search"))
        self._searchbutton.clicked.connect(self._search)
        searchbar.addWidget(self._searchbutton)

        results = QtWidgets.QHBoxLayout()

        # Result view, as a tree with metadata
        self._result_tree = QtWidgets.QTreeWidget()
        self._result_tree.setColumnCount(3)
        self._result_tree.setHeaderLabels([_("Key"), _("Value"), _("Type")])
        self._result_tree.itemSelectionChanged.connect(self._selection_changed)
        results.addWidget(self._result_tree)

        # Buttons, enabled/disabled when the selection changes
        buttons = QtWidgets.QVBoxLayout()
        self._buttons = self._create_buttons()

        for _name, button in self._buttons:
            buttons.addWidget(button)
        self._selection_changed()
        results.addLayout(buttons)

        layout = QtWidgets.QVBoxLayout()
        layout.addLayout(searchbar)
        layout.addLayout(results)

        widget = QtWidgets.QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)

        self._search()
Exemple #13
0
    def __init__(self, store):
        QtWidgets.QMainWindow.__init__(self)
        self.setWindowTitle(self.WINDOW_TITLE)

        self.store = store

        searchbar = QtWidgets.QHBoxLayout()

        self._needs_refresh = False

        # Input line for the query
        self._input = QtWidgets.QLineEdit()
        self._input.setPlaceholderText(_("Enter query here"))
        self._input.returnPressed.connect(self._search)
        self._input.textEdited.connect(lambda t: self._set_needs_refresh())
        searchbar.addWidget(self._input)

        # Search button
        self._searchbutton = QtWidgets.QPushButton(_("Search"))
        self._searchbutton.clicked.connect(self._search)
        searchbar.addWidget(self._searchbutton)

        results = QtWidgets.QHBoxLayout()

        # Result view, as a tree with metadata
        self._result_tree = QtWidgets.QTreeWidget()
        self._result_tree.setColumnCount(3)
        self._result_tree.setHeaderLabels([_("Key"), _("Value"), _("Type")])
        self._result_tree.itemSelectionChanged.connect(self._selection_changed)
        results.addWidget(self._result_tree)

        # Buttons, enabled/disabled when the selection changes
        buttons = QtWidgets.QVBoxLayout()
        self._buttons = self._create_buttons()

        for _name, button in self._buttons:
            buttons.addWidget(button)
        self._selection_changed()
        results.addLayout(buttons)

        layout = QtWidgets.QVBoxLayout()
        layout.addLayout(searchbar)
        layout.addLayout(results)

        widget = QtWidgets.QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)

        self._search()
Exemple #14
0
 def _delete(self):
     items = self._result_tree.selectedItems()
     if not items:
         return
     confirm = QtWidgets.QMessageBox.question(
         self,
         _("Are you sure?"),
         _n("You are about to delete {num} entry from the store. "
            "Please confirm.",
            "You are about to delete {num} entries from the store. "
            "Please confirm.",
            len(items),
            num=len(items)),
         QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel,
         QtWidgets.QMessageBox.Cancel)
     if confirm == QtWidgets.QMessageBox.Ok:
         ids = set([i.entry.objectid for i in items])
         i = 0
         while i < self._result_tree.topLevelItemCount():
             oid = self._result_tree.topLevelItem(i).entry.objectid
             if oid in ids:
                 self.store.remove(oid)
                 self._result_tree.takeTopLevelItem(i)
             else:
                 i += 1
Exemple #15
0
def cmd_query(store, args):
    """Query command.

    query [-d] [-t] [key1=value1] [...]
    """
    pydict = False
    types = False
    while args and args[0][0] == '-':
        if args[0] == '-d':
            pydict = True
        elif args[0] == '-t':
            types = True
        elif args[0] == '--':
            del args[0]
            break
        else:
            sys.stderr.write(_("Unknown option: {opt}\n", opt=args[0]))
            sys.exit(1)
        del args[0]
    h, metadata = parse_query_metadata(args)
    if h is not None:
        entries = [store.get(h)]
    else:
        entries = store.query(metadata)

    if not pydict:
        for entry in sorted(entries, key=lambda e: e.objectid):
            sys.stdout.write("%s\n" % entry.objectid)
            for k, v in sorted(entry.metadata.items(), key=lambda p: p[0]):
                if types:
                    if isinstance(v, int_types):
                        v = 'int:%d' % v
                    else:  # isinstance(v, string_types):
                        v = 'str:%s' % v
                sys.stdout.write("\t%s\t%s\n" % (k, v))
    else:
        sys.stdout.write('{')
        for entry_nb, entry in enumerate(sorted(entries,
                                                key=lambda e: e.objectid)):
            sys.stdout.write(',\n' if entry_nb > 0 else '\n')
            sys.stdout.write('    "%s": {' % entry.objectid)
            for meta_nb, (k, v) in enumerate(sorted(entry.metadata.items(),
                                                    key=lambda p: p[0])):
                sys.stdout.write(',\n' if meta_nb > 0 else '\n')
                if types:
                    if isinstance(v, int_types):
                        v = '{"type": "int", "value": %d}' % v
                    else:  # isinstance(v, string_types):
                        assert isinstance(v, unicode_type)
                        v = '{"type": "str", "value": %s}' % json.dumps(v)
                else:
                    if isinstance(v, int_types):
                        v = '%d' % v
                    else:  # isinstance(v, string_types):
                        assert isinstance(v, unicode_type)
                        v = json.dumps(v)
                k = json.dumps(k)
                sys.stdout.write("        %s: %s" % (k, v))
            sys.stdout.write('\n    }')
        sys.stdout.write('\n}\n')
Exemple #16
0
def cmd_remove(store, args):
    """Remove command.

    remove [-f] <filehash>
    remove [-f] <key1=value1> [...]
    """
    if args and args[0] == '-f':
        del args[0]
        force = True
    else:
        force = False
    h, metadata = parse_query_metadata(args)
    if h is not None:
        store.remove(h)
    else:
        entries = store.query(metadata)
        if not args and not force:
            nb = sum(1 for e in entries)
            if nb:
                sys.stderr.write(_(
                    "Error: not removing files unconditionally unless -f "
                    "is given\n"
                    "(command would have removed {nb} files)\n",
                    nb=nb))
                sys.exit(1)
        for e in entries:
            store.remove(e)
Exemple #17
0
 def _delete(self):
     items = self._result_tree.selectedItems()
     if not items:
         return
     confirm = QtWidgets.QMessageBox.question(
         self, _("Are you sure?"),
         _n(
             "You are about to delete {num} entry from the store. "
             "Please confirm.",
             "You are about to delete {num} entries from the store. "
             "Please confirm.",
             len(items),
             num=len(items)),
         QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel,
         QtWidgets.QMessageBox.Cancel)
     if confirm == QtWidgets.QMessageBox.Ok:
         ids = set([i.entry.objectid for i in items])
         i = 0
         while i < self._result_tree.topLevelItemCount():
             oid = self._result_tree.topLevelItem(i).entry.objectid
             if oid in ids:
                 self.store.remove(oid)
                 self._result_tree.takeTopLevelItem(i)
             else:
                 i += 1
Exemple #18
0
def cmd_remove(store, args):
    """Remove command.

    remove [-f] <filehash>
    remove [-f] <key1=value1> [...]
    """
    if args and args[0] == '-f':
        del args[0]
        force = True
    else:
        force = False
    h, metadata = parse_query_metadata(args)
    if h is not None:
        store.remove(h)
    else:
        entries = store.query(metadata)
        if not args and not force:
            nb = sum(1 for e in entries)
            if nb:
                sys.stderr.write(
                    _(
                        "Error: not removing files unconditionally unless -f "
                        "is given\n"
                        "(command would have removed {nb} files)\n",
                        nb=nb))
                sys.exit(1)
        for e in entries:
            store.remove(e)
Exemple #19
0
def cmd_query(store, args):
    """Query command.

    query [-d] [-t] [key1=value1] [...]
    """
    pydict = False
    types = False
    while args and args[0][0] == '-':
        if args[0] == '-d':
            pydict = True
        elif args[0] == '-t':
            types = True
        elif args[0] == '--':
            del args[0]
            break
        else:
            sys.stderr.write(_("Unknown option: {opt}\n", opt=args[0]))
            sys.exit(1)
        del args[0]
    h, metadata = parse_query_metadata(args)
    if h is not None:
        entries = [store.get(h)]
    else:
        entries = store.query(metadata)

    if not pydict:
        for entry in sorted(entries, key=lambda e: e.objectid):
            sys.stdout.write("%s\n" % entry.objectid)
            for k, v in sorted(entry.metadata.items(), key=lambda p: p[0]):
                if types:
                    if isinstance(v, int_types):
                        v = 'int:%d' % v
                    else:  # isinstance(v, string_types):
                        v = 'str:%s' % v
                sys.stdout.write("\t%s\t%s\n" % (k, v))
    else:
        sys.stdout.write('{')
        for entry_nb, entry in enumerate(
                sorted(entries, key=lambda e: e.objectid)):
            sys.stdout.write(',\n' if entry_nb > 0 else '\n')
            sys.stdout.write('    "%s": {' % entry.objectid)
            for meta_nb, (k, v) in enumerate(
                    sorted(entry.metadata.items(), key=lambda p: p[0])):
                sys.stdout.write(',\n' if meta_nb > 0 else '\n')
                if types:
                    if isinstance(v, int_types):
                        v = '{"type": "int", "value": %d}' % v
                    else:  # isinstance(v, string_types):
                        assert isinstance(v, unicode_type)
                        v = '{"type": "str", "value": %s}' % json.dumps(v)
                else:
                    if isinstance(v, int_types):
                        v = '%d' % v
                    else:  # isinstance(v, string_types):
                        assert isinstance(v, unicode_type)
                        v = json.dumps(v)
                k = json.dumps(k)
                sys.stdout.write("        %s: %s" % (k, v))
            sys.stdout.write('\n    }')
        sys.stdout.write('\n}\n')
Exemple #20
0
def cmd_verify(store, args):
    """Verify command.

    This command accepts no argument.
    """
    if args:
        sys.stderr.write(_("verify command accepts no argument\n"))
        sys.exit(1)
    store.verify()
Exemple #21
0
def cmd_add(store, args):
    """Add command.

    add <filename> [key1=value1] [...]
    """
    if not args:
        sys.stderr.write(_("Missing filename\n"))
        sys.exit(1)
    filename = args[0]
    if not os.path.exists(filename):
        sys.stderr.write(_("Path does not exist: {path}\n", path=filename))
        sys.exit(1)
    metadata = parse_new_metadata(args[1:])
    if os.path.isdir(filename):
        entry = store.add_directory(filename, metadata)
    else:
        entry = store.add_file(filename, metadata)
    sys.stdout.write('%s\n' % entry.objectid)
Exemple #22
0
def cmd_add(store, args):
    """Add command.

    add <filename> [key1=value1] [...]
    """
    if not args:
        sys.stderr.write(_("Missing filename\n"))
        sys.exit(1)
    filename = args[0]
    if not os.path.exists(filename):
        sys.stderr.write(_("Path does not exist: {path}\n", path=filename))
        sys.exit(1)
    metadata = parse_new_metadata(args[1:])
    if os.path.isdir(filename):
        entry = store.add_directory(filename, metadata)
    else:
        entry = store.add_file(filename, metadata)
    sys.stdout.write('%s\n' % entry.objectid)
Exemple #23
0
def cmd_verify(store, args):
    """Verify command.

    This command accepts no argument.
    """
    if args:
        sys.stderr.write(_("verify command accepts no argument\n"))
        sys.exit(1)
    store.verify()
Exemple #24
0
    def _ok_clicked(self):
        error = None

        metadata = {}

        for row in range(self._table.rowCount()):
            key = self._table.item(row, 0).text()
            type_ = self._table.cellWidget(row, 1).currentText()
            value = self._table.item(row, 2).text()

            if not key:
                error = _("Empty key")

            if type_ == 'int':
                try:
                    value = int(value)
                except ValueError:
                    error = (_("Invalid int value for %(key)s (row %(row)d)") %
                             {
                                 'key': key,
                                 'row': row + 1
                             })
            elif type_ != 'str':
                error = _("Invalid type (row %d)") % (row + 1)

            if key in metadata:
                error = _("Duplicate key %s" % key)

            if error is not None:
                break

            metadata[key] = {'type': type_, 'value': value}

        if error is not None:
            QtWidgets.QMessageBox.critical(self, _("Invalid values"), error)
            return

        metadata['hash'] = self._entry.metadata['hash']

        self._parent.change_metadata(self._entry.objectid, metadata,
                                     self._remove_original.isChecked())
        self.setVisible(False)
Exemple #25
0
    def _ok_clicked(self):
        error = None

        metadata = {}

        for row in range(self._table.rowCount()):
            key = self._table.item(row, 0).text()
            type_ = self._table.cellWidget(row, 1).currentText()
            value = self._table.item(row, 2).text()

            if not key:
                error = _("Empty key")

            if type_ == 'int':
                try:
                    value = int(value)
                except ValueError:
                    error = (_("Invalid int value for %(key)s (row %(row)d)") %
                             {'key': key, 'row': row + 1})
            elif type_ != 'str':
                error = _("Invalid type (row %d)") % (row + 1)

            if key in metadata:
                error = _("Duplicate key %s" % key)

            if error is not None:
                break

            metadata[key] = {'type': type_, 'value': value}

        if error is not None:
            QtWidgets.QMessageBox.critical(self, _("Invalid values"), error)
            return

        metadata['hash'] = self._entry.metadata['hash']

        self._parent.change_metadata(self._entry.objectid, metadata,
                                     self._remove_original.isChecked())
        self.setVisible(False)
Exemple #26
0
    def __init__(self, entry, parent):
        QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.Dialog)
        self.setWindowModality(QtCore.Qt.ApplicationModal)

        self._entry = entry
        self._parent = parent

        label = QtWidgets.QLabel(_("Editing entry %s") % entry.objectid)

        self._table = QtWidgets.QTableWidget()
        self._table.setColumnCount(3)
        self._table.setHorizontalHeaderLabels(['key', 'type', 'value'])
        self._table.setSortingEnabled(True)
        self._table.sortByColumn(0, QtCore.Qt.AscendingOrder)
        self._table.resizeColumnsToContents()
        scrollarea = QtWidgets.QScrollArea()
        scrollarea.setWidgetResizable(True)
        scrollarea.setWidget(self._table)

        plus = QtWidgets.QPushButton(_("+"))
        plus.clicked.connect(self._add_row)
        minus = QtWidgets.QPushButton(_("-"))
        minus.clicked.connect(self._remove_row)
        controls = QtWidgets.QVBoxLayout()
        controls.addWidget(plus)
        controls.addWidget(minus)

        table_row = QtWidgets.QHBoxLayout()
        table_row.addWidget(scrollarea)
        table_row.addLayout(controls)

        self._remove_original = QtWidgets.QCheckBox(_("Remove original entry"))
        self._remove_original.setChecked(False)
        self._remove_original.stateChanged.connect(self._mode_changed)

        self._ok_button = QtWidgets.QPushButton(_("Create new entry"))
        self._ok_button.clicked.connect(self._ok_clicked)
        cancel_button = QtWidgets.QPushButton(_("Cancel"))
        cancel_button.clicked.connect(lambda: self.setVisible(False))
        buttons = QtWidgets.QHBoxLayout()
        buttons.addStretch()
        buttons.addWidget(self._ok_button)
        buttons.addWidget(cancel_button)

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(label)
        layout.addLayout(table_row)
        layout.addWidget(self._remove_original)
        layout.addLayout(buttons)
        self.setLayout(layout)

        self._table.setSortingEnabled(False)
        for k, v in self._entry.metadata.items():
            if k == 'hash':
                continue
            self._add_row(k, v, sorting_disabled=True)
        self._table.setSortingEnabled(True)
Exemple #27
0
    def __init__(self, entry, parent):
        QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.Dialog)
        self.setWindowModality(QtCore.Qt.ApplicationModal)

        self._entry = entry
        self._parent = parent

        label = QtWidgets.QLabel(_("Editing entry %s") % entry.objectid)

        self._table = QtWidgets.QTableWidget()
        self._table.setColumnCount(3)
        self._table.setHorizontalHeaderLabels(['key', 'type', 'value'])
        self._table.setSortingEnabled(True)
        self._table.sortByColumn(0, QtCore.Qt.AscendingOrder)
        self._table.resizeColumnsToContents()
        scrollarea = QtWidgets.QScrollArea()
        scrollarea.setWidgetResizable(True)
        scrollarea.setWidget(self._table)

        plus = QtWidgets.QPushButton(_("+"))
        plus.clicked.connect(self._add_row)
        minus = QtWidgets.QPushButton(_("-"))
        minus.clicked.connect(self._remove_row)
        controls = QtWidgets.QVBoxLayout()
        controls.addWidget(plus)
        controls.addWidget(minus)

        table_row = QtWidgets.QHBoxLayout()
        table_row.addWidget(scrollarea)
        table_row.addLayout(controls)

        self._remove_original = QtWidgets.QCheckBox(_("Remove original entry"))
        self._remove_original.setChecked(False)
        self._remove_original.stateChanged.connect(self._mode_changed)

        self._ok_button = QtWidgets.QPushButton(_("Create new entry"))
        self._ok_button.clicked.connect(self._ok_clicked)
        cancel_button = QtWidgets.QPushButton(_("Cancel"))
        cancel_button.clicked.connect(lambda: self.setVisible(False))
        buttons = QtWidgets.QHBoxLayout()
        buttons.addStretch()
        buttons.addWidget(self._ok_button)
        buttons.addWidget(cancel_button)

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(label)
        layout.addLayout(table_row)
        layout.addWidget(self._remove_original)
        layout.addLayout(buttons)
        self.setLayout(layout)

        self._table.setSortingEnabled(False)
        for k, v in self._entry.metadata.items():
            if k == 'hash':
                continue
            self._add_row(k, v, sorting_disabled=True)
        self._table.setSortingEnabled(True)
Exemple #28
0
class StoreViewerWindow(QtWidgets.QMainWindow):
    WINDOW_TITLE = _("file_archive viewer")

    MAX_RESULTS = 100

    def __init__(self, store):
        QtWidgets.QMainWindow.__init__(self)
        self.setWindowTitle(self.WINDOW_TITLE)

        self.store = store

        searchbar = QtWidgets.QHBoxLayout()

        self._needs_refresh = False

        # Input line for the query
        self._input = QtWidgets.QLineEdit()
        self._input.setPlaceholderText(_("Enter query here"))
        self._input.returnPressed.connect(self._search)
        self._input.textEdited.connect(lambda t: self._set_needs_refresh())
        searchbar.addWidget(self._input)

        # Search button
        self._searchbutton = QtWidgets.QPushButton(_("Search"))
        self._searchbutton.clicked.connect(self._search)
        searchbar.addWidget(self._searchbutton)

        results = QtWidgets.QHBoxLayout()

        # Result view, as a tree with metadata
        self._result_tree = QtWidgets.QTreeWidget()
        self._result_tree.setColumnCount(3)
        self._result_tree.setHeaderLabels([_("Key"), _("Value"), _("Type")])
        self._result_tree.itemSelectionChanged.connect(self._selection_changed)
        results.addWidget(self._result_tree)

        # Buttons, enabled/disabled when the selection changes
        buttons = QtWidgets.QVBoxLayout()
        self._buttons = self._create_buttons()

        for _name, button in self._buttons:
            buttons.addWidget(button)
        self._selection_changed()
        results.addLayout(buttons)

        layout = QtWidgets.QVBoxLayout()
        layout.addLayout(searchbar)
        layout.addLayout(results)

        widget = QtWidgets.QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)

        self._search()

    def _create_buttons(self):
        buttons = []

        # Open button; uses the system to choose the program to open with
        # (on Windows, might ask you what to use every time because of filename
        # scheme)
        open_button = QtWidgets.QPushButton(_("Open"))
        if openfile is not None:
            open_button.clicked.connect(self._openfile)
            buttons.append(('single', open_button))
        else:
            open_button.setEnabled(False)

        # Copy hash button
        copy_button = QtWidgets.QPushButton(_("Copy ID"))
        copy_button.clicked.connect(self._copy_objectid)
        buttons.append(('single', copy_button))

        # Edit metadata button
        edit_button = QtWidgets.QPushButton(_("Edit metadata..."))
        edit_button.clicked.connect(self._edit_metadata)
        buttons.append(('single', edit_button))

        # Delete button, removes what's selected (with confirmation)
        remove_button = QtWidgets.QPushButton(_("Delete"))
        remove_button.clicked.connect(self._delete)
        buttons.append(('multi', remove_button))

        return buttons

    def _set_needs_refresh(self, needs=True):
        if needs == self._needs_refresh:
            return

        if needs:
            self._searchbutton.setStyleSheet('font-weight: bold;')
        else:
            self._searchbutton.setStyleSheet('')
        self._needs_refresh = needs

    def _alter_search_conditions(self, conditions):
        return conditions

    def _search(self):
        error = None

        query = self._input.text()

        if len(query.split()) == 1 and all(o not in query.strip()
                                           for o in '=<>'):
            objectid = query.strip()
            try:
                entries = [self.store.get(objectid)]
            except KeyError:
                error = _("objectid '{oid}' not found", oid=objectid)

        else:
            try:
                conditions = parse_expression(query)
            except tdparser.Error as e:
                error = e.args[0]
            else:
                conditions = self._alter_search_conditions(conditions)
                entries = self.store.query(conditions, limit=self.MAX_RESULTS)

        self._result_tree.clear()

        if error is not None:
            w = QtWidgets.QTreeWidgetItem([error])
            w.setForeground(0, QtGui.QColor(255, 0, 0))
            self._result_tree.addTopLevelItem(w)
            self._result_tree.setFirstItemColumnSpanned(w, True)
        else:
            for i, entry in enumerate(entries):
                file_item = FileItem(entry)
                f = file_item.font(0)
                f.setBold(True)
                file_item.setFont(0, f)
                self._result_tree.addTopLevelItem(file_item)
                self._result_tree.setFirstItemColumnSpanned(file_item, True)
                for k, v in entry.metadata.items():
                    file_item.addChild(MetadataItem(entry, k, v))

                if i + 1 == self.MAX_RESULTS:
                    last_item = QtWidgets.QTreeWidgetItem([
                        _("... stripped after {nb} results...",
                          nb=self.MAX_RESULTS)
                    ])
                    f = last_item.font(0)
                    f.setBold(True)
                    f.setItalic(True)
                    last_item.setFont(0, f)
                    self._result_tree.addTopLevelItem(last_item)
                    self._result_tree.setFirstItemColumnSpanned(
                        last_item, True)
                    break
            if self._result_tree.topLevelItemCount() == 0:
                w = QtWidgets.QTreeWidgetItem([_("No matches")])
                self._result_tree.addTopLevelItem(w)
                self._result_tree.setFirstItemColumnSpanned(w, True)
            self._result_tree.expandAll()

        self._set_needs_refresh(False)

    def _selection_changed(self):
        items = self._result_tree.selectedItems()
        for t, button in self._buttons:
            if t == 'single':
                button.setEnabled(
                    len(items) == 1 and isinstance(items[0], FileItem))
            elif t == 'multi':
                button.setEnabled(
                    bool(items)
                    and all(isinstance(i, FileItem) for i in items))

    def _openfile(self):
        item = self._result_tree.currentItem()
        if item is not None:
            openfile(item.entry.filename)

    def _copy_objectid(self):
        items = self._result_tree.selectedItems()
        if not items:
            return
        objectid = items[0].entry.objectid

        clipboard = QtWidgets.QApplication.clipboard()
        clipboard.setText(objectid)

    def _edit_metadata(self):
        items = self._result_tree.selectedItems()
        if not items:
            return
        entry = items[0].entry

        editor = MetadataEditor(entry, self)
        editor.show()

    def change_metadata(self, old_objectid, metadata, remove_original=False):
        new_objectid = hash_metadata(metadata)
        if new_objectid == old_objectid:
            return

        self.store.metadata.add(new_objectid, metadata)

        if remove_original:
            self.store.remove(old_objectid)

        self._search()

    def _delete(self):
        items = self._result_tree.selectedItems()
        if not items:
            return
        confirm = QtWidgets.QMessageBox.question(
            self, _("Are you sure?"),
            _n(
                "You are about to delete {num} entry from the store. "
                "Please confirm.",
                "You are about to delete {num} entries from the store. "
                "Please confirm.",
                len(items),
                num=len(items)),
            QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel,
            QtWidgets.QMessageBox.Cancel)
        if confirm == QtWidgets.QMessageBox.Ok:
            ids = set([i.entry.objectid for i in items])
            i = 0
            while i < self._result_tree.topLevelItemCount():
                oid = self._result_tree.topLevelItem(i).entry.objectid
                if oid in ids:
                    self.store.remove(oid)
                    self._result_tree.takeTopLevelItem(i)
                else:
                    i += 1
Exemple #29
0
 def _mode_changed(self, remove_original):
     if remove_original:
         self._ok_button.setText(_("Replace entry"))
     else:
         self._ok_button.setText(_("Create new entry"))
Exemple #30
0
 def _mode_changed(self, remove_original):
     if remove_original:
         self._ok_button.setText(_("Replace entry"))
     else:
         self._ok_button.setText(_("Create new entry"))
Exemple #31
0
def cmd_view(store, args):
    if args:
        sys.stderr.write(_("view command accepts no argument\n"))
        sys.exit(1)
    from file_archive.viewer import run_viewer
    run_viewer(store)
Exemple #32
0
def parse_query_metadata(args):
    """Parses a list of key=value arguments or a hash value.

    Returns (hash:str, metadata:dict)
    """
    if len(args) == 1 and '=' not in args[0]:
        return args[0], None
    else:
        metadata = {}
        for a in args:
            k = a.split('=', 1)
            if len(k) != 2:
                sys.stderr.write(
                    _("Metadata should have format key=value, "
                      "key=type:value (eg. age=int:23) or "
                      "key=type:req (eg. age=int:>21)\n"))
                sys.exit(1)
            k, v = k
            if ':' in v:
                t, v = v.split(':', 1)
                if t == 'int':
                    if v[0] == '>':
                        req, v = 'gt', v[1:]
                    elif v[0] == '<':
                        req, v = 'lt', v[1:]
                    else:
                        req = 'equal'
                    v = int(v)
                elif t == 'str':
                    req = 'equal'
                else:
                    sys.stderr.write(
                        _(
                            "Metadata has unknown type '{t}'! "
                            "Only 'str' and 'int' are supported.\n"
                            "If you meant a string with a ':', "
                            "use 'str:mystring'",
                            t=t))
                    sys.exit(1)
            else:
                t = 'str'
                req = 'equal'
            if t == 'str' and isinstance(v, bytes):
                v = v.decode(locale.getpreferredencoding())
            if k in metadata:
                if t != metadata[k]['type']:
                    sys.stderr.write(
                        _(
                            "Differing types for conditions on "
                            "key {k}: {t1}, {t2}\n",
                            k=k,
                            t1=metadata[k]['type'],
                            t2=t))
                    sys.exit(1)
                if req in metadata[k]:
                    sys.stderr.write(
                        _("Multiple conditions {cond} on key "
                          "{k}\n",
                          cond=req,
                          k=k))
                    sys.exit(1)
                metadata[k][req] = v
            else:
                metadata[k] = {'type': t, req: v}
        return None, metadata
Exemple #33
0
def cmd_view(store, args):
    if args:
        sys.stderr.write(_("view command accepts no argument\n"))
        sys.exit(1)
    from file_archive.viewer import run_viewer
    run_viewer(store)