Raymond's edits are mostly focused on file uploading and sending file previews to the frontend, which was a big mystery until he took it on.
API Method Additions/Changes
User Info
Role Determination
This is used to determine the person's relation to the assignment. If no relation is determined, a 400 error is returned.
HashMap<String, Object> assignmentData = new HashMap<>();
assignmentData.put("role", null);
// good ID, so continue to check relationship
for (ClassPeriod cp : classService.getClassPeriodsByAssignment(assignment)) {
for (Person student : cp.getStudents()) {
if (student.getEmail().equals(existingPerson.getEmail())) {
assignmentData.put("role", "student"); // person has teacher access to the assignment
}
}
for (Person leader : cp.getLeaders()) {
if (leader.getEmail().equals(existingPerson.getEmail())) {
assignmentData.put("role", "teacher"); // person has teacher access to the assignment
}
}
}
// handler for invalid user accessing data
if (assignmentData.get("role") == null) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
Role-Based Data Returning
We didn't want any additional student submission information to be sent unless the person was a teacher, so we did the following in the info fetching method:
// if the student is determined to have no relationship to the assignment, null indicates they cannot access w/ role
// manually adding to data to prevent student from getting submission access
HashMap<String, Object> assignmentDetails = new HashMap<>();
assignmentDetails.put("allowedFileTypes", assignment.getAllowedFileTypes());
assignmentDetails.put("allowedSubmissions", assignment.getAllowedSubmissions());
assignmentDetails.put("content", assignment.getContent());
assignmentDetails.put("dateDue", assignment.getDateDue());
assignmentDetails.put("id", assignment.getId());
assignmentDetails.put("name", assignment.getName());
assignmentDetails.put("points", assignment.getPoints());
if (assignmentData.get("role").equals("teacher")) {
assignmentDetails.put("submissions", assignment.getSubmissions());
// retrieving the list of ClassPeriod objects for the given assignment
List<ClassPeriod> classPeriods = classService.getClassPeriodsByAssignment(assignment);
// making a HashSet to store unique Person objects
Set<Person> uniqueStudents = new LinkedHashSet<>(); // SORT ALL THESE STUDENTS ALPHABETICALLY
// iterating through each ClassPeriod object
for (ClassPeriod classPeriod : classPeriods) {
Collection<Person> students = classPeriod.getStudents();
for (Person student : students) {
uniqueStudents.add(student);
}
}
// SORTING ALPHABETICALLY when added
assignmentDetails.put("allAssignees", sortLinkedHashSetByName(uniqueStudents));
// new set for unique submissions
Set<Person> uniqueSubmitters = new HashSet<>();
// iterating through each submission
for (AssignmentSubmission submission : assignment.getSubmissions()) {
uniqueSubmitters.add(submission.getSubmitter());
}
assignmentDetails.put("allSubmitters", uniqueSubmitters);
}
Assignment Grading
The method (shortened below) is used for grading. The actual grade change is made by a new AssignmentSubmissionDetailsService method that does exactly what the name describes, exactly as expected (get submission by ID, then use setter).
@PostMapping("/cookie/{id}/grading")
public ResponseEntity<?> getAssignmentSubmissionsWithCookie(@CookieValue("jwt") String jwtToken,
@PathVariable long id,
@RequestParam int score) {
// JWT and submission verification takes place here...
// then, teacher verification, shown below...
boolean isTeacher = false;
// good ID, so continue to check relationship
for (ClassPeriod cp : classService.getClassPeriodsByAssignment(assignmentDetailsService.getBySubmission(submission))) {
for (Person leader : cp.getLeaders()) {
if (leader.getEmail().equals(existingPerson.getEmail())) {
isTeacher = true; // person has teacher access to the assignment
}
}
}
// handler for invalid user accessing data
if (!(isTeacher)) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
// if determined to be teacher, the grade is updated
subDetailsService.scoreSubmission(submission.getId(), score);
Revised AssignmentSubmission Method
This method is a revision of Raymond's original /upload/ method that was used for more basic testing of file upload. Some basic fetching and verification of the submission are skipped for brevity, but notice the file upload aspects.
@PostMapping("/submit/{id}/{submissionTimeString}")
public ResponseEntity<Object> handleFileUpload(@CookieValue("jwt") String jwtToken,
@RequestPart("file") MultipartFile file,
@PathVariable long id,
@PathVariable String submissionTimeString) {
// VERIFICATION OF USER, ASSIGNMENT, ETC. SKIPPED HERE...
// blah blah blah...
// processing file upload if the user has been verified (RAYMOND CODE)
try {
//check if file type is null: edge case
String contentType = file.getContentType();
if (contentType == null) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("File content type is not supported");
}
String fileExtension = getFileExtension(file.getOriginalFilename());
if (!isValidFileType(submittedAssignment.getAllowedFileTypes(), fileExtension, contentType)) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("File type is not supported");
}
// Create the temporary upload directory if it doesn't exist
File tempDirectory = new File(tempUploadDir);
if (!tempDirectory.exists()) {
tempDirectory.mkdirs();
}
// Save the file to the temporary upload directory
String tempFilePath = tempUploadDir + File.separator + file.getOriginalFilename();
file.transferTo(new File(tempFilePath));
// Ensure unique file name in the final upload directory
String uniqueFileName = ensureUniqueFileName(uploadDir, file.getOriginalFilename());
// Move the file to the final destination
String finalFilePath = uploadDir + File.separator + uniqueFileName;
new File(tempFilePath).renameTo(new File(finalFilePath));
// saving the new assignment submission following (DREW CODE)
AssignmentSubmission submission = new AssignmentSubmission(existingPerson, finalFilePath, submissionTime, submissionNumber);
subDetailsService.save(submission); // saving the new submission
assignmentDetailsService.addSubmissionToAssignment(submittedAssignment, submission); // adding the submission to the assignment
assignmentDetailsService.save(submittedAssignment);
return new ResponseEntity<>("Submission to the assignment \"" + submittedAssignment.getName() + "\" was successful!", HttpStatus.CREATED);
// finished processing!!! wow!!!
} catch (IOException e) {
return ResponseEntity.status(500).body("Failed to upload file");
}
}
Prevention of Duplicate Files
In earlier renditions, if a file has the same name as another file, it would not be uploaded and the reference made in the database would be to another person's submission. This is a big error and even bigger privacy concern! Here's how Drew fixed it:
// Method to ensure unique file name
private String ensureUniqueFileName(String uploadDir, String originalFilename) {
File file = new File(uploadDir + File.separator + originalFilename);
String name = originalFilename;
String extension = "";
int dotIndex = originalFilename.lastIndexOf('.');
if (dotIndex != -1) {
name = originalFilename.substring(0, dotIndex);
extension = originalFilename.substring(dotIndex);
}
int counter = 0;
while (file.exists()) {
counter++;
String newName = name + " (" + counter + ")" + extension;
file = new File(uploadDir + File.separator + newName);
}
return file.getName();
}
Here's proof of it working:
Additional Notes
Local testing has been performed to confirm functionality
However, file uploading and viewing has not been tested on deployed yet (by necessity), but we will determine functionality through merge
All other remaining changes were made to the frontend
Prior Planning
(AJ's contributions were on the frontend using an existing post method, so they will not be discussed in this PR.)
Drew's Ticket
Focused on implementing specialized
Assignment
andAssignmentSubmission
methods for frontend data display and grading.Raymond's Ticket
Raymond's edits are mostly focused on file uploading and sending file previews to the frontend, which was a big mystery until he took it on.
API Method Additions/Changes
User Info
Role Determination
This is used to determine the person's relation to the assignment. If no relation is determined, a 400 error is returned.
Role-Based Data Returning
We didn't want any additional student submission information to be sent unless the person was a teacher, so we did the following in the info fetching method:
Assignment Grading
The method (shortened below) is used for grading. The actual grade change is made by a new
AssignmentSubmissionDetailsService
method that does exactly what the name describes, exactly as expected (get submission by ID, then use setter).Revised AssignmentSubmission Method
This method is a revision of Raymond's original /upload/ method that was used for more basic testing of file upload. Some basic fetching and verification of the submission are skipped for brevity, but notice the file upload aspects.
Prevention of Duplicate Files
In earlier renditions, if a file has the same name as another file, it would not be uploaded and the reference made in the database would be to another person's submission. This is a big error and even bigger privacy concern! Here's how Drew fixed it:
Here's proof of it working:
Additional Notes