CanvasController.java

1
package edu.ucsb.cs156.frontiers.controllers;
2
3
import static edu.ucsb.cs156.frontiers.controllers.RosterStudentsController.upsertStudent;
4
5
import edu.ucsb.cs156.frontiers.entities.Course;
6
import edu.ucsb.cs156.frontiers.entities.Job;
7
import edu.ucsb.cs156.frontiers.entities.RosterStudent;
8
import edu.ucsb.cs156.frontiers.enums.InsertStatus;
9
import edu.ucsb.cs156.frontiers.enums.RosterStatus;
10
import edu.ucsb.cs156.frontiers.errors.EntityNotFoundException;
11
import edu.ucsb.cs156.frontiers.jobs.PullTeamsFromCanvasJob;
12
import edu.ucsb.cs156.frontiers.jobs.RemoveStudentsJob;
13
import edu.ucsb.cs156.frontiers.models.CanvasGroupSet;
14
import edu.ucsb.cs156.frontiers.models.LoadResult;
15
import edu.ucsb.cs156.frontiers.models.UpsertResponse;
16
import edu.ucsb.cs156.frontiers.repositories.CourseRepository;
17
import edu.ucsb.cs156.frontiers.repositories.RosterStudentRepository;
18
import edu.ucsb.cs156.frontiers.repositories.TeamMemberRepository;
19
import edu.ucsb.cs156.frontiers.repositories.TeamRepository;
20
import edu.ucsb.cs156.frontiers.services.CanvasService;
21
import edu.ucsb.cs156.frontiers.services.OrganizationMemberService;
22
import edu.ucsb.cs156.frontiers.services.UpdateUserService;
23
import edu.ucsb.cs156.frontiers.services.jobs.JobService;
24
import io.swagger.v3.oas.annotations.Operation;
25
import io.swagger.v3.oas.annotations.Parameter;
26
import io.swagger.v3.oas.annotations.tags.Tag;
27
import java.util.ArrayList;
28
import java.util.List;
29
import lombok.extern.slf4j.Slf4j;
30
import org.springframework.http.HttpStatus;
31
import org.springframework.http.ResponseEntity;
32
import org.springframework.security.access.prepost.PreAuthorize;
33
import org.springframework.web.bind.annotation.GetMapping;
34
import org.springframework.web.bind.annotation.PostMapping;
35
import org.springframework.web.bind.annotation.RequestMapping;
36
import org.springframework.web.bind.annotation.RequestParam;
37
import org.springframework.web.bind.annotation.RestController;
38
39
@RestController
40
@RequestMapping("/api/courses/canvas")
41
@Tag(name = "Canvas")
42
@Slf4j
43
public class CanvasController extends ApiController {
44
45
  private final CourseRepository courseRepository;
46
  private final CanvasService canvasService;
47
  private final TeamRepository teamRepository;
48
  private final JobService jobService;
49
  private final RosterStudentRepository rosterStudentRepository;
50
  private final UpdateUserService updateUserService;
51
  private final OrganizationMemberService organizationMemberService;
52
  private final TeamMemberRepository teamMemberRepository;
53
54
  public CanvasController(
55
      CourseRepository courseRepository,
56
      CanvasService canvasService,
57
      TeamRepository teamRepository,
58
      JobService jobService,
59
      RosterStudentRepository rosterStudentRepository,
60
      UpdateUserService updateUserService,
61
      OrganizationMemberService organizationMemberService,
62
      TeamMemberRepository teamMemberRepository) {
63
    this.courseRepository = courseRepository;
64
    this.canvasService = canvasService;
65
    this.teamRepository = teamRepository;
66
    this.jobService = jobService;
67
    this.rosterStudentRepository = rosterStudentRepository;
68
    this.updateUserService = updateUserService;
69
    this.organizationMemberService = organizationMemberService;
70
    this.teamMemberRepository = teamMemberRepository;
71
  }
72
73
  /**
74
   * Upload Roster students for Course from Canvas. It is important to keep the code in this method
75
   * consistent with the code in uploadRosterStudentsCSV.
76
   *
77
   * @param courseId the internal course ID in Frontiers
78
   * @return LoadResult with counts of inserted, updated, dropped students and any rejected students
79
   */
80
  @Operation(summary = "Upload Roster students for Course from Canvas")
81
  @PreAuthorize("@CourseSecurity.hasInstructorPermissions(#root, #courseId)")
82
  @PostMapping("/sync/students")
83
  public ResponseEntity<LoadResult> uploadRosterFromCanvas(
84
      @Parameter(name = "courseId") @RequestParam Long courseId) {
85
86
    Course course =
87
        courseRepository
88
            .findById(courseId)
89 1 1. lambda$uploadRosterFromCanvas$0 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CanvasController::lambda$uploadRosterFromCanvas$0 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId.toString()));
90
91
    course.getRosterStudents().stream()
92 2 1. lambda$uploadRosterFromCanvas$1 : negated conditional → KILLED
2. lambda$uploadRosterFromCanvas$1 : replaced boolean return with true for edu/ucsb/cs156/frontiers/controllers/CanvasController::lambda$uploadRosterFromCanvas$1 → KILLED
        .filter(filteredStudent -> filteredStudent.getRosterStatus() == RosterStatus.ROSTER)
93 2 1. uploadRosterFromCanvas : removed call to java/util/stream/Stream::forEach → KILLED
2. lambda$uploadRosterFromCanvas$2 : removed call to edu/ucsb/cs156/frontiers/entities/RosterStudent::setRosterStatus → KILLED
        .forEach(student -> student.setRosterStatus(RosterStatus.DROPPED));
94
95
    int counts[] = {0, 0};
96
    List<RosterStudent> rejectedStudents = new ArrayList<>();
97
98
    List<RosterStudent> canvasStudents = canvasService.getCanvasRoster(course);
99
    for (RosterStudent rosterStudent : canvasStudents) {
100
      UpsertResponse upsertResponse = upsertStudent(rosterStudent, course, RosterStatus.ROSTER);
101 1 1. uploadRosterFromCanvas : negated conditional → KILLED
      if (upsertResponse.getInsertStatus() == InsertStatus.REJECTED) {
102
        rejectedStudents.add(upsertResponse.rosterStudent());
103
      } else {
104
        InsertStatus s = upsertResponse.getInsertStatus();
105 1 1. uploadRosterFromCanvas : negated conditional → KILLED
        if (s == InsertStatus.INSERTED) {
106
          course.getRosterStudents().add(upsertResponse.rosterStudent());
107
        }
108 1 1. uploadRosterFromCanvas : Replaced integer addition with subtraction → KILLED
        counts[s.ordinal()]++;
109
      }
110
    }
111
112 1 1. uploadRosterFromCanvas : negated conditional → KILLED
    if (rejectedStudents.isEmpty()) {
113
      List<RosterStudent> droppedStudents =
114
          course.getRosterStudents().stream()
115 2 1. lambda$uploadRosterFromCanvas$3 : negated conditional → KILLED
2. lambda$uploadRosterFromCanvas$3 : replaced boolean return with true for edu/ucsb/cs156/frontiers/controllers/CanvasController::lambda$uploadRosterFromCanvas$3 → KILLED
              .filter(student -> student.getRosterStatus() == RosterStatus.DROPPED)
116
              .toList();
117
      LoadResult successfulResult =
118
          new LoadResult(
119
              counts[InsertStatus.INSERTED.ordinal()],
120
              counts[InsertStatus.UPDATED.ordinal()],
121
              droppedStudents.size(),
122
              List.of());
123
      rosterStudentRepository.saveAll(course.getRosterStudents());
124 1 1. uploadRosterFromCanvas : removed call to edu/ucsb/cs156/frontiers/services/UpdateUserService::attachUsersToRosterStudents → KILLED
      updateUserService.attachUsersToRosterStudents(course.getRosterStudents());
125
      RemoveStudentsJob job =
126
          RemoveStudentsJob.builder()
127
              .students(droppedStudents)
128
              .organizationMemberService(organizationMemberService)
129
              .rosterStudentRepository(rosterStudentRepository)
130
              .build();
131
      jobService.runAsJob(job);
132 1 1. uploadRosterFromCanvas : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CanvasController::uploadRosterFromCanvas → KILLED
      return ResponseEntity.ok(successfulResult);
133
    } else {
134
      LoadResult conflictResult = new LoadResult(0, 0, 0, rejectedStudents);
135 1 1. uploadRosterFromCanvas : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CanvasController::uploadRosterFromCanvas → KILLED
      return ResponseEntity.status(HttpStatus.CONFLICT).body(conflictResult);
136
    }
137
  }
138
139
  @Operation(summary = "See available Canvas GroupSets")
140
  @GetMapping("/groupsets")
141
  @PreAuthorize("@CourseSecurity.hasInstructorPermissions(#root, #courseId)")
142
  public List<CanvasGroupSet> getCanvasGroupSets(
143
      @Parameter(name = "courseId") @RequestParam Long courseId) {
144
    Course course =
145
        courseRepository
146
            .findById(courseId)
147 1 1. lambda$getCanvasGroupSets$4 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CanvasController::lambda$getCanvasGroupSets$4 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId.toString()));
148 1 1. getCanvasGroupSets : replaced return value with Collections.emptyList for edu/ucsb/cs156/frontiers/controllers/CanvasController::getCanvasGroupSets → KILLED
    return canvasService.getCanvasGroupSets(course);
149
  }
150
151
  @Operation(summary = "Load Groups from Canvas")
152
  @PostMapping("/sync/teams")
153
  @PreAuthorize("@CourseSecurity.hasInstructorPermissions(#root, #courseId)")
154
  public Job loadCanvasTeams(
155
      @Parameter(name = "courseId") @RequestParam Long courseId,
156
      @Parameter(name = "groupSetId") @RequestParam String groupSetId) {
157
    Course course =
158
        courseRepository
159
            .findById(courseId)
160 1 1. lambda$loadCanvasTeams$5 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CanvasController::lambda$loadCanvasTeams$5 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId.toString()));
161
    PullTeamsFromCanvasJob job =
162
        PullTeamsFromCanvasJob.builder()
163
            .course(course)
164
            .canvasService(canvasService)
165
            .teamRepository(teamRepository)
166
            .teamMemberRepository(teamMemberRepository)
167
            .courseRepository(courseRepository)
168
            .groupsetId(groupSetId)
169
            .build();
170 1 1. loadCanvasTeams : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CanvasController::loadCanvasTeams → KILLED
    return jobService.runAsJob(job);
171
  }
172
}

Mutations

89

1.1
Location : lambda$uploadRosterFromCanvas$0
Killed by : edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests]/[method:testUploadRosterFromCanvas_courseNotFound()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CanvasController::lambda$uploadRosterFromCanvas$0 → KILLED

92

1.1
Location : lambda$uploadRosterFromCanvas$1
Killed by : edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests]/[method:testUploadRosterFromCanvas_manualStudentStaysManual()]
negated conditional → KILLED

2.2
Location : lambda$uploadRosterFromCanvas$1
Killed by : edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests]/[method:testUploadRosterFromCanvas_manualStudentStaysManual()]
replaced boolean return with true for edu/ucsb/cs156/frontiers/controllers/CanvasController::lambda$uploadRosterFromCanvas$1 → KILLED

93

1.1
Location : uploadRosterFromCanvas
Killed by : edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests]/[method:testUploadRosterFromCanvas_handlesDroppedStudents()]
removed call to java/util/stream/Stream::forEach → KILLED

2.2
Location : lambda$uploadRosterFromCanvas$2
Killed by : edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests]/[method:testUploadRosterFromCanvas_handlesDroppedStudents()]
removed call to edu/ucsb/cs156/frontiers/entities/RosterStudent::setRosterStatus → KILLED

101

1.1
Location : uploadRosterFromCanvas
Killed by : edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests]/[method:testUploadRosterFromCanvas_setsPENDINGStatusWhenNoInstallationId()]
negated conditional → KILLED

105

1.1
Location : uploadRosterFromCanvas
Killed by : edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests]/[method:testUploadRosterFromCanvas_insertsNewStudents()]
negated conditional → KILLED

108

1.1
Location : uploadRosterFromCanvas
Killed by : edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests]/[method:testUploadRosterFromCanvas_manualStudentStaysManual()]
Replaced integer addition with subtraction → KILLED

112

1.1
Location : uploadRosterFromCanvas
Killed by : edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests]/[method:testUploadRosterFromCanvas_setsPENDINGStatusWhenNoInstallationId()]
negated conditional → KILLED

115

1.1
Location : lambda$uploadRosterFromCanvas$3
Killed by : edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests]/[method:testUploadRosterFromCanvas_manualStudentStaysManual()]
negated conditional → KILLED

2.2
Location : lambda$uploadRosterFromCanvas$3
Killed by : edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests]/[method:testUploadRosterFromCanvas_manualStudentStaysManual()]
replaced boolean return with true for edu/ucsb/cs156/frontiers/controllers/CanvasController::lambda$uploadRosterFromCanvas$3 → KILLED

124

1.1
Location : uploadRosterFromCanvas
Killed by : edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests]/[method:testUploadRosterFromCanvas_insertsNewStudents()]
removed call to edu/ucsb/cs156/frontiers/services/UpdateUserService::attachUsersToRosterStudents → KILLED

132

1.1
Location : uploadRosterFromCanvas
Killed by : edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests]/[method:testUploadRosterFromCanvas_manualStudentStaysManual()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CanvasController::uploadRosterFromCanvas → KILLED

135

1.1
Location : uploadRosterFromCanvas
Killed by : edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests]/[method:testUploadRosterFromCanvas_handlesRejectedStudents()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CanvasController::uploadRosterFromCanvas → KILLED

147

1.1
Location : lambda$getCanvasGroupSets$4
Killed by : edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests]/[method:testGetCanvasGroupSets_courseNotFound()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CanvasController::lambda$getCanvasGroupSets$4 → KILLED

148

1.1
Location : getCanvasGroupSets
Killed by : edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests]/[method:testGetCanvasGroupSets_returnsGroupSets()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/frontiers/controllers/CanvasController::getCanvasGroupSets → KILLED

160

1.1
Location : lambda$loadCanvasTeams$5
Killed by : edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests]/[method:testLoadCanvasTeams_courseNotFound()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CanvasController::lambda$loadCanvasTeams$5 → KILLED

170

1.1
Location : loadCanvasTeams
Killed by : edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CanvasControllerTests]/[method:testLoadCanvasTeams_startsJob()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CanvasController::loadCanvasTeams → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0