2023-05-05 04:06:43 +00:00
|
|
|
import re
|
|
|
|
from typing import Any, Optional, cast, List, Dict
|
|
|
|
|
|
|
|
from pydantic import validator, Field
|
|
|
|
|
|
|
|
from simnet.models.base import APIModel
|
|
|
|
from simnet.models.genshin.chronicle.abyss import SpiralAbyssPair
|
|
|
|
from simnet.models.genshin.chronicle.characters import PartialCharacter, Character
|
|
|
|
from simnet.models.lab.record import UserInfo
|
|
|
|
|
|
|
|
|
|
|
|
class Stats(APIModel):
|
|
|
|
"""Overall user stats.
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
achievements (int): Number of achievements completed by the user.
|
|
|
|
days_active (int): Number of days the user has been active.
|
|
|
|
characters (int): Number of characters owned by the user.
|
|
|
|
spiral_abyss (str): The floor and level reached by the user in Spiral Abyss.
|
|
|
|
anemoculi (int): Number of Anemoculus collected by the user.
|
|
|
|
geoculi (int): Number of Geoculus collected by the user.
|
|
|
|
dendroculi (int): Number of Dendroculus collected by the user.
|
|
|
|
electroculi (int): Number of Electroculus collected by the user.
|
2023-08-21 02:05:36 +00:00
|
|
|
hydroculi (int): Number of Hydroculus opened by the user.
|
2023-05-05 04:06:43 +00:00
|
|
|
common_chests (int): Number of Common Chests opened by the user.
|
|
|
|
exquisite_chests (int): Number of Exquisite Chests opened by the user.
|
|
|
|
precious_chests (int): Number of Precious Chests opened by the user.
|
|
|
|
luxurious_chests (int): Number of Luxurious Chests opened by the user.
|
|
|
|
remarkable_chests (int): Number of Magic Chests opened by the user.
|
|
|
|
unlocked_waypoints (int): Number of waypoints unlocked by the user.
|
|
|
|
unlocked_domains (int): Number of domains unlocked by the user.
|
|
|
|
"""
|
|
|
|
|
2023-05-08 00:53:48 +00:00
|
|
|
achievements: int = Field(aliases="achievement_number")
|
|
|
|
days_active: int = Field(aliases="active_day_number")
|
|
|
|
characters: int = Field(aliases="avatar_number")
|
|
|
|
spiral_abyss: str = Field(aliases="spiral_abyss")
|
|
|
|
anemoculi: int = Field(aliases="anemoculus_number")
|
|
|
|
geoculi: int = Field(aliases="geoculus_number")
|
|
|
|
dendroculi: int = Field(aliases="dendroculus_number")
|
|
|
|
electroculi: int = Field(aliases="electroculus_number")
|
2023-08-21 02:05:36 +00:00
|
|
|
hydroculi: int = Field(aliases="hydroculus_number")
|
2023-05-08 00:53:48 +00:00
|
|
|
common_chests: int = Field(aliases="common_chest_number")
|
|
|
|
exquisite_chests: int = Field(aliases="exquisite_chest_number")
|
|
|
|
precious_chests: int = Field(aliases="precious_chest_number")
|
|
|
|
luxurious_chests: int = Field(aliases="luxurious_chest_number")
|
|
|
|
remarkable_chests: int = Field(aliases="magic_chest_number")
|
|
|
|
unlocked_waypoints: int = Field(aliases="way_point_number")
|
|
|
|
unlocked_domains: int = Field(aliases="domain_number")
|
2023-05-05 04:06:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Offering(APIModel):
|
|
|
|
"""Exploration offering.
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
name (str): The name of the offering.
|
|
|
|
level (int): The level of the offering.
|
|
|
|
icon (str): The icon of the offering.
|
|
|
|
"""
|
|
|
|
|
|
|
|
name: str
|
|
|
|
level: int
|
|
|
|
icon: str = ""
|
|
|
|
|
|
|
|
|
|
|
|
class Exploration(APIModel):
|
|
|
|
"""Exploration data.
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
id (int): The ID of the exploration.
|
|
|
|
parent_id (int): The ID of the parent exploration.
|
|
|
|
name (str): The name of the exploration.
|
|
|
|
raw_explored (int): The raw percentage of the exploration completed.
|
|
|
|
type (str): The type of the exploration.
|
|
|
|
level (int): The level of the exploration.
|
|
|
|
icon (str): The icon of the exploration.
|
|
|
|
inner_icon (str): The inner icon of the exploration.
|
|
|
|
background_image (str): The background image of the exploration.
|
|
|
|
cover (str): The cover of the exploration.
|
|
|
|
map_url (str): The URL of the exploration map.
|
|
|
|
offerings (List[Offering]): The list of offerings of the exploration.
|
|
|
|
"""
|
|
|
|
|
|
|
|
id: int
|
|
|
|
parent_id: int
|
|
|
|
name: str
|
|
|
|
raw_explored: int = Field(alias="exploration_percentage")
|
|
|
|
|
|
|
|
# deprecated in a sense:
|
|
|
|
type: str
|
|
|
|
level: int
|
|
|
|
|
|
|
|
icon: str
|
|
|
|
inner_icon: str
|
|
|
|
background_image: str
|
|
|
|
cover: str
|
|
|
|
map_url: str
|
|
|
|
|
|
|
|
offerings: List[Offering]
|
|
|
|
|
|
|
|
@property
|
|
|
|
def explored(self) -> float:
|
|
|
|
"""The percentage of the exploration completed.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
The percentage of the exploration completed.
|
|
|
|
"""
|
|
|
|
return self.raw_explored / 10
|
|
|
|
|
|
|
|
@validator("offerings", pre=True)
|
2023-05-12 03:24:06 +00:00
|
|
|
def add_base_offering(cls, offerings: List[Any], values: Dict[str, Any]) -> List[Any]:
|
2023-05-05 04:06:43 +00:00
|
|
|
"""Add a base offering if the exploration type is Reputation.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
offerings (List[Any]): The list of offerings.
|
|
|
|
values (Dict[str, Any]): The dict of attribute values.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
The updated list of offerings.
|
|
|
|
"""
|
2023-05-12 03:24:06 +00:00
|
|
|
if values["type"] == "Reputation" and not any(values["type"] == o["name"] for o in offerings):
|
2023-05-05 04:06:43 +00:00
|
|
|
offerings = [*offerings, dict(name=values["type"], level=values["level"])]
|
|
|
|
|
|
|
|
return offerings
|
|
|
|
|
|
|
|
|
|
|
|
class TeapotRealm(APIModel):
|
|
|
|
"""A specific teapot realm.
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
name (str): The name of the teapot realm.
|
|
|
|
icon (str): The icon of the teapot realm.
|
|
|
|
"""
|
|
|
|
|
|
|
|
name: str
|
|
|
|
icon: str
|
|
|
|
|
|
|
|
@property
|
|
|
|
def id(self) -> int:
|
|
|
|
"""The ID of the teapot realm.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
The ID of the teapot realm.
|
|
|
|
"""
|
|
|
|
match = re.search(r"\d", self.icon)
|
|
|
|
return int(match.group()) if match else 0
|
|
|
|
|
|
|
|
|
|
|
|
class Teapot(APIModel):
|
|
|
|
"""User's Serenitea Teapot.
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
realms (List[TeapotRealm]): The list of teapot realms of the user.
|
|
|
|
level (int): The level of the teapot.
|
|
|
|
visitors (int): The number of visitors to the teapot.
|
|
|
|
comfort (int): The comfort level of the teapot.
|
|
|
|
items (int): The number of items in the teapot.
|
|
|
|
comfort_name (str): The name of the comfort level.
|
|
|
|
comfort_icon (str): The icon of the comfort level.
|
|
|
|
"""
|
|
|
|
|
|
|
|
realms: List[TeapotRealm]
|
|
|
|
level: int
|
|
|
|
visitors: int = Field(alias="visit_num")
|
|
|
|
comfort: int = Field(alias="comfort_num")
|
|
|
|
items: int = Field(alias="item_num")
|
|
|
|
comfort_name: str = Field(alias="comfort_level_name")
|
|
|
|
comfort_icon: str = Field(alias="comfort_level_icon")
|
|
|
|
|
|
|
|
|
|
|
|
class PartialGenshinUserStats(APIModel):
|
|
|
|
"""User stats with characters without equipment.
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
info (UserInfo): The user's information.
|
|
|
|
stats (Stats): The user's stats.
|
|
|
|
characters (List[PartialCharacter]): The list of the user's characters without equipment.
|
|
|
|
explorations (List[Exploration]): The list of the user's explorations.
|
|
|
|
teapot (Optional[Teapot]): The user's Serenitea Teapot.
|
|
|
|
"""
|
|
|
|
|
2023-07-18 04:58:02 +00:00
|
|
|
info: UserInfo = Field(aliases="role")
|
2023-05-05 04:06:43 +00:00
|
|
|
stats: Stats
|
2023-05-08 00:53:48 +00:00
|
|
|
characters: List[PartialCharacter] = Field(aliases="avatars")
|
|
|
|
explorations: List[Exploration] = Field(aliases="world_explorations")
|
|
|
|
teapot: Optional[Teapot] = Field(aliases="homes")
|
2023-05-05 04:06:43 +00:00
|
|
|
|
|
|
|
@validator("teapot", pre=True)
|
|
|
|
def format_teapot(cls, v: Any) -> Optional[Dict[str, Any]]:
|
|
|
|
"""Format the user's Serenitea Teapot.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
v (Any): The original value of the user's Serenitea Teapot.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
The formatted user's Serenitea Teapot.
|
|
|
|
"""
|
|
|
|
if not v:
|
|
|
|
return None
|
|
|
|
if isinstance(v, dict):
|
|
|
|
return cast("dict[str, Any]", v)
|
|
|
|
return {**v[0], "realms": v}
|
|
|
|
|
|
|
|
|
|
|
|
class GenshinUserStats(PartialGenshinUserStats):
|
|
|
|
"""User stats with characters with equipment.
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
characters (List[Character]): The list of the user's characters with equipment.
|
|
|
|
"""
|
|
|
|
|
|
|
|
characters: List[Character] = Field(alias="avatars")
|
|
|
|
|
|
|
|
|
|
|
|
class FullGenshinUserStats(GenshinUserStats):
|
|
|
|
"""User stats with all data a user can have.
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
abyss (SpiralAbyssPair): The user's Spiral Abyss data.
|
|
|
|
activities (Dict): The user's activities data.
|
|
|
|
"""
|
|
|
|
|
|
|
|
abyss: SpiralAbyssPair
|
|
|
|
activities: Dict
|