Compare commits

...

4 Commits

Author SHA1 Message Date
Keith Edmunds
8e48d63ebb WIP: queries management
Menus and management working. Wrong tracks showing up in queries.
2025-03-02 19:14:53 +00:00
Keith Edmunds
aa6ab03555 Make manage queries and manage templates into classes 2025-02-28 11:25:29 +00:00
Keith Edmunds
90d72464cb Clean up handling of separators in dynamic menu 2025-02-27 08:13:29 +00:00
Keith Edmunds
82e707a6f6 Make filter field in queries table non-nullable 2025-02-27 08:12:48 +00:00
9 changed files with 669 additions and 257 deletions

View File

@ -80,6 +80,7 @@ class FileErrors(NamedTuple):
@dataclass
class Filter:
version: int = 1
path_type: str = "contains"
path: Optional[str] = None
last_played_number: Optional[int] = None

View File

@ -49,6 +49,18 @@ class Config(object):
FADEOUT_DB = -10
FADEOUT_SECONDS = 5
FADEOUT_STEPS_PER_SECOND = 5
FILTER_DURATION_LONGER = "longer than"
FILTER_DURATION_MINUTES = "minutes"
FILTER_DURATION_SECONDS = "seconds"
FILTER_DURATION_SHORTER = "shorter than"
FILTER_PATH_CONTAINS = "contains"
FILTER_PATH_EXCLUDING = "excluding"
FILTER_PLAYED_BEFORE = "before"
FILTER_PLAYED_DAYS = "days"
FILTER_PLAYED_MONTHS = "months"
FILTER_PLAYED_NEVER = "never"
FILTER_PLAYED_WEEKS = "weeks"
FILTER_PLAYED_YEARS = "years"
FUZZYMATCH_MINIMUM_LIST = 60.0
FUZZYMATCH_MINIMUM_SELECT_ARTIST = 80.0
FUZZYMATCH_MINIMUM_SELECT_TITLE = 80.0

View File

@ -154,7 +154,7 @@ class QueriesTable(Model):
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(String(128), nullable=False)
_filter_data: Mapped[dict | None] = mapped_column("filter_data", JSONEncodedDict, nullable=True)
_filter_data: Mapped[dict | None] = mapped_column("filter_data", JSONEncodedDict, nullable=False)
favourite: Mapped[bool] = mapped_column(Boolean, nullable=False, index=False, default=False)
def _get_filter(self) -> Filter:

View File

@ -10,7 +10,7 @@ import ssl
import tempfile
# PyQt imports
from PyQt6.QtWidgets import QMainWindow, QMessageBox, QWidget
from PyQt6.QtWidgets import QInputDialog, QMainWindow, QMessageBox, QWidget
# Third party imports
from mutagen.flac import FLAC # type: ignore
@ -150,6 +150,23 @@ def get_audio_metadata(filepath: str) -> AudioMetadata:
)
def get_name(prompt: str, default: str = "") -> str | None:
"""Get a name from the user"""
dlg = QInputDialog()
dlg.setInputMode(QInputDialog.InputMode.TextInput)
dlg.setLabelText(prompt)
while True:
if default:
dlg.setTextValue(default)
dlg.resize(500, 100)
ok = dlg.exec()
if ok:
return dlg.textValue()
return None
def get_relative_date(
past_date: Optional[dt.datetime], reference_date: Optional[dt.datetime] = None
) -> str:

View File

@ -4,10 +4,10 @@ menus:
- text: "Save as Template"
handler: "save_as_template"
- text: "Manage Templates"
handler: "manage_templates"
handler: "manage_templates_wrapper"
- separator: true
- text: "Manage Queries"
handler: "manage_queries"
handler: "manage_queries_wrapper"
- separator: true
- text: "Exit"
handler: "close"

View File

@ -25,7 +25,7 @@ from sqlalchemy.orm.session import Session
from sqlalchemy.engine.row import RowMapping
# App imports
from classes import ApplicationError
from classes import ApplicationError, Filter
from config import Config
from dbmanager import DatabaseManager
import dbtables
@ -610,11 +610,21 @@ class Queries(dbtables.QueriesTable):
session.commit()
@classmethod
def get_all_queries(cls, session: Session) -> Sequence["Queries"]:
def get_all(cls, session: Session) -> Sequence["Queries"]:
"""Returns a list of all queries ordered by name"""
return session.scalars(select(cls).order_by(cls.name)).all()
@classmethod
def get_favourites(cls, session: Session) -> Sequence["Queries"]:
"""Returns a list of favourite queries ordered by name"""
return session.scalars(
select(cls)
.where(cls.favourite.is_(True))
.order_by(cls.name)
).all()
class Settings(dbtables.SettingsTable):
def __init__(self, session: Session, name: str) -> None:
@ -700,6 +710,40 @@ class Tracks(dbtables.TracksTable):
.all()
)
@classmethod
def get_filtered_tracks(cls, session: Session, filter: Filter) -> Sequence["Tracks"]:
"""
Return tracks matching filter
"""
query = select(cls)
if filter.path:
if filter.path_type == "contains":
query = query.where(cls.path.ilike(f"%{filter.path}%"))
elif filter.path_type == "excluding":
query = query.where(cls.path.notilike(f"%{filter.path}%"))
else:
raise ApplicationError(f"Can't process filter path ({filter=})")
# TODO
# if last_played_number:
# need group_by track_id and having max/min lastplayed gt/lt, etc
seconds_duration = filter.duration_number
if filter.duration_unit == Config.FILTER_DURATION_MINUTES:
seconds_duration *= 60
elif filter.duration_unit != Config.FILTER_DURATION_SECONDS:
raise ApplicationError(f"Can't process filter duration ({filter=})")
if filter.duration_type == Config.FILTER_DURATION_LONGER:
query = query.where(cls.duration >= seconds_duration)
elif filter.duration_unit == Config.FILTER_DURATION_SHORTER:
query = query.where(cls.duration <= seconds_duration)
else:
raise ApplicationError(f"Can't process filter duration type ({filter=})")
records = session.scalars(
query).unique().all()
return records
@classmethod
def get_by_path(cls, session: Session, path: str) -> Optional["Tracks"]:
"""

File diff suppressed because it is too large Load Diff

View File

@ -38,7 +38,7 @@ from helpers import (
show_warning,
)
from log import log
from models import db, Playdates
from models import db, Playdates, Tracks
from music_manager import RowAndTrack
@ -228,20 +228,20 @@ class QuerylistModel(QAbstractTableModel):
row = 0
try:
results = Tracks.get_filtered(self.session, self.filter)
results = Tracks.get_filtered_tracks(self.session, self.filter)
for result in results:
if hasattr(result, "lastplayed"):
lastplayed = result["lastplayed"]
else:
lastplayed = None
queryrow = QueryRow(
artist=result["artist"],
bitrate=result["bitrate"],
duration=result["duration"],
artist=result.artist,
bitrate=result.bitrate or 0,
duration=result.duration,
lastplayed=lastplayed,
path=result["path"],
title=result["title"],
track_id=result["id"],
path=result.path,
title=result.title,
track_id=result.id,
)
self.querylist_rows[row] = queryrow

View File

@ -33,7 +33,7 @@ def upgrade_() -> None:
op.create_table('queries',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('name', sa.String(length=128), nullable=False),
sa.Column('filter_data', dbtables.JSONEncodedDict(), nullable=True),
sa.Column('filter_data', dbtables.JSONEncodedDict(), nullable=False),
sa.Column('favourite', sa.Boolean(), nullable=False),
sa.PrimaryKeyConstraint('id')
)