ShoobyDoo / OPGG.py

An unofficial Python library for accessing OPGG data.
BSD 3-Clause "New" or "Revised" License
13 stars 5 forks source link

Convert all property objects into dataclasses #16

Closed ShoobyDoo closed 3 months ago

ShoobyDoo commented 4 months ago

Currently there seems to be a ton of excess code with simple property objects that is inflating the code size uncessarily. This can be circumvented by using dataclasses. It also seems to be easier to create a dynamic test method as iterating over each property in the dataclass can be easily compared to the json object(s) returned by opgg.

An example of how much shorter an object can get:

Old game class

class Game:
    """
    Represents a game played by a summoner.\n
    `Note: Major updates to the Game object returned from the OPGG api have been made recently (~2024)`\n
    `This object will need to be completely overhauled, as well as some new objects for the new fields`

    ### Properties:
        `game_id` - Internal game id\n
        `champion` - Champion played\n
        `kill` - Number of kills\n
        `death` - Number of deaths\n
        `assist` - Number of assists\n
        `position` - Position played\n
        `is_win` - Whether the game was won\n
        `is_remake` - Whether the game was a remake\n
        `op_score` - OP.GG score\n
        `op_score_rank` - OP.GG score rank\n
        `is_opscore_max_in_team` - Whether the OP.GG score was the highest in the team\n
        `created_at` - When the game was played\n
    """
    def __init__(self,
                 id: str,
                 champion: Champion,
                 kill: int,
                 death: int,
                 assist: int,
                 position: str,
                 is_win: bool,
                 is_remake: bool,
                 op_score: float,
                 op_score_rank: int,
                 is_opscore_max_in_team: bool,
                 created_at: datetime) -> None:
        self._id = id
        self._champion = champion
        self._kill = kill
        self._death = death
        self._assist = assist
        self._position = position if position != None else "ARAM"
        self._is_win = is_win
        self._is_remake = is_remake
        self._op_score = op_score
        self._op_score_rank = op_score_rank
        self._is_opscore_max_in_team = is_opscore_max_in_team
        self._created_at = created_at

    @property
    def id(self) -> str:
        """
        A `str` representing the Internal game id
        """
        return self._id

    @id.setter
    def id(self, value: str) -> None:
        self._id = value

    @property
    def champion(self) -> Champion:
        """
        A `Champion` object representing the champion played
        """
        return self._champion

    @champion.setter
    def champion(self, value: Champion) -> None:
        self._champion = value

    @property
    def kill(self) -> int:
        """
        An `int` representing the number of kills
        """
        return self._kill

    @kill.setter
    def kill(self, value: int) -> None:
        self._kill = value

    @property
    def death(self) -> int:
        """
        An `int` representing the number of deaths
        """
        return self._death

    @death.setter
    def death(self, value: int) -> None:
        self._death = value

    @property
    def assist(self) -> int:
        """
        An `int` representing the number of assists
        """
        return self._assist

    @assist.setter
    def assist(self, value: int) -> None:
        self._assist = value

    @property
    def position(self) -> str:
        """
        A `str` representing the position played
        """
        return self._position

    @position.setter
    def position(self, value: str) -> None:
        self._position = value

    @property
    def is_win(self) -> bool:
        """
        A `bool` representing whether the game was won
        """
        return self._is_win

    @is_win.setter
    def is_win(self, value: bool) -> None:
        self._is_win = value

    @property
    def is_remake(self) -> bool:
        """
        A `bool` representing whether the game was a remake
        """
        return self._is_remake

    @is_remake.setter
    def is_remake(self, value: bool) -> None:
        self._is_remake = value

    @property
    def op_score(self) -> float:
        """
        A `float` representing the OP.GG score
        """
        return self._op_score

    @op_score.setter
    def op_score(self, value: float) -> None:
        self._op_score = value

    @property
    def op_score_rank(self) -> int:
        """
        An `int` representing the OP.GG score rank
        """
        return self._op_score_rank

    @op_score_rank.setter
    def op_score_rank(self, value: int) -> None:
        self._op_score_rank = value

    @property
    def is_opscore_max_in_team(self) -> bool:
        """
        A `bool` representing whether the OP.GG score was the highest in the team (MVP)
        """
        return self._is_opscore_max_in_team

    @is_opscore_max_in_team.setter
    def is_opscore_max_in_team(self, value: bool) -> None:
        self._is_opscore_max_in_team = value

    @property
    def created_at(self) -> datetime:
        """
        A `datetime` object representing when the game was played
        """
        return self._created_at

    @created_at.setter
    def created_at(self, value: datetime) -> None:
        self._created_at = value

    def __repr__(self) -> str:
        return f"Game(champion={self._champion}, kill={self._kill}, death={self._death}, assist={self._assist}, position={self._position}, is_win={self._is_win})"

New game class

@dataclass
class GameNew:
    """
    Represents a game played by a summoner.\n

    ### Properties:
        `id: str` - Internal game id\n
        `created_at: datetime` - Date & Time of game\n
        `game_map: GameMap` - Game map (Summoners Rift, Howling Abyss, etc.)\n
        `queue_info: QueueInfo` - Queue info\n
        `version: str` - Game version\n
        `game_length_second: int` - Length of game in seconds\n
        `is_remake: bool` - Whether or not the game was a remake\n
        `is_opscore_active: bool` - Whether or not the opscore was active\n
        `is_recorded: bool` - Whether or not the game was recorded\n
        `record_info: Any` - Unknown return value and type\n
        `average_tier_info: Tier` - Average Tier of the game\n
        `participants: list[Participant]` - List of participants in a game
        `teams: list[Team]` - List of teams in a game
        `memo: Any` - Unknown return value and type
        `myData: Participant` - Contains your participant record
    """
    id: str
    created_at: datetime
    game_map: str
    queue_info: QueueInfo
    version: str
    game_length_second: int
    is_remake: bool
    is_opscore_active: bool = True
    is_recorded: bool
    record_info: Any                
    average_tier_info: Tier
    participants: list[Participant]
    teams: list[Team]
    memo: Any                       
    myData: Participant

    def __repr__(self) -> str:
        return f"Game(champion={OPGG.get_champion_by(By.ID, self.myData.champion_id)}, kill={self.myData.stats.kill}, death={self.myData.stats.death}, assist={self.myData.stats.assist}, position={self.myData.position}, is_win={self.myData.stats.result == 'WIN'})"
ShoobyDoo commented 3 months ago

The caveat to this was that I could not add docstring comments to the properties, and I think that is too much of a dealbreaker. I will keep the same convention as it was and instead write a better way to get object properties into the test method.