db: Give Albums knowledge about their Media

We create a filter on the Media table for each Album object, but
only match Media that have a name set. I also adjust filtering to
display Albums that have a matching Medium.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2022-08-26 14:21:46 -04:00
parent 51a13a8a04
commit 87606f8fac
3 changed files with 82 additions and 11 deletions

View File

@ -3,6 +3,8 @@
import pathlib
import sqlite3
from gi.repository import GObject
from gi.repository import Gtk
from .media import Medium
from .. import format
from . import playlist
@ -16,10 +18,23 @@ class Album(playlist.Playlist):
mbid = GObject.Property(type=str)
cover = GObject.Property(type=GObject.TYPE_PYOBJECT)
def __init__(self, **kwargs):
"""Initialize an Album object."""
super().__init__(**kwargs)
self.add_children(self.table.sql.media,
Gtk.CustomFilter.new(self.__match_medium))
def __match_medium(self, medium: Medium) -> bool:
return medium.albumid == self.albumid and len(medium.name) > 0
def get_artists(self) -> list[playlist.Playlist]:
"""Get a list of artists for this album."""
return self.table.get_artists(self)
def get_media(self) -> list[Medium]:
"""Get a list of media for this album."""
return self.table.get_media(self)
@property
def primary_key(self) -> int:
"""Get the Album primary key."""
@ -51,12 +66,15 @@ class Table(playlist.Table):
"""Delete an album."""
for artist in album.get_artists():
artist.remove_album(album)
for medium in album.get_media():
medium.delete()
return self.sql("DELETE FROM albums WHERE albumid=?", album.albumid)
def do_sql_glob(self, glob: str) -> sqlite3.Cursor:
"""Search for albums matching the search text."""
return self.sql("""SELECT albumid FROM albums
WHERE CASEFOLD(name) GLOB ?""", glob)
return self.sql("""SELECT albumid FROM album_artist_view
WHERE CASEFOLD(album) GLOB :glob
OR CASEFOLD(medium) GLOB :glob""", glob=glob)
def do_sql_insert(self, name: str, artist: str,
release: str, *, mbid: str = "",
@ -99,3 +117,9 @@ class Table(playlist.Table):
WHERE albumid=?""", album.albumid).fetchall()
artists = [self.sql.artists.rows.get(row["artistid"]) for row in rows]
return list(filter(None, artists))
def get_media(self, album: Album) -> list[Medium]:
"""Get the list of media for this album."""
rows = self.sql("SELECT mediumid FROM media WHERE albumid=?",
album.albumid)
return [self.sql.media.rows.get(row["mediumid"]) for row in rows]

View File

@ -184,11 +184,11 @@ CREATE TRIGGER media_delete_trigger AFTER DELETE ON media
END;
/*******************************************
* *
* Artist <--> Album Linking *
* *
*******************************************/
/*******************************************************
* *
* Artist <--> Album <--> Medium Linking *
* *
*******************************************************/
CREATE TABLE album_artist_link (
artistid INTEGER NOT NULL REFERENCES artists (artistid)
@ -202,10 +202,12 @@ CREATE TABLE album_artist_link (
CREATE VIEW album_artist_view AS
SELECT artistid, artists.name as artist,
albumid, COALESCE(albums.name, "") as album
albumid, COALESCE(albums.name, "") as album,
media.mediumid, COALESCE(media.name, "") as medium
FROM artists
LEFT JOIN album_artist_link USING (artistid)
LEFT JOIN albums USING (albumid);
LEFT JOIN albums USING (albumid)
LEFT JOIN media USING (albumid);
/******************************************

View File

@ -4,6 +4,7 @@ import pathlib
import unittest.mock
import emmental.db
import tests.util
from gi.repository import Gtk
class TestAlbumObject(tests.util.TestCase):
@ -48,6 +49,30 @@ class TestAlbumObject(tests.util.TestCase):
mock.assert_called_with(self.album)
self.assertEqual(self.album.parent, 1)
def test_get_media(self):
"""Test getting the list of album media."""
with unittest.mock.patch.object(self.table, "get_media",
return_value=[1, 2, 3]) as mock:
self.assertListEqual(self.album.get_media(), [1, 2, 3])
mock.assert_called_with(self.album)
def test_media_model(self):
"""Test getting a Gio.ListModel representing this Album's media."""
self.assertIsInstance(self.album.children, Gtk.FilterListModel)
self.assertIsInstance(self.album.children.get_filter(),
Gtk.CustomFilter)
self.assertEqual(self.album.children.get_model(), self.sql.media)
album = self.table.create("Test Album", "Album Artist", "2023-03")
medium = self.sql.media.create(album, "Test Medium", number=1)
self.assertTrue(album.children.get_filter().match(medium))
medium.albumid = album.albumid + 1
self.assertFalse(album.children.get_filter().match(medium))
medium = self.sql.media.create(album, "", number=2)
self.assertFalse(album.children.get_filter().match(medium))
class TestAlbumTable(tests.util.TestCase):
"""Tests our album table."""
@ -117,11 +142,13 @@ class TestAlbumTable(tests.util.TestCase):
"""Test deleting an album playlist."""
artist = self.sql.artists.create("Test Artist")
album = self.table.create("Test Album", "Album Artist", "2023-03")
medium = self.sql.media.create(album, "Test Medium", number=1)
artist.add_album(album)
self.assertTrue(album.delete())
self.assertIsNone(self.table.index(album))
self.assertFalse(artist.has_album(album))
self.assertIsNone(self.sql.media.index(medium))
cur = self.sql("SELECT COUNT(name) FROM albums")
self.assertEqual(cur.fetchone()["COUNT(name)"], 0)
@ -139,14 +166,24 @@ class TestAlbumTable(tests.util.TestCase):
def test_filter(self):
"""Test filtering an album playlist."""
self.table.create("Album 1", "Album Artist", "2023-03")
self.table.create("Album 2", "Album Artist", "2023-03")
artist = self.sql.artists.create("Test Artist")
album1 = self.table.create("Album 1", "Album Artist", "2023-03")
album2 = self.table.create("Album 2", "Album Artist", "2023-03")
artist.add_album(album1)
artist.add_album(album2)
self.sql.media.create(album2, "Medium 3", number=3)
self.sql.media.create(album2, "", number=4)
self.table.filter("*1", now=True)
self.assertSetEqual(self.table.get_filter().keys, {1})
self.table.filter("album*", now=True)
self.assertSetEqual(self.table.get_filter().keys, {1, 2})
self.table.filter("*3", now=True)
self.assertSetEqual(self.table.get_filter().keys, {2})
def test_get_sort_key(self):
"""Test the get_sort_key() function."""
album1 = self.table.create("Album 1", "Album Artist", "2023-02")
@ -233,3 +270,11 @@ class TestAlbumTable(tests.util.TestCase):
del self.sql.artists.rows[artist1.artistid]
self.assertListEqual(self.table.get_artists(album), [artist2])
def test_get_media(self):
"""Test getting the list of media for an album."""
album = self.table.create("Test Album", "Album Artist", "2023-03")
medium1 = self.sql.media.create(album, "", number=1)
medium2 = self.sql.media.create(album, "", number=2)
self.assertListEqual(self.table.get_media(album), [medium1, medium2])