diff --git a/src/renders/images.py b/src/renders/images.py
index 5a1aa03..fffcf2f 100644
--- a/src/renders/images.py
+++ b/src/renders/images.py
@@ -20,6 +20,10 @@ def __init__(self) -> None:
async def get_pokemon_left_sprites(self) -> dict[PokemonType, tuple[str, str]]:
return {pokemon: await self._create_left_sprite_urls(pokemon.national_no) for pokemon in PokemonType}
+ @file_cache("pokemon_right_sprites")
+ async def get_pokemon_right_sprites(self) -> dict[PokemonType, tuple[str, str]]:
+ return {pokemon: await self._create_right_sprite_urls(pokemon.national_no) for pokemon in PokemonType}
+
@file_cache("pokeball")
async def get_pokeball(self) -> str:
return await self._get_as_base64(POKEBALL_URL)
@@ -41,3 +45,16 @@ async def _create_left_sprite_urls(self, national_no: int) -> tuple[str, str]:
frame_2 = poke_url + "_2.png"
return (await self._get_as_base64(frame_1), await self._get_as_base64(frame_2))
+
+ async def _create_right_sprite_urls(self, national_no: int) -> tuple[str, str]:
+ start_url = f"{SPRITE_BASE_URL}/o-r_hgss"
+ poke_url = start_url + "/o-r_hs_" + str(national_no).zfill(3)
+
+ if national_no in [3, 25]:
+ frame_1 = poke_url + "_m-1.png"
+ frame_2 = poke_url + "_m-2.png"
+ else:
+ frame_1 = poke_url + "_1.png"
+ frame_2 = poke_url + "_2.png"
+
+ return (await self._get_as_base64(frame_1), await self._get_as_base64(frame_2))
diff --git a/src/renders/svg.py b/src/renders/svg.py
index 4027754..96236b9 100644
--- a/src/renders/svg.py
+++ b/src/renders/svg.py
@@ -2,10 +2,14 @@
import random
from typing import Generator
+from src.exceptions.base import InternalError
from src.models.pokemon_type import PokemonType
from src.renders.images import ImageLoader
+from src.schemas.pokemons import PokemonFace
from src.template import svgs as svgs_templates
+pokemon_templates = {PokemonFace.LEFT: svgs_templates.pokemon_left, PokemonFace.RIGHT: svgs_templates.pokemon_right}
+
class SVGRenderer:
pokeball: str
@@ -16,26 +20,32 @@ async def prepare(cls, *, image_loader: ImageLoader):
logging.info("Load images start")
cls.pokeball = await image_loader.get_pokeball()
cls.pokemon_left_sprites = await image_loader.get_pokemon_left_sprites()
+ cls.pokemon_right_sprites = await image_loader.get_pokemon_right_sprites()
logging.info("Load images end")
- def render_svg(self, *, pokemons: list[PokemonType], commit_point: int, username: str) -> str:
+ def render_svg(self, *, pokemons: list[PokemonType], commit_point: int, username: str, face: PokemonFace) -> str:
return svgs_templates.base.format(
username=username,
commit_point=commit_point,
n_pokemons=len(pokemons),
- pokemons="\n".join(self._render_pokemons(pokemons)),
+ pokemons="\n".join(self._render_pokemons(pokemons, face)),
poke_ball_url=self.pokeball,
)
- def _render_pokemons(self, pokemons: list[PokemonType]) -> Generator[str, None, None]:
+ def _render_pokemons(self, pokemons: list[PokemonType], face: PokemonFace) -> Generator[str, None, None]:
for idx, pokemon in enumerate(pokemons):
num = idx + 1
duration = random.uniform(10, 15)
offset = random.randint(-75, 80)
delay = random.uniform(0, 10)
- frame_1, frame_2 = self.pokemon_left_sprites[pokemon]
+ if face is PokemonFace.LEFT:
+ frame_1, frame_2 = self.pokemon_left_sprites[pokemon]
+ elif face is PokemonFace.RIGHT:
+ frame_1, frame_2 = self.pokemon_right_sprites[pokemon]
+ else:
+ raise InternalError
- yield svgs_templates.pokemon.format(
+ yield pokemon_templates[face].format(
num=num, duration=duration, offset=offset, delay=delay, frame_1=frame_1, frame_2=frame_2
)
diff --git a/src/routes/pokemons.py b/src/routes/pokemons.py
index 659e0db..a12fc40 100644
--- a/src/routes/pokemons.py
+++ b/src/routes/pokemons.py
@@ -1,19 +1,18 @@
-from fastapi import Response
+from fastapi import Query, Response
from fastapi.routing import APIRouter
from src.dependencies.db import SessionDep
from src.dependencies.facades import PokemonsFacadeDep
+from src.schemas.pokemons import PokemonFace
router = APIRouter()
@router.get("/pokemons/{username}")
async def get_pokemons_svg(
- username: str,
- session: SessionDep,
- pokemons_facade: PokemonsFacadeDep,
+ username: str, session: SessionDep, pokemons_facade: PokemonsFacadeDep, face: PokemonFace = Query(PokemonFace.LEFT)
):
return Response(
- content=await pokemons_facade.render_pokemons(session=session, username=username),
+ content=await pokemons_facade.render_pokemons(session=session, username=username, face=face),
headers={"content-type": "image/svg+xml", "Cache-Control": "max-age=3600"},
)
diff --git a/src/schemas/pokemons.py b/src/schemas/pokemons.py
new file mode 100644
index 0000000..7074d68
--- /dev/null
+++ b/src/schemas/pokemons.py
@@ -0,0 +1,6 @@
+from enum import Enum
+
+
+class PokemonFace(str, Enum):
+ LEFT = "left"
+ RIGHT = "right"
diff --git a/src/services/pokemons_facade.py b/src/services/pokemons_facade.py
index 4a0e7e1..1046196 100644
--- a/src/services/pokemons_facade.py
+++ b/src/services/pokemons_facade.py
@@ -6,6 +6,7 @@
from src.exceptions.external import GithubAPIRequestFailedError, GithubAPIUnavailableError
from src.external.github_api import GithubAPI
from src.renders.svg import SVGRenderer
+from src.schemas.pokemons import PokemonFace
from src.services.user import UserService
from src.setting import settings
@@ -24,7 +25,7 @@ def __init__(
self._renderer = renderer
self._background_tasks = background_tasks
- async def render_pokemons(self, *, session: AsyncSession, username: str) -> str:
+ async def render_pokemons(self, *, session: AsyncSession, username: str, face: PokemonFace) -> str:
user = await self._user_service.get_user(session=session, username=username)
if user is not None:
@@ -43,7 +44,7 @@ async def render_pokemons(self, *, session: AsyncSession, username: str) -> str:
raise GithubAPIUnavailableError from e
return self._renderer.render_svg(
- pokemons=user.pokemon_types, commit_point=user.total_commit_point, username=username
+ pokemons=user.pokemon_types, commit_point=user.total_commit_point, username=username, face=face
)
async def _get_commit_points(self, username: str) -> dict[int, int]:
diff --git a/src/template/svgs.py b/src/template/svgs.py
index f80e56d..460fa54 100644
--- a/src/template/svgs.py
+++ b/src/template/svgs.py
@@ -8,4 +8,5 @@ def load_svg_template(path: str) -> str:
base = load_svg_template("templates/svgs/base.svg")
-pokemon = load_svg_template("templates/svgs/pokemon.svg")
+pokemon_left = load_svg_template("templates/svgs/pokemon-left.svg")
+pokemon_right = load_svg_template("templates/svgs/pokemon-right.svg")
diff --git a/templates/svgs/base.svg b/templates/svgs/base.svg
index 2748a3b..041c22f 100644
--- a/templates/svgs/base.svg
+++ b/templates/svgs/base.svg
@@ -25,16 +25,28 @@
.pokemon {
opacity: 0;
- animation-name: move-pokemon;
+ animation-name: move-pokemon-left;
animation-duration: var(--pokemon-move-duration);
animation-delay: var(--pokemon-move-delay);
animation-iteration-count: infinite;
animation-timing-function: linear;
}
- @keyframes move-pokemon {
+
+ .pokemon.left-face {
+ animation-name: move-pokemon-left;
+ }
+ .pokemon.right-face {
+ animation-name: move-pokemon-right;
+ }
+
+ @keyframes move-pokemon-left {
0% {opacity: 1; transform: translate(120%, calc(64px + var(--pokemon-y-offsets)));}
100% {opacity: 1; transform: translate(-120%, calc(64px + var(--pokemon-y-offsets)));}
}
+ @keyframes move-pokemon-right {
+ 0% {opacity: 1; transform: translate(-120%, calc(64px + var(--pokemon-y-offsets)));}
+ 100% {opacity: 1; transform: translate(120%, calc(64px + var(--pokemon-y-offsets)));}
+ }
diff --git a/templates/svgs/pokemon.svg b/templates/svgs/pokemon-left.svg
similarity index 92%
rename from templates/svgs/pokemon.svg
rename to templates/svgs/pokemon-left.svg
index f6e6519..bc2214a 100644
--- a/templates/svgs/pokemon.svg
+++ b/templates/svgs/pokemon-left.svg
@@ -8,7 +8,7 @@
}
-
+
diff --git a/templates/svgs/pokemon-right.svg b/templates/svgs/pokemon-right.svg
new file mode 100644
index 0000000..05c0216
--- /dev/null
+++ b/templates/svgs/pokemon-right.svg
@@ -0,0 +1,17 @@
+
\ No newline at end of file