WIP: template management: new, rename, delete working

This commit is contained in:
Keith Edmunds 2025-02-22 19:16:42 +00:00
parent e10c2adafe
commit 5f396a0993
4 changed files with 145 additions and 89 deletions

View File

@ -203,12 +203,12 @@ class Playlists(dbtables.PlaylistsTable):
@classmethod
def create_playlist_from_template(
cls, session: Session, template: "Playlists", playlist_name: str
cls, session: Session, template_id: int, playlist_name: str
) -> Optional["Playlists"]:
"""Create a new playlist from template"""
# Sanity check
if not template.id:
if not template_id:
return None
playlist = cls(session, playlist_name)
@ -217,7 +217,7 @@ class Playlists(dbtables.PlaylistsTable):
if not playlist or not playlist.id:
return None
PlaylistRows.copy_playlist(session, template.id, playlist.id)
PlaylistRows.copy_playlist(session, template_id, playlist.id)
return playlist

View File

@ -83,18 +83,6 @@ from utilities import check_db, update_bitrates
import helpers
class DownloadCSV(QDialog):
def __init__(self, parent=None):
super().__init__()
self.ui = Ui_DateSelect()
self.ui.setupUi(self)
self.ui.dateTimeEdit.setDate(QDate.currentDate())
self.ui.dateTimeEdit.setTime(QTime(19, 59, 0))
self.ui.buttonBox.accepted.connect(self.accept)
self.ui.buttonBox.rejected.connect(self.reject)
class Current:
base_model: PlaylistModel
proxy_model: PlaylistProxyModel
@ -267,7 +255,16 @@ class ItemlistManager(QDialog):
self.callbacks.edit(item_id)
def rename_item(self, item_id: int) -> None:
print(f"Rename item {item_id}")
new_name = self.callbacks.rename(item_id)
if not new_name:
return
# Rename item in list
for row in range(self.table.rowCount()):
item = self.table.item(row, 0)
if item and self.items[row].id == item_id:
item.setText(new_name)
self.items[row].name = new_name
break
def toggle_favourite(self, item_id: int, checked: bool) -> None:
print(f"Toggle favourite for item {item_id}: {checked}")
@ -294,7 +291,7 @@ class ItemlistManagerCallbacks:
edit: Callable[[int], None]
favourite: Callable[[int, bool], None]
new_item: Callable[[], None]
rename: Callable[[int], None]
rename: Callable[[int], Optional[str]]
class PreviewManager:
@ -459,16 +456,21 @@ class TemplateSelectorDialog(QDialog):
Class to manage user selection of template
"""
def __init__(self, templates: list[tuple[str, int]]) -> None:
def __init__(
self, templates: list[tuple[str, int]], template_prompt: Optional[str]
) -> None:
super().__init__()
self.templates = templates
self.template_prompt = template_prompt
self.selected_id = None
self.init_ui()
def init_ui(self):
# Create label
label = QLabel("Select template:")
if not self.template_prompt:
self.template_prompt = "Select template:"
label = QLabel(self.template_prompt)
# Create combo box
self.combo_box = QComboBox()
@ -868,14 +870,22 @@ class Window(QMainWindow):
self.signals.search_songfacts_signal.connect(self.open_songfacts_browser)
self.signals.search_wikipedia_signal.connect(self.open_wikipedia_browser)
def create_playlist(
self, session: Session, playlist_name: str
) -> Optional[Playlists]:
def create_playlist(self, session: Session, template_id: int) -> Optional[Playlists]:
"""Create new playlist"""
log.debug(f"create_playlist({playlist_name=}")
# Get a name for this new playlist
playlist_name = self.solicit_playlist_name(session)
if not playlist_name:
return None
# If template.id == 0, user doesn't want a template
playlist: Optional[Playlists]
if template_id == 0:
playlist = Playlists(session, playlist_name)
else:
playlist = Playlists.create_playlist_from_template(
session, template_id, playlist_name
)
if playlist:
return playlist
@ -1072,6 +1082,18 @@ class Window(QMainWindow):
if track_sequence.current:
track_sequence.current.fade()
def get_tab_index_for_playlist(self, playlist_id: int) -> Optional[int]:
"""
Return the tab index for the passed playlist_id if it is displayed,
else return None.
"""
for idx in range(self.playlist_section.tabPlaylist.count()):
if self.playlist_section.tabPlaylist.widget(idx).playlist_id == playlist_id:
return idx
return None
def hide_played(self):
"""Toggle hide played tracks"""
@ -1178,6 +1200,7 @@ class Window(QMainWindow):
Delete / edit templates
"""
# Define callbacks to handle management options
def delete(template_id: int) -> None:
"""delete template"""
@ -1191,19 +1214,15 @@ class Window(QMainWindow):
f"Delete template '{template.name}': " "Are you sure?",
):
# If template is currently open, re-check
for idx in range(self.playlist_section.tabPlaylist.count()):
if (
self.playlist_section.tabPlaylist.widget(idx).playlist_id
== template_id
):
open_idx = self.get_tab_index_for_playlist(template_id)
if open_idx:
if not helpers.ask_yes_no(
"Delete open template",
f"Template '{template.name}' is currently open. Really delete?"
f"Template '{template.name}' is currently open. Really delete?",
):
return
else:
self.playlist_section.tabPlaylist.removeTab(idx)
break
self.playlist_section.tabPlaylist.removeTab(open_idx)
log.info(f"manage_templates: delete {template=}")
template.delete(session)
@ -1223,25 +1242,50 @@ class Window(QMainWindow):
self.playlist_section.tabPlaylist.setCurrentIndex(idx)
def favourite(template_id: int, favourite: bool) -> None:
"""favourite template"""
"""Mark template as (not) favourite"""
print(f"manage_templates.favourite({template_id=}")
print(f"{session=}")
def new_item() -> None:
"""new item"""
"""Create new template"""
print("manage_templates.new()")
print(f"{session=}")
# Get base template
template_id = self.solicit_template_to_use(
session, template_prmompt="New template based upon:"
)
if template_id is None:
return
def rename(template_id: int) -> None:
new_template = self.create_playlist(session, template_id)
if new_template:
self.open_playlist(session, new_template)
def rename(template_id: int) -> Optional[str]:
"""rename template"""
print(f"manage_templates.rename({template_id=}")
print(f"{session=}")
template = session.get(Playlists, template_id)
if not template:
raise ApplicationError(
f"manage_templeate.delete({template_id=}) can't load template"
)
new_name = self.solicit_playlist_name(session, template.name)
if new_name:
template.rename(session, new_name)
idx = self.tabBar.currentIndex()
self.tabBar.setTabText(idx, new_name)
session.commit()
return new_name
return None
# Call listitem management dialog to manage templates
callbacks = ItemlistManagerCallbacks(
delete=delete, edit=edit, favourite=favourite, new_item=new_item, rename=rename
delete=delete,
edit=edit,
favourite=favourite,
new_item=new_item,
rename=rename,
)
# Build a list of templates
@ -1251,8 +1295,10 @@ class Window(QMainWindow):
for template in Playlists.get_all_templates(session):
# TODO: need to add in favourites
template_list.append(ItemlistItem(name=template.name, id=template.id))
x = ItemlistManager(template_list, callbacks)
x.show()
# We need to retain a reference to the dialog box to stop it
# going out of scope and being garbage-collected.
self.dlg = ItemlistManager(template_list, callbacks)
self.dlg.show()
def mark_rows_for_moving(self) -> None:
"""
@ -1337,37 +1383,17 @@ class Window(QMainWindow):
self.move_playlist_rows(unplayed_rows)
self.disable_selection_timing = False
def new_playlist(self) -> None:
def new_playlist(self) -> Optional[Playlists]:
"""
Create new playlist, optionally from template
"""
# Build a list of (template-name, playlist-id) tuples starting
# with the "no template" entry
template_list: list[tuple[str, int]] = []
template_list.append((Config.NO_TEMPLATE_NAME, 0))
with db.Session() as session:
for template in Playlists.get_all_templates(session):
template_list.append((template.name, template.id))
template_id = self.solicit_template_to_use(session)
if not template_id:
return None # User cancelled
dlg = TemplateSelectorDialog(template_list)
if not dlg.exec():
return # User cancelled
template_id = dlg.selected_id
# Get a name for this new playlist
playlist_name = self.solicit_playlist_name(session)
if not playlist_name:
return
# If template_id == 0, user doesn't want a template
if template_id == 0:
playlist = self.create_playlist(session, playlist_name)
else:
playlist = Playlists.create_playlist_from_template(
session, template, playlist_name
)
playlist = self.create_playlist(session, template_id)
if playlist:
playlist.mark_open()
@ -1376,10 +1402,13 @@ class Window(QMainWindow):
session.commit()
idx = self.create_playlist_tab(playlist)
self.playlist_section.tabPlaylist.setCurrentIndex(idx)
return playlist
else:
log.error("Playlist failed to create")
ApplicationError("new_playlist: Playlist failed to create")
def open_playlist(self) -> None:
return None
def open_existing_playlist(self) -> None:
"""Open existing playlist"""
with db.Session() as session:
@ -1388,6 +1417,11 @@ class Window(QMainWindow):
dlg.exec()
playlist = dlg.playlist
if playlist:
self.open_playlist(session, playlist)
def open_playlist(self, session: Session, playlist: Playlists) -> None:
"""Open passed playlist"""
idx = self.create_playlist_tab(playlist)
playlist.mark_open()
session.commit()
@ -1859,24 +1893,45 @@ class Window(QMainWindow):
# Switch to correct tab
if playlist_id != self.current.playlist_id:
for idx in range(self.playlist_section.tabPlaylist.count()):
if (
self.playlist_section.tabPlaylist.widget(idx).playlist_id
== playlist_id
):
self.playlist_section.tabPlaylist.setCurrentIndex(idx)
break
open_idx = self.get_tab_index_for_playlist(playlist_id)
if open_idx:
self.playlist_section.tabPlaylist.setCurrentIndex(open_idx)
else:
raise ApplicationError(
f"show_track() can't find current playlist tab {playlist_id=}"
)
self.active_tab().scroll_to_top(playlist_track.row_number)
def solicit_template_to_use(
self, session: Session, template_prmompt: Optional[str] = None
) -> Optional[int]:
"""
Have user select a template. Return the template.id, or None if they cancel.
template_id of zero means don't use a template.
"""
template_name_id_list: list[tuple[str, int]] = []
template_name_id_list.append((Config.NO_TEMPLATE_NAME, 0))
with db.Session() as session:
for template in Playlists.get_all_templates(session):
template_name_id_list.append((template.name, template.id))
dlg = TemplateSelectorDialog(template_name_id_list, template_prmompt)
if not dlg.exec() or dlg.selected_id is None:
return None # User cancelled
return dlg.selected_id
def solicit_playlist_name(
self, session: Session, default: str = ""
self, session: Session, default: str = "", prompt: str = "Playlist name:"
) -> Optional[str]:
"""Get name of new playlist from user"""
dlg = QInputDialog(self)
dlg.setInputMode(QInputDialog.InputMode.TextInput)
dlg.setLabelText("Playlist name:")
dlg.setLabelText(prompt)
while True:
if default:
dlg.setTextValue(default)

View File

@ -26,6 +26,7 @@ from PyQt6.QtGui import (
)
# Third party imports
from sqlalchemy.orm.session import Session
import obswebsocket # type: ignore
# import snoop # type: ignore
@ -772,7 +773,7 @@ class PlaylistModel(QAbstractTableModel):
return None
def load_data(self, session: db.session) -> None:
def load_data(self, session: Session) -> None:
"""
Same as refresh data, but only used when creating playslit.
Distinguishes profile time between initial load and other
@ -1061,7 +1062,7 @@ class PlaylistModel(QAbstractTableModel):
# Update display
self.invalidate_row(track_sequence.previous.row_number)
def refresh_data(self, session: db.session) -> None:
def refresh_data(self, session: Session) -> None:
"""
Populate self.playlist_rows with playlist data

View File

@ -13,7 +13,7 @@ menus:
- title: "&Playlist"
actions:
- text: "Open Playlist"
handler: "open_playlist"
handler: "open_existing_playlist"
shortcut: "Ctrl+O"
- text: "New Playlist"
handler: "new_playlist_dynamic_submenu"