diff --git a/emmental/tracklist/sorter.py b/emmental/tracklist/sorter.py new file mode 100644 index 0000000..3b0b282 --- /dev/null +++ b/emmental/tracklist/sorter.py @@ -0,0 +1,55 @@ +# Copyright 2022 (c) Anna Schumaker. +"""A Sorter designed for sorting Playlists.""" +from gi.repository import GObject +from gi.repository import Gio + +SORT_FIELDS = {"Album": ["album"], "Artist": ["artist"], + "Album Artist": ["albumartist"], + "Filepath": ["filepath"], "Length": ["length"], + "Last Played Date": ["lastplayed"], + "Last Started Date": ["laststarted"], + "Play Count": ["playcount"], "Release Date": ["release"], + "Title": ["title"], "Track Number": ["mediumno", "number"]} + + +class SortField(GObject.GObject): + """Used to represent a single field in the sort order.""" + + name = GObject.Property(type=str) + model = GObject.Property(type=Gio.ListModel) + + enabled = GObject.Property(type=bool, default=False) + reversed = GObject.Property(type=bool, default=False) + + def __init__(self, model: Gio.ListStore, name: str): + """Initialize a Sort Field.""" + if name not in SORT_FIELDS: + raise KeyError + super().__init__(model=model, name=name) + + def __str__(self) -> str: + """Convert this Field into a string.""" + columns = SORT_FIELDS[self.name] + if self.reversed: + columns = [f"{col} DESC" for col in columns] + return ", ".join(columns) + + def disable(self) -> bool: + """Disable this Sort Field.""" + return self.model.disable(self) + + def enable(self) -> bool: + """Enable this Sort Field.""" + return self.model.enable(self) + + def move_down(self) -> bool: + """Move this Sort Field down in the list.""" + return self.model.move_down(self) + + def move_up(self) -> bool: + """Move this Sort Field up in the list.""" + return self.model.move_up(self) + + def reverse(self) -> None: + """Reverse the direction of this Sort Field.""" + self.model.reverse(self) diff --git a/tests/tracklist/test_sorter.py b/tests/tracklist/test_sorter.py new file mode 100644 index 0000000..e75b546 --- /dev/null +++ b/tests/tracklist/test_sorter.py @@ -0,0 +1,86 @@ +# Copyright 2022 (c) Anna Schumaker. +"""Tests our Playlist Sorter.""" +import emmental.tracklist.sorter +import unittest +from gi.repository import GObject +from gi.repository import Gio + + +class TestSortConstants(unittest.TestCase): + """Test case for Sort Order constants.""" + + def test_sort_fields(self): + """Test the sort fields definition dictionary.""" + self.assertDictEqual(emmental.tracklist.sorter.SORT_FIELDS, + {"Album": ["album"], "Artist": ["artist"], + "Album Artist": ["albumartist"], + "Filepath": ["filepath"], "Length": ["length"], + "Last Played Date": ["lastplayed"], + "Last Started Date": ["laststarted"], + "Play Count": ["playcount"], + "Release Date": ["release"], + "Title": ["title"], + "Track Number": ["mediumno", "number"]}) + + +class TestSortField(unittest.TestCase): + """Test case for our SortField object.""" + + def setUp(self): + """Set up common variables.""" + self.model = Gio.ListStore() + self.field = emmental.tracklist.sorter.SortField(self.model, "Album") + + def test_init(self): + """Test that the Sort Field is initialized correctly.""" + self.assertIsInstance(self.field, GObject.GObject) + self.assertEqual(self.field.name, "Album") + self.assertEqual(self.field.model, self.model) + self.assertFalse(self.field.enabled) + self.assertFalse(self.field.reversed) + + with self.assertRaises(KeyError): + emmental.tracklist.sorter.SortField(self.model, "Invalid Field") + + def test_str(self): + """Test converting a Sort Field to a string.""" + for (field, cols) in emmental.tracklist.sorter.SORT_FIELDS.items(): + expected = ", ".join(cols) + reversed = ", ".join([f"{c} DESC" for c in cols]) + with self.subTest(field=field, expected=(expected, reversed)): + self.field.name = field + self.field.reversed = False + self.assertEqual(str(self.field), expected) + + self.field.reversed = True + self.assertEqual(str(self.field), reversed) + + def test_disable(self): + """Test disabling a Sort Field.""" + self.model.disable = unittest.mock.Mock() + self.field.disable() + self.model.disable.assert_called_with(self.field) + + def test_enable(self): + """Test enabling a Sort Field.""" + self.model.enable = unittest.mock.Mock() + self.field.enable() + self.model.enable.assert_called_with(self.field) + + def test_move_down(self): + """Test moving a Sort Field down.""" + self.model.move_down = unittest.mock.Mock() + self.field.move_down() + self.model.move_down.assert_called_with(self.field) + + def test_move_up(self): + """Test moving a Sort Field up.""" + self.model.move_up = unittest.mock.Mock() + self.field.move_up() + self.model.move_up.assert_called_with(self.field) + + def test_reverse(self): + """Test reversing a Sort Field.""" + self.model.reverse = unittest.mock.Mock() + self.field.reverse() + self.model.reverse.assert_called_with(self.field)