Select from query working (may need tidying)

This commit is contained in:
Keith Edmunds 2025-03-04 10:32:11 +00:00
parent 8e48d63ebb
commit 67c48f5022
5 changed files with 64 additions and 19 deletions

View File

@ -84,7 +84,7 @@ class Filter:
path_type: str = "contains"
path: Optional[str] = None
last_played_number: Optional[int] = None
last_played_type: str = "before"
last_played_comparator: str = "before"
last_played_unit: str = "years"
duration_type: str = "longer than"
duration_number: int = 0

View File

@ -39,6 +39,7 @@ class Config(object):
DISPLAY_SQL = False
DO_NOT_IMPORT = "Do not import"
ENGINE_OPTIONS = dict(pool_pre_ping=True)
# ENGINE_OPTIONS = dict(pool_pre_ping=True, echo=True)
EPOCH = dt.datetime(1970, 1, 1)
ERRORS_FROM = ["noreply@midnighthax.com"]
ERRORS_TO = ["kae@midnighthax.com"]
@ -55,10 +56,11 @@ class Config(object):
FILTER_DURATION_SHORTER = "shorter than"
FILTER_PATH_CONTAINS = "contains"
FILTER_PATH_EXCLUDING = "excluding"
FILTER_PLAYED_BEFORE = "before"
FILTER_PLAYED_COMPARATOR_ANYTIME = "Any time"
FILTER_PLAYED_COMPARATOR_BEFORE = "before"
FILTER_PLAYED_COMPARATOR_NEVER = "never"
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

View File

@ -599,7 +599,11 @@ class PlaylistRows(dbtables.PlaylistRowsTable):
class Queries(dbtables.QueriesTable):
def __init__(
self, session: Session, name: str, filter: dbtables.Filter, favourite: bool = False
self,
session: Session,
name: str,
filter: dbtables.Filter,
favourite: bool = False,
) -> None:
"""Create new query"""
@ -620,9 +624,7 @@ class Queries(dbtables.QueriesTable):
"""Returns a list of favourite queries ordered by name"""
return session.scalars(
select(cls)
.where(cls.favourite.is_(True))
.order_by(cls.name)
select(cls).where(cls.favourite.is_(True)).order_by(cls.name)
).all()
@ -711,12 +713,16 @@ class Tracks(dbtables.TracksTable):
)
@classmethod
def get_filtered_tracks(cls, session: Session, filter: Filter) -> Sequence["Tracks"]:
def get_filtered_tracks(
cls, session: Session, filter: Filter
) -> Sequence["Tracks"]:
"""
Return tracks matching filter
"""
query = select(cls)
# Path specification
if filter.path:
if filter.path_type == "contains":
query = query.where(cls.path.ilike(f"%{filter.path}%"))
@ -724,14 +730,14 @@ class Tracks(dbtables.TracksTable):
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
# Duration specification
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:
@ -739,8 +745,41 @@ class Tracks(dbtables.TracksTable):
else:
raise ApplicationError(f"Can't process filter duration type ({filter=})")
records = session.scalars(
query).unique().all()
# Last played specification
if (
filter.last_played_number
and filter.last_played_comparator != Config.FILTER_PLAYED_COMPARATOR_ANYTIME
):
now = dt.datetime.now()
since = now - dt.timedelta(days=365 * filter.last_played_number)
if filter.duration_unit == Config.FILTER_PLAYED_DAYS:
since = now - dt.timedelta(days=filter.last_played_number)
elif filter.duration_unit == Config.FILTER_PLAYED_WEEKS:
since = now - dt.timedelta(days=7 * filter.last_played_number)
if filter.duration_unit == Config.FILTER_PLAYED_MONTHS:
since = now - dt.timedelta(days=30 * filter.last_played_number)
if filter.last_played_comparator == Config.FILTER_PLAYED_COMPARATOR_NEVER:
# Select tracks that have never been played
query = query.outerjoin(Playdates, cls.id == Playdates.track_id).where(
Playdates.id.is_(None)
)
elif (
filter.last_played_comparator == Config.FILTER_PLAYED_COMPARATOR_BEFORE
):
subquery = (
select(
Playdates.track_id,
func.max(Playdates.lastplayed).label("max_last_played"),
)
.group_by(Playdates.track_id)
.subquery()
)
query = query.join(subquery, Tracks.id == subquery.c.track_id).where(
subquery.c.max_last_played < since
)
records = session.scalars(query).unique().all()
return records

View File

@ -48,7 +48,6 @@ from PyQt6.QtWidgets import (
QMenu,
QMessageBox,
QPushButton,
QSizePolicy,
QSpinBox,
QTableView,
QTableWidget,
@ -215,10 +214,14 @@ class FilterDialog(QDialog):
last_played_label = QLabel("Last played")
self.last_played_combo = QComboBox()
self.last_played_combo.addItems(
[Config.FILTER_PLAYED_BEFORE, Config.FILTER_PLAYED_NEVER]
[
Config.FILTER_PLAYED_COMPARATOR_BEFORE,
Config.FILTER_PLAYED_COMPARATOR_NEVER,
Config.FILTER_PLAYED_COMPARATOR_ANYTIME,
]
)
for idx in range(self.last_played_combo.count()):
if self.last_played_combo.itemText(idx) == filter.last_played_type:
if self.last_played_combo.itemText(idx) == filter.last_played_comparator:
self.last_played_combo.setCurrentIndex(idx)
break
@ -316,7 +319,7 @@ class FilterDialog(QDialog):
self.filter.path_type = self.path_combo.currentText()
self.filter.path = self.path_text.text()
self.filter.last_played_number = self.last_played_spinbox.value()
self.filter.last_played_type = self.last_played_combo.currentText()
self.filter.last_played_comparator = self.last_played_combo.currentText()
self.filter.last_played_unit = self.last_played_unit.currentText()
self.filter.duration_type = self.duration_combo.currentText()
self.filter.duration_number = self.duration_spinbox.value()

View File

@ -230,8 +230,9 @@ class QuerylistModel(QAbstractTableModel):
try:
results = Tracks.get_filtered_tracks(self.session, self.filter)
for result in results:
if hasattr(result, "lastplayed"):
lastplayed = result["lastplayed"]
if hasattr(result, "playdates"):
pds = result.playdates
lastplayed = max([a.lastplayed for a in pds])
else:
lastplayed = None
queryrow = QueryRow(