summaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
authorSzymon Szukalski <szymon@skas.io>2024-07-23 23:23:45 +1000
committerSzymon Szukalski <szymon@skas.io>2024-07-23 23:23:45 +1000
commitf08b2fa7e6a977a18d6b9f14fb73c18ec73ec5df (patch)
tree48ce214bc609130748b9630c2017000cafd29c2b /src/main/java
parentfa34f76ad8ebccb96012b45a4207532846cfa03f (diff)
Implement domain services
Implement core business logic for working with Student, Test, and TestResult.
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/com/stileeducation/markr/controller/TestResultsController.java58
-rw-r--r--src/main/java/com/stileeducation/markr/service/StudentService.java28
-rw-r--r--src/main/java/com/stileeducation/markr/service/TestResultsService.java148
-rw-r--r--src/main/java/com/stileeducation/markr/service/TestResultsServiceImpl.java29
-rw-r--r--src/main/java/com/stileeducation/markr/service/TestService.java28
5 files changed, 255 insertions, 36 deletions
diff --git a/src/main/java/com/stileeducation/markr/controller/TestResultsController.java b/src/main/java/com/stileeducation/markr/controller/TestResultsController.java
index 5667027..c69492d 100644
--- a/src/main/java/com/stileeducation/markr/controller/TestResultsController.java
+++ b/src/main/java/com/stileeducation/markr/controller/TestResultsController.java
@@ -1,8 +1,17 @@
package com.stileeducation.markr.controller;
import com.stileeducation.markr.dto.AggregatedTestResultsDTO;
+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.TestRepository;
+import com.stileeducation.markr.repository.TestResultRepository;
+import com.stileeducation.markr.service.StudentService;
import com.stileeducation.markr.service.TestResultsService;
+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.web.bind.annotation.*;
@@ -14,7 +23,20 @@ public class TestResultsController {
public static final String IMPORT_ENDPOINT = "/import";
public static final String AGGREGATE_ENDPOINT = "/results/{test-id}/aggregate";
- private final TestResultsService testResultsService;
+ @Autowired
+ private StudentService studentService;
+
+ @Autowired
+ private TestService testService;
+
+ @Autowired
+ private TestResultsService testResultsService;
+
+ @Autowired
+ private TestRepository testRepository;
+
+ @Autowired
+ private TestResultRepository testResultRepository;
public TestResultsController(TestResultsService testResultsService) {
this.testResultsService = testResultsService;
@@ -23,7 +45,39 @@ public class TestResultsController {
// TODO consider return value
@PostMapping(value = IMPORT_ENDPOINT, consumes = "text/xml+markr", produces = "application/json")
public ResponseEntity<Void> handleXmlRequest(@RequestBody MCQTestResultsDTO testResults) {
- testResultsService.importTestResults(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?)
+
+ 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);
}
diff --git a/src/main/java/com/stileeducation/markr/service/StudentService.java b/src/main/java/com/stileeducation/markr/service/StudentService.java
new file mode 100644
index 0000000..4af4d63
--- /dev/null
+++ b/src/main/java/com/stileeducation/markr/service/StudentService.java
@@ -0,0 +1,28 @@
+package com.stileeducation.markr.service;
+
+import com.stileeducation.markr.entity.Student;
+import com.stileeducation.markr.repository.StudentRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Optional;
+
+@Service
+public class StudentService {
+
+ @Autowired
+ private StudentRepository studentRepository;
+
+ public Student findOrCreateStudent(String firstName, String lastName, String studentNumber) {
+ Optional<Student> optionalStudent = studentRepository.findByStudentNumber(studentNumber);
+ if (optionalStudent.isPresent()) {
+ return optionalStudent.get();
+ } else {
+ Student student = new Student();
+ student.setFirstName(firstName);
+ student.setLastName(lastName);
+ student.setStudentNumber(studentNumber);
+ 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 2740e50..8d1b314 100644
--- a/src/main/java/com/stileeducation/markr/service/TestResultsService.java
+++ b/src/main/java/com/stileeducation/markr/service/TestResultsService.java
@@ -1,10 +1,148 @@
package com.stileeducation.markr.service;
import com.stileeducation.markr.dto.AggregatedTestResultsDTO;
-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.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
-public interface TestResultsService {
- MCQTestResultsDTO importTestResults(MCQTestResultsDTO mcqTestResults);
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
- AggregatedTestResultsDTO aggregateTestResults(String testId);
-}
+@Service
+public class TestResultsService {
+
+ @Autowired
+ private TestResultRepository testResultRepository;
+
+ @Autowired
+ private StudentRepository studentRepository;
+
+ @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"));
+
+ Optional<TestResult> optionalTestResult = testResultRepository.findByStudentAndTest(student, test);
+ if (optionalTestResult.isPresent()) {
+ return optionalTestResult.get();
+ } else {
+ TestResult testResult = new TestResult();
+ testResult.setStudent(student);
+ testResult.setTest(test);
+ testResult.setMarksAwarded(marksAwarded);
+ return testResultRepository.save(testResult);
+ }
+ }
+
+ public List<TestResult> findAllByTestId(String testId) {
+ return testResultRepository.findAllByTestId(testId);
+ }
+
+ public double calculateMeanOfTestResults(List<TestResult> results) {
+ if (results.isEmpty()) {
+ return 0.0;
+ }
+ return results.stream()
+ .mapToInt(TestResult::getMarksAwarded)
+ .average()
+ .orElse(0.0);
+ }
+
+ public double calculateMinOfTestResults(List<TestResult> results) {
+ if (results.isEmpty()) {
+ return 0.0;
+ }
+ return results
+ .stream()
+ .mapToDouble(TestResult::getMarksAwarded)
+ .min()
+ .orElse(0.0);
+ }
+
+ public double calculateMaxOfTestResults(List<TestResult> results) {
+ if (results.isEmpty()) {
+ return 0.0;
+ }
+ return results
+ .stream()
+ .mapToDouble(TestResult::getMarksAwarded)
+ .max()
+ .orElse(0.0);
+ }
+
+ public double calculateStandardDeviationOfTestResults(List<TestResult> results, double mean) {
+ if (results.isEmpty()) {
+ return 0.0; // or throw an exception if no results are found
+ }
+
+ // Calculate the variance
+ double variance = results.stream()
+ .mapToDouble(result -> Math.pow(result.getMarksAwarded() - mean, 2))
+ .average()
+ .orElse(0.0);
+
+ // Return the standard deviation
+ return Math.sqrt(variance);
+ }
+
+ public double calculatePercentile(List<Integer> sortedMarks, double percentile) {
+ if (sortedMarks.isEmpty()) {
+ return 0.0;
+ }
+ int index = (int) Math.floor(percentile / 100.0 * sortedMarks.size()) - 1;
+ return sortedMarks.get(Math.min(index, sortedMarks.size() - 1));
+ }
+
+ public List<Integer> getSortedMarks(List<TestResult> testResults) {
+ return testResults.stream()
+ .map(TestResult::getMarksAwarded)
+ .sorted()
+ .collect(Collectors.toList());
+ }
+
+ public double calculate25thPercentile(List<Integer> sortedMarks) {
+ return calculatePercentile(sortedMarks, 25.0);
+ }
+
+ public double calculate50thPercentile(List<Integer> sortedMarks) {
+ return calculatePercentile(sortedMarks, 50.0);
+ }
+
+ public double calculate75thPercentile(List<Integer> sortedMarks) {
+ return calculatePercentile(sortedMarks, 75.0);
+ }
+
+ public AggregatedTestResultsDTO aggregateTestResults(String testId) {
+ List<TestResult> testResults = findAllByTestId(testId);
+ List<Integer> sortedMarks = getSortedMarks(testResults);
+
+ AggregatedTestResultsDTO results = new AggregatedTestResultsDTO();
+
+ results.setMean(calculateMeanOfTestResults(testResults));
+
+ results.setStddev(calculateStandardDeviationOfTestResults(testResults, results.getMean()));
+
+ results.setMin(calculateMinOfTestResults(testResults));
+
+ results.setMax(calculateMaxOfTestResults(testResults));
+
+ results.setP25(calculate25thPercentile(sortedMarks));
+
+ results.setP50(calculate50thPercentile(sortedMarks));
+
+ results.setP75(calculate75thPercentile(sortedMarks));
+
+ results.setCount(testResults.size());
+
+ return results;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/com/stileeducation/markr/service/TestResultsServiceImpl.java b/src/main/java/com/stileeducation/markr/service/TestResultsServiceImpl.java
deleted file mode 100644
index abb7f18..0000000
--- a/src/main/java/com/stileeducation/markr/service/TestResultsServiceImpl.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.stileeducation.markr.service;
-
-import com.stileeducation.markr.dto.AggregatedTestResultsDTO;
-import com.stileeducation.markr.dto.MCQTestResultsDTO;
-import org.springframework.stereotype.Service;
-
-@Service
-public class TestResultsServiceImpl implements TestResultsService {
-
- @Override
- public MCQTestResultsDTO importTestResults(MCQTestResultsDTO testResults) {
- return testResults;
- }
-
- @Override
- public AggregatedTestResultsDTO aggregateTestResults(String testId) {
- AggregatedTestResultsDTO results = new AggregatedTestResultsDTO();
- results.setMean(65.0);
- results.setStddev(0.0);
- results.setMin(65.0);
- results.setMax(65.0);
- results.setP25(65.0);
- results.setP50(65.0);
- results.setP75(65.0);
- results.setCount(1);
- return results;
- }
-
-} \ 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
new file mode 100644
index 0000000..f3ba98c
--- /dev/null
+++ b/src/main/java/com/stileeducation/markr/service/TestService.java
@@ -0,0 +1,28 @@
+package com.stileeducation.markr.service;
+
+import com.stileeducation.markr.entity.Test;
+import com.stileeducation.markr.repository.TestRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Optional;
+
+@Service
+public class TestService {
+
+ @Autowired
+ TestRepository testRepository;
+
+ public Test findOrCreateTest(String testId, Integer marksAvailable) {
+ Optional<Test> optionalTest = testRepository.findByTestId(testId);
+ if (optionalTest.isPresent()) {
+ return optionalTest.get();
+ } else {
+ Test test = new Test();
+ test.setTestId(testId);
+ test.setMarksAvailable(marksAvailable);
+ return testRepository.save(test);
+ }
+ }
+
+}