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

43 statements  

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

1import logging 

2from statistics import mean 

3 

4from app.modules.reviews.repository import ReviewRepository 

5 

6logger = logging.getLogger(__name__) 

7 

8 

9class ReviewService: 

10 def __init__(self, repo: ReviewRepository): 

11 self.repo = repo 

12 

13 def create_review(self, card_id: int, rating: int): 

14 logger.debug("create_review called: card_id=%s rating=%s", card_id, rating) 

15 

16 if rating < 1 or rating > 5: 

17 logger.warning( 

18 "create_review validation failed: rating out of range (1-5): %s for card_id=%s", 

19 rating, 

20 card_id, 

21 ) 

22 raise ValueError("Rating must be 1–5") 

23 

24 try: 

25 review = self.repo.create(card_id, rating) 

26 logger.info( 

27 "Review created: card_id=%s rating=%s review_id=%s", 

28 card_id, 

29 rating, 

30 getattr(review, "id", None), 

31 ) 

32 return review 

33 except Exception: 

34 logger.exception("Error creating review for card_id=%s rating=%s", card_id, rating) 

35 raise 

36 

37 def compute_deck_score(self, deck_id: int): 

38 logger.debug("compute_deck_score called: deck_id=%s", deck_id) 

39 try: 

40 reviews = self.repo.get_by_deck(deck_id) 

41 except Exception: 

42 logger.exception("Failed to fetch reviews for deck_id=%s", deck_id) 

43 raise 

44 

45 if not reviews: 

46 logger.info("No reviews found for deck_id=%s; returning zeroed score", deck_id) 

47 return {"deck_id": deck_id, "average_score": 0.0, "review_count": 0} 

48 

49 scores = [r.rating for r in reviews] 

50 average = round(mean(scores), 2) 

51 count = len(scores) 

52 logger.info( 

53 "Computed deck score: deck_id=%s average_score=%s review_count=%s", 

54 deck_id, 

55 average, 

56 count, 

57 ) 

58 

59 return {"deck_id": deck_id, "average_score": average, "review_count": count} 

60 

61 def delete_deck_reviews(self, deck_id: int): 

62 logger.debug("delete_deck_reviews called: deck_id=%s", deck_id) 

63 try: 

64 result = self.repo.delete_by_deck(deck_id) 

65 logger.info( 

66 "Deleted reviews for deck_id=%s result=%s", 

67 deck_id, 

68 getattr(result, "deleted_count", result), 

69 ) 

70 return result 

71 except Exception: 

72 logger.exception("Failed to delete reviews for deck_id=%s", deck_id) 

73 raise