From 99eb4abee336611fae1edf23c04056a83cbb49bd Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Fri, 7 Oct 2022 14:46:55 -0400 Subject: [PATCH] db: Give Decades knowledge about their Tracks & Properties I expand on the decades_view to include additional playlist properties, and configure the default sort order to sort by year first. I then set up the decade_tracks_view to make it easy to select tracks that belong to a specific decade. Signed-off-by: Anna Schumaker --- emmental/db/decades.py | 19 +++++++++++++++ emmental/db/emmental.sql | 13 ++++++++-- tests/db/test_decades.py | 51 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/emmental/db/decades.py b/emmental/db/decades.py index 5cb51de..9d1bdb3 100644 --- a/emmental/db/decades.py +++ b/emmental/db/decades.py @@ -5,6 +5,7 @@ from gi.repository import GObject from gi.repository import Gtk from .years import Year from . import playlist +from . import tracks class Decade(playlist.Playlist): @@ -34,6 +35,15 @@ class Decade(playlist.Playlist): class Table(playlist.Table): """Our Decade Table.""" + def __init__(self, sql: GObject.TYPE_PYOBJECT, **kwargs): + """Initialize the Decade table.""" + super().__init__(sql=sql, autodelete=True, + system_tracks=False, **kwargs) + + def do_add_track(self, decade: Decade, track: tracks.Track) -> bool: + """Verify adding a Track to the Decade playlist.""" + return (track.year // 10 * 10) == decade.decade + def do_construct(self, **kwargs) -> Decade: """Construct a new Decade playlist.""" return Decade(**kwargs) @@ -42,6 +52,10 @@ class Table(playlist.Table): """Get the sort key for the requested decade.""" return decade.decade + def do_remove_track(self, decade: Decade, track: tracks.Track) -> bool: + """Verify removing a Track from the Decade playlist.""" + return True + def do_sql_delete(self, decade: Decade) -> sqlite3.Cursor: """Delete a decade.""" for year in decade.get_years(): @@ -71,6 +85,11 @@ class Table(playlist.Table): return self.sql("SELECT decade FROM decades WHERE decade=?", year // 10 * 10) + def do_sql_select_trackids(self, decade: Decade) -> sqlite3.Cursor: + """Load a Decade's Tracks from the database.""" + return self.sql("""SELECT trackid FROM decade_tracks_view + WHERE decade=?""", decade.decade) + def get_years(self, decade: Decade) -> list[Year]: """Get the list of years for this decade.""" rows = self.sql("SELECT year FROM years WHERE (year / 10 * 10)=?", diff --git a/emmental/db/emmental.sql b/emmental/db/emmental.sql index e345c1c..50d5873 100644 --- a/emmental/db/emmental.sql +++ b/emmental/db/emmental.sql @@ -317,13 +317,15 @@ CREATE TABLE decades ( ); CREATE VIEW decades_view AS - SELECT decade, propertyid, FORMAT("The %ds", decade) as name, active + SELECT decade, propertyid, FORMAT("The %ds", decade) as name, + active, loop, shuffle, sort_order, current_trackid FROM decades JOIN playlist_properties USING (propertyid); CREATE TRIGGER decades_insert_trigger AFTER INSERT ON decades BEGIN - INSERT INTO playlist_properties (active) VALUES (False); + INSERT INTO playlist_properties (active, sort_order) + VALUES (False, "release, albumartist, album, mediumno, number"); UPDATE decades SET propertyid = last_insert_rowid() WHERE decade = NEW.decade; END; @@ -555,6 +557,13 @@ CREATE VIEW genre_tracks_view AS JOIN libraries USING (libraryid) WHERE libraries.deleting = False; +CREATE VIEW decade_tracks_view AS + SELECT tracks.trackid, decades.decade + FROM tracks + JOIN decades ON (tracks.year / 10 * 10) = decades.decade + JOIN libraries USING (libraryid) + WHERE libraries.deleting = False; + /**************************************************** * * diff --git a/tests/db/test_decades.py b/tests/db/test_decades.py index e551775..56fdffe 100644 --- a/tests/db/test_decades.py +++ b/tests/db/test_decades.py @@ -1,5 +1,6 @@ # Copyright 2022 (c) Anna Schumaker """Tests our decade Gio.ListModel.""" +import pathlib import unittest.mock import emmental.db import tests.util @@ -57,10 +58,27 @@ class TestDecadeTable(tests.util.TestCase): self.table = self.sql.decades self.sql.years.load() + self.album = self.sql.albums.create("Test Album", "Test Artist", "123") + self.medium = self.sql.media.create(self.album, "", number=1) + self.library = self.sql.libraries.create(pathlib.Path("/a/b")) + self.year = self.sql.years.create(2023) + self.track = self.sql.tracks.create(self.library, + pathlib.Path("/a/b/c/1.ogg"), + self.medium, self.year) + def test_init(self): """Test that the decade table is configured correctly.""" self.assertIsInstance(self.table, emmental.db.playlist.Table) self.assertEqual(len(self.table), 0) + self.assertTrue(self.table.autodelete) + self.assertFalse(self.table.system_tracks) + + def test_add_track(self): + """Test adding a Track to a Decade playlist.""" + decade = self.table.create(2020) + self.assertTrue(self.table.add_track(decade, self.track)) + decade2 = self.table.create(1990) + self.assertFalse(self.table.add_track(decade2, self.track)) def test_construct(self): """Test constructing a decade playlist.""" @@ -72,6 +90,7 @@ class TestDecadeTable(tests.util.TestCase): self.assertEqual(decade.decade, 1980) self.assertEqual(decade.name, "The 1980s") self.assertFalse(decade.active) + self.assertFalse(decade.tracks_movable) def test_create(self): """Test creating a decade playlist.""" @@ -79,6 +98,8 @@ class TestDecadeTable(tests.util.TestCase): self.assertIsInstance(decade, emmental.db.decades.Decade) self.assertEqual(decade.decade, 1980) self.assertEqual(decade.name, "The 1980s") + self.assertEqual(decade.sort_order, + "release, albumartist, album, mediumno, number") cur = self.sql("SELECT COUNT(decade) FROM decades") self.assertEqual(cur.fetchone()["COUNT(decade)"], 1) @@ -91,6 +112,7 @@ class TestDecadeTable(tests.util.TestCase): def test_delete(self): """Test deleting a decade playlist.""" + self.year.delete() decade = self.table.create(1980) self.sql.years.create(1988) self.assertTrue(decade.delete()) @@ -131,6 +153,15 @@ class TestDecadeTable(tests.util.TestCase): decade = self.table.create(1980) self.assertEqual(self.table.get_sort_key(decade), 1980) + def test_get_trackids(self): + """Test loading decade tracks from the database.""" + decade = self.table.create(2020) + track2 = self.sql.tracks.create(self.library, + pathlib.Path("/a/b/c/2.ogg"), + self.medium, self.year) + self.assertSetEqual(self.table.get_trackids(decade), + {self.track.trackid, track2.trackid}) + def test_load(self): """Load the decade table from the database.""" self.table.create(1980) @@ -154,14 +185,28 @@ class TestDecadeTable(tests.util.TestCase): self.assertEqual(self.table.lookup(1988), decade) self.assertIsNone(self.table.lookup(1990)) + def test_remove_track(self): + """Test removing a Track from the Decade.""" + decade = self.table.create(2023) + self.assertTrue(self.table.remove_track(decade, unittest.mock.Mock())) + def test_update(self): """Test updating decade attributes.""" decade = self.table.create(1980) decade.active = True + decade.loop = "Track" + decade.shuffle = True + decade.sort_order = "trackid" - row = self.sql("""SELECT active FROM playlist_properties - WHERE propertyid=?""", decade.propertyid).fetchone() - self.assertEqual(row["active"], True) + row = self.sql("""SELECT active, loop, shuffle, + sort_order, current_trackid + FROM decades_view WHERE decade=?""", + decade.decade).fetchone() + self.assertTrue(row["active"]) + self.assertEqual(row["loop"], "Track") + self.assertTrue(row["shuffle"]) + self.assertEqual(row["sort_order"], "trackid") + self.assertIsNone(row["current_trackid"]) def test_get_years(self): """Test getting the list of years for a decade."""