Coverage for src/app/modules/decks/services.py: 100%
62 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-24 08:34 +0000
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-24 08:34 +0000
1import logging
3from app.modules.decks.repository import DeckRepository
5logger = logging.getLogger(__name__)
8class DeckService:
9 def __init__(self, repo: DeckRepository):
10 self.repo = repo
12 def create_deck(self, name: str):
13 logger.debug("create_deck called: name=%s", name)
15 if not name.strip():
16 logger.warning("create_deck validation failed: empty name")
17 raise ValueError("Deck name cannot be empty")
19 try:
20 deck = self.repo.create(name=name.strip())
21 logger.info("Deck created: id=%s name=%s", getattr(deck, "id", None), name.strip())
22 return deck
23 except Exception:
24 logger.exception("Error creating deck with name=%s", name)
25 raise
27 def get_deck(self, deck_id: int):
28 logger.debug("get_deck called: deck_id=%s", deck_id)
29 try:
30 deck = self.repo.get(deck_id)
31 except Exception:
32 logger.exception("Failed to fetch deck_id=%s", deck_id)
33 raise
35 if not deck:
36 logger.info("Deck not found: deck_id=%s", deck_id)
37 raise ValueError("Deck not found")
39 logger.debug("Deck fetched: deck_id=%s", deck_id)
40 return deck
42 def get_deck_with_cards(self, deck_id: int):
43 logger.debug("get_deck_with_cards called: deck_id=%s", deck_id)
44 try:
45 deck = self.repo.get_with_cards(deck_id)
46 except Exception:
47 logger.exception("Failed to fetch deck with cards for deck_id=%s", deck_id)
48 raise
50 if not deck:
51 logger.info("Deck not found (with cards): deck_id=%s", deck_id)
52 raise ValueError("Deck not found")
54 card_count = len(getattr(deck, "cards", []))
55 logger.debug(
56 "Transforming deck to API shape: deck_id=%s card_count=%s", deck_id, card_count
57 )
59 # transform ORM → API shape
60 result = {
61 "id": deck.id,
62 "name": deck.name,
63 "cards": [{"id": c.id, "front": c.front, "back": c.back} for c in deck.cards],
64 }
66 logger.info("Deck with cards prepared: deck_id=%s card_count=%s", deck_id, card_count)
67 return result
69 def delete_deck(self, deck_id: int):
70 logger.debug("delete_deck called: deck_id=%s", deck_id)
71 try:
72 deck = self.repo.get(deck_id)
73 except Exception:
74 logger.exception("Failed to fetch deck before delete: deck_id=%s", deck_id)
75 raise
77 if not deck:
78 logger.info("Attempted to delete non-existent deck: deck_id=%s", deck_id)
79 raise ValueError("Deck not found")
81 try:
82 # cascade behavior happens via ORM config OR manual cleanup fallback
83 self.repo.delete(deck)
84 logger.info("Deleted deck: deck_id=%s", deck_id)
85 return {"status": "deleted"}
86 except Exception:
87 logger.exception("Failed to delete deck_id=%s", deck_id)
88 raise