feat(user): support view recent & best scores
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime, timedelta
|
||||||
from typing import TYPE_CHECKING, NotRequired, TypedDict
|
from typing import TYPE_CHECKING, NotRequired, TypedDict
|
||||||
|
|
||||||
from app.models.model import UTCBaseModel
|
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 app.models.user import Country, Page, RankHistory
|
||||||
|
|
||||||
from .achievement import UserAchievement, UserAchievementResp
|
from .achievement import UserAchievement, UserAchievementResp
|
||||||
|
from .beatmap_playcounts import BeatmapPlaycounts
|
||||||
from .daily_challenge import DailyChallengeStats, DailyChallengeStatsResp
|
from .daily_challenge import DailyChallengeStats, DailyChallengeStatsResp
|
||||||
from .monthly_playcounts import MonthlyPlaycounts, MonthlyPlaycountsResp
|
from .monthly_playcounts import MonthlyPlaycounts, MonthlyPlaycountsResp
|
||||||
from .statistics import UserStatistics, UserStatisticsResp
|
from .statistics import UserStatistics, UserStatisticsResp
|
||||||
@@ -21,6 +22,7 @@ from sqlmodel import (
|
|||||||
Field,
|
Field,
|
||||||
Relationship,
|
Relationship,
|
||||||
SQLModel,
|
SQLModel,
|
||||||
|
col,
|
||||||
func,
|
func,
|
||||||
select,
|
select,
|
||||||
)
|
)
|
||||||
@@ -164,7 +166,7 @@ class UserResp(UserBase):
|
|||||||
is_online: bool = False
|
is_online: bool = False
|
||||||
groups: list = [] # TODO
|
groups: list = [] # TODO
|
||||||
country: Country = Field(default_factory=lambda: Country(code="CN", name="China"))
|
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
|
graveyard_beatmapset_count: int = 0 # TODO
|
||||||
guest_beatmapset_count: int = 0 # TODO
|
guest_beatmapset_count: int = 0 # TODO
|
||||||
loved_beatmapset_count: int = 0 # TODO
|
loved_beatmapset_count: int = 0 # TODO
|
||||||
@@ -176,9 +178,10 @@ class UserResp(UserBase):
|
|||||||
follower_count: int = 0
|
follower_count: int = 0
|
||||||
friends: list["RelationshipResp"] | None = None
|
friends: list["RelationshipResp"] | None = None
|
||||||
scores_best_count: int = 0
|
scores_best_count: int = 0
|
||||||
scores_first_count: int = 0
|
scores_first_count: int = 0 # TODO
|
||||||
scores_recent_count: int = 0
|
scores_recent_count: int = 0
|
||||||
scores_pinned_count: int = 0
|
scores_pinned_count: int = 0
|
||||||
|
beatmap_playcounts_count: int = 0
|
||||||
account_history: list[UserAccountHistoryResp] = []
|
account_history: list[UserAccountHistoryResp] = []
|
||||||
active_tournament_banners: list[dict] = [] # TODO
|
active_tournament_banners: list[dict] = [] # TODO
|
||||||
kudosu: Kudosu = Field(default_factory=lambda: Kudosu(available=0, total=0)) # 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 app.dependencies.database import get_redis
|
||||||
|
|
||||||
from .best_score import BestScore
|
from .best_score import BestScore
|
||||||
|
from .favourite_beatmapset import FavouriteBeatmapset
|
||||||
from .relationship import Relationship, RelationshipResp, RelationshipType
|
from .relationship import Relationship, RelationshipResp, RelationshipType
|
||||||
|
from .score import Score
|
||||||
|
|
||||||
|
ruleset = ruleset or obj.playmode
|
||||||
|
|
||||||
u = cls.model_validate(obj.model_dump())
|
u = cls.model_validate(obj.model_dump())
|
||||||
u.id = obj.id
|
u.id = obj.id
|
||||||
@@ -275,7 +282,7 @@ class UserResp(UserBase):
|
|||||||
if "statistics" in include:
|
if "statistics" in include:
|
||||||
current_stattistics = None
|
current_stattistics = None
|
||||||
for i in await obj.awaitable_attrs.statistics:
|
for i in await obj.awaitable_attrs.statistics:
|
||||||
if i.mode == (ruleset or obj.playmode):
|
if i.mode == ruleset:
|
||||||
current_stattistics = i
|
current_stattistics = i
|
||||||
break
|
break
|
||||||
u.statistics = (
|
u.statistics = (
|
||||||
@@ -302,6 +309,58 @@ class UserResp(UserBase):
|
|||||||
for ua in await obj.awaitable_attrs.achievement
|
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
|
return u
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import UTC, datetime, timedelta
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
|
||||||
from app.database import (
|
from app.database import (
|
||||||
@@ -10,6 +11,7 @@ from app.database import (
|
|||||||
UserResp,
|
UserResp,
|
||||||
)
|
)
|
||||||
from app.database.lazer_user import SEARCH_INCLUDED
|
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.database.score import Score, ScoreResp
|
||||||
from app.dependencies.database import get_db
|
from app.dependencies.database import get_db
|
||||||
from app.dependencies.user import get_current_user
|
from app.dependencies.user import get_current_user
|
||||||
@@ -20,7 +22,7 @@ from .api_router import router
|
|||||||
|
|
||||||
from fastapi import Depends, HTTPException, Query
|
from fastapi import Depends, HTTPException, Query
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from sqlmodel import false, select
|
from sqlmodel import exists, false, select
|
||||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||||
from sqlmodel.sql.expression import col
|
from sqlmodel.sql.expression import col
|
||||||
|
|
||||||
@@ -152,15 +154,19 @@ async def get_user_scores(
|
|||||||
|
|
||||||
gamemode = mode or db_user.playmode
|
gamemode = mode or db_user.playmode
|
||||||
order_by = None
|
order_by = None
|
||||||
where_clause = (
|
where_clause = (col(Score.user_id) == db_user.id) & (
|
||||||
(col(Score.user_id) == db_user.id)
|
col(Score.gamemode) == gamemode
|
||||||
& (col(Score.gamemode) == gamemode)
|
|
||||||
& (col(Score.passed).is_(True))
|
|
||||||
)
|
)
|
||||||
|
if not include_fails:
|
||||||
|
where_clause &= col(Score.passed).is_(True)
|
||||||
if type == "pinned":
|
if type == "pinned":
|
||||||
where_clause &= Score.pinned_order > 0
|
where_clause &= Score.pinned_order > 0
|
||||||
order_by = col(Score.pinned_order).asc()
|
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
|
# TODO
|
||||||
where_clause &= false()
|
where_clause &= false()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user