-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #58 from 2jun0/GDET-70
GDET-70: 별칭 가져오기
- Loading branch information
Showing
35 changed files
with
568 additions
and
249 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
from datetime import datetime | ||
|
||
from pydantic import BaseModel | ||
from sqlalchemy import ForeignKey, String | ||
from sqlalchemy.orm import Mapped, mapped_column | ||
|
||
from ..model import Base, CreatedAtMixin, UpdatedAtMixin | ||
|
||
|
||
class GameAlias(CreatedAtMixin, UpdatedAtMixin, Base): | ||
__tablename__ = "game_alias" | ||
|
||
id: Mapped[int] = mapped_column(primary_key=True) | ||
name: Mapped[str] = mapped_column(String(64)) | ||
|
||
game_id: Mapped[int] = mapped_column(ForeignKey("game.id")) | ||
|
||
def __repr__(self) -> str: | ||
return f"GameAlias(id={self.id}, name={self.name}, game_id={self.game_id}" | ||
|
||
def to_dto(self) -> "GameAliasDto": | ||
return GameAliasDto( | ||
id=self.id, | ||
name=self.name, | ||
game_id=self.game_id, | ||
created_at=self.created_at, | ||
updated_at=self.updated_at, | ||
) | ||
|
||
|
||
class GameAliasDto(BaseModel): | ||
id: int | ||
name: str | ||
game_id: int | ||
created_at: datetime | ||
updated_at: datetime |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,12 @@ | ||
from collections.abc import Sequence | ||
from typing import Optional, TypedDict | ||
from typing import TypedDict | ||
|
||
STEAM_ID = int | ||
|
||
|
||
class SaveGame(TypedDict): | ||
steam_id: int | ||
name: str | ||
kr_name: Optional[str] | ||
aliases: Sequence[str] | ||
released_at: float | ||
genres: Sequence[str] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
def is_aldecimal(s: str) -> bool: | ||
return all(c.isdecimal() or c.isalpha() for c in s) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,11 @@ | ||
from datetime import datetime | ||
from typing import Optional, Sequence | ||
from typing import Sequence | ||
|
||
from pydantic import BaseModel | ||
|
||
|
||
class Game(BaseModel): | ||
id: int | ||
steam_id: int | ||
name: str | ||
kr_name: Optional[str] | ||
released_at: datetime | ||
genres: Sequence[str] | ||
updated_at: datetime | ||
created_at: datetime | ||
|
||
|
||
class SaveGame(BaseModel): | ||
steam_id: int | ||
name: str | ||
kr_name: Optional[str] | ||
released_at: float | ||
genres: Sequence[str] | ||
aliases: Sequence[str] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
from collections.abc import Iterable, Sequence | ||
from functools import cache, wraps | ||
|
||
from .model import IGDBAlternativeName, IGDBExternalGames, IGDBGame | ||
from ..config import setting | ||
|
||
import requests | ||
|
||
STEAM_CATEGORY = 1 | ||
MAX_LIMIT = 500 | ||
|
||
|
||
def batch(size: int): | ||
def batch_decorator(func): | ||
|
||
@wraps(func) | ||
def wrapper(_input: Sequence[int], **kwargs): | ||
outputs = [] | ||
|
||
for srt_idx in range(0, len(_input), size): | ||
batch_input = _input[srt_idx : srt_idx + size] | ||
batch_output = func(batch_input, **kwargs) | ||
outputs.extend(batch_output) | ||
|
||
return outputs | ||
|
||
return wrapper | ||
|
||
return batch_decorator | ||
|
||
|
||
@cache | ||
def _get_token(): | ||
res = requests.post( | ||
"https://id.twitch.tv/oauth2/token" | ||
f"?client_id={setting.IGDB_CLIENT_ID}&client_secret={setting.IGDB_CLIENT_SECRET}&grant_type=client_credentials", | ||
) | ||
token = res.json()["access_token"] | ||
return token | ||
|
||
|
||
def get_external_games(steam_ids: Iterable, category: int, limit: int = MAX_LIMIT) -> list[IGDBExternalGames]: | ||
# category,checksum,countries,created_at,game,media,name,platform,uid,updated_at,url,year; | ||
|
||
uids = ",".join([f'"{id}"' for id in steam_ids]) | ||
token = _get_token() | ||
|
||
response = requests.post( | ||
"https://api.igdb.com/v4/external_games", | ||
**{ | ||
"headers": {"Client-ID": setting.IGDB_CLIENT_ID, "Authorization": f"Bearer {token}"}, | ||
"data": f"fields game,uid; where uid=({uids}) & category={STEAM_CATEGORY}; limit {limit};", | ||
}, | ||
) | ||
response.raise_for_status() | ||
|
||
return response.json() | ||
|
||
|
||
def get_steam_games(steam_ids: Iterable, limit: int = MAX_LIMIT) -> list[IGDBExternalGames]: | ||
return get_external_games(steam_ids, STEAM_CATEGORY, limit) | ||
|
||
|
||
def get_games(ids: Iterable[int], limit: int = MAX_LIMIT) -> list[IGDBGame]: | ||
# checksum,cover,created_at,game,name,region,updated_at; | ||
|
||
ids_ = ",".join(map(str, ids)) | ||
token = _get_token() | ||
|
||
response = requests.post( | ||
"https://api.igdb.com/v4/games", | ||
**{ | ||
"headers": {"Client-ID": setting.IGDB_CLIENT_ID, "Authorization": f"Bearer {token}"}, | ||
"data": f"fields alternative_names; where id=({ids_}); limit {limit};", | ||
}, | ||
) | ||
response.raise_for_status() | ||
|
||
return response.json() | ||
|
||
|
||
def get_alternative_names(ids: Iterable[int], limit: int = MAX_LIMIT) -> list[IGDBAlternativeName]: | ||
# checksum,cover,created_at,game,name,region,updated_at; | ||
|
||
ids_ = ",".join(map(str, ids)) | ||
token = _get_token() | ||
|
||
response = requests.post( | ||
"https://api.igdb.com/v4/alternative_names", | ||
**{ | ||
"headers": {"Client-ID": setting.IGDB_CLIENT_ID, "Authorization": f"Bearer {token}"}, | ||
"data": f"fields name,game; where id=({ids_}); limit {limit};", | ||
}, | ||
) | ||
response.raise_for_status() | ||
|
||
return response.json() | ||
|
||
|
||
@batch(MAX_LIMIT) | ||
def get_steam_games_batch(steam_ids: Sequence[int]) -> list[IGDBExternalGames]: | ||
if len(steam_ids) == 0: | ||
return [] | ||
|
||
return get_steam_games(steam_ids=steam_ids) | ||
|
||
|
||
@batch(MAX_LIMIT) | ||
def get_games_batch(game_ids: Sequence[int]) -> list[IGDBGame]: | ||
if len(game_ids) == 0: | ||
return [] | ||
|
||
return get_games(ids=game_ids) | ||
|
||
|
||
@batch(MAX_LIMIT) | ||
def get_alternative_names_batch(alternative_name_ids: Sequence[int]) -> list[IGDBAlternativeName]: | ||
if len(alternative_name_ids) == 0: | ||
return [] | ||
|
||
return get_alternative_names(ids=alternative_name_ids) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
from typing import TypedDict | ||
|
||
|
||
class IGDBExternalGames(TypedDict): | ||
id: int | ||
uid: str | ||
game: int | ||
|
||
|
||
class IGDBGame(TypedDict): | ||
id: int | ||
alternative_names: list[int] | ||
|
||
|
||
class IGDBAlternativeName(TypedDict): | ||
id: int | ||
name: str | ||
game: int |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.