feat(user): add monthly playcounts
This commit is contained in:
@@ -7,6 +7,7 @@ from app.models.user import Country, Page, RankHistory
|
||||
|
||||
from .achievement import UserAchievement, UserAchievementResp
|
||||
from .daily_challenge import DailyChallengeStats, DailyChallengeStatsResp
|
||||
from .monthly_playcounts import MonthlyPlaycounts, MonthlyPlaycountsResp
|
||||
from .statistics import UserStatistics, UserStatisticsResp
|
||||
from .team import Team, TeamMember
|
||||
from .user_account_history import UserAccountHistory, UserAccountHistoryResp
|
||||
@@ -141,6 +142,7 @@ class User(UserBase, table=True):
|
||||
daily_challenge_stats: DailyChallengeStats | None = Relationship(
|
||||
back_populates="user"
|
||||
)
|
||||
monthly_playcounts: list[MonthlyPlaycounts] = Relationship(back_populates="user")
|
||||
|
||||
email: str = Field(max_length=254, unique=True, index=True, exclude=True)
|
||||
priv: int = Field(default=1, exclude=True)
|
||||
@@ -160,6 +162,7 @@ class User(UserBase, table=True):
|
||||
selectinload(cls.achievement), # pyright: ignore[reportArgumentType]
|
||||
joinedload(cls.team_membership).joinedload(TeamMember.team), # pyright: ignore[reportArgumentType]
|
||||
joinedload(cls.daily_challenge_stats), # pyright: ignore[reportArgumentType]
|
||||
selectinload(cls.monthly_playcounts), # pyright: ignore[reportArgumentType]
|
||||
)
|
||||
|
||||
|
||||
@@ -186,7 +189,7 @@ class UserResp(UserBase):
|
||||
account_history: list[UserAccountHistoryResp] = []
|
||||
active_tournament_banners: list[dict] = [] # TODO
|
||||
kudosu: Kudosu = Field(default_factory=lambda: Kudosu(available=0, total=0)) # TODO
|
||||
monthly_playcounts: list = Field(default_factory=list) # TODO
|
||||
monthly_playcounts: list[MonthlyPlaycountsResp] = Field(default_factory=list)
|
||||
unread_pm_count: int = 0 # TODO
|
||||
rank_history: RankHistory | None = None # TODO
|
||||
rank_highest: RankHighest | None = None # TODO
|
||||
@@ -196,7 +199,7 @@ class UserResp(UserBase):
|
||||
cover_url: str = "" # deprecated
|
||||
team: Team | None = None
|
||||
session_verified: bool = True
|
||||
daily_challenge_user_stats: DailyChallengeStatsResp | None = None # TODO
|
||||
daily_challenge_user_stats: DailyChallengeStatsResp | None = None
|
||||
|
||||
# TODO: monthly_playcounts, unread_pm_count, rank_history, user_preferences
|
||||
|
||||
@@ -292,9 +295,36 @@ class UserResp(UserBase):
|
||||
i.mode.value: UserStatisticsResp.from_db(i) for i in obj.statistics
|
||||
}
|
||||
|
||||
if "monthly_playcounts" in include:
|
||||
u.monthly_playcounts = [
|
||||
MonthlyPlaycountsResp.from_db(pc) for pc in obj.monthly_playcounts
|
||||
]
|
||||
|
||||
if "achievements" in include:
|
||||
u.user_achievements = [
|
||||
UserAchievementResp.from_db(ua) for ua in obj.achievement
|
||||
]
|
||||
|
||||
return u
|
||||
|
||||
|
||||
ALL_INCLUDED = [
|
||||
"friends",
|
||||
"team",
|
||||
"account_history",
|
||||
"daily_challenge_user_stats",
|
||||
"statistics",
|
||||
"statistics_rulesets",
|
||||
"achievements",
|
||||
"monthly_playcounts",
|
||||
]
|
||||
|
||||
|
||||
SEARCH_INCLUDED = [
|
||||
"team",
|
||||
"daily_challenge_user_stats",
|
||||
"statistics",
|
||||
"statistics_rulesets",
|
||||
"achievements",
|
||||
"monthly_playcounts",
|
||||
]
|
||||
|
||||
43
app/database/monthly_playcounts.py
Normal file
43
app/database/monthly_playcounts.py
Normal file
@@ -0,0 +1,43 @@
|
||||
from datetime import date
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sqlmodel import (
|
||||
BigInteger,
|
||||
Column,
|
||||
Field,
|
||||
ForeignKey,
|
||||
Relationship,
|
||||
SQLModel,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .lazer_user import User
|
||||
|
||||
|
||||
class MonthlyPlaycounts(SQLModel, table=True):
|
||||
__tablename__ = "monthly_playcounts" # pyright: ignore[reportAssignmentType]
|
||||
|
||||
id: int | None = Field(
|
||||
default=None,
|
||||
sa_column=Column(BigInteger, primary_key=True, autoincrement=True),
|
||||
)
|
||||
user_id: int = Field(
|
||||
sa_column=Column(BigInteger, ForeignKey("lazer_users.id"), index=True)
|
||||
)
|
||||
year: int = Field(index=True)
|
||||
month: int = Field(index=True)
|
||||
playcount: int = Field(default=0)
|
||||
|
||||
user: "User" = Relationship(back_populates="monthly_playcounts")
|
||||
|
||||
|
||||
class MonthlyPlaycountsResp(SQLModel):
|
||||
start_date: date
|
||||
count: int
|
||||
|
||||
@classmethod
|
||||
def from_db(cls, db_model: MonthlyPlaycounts) -> "MonthlyPlaycountsResp":
|
||||
return cls(
|
||||
start_date=date(db_model.year, db_model.month, 1),
|
||||
count=db_model.playcount,
|
||||
)
|
||||
@@ -1,6 +1,6 @@
|
||||
import asyncio
|
||||
from collections.abc import Sequence
|
||||
from datetime import UTC, datetime
|
||||
from datetime import UTC, date, datetime
|
||||
import math
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
@@ -30,6 +30,7 @@ from .beatmap import Beatmap, BeatmapResp
|
||||
from .beatmapset import Beatmapset, BeatmapsetResp
|
||||
from .best_score import BestScore
|
||||
from .lazer_user import User, UserResp
|
||||
from .monthly_playcounts import MonthlyPlaycounts
|
||||
from .score_token import ScoreToken
|
||||
|
||||
from redis import Redis
|
||||
@@ -501,8 +502,22 @@ async def process_user(
|
||||
previous_score_best = await get_user_best_score_in_beatmap(
|
||||
session, score.beatmap_id, user.id, score.gamemode
|
||||
)
|
||||
statistics = None
|
||||
add_to_db = False
|
||||
mouthly_playcount = (
|
||||
await session.exec(
|
||||
select(MonthlyPlaycounts).where(
|
||||
MonthlyPlaycounts.user_id == user.id,
|
||||
MonthlyPlaycounts.year == date.today().year,
|
||||
MonthlyPlaycounts.month == date.today().month,
|
||||
)
|
||||
)
|
||||
).first()
|
||||
if mouthly_playcount is None:
|
||||
mouthly_playcount = MonthlyPlaycounts(
|
||||
user_id=user.id, year=date.today().year, month=date.today().month
|
||||
)
|
||||
add_to_db = True
|
||||
statistics = None
|
||||
for i in user.statistics:
|
||||
if i.mode == score.gamemode.value:
|
||||
statistics = i
|
||||
@@ -547,6 +562,7 @@ async def process_user(
|
||||
statistics.level_current = calculate_score_to_level(statistics.ranked_score)
|
||||
statistics.maximum_combo = max(statistics.maximum_combo, score.max_combo)
|
||||
statistics.play_count += 1
|
||||
mouthly_playcount.playcount += 1
|
||||
statistics.play_time += int((score.ended_at - score.started_at).total_seconds())
|
||||
statistics.count_100 += score.n100 + score.nkatu
|
||||
statistics.count_300 += score.n300 + score.ngeki
|
||||
@@ -569,9 +585,8 @@ async def process_user(
|
||||
acc_sum = clamp(acc_sum, 0.0, 100.0)
|
||||
statistics.pp = pp_sum
|
||||
statistics.hit_accuracy = acc_sum
|
||||
|
||||
if add_to_db:
|
||||
session.add(statistics)
|
||||
session.add(mouthly_playcount)
|
||||
await session.commit()
|
||||
await session.refresh(user)
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from app.database import User, UserResp
|
||||
from app.database.lazer_user import ALL_INCLUDED
|
||||
from app.dependencies import get_current_user
|
||||
from app.dependencies.database import get_db
|
||||
from app.models.score import GameMode
|
||||
@@ -21,14 +22,6 @@ async def get_user_info_default(
|
||||
return await UserResp.from_db(
|
||||
current_user,
|
||||
session,
|
||||
[
|
||||
"friends",
|
||||
"team",
|
||||
"account_history",
|
||||
"daily_challenge_user_stats",
|
||||
"statistics",
|
||||
"statistics_rulesets",
|
||||
"achievements",
|
||||
],
|
||||
ALL_INCLUDED,
|
||||
ruleset,
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from app.database import User, UserResp
|
||||
from app.database.lazer_user import SEARCH_INCLUDED
|
||||
from app.dependencies.database import get_db
|
||||
from app.models.score import GameMode
|
||||
|
||||
@@ -17,15 +18,6 @@ class BatchUserResponse(BaseModel):
|
||||
users: list[UserResp]
|
||||
|
||||
|
||||
SEARCH_INCLUDE = [
|
||||
"team",
|
||||
"daily_challenge_user_stats",
|
||||
"statistics",
|
||||
"statistics_rulesets",
|
||||
"achievements",
|
||||
]
|
||||
|
||||
|
||||
@router.get("/users", response_model=BatchUserResponse)
|
||||
@router.get("/users/lookup", response_model=BatchUserResponse)
|
||||
@router.get("/users/lookup/", response_model=BatchUserResponse)
|
||||
@@ -54,7 +46,7 @@ async def get_users(
|
||||
await UserResp.from_db(
|
||||
searched_user,
|
||||
session,
|
||||
include=SEARCH_INCLUDE,
|
||||
include=SEARCH_INCLUDED,
|
||||
)
|
||||
for searched_user in searched_users
|
||||
]
|
||||
@@ -85,6 +77,6 @@ async def get_user_info(
|
||||
return await UserResp.from_db(
|
||||
searched_user,
|
||||
session,
|
||||
include=SEARCH_INCLUDE,
|
||||
include=SEARCH_INCLUDED,
|
||||
ruleset=ruleset,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user