from abc import ABCMeta, abstractmethod from pathlib import Path from aiocache import Cache, cached # type: ignore from sqlalchemy import select from server.infra.db import AsyncDB from server.modules.descriptions.domain import Breed from server.modules.descriptions.repository import models class ACharactersRepository(metaclass=ABCMeta): @abstractmethod async def get_characters(self) -> list[Breed]: pass @abstractmethod async def get_character(self, alias: str) -> Breed | None: pass class CharactersRepository(ACharactersRepository): def __init__(self): pass @cached(ttl=60, cache=Cache.MEMORY) async def get_characters(self) -> list[Breed]: breed_dir = Path("server/modules/descriptions/repository/breed_descriptions") breeds: list[Breed] = [] # Идем по каждому текстовому файлу с описанием породы for breed_file in breed_dir.glob("*.txt"): breed_name = breed_file.stem # имя файла без расширения - название породы description = breed_file.read_text(encoding="utf-8") # читаем описание из файла breeds.append( Breed( id=breed_name, name=breed_name.replace("_", " "), alias=breed_file.stem, description=description.strip(), ) ) breeds.sort(key=lambda b: b.name) return breeds async def get_character(self, alias: str) -> Breed | None: breeds = await self.get_characters() data = [b for b in breeds if b.alias == alias] if len(data) == 0: return None return data[0] class PGCharactersRepository(ACharactersRepository): _db: AsyncDB def __init__(self, db: AsyncDB): self._db = db # ───────────────────────────────────────────────────────────────────── # # 8️⃣ Кешируемый метод, который возвращает **все** породы # ───────────────────────────────────────────────────────────────────── # @cached(ttl=60, cache=Cache.MEMORY) # 1‑мин. кеш async def get_characters(self) -> list[Breed]: """ Читает данные из таблицы `beerds.beerds` и преобразует каждую строку в экземпляр `Breed`. Поле `signs` игнорируется – в `Breed` его нет. """ async with self._db.async_session() as session: # Писем SELECT‑запрос (получаем все строки) stmt = select( # type: ignore models.Beerds.id, models.Beerds.name, models.Beerds.alias, models.Beerds.descriptions, ) result = await session.execute(stmt) rows = result.fetchall() # Конвертируем в Breed breeds: list[Breed] = [ Breed( id=str(row.id), name=row.name.strip(), alias=row.alias.strip(), description=None, ) for row in rows ] # Сортируем по имени, как было в файле‑реализации breeds.sort(key=lambda b: b.name.lower()) return breeds # ───────────────────────────────────────────────────────────────────── # # 9️⃣ Получить конкретную породу по псевдониму # ───────────────────────────────────────────────────────────────────── # async def get_character(self, alias: str) -> Breed | None: """ Быстрый запрос без получения всех пород. Если результат пустой – возвращаем `None`. """ async with self._db.async_session() as session: stmt = ( select( # type: ignore models.Beerds.id, models.Beerds.name, models.Beerds.alias, models.Beerds.descriptions, ) .where(models.Beerds.alias == alias) .limit(1) ) result = await session.execute(stmt) row = result.fetchone() if row is None: # pragma: no cover return None return Breed( id=str(row.id), name=row.name.strip(), alias=row.alias.strip(), description=row.descriptions.strip(), )