Coverage for src/app/modules/cards/services.py: 100%

57 statements  

« prev     ^ index     » next       coverage.py v7.14.3, created at 2026-06-24 08:34 +0000

1import logging 

2 

3from app.modules.cards.repository import CardRepository 

4from app.modules.cards.schemas import CardCreate 

5 

6logger = logging.getLogger(__name__) 

7 

8 

9class CardService: 

10 def __init__(self, repo: CardRepository): 

11 self.repo = repo 

12 

13 def create_card(self, data: CardCreate): 

14 logger.debug( 

15 "create_card called: deck_id=%s front_present=%s back_present=%s", 

16 data.deck_id, 

17 bool(data.front), 

18 bool(data.back), 

19 ) 

20 # business rule example (simple but real) 

21 if not data.front or not data.back: 

22 logger.warning( 

23 "create_card validation failed: missing front/back: deck_id=%s", data.deck_id 

24 ) 

25 raise ValueError("Card must have front and back") 

26 

27 try: 

28 card = self.repo.create( 

29 front=data.front.strip(), 

30 back=data.back.strip(), 

31 deck_id=data.deck_id, 

32 ) 

33 logger.info("Card created: id=%s deck_id=%s", getattr(card, "id", None), data.deck_id) 

34 return card 

35 except Exception: 

36 logger.exception( 

37 "Error creating card for deck_id=%s front=%s", data.deck_id, data.front 

38 ) 

39 raise 

40 

41 def get_card(self, card_id: int): 

42 logger.debug("get_card called: card_id=%s", card_id) 

43 try: 

44 card = self.repo.get(card_id) 

45 except Exception: 

46 logger.exception("Failed to fetch card_id=%s", card_id) 

47 raise 

48 

49 if not card: 

50 logger.info("Card not found: card_id=%s", card_id) 

51 raise ValueError("Card not found") 

52 

53 logger.debug("Card fetched: card_id=%s", card_id) 

54 return card 

55 

56 def get_cards_for_deck(self, deck_id: int): 

57 logger.debug("get_cards_for_deck called: deck_id=%s", deck_id) 

58 try: 

59 cards = self.repo.list_by_deck(deck_id) 

60 logger.info("Fetched %s cards for deck_id=%s", len(cards), deck_id) 

61 return cards 

62 except Exception: 

63 logger.exception("Failed to list cards for deck_id=%s", deck_id) 

64 raise 

65 

66 def delete_card(self, card_id: int): 

67 logger.debug("delete_card called: card_id=%s", card_id) 

68 try: 

69 card = self.repo.get(card_id) 

70 except Exception: 

71 logger.exception("Failed to fetch card before delete: card_id=%s", card_id) 

72 raise 

73 

74 if not card: 

75 logger.info("Attempted to delete non-existent card: card_id=%s", card_id) 

76 raise ValueError("Card not found") 

77 

78 try: 

79 self.repo.delete(card_id) 

80 logger.info("Deleted card: card_id=%s", card_id) 

81 return {"status": "deleted"} 

82 except Exception: 

83 logger.exception("Failed to delete card_id=%s", card_id) 

84 raise