From ac685426d9e185574ffd8e402d2e7da67e35c638 Mon Sep 17 00:00:00 2001 From: Keith Edmunds Date: Sat, 12 Apr 2025 11:15:54 +0100 Subject: [PATCH] Implement NoteColoursDTO --- app/classes.py | 13 +++++++++++ app/ds.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/app/classes.py b/app/classes.py index 2da2378..d41e12f 100644 --- a/app/classes.py +++ b/app/classes.py @@ -95,6 +95,19 @@ class PlaydatesDTO(TrackDTO): lastplayed: dt.datetime +@dataclass +class NoteColoursDTO: + notecolour_id: int + substring: str + colour: str + enabled: bool = True + foreground: str | None = None + is_regex: bool = False + is_casesensitive: bool = False + order: int | None = None + strip_substring: bool = True + + class ApplicationError(Exception): """ Custom exception diff --git a/app/ds.py b/app/ds.py index 6cd29b4..a9c6c3e 100644 --- a/app/ds.py +++ b/app/ds.py @@ -5,6 +5,8 @@ import re # PyQt imports # Third party imports +from dogpile.cache import make_region +from dogpile.cache.api import NO_VALUE from sqlalchemy import ( delete, func, @@ -19,6 +21,7 @@ from sqlalchemy.sql.elements import BinaryExpression, ColumnElement from classes import ( ApplicationError, Filter, + NoteColoursDTO, PlaydatesDTO, PlaylistDTO, PlaylistRowDTO, @@ -39,6 +42,13 @@ from models import ( ) +# Configure the dogpile cache region +cache_region = make_region().configure( + 'dogpile.cache.memory', # Use in-memory caching for now (switch to Redis if needed) + expiration_time=600 # Cache expires after 10 minutes +) + + # Helper functions @log_call def _remove_substring_case_insensitive(parent_string: str, substring: str) -> str: @@ -68,13 +78,53 @@ def _remove_substring_case_insensitive(parent_string: str, substring: str) -> st # Notecolour functions -def _get_colour_record(text: str) -> tuple[NoteColours | None, str]: +def _all_notecolours(session: Session) -> list[NoteColoursDTO]: + """ + Return all notecolour records + """ + + cache_key = "note_colours_all" + cached_result = cache_region.get(cache_key) + + if cached_result is not NO_VALUE: + return cached_result + + # Query the database + records = session.scalars( + select(NoteColours) + .where( + NoteColours.enabled.is_(True), + ) + .order_by(NoteColours.order) + ).all() + + results: list[NoteColoursDTO] = [] + for record in records: + result = NoteColoursDTO( + notecolour_id=record.id, + substring=record.substring, + colour=record.colour, + enabled=record.enabled, + foreground=record.foreground, + is_regex=record.is_regex, + is_casesensitive=record.is_casesensitive, + order=record.order, + strip_substring=record.strip_substring, + ) + results.append(result) + + cache_region.set(cache_key, results) + + return results + + +def _get_colour_record(text: str) -> tuple[NoteColoursDTO | None, str]: """ Parse text and return first matching colour record or None """ with db.Session() as session: - for rec in NoteColours.get_all(session): + for rec in _all_notecolours(session): if rec.is_regex: flags = re.UNICODE if not rec.is_casesensitive: @@ -751,7 +801,7 @@ def copy_playlist(src_id: int, dst_id: int) -> None: with db.Session() as session: src_rows = session.scalars( - select(PlaylistRows).filter(PlaylistRows.playlist_id == src_id) + select(PlaylistRows).where(PlaylistRows.playlist_id == src_id) ).all() for plr in src_rows: @@ -763,6 +813,8 @@ def copy_playlist(src_id: int, dst_id: int) -> None: track_id=plr.track_id, ) + session.commit() + def playlist_row_count(playlist_id: int) -> int: """