gwoe-antragspruefer/app/repositories/abonnement_repository.py

139 lines
4.1 KiB
Python
Raw Normal View History

"""AbonnementRepository — Port für E-Mail-Digest-Abos (#124).
Kapselt die `email_subscriptions`-Tabelle. Der Name Abonnement" ist die
Ubiquitous-Language-Form (Kapitel 4 der DDD-Bewertung); intern heißt die
Tabelle weiter `email_subscriptions`.
"""
from __future__ import annotations
from typing import Optional, Protocol, runtime_checkable
from .. import database
@runtime_checkable
class AbonnementRepository(Protocol):
async def create(
self,
user_id: str,
email: str,
bundesland: Optional[str] = None,
partei: Optional[str] = None,
frequency: str = "daily",
) -> int: ...
async def list_by_user(self, user_id: str) -> list[dict]: ...
async def list_all(self) -> list[dict]: ...
async def list_due(self, frequency: str = "daily") -> list[dict]: ...
async def delete(self, user_id: str, sub_id: int) -> bool: ...
async def delete_by_id(self, sub_id: int) -> bool: ...
async def mark_sent(self, sub_id: int) -> None: ...
class SqliteAbonnementRepository:
async def create(
self,
user_id: str,
email: str,
bundesland: Optional[str] = None,
partei: Optional[str] = None,
frequency: str = "daily",
) -> int:
return await database.create_subscription(
user_id, email, bundesland, partei, frequency,
)
async def list_by_user(self, user_id: str) -> list[dict]:
return await database.list_subscriptions(user_id)
async def list_all(self) -> list[dict]:
return await database.list_all_subscriptions()
async def list_due(self, frequency: str = "daily") -> list[dict]:
return await database.get_all_subscriptions_due(frequency)
async def delete(self, user_id: str, sub_id: int) -> bool:
return await database.delete_subscription(user_id, sub_id)
async def delete_by_id(self, sub_id: int) -> bool:
return await database.delete_subscription_by_id(sub_id)
async def mark_sent(self, sub_id: int) -> None:
await database.mark_subscription_sent(sub_id)
class InMemoryAbonnementRepository:
"""Test-Fake. Ignoriert ``last_sent``-Zeitberechnung — ``list_due`` gibt
einfach alle zurück, bei denen ``last_sent`` ``None`` ist. Für
Zeit-bezogene Tests explizit ``mark_sent`` nutzen."""
def __init__(self) -> None:
self._subs: list[dict] = []
self._next_id = 1
async def create(
self,
user_id: str,
email: str,
bundesland: Optional[str] = None,
partei: Optional[str] = None,
frequency: str = "daily",
) -> int:
sid = self._next_id
self._next_id += 1
self._subs.append({
"id": sid,
"user_id": user_id,
"email": email,
"bundesland": bundesland,
"partei": partei,
"frequency": frequency,
"last_sent": None,
"created_at": "",
})
return sid
async def list_by_user(self, user_id: str) -> list[dict]:
return [dict(s) for s in self._subs if s["user_id"] == user_id]
async def list_all(self) -> list[dict]:
return [dict(s) for s in self._subs]
async def list_due(self, frequency: str = "daily") -> list[dict]:
return [
dict(s) for s in self._subs
if s["frequency"] == frequency and s.get("last_sent") is None
]
async def delete(self, user_id: str, sub_id: int) -> bool:
for i, s in enumerate(self._subs):
if s["id"] == sub_id and s["user_id"] == user_id:
self._subs.pop(i)
return True
return False
async def delete_by_id(self, sub_id: int) -> bool:
for i, s in enumerate(self._subs):
if s["id"] == sub_id:
self._subs.pop(i)
return True
return False
async def mark_sent(self, sub_id: int) -> None:
for s in self._subs:
if s["id"] == sub_id:
s["last_sent"] = "sent"
_default_abonnement_repo: AbonnementRepository = SqliteAbonnementRepository()
def get_abonnement_repository() -> AbonnementRepository:
return _default_abonnement_repo