适配新 records API

This commit is contained in:
2024-08-09 16:23:12 +08:00
parent 429f99f77e
commit 02e703ea91
6 changed files with 84 additions and 17 deletions

View File

@@ -7,12 +7,12 @@ from sqlalchemy.orm import Mapped, MappedAsDataclass, mapped_column
from ....db.models import PydanticType
from .schemas.base import SuccessModel
from .typing import Summaries
from .typing import Records, Summaries
class TETRIOHistoricalData(MappedAsDataclass, Model):
id: Mapped[int] = mapped_column(init=False, primary_key=True)
user_unique_identifier: Mapped[str] = mapped_column(String(24), index=True)
api_type: Mapped[Literal['User Info', Summaries]] = mapped_column(String(32), index=True)
api_type: Mapped[Literal['User Info', Records, Summaries]] = mapped_column(String(32), index=True)
data: Mapped[SuccessModel] = mapped_column(PydanticType(get_model=[SuccessModel.__subclasses__], models=set()))
update_time: Mapped[datetime] = mapped_column(DateTime, index=True)

View File

@@ -1,5 +1,6 @@
from enum import Enum
from types import MappingProxyType
from typing import Literal, overload
from typing import Literal, NamedTuple, cast, overload
from async_lru import alru_cache
from nonebot.compat import type_validate_json
@@ -11,24 +12,47 @@ from ..constant import BASE_URL, USER_ID, USER_NAME
from .cache import Cache
from .models import TETRIOHistoricalData
from .schemas.base import FailedModel
from .schemas.records.solo import Solo as SoloRecord
from .schemas.records.solo import SoloSuccessModel as RecordsSoloSuccessModel
from .schemas.summaries import (
AchievementsSuccessModel,
SoloSuccessModel,
SummariesModel,
ZenithSuccessModel,
ZenSuccessModel,
)
from .schemas.summaries import (
SoloSuccessModel as SummariesSoloSuccessModel,
)
from .schemas.summaries.base import User as SummariesUser
from .schemas.user import User
from .schemas.user_info import UserInfo, UserInfoSuccess
from .typing import Summaries
from .typing import Records, Summaries
class RecordModeType(str, Enum):
Sprint = '40l'
Blitz = 'blitz'
class RecordType(str, Enum):
Top = 'top'
Recent = 'recent'
Progression = 'progression'
class RecordKey(NamedTuple):
mode_type: RecordModeType
record_type: RecordType
def to_records(self) -> Records:
return cast(Records, f'{self.mode_type.value}_{self.record_type.value}')
class Player:
__SUMMARIES_MAPPING: MappingProxyType[Summaries, type[SummariesModel]] = MappingProxyType(
{
'40l': SoloSuccessModel,
'blitz': SoloSuccessModel,
'40l': SummariesSoloSuccessModel,
'blitz': SummariesSoloSuccessModel,
'zenith': ZenithSuccessModel,
'zenithex': ZenithSuccessModel,
'zen': ZenSuccessModel,
@@ -58,6 +82,7 @@ class Player:
self.__user: User | None = None
self._user_info: UserInfoSuccess | None = None
self._summaries: dict[Summaries, SummariesModel] = {}
self._records: dict[RecordKey, RecordsSoloSuccessModel] = {}
@property
def _request_user_parameter(self) -> str:
@@ -108,7 +133,7 @@ class Player:
return self._user_info
@overload
async def get_summaries(self, summaries_type: Literal['40l', 'blitz']) -> SoloSuccessModel: ...
async def get_summaries(self, summaries_type: Literal['40l', 'blitz']) -> SummariesSoloSuccessModel: ...
@overload
async def get_summaries(self, summaries_type: Literal['zenith', 'zenithex']) -> ZenithSuccessModel: ...
@overload
@@ -142,12 +167,12 @@ class Player:
@property
@alru_cache
async def sprint(self) -> SoloSuccessModel:
async def sprint(self) -> SummariesSoloSuccessModel:
return await self.get_summaries('40l')
@property
@alru_cache
async def blitz(self) -> SoloSuccessModel:
async def blitz(self) -> SummariesSoloSuccessModel:
return await self.get_summaries('blitz')
@property
@@ -185,3 +210,33 @@ class Player:
if (user := (await self._get_local_summaries_user())) is not None:
return user.banner_revision
return (await self.get_info()).data.banner_revision
async def get_records(self, mode_type: RecordModeType, records_type: RecordType) -> RecordsSoloSuccessModel:
if (record_key := RecordKey(mode_type, records_type)) not in self._records:
raw_records = await Cache.get(
splice_url(
[
BASE_URL,
'users/',
f'{self._request_user_parameter}/',
'records/',
f'{mode_type}/',
records_type,
]
)
)
records: RecordsSoloSuccessModel | FailedModel = type_validate_json(SoloRecord, raw_records) # type: ignore[arg-type]
if isinstance(records, FailedModel):
msg = f'用户Summaries数据请求错误:\n{records.error}'
raise RequestError(msg)
self._records[record_key] = records
await anti_duplicate_add(
TETRIOHistoricalData,
TETRIOHistoricalData(
user_unique_identifier=(await self.user).unique_identifier,
api_type=record_key.to_records(),
data=records,
update_time=records.cache.cached_at,
),
)
return self._records[record_key]

View File

@@ -1,6 +1,8 @@
from typing import TypeAlias
from pydantic import BaseModel
from ..base import SuccessModel
from ..base import FailedModel, SuccessModel
from ..base.solo import Record
@@ -8,5 +10,8 @@ class Data(BaseModel):
entries: list[Record]
class Model(SuccessModel):
class SoloSuccessModel(SuccessModel):
data: Data
Solo: TypeAlias = SoloSuccessModel | FailedModel

View File

@@ -1,5 +1,5 @@
from .achievements import Achievements, AchievementsSuccessModel
from .solo import Blitz, SoloSuccessModel, Sprint
from .solo import Solo, SoloSuccessModel
from .zen import Zen, ZenSuccessModel
from .zenith import Zenith, ZenithEx, ZenithSuccessModel
@@ -8,8 +8,7 @@ SummariesModel = AchievementsSuccessModel | SoloSuccessModel | ZenSuccessModel |
__all__ = [
'Achievements',
'AchievementsSuccessModel',
'Blitz',
'Sprint',
'Solo',
'SoloSuccessModel',
'Zen',
'ZenSuccessModel',

View File

@@ -21,5 +21,4 @@ class SoloSuccessModel(SuccessModel):
data: Data
Sprint: TypeAlias = SoloSuccessModel | FailedModel
Blitz: TypeAlias = SoloSuccessModel | FailedModel
Solo: TypeAlias = SoloSuccessModel | FailedModel

View File

@@ -31,3 +31,12 @@ Summaries = Literal[
'zen',
'achievements',
]
Records = Literal[
'40l_top',
'40l_recent',
'40l_progression',
'blitz_top',
'blitz_recent',
'blitz_progression',
]