From b4d8a7cfaa8fa2c54b2c004ad97bc025c8c84c1c Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Mon, 10 Oct 2022 13:30:16 -0400 Subject: [PATCH] db: Give Artists knowledge about their Tracks & Properties I expand on the artists_view to include additional playlist properties, and configure the default sort_order to sort albums in an intuitive way. I then configure the Artists table to us the system_tracks table to manage each artist's associated tracks and set up the artist_tracks_view to make it easy for Tracks to find their Artists. Signed-off-by: Anna Schumaker --- emmental/db/artists.py | 2 +- emmental/db/emmental.sql | 14 ++++++++++++-- emmental/db/tracks.py | 10 ++++++++++ tests/db/test_artists.py | 18 ++++++++++++++++-- tests/db/test_tracks.py | 18 ++++++++++++++++++ 5 files changed, 57 insertions(+), 5 deletions(-) diff --git a/emmental/db/artists.py b/emmental/db/artists.py index 664cc5a..c3728a2 100644 --- a/emmental/db/artists.py +++ b/emmental/db/artists.py @@ -78,7 +78,7 @@ class Table(playlist.Table): def __init__(self, sql: GObject.TYPE_PYOBJECT, show_all: bool = False, **kwargs): """Initialize an Artist model.""" - super().__init__(sql=sql, show_all=show_all, + super().__init__(sql=sql, show_all=show_all, autodelete=True, filter=Filter(show_all=show_all), **kwargs) self.bind_property("show-all", self.get_filter(), "show-all") diff --git a/emmental/db/emmental.sql b/emmental/db/emmental.sql index 66e8e70..ed139bc 100644 --- a/emmental/db/emmental.sql +++ b/emmental/db/emmental.sql @@ -140,14 +140,16 @@ CREATE TABLE artists ( ); CREATE VIEW artists_view AS - SELECT artistid, propertyid, name, mbid, active + SELECT artistid, propertyid, name, mbid, + active, loop, shuffle, sort_order, current_trackid FROM artists JOIN playlist_properties USING (propertyid); CREATE TRIGGER artists_insert_trigger AFTER INSERT ON artists BEGIN - INSERT INTO playlist_properties (active) VALUES (False); + INSERT INTO playlist_properties (active, sort_order) + VALUES (False, "release, album, mediumno, number"); UPDATE artists SET propertyid = last_insert_rowid(), mbid = LOWER(NEW.mbid) WHERE artistid = NEW.artistid; @@ -516,6 +518,14 @@ CREATE VIEW unplayed_tracks_view AS JOIN libraries USING (libraryid) WHERE tracks.playcount == 0 AND libraries.deleting = FALSE; +CREATE VIEW artist_tracks_view AS + SELECT tracks.trackid, artists.artistid + FROM tracks + JOIN system_tracks USING (trackid) + JOIN artists USING (propertyid) + JOIN libraries USING (libraryid) + WHERE libraries.deleting = False; + /**************************************************** * * diff --git a/emmental/db/tracks.py b/emmental/db/tracks.py index 6e52542..2c28bfd 100644 --- a/emmental/db/tracks.py +++ b/emmental/db/tracks.py @@ -46,6 +46,10 @@ class Track(table.Row): case _: return super().do_update(column) return True + def get_artists(self) -> list[table.Row]: + """Get a list of Artists for this Track.""" + return self.table.get_artists(self) + def get_library(self) -> table.Row | None: """Get the Library associated with this Track.""" return self.table.sql.libraries.rows.get(self.libraryid) @@ -192,6 +196,12 @@ class Table(table.Table): return self.sql(f"UPDATE tracks SET {column}=? WHERE trackid=?", newval, track.trackid) + def get_artists(self, track: Track) -> list[table.Row]: + """Get the set of Artists for a specific Track.""" + rows = self.sql("""SELECT artistid FROM artist_tracks_view + WHERE trackid=?""", track.trackid).fetchall() + return [self.sql.artists.rows.get(row["artistid"]) for row in rows] + def map_sort_order(self, ordering: str) -> dict[int, int]: """Get a lookup table for Track sort keys.""" ordering = ordering if len(ordering) > 0 else "trackid" diff --git a/tests/db/test_artists.py b/tests/db/test_artists.py index f949b83..a28a6e8 100644 --- a/tests/db/test_artists.py +++ b/tests/db/test_artists.py @@ -104,6 +104,8 @@ class TestArtistTable(tests.util.TestCase): self.assertIsInstance(self.table.get_filter(), emmental.db.artists.Filter) self.assertEqual(len(self.table), 0) + self.assertTrue(self.table.autodelete) + self.assertTrue(self.table.system_tracks) def test_construct(self): """Test constructing a new artist playlist.""" @@ -116,6 +118,7 @@ class TestArtistTable(tests.util.TestCase): self.assertEqual(artist.name, "Test Artist") self.assertEqual(artist.mbid, "ab-cd-ef") self.assertFalse(artist.active) + self.assertFalse(artist.tracks_movable) def test_create(self): """Test creating an artist playlist.""" @@ -125,6 +128,8 @@ class TestArtistTable(tests.util.TestCase): self.assertIsInstance(artist1, emmental.db.artists.Artist) self.assertEqual(artist1.name, "Test Artist") self.assertEqual(artist1.mbid, "") + self.assertEqual(artist1.sort_order, + "release, album, mediumno, number") self.assertEqual(self.table[0], artist1) cur = self.sql("SELECT COUNT(name) FROM artists") @@ -243,10 +248,19 @@ class TestArtistTable(tests.util.TestCase): """Test updating artist attributes.""" artist = self.table.create("Test Artist") artist.active = True + artist.loop = "Track" + artist.sort_order = "trackid" + artist.shuffle = True - row = self.sql("""SELECT active FROM playlist_properties - WHERE propertyid=?""", artist.propertyid).fetchone() + row = self.sql("""SELECT active, loop, sort_order, + shuffle, current_trackid + FROM artists_view WHERE artistid=?""", + artist.artistid).fetchone() self.assertTrue(row["active"]) + self.assertEqual(row["loop"], "Track") + self.assertEqual(row["sort_order"], "trackid") + self.assertTrue(row["shuffle"]) + self.assertIsNone(row["current_trackid"]) def test_add_remove_album(self): """Test adding an album to an artist.""" diff --git a/tests/db/test_tracks.py b/tests/db/test_tracks.py index 9ba0ea3..af218c2 100644 --- a/tests/db/test_tracks.py +++ b/tests/db/test_tracks.py @@ -64,6 +64,12 @@ class TestTrackObject(tests.util.TestCase): self.assertIsNone(self.track.lastplayed) self.assertIsNone(self.track.restarted) + def test_get_artists(self): + """Test getting the Artist list for a Track.""" + self.table.get_artists = unittest.mock.Mock(return_value=[1, 2, 3]) + self.assertListEqual(self.track.get_artists(), [1, 2, 3]) + self.table.get_artists.assert_called_with(self.track) + def test_get_library(self): """Test getting the Library associated with a Track.""" self.assertEqual(self.track.get_library(), self.library) @@ -442,6 +448,18 @@ class TestTrackTable(tests.util.TestCase): track.trackid).fetchone() self.assertFalse(row["active"]) + def test_get_artists(self): + """Test finding the artists for a track.""" + track = self.tracks.create(self.library, pathlib.Path("a/b/1.ogg"), + self.medium, self.year) + artist1 = self.sql.artists.create("Artist 1") + artist2 = self.sql.artists.create("Artist 2") + + artist1.add_track(track) + artist2.add_track(track) + self.assertListEqual(self.tracks.get_artists(track), + [artist1, artist2]) + def test_mark_path_active(self): """Test marking a path as active.""" self.tracks.create(self.library, pathlib.Path("/a/b/1.ogg"),