128 lines
5.1 KiB
Python
128 lines
5.1 KiB
Python
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=row.descriptions.strip(),
|
||
)
|
||
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(),
|
||
)
|