mirror of
https://github.com/nihilvux/bancho.py.git
synced 2025-09-16 02:28:38 -07:00
247 lines
7.2 KiB
Python
247 lines
7.2 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import TypedDict
|
|
from typing import cast
|
|
|
|
from sqlalchemy import Column
|
|
from sqlalchemy import DateTime
|
|
from sqlalchemy import Index
|
|
from sqlalchemy import Integer
|
|
from sqlalchemy import String
|
|
from sqlalchemy import func
|
|
from sqlalchemy import insert
|
|
from sqlalchemy import select
|
|
from sqlalchemy import update
|
|
from sqlalchemy.dialects.mysql import FLOAT
|
|
from sqlalchemy.dialects.mysql import TINYINT
|
|
|
|
import app.state.services
|
|
from app._typing import UNSET
|
|
from app._typing import _UnsetSentinel
|
|
from app.repositories import Base
|
|
|
|
|
|
class ScoresTable(Base):
|
|
__tablename__ = "scores"
|
|
|
|
id = Column("id", Integer, nullable=False, primary_key=True, autoincrement=True)
|
|
map_md5 = Column("map_md5", String(32), nullable=False)
|
|
score = Column("score", Integer, nullable=False)
|
|
pp = Column("pp", FLOAT(precision=6, scale=3), nullable=False)
|
|
acc = Column("acc", FLOAT(precision=6, scale=3), nullable=False)
|
|
max_combo = Column("max_combo", Integer, nullable=False)
|
|
mods = Column("mods", Integer, nullable=False)
|
|
n300 = Column("n300", Integer, nullable=False)
|
|
n100 = Column("n100", Integer, nullable=False)
|
|
n50 = Column("n50", Integer, nullable=False)
|
|
nmiss = Column("nmiss", Integer, nullable=False)
|
|
ngeki = Column("ngeki", Integer, nullable=False)
|
|
nkatu = Column("nkatu", Integer, nullable=False)
|
|
grade = Column("grade", String(2), nullable=False, server_default="N")
|
|
status = Column("status", Integer, nullable=False)
|
|
mode = Column("mode", Integer, nullable=False)
|
|
play_time = Column("play_time", DateTime, nullable=False)
|
|
time_elapsed = Column("time_elapsed", Integer, nullable=False)
|
|
client_flags = Column("client_flags", Integer, nullable=False)
|
|
userid = Column("userid", Integer, nullable=False)
|
|
perfect = Column("perfect", TINYINT(1), nullable=False)
|
|
online_checksum = Column("online_checksum", String(32), nullable=False)
|
|
|
|
__table_args__ = (
|
|
Index("scores_map_md5_index", map_md5),
|
|
Index("scores_score_index", score),
|
|
Index("scores_pp_index", pp),
|
|
Index("scores_mods_index", mods),
|
|
Index("scores_status_index", status),
|
|
Index("scores_mode_index", mode),
|
|
Index("scores_play_time_index", play_time),
|
|
Index("scores_userid_index", userid),
|
|
Index("scores_online_checksum_index", online_checksum),
|
|
)
|
|
|
|
|
|
READ_PARAMS = (
|
|
ScoresTable.id,
|
|
ScoresTable.map_md5,
|
|
ScoresTable.score,
|
|
ScoresTable.pp,
|
|
ScoresTable.acc,
|
|
ScoresTable.max_combo,
|
|
ScoresTable.mods,
|
|
ScoresTable.n300,
|
|
ScoresTable.n100,
|
|
ScoresTable.n50,
|
|
ScoresTable.nmiss,
|
|
ScoresTable.ngeki,
|
|
ScoresTable.nkatu,
|
|
ScoresTable.grade,
|
|
ScoresTable.status,
|
|
ScoresTable.mode,
|
|
ScoresTable.play_time,
|
|
ScoresTable.time_elapsed,
|
|
ScoresTable.client_flags,
|
|
ScoresTable.userid,
|
|
ScoresTable.perfect,
|
|
ScoresTable.online_checksum,
|
|
)
|
|
|
|
|
|
class Score(TypedDict):
|
|
id: int
|
|
map_md5: str
|
|
score: int
|
|
pp: float
|
|
acc: float
|
|
max_combo: int
|
|
mods: int
|
|
n300: int
|
|
n100: int
|
|
n50: int
|
|
nmiss: int
|
|
ngeki: int
|
|
nkatu: int
|
|
grade: str
|
|
status: int
|
|
mode: int
|
|
play_time: datetime
|
|
time_elapsed: int
|
|
client_flags: int
|
|
userid: int
|
|
perfect: int
|
|
online_checksum: str
|
|
|
|
|
|
async def create(
|
|
map_md5: str,
|
|
score: int,
|
|
pp: float,
|
|
acc: float,
|
|
max_combo: int,
|
|
mods: int,
|
|
n300: int,
|
|
n100: int,
|
|
n50: int,
|
|
nmiss: int,
|
|
ngeki: int,
|
|
nkatu: int,
|
|
grade: str,
|
|
status: int,
|
|
mode: int,
|
|
play_time: datetime,
|
|
time_elapsed: int,
|
|
client_flags: int,
|
|
user_id: int,
|
|
perfect: int,
|
|
online_checksum: str,
|
|
) -> Score:
|
|
insert_stmt = insert(ScoresTable).values(
|
|
map_md5=map_md5,
|
|
score=score,
|
|
pp=pp,
|
|
acc=acc,
|
|
max_combo=max_combo,
|
|
mods=mods,
|
|
n300=n300,
|
|
n100=n100,
|
|
n50=n50,
|
|
nmiss=nmiss,
|
|
ngeki=ngeki,
|
|
nkatu=nkatu,
|
|
grade=grade,
|
|
status=status,
|
|
mode=mode,
|
|
play_time=play_time,
|
|
time_elapsed=time_elapsed,
|
|
client_flags=client_flags,
|
|
userid=user_id,
|
|
perfect=perfect,
|
|
online_checksum=online_checksum,
|
|
)
|
|
rec_id = await app.state.services.database.execute(insert_stmt)
|
|
|
|
select_stmt = select(*READ_PARAMS).where(ScoresTable.id == rec_id)
|
|
_score = await app.state.services.database.fetch_one(select_stmt)
|
|
assert _score is not None
|
|
return cast(Score, _score)
|
|
|
|
|
|
async def fetch_one(id: int) -> Score | None:
|
|
select_stmt = select(*READ_PARAMS).where(ScoresTable.id == id)
|
|
_score = await app.state.services.database.fetch_one(select_stmt)
|
|
return cast(Score | None, _score)
|
|
|
|
|
|
async def fetch_count(
|
|
map_md5: str | None = None,
|
|
mods: int | None = None,
|
|
status: int | None = None,
|
|
mode: int | None = None,
|
|
user_id: int | None = None,
|
|
) -> int:
|
|
select_stmt = select(func.count().label("count")).select_from(ScoresTable)
|
|
if map_md5 is not None:
|
|
select_stmt = select_stmt.where(ScoresTable.map_md5 == map_md5)
|
|
if mods is not None:
|
|
select_stmt = select_stmt.where(ScoresTable.mods == mods)
|
|
if status is not None:
|
|
select_stmt = select_stmt.where(ScoresTable.status == status)
|
|
if mode is not None:
|
|
select_stmt = select_stmt.where(ScoresTable.mode == mode)
|
|
if user_id is not None:
|
|
select_stmt = select_stmt.where(ScoresTable.userid == user_id)
|
|
|
|
rec = await app.state.services.database.fetch_one(select_stmt)
|
|
assert rec is not None
|
|
return cast(int, rec["count"])
|
|
|
|
|
|
async def fetch_many(
|
|
map_md5: str | None = None,
|
|
mods: int | None = None,
|
|
status: int | None = None,
|
|
mode: int | None = None,
|
|
user_id: int | None = None,
|
|
page: int | None = None,
|
|
page_size: int | None = None,
|
|
) -> list[Score]:
|
|
select_stmt = select(*READ_PARAMS)
|
|
if map_md5 is not None:
|
|
select_stmt = select_stmt.where(ScoresTable.map_md5 == map_md5)
|
|
if mods is not None:
|
|
select_stmt = select_stmt.where(ScoresTable.mods == mods)
|
|
if status is not None:
|
|
select_stmt = select_stmt.where(ScoresTable.status == status)
|
|
if mode is not None:
|
|
select_stmt = select_stmt.where(ScoresTable.mode == mode)
|
|
if user_id is not None:
|
|
select_stmt = select_stmt.where(ScoresTable.userid == user_id)
|
|
|
|
if page is not None and page_size is not None:
|
|
select_stmt = select_stmt.limit(page_size).offset((page - 1) * page_size)
|
|
|
|
scores = await app.state.services.database.fetch_all(select_stmt)
|
|
return cast(list[Score], scores)
|
|
|
|
|
|
async def partial_update(
|
|
id: int,
|
|
pp: float | _UnsetSentinel = UNSET,
|
|
status: int | _UnsetSentinel = UNSET,
|
|
) -> Score | None:
|
|
"""Update an existing score."""
|
|
update_stmt = update(ScoresTable).where(ScoresTable.id == id)
|
|
if not isinstance(pp, _UnsetSentinel):
|
|
update_stmt = update_stmt.values(pp=pp)
|
|
if not isinstance(status, _UnsetSentinel):
|
|
update_stmt = update_stmt.values(status=status)
|
|
|
|
await app.state.services.database.execute(update_stmt)
|
|
|
|
select_stmt = select(*READ_PARAMS).where(ScoresTable.id == id)
|
|
_score = await app.state.services.database.fetch_one(select_stmt)
|
|
return cast(Score | None, _score)
|
|
|
|
|
|
# TODO: delete
|