summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pom.xml6
-rw-r--r--src/main/java/com/stileeducation/markr/entity/TestResult.java10
-rw-r--r--src/main/java/com/stileeducation/markr/service/TestResultsService.java82
-rw-r--r--src/test/java/com/stileeducation/markr/service/TestResultsServiceTest.java175
-rw-r--r--src/test/java/com/stileeducation/markr/util/StudentBuilder.java50
-rw-r--r--src/test/java/com/stileeducation/markr/util/TestBuilder.java43
-rw-r--r--src/test/java/com/stileeducation/markr/util/TestResultBuilder.java36
7 files changed, 360 insertions, 42 deletions
diff --git a/pom.xml b/pom.xml
index 0281f72..55b3297 100644
--- a/pom.xml
+++ b/pom.xml
@@ -65,6 +65,12 @@
</dependency>
<dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-math3</artifactId>
+ <version>3.6.1</version>
+ </dependency>
+
+ <dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
diff --git a/src/main/java/com/stileeducation/markr/entity/TestResult.java b/src/main/java/com/stileeducation/markr/entity/TestResult.java
index cb7cea5..0d68f62 100644
--- a/src/main/java/com/stileeducation/markr/entity/TestResult.java
+++ b/src/main/java/com/stileeducation/markr/entity/TestResult.java
@@ -8,6 +8,16 @@ import java.util.Objects;
@Table(name = "test_results")
public class TestResult {
+ public TestResult() {
+ }
+
+ public TestResult(Long id, Student student, Test test, Integer marksAwarded) {
+ this.id = id;
+ this.student = student;
+ this.test = test;
+ this.marksAwarded = marksAwarded;
+ }
+
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
diff --git a/src/main/java/com/stileeducation/markr/service/TestResultsService.java b/src/main/java/com/stileeducation/markr/service/TestResultsService.java
index 8d1b314..95b42f3 100644
--- a/src/main/java/com/stileeducation/markr/service/TestResultsService.java
+++ b/src/main/java/com/stileeducation/markr/service/TestResultsService.java
@@ -7,16 +7,18 @@ 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;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
-import java.util.stream.Collectors;
@Service
public class TestResultsService {
+ public static final boolean IS_BIAS_CORRECTED = false;
@Autowired
private TestResultRepository testResultRepository;
@@ -78,68 +80,64 @@ public class TestResultsService {
.orElse(0.0);
}
- public double calculateStandardDeviationOfTestResults(List<TestResult> results, double mean) {
+ public double calculateStandardDeviationOfTestResults(List<TestResult> results) {
if (results.isEmpty()) {
- return 0.0; // or throw an exception if no results are found
+ return 0.0;
}
+ double[] marks =
+ results.stream()
+ .mapToDouble(TestResult::getMarksAwarded)
+ .toArray();
- // 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);
+ StandardDeviation standardDeviation = new StandardDeviation(IS_BIAS_CORRECTED);
+ return standardDeviation.evaluate(marks);
}
- public double calculatePercentile(List<Integer> sortedMarks, double percentile) {
- if (sortedMarks.isEmpty()) {
+ public double calculate25thPercentile(List<TestResult> results) {
+ if (results.isEmpty()) {
return 0.0;
}
- int index = (int) Math.floor(percentile / 100.0 * sortedMarks.size()) - 1;
- return sortedMarks.get(Math.min(index, sortedMarks.size() - 1));
+ double[] marks =
+ results.stream()
+ .mapToDouble(TestResult::getMarksAwarded)
+ .toArray();
+ return new Percentile().evaluate(marks, 25.0);
}
- 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 calculate50thPercentile(List<TestResult> results) {
+ if (results.isEmpty()) {
+ return 0.0;
+ }
+ double[] marks =
+ results.stream()
+ .mapToDouble(TestResult::getMarksAwarded)
+ .toArray();
+ return new Percentile().evaluate(marks, 50.0);
}
- public double calculate75thPercentile(List<Integer> sortedMarks) {
- return calculatePercentile(sortedMarks, 75.0);
+ public double calculate75thPercentile(List<TestResult> results) {
+ if (results.isEmpty()) {
+ return 0.0;
+ }
+ double[] marks =
+ results.stream()
+ .mapToDouble(TestResult::getMarksAwarded)
+ .toArray();
+ return new Percentile().evaluate(marks, 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.setStddev(calculateStandardDeviationOfTestResults(testResults));
results.setMin(calculateMinOfTestResults(testResults));
-
results.setMax(calculateMaxOfTestResults(testResults));
-
- results.setP25(calculate25thPercentile(sortedMarks));
-
- results.setP50(calculate50thPercentile(sortedMarks));
-
- results.setP75(calculate75thPercentile(sortedMarks));
-
+ results.setP25(calculate25thPercentile(testResults));
+ results.setP50(calculate50thPercentile(testResults));
+ results.setP75(calculate75thPercentile(testResults));
results.setCount(testResults.size());
return results;
diff --git a/src/test/java/com/stileeducation/markr/service/TestResultsServiceTest.java b/src/test/java/com/stileeducation/markr/service/TestResultsServiceTest.java
new file mode 100644
index 0000000..474d731
--- /dev/null
+++ b/src/test/java/com/stileeducation/markr/service/TestResultsServiceTest.java
@@ -0,0 +1,175 @@
+package com.stileeducation.markr.service;
+
+import com.stileeducation.markr.entity.Student;
+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 com.stileeducation.markr.util.StudentBuilder;
+import com.stileeducation.markr.util.TestBuilder;
+import com.stileeducation.markr.util.TestResultBuilder;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+
+class TestResultsServiceTest {
+
+ private static Student student1;
+ private static Student student2;
+ private static Student student3;
+ private static Student student4;
+
+ private static com.stileeducation.markr.entity.Test test1;
+
+ private static TestResult testResult1;
+ private static TestResult testResult2;
+ private static TestResult testResult3;
+ private static TestResult testResult4;
+ private static TestResult testResult5;
+ private static TestResult testResult6;
+
+ private static List<TestResult> test1Results;
+
+ @Mock
+ private TestResultRepository testResultRepository;
+
+ @Mock
+ private StudentRepository studentRepository;
+
+ @Mock
+ private TestRepository testRepository;
+
+ @InjectMocks
+ private TestResultsService testResultsService;
+
+ @BeforeAll
+ static void setupTestData() {
+ student1 = new StudentBuilder()
+ .withId(1L)
+ .withStudentNumber("555123")
+ .withFirstName("Emily")
+ .withLastName("Clark")
+ .build();
+
+ student2 = new StudentBuilder()
+ .withId(2L)
+ .withStudentNumber("555456")
+ .withFirstName("James")
+ .withLastName("Taylor")
+ .build();
+
+ student3 = new StudentBuilder()
+ .withId(3L)
+ .withStudentNumber("555789")
+ .withFirstName("Sophia")
+ .withLastName("Martinez")
+ .build();
+
+ student4 = new StudentBuilder()
+ .withId(4L)
+ .withStudentNumber("555012")
+ .withFirstName("Liam")
+ .withLastName("Anderson")
+ .build();
+
+ test1 = new TestBuilder()
+ .withId(1L)
+ .withTestId("1234")
+ .withMarksAvailable(100)
+ .build();
+
+ testResult1 = new TestResultBuilder()
+ .withStudent(student1)
+ .withTest(test1)
+ .withMarksAwarded(10)
+ .build();
+
+ testResult2 = new TestResultBuilder()
+ .withStudent(student2)
+ .withTest(test1)
+ .withMarksAwarded(20)
+ .build();
+
+ testResult3 = new TestResultBuilder()
+ .withStudent(student3)
+ .withTest(test1)
+ .withMarksAwarded(40)
+ .build();
+
+ testResult4 = new TestResultBuilder()
+ .withStudent(student4)
+ .withTest(test1)
+ .withMarksAwarded(50)
+ .build();
+
+ testResult5 = new TestResultBuilder()
+ .withStudent(student3)
+ .withTest(test1)
+ .withMarksAwarded(70)
+ .build();
+
+ testResult6 = new TestResultBuilder()
+ .withStudent(student4)
+ .withTest(test1)
+ .withMarksAwarded(80)
+ .build();
+
+ test1Results = List.of(testResult1, testResult2, testResult3, testResult4, testResult5, testResult6);
+ }
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.openMocks(this);
+ }
+
+ @Test
+ void calculateMeanOfTestResults() {
+ double mean = testResultsService.calculateMeanOfTestResults(test1Results);
+ assertEquals(45.0, mean, 0.01);
+ }
+
+ @Test
+ void calculateMinOfTestResults() {
+ double min = testResultsService.calculateMinOfTestResults(test1Results);
+ assertEquals(10.0, min, 0.01);
+ }
+
+ @Test
+ void calculateMaxOfTestResults() {
+ double max = testResultsService.calculateMaxOfTestResults(test1Results);
+ assertEquals(80.0, max, 0.01);
+ }
+
+ @Test
+ void calculateStandardDeviationOfTestResults() {
+ double standardDeviationOfTestResults = testResultsService.calculateStandardDeviationOfTestResults(test1Results);
+ assertEquals(25, standardDeviationOfTestResults, 0.01);
+ }
+
+ @Test
+ void calculate25thPercentile() {
+ double percentile = testResultsService.calculate25thPercentile(test1Results);
+ assertEquals(17.5, percentile, 0.01);
+ }
+
+ @Test
+ void calculate50thPercentile() {
+ double percentile = testResultsService.calculate50thPercentile(test1Results);
+ assertEquals(45, percentile, 0.01);
+ }
+
+ @Test
+ void calculate75thPercentile() {
+ double percentile = testResultsService.calculate75thPercentile(test1Results);
+ assertEquals(72.5, percentile, 0.01);
+ }
+
+} \ No newline at end of file
diff --git a/src/test/java/com/stileeducation/markr/util/StudentBuilder.java b/src/test/java/com/stileeducation/markr/util/StudentBuilder.java
new file mode 100644
index 0000000..5189f19
--- /dev/null
+++ b/src/test/java/com/stileeducation/markr/util/StudentBuilder.java
@@ -0,0 +1,50 @@
+package com.stileeducation.markr.util;
+
+import com.stileeducation.markr.entity.Student;
+import com.stileeducation.markr.entity.TestResult;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class StudentBuilder {
+ private Long id;
+ private String firstName;
+ private String lastName;
+ private String studentNumber;
+ private Set<TestResult> testResults = new HashSet<>();
+
+ public StudentBuilder withId(Long id) {
+ this.id = id;
+ return this;
+ }
+
+ public StudentBuilder withFirstName(String firstName) {
+ this.firstName = firstName;
+ return this;
+ }
+
+ public StudentBuilder withLastName(String lastName) {
+ this.lastName = lastName;
+ return this;
+ }
+
+ public StudentBuilder withStudentNumber(String studentNumber) {
+ this.studentNumber = studentNumber;
+ return this;
+ }
+
+ public StudentBuilder withTestResults(Set<TestResult> testResults) {
+ this.testResults = testResults;
+ return this;
+ }
+
+ public Student build() {
+ Student student = new Student();
+ student.setId(id);
+ student.setFirstName(firstName);
+ student.setLastName(lastName);
+ student.setStudentNumber(studentNumber);
+ student.setTestResults(testResults);
+ return student;
+ }
+} \ No newline at end of file
diff --git a/src/test/java/com/stileeducation/markr/util/TestBuilder.java b/src/test/java/com/stileeducation/markr/util/TestBuilder.java
new file mode 100644
index 0000000..5c513f3
--- /dev/null
+++ b/src/test/java/com/stileeducation/markr/util/TestBuilder.java
@@ -0,0 +1,43 @@
+package com.stileeducation.markr.util;
+
+import com.stileeducation.markr.entity.Test;
+import com.stileeducation.markr.entity.TestResult;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class TestBuilder {
+ private Long id;
+ private String testId;
+ private Integer marksAvailable;
+ private Set<TestResult> testResults = new HashSet<>();
+
+ public TestBuilder withId(Long id) {
+ this.id = id;
+ return this;
+ }
+
+ public TestBuilder withTestId(String testId) {
+ this.testId = testId;
+ return this;
+ }
+
+ public TestBuilder withMarksAvailable(Integer marksAvailable) {
+ this.marksAvailable = marksAvailable;
+ return this;
+ }
+
+ public TestBuilder withTestResults(Set<TestResult> testResults) {
+ this.testResults = testResults;
+ return this;
+ }
+
+ public Test build() {
+ Test test = new Test();
+ test.setId(id);
+ test.setTestId(testId);
+ test.setMarksAvailable(marksAvailable);
+ test.setTestResults(testResults);
+ return test;
+ }
+}
diff --git a/src/test/java/com/stileeducation/markr/util/TestResultBuilder.java b/src/test/java/com/stileeducation/markr/util/TestResultBuilder.java
new file mode 100644
index 0000000..db26e15
--- /dev/null
+++ b/src/test/java/com/stileeducation/markr/util/TestResultBuilder.java
@@ -0,0 +1,36 @@
+package com.stileeducation.markr.util;
+
+import com.stileeducation.markr.entity.Student;
+import com.stileeducation.markr.entity.Test;
+import com.stileeducation.markr.entity.TestResult;
+
+public class TestResultBuilder {
+ private Long id;
+ private Student student;
+ private Test test;
+ private Integer marksAwarded;
+
+ public TestResultBuilder withId(Long id) {
+ this.id = id;
+ return this;
+ }
+
+ public TestResultBuilder withStudent(Student student) {
+ this.student = student;
+ return this;
+ }
+
+ public TestResultBuilder withTest(Test test) {
+ this.test = test;
+ return this;
+ }
+
+ public TestResultBuilder withMarksAwarded(Integer marksAwarded) {
+ this.marksAwarded = marksAwarded;
+ return this;
+ }
+
+ public TestResult build() {
+ return new TestResult(id, student, test, marksAwarded);
+ }
+} \ No newline at end of file