Getting the playlist processing logic organized and implemented.
Signed-off-by: Cliff Hill <xlorep@darkhelm.org>
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from enum import StrEnum
|
from enum import StrEnum
|
||||||
from enum import auto
|
from enum import auto
|
||||||
|
from typing import Generator
|
||||||
|
|
||||||
|
|
||||||
class Sublist(StrEnum):
|
class Sublist(StrEnum):
|
||||||
@@ -20,3 +21,9 @@ class Sublist(StrEnum):
|
|||||||
LEAST_RECENTLY_PLAYED = auto()
|
LEAST_RECENTLY_PLAYED = auto()
|
||||||
LEAST_OFTEN_PLAYED = auto()
|
LEAST_OFTEN_PLAYED = auto()
|
||||||
RANDOM = auto()
|
RANDOM = auto()
|
||||||
|
|
||||||
|
def standard(self) -> Generator["Sublist", None, None]:
|
||||||
|
"""Generate the standard sublist types one at a time."""
|
||||||
|
yield self.LEAST_RECENTLY_ADDED
|
||||||
|
yield self.LEAST_RECENTLY_PLAYED
|
||||||
|
yield self.LEAST_OFTEN_PLAYED
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
"""All database models are defined here."""
|
"""All database models are defined here."""
|
||||||
|
|
||||||
import enum
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from playlist import enums
|
||||||
from sqlalchemy import Boolean
|
from sqlalchemy import Boolean
|
||||||
from sqlalchemy import Column
|
from sqlalchemy import Column
|
||||||
from sqlalchemy import DateTime
|
from sqlalchemy import DateTime
|
||||||
@@ -112,7 +112,7 @@ class PlaylistTrack:
|
|||||||
holiday_id: Mapped[int] = mapped_column(ForeignKey("holidays.id"), nullable=True)
|
holiday_id: Mapped[int] = mapped_column(ForeignKey("holidays.id"), nullable=True)
|
||||||
episode_id: Mapped[int] = mapped_column(ForeignKey("episodes.id"), nullable=True)
|
episode_id: Mapped[int] = mapped_column(ForeignKey("episodes.id"), nullable=True)
|
||||||
favorite: Mapped[bool] = mapped_column(Boolean, nullable=True)
|
favorite: Mapped[bool] = mapped_column(Boolean, nullable=True)
|
||||||
sublist: Mapped[str] = mapped_column(Enum(enum.Sublist), nullable=True)
|
sublist: Mapped[str] = mapped_column(Enum(enums.Sublist), nullable=True)
|
||||||
|
|
||||||
playlist: Mapped["Playlist"] = relationship("Playlist")
|
playlist: Mapped["Playlist"] = relationship("Playlist")
|
||||||
track: Mapped["Track"] = relationship("Track")
|
track: Mapped["Track"] = relationship("Track")
|
||||||
@@ -171,6 +171,9 @@ class Track:
|
|||||||
genre: Mapped["Genre"] = relationship("Genre", back_populates="tracks")
|
genre: Mapped["Genre"] = relationship("Genre", back_populates="tracks")
|
||||||
category_id: Mapped[int] = mapped_column(ForeignKey("categories.id"))
|
category_id: Mapped[int] = mapped_column(ForeignKey("categories.id"))
|
||||||
category: Mapped["Category"] = relationship("Category", back_populates="tracks")
|
category: Mapped["Category"] = relationship("Category", back_populates="tracks")
|
||||||
|
holidays: Mapped[list["Holiday"]] = relationship(
|
||||||
|
"Holiday", secondary=holiday_track_association, back_populates="tracks"
|
||||||
|
)
|
||||||
playlist_tracks: Mapped[list["PlaylistTrack"]] = relationship(
|
playlist_tracks: Mapped[list["PlaylistTrack"]] = relationship(
|
||||||
"PlaylistTrack", back_populates="track"
|
"PlaylistTrack", back_populates="track"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,204 @@
|
|||||||
|
"""Contains the functions that are used to generate the playlist."""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from aiologger import Logger
|
||||||
|
from playlist import enums
|
||||||
|
from playlist import sql
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
|
||||||
|
logger = Logger.with_default_handlers()
|
||||||
|
|
||||||
|
|
||||||
|
async def generate_sublists(
|
||||||
|
session: AsyncSession,
|
||||||
|
playlist_entries: set[int],
|
||||||
|
sublist_counts: dict[enums.Sublist, int],
|
||||||
|
is_category: bool,
|
||||||
|
type_id: int,
|
||||||
|
is_favorite: bool,
|
||||||
|
base_count: int,
|
||||||
|
max_count: int,
|
||||||
|
) -> None:
|
||||||
|
"""Generate sublists for a given category and type (favorites or general).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
session (AsyncSession): The database session.
|
||||||
|
playlist_entries (set[int]): The set of track IDs in the playlist.
|
||||||
|
sublist_counts (dict[enums.Sublist, int]): The counts of existing tracks in the
|
||||||
|
given sublist in the current playlist.
|
||||||
|
is_category (bool): True if it is a category, false if it is a holiday.
|
||||||
|
type_id (int): The category or holiday ID for which to generate playlists.
|
||||||
|
is_favorite (bool): True for favorites only, False for general tracks.
|
||||||
|
base_count (int): The base number of tracks to be included from regular sublists.
|
||||||
|
max_count (int): The max number of tracks to be included from regular sublists.
|
||||||
|
"""
|
||||||
|
for sublist in enums.Sublist.standard():
|
||||||
|
existing_count = sublist_counts.get(sublist, 0)
|
||||||
|
allowed_count = base_count - existing_count
|
||||||
|
tracks = await sql.fetch_tracks_for_sublist(
|
||||||
|
session,
|
||||||
|
is_category,
|
||||||
|
type_id,
|
||||||
|
is_favorite,
|
||||||
|
sublist,
|
||||||
|
allowed_count,
|
||||||
|
playlist_entries,
|
||||||
|
)
|
||||||
|
for track in tracks:
|
||||||
|
await sql.insert_into_playlist(
|
||||||
|
session, is_category, type_id, track.id, sublist, is_favorite
|
||||||
|
)
|
||||||
|
playlist_entries.add(track.id)
|
||||||
|
|
||||||
|
# Calculate how many tracks to randomly add
|
||||||
|
remaining_count = max_count - len(playlist_entries)
|
||||||
|
if remaining_count > 0:
|
||||||
|
await generate_random_sublist(
|
||||||
|
session,
|
||||||
|
playlist_entries,
|
||||||
|
sublist_counts.get(enums.Sublist.RANDOM, 0),
|
||||||
|
type_id,
|
||||||
|
is_favorite,
|
||||||
|
remaining_count,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def generate_random_sublist(
|
||||||
|
session: AsyncSession,
|
||||||
|
playlist_entries: set[int],
|
||||||
|
existing_count: int,
|
||||||
|
is_category: bool,
|
||||||
|
type_id: int,
|
||||||
|
is_favorite: bool,
|
||||||
|
count: int,
|
||||||
|
) -> None:
|
||||||
|
"""Generate random sublist for the remaining slots in the playlist.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
session (AsyncSession): The database session.
|
||||||
|
playlist_entries (set[int]): The set of track IDs in the playlist.
|
||||||
|
existing_count (int): The number of random tracks already existing in the playlist.
|
||||||
|
is_category (bool): True if it is a category, false if it is a holiday.
|
||||||
|
type_id (int): The category or holiday ID for which to generate the random sublist.
|
||||||
|
is_favorite (bool): True for favorites only, False for general tracks.
|
||||||
|
count (int): Number of random tracks to add.
|
||||||
|
existing_entries (set): Set of track IDs already added to avoid duplicates.
|
||||||
|
"""
|
||||||
|
allowed_count = count - existing_count
|
||||||
|
random_tracks = await sql.fetch_tracks_for_sublist(
|
||||||
|
session,
|
||||||
|
type_id,
|
||||||
|
is_favorite,
|
||||||
|
enums.Sublist.RANDOM,
|
||||||
|
count,
|
||||||
|
allowed_count,
|
||||||
|
playlist_entries,
|
||||||
|
)
|
||||||
|
for track in random_tracks:
|
||||||
|
await sql.insert_into_playlist(
|
||||||
|
session, type_id, track.id, enums.Sublist.RANDOM, is_favorite
|
||||||
|
)
|
||||||
|
playlist_entries.add(track.id)
|
||||||
|
if len(playlist_entries) >= count:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
async def process_playlists(
|
||||||
|
playlist_entries: set[int],
|
||||||
|
playlist_counts: dict[tuple[bool, int, bool, enums.Sublist], int],
|
||||||
|
is_category: bool,
|
||||||
|
type_id: int,
|
||||||
|
favorite_base: int,
|
||||||
|
general_base: int,
|
||||||
|
favorite_max: int,
|
||||||
|
general_max: int,
|
||||||
|
) -> None:
|
||||||
|
"""Process playlists for both favorites and general tracks for a given category or holiday.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
playlist_entries (set[int]): The IDs of the tracks currently in the playlist.
|
||||||
|
playlist_counts (dict[tuple[bool, int, bool, enums.Sublist], int]): Counts of the
|
||||||
|
different sublist types within the playlist.
|
||||||
|
is_category (bool): True if it is a category, false if it is a holiday.
|
||||||
|
type_id (int): The category or holiday ID for which to generate the random sublist.
|
||||||
|
favorite_base (int): The maximum number of tracks for a favorite standard sublist.
|
||||||
|
general_base (int): The maximum number of tracks for a general standard sublist.
|
||||||
|
favorite_max (int): The maximum number of tracks for favorite tracks.
|
||||||
|
general_max (int): The maximum number of tracks for general tracks.
|
||||||
|
"""
|
||||||
|
async with sql.async_session_maker() as session:
|
||||||
|
favorite_counts = {
|
||||||
|
key[3]: count
|
||||||
|
for key, count in playlist_counts.items()
|
||||||
|
if key[0] is is_category and key[1] == type_id and key[2] is True
|
||||||
|
}
|
||||||
|
general_counts = {
|
||||||
|
key[3]: count
|
||||||
|
for key, count in playlist_counts.items()
|
||||||
|
if key[0] is is_category and key[1] == type_id and key[2] is False
|
||||||
|
}
|
||||||
|
await generate_sublists(
|
||||||
|
session,
|
||||||
|
playlist_entries,
|
||||||
|
favorite_counts,
|
||||||
|
is_category,
|
||||||
|
type_id,
|
||||||
|
True,
|
||||||
|
favorite_base,
|
||||||
|
favorite_max,
|
||||||
|
)
|
||||||
|
await generate_sublists(
|
||||||
|
session,
|
||||||
|
playlist_entries,
|
||||||
|
general_counts,
|
||||||
|
is_category,
|
||||||
|
type_id,
|
||||||
|
False,
|
||||||
|
general_base,
|
||||||
|
general_max,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def generate_playlist_for_all() -> None:
|
||||||
|
"""Generate playlists for all categories & holidays."""
|
||||||
|
(
|
||||||
|
existing_track_ids,
|
||||||
|
existing_playlist_counts,
|
||||||
|
) = await sql.get_existing_playlist_track_info()
|
||||||
|
category_ids = tuple(await sql.gen_all_category_ids())
|
||||||
|
holiday_ids = tuple(sql.gen_active_holiday_ids())
|
||||||
|
stats = await sql.get_statistics()
|
||||||
|
category_favorite_base = stats.max_regular_favorite_base
|
||||||
|
category_general_base = stats.max_regular_general_base
|
||||||
|
category_favorite_max = stats.max_regular_favorite_tracks
|
||||||
|
category_general_max = stats.max_regular_general_tracks
|
||||||
|
holiday_favorite_base = stats.max_holiday_favorite_base
|
||||||
|
holiday_general_base = stats.max_holiday_general_base
|
||||||
|
holiday_favorite_max = stats.max_holiday_favorite_tracks
|
||||||
|
holiday_general_max = stats.max_holiday_general_tracks
|
||||||
|
tasks = [
|
||||||
|
process_playlists(
|
||||||
|
playlist_entries=existing_track_ids,
|
||||||
|
playlist_counts=existing_playlist_counts,
|
||||||
|
is_category=True,
|
||||||
|
type_id=category_id,
|
||||||
|
favorite_base=category_favorite_base,
|
||||||
|
general_base=category_general_base,
|
||||||
|
favorite_max=category_favorite_max,
|
||||||
|
general_max=category_general_max,
|
||||||
|
)
|
||||||
|
for category_id in category_ids
|
||||||
|
] + [
|
||||||
|
process_playlists(
|
||||||
|
is_category=False,
|
||||||
|
type_id=holiday_id,
|
||||||
|
favorite_base=holiday_favorite_base,
|
||||||
|
general_base=holiday_general_base,
|
||||||
|
favorite_max=holiday_favorite_max,
|
||||||
|
general_max=holiday_general_max,
|
||||||
|
)
|
||||||
|
for holiday_id in holiday_ids
|
||||||
|
]
|
||||||
|
await asyncio.gather(*tasks)
|
||||||
+178
-34
@@ -1,11 +1,15 @@
|
|||||||
"""Contains all SQL code here."""
|
"""Contains all SQL code here."""
|
||||||
|
|
||||||
|
from collections import Counter
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from typing import AsyncGenerator
|
||||||
|
|
||||||
import env
|
|
||||||
import models
|
|
||||||
from aiologger import Logger
|
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.exc import SQLAlchemyError
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy.ext.asyncio import create_async_engine
|
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:
|
async with async_session_maker() as session:
|
||||||
try:
|
try:
|
||||||
async with session.begin():
|
async with session.begin():
|
||||||
stats = await session.execute(
|
stats = await session.get(models.Statistics, 1)
|
||||||
select(models.Statistics).order_by(models.Statistics.id)
|
|
||||||
)
|
|
||||||
stats_obj = stats.scalars().first()
|
stats_obj = stats.scalars().first()
|
||||||
|
|
||||||
if operation == "start":
|
if operation == "start":
|
||||||
@@ -214,32 +216,6 @@ async def remove_holiday_by_id(session: AsyncSession, holiday_id: int) -> None:
|
|||||||
await session.rollback()
|
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(
|
async def update_statistics(
|
||||||
new_average_duration: float,
|
new_average_duration: float,
|
||||||
new_podcast_length: float,
|
new_podcast_length: float,
|
||||||
@@ -283,9 +259,7 @@ async def update_statistics(
|
|||||||
try:
|
try:
|
||||||
async with session.begin():
|
async with session.begin():
|
||||||
# Assuming there's only one statistics record, or you might need to handle this differently
|
# Assuming there's only one statistics record, or you might need to handle this differently
|
||||||
stats = await session.get(
|
stats = await session.get(models.Statistics, 1)
|
||||||
models.Statistics, 1
|
|
||||||
) # Adjust based on how you access the statistics record
|
|
||||||
if stats:
|
if stats:
|
||||||
stats.average_track_length = new_average_duration
|
stats.average_track_length = new_average_duration
|
||||||
stats.total_podcast_length = new_podcast_length
|
stats.total_podcast_length = new_podcast_length
|
||||||
@@ -328,3 +302,173 @@ async def update_statistics(
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to update the statistics model: {e}")
|
print(f"Failed to update the statistics model: {e}")
|
||||||
await session.rollback()
|
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