PSCourseController.java
package edu.ucsb.cs156.courses.controllers;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.ucsb.cs156.courses.entities.PSCourse;
import edu.ucsb.cs156.courses.entities.PersonalSchedule;
import edu.ucsb.cs156.courses.entities.User;
import edu.ucsb.cs156.courses.errors.BadEnrollCdException;
import edu.ucsb.cs156.courses.errors.EntityNotFoundException;
import edu.ucsb.cs156.courses.models.CurrentUser;
import edu.ucsb.cs156.courses.repositories.PSCourseRepository;
import edu.ucsb.cs156.courses.repositories.PersonalScheduleRepository;
import edu.ucsb.cs156.courses.services.UCSBCurriculumService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Optional;
import javax.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "PSCourse")
@RequestMapping("/api/courses")
@RestController
@Slf4j
public class PSCourseController extends ApiController {
@Autowired PSCourseRepository coursesRepository;
@Autowired PersonalScheduleRepository personalScheduleRepository;
@Autowired UCSBCurriculumService ucsbCurriculumService;
@Autowired ObjectMapper mapper;
@Operation(summary = "List all courses (admin)")
@PreAuthorize("hasRole('ROLE_ADMIN')")
@GetMapping("/admin/all")
public Iterable<PSCourse> allUsersCourses() {
Iterable<PSCourse> courses = coursesRepository.findAll();
return courses;
}
@Operation(summary = "List all courses (user)")
@PreAuthorize("hasRole('ROLE_USER')")
@GetMapping("/user/all")
public Iterable<PSCourse> thisUsersCourses() {
CurrentUser currentUser = getCurrentUser();
Iterable<PSCourse> courses = coursesRepository.findAllByUserId(currentUser.getUser().getId());
return courses;
}
@Operation(summary = "List all courses for a specified psId (admin)")
@PreAuthorize("hasRole('ROLE_ADMIN')")
@GetMapping("/admin/psid/all")
public Iterable<PSCourse> allCoursesForPsId(@Parameter(name = "psId") @RequestParam Long psId) {
Iterable<PSCourse> courses = coursesRepository.findAllByPsId(psId);
return courses;
}
@Operation(summary = "List all courses for a specified psId (user)")
@PreAuthorize("hasRole('ROLE_USER')")
@GetMapping("/user/psid/all")
public Iterable<PSCourse> thisUsersCoursesForPsId(
@Parameter(name = "psId") @RequestParam Long psId) {
User currentUser = getCurrentUser().getUser();
Iterable<PSCourse> courses = coursesRepository.findAllByPsIdAndUser(psId, currentUser);
return courses;
}
@Operation(summary = "Get a single course (admin)")
@PreAuthorize("hasRole('ROLE_ADMIN')")
@GetMapping("/admin")
public PSCourse getCourseById_admin(@Parameter(name = "id") @RequestParam Long id) {
PSCourse courses =
coursesRepository
.findById(id)
.orElseThrow(() -> new EntityNotFoundException(PSCourse.class, id));
return courses;
}
@Operation(summary = "Get a single course (user)")
@PreAuthorize("hasRole('ROLE_USER')")
@GetMapping("/user")
public PSCourse getCourseById(@Parameter(name = "id") @RequestParam Long id) {
User currentUser = getCurrentUser().getUser();
PSCourse courses =
coursesRepository
.findByIdAndUser(id, currentUser)
.orElseThrow(() -> new EntityNotFoundException(PSCourse.class, id));
return courses;
}
@Operation(summary = "Create a new course")
@PreAuthorize("hasRole('ROLE_USER')")
@PostMapping("/post")
public ArrayList<PSCourse> postCourses(
@Parameter(name = "enrollCd") @RequestParam String enrollCd,
@Parameter(name = "psId") @RequestParam Long psId)
throws JsonProcessingException {
CurrentUser currentUser = getCurrentUser();
log.info("currentUser={}", currentUser);
PersonalSchedule checkPsId =
personalScheduleRepository
.findByIdAndUser(psId, currentUser.getUser())
.orElseThrow(() -> new EntityNotFoundException(PersonalSchedule.class, psId));
String body = ucsbCurriculumService.getAllSections(enrollCd, checkPsId.getQuarter());
if (body.equals("{\"error\": \"401: Unauthorized\"}")
|| body.equals("{\"error\": \"Enroll code doesn't exist in that quarter.\"}")) {
throw new BadEnrollCdException(enrollCd);
}
String enrollCdPrimary = null;
boolean hasSecondary = false;
Iterator<JsonNode> it = mapper.readTree(body).path("classSections").elements();
while (it.hasNext()) {
JsonNode classSection = it.next();
String section = classSection.path("section").asText();
if (section.endsWith("00")) {
String currentEnrollCd = classSection.path("enrollCode").asText();
enrollCdPrimary = currentEnrollCd;
if (hasSecondary) break;
} else {
hasSecondary = true;
}
}
if (enrollCdPrimary == null) {
enrollCdPrimary = enrollCd;
hasSecondary = false;
}
if (coursesRepository.findByPsIdAndEnrollCd(psId, enrollCdPrimary).isPresent()) {
throw new IllegalArgumentException("class exists in schedule");
}
ArrayList<PSCourse> savedCourses = new ArrayList<>();
if (!enrollCdPrimary.equals(enrollCd)) {
String enrollCdSecondary = enrollCd;
PSCourse secondary = new PSCourse();
secondary.setUser(currentUser.getUser());
secondary.setEnrollCd(enrollCdSecondary);
secondary.setPsId(psId);
PSCourse savedSecondary = coursesRepository.save(secondary);
savedCourses.add(savedSecondary);
} else if (hasSecondary) {
throw new IllegalArgumentException(
enrollCd
+ " is for a course with sections; please add a specific section and the lecture will be automatically added");
}
PSCourse primary = new PSCourse();
primary.setUser(currentUser.getUser());
primary.setEnrollCd(enrollCdPrimary);
primary.setPsId(psId);
PSCourse savedPrimary = coursesRepository.save(primary);
savedCourses.add(savedPrimary);
return savedCourses;
}
@Operation(summary = "Delete a course (admin)")
@PreAuthorize("hasRole('ROLE_ADMIN')")
@DeleteMapping("/admin")
public Object deleteCourses_Admin(@Parameter(name = "id") @RequestParam Long id) {
PSCourse courses =
coursesRepository
.findById(id)
.orElseThrow(() -> new EntityNotFoundException(PSCourse.class, id));
coursesRepository.delete(courses);
return genericMessage("PSCourse with id %s deleted".formatted(id));
}
@Operation(summary = "Delete a course (user)")
@PreAuthorize("hasRole('ROLE_USER')")
@DeleteMapping("/user")
public Object deleteCourses(@Parameter(name = "id") @RequestParam Long id)
throws JsonProcessingException {
User currentUser = getCurrentUser().getUser();
PSCourse psCourse =
coursesRepository
.findByIdAndUser(id, currentUser)
.orElseThrow(() -> new EntityNotFoundException(PSCourse.class, id));
long psId = psCourse.getPsId();
PersonalSchedule checkPsId =
personalScheduleRepository
.findByIdAndUser(psId, currentUser)
.orElseThrow(() -> new EntityNotFoundException(PersonalSchedule.class, psId));
String body =
ucsbCurriculumService.getAllSections(psCourse.getEnrollCd(), checkPsId.getQuarter());
if (body.equals("{\"error\": \"401: Unauthorized\"}")
|| body.equals("{\"error\": \"Enroll code doesn't exist in that quarter.\"}")) {
coursesRepository.delete(psCourse);
return genericMessage("PSCourse with id %s deleted".formatted(id));
}
Iterator<JsonNode> it = mapper.readTree(body).path("classSections").elements();
Optional<Long> primaryId = Optional.empty();
Optional<Long> secondaryId = Optional.empty();
while (it.hasNext()) {
JsonNode classSection = it.next();
String section = classSection.path("section").asText();
String currentEnrollCd = classSection.path("enrollCode").asText();
Optional<PSCourse> currentPsCourse =
coursesRepository.findByPsIdAndEnrollCd(psId, currentEnrollCd);
if (!currentPsCourse.isPresent()) continue;
Optional<Long> idOpt = Optional.of(currentPsCourse.get().getId());
if (section.endsWith("00")) primaryId = idOpt;
else secondaryId = idOpt;
coursesRepository.delete(currentPsCourse.get());
}
if (primaryId.isPresent() && secondaryId.isPresent()) {
if (primaryId.get() == id)
return genericMessage(
"PSCourse with id %s and matching secondary with id %s deleted"
.formatted(id, secondaryId.get()));
else
return genericMessage(
"PSCourse with id %s and matching primary with id %s deleted"
.formatted(id, primaryId.get()));
}
return genericMessage("PSCourse with id %s deleted".formatted(id));
}
@Operation(summary = "Delete a course with psid and enroll code (user)")
@PreAuthorize("hasRole('ROLE_USER')")
@DeleteMapping("/user/psid")
public Object deleteCourses_PSID(
@Parameter(name = "enrollCd") @RequestParam String enrollCd,
@Parameter(name = "psId") @RequestParam Long psId)
throws JsonProcessingException {
User currentUser = getCurrentUser().getUser();
PSCourse psCourse =
coursesRepository
.findByPsIdAndEnrollCd(psId, enrollCd)
.orElseThrow(
() ->
new EntityNotFoundException(
PSCourse.class, "psId", psId, "enrollCode", enrollCd));
long id = psCourse.getId();
PersonalSchedule checkPsId =
personalScheduleRepository
.findByIdAndUser(psId, currentUser)
.orElseThrow(() -> new EntityNotFoundException(PersonalSchedule.class, psId));
String body =
ucsbCurriculumService.getAllSections(psCourse.getEnrollCd(), checkPsId.getQuarter());
if (body.equals("{\"error\": \"401: Unauthorized\"}")
|| body.equals("{\"error\": \"Enroll code doesn't exist in that quarter.\"}")) {
coursesRepository.delete(psCourse);
return genericMessage(
"PSCourse with psId %s and enroll code %s deleted".formatted(psId, enrollCd));
}
Iterator<JsonNode> it = mapper.readTree(body).path("classSections").elements();
Optional<Long> primaryId = Optional.empty();
Optional<Long> secondaryId = Optional.empty();
while (it.hasNext()) {
JsonNode classSection = it.next();
String section = classSection.path("section").asText();
String currentEnrollCd = classSection.path("enrollCode").asText();
Optional<PSCourse> currentPsCourse =
coursesRepository.findByPsIdAndEnrollCd(psId, currentEnrollCd);
if (!currentPsCourse.isPresent()) continue;
Optional<Long> idOpt = Optional.of(currentPsCourse.get().getId());
if (section.endsWith("00")) primaryId = idOpt;
else secondaryId = idOpt;
coursesRepository.delete(currentPsCourse.get());
}
if (primaryId.isPresent() && secondaryId.isPresent()) {
if (primaryId.get() == id)
return genericMessage(
"PSCourse with id %s and matching secondary with id %s deleted"
.formatted(id, secondaryId.get()));
else
return genericMessage(
"PSCourse with id %s and matching primary with id %s deleted"
.formatted(id, primaryId.get()));
}
return genericMessage(
"PSCourse with psId %s and enroll code %s deleted".formatted(psId, enrollCd));
}
@Operation(summary = "Update a single Course (admin)")
@PreAuthorize("hasRole('ROLE_ADMIN')")
@PutMapping("/admin")
public PSCourse putCourseById_admin(
@Parameter(name = "id") @RequestParam Long id, @RequestBody @Valid PSCourse incomingCourses) {
PSCourse courses =
coursesRepository
.findById(id)
.orElseThrow(() -> new EntityNotFoundException(PSCourse.class, id));
courses.setEnrollCd(incomingCourses.getEnrollCd());
courses.setPsId(incomingCourses.getPsId());
coursesRepository.save(courses);
return courses;
}
@Operation(summary = "Update a single course (user)")
@PreAuthorize("hasRole('ROLE_USER')")
@PutMapping("/user")
public PSCourse putCoursesById(
@Parameter(name = "id") @RequestParam Long id, @RequestBody @Valid PSCourse incomingCourses) {
User currentUser = getCurrentUser().getUser();
PSCourse courses =
coursesRepository
.findByIdAndUser(id, currentUser)
.orElseThrow(() -> new EntityNotFoundException(PSCourse.class, id));
courses.setEnrollCd(incomingCourses.getEnrollCd());
courses.setPsId(incomingCourses.getPsId());
coursesRepository.save(courses);
return courses;
}
}