From e1c6234dd70bddc96aab44bf64f131e5a07cf34d Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Wed, 16 Feb 2022 11:41:07 +0100 Subject: [PATCH] Have find_best_match and sort_by_similarity accept any iterable --- picard/album.py | 4 ++-- picard/cluster.py | 4 ++-- picard/file.py | 2 +- picard/ui/searchdialog/track.py | 4 ++-- picard/util/__init__.py | 18 ++++++++++++++++-- test/test_utils.py | 9 +++------ 6 files changed, 26 insertions(+), 15 deletions(-) diff --git a/picard/album.py b/picard/album.py index fc4e79fed..76d956ea1 100644 --- a/picard/album.py +++ b/picard/album.py @@ -688,7 +688,7 @@ class Album(DataObject, Item): similarity = track.metadata.length_score(track.metadata.length, file.metadata.length) yield SimMatchAlbum(similarity=similarity, track=track) - best_match = find_best_match(mbid_candidates, no_match) + best_match = find_best_match(mbid_candidates(), no_match) if best_match != no_match: yield (file, best_match.result.track) continue @@ -700,7 +700,7 @@ class Album(DataObject, Item): if similarity >= threshold: yield SimMatchAlbum(similarity=similarity, track=track) - best_match = find_best_match(similarity_candidates, no_match) + best_match = find_best_match(similarity_candidates(), no_match) yield (file, best_match.result.track) def match_files(self, files): diff --git a/picard/cluster.py b/picard/cluster.py index a97fce332..035732d26 100644 --- a/picard/cluster.py +++ b/picard/cluster.py @@ -6,7 +6,7 @@ # Copyright (C) 2006-2008, 2011 Lukáš Lalinský # Copyright (C) 2008 Hendrik van Antwerpen # Copyright (C) 2008 Will -# Copyright (C) 2010-2011, 2014, 2018-2021 Philipp Wolfer +# Copyright (C) 2010-2011, 2014, 2018-2022 Philipp Wolfer # Copyright (C) 2011-2013 Michael Wiencek # Copyright (C) 2012 Chad Wilson # Copyright (C) 2012 Wieland Hoffmann @@ -271,7 +271,7 @@ class Cluster(FileList): yield match no_match = SimMatchRelease(similarity=-1, release=None) - best_match = find_best_match(candidates, no_match) + best_match = find_best_match(candidates(), no_match) return best_match.result.release def lookup_metadata(self): diff --git a/picard/file.py b/picard/file.py index 1352e6ddc..473b7846a 100644 --- a/picard/file.py +++ b/picard/file.py @@ -830,7 +830,7 @@ class File(QtCore.QObject, Item): yield self.metadata.compare_to_track(track, self.comparison_weights) no_match = SimMatchTrack(similarity=-1, releasegroup=None, release=None, track=None) - best_match = find_best_match(candidates, no_match) + best_match = find_best_match(candidates(), no_match) if best_match.similarity < threshold: return None diff --git a/picard/ui/searchdialog/track.py b/picard/ui/searchdialog/track.py index 0264e49c2..8f2dc1208 100644 --- a/picard/ui/searchdialog/track.py +++ b/picard/ui/searchdialog/track.py @@ -5,7 +5,7 @@ # Copyright (C) 2016 Rahul Raturi # Copyright (C) 2018 Antonio Larrosa # Copyright (C) 2018-2021 Laurent Monin -# Copyright (C) 2018-2021 Philipp Wolfer +# Copyright (C) 2018-2022 Philipp Wolfer # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -134,7 +134,7 @@ class TrackSearchDialog(SearchDialog): for track in tracks: yield metadata.compare_to_track(track, File.comparison_weights) - tracks = [result.track for result in sort_by_similarity(candidates)] + tracks = [result.track for result in sort_by_similarity(candidates())] del self.search_results[:] # Clear existing data self.parse_tracks(tracks) diff --git a/picard/util/__init__.py b/picard/util/__init__.py index b2c7dc778..f583ef3ed 100644 --- a/picard/util/__init__.py +++ b/picard/util/__init__.py @@ -724,15 +724,29 @@ BestMatch = namedtuple('BestMatch', ('similarity', 'result')) def sort_by_similarity(candidates): + """Sorts the objects in candidates by similarity. + + Args: + candidates: Iterable with objects having a `similarity` attribute + Returns: List of candidates sorted by similarity (highest similarity first) + """ return sorted( - candidates(), + candidates, reverse=True, key=attrgetter('similarity') ) def find_best_match(candidates, no_match): - best_match = max(candidates(), key=attrgetter('similarity'), default=no_match) + """Returns a BestMatch based on the similarity of candidates. + + Args: + candidates: Iterable with objects having a `similarity` attribute + no_match: Match to return if there was no candidate + + Returns: `BestMatch` with the similarity and the matched object as result. + """ + best_match = max(candidates, key=attrgetter('similarity'), default=no_match) return BestMatch(similarity=best_match.similarity, result=best_match) diff --git a/test/test_utils.py b/test/test_utils.py index 7343a6930..17eda1f69 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -407,16 +407,13 @@ class SortBySimilarity(PicardTestCase): SimMatchTest(similarity=0.75, name='c'), ] - def candidates(self): - yield from self.test_values - def test_sort_by_similarity(self): - results = [result.name for result in sort_by_similarity(self.candidates)] + results = [result.name for result in sort_by_similarity(self.test_values)] self.assertEqual(results, ['b', 'c', 'd', 'a']) def test_findbestmatch(self): no_match = SimMatchTest(similarity=-1, name='no_match') - best_match = find_best_match(self.candidates, no_match) + best_match = find_best_match(self.test_values, no_match) self.assertEqual(best_match.result.name, 'b') self.assertEqual(best_match.similarity, 0.75) @@ -425,7 +422,7 @@ class SortBySimilarity(PicardTestCase): self.test_values = [] no_match = SimMatchTest(similarity=-1, name='no_match') - best_match = find_best_match(self.candidates, no_match) + best_match = find_best_match(self.test_values, no_match) self.assertEqual(best_match.result.name, 'no_match') self.assertEqual(best_match.similarity, -1)