From ff835832c82e3f6b5f842ddad6952d72f8bec5ae Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Wed, 9 Nov 2022 10:59:35 -0500 Subject: [PATCH] db: Save Track data when deleting This includes the favorite status, playcount, last played timestamp, and last started timestamp. These values will be restored if a Track with the same mbid is created at a later time. Signed-off-by: Anna Schumaker --- emmental/db/emmental.sql | 35 +++++++++++++++++++++++++++ emmental/db/tracks.py | 16 +++++++------ tests/db/test_tracks.py | 51 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 7 deletions(-) diff --git a/emmental/db/emmental.sql b/emmental/db/emmental.sql index 1928e75..2dc15d7 100644 --- a/emmental/db/emmental.sql +++ b/emmental/db/emmental.sql @@ -395,6 +395,41 @@ CREATE TRIGGER tracks_active_trigger END; +/**************************************************** + * * + * Data saved when Tracks are deleted * + * * + ****************************************************/ + +CREATE TABLE saved_track_data ( + mbid TEXT PRIMARY KEY, + favorite BOOLEAN NOT NULL DEFAULT FALSE, + playcount INTEGER NOT NULL DEFAULT 0, + lastplayed TIMESTAMP DEFAULT NULL, + laststarted TIMESTAMP DEFAULT NULL +); + +CREATE TRIGGER tracks_delete_save BEFORE DELETE ON tracks + WHEN OLD.mbid != "" BEGIN + INSERT INTO saved_track_data + (mbid, favorite, playcount, lastplayed, laststarted) + VALUES (OLD.mbid, OLD.favorite, OLD.playcount, + OLD.lastplayed, OLD.laststarted); + END; + +CREATE TRIGGER tracks_insert_restore AFTER INSERT ON tracks + WHEN NEW.mbid != "" BEGIN + UPDATE tracks SET favorite = saved_track_data.favorite, + playcount = saved_track_data.playcount, + lastplayed = saved_track_data.lastplayed, + laststarted = saved_track_data.laststarted + FROM saved_track_data + WHERE tracks.mbid = saved_track_data.mbid AND + tracks.mbid = NEW.mbid; + DELETE FROM saved_track_data WHERE mbid = NEW.mbid; + END; + + /****************************************** * * * Create Default Playlists * diff --git a/emmental/db/tracks.py b/emmental/db/tracks.py index c671e01..58c527d 100644 --- a/emmental/db/tracks.py +++ b/emmental/db/tracks.py @@ -146,13 +146,15 @@ class Table(table.Table): number: int = 0, length: float = 0.0, artist: str = "", mbid: str = "", mtime: float = 0.0) -> sqlite3.Cursor: """Insert a new Track into the database.""" - return self.sql("""INSERT INTO tracks - (libraryid, mediumid, path, year, title, - number, length, artist, mbid, mtime) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - RETURNING *""", - library.libraryid, medium.mediumid, path, year.year, - title, number, length, artist, mbid, mtime) + if cur := self.sql("""INSERT INTO tracks + (libraryid, mediumid, path, year, title, + number, length, artist, mbid, mtime) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + RETURNING *""", + library.libraryid, medium.mediumid, path, year.year, + title, number, length, artist, mbid, mtime): + return self.sql("SELECT * FROM tracks WHERE trackid=?", + cur.lastrowid) def do_sql_select_all(self) -> sqlite3.Cursor: """Load Tracks from the database.""" diff --git a/tests/db/test_tracks.py b/tests/db/test_tracks.py index ca12680..9cabc87 100644 --- a/tests/db/test_tracks.py +++ b/tests/db/test_tracks.py @@ -2,6 +2,7 @@ """Tests our track Gio.ListModel.""" import datetime import pathlib +import unittest import emmental.db.tracks import tests.util import unittest.mock @@ -231,6 +232,33 @@ class TestTrackTable(tests.util.TestCase): cur = self.sql("SELECT COUNT(*) FROM tracks") self.assertEqual(cur.fetchone()["COUNT(*)"], 2) + def test_create_restore(self): + """Test restoring saved track data.""" + now = datetime.datetime.now() + self.sql("""INSERT INTO saved_track_data + (mbid, favorite, playcount, lastplayed, laststarted) + VALUES (?, ?, ?, ? , ?)""", "ab-cd-ef", True, 42, now, now) + + track1 = self.tracks.create(self.library, pathlib.Path("/a/b/1.ogg"), + self.medium, self.year) + self.assertFalse(track1.favorite) + self.assertEqual(track1.playcount, 0) + self.assertIsNone(track1.lastplayed) + self.assertIsNone(track1.laststarted) + + row = self.sql("SELECT COUNT(*) FROM saved_track_data").fetchone() + self.assertEqual(row["COUNT(*)"], 1) + + track2 = self.tracks.create(self.library, pathlib.Path("/a/b/2.ogg"), + self.medium, self.year, mbid="ab-cd-ef") + self.assertTrue(track2.favorite) + self.assertEqual(track2.playcount, 42) + self.assertEqual(track2.lastplayed, now) + self.assertEqual(track2.laststarted, now) + + row = self.sql("SELECT COUNT(*) FROM saved_track_data").fetchone() + self.assertEqual(row["COUNT(*)"], 0) + def test_delete(self): """Test deleting a Track.""" track = self.tracks.create(self.library, pathlib.Path("/a/b/c.ogg"), @@ -245,6 +273,29 @@ class TestTrackTable(tests.util.TestCase): self.assertFalse(track.delete()) + def test_delete_save(self): + """Test saving track data when a track is deleted.""" + now = datetime.datetime.now() + track1 = self.tracks.create(self.library, pathlib.Path("/a/b/1.ogg"), + self.medium, self.year, mbid="ab-cd-ef") + track2 = self.tracks.create(self.library, pathlib.Path("/a/b/2.ogg"), + self.medium, self.year) + + self.sql("""UPDATE tracks + SET favorite=?, laststarted=?, lastplayed=?, playcount=? + WHERE trackid=?""", True, now, now, 42, track1.trackid) + + track1.delete() + track2.delete() + + rows = self.sql("SELECT * FROM saved_track_data").fetchall() + self.assertEqual(len(rows), 1) + self.assertEqual(rows[0]["mbid"], "ab-cd-ef") + self.assertEqual(rows[0]["favorite"], True) + self.assertEqual(rows[0]["laststarted"], now) + self.assertEqual(rows[0]["lastplayed"], now) + self.assertEqual(rows[0]["playcount"], 42) + def test_filter(self): """Test filtering the Track table.""" self.tracks.create(self.library, pathlib.Path("/a/b/1.ogg"),