From 558554d086b36e17d7ca0f1582a95f1bc794c097 Mon Sep 17 00:00:00 2001 From: Keith Edmunds Date: Mon, 9 Dec 2024 08:45:41 +0000 Subject: [PATCH] Implement "remove comments" Fixes #185 --- app/playlistmodel.py | 55 ++++++++++++++++++++++++++++++++++++++------ app/playlists.py | 14 +++++++++++ 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/app/playlistmodel.py b/app/playlistmodel.py index afdf28b..9f6db70 100644 --- a/app/playlistmodel.py +++ b/app/playlistmodel.py @@ -40,6 +40,7 @@ from classes import ( ) from config import Config from helpers import ( + ask_yes_no, file_is_unreadable, get_embedded_time, get_relative_date, @@ -67,7 +68,7 @@ class PlaylistModel(QAbstractTableModel): database refresh_data() will repopulate all of playlist_rows from the - database + database. """ def __init__( @@ -993,10 +994,17 @@ class PlaylistModel(QAbstractTableModel): @line_profiler.profile def refresh_data(self, session: db.session, dummy_for_profiling=None) -> None: - """Populate self.playlist_rows with playlist data""" + """ + Populate self.playlist_rows with playlist data + + We used to clear self.playlist_rows each time but that's + expensive and slow on big playlists. Instead we track where rows + are in database versus self.playlist_rows and fixup the latter. + This works well for news rows added and for rows moved, but + doesn't work for changed comments so they must be handled using + refresh_row(). + """ - # We used to clear self.playlist_rows each time but that's - # expensive and slow on big playlists # Note where each playlist_id is plid_to_row: dict[int, int] = {} @@ -1016,12 +1024,13 @@ class PlaylistModel(QAbstractTableModel): # Copy to self.playlist_rows self.playlist_rows = new_playlist_rows - # Same as refresh data, but only used when creating playslit. - # Distinguishes profile time between initial load and other - # refreshes. def load_data(self, session: db.session, dummy_for_profiling=None) -> None: """Populate self.playlist_rows with playlist data""" + # Same as refresh data, but only used when creating playslit. + # Distinguishes profile time between initial load and other + # refreshes. + # We used to clear self.playlist_rows each time but that's # expensive and slow on big playlists @@ -1107,6 +1116,38 @@ class PlaylistModel(QAbstractTableModel): self.update_track_times() + def remove_comments(self, row_numbers: list[int]) -> None: + """ + Remove comments from passed rows + """ + + if not row_numbers: + return + + # Safety check + if not ask_yes_no( + title="Remove comments", + question=f"Remove comments from {len(row_numbers)} rows?" + ): + return + + with db.Session() as session: + for row_number in row_numbers: + playlist_row = session.get( + PlaylistRows, self.playlist_rows[row_number].playlistrow_id + ) + if playlist_row.track_id: + playlist_row.note = "" + # We can't use refresh_data() because its + # optimisations mean it won't update comments in + # self.playlist_rows + # The "correct" approach would be to re-read from the + # database but we optimise here by simply updating + # self.playlist_rows directly. + self.playlist_rows[row_number].note = "" + session.commit() + self.invalidate_rows(row_numbers) + def _reversed_contiguous_row_groups( self, row_numbers: list[int] ) -> list[list[int]]: diff --git a/app/playlists.py b/app/playlists.py index 35ca3c8..bd1a566 100644 --- a/app/playlists.py +++ b/app/playlists.py @@ -505,6 +505,9 @@ class PlaylistTab(QTableView): lambda: proxy_model.remove_track(model_row_number), ) + # Remove comments + self._add_context_menu("Remove comments", lambda: self._remove_comments()) + # Add track to section header (ie, make this a track row) if header_row: self._add_context_menu("Add a track", lambda: self._add_track()) @@ -778,6 +781,17 @@ class PlaylistTab(QTableView): except ApplicationError as e: show_warning(self.musicmuster, "Audacity error", str(e)) + def _remove_comments(self) -> None: + """ + Remove comments from selected rows + """ + + row_numbers = self.selected_model_row_numbers() + if not row_numbers: + return + + self.source_model.remove_comments(row_numbers) + def _rescan(self, row_number: int) -> None: """Rescan track"""