diff --git a/app/repository.py b/app/repository.py index aae57a4..9e619e4 100644 --- a/app/repository.py +++ b/app/repository.py @@ -316,43 +316,55 @@ def move_rows_to_playlist( Move rows between playlists. """ + log.debug( + f"move_rows_to_playlist({from_rows=}, {from_playlist_id=}, {to_row=}, {to_playlist_id=})" + ) + with db.Session() as session: - # Prepare desination playlist - # Find last used row - last_row = session.execute( - select(func.max(PlaylistRows.row_number)).where( - PlaylistRows.playlist_id == to_playlist_id - ) - ).scalar_one() - if last_row is None: - last_row = -1 - # Make room in destination - if to_row <= last_row: - _move_rows(session, to_playlist_id, to_row, len(from_rows)) - # Move rows - row_offset = to_row - min(from_rows) - stmt = ( + # Sanity check row numbers + _check_playlist_integrity(session, from_playlist_id, fix=False) + _check_playlist_integrity(session, to_playlist_id, fix=False) + + # Check there are no playlist rows with playlist_id == PENDING_MOVE + pending_move_rows = get_playlist_rows(Config.PLAYLIST_PENDING_MOVE) + if pending_move_rows: + raise ApplicationError(f"move_rows_to_playlist: {pending_move_rows=}") + + # Put rows to be moved into PENDING_MOVE playlist + session.execute( update(PlaylistRows) .where( PlaylistRows.playlist_id == from_playlist_id, PlaylistRows.row_number.in_(from_rows), ) - .values( - playlist_id=to_playlist_id, - row_number=PlaylistRows.row_number + row_offset, - ) + .values(playlist_id=Config.PLAYLIST_PENDING_MOVE) ) - session.execute(stmt) - # Remove gaps in source - _move_rows( - session=session, - playlist_id=from_playlist_id, - starting_row=max(from_rows) + 1, - move_by=(len(from_rows) * -1), - ) - # Commit changes session.commit() - # Sanity check + + # Resequence remaining row numbers + _check_playlist_integrity(session, from_playlist_id, fix=True) + + # Make space for moved rows. + _move_rows(session, to_playlist_id, to_row, len(from_rows)) + + # Move the PENDING_MOVE rows back and fixup row numbers + update_list: list[dict[str, int]] = [] + next_row = to_row + # PLAYLIST_PENDING_MOVE may have gaps so don't check it + for row_to_move in get_playlist_rows( + Config.PLAYLIST_PENDING_MOVE, check_playlist_itegrity=False + ): + update_list.append( + {"id": row_to_move.playlistrow_id, "row_number": next_row} + ) + update_list.append( + {"id": row_to_move.playlistrow_id, "playlist_id": to_playlist_id} + ) + next_row += 1 + session.execute(update(PlaylistRows), update_list) + session.commit() + + # Sanity check row numbers _check_playlist_integrity(session, from_playlist_id, fix=False) _check_playlist_integrity(session, to_playlist_id, fix=False) @@ -416,9 +428,16 @@ def move_rows_within_playlist( # Move the PENDING_MOVE rows back and fixup row numbers update_list: list[dict[str, int]] = [] next_row = space_row - for row_to_move in get_playlist_rows(Config.PLAYLIST_PENDING_MOVE): - update_list.append({"id": row_to_move.playlistrow_id, "row_number": next_row}) - update_list.append({"id": row_to_move.playlistrow_id, "playlist_id": playlist_id}) + # PLAYLIST_PENDING_MOVE may have gaps so don't check it + for row_to_move in get_playlist_rows( + Config.PLAYLIST_PENDING_MOVE, check_playlist_itegrity=False + ): + update_list.append( + {"id": row_to_move.playlistrow_id, "row_number": next_row} + ) + update_list.append( + {"id": row_to_move.playlistrow_id, "playlist_id": playlist_id} + ) next_row += 1 session.execute(update(PlaylistRows), update_list) session.commit() @@ -554,7 +573,9 @@ def get_playlist_row(playlistrow_id: int) -> PlaylistRowDTO | None: return dto -def get_playlist_rows(playlist_id: int) -> list[PlaylistRowDTO]: +def get_playlist_rows( + playlist_id: int, check_playlist_itegrity=True +) -> list[PlaylistRowDTO]: # Alias PlaydatesTable for subquery LatestPlaydate = aliased(Playdates) @@ -597,7 +618,10 @@ def get_playlist_rows(playlist_id: int) -> list[PlaylistRowDTO]: results = session.execute(stmt).all() # Sanity check # TODO: would be good to be confident at removing this - _check_playlist_integrity(session=session, playlist_rows=results, fix=False) + if check_playlist_itegrity: + _check_playlist_integrity( + session=session, playlist_id=playlist_id, fix=False + ) dto_list = [] for row in results: diff --git a/tests/test_repository.py b/tests/test_repository.py index 89afd1e..16521e8 100644 --- a/tests/test_repository.py +++ b/tests/test_repository.py @@ -241,3 +241,34 @@ class MyTestCase(unittest.TestCase): for row in repository.get_playlist_rows(playlist.playlist_id): new_order.append(int(row.note)) assert new_order == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + + def test_move_rows_to_playlist(self): + number_of_rows = 11 + rows_to_move = [2, 4, 6] + to_row = 5 + + (playlist_src, model_src) = self.create_rows("src playlist", number_of_rows) + (playlist_dst, model_dst) = self.create_rows("dst playlist", number_of_rows) + + repository.move_rows_to_playlist( + rows_to_move, playlist_src.playlist_id, to_row, playlist_dst.playlist_id + ) + + # Check we have all rows and plr_rownums are correct + new_order_src = [] + for row in repository.get_playlist_rows(playlist_src.playlist_id): + new_order_src.append(int(row.note)) + assert new_order_src == [0, 1, 3, 5, 7, 8, 9, 10] + new_order_dst = [] + for row in repository.get_playlist_rows(playlist_dst.playlist_id): + new_order_dst.append(int(row.note)) + assert new_order_dst == [0, 1, 2, 3, 4, 2, 4, 6, 5, 6, 7, 8, 9, 10] + + def test_remove_rows(self): + pass + + def test_get_playlist_by_id(self): + pass + + def test_settings(self): + pass