wip(db): v5 models

- Literally reverting f19ac4d8d5
This commit is contained in:
2025-05-31 14:34:45 +08:00
parent 4ea49ebeda
commit 9d7054d29a
12 changed files with 463 additions and 422 deletions

View File

@ -0,0 +1,192 @@
from datetime import datetime, timezone
from typing import Optional
from uuid import UUID, uuid4
from sqlalchemy import Integer, String, Text, Uuid, case, func, inspect, select, text
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy_utils import create_view
from ._base import ModelBase, ModelViewBase, ReprHelper
from .chart_info import ChartInfo
from .difficulty import Difficulty
__all__ = [
"CalculatedPotential",
"PlayResult",
"PlayResultBest",
"PlayResultCalculated",
]
class PlayResult(ModelBase, ReprHelper):
__tablename__ = "play_result"
id: Mapped[int] = mapped_column(autoincrement=True, primary_key=True)
uuid: Mapped[UUID] = mapped_column(
Uuid, nullable=False, unique=True, default=lambda: uuid4()
)
song_id: Mapped[str] = mapped_column(String)
rating_class: Mapped[int] = mapped_column(Integer)
played_at: Mapped[Optional[datetime]] = mapped_column(
default=lambda: datetime.now(timezone.utc)
)
score: Mapped[int]
pure: Mapped[Optional[int]]
pure_early: Mapped[Optional[int]]
pure_late: Mapped[Optional[int]]
far: Mapped[Optional[int]]
far_early: Mapped[Optional[int]]
far_late: Mapped[Optional[int]]
lost: Mapped[Optional[int]]
max_recall: Mapped[Optional[int]]
clear_type: Mapped[Optional[int]]
modifier: Mapped[Optional[int]]
comment: Mapped[Optional[str]] = mapped_column(Text)
class PlayResultCalculated(ModelViewBase, ReprHelper):
__tablename__ = "play_results_calculated"
id: Mapped[int]
uuid: Mapped[UUID]
song_id: Mapped[str]
rating_class: Mapped[int]
score: Mapped[int]
pure: Mapped[Optional[int]]
pure_early: Mapped[Optional[int]]
pure_late: Mapped[Optional[int]]
shiny_pure: Mapped[Optional[int]]
far: Mapped[Optional[int]]
far_early: Mapped[Optional[int]]
far_late: Mapped[Optional[int]]
lost: Mapped[Optional[int]]
played_at: Mapped[Optional[datetime]]
max_recall: Mapped[Optional[int]]
modifier: Mapped[Optional[int]]
clear_type: Mapped[Optional[int]]
potential: Mapped[float]
comment: Mapped[Optional[str]]
__table__ = create_view(
name=__tablename__,
selectable=select(
PlayResult.id,
Difficulty.song_id,
Difficulty.rating_class,
PlayResult.score,
PlayResult.pure,
(
case(
(
(
ChartInfo.notes.is_not(None)
& PlayResult.pure.is_not(None)
& PlayResult.far.is_not(None)
& (ChartInfo.notes != 0)
),
PlayResult.score
- func.floor(
(PlayResult.pure * 10000000.0 / ChartInfo.notes)
+ (PlayResult.far * 0.5 * 10000000.0 / ChartInfo.notes)
),
),
else_=text("NULL"),
)
).label("shiny_pure"),
PlayResult.far,
PlayResult.lost,
PlayResult.played_at,
PlayResult.max_recall,
PlayResult.modifier,
PlayResult.clear_type,
case(
(PlayResult.score >= 10000000, ChartInfo.constant / 10.0 + 2), # noqa: PLR2004
(
PlayResult.score >= 9800000, # noqa: PLR2004
ChartInfo.constant / 10.0
+ 1
+ (PlayResult.score - 9800000) / 200000.0,
),
else_=func.max(
(ChartInfo.constant / 10.0)
+ (PlayResult.score - 9500000) / 300000.0,
0,
),
).label("potential"),
PlayResult.comment,
)
.select_from(Difficulty)
.join(
ChartInfo,
(Difficulty.song_id == ChartInfo.song_id)
& (Difficulty.rating_class == ChartInfo.rating_class),
)
.join(
PlayResult,
(Difficulty.song_id == PlayResult.song_id)
& (Difficulty.rating_class == PlayResult.rating_class),
),
metadata=ModelViewBase.metadata,
cascade_on_drop=False,
)
class PlayResultBest(ModelViewBase, ReprHelper):
__tablename__ = "play_results_best"
id: Mapped[int]
uuid: Mapped[UUID]
song_id: Mapped[str]
rating_class: Mapped[int]
score: Mapped[int]
pure: Mapped[Optional[int]]
pure_early: Mapped[Optional[int]]
pure_late: Mapped[Optional[int]]
shiny_pure: Mapped[Optional[int]]
far: Mapped[Optional[int]]
far_early: Mapped[Optional[int]]
far_late: Mapped[Optional[int]]
lost: Mapped[Optional[int]]
played_at: Mapped[Optional[datetime]]
max_recall: Mapped[Optional[int]]
modifier: Mapped[Optional[int]]
clear_type: Mapped[Optional[int]]
potential: Mapped[float]
comment: Mapped[Optional[str]]
__table__ = create_view(
name=__tablename__,
selectable=select(
*[
col
for col in inspect(PlayResultCalculated).columns
if col.name != "potential"
],
func.max(PlayResultCalculated.potential).label("potential"),
)
.select_from(PlayResultCalculated)
.group_by(PlayResultCalculated.song_id, PlayResultCalculated.rating_class)
.order_by(PlayResultCalculated.potential.desc()),
metadata=ModelViewBase.metadata,
cascade_on_drop=False,
)
class CalculatedPotential(ModelViewBase, ReprHelper):
__tablename__ = "calculated_potential"
b30: Mapped[float]
_select_bests_subquery = (
select(PlayResultBest.potential.label("b30_sum"))
.order_by(PlayResultBest.potential.desc())
.limit(30)
.subquery()
)
__table__ = create_view(
name=__tablename__,
selectable=select(func.avg(_select_bests_subquery.c.b30_sum).label("b30")),
metadata=ModelViewBase.metadata,
cascade_on_drop=False,
)