| 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 |
|
| 92 |
1.1 2.2 |
|
| 93 |
1.1 2.2 |
|
| 101 |
1.1 |
|
| 105 |
1.1 |
|
| 108 |
1.1 |
|
| 112 |
1.1 |
|
| 115 |
1.1 2.2 |
|
| 124 |
1.1 |
|
| 132 |
1.1 |
|
| 135 |
1.1 |
|
| 147 |
1.1 |
|
| 148 |
1.1 |
|
| 160 |
1.1 |
|
| 170 |
1.1 |