Compare commits

..

No commits in common. "c626d91f26ca12426b9af0c6f240f496e4a3182f" and "5769e34412bf3d3edfce3839be11e62e485073f0" have entirely different histories.

7 changed files with 165 additions and 313 deletions

View File

@ -81,11 +81,8 @@ class MusicMusterSignals(QObject):
add_track_to_header_signal = pyqtSignal(int, int, int) add_track_to_header_signal = pyqtSignal(int, int, int)
add_track_to_playlist_signal = pyqtSignal(int, int, int, str) add_track_to_playlist_signal = pyqtSignal(int, int, int, str)
begin_reset_model_signal = pyqtSignal(int)
enable_escape_signal = pyqtSignal(bool) enable_escape_signal = pyqtSignal(bool)
end_reset_model_signal = pyqtSignal(int)
next_track_changed_signal = pyqtSignal() next_track_changed_signal = pyqtSignal()
row_order_changed_signal = pyqtSignal(int)
search_songfacts_signal = pyqtSignal(str) search_songfacts_signal = pyqtSignal(str)
search_wikipedia_signal = pyqtSignal(str) search_wikipedia_signal = pyqtSignal(str)
show_warning_signal = pyqtSignal(str, str) show_warning_signal = pyqtSignal(str, str)
@ -139,10 +136,10 @@ class PlaylistTrack:
Update with new plr information Update with new plr information
""" """
session.add(plr)
self.plr_rownum = plr.plr_rownum
if not plr.track: if not plr.track:
return return
session.add(plr)
track = plr.track track = plr.track
self.artist = track.artist self.artist = track.artist
@ -152,6 +149,7 @@ class PlaylistTrack:
self.path = track.path self.path = track.path
self.playlist_id = plr.playlist_id self.playlist_id = plr.playlist_id
self.plr_id = plr.id self.plr_id = plr.id
self.plr_rownum = plr.plr_rownum
self.silence_at = track.silence_at self.silence_at = track.silence_at
self.start_gap = track.start_gap self.start_gap = track.start_gap
self.start_time = None self.start_time = None

View File

@ -6,7 +6,6 @@ from config import Config
from dbconfig import scoped_session from dbconfig import scoped_session
from datetime import datetime from datetime import datetime
from pprint import pprint
from typing import List, Optional, Sequence from typing import List, Optional, Sequence
from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.ext.associationproxy import association_proxy
@ -315,7 +314,6 @@ class Playlists(Base):
"""Mark playlist as loaded and used now""" """Mark playlist as loaded and used now"""
self.open = True self.open = True
self.last_used = datetime.now()
@staticmethod @staticmethod
def name_is_available(session: scoped_session, name: str) -> bool: def name_is_available(session: scoped_session, name: str) -> bool:

View File

@ -221,8 +221,6 @@ class Window(QMainWindow, Ui_MainWindow):
self.active_tab = lambda: self.tabPlaylist.currentWidget() self.active_tab = lambda: self.tabPlaylist.currentWidget()
self.active_model = lambda: self.tabPlaylist.currentWidget().model() self.active_model = lambda: self.tabPlaylist.currentWidget().model()
self.move_source_rows: Optional[List[int]] = None
self.move_source_model: Optional[PlaylistModel] = None
self.load_last_playlists() self.load_last_playlists()
if Config.CARTS_HIDE: if Config.CARTS_HIDE:
@ -612,10 +610,10 @@ class Window(QMainWindow, Ui_MainWindow):
Cut rows ready for pasting. Cut rows ready for pasting.
""" """
with Session() as session:
# Save the selected PlaylistRows items ready for a later # Save the selected PlaylistRows items ready for a later
# paste # paste
self.move_source_rows = self.active_tab().get_selected_rows() self.selected_plrs = self.active_tab().get_selected_playlistrows(session)
self.move_source_model = self.active_model()
def debug(self): def debug(self):
"""Invoke debugger""" """Invoke debugger"""
@ -927,17 +925,38 @@ class Window(QMainWindow, Ui_MainWindow):
self.signals.search_wikipedia_signal.emit(track_info.title) self.signals.search_wikipedia_signal.emit(track_info.title)
def move_playlist_rows(self, row_numbers: List[int]) -> None: def move_playlist_rows(
self, session: scoped_session, playlistrows: Sequence[PlaylistRows]
) -> None:
""" """
Move passed playlist rows to another playlist Move passed playlist rows to another playlist
Actions required:
- exclude current/next tracks from being moved
- identify destination playlist
- update playlist for the rows in the database
- remove them from the display
- update destination playlist display if loaded
""" """
# Remove current/next rows from list
plrs_to_move = [
plr
for plr in playlistrows
if plr.id not in [track_sequence.now.plr_id, track_sequence.next.plr_id]
]
rows_to_delete = [
plr.plr_rownum for plr in plrs_to_move if plr.plr_rownum is not None
]
if not rows_to_delete:
return
# Identify destination playlist # Identify destination playlist
playlists = [] playlists = []
visible_tab = self.active_tab() visible_tab = self.active_tab()
source_playlist_id = visible_tab.playlist_id source_playlist_id = visible_tab.playlist_id
with Session() as session:
for playlist in Playlists.get_all(session): for playlist in Playlists.get_all(session):
if playlist.id == source_playlist_id: if playlist.id == source_playlist_id:
continue continue
@ -948,40 +967,64 @@ class Window(QMainWindow, Ui_MainWindow):
dlg.exec() dlg.exec()
if not dlg.playlist: if not dlg.playlist:
return return
to_playlist_id = dlg.playlist.id destination_playlist_id = dlg.playlist.id
# Get row number in destination playlist # Update destination playlist in the database
last_row = PlaylistRows.get_last_used_row(session, to_playlist_id) last_row = PlaylistRows.get_last_used_row(session, destination_playlist_id)
if last_row is not None: if last_row is not None:
to_row = last_row + 1 next_row = last_row + 1
else: else:
to_row = 0 next_row = 0
# Move rows for plr in plrs_to_move:
self.active_model().move_rows_between_playlists( plr.plr_rownum = next_row
row_numbers, to_row, to_playlist_id next_row += 1
) plr.playlist_id = destination_playlist_id
# Reset played as it's not been played on this playlist
plr.played = False
session.commit()
# Remove moved rows from display and save visible playlist
visible_tab.remove_rows(rows_to_delete)
visible_tab.save_playlist(session)
# Disable sort undo
self.sort_undo: List[int] = []
# Update destination playlist_tab if visible (if not visible, it
# will be re-populated when it is opened)
destination_playlist_tab = None
for tab in range(self.tabPlaylist.count()):
if self.tabPlaylist.widget(tab).playlist_id == dlg.playlist.id:
destination_playlist_tab = self.tabPlaylist.widget(tab)
break
if destination_playlist_tab:
destination_playlist_tab.populate_display(session, dlg.playlist.id)
def move_selected(self) -> None: def move_selected(self) -> None:
""" """
Move selected rows to another playlist Move selected rows to another playlist
""" """
selected_rows = self.active_tab().get_selected_rows() with Session() as session:
if not selected_rows: selected_plrs = self.active_tab().get_selected_playlistrows(session)
if not selected_plrs:
return return
self.move_playlist_rows(selected_rows) self.move_playlist_rows(session, selected_plrs)
def move_unplayed(self) -> None: def move_unplayed(self) -> None:
""" """
Move unplayed rows to another playlist Move unplayed rows to another playlist
""" """
unplayed_rows = self.active_model().get_unplayed_rows() playlist_id = self.active_tab().playlist_id
if not unplayed_rows: with Session() as session:
return unplayed_plrs = PlaylistRows.get_unplayed_rows(session, playlist_id)
self.move_playlist_rows(unplayed_rows) if helpers.ask_yes_no(
"Move tracks", f"Move {len(unplayed_plrs)} tracks:" " Are you sure?"
):
self.move_playlist_rows(session, unplayed_plrs)
def new_from_template(self) -> None: def new_from_template(self) -> None:
"""Create new playlist from template""" """Create new playlist from template"""
@ -1022,25 +1065,69 @@ class Window(QMainWindow, Ui_MainWindow):
def paste_rows(self) -> None: def paste_rows(self) -> None:
""" """
Paste earlier cut rows. Paste earlier cut rows.
Process:
- ensure we have some cut rows
- if not pasting at end of playlist, move later rows down
- update plrs with correct playlist and row
- if moving between playlists: renumber source playlist rows
- else: check integrity of playlist rows
""" """
if self.move_source_rows is None or self.move_source_model is None: if not self.selected_plrs:
return return
to_playlist_id = self.active_tab().playlist_id playlist_tab = self.active_tab()
selected_rows = self.active_tab().get_selected_rows() dst_playlist_id = playlist_tab.playlist_id
if selected_rows: dst_row = self.active_tab().get_new_row_number()
destination_row = selected_rows[0]
else:
destination_row = self.active_model().rowCount()
if to_playlist_id == self.move_source_model.playlist_id: with Session() as session:
self.move_source_model.move_rows(self.move_source_rows, destination_row) # Create space in destination playlist
else: PlaylistRows.move_rows_down(
self.move_source_model.move_rows_between_playlists( session, dst_playlist_id, dst_row, len(self.selected_plrs)
self.move_source_rows, destination_row, to_playlist_id
) )
self.move_source_rows = self.move_source_model = None session.commit()
# Update plrs
row = dst_row
src_playlist_id = None
for plr in self.selected_plrs:
# Update moved rows
session.add(plr)
if not src_playlist_id:
src_playlist_id = plr.playlist_id
plr.playlist_id = dst_playlist_id
plr.plr_rownum = row
row += 1
if not src_playlist_id:
return
session.flush()
# Update display
self.active_tab().populate_display(
session, dst_playlist_id, scroll_to_top=False
)
# If source playlist is not destination playlist, fixup row
# numbers and update display
if src_playlist_id != dst_playlist_id:
PlaylistRows.fixup_rownumbers(session, src_playlist_id)
# Update source playlist_tab if visible (if not visible, it
# will be re-populated when it is opened)
source_playlist_tab = None
for tab in range(self.tabPlaylist.count()):
if self.tabPlaylist.widget(tab).playlist_id == src_playlist_id:
source_playlist_tab = self.tabPlaylist.widget(tab)
break
if source_playlist_tab:
source_playlist_tab.populate_display(
session, src_playlist_id, scroll_to_top=False
)
# Reset so rows can't be repasted
self.selected_plrs = None
def play_next(self, position: Optional[float] = None) -> None: def play_next(self, position: Optional[float] = None) -> None:
""" """

View File

@ -2,7 +2,6 @@ from dataclasses import dataclass
from datetime import datetime, timedelta from datetime import datetime, timedelta
from enum import auto, Enum from enum import auto, Enum
from operator import attrgetter from operator import attrgetter
from pprint import pprint
from typing import List, Optional from typing import List, Optional
from PyQt6.QtCore import ( from PyQt6.QtCore import (
@ -125,9 +124,6 @@ class PlaylistModel(QAbstractTableModel):
self.signals.add_track_to_header_signal.connect(self.add_track_to_header) self.signals.add_track_to_header_signal.connect(self.add_track_to_header)
self.signals.add_track_to_playlist_signal.connect(self.add_track) self.signals.add_track_to_playlist_signal.connect(self.add_track)
self.signals.begin_reset_model_signal.connect(self.begin_reset_model)
self.signals.end_reset_model_signal.connect(self.end_reset_model)
self.signals.row_order_changed_signal.connect(self.row_order_changed)
with Session() as session: with Session() as session:
# Ensure row numbers in playlist are contiguous # Ensure row numbers in playlist are contiguous
@ -234,15 +230,6 @@ class PlaylistModel(QAbstractTableModel):
return QBrush() return QBrush()
def begin_reset_model(self, playlist_id: int) -> None:
"""
Reset model if playlist_id is ours
"""
if playlist_id != self.playlist_id:
return
super().beginResetModel()
def columnCount(self, parent: QModelIndex = QModelIndex()) -> int: def columnCount(self, parent: QModelIndex = QModelIndex()) -> int:
"""Standard function for view""" """Standard function for view"""
@ -297,7 +284,11 @@ class PlaylistModel(QAbstractTableModel):
# Find next track # Find next track
# Get all unplayed track rows # Get all unplayed track rows
next_row = None next_row = None
unplayed_rows = self.get_unplayed_rows() unplayed_rows = [
a.plr_rownum
for a in PlaylistRows.get_unplayed_rows(session, self.playlist_id)
]
if unplayed_rows: if unplayed_rows:
try: try:
# Find next row after current track # Find next row after current track
@ -366,7 +357,7 @@ class PlaylistModel(QAbstractTableModel):
PlaylistRows.fixup_rownumbers(session, self.playlist_id) PlaylistRows.fixup_rownumbers(session, self.playlist_id)
self.refresh_data(session) self.refresh_data(session)
self.row_order_changed(self.playlist_id) self.update_track_times()
def display_role(self, row: int, column: int, prd: PlaylistRowData) -> QVariant: def display_role(self, row: int, column: int, prd: PlaylistRowData) -> QVariant:
""" """
@ -412,18 +403,6 @@ class PlaylistModel(QAbstractTableModel):
return QVariant() return QVariant()
def end_reset_model(self, playlist_id: int) -> None:
"""
End model reset if this is our playlist
"""
if playlist_id != self.playlist_id:
return
with Session() as session:
self.refresh_data(session)
super().endResetModel()
self.row_order_changed(self.playlist_id)
def get_duplicate_rows(self) -> List[int]: def get_duplicate_rows(self) -> List[int]:
""" """
Return a list of duplicate rows. If track appears in rows 2, 3 and 4, return [3, 4] Return a list of duplicate rows. If track appears in rows 2, 3 and 4, return [3, 4]
@ -539,13 +518,6 @@ class PlaylistModel(QAbstractTableModel):
return self.playlist_rows[row_number] return self.playlist_rows[row_number]
def get_unplayed_rows(self) -> List[int]:
"""
Return a list of unplayed row numbers
"""
return [a.plr_rownum for a in self.playlist_rows.values() if not a.played]
def headerData( def headerData(
self, self,
section: int, section: int,
@ -703,7 +675,6 @@ class PlaylistModel(QAbstractTableModel):
self.refresh_data(session) self.refresh_data(session)
super().endInsertRows() super().endInsertRows()
self.row_order_changed(self.playlist_id)
self.invalidate_rows(list(range(plr.plr_rownum, len(self.playlist_rows)))) self.invalidate_rows(list(range(plr.plr_rownum, len(self.playlist_rows))))
return plr return plr
@ -802,63 +773,8 @@ class PlaylistModel(QAbstractTableModel):
self.refresh_data(session) self.refresh_data(session)
# Update display # Update display
self.signals.row_order_changed_signal.emit(self.playlist_id)
self.invalidate_rows(list(row_map.keys()))
def move_rows_between_playlists(
self, from_rows: List[int], to_row_number: int, to_playlist_id: int
) -> None:
"""
Move the playlist rows given to to_row and below of to_playlist.
"""
# Row removal must be wrapped in beginRemoveRows ..
# endRemoveRows and the row range must be contiguous. Process
# the highest rows first so the lower row numbers are unchanged
row_groups = self._reversed_contiguous_row_groups(from_rows)
next_to_row = to_row_number
# Prepare destination playlist for a reset
self.signals.begin_reset_model_signal.emit(to_playlist_id)
with Session() as session:
# Make room in destination playlist
max_destination_row_number = PlaylistRows.get_last_used_row(
session, to_playlist_id
)
if (
max_destination_row_number
and to_row_number <= max_destination_row_number
):
PlaylistRows.move_rows_down(
session, to_playlist_id, to_row_number, len(from_rows)
)
for row_group in row_groups:
super().beginRemoveRows(QModelIndex(), min(row_group), max(row_group))
for plr in PlaylistRows.plrids_to_plrs(
session,
self.playlist_id,
[self.playlist_rows[a].plrid for a in row_group],
):
if plr.id == track_sequence.now.plr_id:
# Don't move current track
continue
plr.playlist_id = to_playlist_id
plr.plr_rownum = next_to_row
next_to_row += 1
self.refresh_data(session)
super().endRemoveRows()
# We need to remove gaps in row numbers after tracks have
# moved.
PlaylistRows.fixup_rownumbers(session, self.playlist_id)
self.refresh_data(session)
# Reset of model must come after session has been closed
self.signals.row_order_changed_signal.emit(self.playlist_id)
self.signals.row_order_changed_signal.emit(to_playlist_id)
self.signals.end_reset_model_signal.emit(to_playlist_id)
self.update_track_times() self.update_track_times()
self.invalidate_rows(list(row_map.keys()))
def open_in_audacity(self, row_number: int) -> None: def open_in_audacity(self, row_number: int) -> None:
""" """
@ -935,63 +851,11 @@ class PlaylistModel(QAbstractTableModel):
self.refresh_row(session, row_number) self.refresh_row(session, row_number)
self.invalidate_row(row_number) self.invalidate_row(row_number)
def _reversed_contiguous_row_groups(
self, row_numbers: List[int]
) -> List[List[int]]:
"""
Take the list of row numbers and split into groups of contiguous rows. Return as a list
of lists with the highest row numbers first.
Example:
input [2, 3, 4, 5, 7, 9, 10, 13, 17, 20, 21]
return: [[20, 21], [17], [13], [9, 10], [7], [2, 3, 4, 5]]
"""
result: List[List[int]] = []
temp: List[int] = []
last_value = row_numbers[0] - 1
for idx in range(len(row_numbers)):
if row_numbers[idx] != last_value + 1:
result.append(temp)
temp = []
last_value = row_numbers[idx]
temp.append(last_value)
if temp:
result.append(temp)
result.reverse()
return result
def rowCount(self, index: QModelIndex = QModelIndex()) -> int: def rowCount(self, index: QModelIndex = QModelIndex()) -> int:
"""Standard function for view""" """Standard function for view"""
return len(self.playlist_rows) return len(self.playlist_rows)
def row_order_changed(self, playlist_id: int) -> None:
"""
Signal handler for when row ordering has changed
"""
if playlist_id != self.playlist_id:
return
with Session() as session:
if track_sequence.next.plr_rownum:
next_plr = session.get(PlaylistRows, track_sequence.next.plr_rownum)
if next_plr:
track_sequence.next.plr_rownum = next_plr.plr_rownum
if track_sequence.now.plr_rownum:
now_plr = session.get(PlaylistRows, track_sequence.now.plr_rownum)
if now_plr:
track_sequence.now.plr_rownum = now_plr.plr_rownum
if track_sequence.previous.plr_rownum:
previous_plr = session.get(PlaylistRows, track_sequence.previous.plr_rownum)
if previous_plr:
track_sequence.previous.plr_rownum = previous_plr.plr_rownum
self.update_track_times()
def selection_is_sortable(self, row_numbers: List[int]) -> bool: def selection_is_sortable(self, row_numbers: List[int]) -> bool:
""" """
Return True if the selection is sortable. That means: Return True if the selection is sortable. That means:

View File

@ -7,7 +7,6 @@ import threading
import obsws_python as obs # type: ignore import obsws_python as obs # type: ignore
from datetime import datetime, timedelta from datetime import datetime, timedelta
from pprint import pprint
from typing import Any, Callable, cast, List, Optional, Tuple, TYPE_CHECKING from typing import Any, Callable, cast, List, Optional, Tuple, TYPE_CHECKING
from PyQt6.QtCore import ( from PyQt6.QtCore import (
@ -542,7 +541,7 @@ class PlaylistTab(QTableView):
# Mark unplayed # Mark unplayed
if track_row and model.is_unplayed_row(row_number): if track_row and model.is_unplayed_row(row_number):
self._add_context_menu( self._add_context_menu(
"Mark unplayed", lambda: self._mark_as_unplayed(self.get_selected_rows()) "Mark unplayed", lambda: self._mark_as_unplayed(self._get_selected_rows())
) )
# Unmark as next # Unmark as next
@ -558,22 +557,22 @@ class PlaylistTab(QTableView):
sort_menu = self.menu.addMenu("Sort") sort_menu = self.menu.addMenu("Sort")
self._add_context_menu( self._add_context_menu(
"by title", "by title",
lambda: model.sort_by_title(self.get_selected_rows()), lambda: model.sort_by_title(self._get_selected_rows()),
parent_menu=sort_menu, parent_menu=sort_menu,
) )
self._add_context_menu( self._add_context_menu(
"by artist", "by artist",
lambda: model.sort_by_artist(self.get_selected_rows()), lambda: model.sort_by_artist(self._get_selected_rows()),
parent_menu=sort_menu, parent_menu=sort_menu,
) )
self._add_context_menu( self._add_context_menu(
"by duration", "by duration",
lambda: model.sort_by_duration(self.get_selected_rows()), lambda: model.sort_by_duration(self._get_selected_rows()),
parent_menu=sort_menu, parent_menu=sort_menu,
) )
self._add_context_menu( self._add_context_menu(
"by last played", "by last played",
lambda: model.sort_by_lastplayed(self.get_selected_rows()), lambda: model.sort_by_lastplayed(self._get_selected_rows()),
parent_menu=sort_menu, parent_menu=sort_menu,
) )
@ -653,7 +652,7 @@ class PlaylistTab(QTableView):
- Pass to model to do the deed - Pass to model to do the deed
""" """
rows_to_delete = self.get_selected_rows() rows_to_delete = self._get_selected_rows()
row_count = len(rows_to_delete) row_count = len(rows_to_delete)
if row_count < 1: if row_count < 1:
return return
@ -664,9 +663,9 @@ class PlaylistTab(QTableView):
return return
model = cast(PlaylistModel, self.model()) model = cast(PlaylistModel, self.model())
model.delete_rows(self.get_selected_rows()) model.delete_rows(self._get_selected_rows())
def get_selected_rows(self) -> List[int]: def _get_selected_rows(self) -> List[int]:
"""Return a list of selected row numbers sorted by row""" """Return a list of selected row numbers sorted by row"""
# Use a set to deduplicate result (a selected row will have all # Use a set to deduplicate result (a selected row will have all
@ -940,13 +939,13 @@ class PlaylistTab(QTableView):
Toggle drag behaviour according to whether rows are selected Toggle drag behaviour according to whether rows are selected
""" """
selected_rows = self.get_selected_rows() selected_rows = self._get_selected_rows()
# If no rows are selected, we have nothing to do # If no rows are selected, we have nothing to do
if len(selected_rows) == 0: if len(selected_rows) == 0:
self.musicmuster.lblSumPlaytime.setText("") self.musicmuster.lblSumPlaytime.setText("")
else: else:
model = cast(PlaylistModel, self.model()) model = cast(PlaylistModel, self.model())
selected_duration = model.get_rows_duration(self.get_selected_rows()) selected_duration = model.get_rows_duration(self._get_selected_rows())
if selected_duration > 0: if selected_duration > 0:
self.musicmuster.lblSumPlaytime.setText( self.musicmuster.lblSumPlaytime.setText(
f"Selected duration: {ms_to_mmss(selected_duration)}" f"Selected duration: {ms_to_mmss(selected_duration)}"

View File

@ -131,12 +131,12 @@ def test_playlist_open_and_close(session):
assert len(Playlists.get_open(session)) == 0 assert len(Playlists.get_open(session)) == 0
assert len(Playlists.get_closed(session)) == 1 assert len(Playlists.get_closed(session)) == 1
playlist.mark_open() playlist.mark_open(session, tab_index=0)
assert len(Playlists.get_open(session)) == 1 assert len(Playlists.get_open(session)) == 1
assert len(Playlists.get_closed(session)) == 0 assert len(Playlists.get_closed(session)) == 0
playlist.close() playlist.close(session)
assert len(Playlists.get_open(session)) == 0 assert len(Playlists.get_open(session)) == 0
assert len(Playlists.get_closed(session)) == 1 assert len(Playlists.get_closed(session)) == 1

View File

@ -1,11 +1,8 @@
from pprint import pprint
from typing import Optional
from app.models import ( from app.models import (
Playlists, Playlists,
Tracks, Tracks,
) )
from PyQt6.QtCore import Qt, QModelIndex from PyQt6.QtCore import Qt
from app.helpers import get_file_metadata from app.helpers import get_file_metadata
from app import playlistmodel from app import playlistmodel
@ -22,8 +19,8 @@ test_tracks = [
] ]
def create_model_with_tracks(session: scoped_session, name: Optional[str] = None) -> "playlistmodel.PlaylistModel": def create_model_with_tracks(session: scoped_session) -> "playlistmodel.PlaylistModel":
playlist = Playlists(session, name or "test playlist") playlist = Playlists(session, "test playlist")
model = playlistmodel.PlaylistModel(playlist.id) model = playlistmodel.PlaylistModel(playlist.id)
for row in range(len(test_tracks)): for row in range(len(test_tracks)):
@ -37,9 +34,9 @@ def create_model_with_tracks(session: scoped_session, name: Optional[str] = None
def create_model_with_playlist_rows( def create_model_with_playlist_rows(
session: scoped_session, rows: int, name: Optional[str] = None session: scoped_session, rows: int
) -> "playlistmodel.PlaylistModel": ) -> "playlistmodel.PlaylistModel":
playlist = Playlists(session, name or "test playlist") playlist = Playlists(session, "test playlist")
# Create a model # Create a model
model = playlistmodel.PlaylistModel(playlist.id) model = playlistmodel.PlaylistModel(playlist.id)
for row in range(rows): for row in range(rows):
@ -271,97 +268,6 @@ def test_insert_track_new_playlist(monkeypatch, session):
) )
def test_reverse_row_groups_one_row(monkeypatch, session):
monkeypatch.setattr(playlistmodel, "Session", session)
rows_to_move = [3]
model_src = create_model_with_playlist_rows(session, 5, name="source")
result = model_src._reversed_contiguous_row_groups(rows_to_move)
assert len(result) == 1
assert result[0] == [3]
def test_reverse_row_groups_multiple_row(monkeypatch, session):
monkeypatch.setattr(playlistmodel, "Session", session)
rows_to_move = [2, 3, 4, 5, 7, 9, 10, 13, 17, 20, 21]
model_src = create_model_with_playlist_rows(session, 5, name="source")
result = model_src._reversed_contiguous_row_groups(rows_to_move)
assert result == [[20, 21], [17], [13], [9, 10], [7], [2, 3, 4, 5]]
def test_move_one_row_between_playlists_to_end(monkeypatch, session):
monkeypatch.setattr(playlistmodel, "Session", session)
create_rowcount = 5
from_rows = [3]
to_row = create_rowcount
model_src = create_model_with_playlist_rows(session, create_rowcount, name="source")
model_dst = create_model_with_playlist_rows(session, create_rowcount, name="destination")
model_src.move_rows_between_playlists(from_rows, to_row, model_dst.playlist_id)
model_dst.refresh_data(session)
assert len(model_src.playlist_rows) == create_rowcount - len(from_rows)
assert len(model_dst.playlist_rows) == create_rowcount + len(from_rows)
assert sorted([a.plr_rownum for a in model_src.playlist_rows.values()]) == list(
range(len(model_src.playlist_rows))
)
def test_move_one_row_between_playlists_to_middle(monkeypatch, session):
monkeypatch.setattr(playlistmodel, "Session", session)
create_rowcount = 5
from_rows = [3]
to_row = 2
model_src = create_model_with_playlist_rows(session, create_rowcount, name="source")
model_dst = create_model_with_playlist_rows(session, create_rowcount, name="destination")
model_src.move_rows_between_playlists(from_rows, to_row, model_dst.playlist_id)
model_dst.refresh_data(session)
# Check the rows of the destination model
row_notes = []
for row_number in range(model_dst.rowCount()):
index = model_dst.index(row_number, playlistmodel.Col.TITLE.value, QModelIndex())
row_notes.append(model_dst.data(index, Qt.ItemDataRole.EditRole).value())
assert len(model_src.playlist_rows) == create_rowcount - len(from_rows)
assert len(model_dst.playlist_rows) == create_rowcount + len(from_rows)
assert [int(a) for a in row_notes] == [0, 1, 3, 2, 3, 4]
def test_move_multiple_rows_between_playlists_to_end(monkeypatch, session):
monkeypatch.setattr(playlistmodel, "Session", session)
create_rowcount = 5
from_rows = [1, 3, 4]
to_row = 2
model_src = create_model_with_playlist_rows(session, create_rowcount, name="source")
model_dst = create_model_with_playlist_rows(session, create_rowcount, name="destination")
model_src.move_rows_between_playlists(from_rows, to_row, model_dst.playlist_id)
model_dst.refresh_data(session)
# Check the rows of the destination model
row_notes = []
for row_number in range(model_dst.rowCount()):
index = model_dst.index(row_number, playlistmodel.Col.TITLE.value, QModelIndex())
row_notes.append(model_dst.data(index, Qt.ItemDataRole.EditRole).value())
assert len(model_src.playlist_rows) == create_rowcount - len(from_rows)
assert len(model_dst.playlist_rows) == create_rowcount + len(from_rows)
assert [int(a) for a in row_notes] == [0, 1, 3, 4, 1, 2, 3, 4]
# def test_edit_header(monkeypatch, session): # edit header row in middle of playlist # def test_edit_header(monkeypatch, session): # edit header row in middle of playlist
# monkeypatch.setattr(playlistmodel, "Session", session) # monkeypatch.setattr(playlistmodel, "Session", session)