feat(playlist): support leaderboard
**UNTESTED**
This commit is contained in:
@@ -15,7 +15,7 @@ from .lazer_user import (
|
||||
User,
|
||||
UserResp,
|
||||
)
|
||||
from .playlist_attempts import ItemAttemptsCount
|
||||
from .playlist_attempts import ItemAttemptsCount, ItemAttemptsResp
|
||||
from .playlist_best_score import PlaylistBestScore
|
||||
from .playlists import Playlist, PlaylistResp
|
||||
from .pp_best_score import PPBestScore
|
||||
@@ -50,6 +50,7 @@ __all__ = [
|
||||
"DailyChallengeStatsResp",
|
||||
"FavouriteBeatmapset",
|
||||
"ItemAttemptsCount",
|
||||
"ItemAttemptsResp",
|
||||
"MultiplayerScores",
|
||||
"OAuthToken",
|
||||
"PPBestScore",
|
||||
|
||||
@@ -1,9 +1,116 @@
|
||||
from sqlmodel import Field, SQLModel
|
||||
from .lazer_user import User, UserResp
|
||||
from .playlist_best_score import PlaylistBestScore
|
||||
|
||||
from sqlmodel import (
|
||||
BigInteger,
|
||||
Column,
|
||||
Field,
|
||||
ForeignKey,
|
||||
Relationship,
|
||||
SQLModel,
|
||||
col,
|
||||
func,
|
||||
select,
|
||||
)
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
|
||||
|
||||
class ItemAttemptsCount(SQLModel, table=True):
|
||||
__tablename__ = "item_attempts_count" # pyright: ignore[reportAssignmentType]
|
||||
id: int = Field(foreign_key="room_playlists.db_id", primary_key=True, index=True)
|
||||
class ItemAttemptsCountBase(SQLModel):
|
||||
room_id: int = Field(foreign_key="rooms.id", index=True)
|
||||
attempts: int = Field(default=0)
|
||||
passed: int = Field(default=0)
|
||||
completed: int = Field(default=0)
|
||||
user_id: int = Field(
|
||||
sa_column=Column(BigInteger, ForeignKey("lazer_users.id"), index=True)
|
||||
)
|
||||
accuracy: float = 0.0
|
||||
pp: float = 0
|
||||
total_score: int = 0
|
||||
|
||||
|
||||
class ItemAttemptsCount(ItemAttemptsCountBase, table=True):
|
||||
__tablename__ = "item_attempts_count" # pyright: ignore[reportAssignmentType]
|
||||
id: int | None = Field(
|
||||
default=None, foreign_key="room_playlists.db_id", primary_key=True
|
||||
)
|
||||
|
||||
user: User = Relationship()
|
||||
|
||||
async def get_position(self, session: AsyncSession) -> int:
|
||||
rownum = (
|
||||
func.row_number()
|
||||
.over(
|
||||
partition_by=col(ItemAttemptsCountBase.room_id),
|
||||
order_by=col(ItemAttemptsCountBase.total_score).desc(),
|
||||
)
|
||||
.label("rn")
|
||||
)
|
||||
subq = select(ItemAttemptsCountBase, rownum).subquery()
|
||||
stmt = select(subq.c.rn).where(subq.c.user_id == self.user_id)
|
||||
result = await session.exec(stmt)
|
||||
return result.one()
|
||||
|
||||
async def update(self, session: AsyncSession):
|
||||
playlist_scores = (
|
||||
await session.exec(
|
||||
select(PlaylistBestScore).where(
|
||||
PlaylistBestScore.room_id == self.room_id,
|
||||
PlaylistBestScore.user_id == self.user_id,
|
||||
)
|
||||
)
|
||||
).all()
|
||||
self.attempts = sum(score.attempts for score in playlist_scores)
|
||||
self.total_score = sum(score.total_score for score in playlist_scores)
|
||||
self.pp = sum(score.score.pp for score in playlist_scores)
|
||||
self.completed = len(playlist_scores)
|
||||
self.accuracy = (
|
||||
sum(score.score.accuracy * score.attempts for score in playlist_scores)
|
||||
/ self.completed
|
||||
if self.completed > 0
|
||||
else 0.0
|
||||
)
|
||||
await session.commit()
|
||||
await session.refresh(self)
|
||||
|
||||
@classmethod
|
||||
async def get_or_create(
|
||||
cls,
|
||||
room_id: int,
|
||||
user_id: int,
|
||||
session: AsyncSession,
|
||||
) -> "ItemAttemptsCount":
|
||||
item_attempts = await session.exec(
|
||||
select(cls).where(
|
||||
cls.room_id == room_id,
|
||||
cls.user_id == user_id,
|
||||
)
|
||||
)
|
||||
item_attempts = item_attempts.first()
|
||||
if item_attempts is None:
|
||||
item_attempts = cls(room_id=room_id, user_id=user_id)
|
||||
session.add(item_attempts)
|
||||
await session.commit()
|
||||
await session.refresh(item_attempts)
|
||||
await item_attempts.update(session)
|
||||
return item_attempts
|
||||
|
||||
|
||||
class ItemAttemptsResp(ItemAttemptsCountBase):
|
||||
user: UserResp | None = None
|
||||
position: int | None = None
|
||||
|
||||
@classmethod
|
||||
async def from_db(
|
||||
cls,
|
||||
item_attempts: ItemAttemptsCount,
|
||||
session: AsyncSession,
|
||||
include: list[str] = [],
|
||||
) -> "ItemAttemptsResp":
|
||||
resp = cls.model_validate(item_attempts)
|
||||
resp.user = await UserResp.from_db(
|
||||
item_attempts.user,
|
||||
session=session,
|
||||
include=["statistics", "team", "daily_challenge_user_stats"],
|
||||
)
|
||||
if "position" in include:
|
||||
resp.position = await item_attempts.get_position(session)
|
||||
return resp
|
||||
|
||||
@@ -32,6 +32,7 @@ class PlaylistBestScore(SQLModel, table=True):
|
||||
room_id: int = Field(foreign_key="rooms.id", index=True)
|
||||
playlist_id: int = Field(foreign_key="room_playlists.id", index=True)
|
||||
total_score: int = Field(default=0, sa_column=Column(BigInteger))
|
||||
attempts: int = Field(default=0) # playlist
|
||||
|
||||
user: User = Relationship()
|
||||
score: "Score" = Relationship(
|
||||
@@ -72,6 +73,7 @@ async def process_playlist_best_score(
|
||||
else:
|
||||
previous.score_id = score_id
|
||||
previous.total_score = total_score
|
||||
previous.attempts += 1
|
||||
await session.commit()
|
||||
await redis.decr(f"multiplayer:{room_id}:gameplay:players")
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ from app.models.room import (
|
||||
)
|
||||
|
||||
from .lazer_user import User, UserResp
|
||||
from .playlist_attempts import ItemAttemptsCount
|
||||
from .playlists import Playlist, PlaylistResp
|
||||
|
||||
from sqlmodel import (
|
||||
@@ -67,13 +66,6 @@ class Room(RoomBase, table=True):
|
||||
"overlaps": "room",
|
||||
}
|
||||
)
|
||||
# playlist_item_attempts: list["ItemAttemptsCount"] = Relationship(
|
||||
# sa_relationship_kwargs={
|
||||
# "lazy": "joined",
|
||||
# "cascade": "all, delete-orphan",
|
||||
# "primaryjoin": "ItemAttemptsCount.room_id == Room.id",
|
||||
# }
|
||||
# )
|
||||
|
||||
|
||||
class RoomResp(RoomBase):
|
||||
@@ -84,7 +76,6 @@ class RoomResp(RoomBase):
|
||||
playlist_item_stats: RoomPlaylistItemStats | None = None
|
||||
difficulty_range: RoomDifficultyRange | None = None
|
||||
current_playlist_item: PlaylistResp | None = None
|
||||
playlist_item_attempts: list[ItemAttemptsCount] = []
|
||||
|
||||
@classmethod
|
||||
async def from_db(cls, room: Room) -> "RoomResp":
|
||||
@@ -112,7 +103,6 @@ class RoomResp(RoomBase):
|
||||
resp.playlist_item_stats = stats
|
||||
resp.difficulty_range = difficulty_range
|
||||
resp.current_playlist_item = resp.playlist[-1] if resp.playlist else None
|
||||
# resp.playlist_item_attempts = room.playlist_item_attempts
|
||||
|
||||
return resp
|
||||
|
||||
|
||||
Reference in New Issue
Block a user