diff --git a/src/main/java/com/testify/Testify_Backend/controller/ExamSetterController.java b/src/main/java/com/testify/Testify_Backend/controller/ExamSetterController.java index 4d364ca..676feea 100644 --- a/src/main/java/com/testify/Testify_Backend/controller/ExamSetterController.java +++ b/src/main/java/com/testify/Testify_Backend/controller/ExamSetterController.java @@ -1,6 +1,10 @@ package com.testify.Testify_Backend.controller; import com.testify.Testify_Backend.responses.GenericAddOrUpdateResponse; +import com.testify.Testify_Backend.responses.exam_management.CandidateResponse; +import com.testify.Testify_Backend.responses.exam_management.ExamResponse; +import com.testify.Testify_Backend.responses.exam_management.OrganizationResponse; +import com.testify.Testify_Backend.service.ExamManagementService; import com.testify.Testify_Backend.responses.exam_management.ExamResponse; import com.testify.Testify_Backend.responses.exam_management.OrganizationResponse; import com.testify.Testify_Backend.responses.examsetter_management.ModerateExamResponse; @@ -20,6 +24,7 @@ public class ExamSetterController { private static final Logger log = LoggerFactory.getLogger(ExamSetterController.class); private final ExamSetterService examSetterService; + private final ExamManagementService examManagementService; @GetMapping("/{setterId}/getOrganizations") public ResponseEntity> getOrganizations(@PathVariable("setterId") long setterId) { @@ -40,6 +45,17 @@ public ResponseEntity addSetterToOrganization(@PathV return ResponseEntity.ok(response); } + @GetMapping("/proctor/{proctorId}/{organizationId}") + public ResponseEntity> getExamsForProctor(@PathVariable Long proctorId, @PathVariable Long organizationId) { + List exams = examSetterService.getExamsForProctor(proctorId, organizationId); + return ResponseEntity.ok(exams); + } + + @GetMapping("/{examId}/candidates") + public ResponseEntity> getCandidatesForExam(@PathVariable Long examId) { + Set candidates = examSetterService.getCandidatesForExam(examId); + return ResponseEntity.ok(candidates); + } @GetMapping("/{examSetterId}/moderating-exams") public ResponseEntity> getModeratingExams(@PathVariable long examSetterId) { List responses = examSetterService.getModeratingExams(examSetterId); @@ -52,4 +68,10 @@ public ResponseEntity> getModeratingExams(@PathVariab return ResponseEntity.ok(responses); } + @PostMapping("/{candidateId}/{examId}/proctorComments") + public ResponseEntity addComment(@PathVariable Long candidateId, @PathVariable Long examId, @RequestBody String content) { + examSetterService.addCommentToCandidate(candidateId, examId, content); + return ResponseEntity.ok("Comment added successfully"); + } + } diff --git a/src/main/java/com/testify/Testify_Backend/model/Candidate.java b/src/main/java/com/testify/Testify_Backend/model/Candidate.java index d0fe041..e8f73f1 100644 --- a/src/main/java/com/testify/Testify_Backend/model/Candidate.java +++ b/src/main/java/com/testify/Testify_Backend/model/Candidate.java @@ -7,6 +7,7 @@ import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; +import java.util.List; import java.util.Set; @Entity @@ -26,4 +27,7 @@ public class Candidate extends User{ @ManyToMany(mappedBy = "candidates") private Set exams; + @OneToMany(mappedBy = "candidate", cascade = CascadeType.ALL, orphanRemoval = true) + private List comments; + } diff --git a/src/main/java/com/testify/Testify_Backend/model/Exam.java b/src/main/java/com/testify/Testify_Backend/model/Exam.java index 5a8c71e..5f55f4e 100644 --- a/src/main/java/com/testify/Testify_Backend/model/Exam.java +++ b/src/main/java/com/testify/Testify_Backend/model/Exam.java @@ -116,4 +116,7 @@ public class Exam { @Column(nullable = false) private boolean hosted = false; + @OneToMany(mappedBy = "exam", cascade = CascadeType.ALL, orphanRemoval = true) + private List proctorComments; + } diff --git a/src/main/java/com/testify/Testify_Backend/model/ProctorComment.java b/src/main/java/com/testify/Testify_Backend/model/ProctorComment.java new file mode 100644 index 0000000..487eddd --- /dev/null +++ b/src/main/java/com/testify/Testify_Backend/model/ProctorComment.java @@ -0,0 +1,32 @@ +package com.testify.Testify_Backend.model; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Entity +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class ProctorComment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "candidate_id", nullable = false) + private Candidate candidate; + + @ManyToOne + @JoinColumn(name = "exam_id", nullable = false) + private Exam exam; + + private String content; + + private LocalDateTime createdAt; +} diff --git a/src/main/java/com/testify/Testify_Backend/repository/CandidateRepository.java b/src/main/java/com/testify/Testify_Backend/repository/CandidateRepository.java index 2c59e61..e96dc14 100644 --- a/src/main/java/com/testify/Testify_Backend/repository/CandidateRepository.java +++ b/src/main/java/com/testify/Testify_Backend/repository/CandidateRepository.java @@ -3,6 +3,7 @@ import com.testify.Testify_Backend.model.Candidate; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import java.time.LocalDateTime; @@ -22,5 +23,8 @@ public interface CandidateRepository extends JpaRepository { List findCandidatesAssignedToExamWithConflictingExams(Long examId, LocalDateTime startDatetime, LocalDateTime endDatetime); boolean existsByEmail(String currentUserEmail); + + @Query("SELECT c FROM Candidate c JOIN c.exams e WHERE e.id = :examId") + Set findByExamId(@Param("examId") Long examId); } diff --git a/src/main/java/com/testify/Testify_Backend/repository/ExamRepository.java b/src/main/java/com/testify/Testify_Backend/repository/ExamRepository.java index 4ac3a88..d8139f6 100644 --- a/src/main/java/com/testify/Testify_Backend/repository/ExamRepository.java +++ b/src/main/java/com/testify/Testify_Backend/repository/ExamRepository.java @@ -32,6 +32,10 @@ public interface ExamRepository extends JpaRepository { @Query("SELECT e FROM Exam e WHERE e.isPrivate = false") List findAllPublicExams(); + @Query("SELECT e FROM Exam e JOIN e.proctors p WHERE p.id = :proctorId AND e.organization.id = :organizationId") + List findByProctorIdAndOrganizationId(@Param("proctorId") Long proctorId, @Param("organizationId") Long organizationId); + + List findByModeratorId(long moderatorId); } diff --git a/src/main/java/com/testify/Testify_Backend/repository/ProctorCommentRepository.java b/src/main/java/com/testify/Testify_Backend/repository/ProctorCommentRepository.java new file mode 100644 index 0000000..1c9b174 --- /dev/null +++ b/src/main/java/com/testify/Testify_Backend/repository/ProctorCommentRepository.java @@ -0,0 +1,8 @@ +package com.testify.Testify_Backend.repository; + +import com.testify.Testify_Backend.model.ProctorComment; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ProctorCommentRepository extends JpaRepository { + +} diff --git a/src/main/java/com/testify/Testify_Backend/service/ExamSetterService.java b/src/main/java/com/testify/Testify_Backend/service/ExamSetterService.java index 9254b42..343963f 100644 --- a/src/main/java/com/testify/Testify_Backend/service/ExamSetterService.java +++ b/src/main/java/com/testify/Testify_Backend/service/ExamSetterService.java @@ -1,6 +1,8 @@ package com.testify.Testify_Backend.service; import com.testify.Testify_Backend.responses.GenericAddOrUpdateResponse; +import com.testify.Testify_Backend.responses.exam_management.CandidateResponse; +import com.testify.Testify_Backend.responses.exam_management.ExamResponse; import com.testify.Testify_Backend.responses.exam_management.OrganizationResponse; import com.testify.Testify_Backend.responses.examsetter_management.ModerateExamResponse; @@ -14,5 +16,10 @@ public interface ExamSetterService { GenericAddOrUpdateResponse addSetterToOrganization(String token); + List getExamsForProctor(Long proctorId, Long organizationId); + + Set getCandidatesForExam(Long examId); List getModeratingExams(long examSetterId); + + void addCommentToCandidate(Long candidateId, Long examId, String content); } diff --git a/src/main/java/com/testify/Testify_Backend/service/ExamSetterServiceImpl.java b/src/main/java/com/testify/Testify_Backend/service/ExamSetterServiceImpl.java index 01a2003..090c8b1 100644 --- a/src/main/java/com/testify/Testify_Backend/service/ExamSetterServiceImpl.java +++ b/src/main/java/com/testify/Testify_Backend/service/ExamSetterServiceImpl.java @@ -1,5 +1,12 @@ package com.testify.Testify_Backend.service; +import com.testify.Testify_Backend.model.*; +import com.testify.Testify_Backend.repository.*; +import com.testify.Testify_Backend.responses.GenericAddOrUpdateResponse; +import com.testify.Testify_Backend.responses.exam_management.CandidateResponse; +import com.testify.Testify_Backend.responses.exam_management.ExamResponse; +import com.testify.Testify_Backend.responses.exam_management.OrganizationResponse; +import jakarta.transaction.Transactional; import com.testify.Testify_Backend.model.ExamSetter; import com.testify.Testify_Backend.model.ExamSetterInvitation; import com.testify.Testify_Backend.model.Organization; @@ -17,6 +24,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import java.time.LocalDateTime; +import java.util.*; import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -30,6 +39,8 @@ public class ExamSetterServiceImpl implements ExamSetterService { private final ExamSetterInvitationRepository examSetterInvitationRepository; private final OrganizationRepository organizationRepository; private final ExamRepository examRepository; + private final CandidateRepository candidateRepository; + private final ProctorCommentRepository proctorCommentRepository; @Autowired private ModelMapper modelMapper; @@ -77,6 +88,28 @@ public GenericAddOrUpdateResponse addSetterToOrganization(String token) { return response; } + @Override + @Transactional + public List getExamsForProctor(Long proctorId, Long organizationId) { + List exams = examRepository.findByProctorIdAndOrganizationId(proctorId, organizationId); // Ensure repository method returns a List + List examResponses = new ArrayList<>(); + for (Exam exam : exams) { + examResponses.add(modelMapper.map(exam, ExamResponse.class)); + } + return examResponses; + } + + + @Override + public Set getCandidatesForExam(Long examId) { + Set candidates = candidateRepository.findByExamId(examId); + Set candidateResponses = new HashSet<>(); + for (Candidate candidate : candidates) { + candidateResponses.add(modelMapper.map(candidate, CandidateResponse.class)); + } + + return candidateResponses; + } public List getModeratingExams(long examSetterId) { return examRepository.findByModeratorId(examSetterId).stream() .map(exam -> new ModerateExamResponse( @@ -87,4 +120,19 @@ public List getModeratingExams(long examSetterId) { )) .collect(Collectors.toList()); } + + @Override + public void addCommentToCandidate(Long candidateId, Long examId, String content) { + Candidate candidate = candidateRepository.findById(candidateId) + .orElseThrow(() -> new RuntimeException("Candidate not found")); + Exam exam = examRepository.findById(examId).orElseThrow(() -> new RuntimeException("Exam not found")); + + ProctorComment comment = new ProctorComment(); + comment.setCandidate(candidate); + comment.setExam(exam); + comment.setContent(content); + comment.setCreatedAt(LocalDateTime.now()); + + proctorCommentRepository.save(comment); + } }