StatisticsController.java

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
Location : getSummary
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:api_statistics_null_lastUpdated_serializes_as_null()]
replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::getSummary → KILLED

58

1.1
Location : sinceFor
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:items_best_window_SIX_MONTHS_passes_correct_since()]
replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::sinceFor → KILLED

62

1.1
Location : clampLimit
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:items_best_minReviews_zero_clamped_to_1()]
replaced int return with 0 for edu/ucsb/cs156/dining/controllers/StatisticsController::clampLimit → KILLED

66

1.1
Location : clampMinReviews
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:items_best_minReviews_zero_clamped_to_1()]
replaced long return with 0 for edu/ucsb/cs156/dining/controllers/StatisticsController::clampMinReviews → KILLED

70

1.1
Location : toRatedItem
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:items_best_default_params_uses_ALL_window_limit_10_minReviews_3()]
replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::toRatedItem → KILLED

94

1.1
Location : bestRatedItems
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:items_best_default_params_uses_ALL_window_limit_10_minReviews_3()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::bestRatedItems → KILLED

112

1.1
Location : worstRatedItems
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:items_worst_default_params_calls_findBottomRatedItems_and_returns_shape()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::worstRatedItems → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0