diff --git a/app/database/beatmap.py b/app/database/beatmap.py index 029915f..2cfb3a6 100644 --- a/app/database/beatmap.py +++ b/app/database/beatmap.py @@ -193,11 +193,9 @@ class BeatmapResp(BeatmapBase): if session: beatmap_["playcount"] = ( await session.exec( - select(func.count()) - .select_from(BeatmapPlaycounts) - .where(BeatmapPlaycounts.beatmap_id == beatmap.id) + select(func.sum(BeatmapPlaycounts.playcount)).where(BeatmapPlaycounts.beatmap_id == beatmap.id) ) - ).one() + ).first() or 0 beatmap_["passcount"] = ( await session.exec( select(func.count()) diff --git a/app/database/beatmapset.py b/app/database/beatmapset.py index 98e53b5..fd58c8b 100644 --- a/app/database/beatmapset.py +++ b/app/database/beatmapset.py @@ -2,6 +2,7 @@ from datetime import datetime from typing import TYPE_CHECKING, NotRequired, Self, TypedDict from app.config import settings +from app.database.beatmap_playcounts import BeatmapPlaycounts from app.models.beatmap import BeatmapRankStatus, Genre, Language from app.models.score import GameMode @@ -74,7 +75,6 @@ class BeatmapsetBase(SQLModel): covers: BeatmapCovers | None = Field(sa_column=Column(JSON)) creator: str = Field(index=True) nsfw: bool = Field(default=False, sa_column=Column(Boolean)) - play_count: int = Field(index=True) preview_url: str source: str = Field(default="") @@ -204,6 +204,7 @@ class BeatmapsetResp(BeatmapsetBase): has_favourited: bool = False favourite_count: int = 0 recent_favourites: list[UserResp] = Field(default_factory=list) + play_count: int = 0 @field_validator( "nsfw", @@ -240,7 +241,7 @@ class BeatmapsetResp(BeatmapsetBase): session: AsyncSession | None = None, user: User | None = None, ) -> "BeatmapsetResp": - from .beatmap import BeatmapResp + from .beatmap import Beatmap, BeatmapResp from .favourite_beatmapset import FavouriteBeatmapset update = { @@ -331,6 +332,14 @@ class BeatmapsetResp(BeatmapsetBase): .where(FavouriteBeatmapset.beatmapset_id == beatmapset.id) ) ).one() + + update["play_count"] = ( + await session.exec( + select(func.sum(BeatmapPlaycounts.playcount)).where( + col(BeatmapPlaycounts.beatmap).has(col(Beatmap.beatmapset_id) == beatmapset.id) + ) + ) + ).first() or 0 return cls.model_validate( update, ) diff --git a/app/database/score.py b/app/database/score.py index 32fd067..1e70136 100644 --- a/app/database/score.py +++ b/app/database/score.py @@ -14,6 +14,7 @@ from app.calculator import ( pre_fetch_and_calculate_pp, ) from app.config import settings +from app.database.beatmap_playcounts import BeatmapPlaycounts from app.database.team import TeamMember from app.dependencies.database import get_redis from app.log import log @@ -1206,6 +1207,9 @@ async def _process_statistics( statistics.play_count += 1 mouthly_playcount.count += 1 statistics.play_time += playtime + + await _process_beatmap_playcount(session, score.beatmap_id, user.id) + logger.debug( "Recorded playtime {playtime}s for score {score_id} (user {user_id})", playtime=playtime, @@ -1254,6 +1258,33 @@ async def _process_statistics( ) +async def _process_beatmap_playcount(session: AsyncSession, beatmap_id: int, user_id: int): + beatmap_playcount = ( + await session.exec( + select(BeatmapPlaycounts).where( + BeatmapPlaycounts.beatmap_id == beatmap_id, + BeatmapPlaycounts.user_id == user_id, + ) + ) + ).first() + if beatmap_playcount is None: + beatmap_playcount = BeatmapPlaycounts(beatmap_id=beatmap_id, user_id=user_id, playcount=1) + session.add(beatmap_playcount) + logger.debug( + "Created beatmap playcount record for user {user_id} on beatmap {beatmap_id}", + user_id=user_id, + beatmap_id=beatmap_id, + ) + else: + beatmap_playcount.playcount += 1 + logger.debug( + "Incremented beatmap playcount for user {user_id} on beatmap {beatmap_id} to {count}", + user_id=user_id, + beatmap_id=beatmap_id, + count=beatmap_playcount.playcount, + ) + + async def process_user( session: AsyncSession, redis: Redis, diff --git a/migrations/versions/2025-10-04_ee13ad926584_beatmapset_remove_play_count.py b/migrations/versions/2025-10-04_ee13ad926584_beatmapset_remove_play_count.py new file mode 100644 index 0000000..2fb2893 --- /dev/null +++ b/migrations/versions/2025-10-04_ee13ad926584_beatmapset_remove_play_count.py @@ -0,0 +1,28 @@ +"""beatmapset: remove play_count + +Revision ID: ee13ad926584 +Revises: 9556cd2ec11f +Create Date: 2025-10-04 10:48:00.985529 + +""" + +from collections.abc import Sequence + +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision: str = "ee13ad926584" +down_revision: str | Sequence[str] | None = "9556cd2ec11f" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + """Upgrade schema.""" + op.drop_column("beatmapsets", "play_count") + + +def downgrade() -> None: + """Downgrade schema.""" + op.add_column("beatmapsets", sa.Column("play_count", sa.Integer(), nullable=False))