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