From 119a5c42f0e43226d371e5fec7b3dc7f2ca2e903 Mon Sep 17 00:00:00 2001 From: Matthias Vogelgesang Date: Mon, 4 Sep 2017 11:23:52 +0200 Subject: [PATCH 1/2] Implement fuzzy search and ranking The regex based method to find key matches does not work very well for deeper hierarchies and partial matching of the hierarchy levels. This approach uses the SequenceMatcher from the difflib module to compute a score for an entry and rank them accordingly. This change also filters out all hidden file system entries to avoid traversing and evaluating Git repositories which are commonly used to store pass storages. --- gnome-pass-search-provider.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/gnome-pass-search-provider.py b/gnome-pass-search-provider.py index f877f33..92a229e 100755 --- a/gnome-pass-search-provider.py +++ b/gnome-pass-search-provider.py @@ -27,8 +27,9 @@ from os import getenv from os import walk -from os.path import expanduser +from os.path import expanduser, join as path_join import re +import difflib import subprocess import dbus @@ -81,22 +82,25 @@ class SearchPassService(dbus.service.Object): pass def get_result_set(self, terms): - names = [] - for term in terms: - names += self.get_password_names(term) - return set(names) + name = ' '.join(terms) + matcher = difflib.SequenceMatcher(b=name, autojunk=False) + matches = [] - def get_password_names(self, name): - names = [] for root, dirs, files in walk(self.password_store): dir_path = root[len(self.password_store) + 1:] - for file in files: - file_path = '{0}/{1}'.format(dir_path, file) - if re.match(r'.*{0}.*\.gpg$'.format(name), - file_path, - re.IGNORECASE): - names.append(file_path[:-4]) - return names + + if dir_path.startswith('.'): + continue + + for filename in files: + path = path_join(dir_path, filename) + matcher.set_seq1(path[:-4]) + score = matcher.ratio() + + if score >= 0.5: + matches.append((score, path[:-4])) + + return [x[1] for x in sorted(matches, key=lambda x: x[0], reverse=True)] def send_password_to_gpaste(self, name): pass_cmd = subprocess.run( From 7b1a5189c7107816fe659f6127100fd1d5544838 Mon Sep 17 00:00:00 2001 From: Jonathan Lestrelin Date: Mon, 4 Sep 2017 15:08:17 +0200 Subject: [PATCH 2/2] Make fuzzy matching happen on every path element instead of on whole path. --- gnome-pass-search-provider.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/gnome-pass-search-provider.py b/gnome-pass-search-provider.py index 92a229e..9a212a0 100755 --- a/gnome-pass-search-provider.py +++ b/gnome-pass-search-provider.py @@ -25,11 +25,12 @@ # Copyright (C) 2012 Red Hat, Inc. # Author: Luke Macken +import difflib from os import getenv from os import walk -from os.path import expanduser, join as path_join +from os.path import expanduser +from os.path import join as path_join import re -import difflib import subprocess import dbus @@ -84,7 +85,7 @@ class SearchPassService(dbus.service.Object): def get_result_set(self, terms): name = ' '.join(terms) matcher = difflib.SequenceMatcher(b=name, autojunk=False) - matches = [] + matches = {} for root, dirs, files in walk(self.password_store): dir_path = root[len(self.password_store) + 1:] @@ -93,14 +94,15 @@ class SearchPassService(dbus.service.Object): continue for filename in files: - path = path_join(dir_path, filename) - matcher.set_seq1(path[:-4]) - score = matcher.ratio() + path = path_join(dir_path, filename)[:-4] + for name in path.split('/'): + matcher.set_seq1(name) + score = matcher.ratio() - if score >= 0.5: - matches.append((score, path[:-4])) + if score >= 0.5 and (path not in matches or score > matches[path]): + matches[path] = score - return [x[1] for x in sorted(matches, key=lambda x: x[0], reverse=True)] + return sorted(matches, key=matches.__getitem__, reverse=True) def send_password_to_gpaste(self, name): pass_cmd = subprocess.run(