db: Add a Year Table

This table allows us to work with Year playlists that are represented
only by the year.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2022-08-31 15:14:47 -04:00
parent 0cf5f80eb4
commit c0edbd9bff
5 changed files with 225 additions and 2 deletions

View File

@ -13,6 +13,7 @@ from . import media
from . import playlists
from . import settings
from . import table
from . import years
SQL_SCRIPT = pathlib.Path(__file__).parent / "emmental.sql"
@ -39,6 +40,7 @@ class Connection(connection.Connection):
self.media = media.Table(self, queue=self.artists.queue)
self.genres = genres.Table(self)
self.decades = decades.Table(self)
self.years = years.Table(self, queue=self.decades.queue)
def close(self) -> None:
"""Close the database connection."""
@ -62,7 +64,7 @@ class Connection(connection.Connection):
def playlist_tables(self) -> Generator[playlist.Table, None, None]:
"""Iterate over each playlist table."""
for tbl in [self.playlists, self.artists, self.albums, self.media,
self.genres, self.decades]:
self.genres, self.decades, self.years]:
yield tbl
def set_active_playlist(self, plist: playlist.Playlist) -> None:

View File

@ -274,6 +274,37 @@ CREATE TRIGGER decades_delete_trigger AFTER DELETE ON decades
END;
/***********************
* *
* Years *
* *
***********************/
CREATE TABLE years (
year INTEGER PRIMARY KEY,
propertyid INTEGER REFERENCES playlist_properties (propertyid)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE VIEW years_view AS
SELECT year, propertyid, FORMAT("%s", year) as name, active
FROM years
JOIN playlist_properties USING (propertyid);
CREATE TRIGGER years_insert_trigger AFTER INSERT ON years
BEGIN
INSERT INTO playlist_properties (active) VALUES (False);
UPDATE years SET propertyid = last_insert_rowid()
WHERE year = NEW.year;
END;
CREATE TRIGGER years_delete_trigger AFTER DELETE ON years
BEGIN
DELETE FROM playlist_properties WHERE propertyid = OLD.propertyid;
END;
/******************************************
* *
* Create Default Playlists *

54
emmental/db/years.py Normal file
View File

@ -0,0 +1,54 @@
# Copyright 2022 (c) Anna Schumaker.
"""A custom Gio.ListModel for managing individual years."""
import sqlite3
from gi.repository import GObject
from . import playlist
class Year(playlist.Playlist):
"""Our custom Year object."""
year = GObject.Property(type=int)
@property
def primary_key(self) -> int:
"""Get this year's primary key."""
return self.year
@GObject.Property(type=playlist.Playlist)
def parent(self) -> playlist.Playlist | None:
"""Get this Year's parent playlist."""
return self.table.sql.decades.lookup(self.year)
class Table(playlist.Table):
"""Our Year Table."""
def do_construct(self, **kwargs) -> Year:
"""Construct a new Year playlist."""
return Year(**kwargs)
def do_get_sort_key(self, year: Year) -> tuple:
"""Get the sort key for a specific year."""
return year.year
def do_sql_delete(self, year: Year) -> sqlite3.Cursor:
"""Delete a year."""
return self.sql("DELETE FROM years WHERE year=?", year.year)
def do_sql_glob(self, glob: str) -> sqlite3.Cursor:
"""Search for years matching the search text."""
return self.sql("SELECT year FROM years_view WHERE name GLOB ?", glob)
def do_sql_insert(self, year: int) -> sqlite3.Cursor | None:
"""Create a new Year playlist."""
if self.sql("INSERT INTO years (year) VALUES (?)", year):
return self.sql("SELECT * FROM years_view WHERE year=?", year)
def do_sql_select_all(self) -> sqlite3.Cursor:
"""Load Years from the database."""
return self.sql("SELECT * FROM years_view")
def do_sql_select_one(self, year: int) -> sqlite3.Cursor:
"""Look up a year."""
return self.sql("SELECT year FROM years WHERE year=?", year)

View File

@ -46,14 +46,17 @@ class TestConnection(tests.util.TestCase):
self.assertIsInstance(self.sql.media, emmental.db.media.Table)
self.assertIsInstance(self.sql.genres, emmental.db.genres.Table)
self.assertIsInstance(self.sql.decades, emmental.db.decades.Table)
self.assertIsInstance(self.sql.years, emmental.db.years.Table)
self.assertEqual(self.sql.albums.queue, self.sql.artists.queue)
self.assertEqual(self.sql.media.queue, self.sql.artists.queue)
self.assertEqual(self.sql.years.queue, self.sql.decades.queue)
self.assertListEqual([tbl for tbl in self.sql.playlist_tables()],
[self.sql.playlists, self.sql.artists,
self.sql.albums, self.sql.media,
self.sql.genres, self.sql.decades])
self.sql.genres, self.sql.decades,
self.sql.years])
def test_load(self):
"""Check that calling load() loads the tables."""

133
tests/db/test_years.py Normal file
View File

@ -0,0 +1,133 @@
# Copyright 2022 (c) Anna Schumaker.
"""Tests our year Gio.ListModel."""
import emmental.db
import tests.util
class TestYearObject(tests.util.TestCase):
"""Tests our year object."""
def setUp(self):
"""Set up common variables."""
super().setUp()
self.table = self.sql.years
self.year = emmental.db.years.Year(table=self.table, propertyid=123,
year=2023, name="2023")
def test_init(self):
"""Test that the Year is set up properly."""
self.assertIsInstance(self.year, emmental.db.playlist.Playlist)
self.assertEqual(self.year.table, self.table)
self.assertEqual(self.year.propertyid, 123)
self.assertEqual(self.year.year, 2023)
self.assertEqual(self.year.primary_key, 2023)
self.assertEqual(self.year.name, "2023")
def test_parent(self):
"""Test finding the parent Decade for this Year."""
self.assertIsNone(self.year.parent)
decade = self.sql.decades.create(2020)
self.assertEqual(self.year.parent, decade)
class TestYearTable(tests.util.TestCase):
"""Tests our year table."""
def setUp(self):
"""Set up common variables."""
super().setUp()
self.table = self.sql.years
def test_init(self):
"""Test that the year model is configured correctly."""
self.assertIsInstance(self.table, emmental.db.playlist.Table)
self.assertEqual(len(self.table), 0)
def test_construct(self):
"""Test constructing a year playlist."""
year = self.table.construct(propertyid=1988, year=1988, name="1988")
self.assertIsInstance(year, emmental.db.years.Year)
self.assertEqual(year.table, self.table)
self.assertEqual(year.propertyid, 1988)
self.assertEqual(year.year, 1988)
self.assertEqual(year.name, "1988")
self.assertFalse(year.active)
def test_create(self):
"""Test creating a year playlist."""
year = self.table.create(1988)
self.assertIsInstance(year, emmental.db.years.Year)
self.assertEqual(year.year, 1988)
self.assertEqual(year.name, "1988")
cur = self.sql("SELECT COUNT(year) FROM years")
self.assertEqual(cur.fetchone()["COUNT(year)"], 1)
row = self.sql("""SELECT COUNT(*) FROM playlist_properties
WHERE propertyid=?""", year.propertyid).fetchone()
self.assertEqual(row["COUNT(*)"], 1)
self.assertIsNone(self.table.create(1988))
def test_delete(self):
"""Test deleting a year playlist."""
year = self.table.create(1988)
self.assertTrue(year.delete())
cur = self.sql("SELECT COUNT(year) FROM years")
self.assertEqual(cur.fetchone()["COUNT(year)"], 0)
self.assertEqual(len(self.table), 0)
self.assertIsNone(self.table.get_item(0))
row = self.sql("""SELECT COUNT(*) FROM playlist_properties
WHERE propertyid=?""", year.propertyid).fetchone()
self.assertEqual(row["COUNT(*)"], 0)
self.assertFalse(year.delete())
def test_filter(self):
"""Test filtering a year playlist."""
self.table.create(1985)
self.table.create(1988)
self.table.filter("*5", now=True)
self.assertSetEqual(self.table.get_filter().keys, {1985})
self.table.filter("19*", now=True)
self.assertSetEqual(self.table.get_filter().keys, {1985, 1988})
def test_get_sort_key(self):
"""Test getting a year playlist's sort key."""
year = self.table.create(1988)
self.assertEqual(self.table.get_sort_key(year), 1988)
def test_load(self):
"""Test loading the year table from the database."""
self.table.create(1985)
self.table.create(1988)
years2 = emmental.db.years.Table(self.sql)
self.assertEqual(len(years2), 0)
years2.load(now=True)
self.assertEqual(len(years2), 2)
self.assertEqual(years2.get_item(0).year, 1985)
self.assertEqual(years2.get_item(0).name, "1985")
self.assertEqual(years2.get_item(1).year, 1988)
self.assertEqual(years2.get_item(1).name, "1988")
def test_lookup(self):
"""Test looking up year playlists."""
year = self.table.create(1988)
self.assertEqual(self.table.lookup(1988), year)
self.assertIsNone(self.table.lookup(1985))
def test_update(self):
"""Test updating year attributes."""
year = self.table.create(1980)
year.active = True
row = self.sql("""SELECT active FROM playlist_properties
WHERE propertyid=?""", year.propertyid).fetchone()
self.assertEqual(row["active"], True)