db: Give Albums knowledge about their Tracks & Properties

I expand on the albums_view to include additional playlist properties,
and configure the default sort order to sort by track numbers in an
intuitive way.

I then set up the album_tracks_view to make it easy to select tracks
that belong to a specific album.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2022-10-05 14:27:06 -04:00
parent b4d8a7cfaa
commit 8e55de26d1
3 changed files with 75 additions and 3 deletions

View File

@ -7,6 +7,7 @@ from gi.repository import Gtk
from .media import Medium
from .. import format
from . import playlist
from . import tracks
class Album(playlist.Playlist):
@ -50,6 +51,15 @@ class Album(playlist.Playlist):
class Table(playlist.Table):
"""Our Album Table."""
def __init__(self, sql: GObject.TYPE_PYOBJECT, **kwargs):
"""Initialize the Album Table."""
super().__init__(sql=sql, autodelete=True,
system_tracks=False, **kwargs)
def do_add_track(self, album: Album, track: tracks.Track) -> bool:
"""Verify adding a Track to the Album playlist."""
return track.get_medium().get_album() == album
def do_construct(self, **kwargs) -> Album:
"""Construct a new album."""
return Album(**kwargs)
@ -62,6 +72,10 @@ class Table(playlist.Table):
format.sort_key(album.artist),
album.release)
def do_remove_track(self, album: Album, track: tracks.Track) -> bool:
"""Verify removing a Track from the Album playlist."""
return True
def do_sql_delete(self, album: Album) -> sqlite3.Cursor:
"""Delete an album."""
for artist in album.get_artists():
@ -106,6 +120,11 @@ class Table(playlist.Table):
return self.sql(f"""SELECT albumid FROM albums
WHERE {" AND ".join(where)}""", *args)
def do_sql_select_trackids(self, album: Album) -> sqlite3.Cursor:
"""Load an Album's Tracks from the database."""
return self.sql("""SELECT trackid FROM album_tracks_view
WHERE albumid=?""", album.albumid)
def do_sql_update(self, album: Album, column: str, newval) -> bool:
"""Rename an album."""
return self.sql(f"UPDATE albums SET {column}=? WHERE albumid=?",

View File

@ -181,14 +181,16 @@ CREATE TABLE albums (
);
CREATE VIEW albums_view AS
SELECT albumid, propertyid, name, mbid, artist, release, cover, active
SELECT albumid, propertyid, name, mbid, artist, release, cover,
active, loop, shuffle, sort_order, current_trackid
FROM albums
JOIN playlist_properties USING (propertyid);
CREATE TRIGGER albums_insert_trigger AFTER INSERT ON albums
BEGIN
INSERT INTO playlist_properties (active) VALUES (False);
INSERT INTO playlist_properties (active, sort_order)
VALUES (False, "mediumno, number");
UPDATE albums SET propertyid = last_insert_rowid(),
mbid = LOWER(NEW.mbid)
WHERE albumid = NEW.albumid;
@ -526,6 +528,14 @@ CREATE VIEW artist_tracks_view AS
JOIN libraries USING (libraryid)
WHERE libraries.deleting = False;
CREATE VIEW album_tracks_view AS
SELECT tracks.trackid, albums.albumid
FROM tracks
JOIN media USING (mediumid)
JOIN albums USING (albumid)
JOIN libraries USING (libraryid)
WHERE libraries.deleting = False;
/****************************************************
* *

View File

@ -82,10 +82,26 @@ class TestAlbumTable(tests.util.TestCase):
tests.util.TestCase.setUp(self)
self.table = self.sql.albums
self.library = self.sql.libraries.create(pathlib.Path("/a/b"))
self.year = self.sql.years.create(2023)
def test_init(self):
"""Test that the album model 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 an Album playlist."""
album = self.table.create("Test Album", "Artist", "2023")
medium = self.sql.media.create(album, "", number=1)
track = self.sql.tracks.create(self.library, pathlib.Path("1.ogg"),
medium, self.year)
self.assertTrue(self.table.add_track(album, track))
album2 = self.table.create("Album 2", "Artist 2", "2022")
self.assertFalse(self.table.add_track(album2, track))
def test_construct(self):
"""Test constructing an album playlist."""
@ -103,6 +119,7 @@ class TestAlbumTable(tests.util.TestCase):
self.assertEqual(album.mbid, "ab-cd-ef")
self.assertEqual(album.cover, tests.util.COVER_JPG)
self.assertFalse(album.active)
self.assertFalse(album.tracks_movable)
def test_create(self):
"""Test creating an album playlist."""
@ -113,6 +130,7 @@ class TestAlbumTable(tests.util.TestCase):
self.assertEqual(album1.release, "2023-03")
self.assertEqual(album1.mbid, "")
self.assertIsNone(album1.cover)
self.assertEqual(album1.sort_order, "mediumno, number")
self.assertEqual(self.table[0], album1)
cur = self.sql("SELECT COUNT(name) FROM albums")
@ -197,6 +215,17 @@ class TestAlbumTable(tests.util.TestCase):
(("album", "2"), False, "ab-cd-ef",
("album", "artist"), "2023-02"))
def test_get_trackids(self):
"""Test loading album tracks from the database."""
album = self.table.create("Test Album", "Test Artist", "2023")
medium = self.sql.media.create(album, "", number=1)
track1 = self.sql.tracks.create(self.library, pathlib.Path("1.ogg"),
medium, self.year)
track2 = self.sql.tracks.create(self.library, pathlib.Path("2.ogg"),
medium, self.year)
self.assertSetEqual(self.table.get_trackids(album),
{track1.trackid, track2.trackid})
def test_load(self):
"""Test loading the album table."""
self.table.create("Album 1", "Album Artist", "2023-03")
@ -242,16 +271,30 @@ class TestAlbumTable(tests.util.TestCase):
self.assertEqual(self.table.lookup(mbid="AB-CD-EF"), album2)
self.assertIsNone(self.table.lookup(mbid="gh-ij-kl"))
def test_remove_track(self):
"""Test removing a Track from the Album."""
album = self.table.create("Test Album", "Artist", "2023")
self.assertTrue(self.table.remove_track(album, unittest.mock.Mock()))
def test_update(self):
"""Test updating album attributes."""
album = self.table.create("Test Album", "Album Artist", "2023-03")
album.cover = tests.util.COVER_JPG
album.active = True
album.loop = "Track"
album.shuffle = True
album.sort_order = "trackid"
row = self.sql("SELECT cover, active FROM albums_view WHERE albumid=?",
row = self.sql("""SELECT cover, active, loop, shuffle,
sort_order, current_trackid
FROM albums_view WHERE albumid=?""",
album.albumid).fetchone()
self.assertEqual(row["cover"], tests.util.COVER_JPG)
self.assertEqual(row["active"], True)
self.assertEqual(row["loop"], "Track")
self.assertTrue(row["shuffle"])
self.assertEqual(row["sort_order"], "trackid")
self.assertIsNone(row["current_trackid"])
album.cover = None
row = self.sql("SELECT cover FROM albums WHERE albumid=?",