db: Give Decades knowledge about their Years

Similar to the Artists tree structure. I create a filter on the Year
table for each Decade object and adjust filtering so a Decade remains
visible if one or more years match the query.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2022-08-31 16:17:45 -04:00
parent c0edbd9bff
commit b7f1a05967
2 changed files with 68 additions and 1 deletions

View File

@ -2,6 +2,8 @@
"""A custom Gio.ListModel for working with decades."""
import sqlite3
from gi.repository import GObject
from gi.repository import Gtk
from .years import Year
from . import playlist
@ -10,6 +12,19 @@ class Decade(playlist.Playlist):
decade = GObject.Property(type=int)
def __init__(self, **kwargs):
"""Initialize a Decade object."""
super().__init__(**kwargs)
self.add_children(self.table.sql.years,
Gtk.CustomFilter.new(self.__match_year))
def __match_year(self, year: Year) -> bool:
return self.decade == year.year // 10 * 10
def get_years(self) -> list[Year]:
"""Get a list of years for this decade."""
return self.table.get_years(self)
@property
def primary_key(self) -> int:
"""Get the primary key of this Decade."""
@ -29,12 +44,16 @@ class Table(playlist.Table):
def do_sql_delete(self, decade: Decade) -> sqlite3.Cursor:
"""Delete a decade."""
for year in decade.get_years():
year.delete()
return self.sql("DELETE FROM decades WHERE decade=?", decade.decade)
def do_sql_glob(self, glob: str) -> sqlite3.Cursor:
"""Search for decades matching the search text."""
return self.sql("""SELECT decade FROM decades_view
WHERE CASEFOLD(name) GLOB ?""", glob)
WHERE CASEFOLD(name) GLOB :glob
UNION SELECT (year / 10 * 10) AS decade
FROM years WHERE year GLOB :glob""", glob=glob)
def do_sql_insert(self, year: int) -> sqlite3.Cursor | None:
"""Create a new Decade playlist."""
@ -51,3 +70,9 @@ class Table(playlist.Table):
"""Look up an decade by year."""
return self.sql("SELECT decade FROM decades WHERE decade=?",
year // 10 * 10)
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)=?",
decade.decade)
return [self.sql.years.rows.get(row["year"]) for row in rows]

View File

@ -1,7 +1,9 @@
# Copyright 2022 (c) Anna Schumaker
"""Tests our decade Gio.ListModel."""
import unittest.mock
import emmental.db
import tests.util
from gi.repository import Gtk
class TestDecadeObject(tests.util.TestCase):
@ -25,6 +27,26 @@ class TestDecadeObject(tests.util.TestCase):
self.assertEqual(self.decade.name, "The 2020s")
self.assertIsNone(self.decade.parent)
def test_get_years(self):
"""Test getting the list of years for this decade."""
with unittest.mock.patch.object(self.table, "get_years",
return_value=[1, 2, 3]) as mock:
self.assertListEqual(self.decade.get_years(), [1, 2, 3])
mock.assert_called_with(self.decade)
def test_years_model(self):
"""Test getting a Gio.ListModel representing a Decade's years."""
self.assertIsInstance(self.decade.children, Gtk.FilterListModel)
self.assertIsInstance(self.decade.children.get_filter(),
Gtk.CustomFilter)
self.assertEqual(self.decade.children.get_model(), self.sql.years)
year = self.sql.years.create(2023)
self.assertTrue(self.decade.children.get_filter().match(year))
year = self.sql.years.create(1988)
self.assertFalse(self.decade.children.get_filter().match(year))
class TestDecadeTable(tests.util.TestCase):
"""Tests our decade table."""
@ -33,6 +55,7 @@ class TestDecadeTable(tests.util.TestCase):
"""Set up common variables."""
super().setUp()
self.table = self.sql.decades
self.sql.years.load()
def test_init(self):
"""Test that the decade table is configured correctly."""
@ -69,6 +92,7 @@ class TestDecadeTable(tests.util.TestCase):
def test_delete(self):
"""Test deleting a decade playlist."""
decade = self.table.create(1980)
self.sql.years.create(1988)
self.assertTrue(decade.delete())
cur = self.sql("SELECT COUNT(decade) FROM decades")
@ -80,6 +104,9 @@ class TestDecadeTable(tests.util.TestCase):
WHERE propertyid=?""", decade.propertyid).fetchone()
self.assertEqual(row["COUNT(*)"], 0)
row = self.sql("""SELECT COUNT(*) FROM years""").fetchone()
self.assertEqual(row["COUNT(*)"], 0)
self.assertFalse(decade.delete())
def test_filter(self):
@ -87,11 +114,18 @@ class TestDecadeTable(tests.util.TestCase):
self.table.create(1980)
self.table.create(1990)
self.sql.years.create(1985)
self.sql.years.create(1988)
self.sql.years.create(1992)
self.table.filter("*80*", now=True)
self.assertSetEqual(self.table.get_filter().keys, {1980})
self.table.filter("the*s", now=True)
self.assertSetEqual(self.table.get_filter().keys, {1980, 1990})
self.table.filter("1988", now=True)
self.assertSetEqual(self.table.get_filter().keys, {1980})
def test_get_sort_key(self):
"""Test getting the sort key for a decade playlist."""
decade = self.table.create(1980)
@ -128,3 +162,11 @@ class TestDecadeTable(tests.util.TestCase):
row = self.sql("""SELECT active FROM playlist_properties
WHERE propertyid=?""", decade.propertyid).fetchone()
self.assertEqual(row["active"], True)
def test_get_years(self):
"""Test getting the list of years for a decade."""
decade = self.table.create(1980)
y1985 = self.sql.years.create(1985)
y1988 = self.sql.years.create(1988)
self.assertListEqual(self.table.get_years(decade), [y1985, y1988])