| 1 | package edu.ucsb.cs156.dining.controllers; | |
| 2 | ||
| 3 | import edu.ucsb.cs156.dining.repositories.ReviewRepository; | |
| 4 | import edu.ucsb.cs156.dining.repositories.projections.ItemRatingProjection; | |
| 5 | import edu.ucsb.cs156.dining.statuses.ModerationStatus; | |
| 6 | import edu.ucsb.cs156.dining.util.StatsWindow; | |
| 7 | import io.swagger.v3.oas.annotations.Operation; | |
| 8 | import io.swagger.v3.oas.annotations.tags.Tag; | |
| 9 | import java.time.LocalDateTime; | |
| 10 | import java.util.List; | |
| 11 | import lombok.extern.slf4j.Slf4j; | |
| 12 | import org.springframework.beans.factory.annotation.Autowired; | |
| 13 | import org.springframework.data.domain.PageRequest; | |
| 14 | import org.springframework.security.access.prepost.PreAuthorize; | |
| 15 | import org.springframework.web.bind.annotation.GetMapping; | |
| 16 | import org.springframework.web.bind.annotation.RequestMapping; | |
| 17 | import org.springframework.web.bind.annotation.RequestParam; | |
| 18 | import org.springframework.web.bind.annotation.RestController; | |
| 19 | ||
| 20 | @Tag(name = "Statistics") | |
| 21 | @RequestMapping("/api/statistics") | |
| 22 | @RestController | |
| 23 | @Slf4j | |
| 24 | public class StatisticsController extends ApiController { | |
| 25 | ||
| 26 | private static final int DEFAULT_LIMIT = 10; | |
| 27 | private static final int MAX_LIMIT = 50; | |
| 28 | private static final long DEFAULT_MIN_REVIEWS = 3; | |
| 29 | ||
| 30 | @Autowired ReviewRepository reviewRepository; | |
| 31 | ||
| 32 | public record StatisticsSummary( | |
| 33 | long totalApprovedReviews, | |
| 34 | long totalMenuItemsReviewed, | |
| 35 | long totalCommonsCovered, | |
| 36 | LocalDateTime lastReviewDate) {} | |
| 37 | ||
| 38 | public record RatedItem( | |
| 39 | Long itemId, | |
| 40 | String name, | |
| 41 | String diningCommonsCode, | |
| 42 | String mealCode, | |
| 43 | Double avgStars, | |
| 44 | Long reviewCount) {} | |
| 45 | ||
| 46 | @Operation(summary = "Get a summary of review statistics") | |
| 47 | @PreAuthorize("hasRole('ROLE_USER')") | |
| 48 | @GetMapping(value = "", produces = "application/json") | |
| 49 | public StatisticsSummary getSummary() { | |
| 50 |
1
1. getSummary : replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::getSummary → KILLED |
return new StatisticsSummary( |
| 51 | reviewRepository.countByStatus(ModerationStatus.APPROVED), | |
| 52 | reviewRepository.countDistinctItemsByStatus(ModerationStatus.APPROVED), | |
| 53 | reviewRepository.countDistinctCommonsByStatus(ModerationStatus.APPROVED), | |
| 54 | reviewRepository.findMaxDateItemServedByStatus(ModerationStatus.APPROVED)); | |
| 55 | } | |
| 56 | ||
| 57 | LocalDateTime sinceFor(StatsWindow window) { | |
| 58 |
1
1. sinceFor : replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::sinceFor → KILLED |
return window.since(LocalDateTime.now()); |
| 59 | } | |
| 60 | ||
| 61 | private static int clampLimit(int limit) { | |
| 62 |
1
1. clampLimit : replaced int return with 0 for edu/ucsb/cs156/dining/controllers/StatisticsController::clampLimit → KILLED |
return Math.min(Math.max(limit, 1), MAX_LIMIT); |
| 63 | } | |
| 64 | ||
| 65 | private static long clampMinReviews(long minReviews) { | |
| 66 |
1
1. clampMinReviews : replaced long return with 0 for edu/ucsb/cs156/dining/controllers/StatisticsController::clampMinReviews → KILLED |
return Math.max(minReviews, 1L); |
| 67 | } | |
| 68 | ||
| 69 | private RatedItem toRatedItem(ItemRatingProjection p) { | |
| 70 |
1
1. toRatedItem : replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::toRatedItem → KILLED |
return new RatedItem( |
| 71 | p.getItemId(), | |
| 72 | p.getName(), | |
| 73 | p.getDiningCommonsCode(), | |
| 74 | p.getMealCode(), | |
| 75 | p.getAvgStars(), | |
| 76 | p.getReviewCount()); | |
| 77 | } | |
| 78 | ||
| 79 | @Operation(summary = "Get best rated menu items by average stars") | |
| 80 | @PreAuthorize("hasRole('ROLE_USER')") | |
| 81 | @GetMapping(value = "/items/best", produces = "application/json") | |
| 82 | public List<RatedItem> bestRatedItems( | |
| 83 | @RequestParam(defaultValue = "ALL") StatsWindow window, | |
| 84 | @RequestParam(defaultValue = "" + DEFAULT_LIMIT) int limit, | |
| 85 | @RequestParam(name = "minReviews", defaultValue = "" + DEFAULT_MIN_REVIEWS) long minReviews) { | |
| 86 | int clampedLimit = clampLimit(limit); | |
| 87 | long clampedMinReviews = clampMinReviews(minReviews); | |
| 88 | ||
| 89 | LocalDateTime since = sinceFor(window); | |
| 90 | List<ItemRatingProjection> projections = | |
| 91 | reviewRepository.findTopRatedItems( | |
| 92 | ModerationStatus.APPROVED, since, clampedMinReviews, PageRequest.of(0, clampedLimit)); | |
| 93 | ||
| 94 |
1
1. bestRatedItems : replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::bestRatedItems → KILLED |
return projections.stream().map(this::toRatedItem).toList(); |
| 95 | } | |
| 96 | ||
| 97 | @Operation(summary = "Get worst rated menu items by average stars") | |
| 98 | @PreAuthorize("hasRole('ROLE_USER')") | |
| 99 | @GetMapping(value = "/items/worst", produces = "application/json") | |
| 100 | public List<RatedItem> worstRatedItems( | |
| 101 | @RequestParam(defaultValue = "ALL") StatsWindow window, | |
| 102 | @RequestParam(defaultValue = "" + DEFAULT_LIMIT) int limit, | |
| 103 | @RequestParam(name = "minReviews", defaultValue = "" + DEFAULT_MIN_REVIEWS) long minReviews) { | |
| 104 | int clampedLimit = clampLimit(limit); | |
| 105 | long clampedMinReviews = clampMinReviews(minReviews); | |
| 106 | ||
| 107 | LocalDateTime since = sinceFor(window); | |
| 108 | List<ItemRatingProjection> projections = | |
| 109 | reviewRepository.findBottomRatedItems( | |
| 110 | ModerationStatus.APPROVED, since, clampedMinReviews, PageRequest.of(0, clampedLimit)); | |
| 111 | ||
| 112 |
1
1. worstRatedItems : replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::worstRatedItems → KILLED |
return projections.stream().map(this::toRatedItem).toList(); |
| 113 | } | |
| 114 | } | |
Mutations | ||
| 50 |
1.1 |
|
| 58 |
1.1 |
|
| 62 |
1.1 |
|
| 66 |
1.1 |
|
| 70 |
1.1 |
|
| 94 |
1.1 |
|
| 112 |
1.1 |