feat(user): support view recent & best scores

This commit is contained in:
MingxuanGame
2025-08-11 07:32:41 +00:00
parent bc782cda01
commit 680c7525b8
2 changed files with 75 additions and 10 deletions

View File

@@ -1,4 +1,4 @@
from datetime import UTC, datetime
from datetime import UTC, datetime, timedelta
from typing import TYPE_CHECKING, NotRequired, TypedDict
from app.models.model import UTCBaseModel
@@ -6,6 +6,7 @@ from app.models.score import GameMode
from app.models.user import Country, Page, RankHistory
from .achievement import UserAchievement, UserAchievementResp
from .beatmap_playcounts import BeatmapPlaycounts
from .daily_challenge import DailyChallengeStats, DailyChallengeStatsResp
from .monthly_playcounts import MonthlyPlaycounts, MonthlyPlaycountsResp
from .statistics import UserStatistics, UserStatisticsResp
@@ -21,6 +22,7 @@ from sqlmodel import (
Field,
Relationship,
SQLModel,
col,
func,
select,
)
@@ -164,7 +166,7 @@ class UserResp(UserBase):
is_online: bool = False
groups: list = [] # TODO
country: Country = Field(default_factory=lambda: Country(code="CN", name="China"))
favourite_beatmapset_count: int = 0 # TODO
favourite_beatmapset_count: int = 0
graveyard_beatmapset_count: int = 0 # TODO
guest_beatmapset_count: int = 0 # TODO
loved_beatmapset_count: int = 0 # TODO
@@ -176,9 +178,10 @@ class UserResp(UserBase):
follower_count: int = 0
friends: list["RelationshipResp"] | None = None
scores_best_count: int = 0
scores_first_count: int = 0
scores_first_count: int = 0 # TODO
scores_recent_count: int = 0
scores_pinned_count: int = 0
beatmap_playcounts_count: int = 0
account_history: list[UserAccountHistoryResp] = []
active_tournament_banners: list[dict] = [] # TODO
kudosu: Kudosu = Field(default_factory=lambda: Kudosu(available=0, total=0)) # TODO
@@ -207,7 +210,11 @@ class UserResp(UserBase):
from app.dependencies.database import get_redis
from .best_score import BestScore
from .favourite_beatmapset import FavouriteBeatmapset
from .relationship import Relationship, RelationshipResp, RelationshipType
from .score import Score
ruleset = ruleset or obj.playmode
u = cls.model_validate(obj.model_dump())
u.id = obj.id
@@ -275,7 +282,7 @@ class UserResp(UserBase):
if "statistics" in include:
current_stattistics = None
for i in await obj.awaitable_attrs.statistics:
if i.mode == (ruleset or obj.playmode):
if i.mode == ruleset:
current_stattistics = i
break
u.statistics = (
@@ -302,6 +309,58 @@ class UserResp(UserBase):
for ua in await obj.awaitable_attrs.achievement
]
u.favourite_beatmapset_count = (
await session.exec(
select(func.count())
.select_from(FavouriteBeatmapset)
.where(FavouriteBeatmapset.user_id == obj.id)
)
).one()
u.scores_pinned_count = (
await session.exec(
select(func.count())
.select_from(Score)
.where(
Score.user_id == obj.id,
Score.pinned_order > 0,
Score.gamemode == ruleset,
col(Score.passed).is_(True),
)
)
).one()
u.scores_best_count = (
await session.exec(
select(func.count())
.select_from(BestScore)
.where(
BestScore.user_id == obj.id,
BestScore.gamemode == ruleset,
)
.limit(200)
)
).one()
u.scores_recent_count = (
await session.exec(
select(func.count())
.select_from(Score)
.where(
Score.user_id == obj.id,
Score.gamemode == ruleset,
col(Score.passed).is_(True),
Score.ended_at > datetime.now(UTC) - timedelta(hours=24),
)
)
).one()
u.beatmap_playcounts_count = (
await session.exec(
select(func.count())
.select_from(BeatmapPlaycounts)
.where(
BeatmapPlaycounts.user_id == obj.id,
)
)
).one()
return u

View File

@@ -1,5 +1,6 @@
from __future__ import annotations
from datetime import UTC, datetime, timedelta
from typing import Literal
from app.database import (
@@ -10,6 +11,7 @@ from app.database import (
UserResp,
)
from app.database.lazer_user import SEARCH_INCLUDED
from app.database.pp_best_score import PPBestScore
from app.database.score import Score, ScoreResp
from app.dependencies.database import get_db
from app.dependencies.user import get_current_user
@@ -20,7 +22,7 @@ from .api_router import router
from fastapi import Depends, HTTPException, Query
from pydantic import BaseModel
from sqlmodel import false, select
from sqlmodel import exists, false, select
from sqlmodel.ext.asyncio.session import AsyncSession
from sqlmodel.sql.expression import col
@@ -152,15 +154,19 @@ async def get_user_scores(
gamemode = mode or db_user.playmode
order_by = None
where_clause = (
(col(Score.user_id) == db_user.id)
& (col(Score.gamemode) == gamemode)
& (col(Score.passed).is_(True))
where_clause = (col(Score.user_id) == db_user.id) & (
col(Score.gamemode) == gamemode
)
if not include_fails:
where_clause &= col(Score.passed).is_(True)
if type == "pinned":
where_clause &= Score.pinned_order > 0
order_by = col(Score.pinned_order).asc()
else:
elif type == "best":
where_clause &= exists().where(col(PPBestScore.score_id) == Score.id)
elif type == "recent":
where_clause &= Score.ended_at > datetime.now(UTC) - timedelta(hours=24)
elif type == "firsts":
# TODO
where_clause &= false()