summaryrefslogtreecommitdiff
path: root/src/main/java/com
diff options
context:
space:
mode:
authorSzymon Szukalski <szymon@skas.io>2024-07-25 20:36:11 +1000
committerSzymon Szukalski <szymon@skas.io>2024-07-25 20:36:11 +1000
commitaa9bdd514ab90d0da0391b879255a22c29450e9a (patch)
tree9ddd1de0ab7e376ead06f55bdb32a6190d3647d5 /src/main/java/com
parentec81d98e90f9fdb4dd12138a365fbbbb3a8efa5f (diff)
Validate import payload and return create/update stats
- Add validation to /import payload - Move import logic to service bean - Track whether entities have been created or update - Report number of created and updated entities as return value for the import endpoint - Add some test coverage to exercise the validators
Diffstat (limited to 'src/main/java/com')
-rw-r--r--src/main/java/com/stileeducation/markr/controller/TestResultsController.java129
-rw-r--r--src/main/java/com/stileeducation/markr/dto/AggregateResponseDTO.java (renamed from src/main/java/com/stileeducation/markr/dto/AggregatedTestResultsDTO.java)4
-rw-r--r--src/main/java/com/stileeducation/markr/dto/ImportResponseDTO.java115
-rw-r--r--src/main/java/com/stileeducation/markr/dto/MCQTestResultDTO.java15
-rw-r--r--src/main/java/com/stileeducation/markr/dto/MCQTestResultsDTO.java2
-rw-r--r--src/main/java/com/stileeducation/markr/dto/SummaryMarksDTO.java12
-rw-r--r--src/main/java/com/stileeducation/markr/entity/Student.java192
-rw-r--r--src/main/java/com/stileeducation/markr/entity/Test.java166
-rw-r--r--src/main/java/com/stileeducation/markr/entity/TestResult.java187
-rw-r--r--src/main/java/com/stileeducation/markr/service/StudentService.java1
-rw-r--r--src/main/java/com/stileeducation/markr/service/TestResultsService.java94
-rw-r--r--src/main/java/com/stileeducation/markr/service/TestService.java12
12 files changed, 611 insertions, 318 deletions
diff --git a/src/main/java/com/stileeducation/markr/controller/TestResultsController.java b/src/main/java/com/stileeducation/markr/controller/TestResultsController.java
index 1376ecb..f5b6070 100644
--- a/src/main/java/com/stileeducation/markr/controller/TestResultsController.java
+++ b/src/main/java/com/stileeducation/markr/controller/TestResultsController.java
@@ -1,11 +1,8 @@
package com.stileeducation.markr.controller;
-import com.stileeducation.markr.dto.AggregatedTestResultsDTO;
-import com.stileeducation.markr.dto.MCQTestResultDTO;
+import com.stileeducation.markr.dto.AggregateResponseDTO;
+import com.stileeducation.markr.dto.ImportResponseDTO;
import com.stileeducation.markr.dto.MCQTestResultsDTO;
-import com.stileeducation.markr.entity.Student;
-import com.stileeducation.markr.entity.Test;
-import com.stileeducation.markr.entity.TestResult;
import com.stileeducation.markr.repository.TestRepository;
import com.stileeducation.markr.repository.TestResultRepository;
import com.stileeducation.markr.service.StudentService;
@@ -14,81 +11,77 @@ import com.stileeducation.markr.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/")
public class TestResultsController {
- public static final String IMPORT_ENDPOINT = "/import";
- public static final String AGGREGATE_ENDPOINT = "/results/{test-id}/aggregate";
+ public static final String IMPORT_ENDPOINT = "/import";
+ public static final String AGGREGATE_ENDPOINT = "/results/{test-id}/aggregate";
- @Autowired
- private StudentService studentService;
+ @Autowired
+ private StudentService studentService;
- @Autowired
- private TestService testService;
+ @Autowired
+ private TestService testService;
- @Autowired
- private TestResultsService testResultsService;
+ @Autowired
+ private TestResultsService testResultsService;
- @Autowired
- private TestRepository testRepository;
+ @Autowired
+ private TestRepository testRepository;
- @Autowired
- private TestResultRepository testResultRepository;
+ @Autowired
+ private TestResultRepository testResultRepository;
- public TestResultsController(TestResultsService testResultsService) {
- this.testResultsService = testResultsService;
- }
-
- // TODO consider return value
- @PostMapping(
- value = IMPORT_ENDPOINT,
- consumes = "text/xml+markr",
- produces = "application/json")
- public ResponseEntity<Void> handleXmlRequest(@RequestBody MCQTestResultsDTO testResults) {
-
- for (MCQTestResultDTO mcqTestResult : testResults.getMcqTestResults()) {
- Student student = studentService
- .findOrCreateStudent(
- mcqTestResult.getFirstName(),
- mcqTestResult.getLastName(),
- mcqTestResult.getStudentNumber());
-
- Test test = testService
- .findOrCreateTest(
- mcqTestResult.getTestId(),
- mcqTestResult.getSummaryMarks().getAvailable());
-
- if (test.getMarksAvailable() < mcqTestResult.getSummaryMarks().getAvailable()) {
- test.setMarksAvailable(mcqTestResult.getSummaryMarks().getAvailable());
- testRepository.save(test);
- }
-
- // Some edge cases to consider
- // obtained is higher than available (assumption?)
+ public TestResultsController(TestResultsService testResultsService) {
+ this.testResultsService = testResultsService;
+ }
- TestResult testResult = testResultsService
- .findOrCreateTestResult(
- student.getId(),
- test.getId(),
- mcqTestResult.getSummaryMarks().getObtained());
-
- if (testResult.getMarksAwarded() < mcqTestResult.getSummaryMarks().getObtained()) {
- testResult.setMarksAwarded(mcqTestResult.getSummaryMarks().getObtained());
- testResultRepository.save(testResult);
- }
- }
-
- return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+ @PostMapping(
+ value = IMPORT_ENDPOINT,
+ consumes = "text/xml+markr",
+ produces = "application/json")
+ public ResponseEntity<ImportResponseDTO> postTestResults(@Validated @RequestBody MCQTestResultsDTO testResults) {
+ ImportResponseDTO response = testResultsService.processTestResults(testResults);
+ if ("failure".equals(response.getStatus())) {
+ return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
-
- @GetMapping(
- value = AGGREGATE_ENDPOINT,
- produces = "application/json")
- public AggregatedTestResultsDTO getAggregatedResults(@PathVariable("test-id") String testId) {
- return testResultsService.aggregateTestResults(testId);
- }
-
+ return new ResponseEntity<>(response, HttpStatus.OK);
+ }
+
+ @GetMapping(
+ value = AGGREGATE_ENDPOINT,
+ produces = "application/json")
+ public AggregateResponseDTO getAggregatedResults(@PathVariable("test-id") String testId) {
+ return testResultsService.aggregateTestResults(testId);
+ }
+
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ public ResponseEntity<ImportResponseDTO> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
+ ImportResponseDTO response = new ImportResponseDTO();
+ response.setStatus("error");
+ response.setMessage("Invalid payload");
+ return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
+ }
+
+ @ExceptionHandler(HttpMessageNotReadableException.class)
+ public ResponseEntity<ImportResponseDTO> handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) {
+ ImportResponseDTO response = new ImportResponseDTO();
+ response.setStatus("error");
+ response.setMessage("Invalid XML payload");
+ return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
+ }
+
+ @ExceptionHandler(Exception.class)
+ public ResponseEntity<ImportResponseDTO> handleGenericException(Exception ex) {
+ ImportResponseDTO response = new ImportResponseDTO();
+ response.setStatus("error");
+ response.setMessage("An unexpected error occurred");
+ return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
+ }
}
diff --git a/src/main/java/com/stileeducation/markr/dto/AggregatedTestResultsDTO.java b/src/main/java/com/stileeducation/markr/dto/AggregateResponseDTO.java
index f5970c3..7232174 100644
--- a/src/main/java/com/stileeducation/markr/dto/AggregatedTestResultsDTO.java
+++ b/src/main/java/com/stileeducation/markr/dto/AggregateResponseDTO.java
@@ -5,7 +5,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.Objects;
@JsonInclude(JsonInclude.Include.NON_NULL)
-public class AggregatedTestResultsDTO {
+public class AggregateResponseDTO {
private double mean;
private double stddev;
@@ -85,7 +85,7 @@ public class AggregatedTestResultsDTO {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- AggregatedTestResultsDTO that = (AggregatedTestResultsDTO) o;
+ AggregateResponseDTO that = (AggregateResponseDTO) o;
return Double.compare(mean, that.mean) == 0
&& Double.compare(stddev, that.stddev) == 0
&& Double.compare(min, that.min) == 0
diff --git a/src/main/java/com/stileeducation/markr/dto/ImportResponseDTO.java b/src/main/java/com/stileeducation/markr/dto/ImportResponseDTO.java
new file mode 100644
index 0000000..8613865
--- /dev/null
+++ b/src/main/java/com/stileeducation/markr/dto/ImportResponseDTO.java
@@ -0,0 +1,115 @@
+package com.stileeducation.markr.dto;
+
+public class ImportResponseDTO {
+
+ private String status;
+ private String message;
+ private ImportData data;
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public ImportData getData() {
+ return data;
+ }
+
+ public void setData(ImportData data) {
+ this.data = data;
+ }
+
+ public static class ImportData {
+ private int studentsCreated = 0;
+ private int studentsUpdated = 0;
+
+ private int testsCreated = 0;
+ private int testsUpdated = 0;
+
+ private int testResultsCreated = 0;
+ private int testResultsUpdated = 0;
+
+ public int getStudentsCreated() {
+ return studentsCreated;
+ }
+
+ public void setStudentsCreated(int studentsCreated) {
+ this.studentsCreated = studentsCreated;
+ }
+
+ public int getStudentsUpdated() {
+ return studentsUpdated;
+ }
+
+ public void setStudentsUpdated(int studentsUpdated) {
+ this.studentsUpdated = studentsUpdated;
+ }
+
+ public int getTestsCreated() {
+ return testsCreated;
+ }
+
+ public void setTestsCreated(int testsCreated) {
+ this.testsCreated = testsCreated;
+ }
+
+ public int getTestsUpdated() {
+ return testsUpdated;
+ }
+
+ public void setTestsUpdated(int testsUpdated) {
+ this.testsUpdated = testsUpdated;
+ }
+
+ public int getTestResultsCreated() {
+ return testResultsCreated;
+ }
+
+ public void setTestResultsCreated(int testResultsCreated) {
+ this.testResultsCreated = testResultsCreated;
+ }
+
+ public int getTestResultsUpdated() {
+ return testResultsUpdated;
+ }
+
+ public void setTestResultsUpdated(int testResultsUpdated) {
+ this.testResultsUpdated = testResultsUpdated;
+ }
+
+ public void incrementStudentsCreated() {
+ this.studentsCreated++;
+ }
+
+ public void incrementStudentsUpdated() {
+ this.studentsUpdated++;
+ }
+
+ public void incrementTestsCreated() {
+ this.testsCreated++;
+ }
+
+ public void incrementTestsUpdated() {
+ this.testsUpdated++;
+ }
+
+ public void incrementTestResultsCreated() {
+ this.testResultsCreated++;
+ }
+
+ public void incrementTestResultsUpdated() {
+ this.testResultsUpdated++;
+ }
+ }
+}
diff --git a/src/main/java/com/stileeducation/markr/dto/MCQTestResultDTO.java b/src/main/java/com/stileeducation/markr/dto/MCQTestResultDTO.java
index b227fe3..da6a0e1 100644
--- a/src/main/java/com/stileeducation/markr/dto/MCQTestResultDTO.java
+++ b/src/main/java/com/stileeducation/markr/dto/MCQTestResultDTO.java
@@ -1,5 +1,8 @@
package com.stileeducation.markr.dto;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
import jakarta.xml.bind.annotation.XmlAttribute;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
@@ -9,11 +12,23 @@ import java.util.Objects;
@XmlRootElement(name = "mcq-test-result")
public class MCQTestResultDTO {
+ @NotBlank
private String scannedOn;
+
+ @NotBlank(message = "First name is mandatory")
private String firstName;
+
+ @NotBlank(message = "Last name is mandatory")
private String lastName;
+
+ @NotBlank(message = "Last name is mandatory")
private String studentNumber;
+
+ @NotBlank(message = "Test id is mandatory")
private String testId;
+
+ @Valid
+ @NotNull
private SummaryMarksDTO summaryMarks;
@XmlAttribute(name = "scanned-on")
diff --git a/src/main/java/com/stileeducation/markr/dto/MCQTestResultsDTO.java b/src/main/java/com/stileeducation/markr/dto/MCQTestResultsDTO.java
index e9ee8a7..fb77125 100644
--- a/src/main/java/com/stileeducation/markr/dto/MCQTestResultsDTO.java
+++ b/src/main/java/com/stileeducation/markr/dto/MCQTestResultsDTO.java
@@ -1,5 +1,6 @@
package com.stileeducation.markr.dto;
+import jakarta.validation.Valid;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
@@ -9,6 +10,7 @@ import java.util.Objects;
@XmlRootElement(name = "mcq-test-results")
public class MCQTestResultsDTO {
+ @Valid
private List<MCQTestResultDTO> mcqTestResults;
@XmlElement(name = "mcq-test-result")
diff --git a/src/main/java/com/stileeducation/markr/dto/SummaryMarksDTO.java b/src/main/java/com/stileeducation/markr/dto/SummaryMarksDTO.java
index a67d19c..788eea0 100644
--- a/src/main/java/com/stileeducation/markr/dto/SummaryMarksDTO.java
+++ b/src/main/java/com/stileeducation/markr/dto/SummaryMarksDTO.java
@@ -1,5 +1,7 @@
package com.stileeducation.markr.dto;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotNull;
import jakarta.xml.bind.annotation.XmlAttribute;
import jakarta.xml.bind.annotation.XmlRootElement;
@@ -7,8 +9,14 @@ import java.util.Objects;
@XmlRootElement(name = "summary-marks")
public class SummaryMarksDTO {
- private int available;
- private int obtained;
+
+ @NotNull(message = "Available marks must not be null")
+ @Min(value = 0, message = "Available marks must be non-negative")
+ private Integer available;
+
+ @NotNull(message = "Obtained marks must not be null")
+ @Min(value = 0, message = "Obtained marks must be non-negative")
+ private Integer obtained;
@XmlAttribute(name = "available")
public int getAvailable() {
diff --git a/src/main/java/com/stileeducation/markr/entity/Student.java b/src/main/java/com/stileeducation/markr/entity/Student.java
index 4e69eab..30a4c2b 100644
--- a/src/main/java/com/stileeducation/markr/entity/Student.java
+++ b/src/main/java/com/stileeducation/markr/entity/Student.java
@@ -10,87 +10,113 @@ import java.util.Set;
@Table(name = "students")
public class Student {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
-
- @Column(name = "first_name", nullable = false)
- private String firstName;
-
- @Column(name = "last_name", nullable = false)
- private String lastName;
-
- @Column(name = "student_number", nullable = false, unique = true)
- private String studentNumber;
-
- @OneToMany(mappedBy = "student", cascade = CascadeType.ALL, orphanRemoval = true)
- private Set<TestResult> testResults = new HashSet<>();
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public String getFirstName() {
- return firstName;
- }
-
- public void setFirstName(String firstName) {
- this.firstName = firstName;
- }
-
- public String getLastName() {
- return lastName;
- }
-
- public void setLastName(String lastName) {
- this.lastName = lastName;
- }
-
- public String getStudentNumber() {
- return studentNumber;
- }
-
- public void setStudentNumber(String studentNumber) {
- this.studentNumber = studentNumber;
- }
-
- public Set<TestResult> getTestResults() {
- return testResults;
- }
-
- public void setTestResults(Set<TestResult> testResults) {
- this.testResults = testResults;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Student student = (Student) o;
- return Objects.equals(id, student.id) &&
- Objects.equals(firstName, student.firstName) &&
- Objects.equals(lastName, student.lastName) &&
- Objects.equals(studentNumber, student.studentNumber) &&
- Objects.equals(testResults, student.testResults);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(id, firstName, lastName, studentNumber, testResults);
- }
-
- @Override
- public String toString() {
- return "Student{" +
- "id=" + id +
- ", firstName='" + firstName + '\'' +
- ", lastName='" + lastName + '\'' +
- ", studentNumber='" + studentNumber + '\'' +
- ", testResults=" + testResults +
- '}';
- }
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "first_name", nullable = false)
+ private String firstName;
+
+ @Column(name = "last_name", nullable = false)
+ private String lastName;
+
+ @Column(name = "student_number", nullable = false, unique = true)
+ private String studentNumber;
+
+ @OneToMany(mappedBy = "student", cascade = CascadeType.ALL, orphanRemoval = true)
+ private Set<TestResult> testResults = new HashSet<>();
+
+ @Transient
+ private boolean created = false;
+
+ @Transient
+ private boolean updated = false;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public String getStudentNumber() {
+ return studentNumber;
+ }
+
+ public void setStudentNumber(String studentNumber) {
+ this.studentNumber = studentNumber;
+ }
+
+ public Set<TestResult> getTestResults() {
+ return testResults;
+ }
+
+ public void setTestResults(Set<TestResult> testResults) {
+ this.testResults = testResults;
+ }
+
+ public boolean isCreated() {
+ return created;
+ }
+
+ public void setCreated(boolean created) {
+ this.created = created;
+ }
+
+ public boolean isUpdated() {
+ return updated;
+ }
+
+ public void setUpdated(boolean updated) {
+ this.updated = updated;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Student student = (Student) o;
+ return Objects.equals(id, student.id) &&
+ Objects.equals(firstName, student.firstName) &&
+ Objects.equals(lastName, student.lastName) &&
+ Objects.equals(studentNumber, student.studentNumber) &&
+ Objects.equals(testResults, student.testResults) &&
+ Objects.equals(created, student.created) &&
+ Objects.equals(updated, student.updated);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, firstName, lastName, studentNumber, testResults);
+ }
+
+ @Override
+ public String toString() {
+ return "Student{" +
+ "id=" + id +
+ ", firstName='" + firstName + '\'' +
+ ", lastName='" + lastName + '\'' +
+ ", studentNumber='" + studentNumber + '\'' +
+ ", testResults=" + testResults +
+ ", created=" + created +
+ ", updated=" + updated +
+ '}';
+ }
} \ No newline at end of file
diff --git a/src/main/java/com/stileeducation/markr/entity/Test.java b/src/main/java/com/stileeducation/markr/entity/Test.java
index 615d0e1..ff9088e 100644
--- a/src/main/java/com/stileeducation/markr/entity/Test.java
+++ b/src/main/java/com/stileeducation/markr/entity/Test.java
@@ -10,74 +10,100 @@ import java.util.Set;
@Table(name = "tests")
public class Test {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
-
- @Column(name = "test_id", nullable = false, unique = true)
- private String testId;
-
- @Column(name = "marks_available", nullable = false)
- private Integer marksAvailable;
-
- @OneToMany(mappedBy = "test", cascade = CascadeType.ALL, orphanRemoval = true)
- private Set<TestResult> testResults = new HashSet<>();
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public String getTestId() {
- return testId;
- }
-
- public void setTestId(String testId) {
- this.testId = testId;
- }
-
- public Integer getMarksAvailable() {
- return marksAvailable;
- }
-
- public void setMarksAvailable(Integer marksAvailable) {
- this.marksAvailable = marksAvailable;
- }
-
- public Set<TestResult> getTestResults() {
- return testResults;
- }
-
- public void setTestResults(Set<TestResult> testResults) {
- this.testResults = testResults;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Test test = (Test) o;
- return Objects.equals(id, test.id) &&
- Objects.equals(testId, test.testId) &&
- Objects.equals(marksAvailable, test.marksAvailable) &&
- Objects.equals(testResults, test.testResults);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(id, testId, marksAvailable, testResults);
- }
-
- @Override
- public String toString() {
- return "Test{" +
- "id=" + id +
- ", testId='" + testId + '\'' +
- ", marksAvailable=" + marksAvailable +
- ", testResults=" + testResults +
- '}';
- }
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "test_id", nullable = false, unique = true)
+ private String testId;
+
+ @Column(name = "marks_available", nullable = false)
+ private Integer marksAvailable;
+
+ @OneToMany(mappedBy = "test", cascade = CascadeType.ALL, orphanRemoval = true)
+ private Set<TestResult> testResults = new HashSet<>();
+
+ @Transient
+ private boolean created = false;
+
+ @Transient
+ private boolean updated = false;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getTestId() {
+ return testId;
+ }
+
+ public void setTestId(String testId) {
+ this.testId = testId;
+ }
+
+ public Integer getMarksAvailable() {
+ return marksAvailable;
+ }
+
+ public void setMarksAvailable(Integer marksAvailable) {
+ this.marksAvailable = marksAvailable;
+ }
+
+ public Set<TestResult> getTestResults() {
+ return testResults;
+ }
+
+ public void setTestResults(Set<TestResult> testResults) {
+ this.testResults = testResults;
+ }
+
+ public boolean isCreated() {
+ return created;
+ }
+
+ public void setCreated(boolean created) {
+ this.created = created;
+ }
+
+ public boolean isUpdated() {
+ return updated;
+ }
+
+ public void setUpdated(boolean updated) {
+ this.updated = updated;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Test test = (Test) o;
+ return Objects.equals(id, test.id) &&
+ Objects.equals(testId, test.testId) &&
+ Objects.equals(marksAvailable, test.marksAvailable) &&
+ Objects.equals(testResults, test.testResults) &&
+ Objects.equals(created, test.created) &&
+ Objects.equals(updated, test.updated);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, testId, marksAvailable, testResults);
+ }
+
+ @Override
+ public String toString() {
+ return "Test{" +
+ "id=" + id +
+ ", testId='" + testId + '\'' +
+ ", marksAvailable=" + marksAvailable +
+ ", testResults=" + testResults +
+ ", created=" + created +
+ ", updated=" + updated +
+ '}';
+ }
}
diff --git a/src/main/java/com/stileeducation/markr/entity/TestResult.java b/src/main/java/com/stileeducation/markr/entity/TestResult.java
index 26b2346..bdbade4 100644
--- a/src/main/java/com/stileeducation/markr/entity/TestResult.java
+++ b/src/main/java/com/stileeducation/markr/entity/TestResult.java
@@ -8,83 +8,112 @@ import java.util.Objects;
@Table(name = "test_results")
public class TestResult {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "student_id", nullable = false)
- private Student student;
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "test_id", nullable = false)
- private Test test;
- @Column(name = "marks_awarded", nullable = false)
- private Integer marksAwarded;
-
- public TestResult() {
- }
-
- public TestResult(Long id, Student student, Test test, Integer marksAwarded) {
- this.id = id;
- this.student = student;
- this.test = test;
- this.marksAwarded = marksAwarded;
- }
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public Student getStudent() {
- return student;
- }
-
- public void setStudent(Student student) {
- this.student = student;
- }
-
- public Test getTest() {
- return test;
- }
-
- public void setTest(Test test) {
- this.test = test;
- }
-
- public Integer getMarksAwarded() {
- return marksAwarded;
- }
-
- public void setMarksAwarded(Integer marksAwarded) {
- this.marksAwarded = marksAwarded;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- TestResult that = (TestResult) o;
- return Objects.equals(id, that.id) &&
- Objects.equals(student, that.student) &&
- Objects.equals(test, that.test) &&
- Objects.equals(marksAwarded, that.marksAwarded);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(id, student, test, marksAwarded);
- }
-
- @Override
- public String toString() {
- return "TestResult{" +
- "id=" + id +
- ", student=" + student +
- ", test=" + test +
- ", marksAwarded=" + marksAwarded +
- '}';
- }
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "student_id", nullable = false)
+ private Student student;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "test_id", nullable = false)
+ private Test test;
+
+ @Column(name = "marks_awarded", nullable = false)
+ private Integer marksAwarded;
+
+ @Transient
+ private boolean created = false;
+
+ @Transient
+ private boolean updated = false;
+
+ public TestResult() {
+ }
+
+ public TestResult(Long id, Student student, Test test, Integer marksAwarded) {
+ this.id = id;
+ this.student = student;
+ this.test = test;
+ this.marksAwarded = marksAwarded;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Student getStudent() {
+ return student;
+ }
+
+ public void setStudent(Student student) {
+ this.student = student;
+ }
+
+ public Test getTest() {
+ return test;
+ }
+
+ public void setTest(Test test) {
+ this.test = test;
+ }
+
+ public Integer getMarksAwarded() {
+ return marksAwarded;
+ }
+
+ public void setMarksAwarded(Integer marksAwarded) {
+ this.marksAwarded = marksAwarded;
+ }
+
+ public boolean isCreated() {
+ return created;
+ }
+
+ public void setCreated(boolean created) {
+ this.created = created;
+ }
+
+ public boolean isUpdated() {
+ return updated;
+ }
+
+ public void setUpdated(boolean updated) {
+ this.updated = updated;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ TestResult that = (TestResult) o;
+ return Objects.equals(id, that.id) &&
+ Objects.equals(student, that.student) &&
+ Objects.equals(test, that.test) &&
+ Objects.equals(marksAwarded, that.marksAwarded) &&
+ Objects.equals(created, that.created) &&
+ Objects.equals(updated, that.updated);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, student, test, marksAwarded);
+ }
+
+ @Override
+ public String toString() {
+ return "TestResult{" +
+ "id=" + id +
+ ", student=" + student +
+ ", test=" + test +
+ ", marksAwarded=" + marksAwarded +
+ ", created=" + created +
+ ", updated=" + updated +
+ '}';
+ }
} \ No newline at end of file
diff --git a/src/main/java/com/stileeducation/markr/service/StudentService.java b/src/main/java/com/stileeducation/markr/service/StudentService.java
index 4af4d63..3ce28c0 100644
--- a/src/main/java/com/stileeducation/markr/service/StudentService.java
+++ b/src/main/java/com/stileeducation/markr/service/StudentService.java
@@ -22,6 +22,7 @@ public class StudentService {
student.setFirstName(firstName);
student.setLastName(lastName);
student.setStudentNumber(studentNumber);
+ student.setCreated(true);
return studentRepository.save(student);
}
}
diff --git a/src/main/java/com/stileeducation/markr/service/TestResultsService.java b/src/main/java/com/stileeducation/markr/service/TestResultsService.java
index 95b42f3..51efe95 100644
--- a/src/main/java/com/stileeducation/markr/service/TestResultsService.java
+++ b/src/main/java/com/stileeducation/markr/service/TestResultsService.java
@@ -1,11 +1,12 @@
package com.stileeducation.markr.service;
-import com.stileeducation.markr.dto.AggregatedTestResultsDTO;
+import com.stileeducation.markr.dto.AggregateResponseDTO;
+import com.stileeducation.markr.dto.ImportResponseDTO;
+import com.stileeducation.markr.dto.MCQTestResultDTO;
+import com.stileeducation.markr.dto.MCQTestResultsDTO;
import com.stileeducation.markr.entity.Student;
import com.stileeducation.markr.entity.Test;
import com.stileeducation.markr.entity.TestResult;
-import com.stileeducation.markr.repository.StudentRepository;
-import com.stileeducation.markr.repository.TestRepository;
import com.stileeducation.markr.repository.TestResultRepository;
import org.apache.commons.math3.stat.descriptive.moment.StandardDeviation;
import org.apache.commons.math3.stat.descriptive.rank.Percentile;
@@ -19,27 +20,34 @@ import java.util.Optional;
public class TestResultsService {
public static final boolean IS_BIAS_CORRECTED = false;
+
@Autowired
private TestResultRepository testResultRepository;
@Autowired
- private StudentRepository studentRepository;
+ private StudentService studentService;
@Autowired
- private TestRepository testRepository;
-
- public TestResult findOrCreateTestResult(Long studentId, Long testId, Integer marksAwarded) {
- Student student = studentRepository.findById(studentId).orElseThrow(() -> new RuntimeException("Student not found"));
- Test test = testRepository.findById(testId).orElseThrow(() -> new RuntimeException("Test not found"));
+ private TestService testService;
+ public TestResult findOrCreateTestResult(Student student, Test test, Integer marksAwarded) {
Optional<TestResult> optionalTestResult = testResultRepository.findByStudentAndTest(student, test);
if (optionalTestResult.isPresent()) {
- return optionalTestResult.get();
+ TestResult testResult = optionalTestResult.get();
+ if (marksAwarded > testResult.getMarksAwarded()) {
+ testResult.setMarksAwarded(marksAwarded);
+ testResult.setUpdated(true);
+ testResultRepository.save(testResult);
+ } else {
+ testResult.setUpdated(false);
+ }
+ return testResult;
} else {
TestResult testResult = new TestResult();
testResult.setStudent(student);
testResult.setTest(test);
testResult.setMarksAwarded(marksAwarded);
+ testResult.setCreated(true);
return testResultRepository.save(testResult);
}
}
@@ -126,10 +134,10 @@ public class TestResultsService {
return new Percentile().evaluate(marks, 75.0);
}
- public AggregatedTestResultsDTO aggregateTestResults(String testId) {
+ public AggregateResponseDTO aggregateTestResults(String testId) {
List<TestResult> testResults = findAllByTestId(testId);
- AggregatedTestResultsDTO results = new AggregatedTestResultsDTO();
+ AggregateResponseDTO results = new AggregateResponseDTO();
results.setMean(calculateMeanOfTestResults(testResults));
results.setStddev(calculateStandardDeviationOfTestResults(testResults));
@@ -143,4 +151,66 @@ public class TestResultsService {
return results;
}
+ public ImportResponseDTO processTestResults(MCQTestResultsDTO testResults) {
+ ImportResponseDTO.ImportData importData = new ImportResponseDTO.ImportData();
+ boolean isValid = true;
+
+ for (MCQTestResultDTO mcqTestResult : testResults.getMcqTestResults()) {
+ try {
+
+ Student student = studentService
+ .findOrCreateStudent(
+ mcqTestResult.getFirstName(),
+ mcqTestResult.getLastName(),
+ mcqTestResult.getStudentNumber());
+
+ if (student.isCreated()) {
+ importData.incrementStudentsCreated();
+ }
+ if (student.isUpdated()) {
+ importData.incrementStudentsUpdated();
+ }
+
+ Test test = testService
+ .findOrCreateTest(
+ mcqTestResult.getTestId(),
+ mcqTestResult.getSummaryMarks().getAvailable());
+
+ if (test.isCreated()) {
+ importData.incrementTestsCreated();
+ }
+ if (test.isUpdated()) {
+ importData.incrementTestsUpdated();
+ }
+
+ TestResult testResult =
+ findOrCreateTestResult(
+ student,
+ test,
+ mcqTestResult.getSummaryMarks().getObtained());
+
+ if (testResult.isCreated()) {
+ importData.incrementTestResultsCreated();
+ }
+ if (testResult.isUpdated()) {
+ importData.incrementTestResultsUpdated();
+ }
+ } catch (Exception e) {
+ isValid = false;
+ }
+ }
+
+ ImportResponseDTO response = new ImportResponseDTO();
+ response.setData(importData);
+
+ if (isValid) {
+ response.setStatus("success");
+ response.setMessage("Import operation completed successfully.");
+ } else {
+ response.setStatus("failure");
+ response.setMessage("Data was invalid or processing failed.");
+ }
+
+ return response;
+ }
} \ No newline at end of file
diff --git a/src/main/java/com/stileeducation/markr/service/TestService.java b/src/main/java/com/stileeducation/markr/service/TestService.java
index f3ba98c..f3231b9 100644
--- a/src/main/java/com/stileeducation/markr/service/TestService.java
+++ b/src/main/java/com/stileeducation/markr/service/TestService.java
@@ -16,13 +16,21 @@ public class TestService {
public Test findOrCreateTest(String testId, Integer marksAvailable) {
Optional<Test> optionalTest = testRepository.findByTestId(testId);
if (optionalTest.isPresent()) {
- return optionalTest.get();
+ Test test = optionalTest.get();
+ if (test.getMarksAvailable() < marksAvailable) {
+ test.setMarksAvailable(marksAvailable);
+ test.setUpdated(true);
+ testRepository.save(test);
+ } else {
+ test.setUpdated(false);
+ }
+ return test;
} else {
Test test = new Test();
test.setTestId(testId);
test.setMarksAvailable(marksAvailable);
+ test.setCreated(true);
return testRepository.save(test);
}
}
-
}