Source code for psnawp_api.models.trophies.trophy_titles

"""Provides the TrophyTitle class for retrieving overall trophy summary for each video game title."""

from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING, Any

from typing_extensions import Self, override

from psnawp_api.core import PSNAWPBadRequestError, PSNAWPNotFoundError
from psnawp_api.models.listing import PaginationIterator
from psnawp_api.models.trophies import PlatformType, TrophySet
from psnawp_api.utils import API_PATH, BASE_PATH, iso_format_to_datetime

if TYPE_CHECKING:
    from collections.abc import Generator
    from datetime import datetime

    from psnawp_api.core import Authenticator
    from psnawp_api.models.listing import PaginationArguments


[docs] @dataclass(frozen=True) class TrophyTitle: """A class containing summary of trophy data for a user for a game title. :var str | None np_service_name: trophy for PS3, PS4, or PS Vita platforms and trophy2 for the PS5 platform. :var str | None np_communication_id: Unique ID of the title. :var str | None trophy_set_version: The current version of the trophy set. :var str | None title_name: Title name. :var str | None title_detail: Title description (PS3, PS4 and PS Vita titles only). :var str | None title_icon_url: URL of the icon for the title. :var frozenset[PlatformType] title_platform: Platforms this title belongs to. :var bool | None has_trophy_groups: True if the title has multiple groups of trophies (eg. DLC trophies which are separate from the main trophy list). :var int | None progress: Percentage of trophies earned for the title. :var bool | None hidden_flag: Title has been hidden on the accounts trophy list (Only for Client). :var TrophySet earned_trophies: Number of trophies for the title which have been earned by type. :var TrophySet defined_trophies: Number of trophies for the title by type. :var datetime.datetime | None last_updated_datetime: Date most recent trophy earned for the title (UTC+00:00 TimeZone). :var str | None np_title_id: Title ID of the title if passed. """ np_service_name: str | None np_communication_id: str | None trophy_set_version: str | None title_name: str | None title_detail: str | None title_icon_url: str | None title_platform: frozenset[PlatformType] has_trophy_groups: bool | None progress: int | None hidden_flag: bool | None earned_trophies: TrophySet defined_trophies: TrophySet last_updated_datetime: datetime | None np_title_id: str | None # when title_id is passed
[docs] class TrophyTitleIterator(PaginationIterator[TrophyTitle]): """Retrieve all game titles associated with an account, and a summary of trophies earned from them. :var list[str] title_ids: Optional param that can be passed via init. Lets user fetch Trophy for specific title ids. .. note:: This class is intended to be used via Client or User class. See :py:meth:`psnawp_api.models.client.Client.trophy_titles`, :py:meth:`psnawp_api.models.client.Client.trophy_titles_for_title`, :py:meth:`psnawp_api.models.user.User.trophy_titles`, and :py:meth:`psnawp_api.models.user.User.trophy_titles_for_title`. """
[docs] def __init__( self, authenticator: Authenticator, url: str, pagination_args: PaginationArguments, title_ids: list[str] | None, ) -> None: """Constructor of TrophyTitles class. :param authenticator: The Authenticator instance used for making authenticated requests to the API. :param url: The url of endpoint. :param pagination_args: Arguments related to pagination like limit and offset. """ super().__init__( authenticator=authenticator, url=url, pagination_args=pagination_args, ) self.title_ids = title_ids
[docs] @classmethod def from_endpoint( cls, authenticator: Authenticator, pagination_args: PaginationArguments, account_id: str, title_ids: list[str] | None, ) -> Self: """Creates an instance of :py:class:`TrophyTitleIterator` from api endpoint.""" if title_ids is None: url = f"{BASE_PATH['trophies']}{API_PATH['trophy_titles'].format(account_id=account_id)}" else: url = f"{BASE_PATH['trophies']}{API_PATH['trophy_titles_for_title'].format(account_id=account_id)}" return cls( authenticator, url, pagination_args, title_ids, )
[docs] @override def fetch_next_page(self) -> Generator[TrophyTitle, None, None]: """Fetches the next page in endpoint with pagination.""" if self.title_ids is None: return self.get_trophy_title() return self.get_trophy_summary_for_title()
[docs] def get_trophy_title(self) -> Generator[TrophyTitle, None, None]: """Retrieve all game titles associated with an account, and a summary of trophies earned from them. :param limit: Limit of titles returned, None means to return all trophy titles. :returns: Generator object with TitleTrophySummary objects :raises PSNAWPForbiddenError: If the user's profile is private """ response = self.authenticator.get( url=self._url, params=self._pagination_args.get_params_dict(), ).json() self._total_item_count = response.get("totalItemCount", 0) trophy_titles: list[dict[str, Any]] = response.get("trophyTitles") for trophy_title in trophy_titles: title_trophy_sum = TrophyTitle( np_service_name=trophy_title.get("npServiceName"), np_communication_id=trophy_title.get("npCommunicationId"), trophy_set_version=trophy_title.get("trophySetVersion"), title_name=trophy_title.get("trophyTitleName"), title_detail=trophy_title.get("trophyTitleDetail"), title_icon_url=trophy_title.get("trophyTitleIconUrl"), title_platform=frozenset( [ PlatformType(platform_str) for platform_str in trophy_title.get( "trophyTitlePlatform", "", ).split(",") ], ), has_trophy_groups=trophy_title.get("hasTrophyGroups"), progress=trophy_title.get("progress"), hidden_flag=trophy_title.get("hiddenFlag"), last_updated_datetime=iso_format_to_datetime(trophy_title.get("lastUpdatedDateTime")), defined_trophies=TrophySet( **trophy_title.get( "definedTrophies", {"bronze": 0, "silver": 0, "gold": 0, "platinum": 0}, ), ), earned_trophies=TrophySet( **trophy_title.get( "earnedTrophies", {"bronze": 0, "silver": 0, "gold": 0, "platinum": 0}, ), ), np_title_id=None, ) self._pagination_args.increment_offset() yield title_trophy_sum offset = response.get("nextOffset") or 0 if offset > 0: self._has_next = True else: self._has_next = False
[docs] def get_trophy_summary_for_title(self) -> Generator[TrophyTitle, None, None]: """Retrieve a summary of the trophies earned by a user for specific titles. :param list[str] title_ids: Unique ID of the title :returns: Generator object with TitleTrophySummary objects :raises PSNAWPForbiddenError: If the user's profile is private """ params = { "npTitleIds": ",".join( self.title_ids if self.title_ids is not None else [], ), } response = self.authenticator.get(url=self._url, params=params).json() self._total_item_count = response.get("totalItemCount", 0) for title in response.get("titles"): for trophy_title in title.get("trophyTitles"): title_trophy_sum = TrophyTitle( np_service_name=trophy_title.get("npServiceName"), np_communication_id=trophy_title.get("npCommunicationId"), trophy_set_version=trophy_title.get("trophySetVersion"), title_name=trophy_title.get("trophyTitleName"), title_detail=trophy_title.get("trophyTitleDetail"), title_icon_url=trophy_title.get("trophyTitleIconUrl"), title_platform=frozenset( [ PlatformType(platform_str) for platform_str in trophy_title.get( "trophyTitlePlatform", "", ).split(",") ], ), has_trophy_groups=trophy_title.get("hasTrophyGroups"), progress=trophy_title.get("progress"), hidden_flag=trophy_title.get("hiddenFlag"), last_updated_datetime=iso_format_to_datetime(trophy_title.get("lastUpdatedDateTime")), defined_trophies=trophy_title.get( "definedTrophies", {"bronze": 0, "silver": 0, "gold": 0, "platinum": 0}, ), earned_trophies=trophy_title.get( "earnedTrophies", {"bronze": 0, "silver": 0, "gold": 0, "platinum": 0}, ), np_title_id=title.get("npTitleId"), ) yield title_trophy_sum # This endpoint does not have pagination self._has_next = False
[docs] @staticmethod def get_np_communication_id( authenticator: Authenticator, title_id: str, account_id: str, ) -> str: """Returns the np communication id of title. This is required for requesting detail about a titles trophies. .. note:: The endpoint only returns useful response back if the account has played that particular video game. :param authenticator: The Authenticator instance used for making authenticated requests to the API. :param title_id: Unique ID of the title :param account_id: Account ID of the user. :returns: np communication id of title :raises PSNAWPNotFoundError: If the user does not have any trophies for that game or the game doesn't exist. """ params = {"npTitleIds": f"{title_id},"} try: response = authenticator.get( url=f"{BASE_PATH['trophies']}{API_PATH['trophy_titles_for_title'].format(account_id=account_id)}", params=params, ).json() except (PSNAWPBadRequestError, PSNAWPNotFoundError) as bad_req: raise PSNAWPNotFoundError( f"Could not find a Video Game with Title: {title_id}", ) from bad_req if len(response.get("titles")[0].get("trophyTitles")) == 0: raise PSNAWPNotFoundError( f"Could not find a Video Game with Title: {title_id}. Most likely the user doesn't own the game.", ) np_comm_id: str = response.get("titles")[0].get("trophyTitles")[0].get("npCommunicationId", title_id) return np_comm_id