Files
g0v0-server/app/router/v2/ranking.py
2025-08-14 14:55:51 +00:00

115 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
from typing import Literal
from app.database import User
from app.database.statistics import UserStatistics, UserStatisticsResp
from app.dependencies import get_current_user
from app.dependencies.database import get_db
from app.models.score import GameMode
from .router import router
from fastapi import Depends, Path, Query, Security
from pydantic import BaseModel
from sqlmodel import col, select
from sqlmodel.ext.asyncio.session import AsyncSession
class CountryStatistics(BaseModel):
country_code: str
active_users: int
play_count: int
ranked_score: int
performance: int
class CountryResponse(BaseModel):
ranking: list[CountryStatistics]
@router.get(
"/rankings/{ruleset}/country",
response_model=CountryResponse,
name="获取地区排行榜",
description="获取在指定模式下的地区排行榜",
tags=["排行榜"],
)
async def get_country_ranking(
ruleset: GameMode = Path(..., description="指定 ruleset"),
page: int = Query(1, ge=1, description="页码"), # TODO
current_user: User = Security(get_current_user, scopes=["public"]),
session: AsyncSession = Depends(get_db),
):
response = CountryResponse(ranking=[])
countries = (await session.exec(select(User.country_code).distinct())).all()
for country in countries:
statistics = (
await session.exec(
select(UserStatistics).where(
UserStatistics.mode == ruleset,
col(UserStatistics.user).has(country_code=country),
)
)
).all()
pp = 0
country_stats = CountryStatistics(
country_code=country,
active_users=0,
play_count=0,
ranked_score=0,
performance=0,
)
for stat in statistics:
country_stats.active_users += 1
country_stats.play_count += stat.play_count
country_stats.ranked_score += stat.ranked_score
pp += stat.pp
country_stats.performance = round(pp)
response.ranking.append(country_stats)
response.ranking.sort(key=lambda x: x.performance, reverse=True)
return response
class TopUsersResponse(BaseModel):
ranking: list[UserStatisticsResp]
@router.get(
"/rankings/{ruleset}/{type}",
response_model=TopUsersResponse,
name="获取用户排行榜",
description="获取在指定模式下的用户排行榜",
tags=["排行榜"],
)
async def get_user_ranking(
ruleset: GameMode = Path(..., description="指定 ruleset"),
type: Literal["performance", "score"] = Path(
..., description="排名类型performance 表现分 / score 计分成绩总分"
),
country: str | None = Query(None, description="国家代码"),
page: int = Query(1, ge=1, description="页码"),
current_user: User = Security(get_current_user, scopes=["public"]),
session: AsyncSession = Depends(get_db),
):
wheres = [col(UserStatistics.mode) == ruleset]
if type == "performance":
order_by = col(UserStatistics.pp).desc()
else:
order_by = col(UserStatistics.ranked_score).desc()
if country:
wheres.append(col(UserStatistics.user).has(country_code=country.upper()))
statistics_list = await session.exec(
select(UserStatistics)
.where(*wheres)
.order_by(order_by)
.limit(50)
.offset(50 * (page - 1))
)
return TopUsersResponse(
ranking=[
await UserStatisticsResp.from_db(statistics, session, None)
for statistics in statistics_list
]
)