Skip to content

Commit

Permalink
refactor: Improve stat related classes
Browse files Browse the repository at this point in the history
Replace `WeaponStat`, `ArtifactMainStat`, and `ArtifactSubStat` with `Stat`.

Replace `CharacterStat` with `FightProp`

Add `formatted_value` attribute to `Stat` and `FightProp`

Remove type: ignore comments
  • Loading branch information
seriaati committed Feb 2, 2024
1 parent e27874f commit ce29f89
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 80 deletions.
5 changes: 1 addition & 4 deletions enka/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from .assets.manager import AssetManager
from .assets.updater import AssetUpdater
from .enums import PERCENT_STAT_TYPES, Element, Language
from .enums import Element, Language
from .exceptions import raise_for_retcode
from .models.icon import Icon, Namecard
from .models.response import ShowcaseResponse
Expand Down Expand Up @@ -148,7 +148,6 @@ def _post_process_character(self, character: "Character") -> "Character":
weapon.name = self._assets.text_map[weapon.name]
for stat in weapon.stats:
stat.name = self._assets.text_map[stat.type.value]
stat.value *= 100 if stat.type.name in PERCENT_STAT_TYPES else 1

# artifacts
for artifact in character.artifacts:
Expand All @@ -157,12 +156,10 @@ def _post_process_character(self, character: "Character") -> "Character":
artifact.main_stat.name = self._assets.text_map[artifact.main_stat.type.value]
for stat in artifact.sub_stats:
stat.name = self._assets.text_map[stat.type.value]
stat.value *= 100 if stat.type.name in PERCENT_STAT_TYPES else 1

# stats
for stat_type, stat in character.stats.items():
stat.name = self._assets.text_map.get(stat_type.name)
stat.value *= 100 if stat_type.name in PERCENT_STAT_TYPES else 1

# constellations
for constellation in character.constellations:
Expand Down
24 changes: 24 additions & 0 deletions enka/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
__all__ = ("PERCENT_STAT_TYPES", "CHARACTER_RARITY_MAP")

PERCENT_STAT_TYPES = (
"FIGHT_PROP_HP_PERCENT",
"FIGHT_PROP_ATTACK_PERCENT",
"FIGHT_PROP_DEFENSE_PERCENT",
"FIGHT_PROP_CRITICAL",
"FIGHT_PROP_CRITICAL_HURT",
"FIGHT_PROP_CHARGE_EFFICIENCY",
"FIGHT_PROP_HEAL_ADD",
"FIGHT_PROP_PHYSICAL_ADD_HURT",
"FIGHT_PROP_FIRE_ADD_HURT",
"FIGHT_PROP_ELEC_ADD_HURT",
"FIGHT_PROP_WATER_ADD_HURT",
"FIGHT_PROP_WIND_ADD_HURT",
"FIGHT_PROP_ROCK_ADD_HURT",
"FIGHT_PROP_ICE_ADD_HURT",
"FIGHT_PROP_GRASS_ADD_HURT",
)

CHARACTER_RARITY_MAP: dict[str, int] = {
"QUALITY_ORANGE": 5,
"QUALITY_PURPLE": 4,
}
19 changes: 0 additions & 19 deletions enka/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,25 +63,6 @@ class StatType(StrEnum):
FIGHT_PROP_GRASS_ADD_HURT = "FIGHT_PROP_GRASS_ADD_HURT"


PERCENT_STAT_TYPES = (
"FIGHT_PROP_HP_PERCENT",
"FIGHT_PROP_ATTACK_PERCENT",
"FIGHT_PROP_DEFENSE_PERCENT",
"FIGHT_PROP_CRITICAL",
"FIGHT_PROP_CRITICAL_HURT",
"FIGHT_PROP_CHARGE_EFFICIENCY",
"FIGHT_PROP_HEAL_ADD",
"FIGHT_PROP_PHYSICAL_ADD_HURT",
"FIGHT_PROP_FIRE_ADD_HURT",
"FIGHT_PROP_ELEC_ADD_HURT",
"FIGHT_PROP_WATER_ADD_HURT",
"FIGHT_PROP_WIND_ADD_HURT",
"FIGHT_PROP_ROCK_ADD_HURT",
"FIGHT_PROP_ICE_ADD_HURT",
"FIGHT_PROP_GRASS_ADD_HURT",
)


class FightPropType(IntEnum):
FIGHT_PROP_NONE = 0
FIGHT_PROP_BASE_HP = 1
Expand Down
119 changes: 62 additions & 57 deletions enka/models/character.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,23 @@
from .icon import Icon

from ..exceptions import InvalidItemTypeError
from ..enums import Element, EquipmentType, FightProp, ItemType, StatType
from ..enums import Element, EquipmentType, ItemType, StatType, FightPropType
from ..constants import PERCENT_STAT_TYPES

__all__ = (
"Stat",
"FightProp",
"Artifact",
"Character",
"ArtifactMainStat",
"ArtifactSubStat",
"Weapon",
"WeaponStat",
"CharacterStat",
"Constellation",
"Talent",
)


class ArtifactMainStat(BaseModel):
class Stat(BaseModel):
"""
Represents the main stat of an artifact.
Represents a stat.
Attributes
----------
Expand All @@ -34,29 +33,49 @@ class ArtifactMainStat(BaseModel):
The stat's name.
"""

type: StatType = Field(alias="mainPropId")
value: float = Field(alias="statValue")
type: StatType
value: float
name: str = Field(None)

@property
def is_percentage(self) -> bool:
return self.type.name in PERCENT_STAT_TYPES

@property
def formatted_value(self) -> str:
if self.is_percentage:
return f"{round(self.value, 1)}%"
return str(round(self.value))


class ArtifactSubStat(BaseModel):
class FightProp(BaseModel):
"""
Represents the sub stat of an artifact.
Represents a fight prop.
Attributes
----------
type: :class:`StatType`
The stat's type (e.g. FIGHT_PROP_HP, FIGHT_PROP_ATTACK, etc.)
type: :class:`FightProp`
The fight prop's type (e.g. FIGHT_PROP_HP, FIGHT_PROP_ATTACK, etc.)
value: :class:`float`
The stat's value.
The fight prop's value.
name: :class:`str`
The stat's name.
The fight prop's name.
"""

type: StatType = Field(alias="appendPropId")
value: float = Field(alias="statValue")
type: FightPropType
value: float
name: str = Field(None)

@property
def is_percentage(self) -> bool:
return self.type.name in PERCENT_STAT_TYPES

@property
def formatted_value(self) -> str:
if self.is_percentage:
return f"{round(self.value, 1)}%"
return str(round(self.value))


class Artifact(BaseModel):
"""
Expand Down Expand Up @@ -99,8 +118,8 @@ class Artifact(BaseModel):
item_type: ItemType = Field(alias="itemType")
name: str = Field(alias="nameTextMapHash")
rarity: int = Field(alias="rankLevel")
main_stat: ArtifactMainStat = Field(alias="reliquaryMainstat")
sub_stats: List[ArtifactSubStat] = Field(alias="reliquarySubstats")
main_stat: Stat = Field(alias="reliquaryMainstat")
sub_stats: List[Stat] = Field(alias="reliquarySubstats")
set_name: str = Field(alias="setNameTextMapHash")

@field_validator("level", mode="before")
Expand All @@ -111,24 +130,16 @@ def _convert_level(cls, v: int) -> int:
def _convert_icon(cls, v: str) -> str:
return f"https://enka.network/ui/{v}.png"

@field_validator("main_stat", mode="before")
def _convert_main_stat(cls, v: Dict[str, Any]) -> Stat:
return Stat(type=StatType(v["mainPropId"]), value=v["statValue"], name="")

class WeaponStat(BaseModel):
"""
Represents a weapon stat.
Attributes
----------
type: :class:`StatType`
The stat's type (e.g. FIGHT_PROP_HP, FIGHT_PROP_ATTACK, etc.)
value: :class:`float`
The stat's value.
name: :class:`str`
The stat's name.
"""

type: StatType = Field(alias="appendPropId")
value: float = Field(alias="statValue")
name: str = Field(None)
@field_validator("sub_stats", mode="before")
def _convert_sub_stats(cls, v: List[Dict[str, Any]]) -> List[Stat]:
return [
Stat(type=StatType(stat["appendPropId"]), value=stat["statValue"], name="")
for stat in v
]


class Weapon(BaseModel):
Expand Down Expand Up @@ -163,7 +174,7 @@ class Weapon(BaseModel):
item_type: ItemType = Field(alias="itemType")
name: str = Field(alias="nameTextMapHash")
rarity: int = Field(alias="rankLevel")
stats: List[WeaponStat] = Field(alias="weaponStats")
stats: List[Stat] = Field(alias="weaponStats")

@field_validator("refinement", mode="before")
def _extract_refinement(cls, v: Dict[str, int]) -> int:
Expand All @@ -173,21 +184,12 @@ def _extract_refinement(cls, v: Dict[str, int]) -> int:
def _convert_icon(cls, v: str) -> str:
return f"https://enka.network/ui/{v}.png"


class CharacterStat(BaseModel):
"""
Represents a character stat.
Attributes
----------
value: :class:`float`
The stat's value.
name: Optional[:class:`str`]
The stat's name.
"""

value: float
name: Optional[str] = Field(None)
@field_validator("stats", mode="before")
def _convert_stats(cls, v: List[Dict[str, Any]]) -> List[Stat]:
return [
Stat(type=StatType(stat["appendPropId"]), value=stat["statValue"], name="")
for stat in v
]


class Constellation(BaseModel):
Expand Down Expand Up @@ -275,7 +277,7 @@ class Character(BaseModel):
id: int = Field(alias="avatarId")
artifacts: List[Artifact]
weapon: Weapon
stats: Dict[FightProp, CharacterStat] = Field(alias="fightPropMap")
stats: Dict[FightPropType, FightProp] = Field(alias="fightPropMap")
constellations: List[Constellation] = Field([], alias="talentIdList")
talents: List[Talent] = Field(alias="skillLevelMap")
ascension: int
Expand All @@ -291,16 +293,19 @@ class Character(BaseModel):
model_config = {"arbitrary_types_allowed": True}

@field_validator("stats", mode="before")
def _convert_stats(cls, v: Dict[str, float]) -> Dict[FightProp, CharacterStat]:
return {FightProp(int(k)): CharacterStat(value=v) for k, v in v.items()} # type: ignore
def _convert_stats(cls, v: Dict[str, float]) -> Dict[FightPropType, FightProp]:
return {
FightPropType(int(k)): FightProp(type=FightPropType(int(k)), value=v, name="")
for k, v in v.items()
}

@field_validator("constellations", mode="before")
def _convert_constellations(cls, v: List[int]) -> List[Constellation]:
return [Constellation(id=constellation_id) for constellation_id in v] # type: ignore
return [Constellation(id=constellation_id, name="", icon="") for constellation_id in v]

@field_validator("talents", mode="before")
def _convert_talents(cls, v: Dict[str, int]) -> List[Talent]:
return [Talent(id=int(k), level=v) for k, v in v.items()] # type: ignore
return [Talent(id=int(k), level=v, name="", icon="") for k, v in v.items()]

@field_validator("weapon", mode="before")
def _flatten_weapon_data(cls, v: Dict[str, Any]) -> Dict[str, Any]:
Expand Down

0 comments on commit ce29f89

Please sign in to comment.