From f9c6dd6d6736ee3adb44119b77fa7e2e8773e178 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Mon, 15 Sep 2014 19:43:22 -0700 Subject: [PATCH] Move SmartArtistSort to library (#953) Models can now have a dict of special sort classes. --- beets/dbcore/db.py | 5 +++++ beets/dbcore/query.py | 19 ------------------- beets/dbcore/queryparse.py | 7 +++---- beets/library.py | 26 +++++++++++++++++++++++++- test/test_dbcore.py | 11 +++++++++++ 5 files changed, 44 insertions(+), 24 deletions(-) diff --git a/beets/dbcore/db.py b/beets/dbcore/db.py index be0e3a93a..5392688e0 100644 --- a/beets/dbcore/db.py +++ b/beets/dbcore/db.py @@ -124,6 +124,11 @@ class Model(object): """Optional Types for non-fixed (i.e., flexible and computed) fields. """ + _sorts = {} + """Optional named sort criteria. The keys are strings and the values + are subclasses of `Sort`. + """ + @classmethod def _getters(cls): """Return a mapping from field names to getter functions. diff --git a/beets/dbcore/query.py b/beets/dbcore/query.py index cc1892a1f..4f6c0c6ab 100644 --- a/beets/dbcore/query.py +++ b/beets/dbcore/query.py @@ -619,25 +619,6 @@ class FixedFieldSort(FieldSort): return "{0} {1}".format(self.field, order) -class SmartArtistSort(Sort): - """Sort by artist (either album artist or track artist), - prioritizing the sort field over the raw field. - """ - def __init__(self, model_cls, is_ascending=True): - self.model_cls = model_cls - self.is_ascending = is_ascending - - def order_clause(self): - order = "ASC" if self.is_ascending else "DESC" - if 'albumartist' in self.model_cls._fields: - field = 'albumartist' - else: - field = 'artist' - return ('(CASE {0}_sort WHEN NULL THEN {0} ' - 'WHEN "" THEN {0} ' - 'ELSE {0}_sort END) {1}').format(field, order) - - class SlowFieldSort(FieldSort): """A sort criterion by some model field other than a fixed field: i.e., a computed or flexible field. diff --git a/beets/dbcore/queryparse.py b/beets/dbcore/queryparse.py index 3d2855088..90963696b 100644 --- a/beets/dbcore/queryparse.py +++ b/beets/dbcore/queryparse.py @@ -136,11 +136,10 @@ def construct_sort_part(model_cls, part): assert direction in ('+', '-'), "part must end with + or -" is_ascending = direction == '+' - if field in model_cls._fields: + if field in model_cls._sorts: + sort = model_cls._sorts[field](model_cls, is_ascending) + elif field in model_cls._fields: sort = query.FixedFieldSort(field, is_ascending) - elif field == 'smartartist': - # Special case for smart artist sort. - sort = query.SmartArtistSort(model_cls, is_ascending) else: # Flexible or computed. sort = query.SlowFieldSort(field, is_ascending) diff --git a/beets/library.py b/beets/library.py index 14f7e37d9..e9ef754aa 100644 --- a/beets/library.py +++ b/beets/library.py @@ -60,7 +60,6 @@ class PathQuery(dbcore.FieldQuery): # Library-specific field types. - class DateType(types.Float): # TODO representation should be `datetime` object # TODO distinguish beetween date and time types @@ -144,6 +143,27 @@ class MusicalKey(types.String): return self.parse(key) +# Library-specific sort types. + +class SmartArtistSort(dbcore.query.Sort): + """Sort by artist (either album artist or track artist), + prioritizing the sort field over the raw field. + """ + def __init__(self, model_cls, is_ascending=True): + self.model_cls = model_cls + self.is_ascending = is_ascending + + def order_clause(self): + order = "ASC" if self.is_ascending else "DESC" + if 'albumartist' in self.model_cls._fields: + field = 'albumartist' + else: + field = 'artist' + return ('(CASE {0}_sort WHEN NULL THEN {0} ' + 'WHEN "" THEN {0} ' + 'ELSE {0}_sort END) {1}').format(field, order) + + # Special path format key. PF_KEY_DEFAULT = 'default' @@ -347,6 +367,8 @@ class Item(LibModel): _formatter = FormattedItemMapping + _sorts = {'smartartist': SmartArtistSort} + @classmethod def _getters(cls): getters = plugins.item_field_getters() @@ -710,6 +732,8 @@ class Album(LibModel): _search_fields = ('album', 'albumartist', 'genre') + _sorts = {'smartartist': SmartArtistSort} + item_keys = [ 'added', 'albumartist', diff --git a/test/test_dbcore.py b/test/test_dbcore.py index 68a4b61ef..395c3cf9c 100644 --- a/test/test_dbcore.py +++ b/test/test_dbcore.py @@ -25,6 +25,10 @@ from tempfile import mkstemp # Fixture: concrete database and model classes. For migration tests, we # have multiple models with different numbers of fields. +class TestSort(dbcore.query.FieldSort): + pass + + class TestModel1(dbcore.Model): _table = 'test' _flex_table = 'testflex' @@ -35,6 +39,9 @@ class TestModel1(dbcore.Model): _types = { 'some_float_field': dbcore.types.FLOAT, } + _sorts = { + 'some_sort': TestSort, + } @classmethod def _getters(cls): @@ -455,6 +462,10 @@ class SortFromStringsTest(unittest.TestCase): self.assertIsInstance(s, dbcore.query.MultipleSort) self.assertIsInstance(s.sorts[0], dbcore.query.SlowFieldSort) + def test_special_sort(self): + s = self.sfs(['some_sort+']) + self.assertIsInstance(s.sorts[0], TestSort) + def suite(): return unittest.TestLoader().loadTestsFromName(__name__)