db: Add a child_set TableSubset to the Playlist

It defaults to a None pointer, but calling add_children() will set one
up with an empty set. I also implement functions for adding, removing,
and verifying child playlists.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2023-06-22 10:14:24 -04:00
parent 67b508384c
commit 85c18fb5fe
2 changed files with 67 additions and 5 deletions

View File

@ -1,6 +1,7 @@
# Copyright 2022 (c) Anna Schumaker # Copyright 2022 (c) Anna Schumaker
"""A customized Gio.ListStore for tracking Playlist GObjects.""" """A customized Gio.ListStore for tracking Playlist GObjects."""
import sqlite3 import sqlite3
import typing
from gi.repository import GObject from gi.repository import GObject
from gi.repository import Gio from gi.repository import Gio
from gi.repository import Gtk from gi.repository import Gtk
@ -28,6 +29,7 @@ class Playlist(table.Row):
tracks_movable = GObject.Property(type=bool, default=False) tracks_movable = GObject.Property(type=bool, default=False)
current_trackid = GObject.Property(type=int) current_trackid = GObject.Property(type=int)
child_set = GObject.Property(type=table.TableSubset)
children = GObject.Property(type=Gtk.FilterListModel) children = GObject.Property(type=Gtk.FilterListModel)
def __init__(self, table: Gio.ListModel, propertyid: int, def __init__(self, table: Gio.ListModel, propertyid: int,
@ -49,19 +51,27 @@ class Playlist(table.Row):
return True return True
def add_children(self, child_table: table.Table, def add_children(self, child_table: table.Table,
child_filter: Gtk.Filter) -> None: child_filter: Gtk.Filter,
child_keys: set | None = None) -> None:
"""Create a FilterListModel for this playlist's children.""" """Create a FilterListModel for this playlist's children."""
child_keys = set() if child_keys is None else child_keys
self.child_set = table.TableSubset(child_table, keys=child_keys)
self.children = Gtk.FilterListModel.new(child_table, child_filter) self.children = Gtk.FilterListModel.new(child_table, child_filter)
self.children.set_incremental(True) self.children.set_incremental(True)
def do_update(self, column: str) -> bool: def do_update(self, column: str) -> bool:
"""Update a Playlist object.""" """Update a Playlist object."""
match column: match column:
case "propertyid" | "name" | "n-tracks" | "children" | \ case "propertyid" | "name" | "n-tracks" | "child-set" | \
"user-tracks" | "tracks-loaded" | "tracks-movable": pass "children" | "user-tracks" | "tracks-loaded" | \
"tracks-movable": pass
case _: return super().do_update(column) case _: return super().do_update(column)
return True return True
def add_child(self, child: typing.Self) -> None:
"""Add a child Playlist to this Playlist."""
self.child_set.add_row(child)
def add_track(self, track: Track, *, idle: bool = False) -> None: def add_track(self, track: Track, *, idle: bool = False) -> None:
"""Add a Track to this Playlist.""" """Add a Track to this Playlist."""
if self.table.add_track(self, track): if self.table.add_track(self, track):
@ -71,6 +81,10 @@ class Playlist(table.Row):
"""Get a dictionary mapping for trackid -> sorted position.""" """Get a dictionary mapping for trackid -> sorted position."""
return self.table.get_track_order(self) return self.table.get_track_order(self)
def has_child(self, child: typing.Self) -> bool:
"""Check if this Playlist has a specific child Playlist."""
return child in self.child_set
def has_track(self, track: Track) -> bool: def has_track(self, track: Track) -> bool:
"""Check if a Track is on this Playlist.""" """Check if a Track is on this Playlist."""
return track in self.tracks return track in self.tracks
@ -95,6 +109,10 @@ class Playlist(table.Row):
self.tracks_loaded = False self.tracks_loaded = False
self.table.queue.push(self.load_tracks, now=not idle) self.table.queue.push(self.load_tracks, now=not idle)
def remove_child(self, child: typing.Self) -> None:
"""Remove a child Playlist from this Playlist."""
self.child_set.remove_row(child)
def remove_track(self, track: table.Row, *, idle: bool = False) -> None: def remove_track(self, track: table.Row, *, idle: bool = False) -> None:
"""Remove a Track from this Playlist.""" """Remove a Track from this Playlist."""
self.table.queue.push(self.__remove_track, track, now=not idle) self.table.queue.push(self.__remove_track, track, now=not idle)

View File

@ -65,15 +65,28 @@ class TestPlaylistRow(unittest.TestCase):
def test_children(self): def test_children(self):
"""Test the child playlist properties.""" """Test the child playlist properties."""
self.assertIsNone(self.playlist.child_set)
self.assertIsNone(self.playlist.children) self.assertIsNone(self.playlist.children)
filter = Gtk.Filter() filter = Gtk.Filter()
self.playlist.add_children(self.table, filter) table = emmental.db.table.Table(None)
self.playlist.add_children(table, filter)
self.assertIsInstance(self.playlist.child_set,
emmental.db.table.TableSubset)
self.assertEqual(self.playlist.child_set.table, table)
self.assertSetEqual(self.playlist.child_set.keyset.keys, set())
self.assertIsInstance(self.playlist.children, Gtk.FilterListModel) self.assertIsInstance(self.playlist.children, Gtk.FilterListModel)
self.assertEqual(self.playlist.children.get_filter(), filter) self.assertEqual(self.playlist.children.get_filter(), filter)
self.assertEqual(self.playlist.children.get_model(), self.table) self.assertEqual(self.playlist.children.get_model(), table)
self.assertTrue(self.playlist.children.get_incremental()) self.assertTrue(self.playlist.children.get_incremental())
playlist2 = emmental.db.playlist.Playlist(table=self.table,
propertyid=2, name="Plist2")
playlist2.add_children(table, filter, {1, 2, 3})
self.assertSetEqual(playlist2.child_set.keyset.keys, {1, 2, 3})
def test_parent(self): def test_parent(self):
"""Test the parent playlist property.""" """Test the parent playlist property."""
self.assertIsNone(self.playlist.parent) self.assertIsNone(self.playlist.parent)
@ -97,6 +110,15 @@ class TestPlaylistRow(unittest.TestCase):
self.table.update.assert_called_with(self.playlist, self.table.update.assert_called_with(self.playlist,
prop, value) prop, value)
def test_add_child(self):
"""Test adding a child playlist to the playlist."""
table = emmental.db.table.Table(None)
child = tests.util.table.MockRow(table=table, number=1)
self.playlist.add_children(table, Gtk.Filter())
self.playlist.add_child(child)
self.assertIn(child, self.playlist.child_set)
def test_add_track(self): def test_add_track(self):
"""Test adding a track to the playlist.""" """Test adding a track to the playlist."""
self.playlist.add_track(self.track, idle=True) self.playlist.add_track(self.track, idle=True)
@ -122,6 +144,18 @@ class TestPlaylistRow(unittest.TestCase):
{1: 3, 2: 2, 3: 1}) {1: 3, 2: 2, 3: 1})
self.table.get_track_order.assert_called_with(self.playlist) self.table.get_track_order.assert_called_with(self.playlist)
def test_has_child(self):
"""Test the playlist has_child() function."""
table = emmental.db.table.Table(None)
child = tests.util.table.MockRow(table=table, number=1)
self.playlist.add_children(table, Gtk.Filter())
self.assertFalse(self.playlist.has_child(child))
self.playlist.add_child(child)
self.assertTrue(self.playlist.has_child(child))
self.playlist.remove_child(child)
self.assertFalse(self.playlist.has_child(child))
def test_has_track(self): def test_has_track(self):
"""Test the playlist has_track() function.""" """Test the playlist has_track() function."""
self.assertFalse(self.playlist.has_track(self.track)) self.assertFalse(self.playlist.has_track(self.track))
@ -141,6 +175,16 @@ class TestPlaylistRow(unittest.TestCase):
self.assertTrue(self.playlist.move_track_up(self.track)) self.assertTrue(self.playlist.move_track_up(self.track))
self.table.move_track_up.assert_called_with(self.playlist, self.track) self.table.move_track_up.assert_called_with(self.playlist, self.track)
def test_remove_child(self):
"""Test removing a child playlist from the playlist."""
table = emmental.db.table.Table(None)
child = tests.util.table.MockRow(table=table, number=1)
self.playlist.add_children(table, Gtk.Filter())
self.playlist.add_child(child)
self.playlist.remove_child(child)
self.assertFalse(child in self.playlist.child_set)
def test_remove_track(self): def test_remove_track(self):
"""Test removing a track from the playlist.""" """Test removing a track from the playlist."""
self.playlist.tracks.trackids.add(self.track.trackid) self.playlist.tracks.trackids.add(self.track.trackid)