Getting the playlist processing logic organized and implemented.
Signed-off-by: Cliff Hill <xlorep@darkhelm.org>
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
"""Contains all SQL code here."""
|
||||
|
||||
from collections import Counter
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
from typing import AsyncGenerator
|
||||
|
||||
import env
|
||||
import models
|
||||
from aiologger import Logger
|
||||
from playlist import enums
|
||||
from playlist import env
|
||||
from playlist import models
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
@@ -110,9 +114,7 @@ async def update_statistics_timestamps(operation: str) -> None:
|
||||
async with async_session_maker() as session:
|
||||
try:
|
||||
async with session.begin():
|
||||
stats = await session.execute(
|
||||
select(models.Statistics).order_by(models.Statistics.id)
|
||||
)
|
||||
stats = await session.get(models.Statistics, 1)
|
||||
stats_obj = stats.scalars().first()
|
||||
|
||||
if operation == "start":
|
||||
@@ -214,32 +216,6 @@ async def remove_holiday_by_id(session: AsyncSession, holiday_id: int) -> None:
|
||||
await session.rollback()
|
||||
|
||||
|
||||
async def update_average_track_length(new_average: float) -> None:
|
||||
"""Update the average track length in the statistics model with the geometric mean.
|
||||
|
||||
Args:
|
||||
new_average (float): The new average track length to be set.
|
||||
"""
|
||||
async with async_session_maker() as session:
|
||||
try:
|
||||
async with session.begin():
|
||||
# Assuming there's only one statistics record, or you might need to handle this differently
|
||||
stats = await session.get(
|
||||
models.Statistics, 1
|
||||
) # Adjust based on how you access the statistics record
|
||||
if stats:
|
||||
stats.average_track_length = new_average
|
||||
await session.commit()
|
||||
else:
|
||||
# If no statistics entry exists, create one
|
||||
new_stats = models.Statistics(average_track_length=new_average)
|
||||
session.add(new_stats)
|
||||
await session.commit()
|
||||
except Exception as e:
|
||||
print(f"Failed to update the average track length: {e}")
|
||||
await session.rollback()
|
||||
|
||||
|
||||
async def update_statistics(
|
||||
new_average_duration: float,
|
||||
new_podcast_length: float,
|
||||
@@ -283,9 +259,7 @@ async def update_statistics(
|
||||
try:
|
||||
async with session.begin():
|
||||
# Assuming there's only one statistics record, or you might need to handle this differently
|
||||
stats = await session.get(
|
||||
models.Statistics, 1
|
||||
) # Adjust based on how you access the statistics record
|
||||
stats = await session.get(models.Statistics, 1)
|
||||
if stats:
|
||||
stats.average_track_length = new_average_duration
|
||||
stats.total_podcast_length = new_podcast_length
|
||||
@@ -328,3 +302,173 @@ async def update_statistics(
|
||||
except Exception as e:
|
||||
print(f"Failed to update the statistics model: {e}")
|
||||
await session.rollback()
|
||||
|
||||
|
||||
async def fetch_tracks_for_sublist(
|
||||
session: AsyncSession,
|
||||
is_category: bool,
|
||||
type_id: int,
|
||||
is_favorite: bool,
|
||||
sublist: enums.Sublist,
|
||||
limit: int,
|
||||
playlist_entries: set[int],
|
||||
) -> AsyncGenerator[models.Track, None]:
|
||||
"""Fetch tracks for a given sublist type within a category.
|
||||
|
||||
Args:
|
||||
session (AsyncSession): The database session.
|
||||
is_category (bool): True if a category, false if a holiday.
|
||||
type_id (int): Category or holiday ID for track filtering.
|
||||
is_favorite (bool): Flag indicating if only favorite tracks should be fetched.
|
||||
sublist (enums.Sublist): Type of sublist to fetch.
|
||||
limit (int): Number of tracks to fetch.
|
||||
playlist_entries (set[int]): The set of track IDs already in the playlist.
|
||||
|
||||
Returns:
|
||||
AsyncGenerator[models.Track, None]: Generates the tracks fitting the criteria.
|
||||
"""
|
||||
try:
|
||||
query = select(models.Track)
|
||||
if is_category:
|
||||
query = query.where(models.Track.category_id == type_id)
|
||||
else:
|
||||
query = query.join(models.Track.holidays).where(
|
||||
models.Holiday.id == type_id
|
||||
)
|
||||
|
||||
query = query.filter(models.Track.id.not_in(playlist_entries))
|
||||
|
||||
if is_favorite:
|
||||
query = query.filter(models.Track.rating == 5)
|
||||
|
||||
match sublist:
|
||||
case enums.Sublist.LEAST_RECENTLY_PLAYED:
|
||||
query = query.order_by(models.Track.last_played.asc())
|
||||
case enums.Sublist.LEAST_OFTEN_PLAYED:
|
||||
query = query.order_by(models.Track.play_count.asc())
|
||||
case enums.Sublist.LEAST_RECENTLY_ADDED:
|
||||
query = query.order_by(models.Track.date_added.asc())
|
||||
case enums.Sublist.RANDOM:
|
||||
query = query.order_by(func.random())
|
||||
case _:
|
||||
raise ValueError(f"Unknown sublist type: {sublist}")
|
||||
|
||||
query = query.limit(limit)
|
||||
result = await session.execute(query)
|
||||
for track in result.scalars():
|
||||
yield track
|
||||
except SQLAlchemyError as e:
|
||||
await logger.error(f"Database error occurred while fetching tracks: {e}")
|
||||
raise
|
||||
except Exception as e:
|
||||
await logger.error(f"Unexpected error occurred while fetching tracks: {e}")
|
||||
raise
|
||||
|
||||
|
||||
async def insert_into_playlist( # noqa: C901
|
||||
session: AsyncSession,
|
||||
is_category: bool,
|
||||
type_id: int,
|
||||
track_id: int,
|
||||
sublist: enums.Sublist,
|
||||
is_favorite: bool,
|
||||
) -> None:
|
||||
"""Insert a track into the playlist.
|
||||
|
||||
Args:
|
||||
session (AsyncSession): The database session.
|
||||
is_category (bool): True is a category, false is a holiday.
|
||||
type_id (int): Category or holiday ID associated with the playlist.
|
||||
track_id (int): Track ID to be added to the playlist.
|
||||
sublist (enums.Sublist): Sublist type under which the track is added.
|
||||
is_favorite (bool): Indicates if the track is added as a favorite.
|
||||
"""
|
||||
try:
|
||||
if is_category:
|
||||
playlist_track = models.PlaylistTrack(
|
||||
category_id=type_id,
|
||||
track_id=track_id,
|
||||
sublist_type=sublist,
|
||||
is_favorite=is_favorite,
|
||||
)
|
||||
else:
|
||||
playlist_track = models.PlaylistTrack(
|
||||
holiday_id=type_id,
|
||||
track_id=track_id,
|
||||
sublist_type=sublist,
|
||||
is_favorite=is_favorite,
|
||||
)
|
||||
session.add(playlist_track)
|
||||
await session.commit()
|
||||
except SQLAlchemyError as e:
|
||||
await logger.error(
|
||||
f"Database error occurred while inserting into playlist: {e}"
|
||||
)
|
||||
await session.rollback()
|
||||
raise
|
||||
except Exception as e:
|
||||
await logger.error(
|
||||
f"Unexpected error occurred while inserting into playlist: {e}"
|
||||
)
|
||||
await session.rollback()
|
||||
raise
|
||||
|
||||
async def gen_all_category_ids() -> AsyncGenerator[int, None, None]:
|
||||
"""Generate the current category ids from the database.
|
||||
|
||||
Yields:
|
||||
int: The category id.
|
||||
"""
|
||||
async with async_session_maker() as session:
|
||||
query = select(models.Category)
|
||||
result = session.execute(query)
|
||||
for row in result.scalars():
|
||||
yield row.id
|
||||
|
||||
async def gen_active_holiday_ids() -> AsyncGenerator[int, None, None]:
|
||||
"""Generate the current active holiday ids from the database.
|
||||
|
||||
Yields:
|
||||
int: The holiday id.
|
||||
"""
|
||||
async with async_session_maker() as session:
|
||||
query = select(models.Holiday).where(models.Holiday.is_active is True)
|
||||
result = session.execute(query)
|
||||
for row in result.scalars():
|
||||
yield row.id
|
||||
|
||||
async def get_existing_playlist_track_info() -> (
|
||||
tuple[set[int], dict[tuple[bool, int, bool, enums.Sublist], int]]
|
||||
):
|
||||
"""Get the existing playlist track information.
|
||||
|
||||
Returns:
|
||||
tuple[set[int], dict[tuple[bool, int, bool, enums.Sublist], int]]: Two pieces:
|
||||
First is a set of the ids of all of the tracks in the playlist. Second is a
|
||||
dict with the key being the combination of is_category, type_id, is_favorite,
|
||||
and sublist; the value is the count of that unique key combination.
|
||||
"""
|
||||
async with async_session_maker() as session:
|
||||
# TODO: Set up user-specific query here.
|
||||
query = select(models.PlaylistTrack).where(
|
||||
models.PlaylistTrack.episode_id is None
|
||||
)
|
||||
result = session.execute(query)
|
||||
ids = set()
|
||||
data = []
|
||||
for row in result.scalars():
|
||||
if row.episode_id is not None:
|
||||
continue
|
||||
ids.append(row.track_id)
|
||||
is_category = row.category_id is not None
|
||||
type_id = row.category_id if is_category else row.holiday_id
|
||||
is_favorite = row.is_favorite
|
||||
sublist = row.sublist
|
||||
data.append((is_category, type_id, is_favorite, sublist))
|
||||
data_counts = Counter(data)
|
||||
return ids, data_counts
|
||||
|
||||
async def get_statistics() -> models.Statistics:
|
||||
stats = await session.get(models.Statistics, 1)
|
||||
stats_obj = stats.scalars().first()
|
||||
return stats_obj
|
||||
|
||||
Reference in New Issue
Block a user