refactor(scores): rename models of score to match with filename

This commit is contained in:
MingxuanGame
2025-10-04 08:01:38 +00:00
parent 80ba65c746
commit 1163a93053
9 changed files with 103 additions and 97 deletions

View File

@@ -12,7 +12,7 @@ from .beatmapset import (
BeatmapsetResp,
)
from .beatmapset_ratings import BeatmapRating
from .best_scores import PPBestScore
from .best_scores import BestScore
from .chat import (
ChannelType,
ChatChannel,
@@ -56,7 +56,7 @@ from .statistics import (
UserStatisticsResp,
)
from .team import Team, TeamMember, TeamRequest
from .total_score_best_scores import BestScore
from .total_score_best_scores import TotalScoreBestScore
from .user import (
MeResp,
User,
@@ -105,7 +105,6 @@ __all__ = [
"Notification",
"OAuthClient",
"OAuthToken",
"PPBestScore",
"PasswordReset",
"Playlist",
"PlaylistAggregateScore",
@@ -131,6 +130,7 @@ __all__ = [
"Team",
"TeamMember",
"TeamRequest",
"TotalScoreBestScore",
"TotpKeys",
"TrustedDevice",
"TrustedDeviceResp",

View File

@@ -22,7 +22,7 @@ if TYPE_CHECKING:
from .score import Score
class PPBestScore(SQLModel, table=True):
class BestScore(SQLModel, table=True):
__tablename__: str = "best_scores"
user_id: int = Field(sa_column=Column(BigInteger, ForeignKey("lazer_users.id"), index=True))
score_id: int = Field(sa_column=Column(BigInteger, ForeignKey("scores.id"), primary_key=True))

View File

@@ -38,7 +38,7 @@ from app.utils import utcnow
from .beatmap import Beatmap, BeatmapResp
from .beatmapset import BeatmapsetResp
from .best_scores import PPBestScore
from .best_scores import BestScore
from .counts import MonthlyPlaycounts
from .events import Event, EventType
from .playlist_best_score import PlaylistBestScore
@@ -47,7 +47,7 @@ from .relationship import (
RelationshipType,
)
from .score_token import ScoreToken
from .total_score_best_scores import BestScore
from .total_score_best_scores import TotalScoreBestScore
from .user import User, UserResp
from pydantic import BaseModel, field_serializer, field_validator
@@ -195,13 +195,13 @@ class Score(ScoreBase, table=True):
# optional
beatmap: Mapped[Beatmap] = Relationship()
user: Mapped[User] = Relationship(sa_relationship_kwargs={"lazy": "joined"})
best_score: Mapped[BestScore | None] = Relationship(
best_score: Mapped[TotalScoreBestScore | None] = Relationship(
back_populates="score",
sa_relationship_kwargs={
"cascade": "all, delete-orphan",
},
)
ranked_score: Mapped[PPBestScore | None] = Relationship(
ranked_score: Mapped[BestScore | None] = Relationship(
back_populates="score",
sa_relationship_kwargs={
"cascade": "all, delete-orphan",
@@ -481,10 +481,10 @@ class ScoreAround(SQLModel):
async def get_best_id(session: AsyncSession, score_id: int) -> int | None:
rownum = (
func.row_number()
.over(partition_by=(col(PPBestScore.user_id), col(PPBestScore.gamemode)), order_by=col(PPBestScore.pp).desc())
.over(partition_by=(col(BestScore.user_id), col(BestScore.gamemode)), order_by=col(BestScore.pp).desc())
.label("rn")
)
subq = select(PPBestScore, rownum).subquery()
subq = select(BestScore, rownum).subquery()
stmt = select(subq.c.rn).where(subq.c.score_id == score_id)
result = await session.exec(stmt)
return result.one_or_none()
@@ -498,8 +498,8 @@ async def _score_where(
user: User | None = None,
) -> list[ColumnElement[bool] | TextClause] | None:
wheres: list[ColumnElement[bool] | TextClause] = [
col(BestScore.beatmap_id) == beatmap,
col(BestScore.gamemode) == mode,
col(TotalScoreBestScore.beatmap_id) == beatmap,
col(TotalScoreBestScore.gamemode) == mode,
]
if type == LeaderboardType.FRIENDS:
@@ -512,19 +512,21 @@ async def _score_where(
)
.subquery()
)
wheres.append(col(BestScore.user_id).in_(select(subq.c.target_id)))
wheres.append(col(TotalScoreBestScore.user_id).in_(select(subq.c.target_id)))
else:
return None
elif type == LeaderboardType.COUNTRY:
if user and user.is_supporter:
wheres.append(col(BestScore.user).has(col(User.country_code) == user.country_code))
wheres.append(col(TotalScoreBestScore.user).has(col(User.country_code) == user.country_code))
else:
return None
elif type == LeaderboardType.TEAM and user:
team_membership = await user.awaitable_attrs.team_membership
if team_membership:
team_id = team_membership.team_id
wheres.append(col(BestScore.user).has(col(User.team_membership).has(TeamMember.team_id == team_id)))
wheres.append(
col(TotalScoreBestScore.user).has(col(User.team_membership).has(TeamMember.team_id == team_id))
)
if mods:
if user and user.is_supporter:
wheres.append(
@@ -558,10 +560,10 @@ async def get_leaderboard(
max_score = sys.maxsize
while limit > 0:
query = (
select(BestScore)
.where(*wheres, BestScore.total_score < max_score)
select(TotalScoreBestScore)
.where(*wheres, TotalScoreBestScore.total_score < max_score)
.limit(limit)
.order_by(col(BestScore.total_score).desc())
.order_by(col(TotalScoreBestScore.total_score).desc())
)
extra_need = 0
for s in await session.exec(query):
@@ -580,13 +582,13 @@ async def get_leaderboard(
user_score = None
if user:
self_query = (
select(BestScore)
.where(BestScore.user_id == user.id)
select(TotalScoreBestScore)
.where(TotalScoreBestScore.user_id == user.id)
.where(
col(BestScore.beatmap_id) == beatmap,
col(BestScore.gamemode) == mode,
col(TotalScoreBestScore.beatmap_id) == beatmap,
col(TotalScoreBestScore.gamemode) == mode,
)
.order_by(col(BestScore.total_score).desc())
.order_by(col(TotalScoreBestScore.total_score).desc())
.limit(1)
)
if mods:
@@ -619,14 +621,14 @@ async def get_score_position_by_user(
func.row_number()
.over(
partition_by=(
col(BestScore.beatmap_id),
col(BestScore.gamemode),
col(TotalScoreBestScore.beatmap_id),
col(TotalScoreBestScore.gamemode),
),
order_by=col(BestScore.total_score).desc(),
order_by=col(TotalScoreBestScore.total_score).desc(),
)
.label("row_number")
)
subq = select(BestScore, rownum).join(Beatmap).where(*wheres).subquery()
subq = select(TotalScoreBestScore, rownum).join(Beatmap).where(*wheres).subquery()
stmt = select(subq.c.row_number).where(subq.c.user_id == user.id)
result = await session.exec(stmt)
s = result.first()
@@ -649,14 +651,14 @@ async def get_score_position_by_id(
func.row_number()
.over(
partition_by=(
col(BestScore.beatmap_id),
col(BestScore.gamemode),
col(TotalScoreBestScore.beatmap_id),
col(TotalScoreBestScore.gamemode),
),
order_by=col(BestScore.total_score).desc(),
order_by=col(TotalScoreBestScore.total_score).desc(),
)
.label("row_number")
)
subq = select(BestScore, rownum).join(Beatmap).where(*wheres).subquery()
subq = select(TotalScoreBestScore, rownum).join(Beatmap).where(*wheres).subquery()
stmt = select(subq.c.row_number).where(subq.c.score_id == score_id)
result = await session.exec(stmt)
s = result.one_or_none()
@@ -668,16 +670,16 @@ async def get_user_best_score_in_beatmap(
beatmap: int,
user: int,
mode: GameMode | None = None,
) -> BestScore | None:
) -> TotalScoreBestScore | None:
return (
await session.exec(
select(BestScore)
select(TotalScoreBestScore)
.where(
BestScore.gamemode == mode if mode is not None else true(),
BestScore.beatmap_id == beatmap,
BestScore.user_id == user,
TotalScoreBestScore.gamemode == mode if mode is not None else true(),
TotalScoreBestScore.beatmap_id == beatmap,
TotalScoreBestScore.user_id == user,
)
.order_by(col(BestScore.total_score).desc())
.order_by(col(TotalScoreBestScore.total_score).desc())
)
).first()
@@ -688,32 +690,32 @@ async def get_user_best_score_with_mod_in_beatmap(
user: int,
mod: list[str],
mode: GameMode | None = None,
) -> BestScore | None:
) -> TotalScoreBestScore | None:
return (
await session.exec(
select(BestScore)
select(TotalScoreBestScore)
.where(
BestScore.gamemode == mode if mode is not None else True,
BestScore.beatmap_id == beatmap,
BestScore.user_id == user,
TotalScoreBestScore.gamemode == mode if mode is not None else True,
TotalScoreBestScore.beatmap_id == beatmap,
TotalScoreBestScore.user_id == user,
text(
"JSON_CONTAINS(total_score_best_scores.mods, :w)"
" AND JSON_CONTAINS(:w, total_score_best_scores.mods)"
).params(w=json.dumps(mod)),
)
.order_by(col(BestScore.total_score).desc())
.order_by(col(TotalScoreBestScore.total_score).desc())
)
).first()
async def get_user_first_scores(
session: AsyncSession, user_id: int, mode: GameMode, limit: int = 5, offset: int = 0
) -> list[BestScore]:
) -> list[TotalScoreBestScore]:
rownum = (
func.row_number()
.over(
partition_by=(col(BestScore.beatmap_id), col(BestScore.gamemode)),
order_by=col(BestScore.total_score).desc(),
partition_by=(col(TotalScoreBestScore.beatmap_id), col(TotalScoreBestScore.gamemode)),
order_by=col(TotalScoreBestScore.total_score).desc(),
)
.label("rn")
)
@@ -721,11 +723,11 @@ async def get_user_first_scores(
# Step 1: Fetch top score_ids in Python
subq = (
select(
col(BestScore.score_id).label("score_id"),
col(BestScore.user_id).label("user_id"),
col(TotalScoreBestScore.score_id).label("score_id"),
col(TotalScoreBestScore.user_id).label("user_id"),
rownum,
)
.where(col(BestScore.gamemode) == mode)
.where(col(TotalScoreBestScore.gamemode) == mode)
.subquery()
)
@@ -734,7 +736,11 @@ async def get_user_first_scores(
top_ids = await session.exec(top_ids_stmt)
top_ids = list(top_ids)
stmt = select(BestScore).where(col(BestScore.score_id).in_(top_ids)).order_by(col(BestScore.total_score).desc())
stmt = (
select(TotalScoreBestScore)
.where(col(TotalScoreBestScore.score_id).in_(top_ids))
.order_by(col(TotalScoreBestScore.total_score).desc())
)
result = await session.exec(stmt)
return list(result.all())
@@ -744,18 +750,18 @@ async def get_user_first_score_count(session: AsyncSession, user_id: int, mode:
rownum = (
func.row_number()
.over(
partition_by=(col(BestScore.beatmap_id), col(BestScore.gamemode)),
order_by=col(BestScore.total_score).desc(),
partition_by=(col(TotalScoreBestScore.beatmap_id), col(TotalScoreBestScore.gamemode)),
order_by=col(TotalScoreBestScore.total_score).desc(),
)
.label("rn")
)
subq = (
select(
col(BestScore.score_id).label("score_id"),
col(BestScore.user_id).label("user_id"),
col(TotalScoreBestScore.score_id).label("score_id"),
col(TotalScoreBestScore.user_id).label("user_id"),
rownum,
)
.where(col(BestScore.gamemode) == mode)
.where(col(TotalScoreBestScore.gamemode) == mode)
.subquery()
)
count_stmt = select(func.count()).where(subq.c.rn == 1, subq.c.user_id == user_id)
@@ -769,13 +775,13 @@ async def get_user_best_pp_in_beatmap(
beatmap: int,
user: int,
mode: GameMode,
) -> PPBestScore | None:
) -> BestScore | None:
return (
await session.exec(
select(PPBestScore).where(
PPBestScore.beatmap_id == beatmap,
PPBestScore.user_id == user,
PPBestScore.gamemode == mode,
select(BestScore).where(
BestScore.beatmap_id == beatmap,
BestScore.user_id == user,
BestScore.gamemode == mode,
)
)
).first()
@@ -800,12 +806,12 @@ async def get_user_best_pp(
user: int,
mode: GameMode,
limit: int = 1000,
) -> Sequence[PPBestScore]:
) -> Sequence[BestScore]:
return (
await session.exec(
select(PPBestScore)
.where(PPBestScore.user_id == user, PPBestScore.gamemode == mode)
.order_by(col(PPBestScore.pp).desc())
select(BestScore)
.where(BestScore.user_id == user, BestScore.gamemode == mode)
.order_by(col(BestScore.pp).desc())
.limit(limit)
)
).all()
@@ -936,7 +942,7 @@ async def _process_score_pp(score: Score, session: AsyncSession, redis: Redis, f
beatmap_id = score.beatmap_id
previous_pp_best = await get_user_best_pp_in_beatmap(session, beatmap_id, user_id, score.gamemode)
if previous_pp_best is None or score.pp > previous_pp_best.pp:
best_score = PPBestScore(
best_score = BestScore(
user_id=user_id,
score_id=score.id,
beatmap_id=beatmap_id,
@@ -1010,12 +1016,12 @@ async def _process_score_events(score: Score, session: AsyncSession):
if rank_global == 1:
displaced_score = (
await session.exec(
select(BestScore)
select(TotalScoreBestScore)
.where(
BestScore.beatmap_id == score.beatmap_id,
BestScore.gamemode == score.gamemode,
TotalScoreBestScore.beatmap_id == score.beatmap_id,
TotalScoreBestScore.gamemode == score.gamemode,
)
.order_by(col(BestScore.total_score).desc())
.order_by(col(TotalScoreBestScore.total_score).desc())
.limit(1)
.offset(1)
)
@@ -1139,7 +1145,7 @@ async def _process_statistics(
# 情况2: 有最佳分数记录但没有该mod组合的记录添加新记录
if previous_score_best is None or previous_score_best_mod is None:
session.add(
BestScore(
TotalScoreBestScore(
user_id=user.id,
beatmap_id=score.beatmap_id,
gamemode=score.gamemode,

View File

@@ -25,7 +25,7 @@ if TYPE_CHECKING:
from .score import Score
class BestScore(SQLModel, table=True):
class TotalScoreBestScore(SQLModel, table=True):
__tablename__: str = "total_score_best_scores"
user_id: int = Field(sa_column=Column(BigInteger, ForeignKey("lazer_users.id"), index=True))
score_id: int = Field(sa_column=Column(BigInteger, ForeignKey("scores.id"), primary_key=True))
@@ -41,7 +41,7 @@ class BestScore(SQLModel, table=True):
user: User = Relationship()
score: "Score" = Relationship(
sa_relationship_kwargs={
"foreign_keys": "[BestScore.score_id]",
"foreign_keys": "[TotalScoreBestScore.score_id]",
"lazy": "joined",
},
back_populates="best_score",
@@ -75,7 +75,7 @@ class BestScore(SQLModel, table=True):
await session.exec(
select(func.max(Score.max_combo)).where(
Score.user_id == self.user_id,
col(Score.id).in_(select(BestScore.score_id)),
col(Score.id).in_(select(TotalScoreBestScore.score_id)),
Score.gamemode == self.gamemode,
)
)

View File

@@ -259,11 +259,11 @@ class UserResp(UserBase):
) -> "UserResp":
from app.dependencies.database import get_redis
from .best_scores import PPBestScore
from .best_scores import BestScore
from .favourite_beatmapset import FavouriteBeatmapset
from .relationship import Relationship, RelationshipResp, RelationshipType
from .score import Score, get_user_first_score_count
from .total_score_best_scores import BestScore
from .total_score_best_scores import TotalScoreBestScore
ruleset = ruleset or obj.playmode
@@ -284,9 +284,9 @@ class UserResp(UserBase):
u.scores_best_count = (
await session.exec(
select(func.count())
.select_from(BestScore)
.select_from(TotalScoreBestScore)
.where(
BestScore.user_id == obj.id,
TotalScoreBestScore.user_id == obj.id,
)
.limit(200)
)
@@ -391,10 +391,10 @@ class UserResp(UserBase):
u.scores_best_count = (
await session.exec(
select(func.count())
.select_from(PPBestScore)
.select_from(BestScore)
.where(
PPBestScore.user_id == obj.id,
PPBestScore.gamemode == ruleset,
BestScore.user_id == obj.id,
BestScore.gamemode == ruleset,
)
.limit(200)
)

View File

@@ -1,7 +1,7 @@
from datetime import datetime, timedelta
from typing import Annotated, Literal
from app.database.best_scores import PPBestScore
from app.database.best_scores import BestScore
from app.database.score import Score, get_leaderboard
from app.dependencies.database import Database
from app.models.mods import int_to_mods, mod_to_save, mods_to_int
@@ -79,7 +79,7 @@ async def get_user_best(
.where(
Score.user_id == user if type == "id" or user.isdigit() else col(Score.user).has(username=user),
Score.gamemode == GameMode.from_int_extra(ruleset_id),
exists().where(col(PPBestScore.score_id) == Score.id),
exists().where(col(BestScore.score_id) == Score.id),
)
.order_by(col(Score.pp).desc())
.options(joinedload(Score.beatmap))

View File

@@ -10,7 +10,7 @@ from app.database import (
User,
UserResp,
)
from app.database.best_scores import PPBestScore
from app.database.best_scores import BestScore
from app.database.events import Event
from app.database.score import LegacyScoreResp, Score, ScoreResp, get_user_first_scores
from app.database.user import SEARCH_INCLUDED
@@ -392,7 +392,7 @@ async def get_user_scores(
where_clause &= Score.pinned_order > 0
order_by = col(Score.pinned_order).asc()
elif type == "best":
where_clause &= exists().where(col(PPBestScore.score_id) == Score.id)
where_clause &= exists().where(col(BestScore.score_id) == Score.id)
order_by = col(Score.pp).desc()
elif type == "recent":
where_clause &= Score.ended_at > utcnow() - timedelta(hours=24)

View File

@@ -4,7 +4,7 @@ import json
from app.calculator import calculate_pp
from app.config import settings
from app.database.beatmap import BannedBeatmaps, Beatmap
from app.database.best_scores import PPBestScore
from app.database.best_scores import BestScore
from app.database.score import Score, calculate_user_pp
from app.database.statistics import UserStatistics
from app.dependencies.database import get_redis, with_db
@@ -35,7 +35,7 @@ async def recalculate_banned_beatmap():
unbanned_beatmaps = [b for b in last_banned_beatmaps if b not in current_banned]
for i in new_banned_beatmaps:
last_banned_beatmaps.add(i)
await session.execute(delete(PPBestScore).where(col(PPBestScore.beatmap_id) == i))
await session.execute(delete(BestScore).where(col(BestScore.beatmap_id) == i))
scores = (await session.exec(select(Score).where(Score.beatmap_id == i, Score.pp > 0))).all()
for score in scores:
score.pp = 0
@@ -58,7 +58,7 @@ async def recalculate_banned_beatmap():
logger.exception(f"Failed to query scores for unbanned beatmap {beatmap_id}")
continue
prev: dict[tuple[int, int], PPBestScore] = {}
prev: dict[tuple[int, int], BestScore] = {}
for score in scores:
attempts = 3
while attempts > 0:
@@ -90,7 +90,7 @@ async def recalculate_banned_beatmap():
continue
key = (score.beatmap_id, score.user_id)
if key not in prev or prev[key].pp < pp:
best_score = PPBestScore(
best_score = BestScore(
user_id=score.user_id,
beatmap_id=beatmap_id,
acc=score.accuracy,

View File

@@ -12,9 +12,9 @@ from app.calculator import (
)
from app.config import settings
from app.const import BANCHOBOT_ID
from app.database import BestScore, UserStatistics
from app.database import TotalScoreBestScore, UserStatistics
from app.database.beatmap import Beatmap
from app.database.best_scores import PPBestScore
from app.database.best_scores import BestScore
from app.database.score import Score, calculate_playtime, calculate_user_pp
from app.dependencies.database import engine, get_redis
from app.dependencies.fetcher import get_fetcher
@@ -42,8 +42,8 @@ async def recalculate():
fetcher = await get_fetcher()
redis = get_redis()
for mode in GameMode:
await session.execute(delete(PPBestScore).where(col(PPBestScore.gamemode) == mode))
await session.execute(delete(BestScore).where(col(BestScore.gamemode) == mode))
await session.execute(delete(TotalScoreBestScore).where(col(TotalScoreBestScore.gamemode) == mode))
await session.commit()
logger.info(f"Recalculating for mode: {mode}")
statistics_list = (
@@ -63,7 +63,7 @@ async def recalculate():
)
await run_in_batches(
[
_recalculate_best_score(statistics.user_id, statistics.mode, session)
_recalculate_total_score_best_score(statistics.user_id, statistics.mode, session)
for statistics in statistics_list
],
batch_size=200,
@@ -97,7 +97,7 @@ async def _recalculate_pp(
)
)
).all()
prev: dict[int, PPBestScore] = {}
prev: dict[int, BestScore] = {}
async def cal(score: Score):
time = 10
@@ -120,7 +120,7 @@ async def _recalculate_pp(
return
score.pp = pp
if score.beatmap_id not in prev or prev[score.beatmap_id].pp < pp:
best_score = PPBestScore(
best_score = BestScore(
user_id=user_id,
beatmap_id=beatmap_id,
acc=score.accuracy,
@@ -153,13 +153,13 @@ async def _recalculate_pp(
session.add(best_score)
async def _recalculate_best_score(
async def _recalculate_total_score_best_score(
user_id: int,
gamemode: GameMode,
session: AsyncSession,
):
async with SEMAPHORE:
beatmap_best_score: dict[int, list[BestScore]] = {}
beatmap_best_score: dict[int, list[TotalScoreBestScore]] = {}
scores = (
await session.exec(
select(Score).where(
@@ -176,7 +176,7 @@ async def _recalculate_best_score(
):
continue
mod_for_save = mod_to_save(score.mods)
bs = BestScore(
bs = TotalScoreBestScore(
user_id=score.user_id,
score_id=score.id,
beatmap_id=score.beatmap_id,