StudentsController.java

1
package edu.ucsb.cs156.organic.controllers;
2
3
import edu.ucsb.cs156.organic.entities.Course;
4
import edu.ucsb.cs156.organic.entities.Staff;
5
import edu.ucsb.cs156.organic.entities.Student;
6
import edu.ucsb.cs156.organic.entities.User;
7
import edu.ucsb.cs156.organic.repositories.CourseRepository;
8
import edu.ucsb.cs156.organic.repositories.StaffRepository;
9
import edu.ucsb.cs156.organic.repositories.StudentRepository;
10
import edu.ucsb.cs156.organic.repositories.UserRepository;
11
import io.swagger.v3.oas.annotations.Operation;
12
import io.swagger.v3.oas.annotations.Parameter;
13
import io.swagger.v3.oas.annotations.tags.Tag;
14
import lombok.extern.slf4j.Slf4j;
15
16
import com.fasterxml.jackson.core.JsonProcessingException;
17
import com.opencsv.CSVReader;
18
import com.opencsv.exceptions.CsvException;
19
20
import org.springframework.beans.factory.annotation.Autowired;
21
import org.springframework.format.annotation.DateTimeFormat;
22
import org.springframework.security.access.AccessDeniedException;
23
import org.springframework.security.access.prepost.PreAuthorize;
24
import org.springframework.web.bind.annotation.GetMapping;
25
import org.springframework.web.bind.annotation.PostMapping;
26
import org.springframework.web.bind.annotation.RequestMapping;
27
import org.springframework.web.bind.annotation.RequestParam;
28
import org.springframework.web.bind.annotation.RestController;
29
import org.springframework.web.multipart.MultipartFile;
30
31
import edu.ucsb.cs156.organic.errors.EntityNotFoundException;
32
33
import java.io.BufferedInputStream;
34
import java.io.BufferedReader;
35
import java.io.FileReader;
36
import java.io.IOException;
37
import java.io.InputStream;
38
import java.io.InputStreamReader;
39
import java.time.LocalDateTime;
40
import java.util.List;
41
import java.util.Map;
42
import java.util.Optional;
43
44
@Tag(name = "Students")
45
@RequestMapping("/api/students")
46
@RestController
47
@Slf4j
48
public class StudentsController extends ApiController {
49
50
    public enum Status {
51
        INSERTED, UPDATED
52
    };
53
54
    @Autowired
55
    CourseRepository courseRepository;
56
57
    @Autowired
58
    StaffRepository staffRepository;
59
60
    @Autowired
61
    StudentRepository studentRepository;
62
63
    @Autowired
64
    UserRepository userRepository;
65
66
    @Operation(summary = "Get Students for course")
67
    @PreAuthorize("hasRole('ROLE_ADMIN')")
68
    @GetMapping("/all")
69
    public Iterable<Student> getStaff(
70
            @Parameter(name = "courseId") @RequestParam Long courseId)
71
            throws JsonProcessingException {
72
73
        Course course = courseRepository.findById(courseId)
74 1 1. lambda$getStaff$0 : replaced return value with null for edu/ucsb/cs156/organic/controllers/StudentsController::lambda$getStaff$0 → KILLED
                .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId.toString()));
75
76
        Iterable<Student> students = studentRepository.findByCourseId(course.getId());
77 1 1. getStaff : replaced return value with null for edu/ucsb/cs156/organic/controllers/StudentsController::getStaff → KILLED
        return students;
78
    }
79
80
    @Operation(summary = "Create a new student for a course")
81
    @PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_INSTRUCTOR', 'ROLE_USER')")
82
    @PostMapping("/post")
83
    public Student postStudent(
84
            @Parameter(name = "courseId", description = "course ID") @RequestParam Long courseId,
85
            @Parameter(name = "email", description = "Email of the student") @RequestParam String email,
86
            @Parameter(name = "fname", description = "First name") @RequestParam String fname,
87
            @Parameter(name = "lname", description = "Last name") @RequestParam String lname,
88
            @Parameter(name = "studentId", description = "Student ID") @RequestParam String studentId) {
89
        Course course = courseRepository.findById(courseId)
90 1 1. lambda$postStudent$1 : replaced return value with null for edu/ucsb/cs156/organic/controllers/StudentsController::lambda$postStudent$1 → KILLED
                .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId.toString()));
91
        User u = getCurrentUser().getUser();
92 1 1. postStudent : negated conditional → KILLED
        if (!u.isAdmin()) {
93
            staffRepository.findByCourseIdAndGithubId(course.getId(), u.getGithubId())
94 1 1. lambda$postStudent$2 : replaced return value with null for edu/ucsb/cs156/organic/controllers/StudentsController::lambda$postStudent$2 → KILLED
                    .orElseThrow(() -> new AccessDeniedException(
95
                            "User is not a staff member for this course"));
96
        }
97
        Student student = Student.builder().courseId(course.getId()).email(email).fname(fname)
98
                .lname(lname).studentId(studentId).build();
99
        studentRepository.save(student);
100 1 1. postStudent : replaced return value with null for edu/ucsb/cs156/organic/controllers/StudentsController::postStudent → KILLED
        return student;
101
    }
102
103
    @Operation(summary = "Upload Students for Course in UCSB Egrades Format")
104
    @PreAuthorize("hasRole('ROLE_ADMIN')")
105
    @PostMapping(value = "/upload/egrades", consumes = { "multipart/form-data" })
106
    public Map<String, String> getStaff(
107
            @Parameter(name = "courseId") @RequestParam Long courseId,
108
            @Parameter(name = "file") @RequestParam("file") MultipartFile file)
109
            throws JsonProcessingException, IOException, CsvException {
110
111
        Course course = courseRepository.findById(courseId)
112 1 1. lambda$getStaff$3 : replaced return value with null for edu/ucsb/cs156/organic/controllers/StudentsController::lambda$getStaff$3 → KILLED
                .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId.toString()));
113
114
        int counts[] = { 0, 0 };
115
116
        try (InputStream inputStream = new BufferedInputStream(file.getInputStream());
117
                InputStreamReader reader = new InputStreamReader(inputStream);
118
                CSVReader csvReader = new CSVReader(reader);) {
119 1 1. getStaff : removed call to com/opencsv/CSVReader::skip → KILLED
            csvReader.skip(2);
120
            List<String[]> myEntries = csvReader.readAll();
121
            for (String[] row : myEntries) {
122
                Student student = fromEgradesCSVRow(row);
123 1 1. getStaff : removed call to edu/ucsb/cs156/organic/entities/Student::setCourseId → KILLED
                student.setCourseId(course.getId());
124
                Status s = upsertStudent(student, course);
125 1 1. getStaff : Replaced integer addition with subtraction → KILLED
                counts[s.ordinal()]++;
126
            }
127
        }
128 1 1. getStaff : replaced return value with Collections.emptyMap for edu/ucsb/cs156/organic/controllers/StudentsController::getStaff → KILLED
        return Map.of(
129
                "filename", file.getOriginalFilename(),
130
                "message", String.format("Inserted %d new students, Updated %d students",
131
                        counts[Status.INSERTED.ordinal()], counts[Status.UPDATED.ordinal()]));
132
133
    }
134
135
    public Student fromEgradesCSVRow(String[] row) {
136 1 1. fromEgradesCSVRow : replaced return value with null for edu/ucsb/cs156/organic/controllers/StudentsController::fromEgradesCSVRow → KILLED
        return Student.builder()
137
                .fname(row[5])
138
                .lname(row[4])
139
                .studentId(row[1])
140
                .email(row[10])
141
                .build();
142
    }
143
144
    public Status upsertStudent(Student student, Course course) {
145
        Optional<Student> existingStudent = studentRepository.findByCourseIdAndStudentId(course.getId(),
146
                student.getStudentId());
147 1 1. upsertStudent : negated conditional → KILLED
        if (existingStudent.isPresent()) {
148
            Student existingStudentObj = existingStudent.get();
149 1 1. upsertStudent : removed call to edu/ucsb/cs156/organic/entities/Student::setFname → KILLED
            existingStudentObj.setFname(student.getFname());
150 1 1. upsertStudent : removed call to edu/ucsb/cs156/organic/entities/Student::setLname → KILLED
            existingStudentObj.setLname(student.getLname());
151 1 1. upsertStudent : removed call to edu/ucsb/cs156/organic/entities/Student::setEmail → KILLED
            existingStudentObj.setEmail(student.getEmail());
152
            studentRepository.save(existingStudentObj);
153 1 1. upsertStudent : replaced return value with null for edu/ucsb/cs156/organic/controllers/StudentsController::upsertStudent → KILLED
            return Status.UPDATED;
154
        } else {
155
            studentRepository.save(student);
156 1 1. upsertStudent : replaced return value with null for edu/ucsb/cs156/organic/controllers/StudentsController::upsertStudent → KILLED
            return Status.INSERTED;
157
        }
158
159
    }
160
161
}

Mutations

74

1.1
Location : lambda$getStaff$0
Killed by : edu.ucsb.cs156.organic.controllers.StudentsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.StudentsControllerTests]/[method:admin_cannot_get_all_students_for_a_non_existing_course()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/StudentsController::lambda$getStaff$0 → KILLED

77

1.1
Location : getStaff
Killed by : edu.ucsb.cs156.organic.controllers.StudentsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.StudentsControllerTests]/[method:admin_can_get_all_students_for_a_course()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/StudentsController::getStaff → KILLED

90

1.1
Location : lambda$postStudent$1
Killed by : edu.ucsb.cs156.organic.controllers.StudentsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.StudentsControllerTests]/[method:can_not_post_student_course_dne()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/StudentsController::lambda$postStudent$1 → KILLED

92

1.1
Location : postStudent
Killed by : edu.ucsb.cs156.organic.controllers.StudentsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.StudentsControllerTests]/[method:user_can_not_post_student_if_not_instructor()]
negated conditional → KILLED

94

1.1
Location : lambda$postStudent$2
Killed by : edu.ucsb.cs156.organic.controllers.StudentsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.StudentsControllerTests]/[method:user_can_not_post_student_if_not_instructor()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/StudentsController::lambda$postStudent$2 → KILLED

100

1.1
Location : postStudent
Killed by : edu.ucsb.cs156.organic.controllers.StudentsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.StudentsControllerTests]/[method:admin_can_post_student()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/StudentsController::postStudent → KILLED

112

1.1
Location : lambda$getStaff$3
Killed by : edu.ucsb.cs156.organic.controllers.StudentsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.StudentsControllerTests]/[method:admin_cannot_upload_students_for_a_non_existing_course()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/StudentsController::lambda$getStaff$3 → KILLED

119

1.1
Location : getStaff
Killed by : edu.ucsb.cs156.organic.controllers.StudentsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.StudentsControllerTests]/[method:admin_can_upload_students_for_an_existing_course()]
removed call to com/opencsv/CSVReader::skip → KILLED

123

1.1
Location : getStaff
Killed by : edu.ucsb.cs156.organic.controllers.StudentsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.StudentsControllerTests]/[method:admin_can_upload_students_for_an_existing_course()]
removed call to edu/ucsb/cs156/organic/entities/Student::setCourseId → KILLED

125

1.1
Location : getStaff
Killed by : edu.ucsb.cs156.organic.controllers.StudentsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.StudentsControllerTests]/[method:admin_can_upload_students_for_an_existing_course()]
Replaced integer addition with subtraction → KILLED

128

1.1
Location : getStaff
Killed by : edu.ucsb.cs156.organic.controllers.StudentsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.StudentsControllerTests]/[method:admin_can_upload_students_for_an_existing_course()]
replaced return value with Collections.emptyMap for edu/ucsb/cs156/organic/controllers/StudentsController::getStaff → KILLED

136

1.1
Location : fromEgradesCSVRow
Killed by : edu.ucsb.cs156.organic.controllers.StudentsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.StudentsControllerTests]/[method:admin_can_upload_students_for_an_existing_course()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/StudentsController::fromEgradesCSVRow → KILLED

147

1.1
Location : upsertStudent
Killed by : edu.ucsb.cs156.organic.controllers.StudentsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.StudentsControllerTests]/[method:admin_can_upload_students_for_an_existing_course()]
negated conditional → KILLED

149

1.1
Location : upsertStudent
Killed by : edu.ucsb.cs156.organic.controllers.StudentsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.StudentsControllerTests]/[method:admin_can_upload_students_for_an_existing_course()]
removed call to edu/ucsb/cs156/organic/entities/Student::setFname → KILLED

150

1.1
Location : upsertStudent
Killed by : edu.ucsb.cs156.organic.controllers.StudentsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.StudentsControllerTests]/[method:admin_can_upload_students_for_an_existing_course()]
removed call to edu/ucsb/cs156/organic/entities/Student::setLname → KILLED

151

1.1
Location : upsertStudent
Killed by : edu.ucsb.cs156.organic.controllers.StudentsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.StudentsControllerTests]/[method:admin_can_upload_students_for_an_existing_course()]
removed call to edu/ucsb/cs156/organic/entities/Student::setEmail → KILLED

153

1.1
Location : upsertStudent
Killed by : edu.ucsb.cs156.organic.controllers.StudentsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.StudentsControllerTests]/[method:admin_can_upload_students_for_an_existing_course()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/StudentsController::upsertStudent → KILLED

156

1.1
Location : upsertStudent
Killed by : edu.ucsb.cs156.organic.controllers.StudentsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.StudentsControllerTests]/[method:admin_can_upload_students_for_an_existing_course()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/StudentsController::upsertStudent → KILLED

Active mutators

Tests examined


Report generated by PIT 1.7.3