From 4e47cd426f5b27f49678114b9a0e521b9bcf404b Mon Sep 17 00:00:00 2001
From: Andreas Svanberg <andreass@dsv.su.se>
Date: Thu, 24 Oct 2024 13:53:21 +0200
Subject: [PATCH] Calculate grades using grade limits, if available.

Grade can be any string rather than a limited set
---
 .../report/GradeCalculatorServiceImpl.java    | 14 +++++++
 .../su/dsv/scipro/report/GradingReport.java   | 10 ++++-
 .../GradingReportTemplateGradeCalculator.java | 39 +++++++++++++++++++
 .../grading/GradingReportPointsPanel.java     |  2 +-
 .../grading/GradingReportPointsPanelTest.java |  2 +-
 5 files changed, 63 insertions(+), 4 deletions(-)
 create mode 100644 core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplateGradeCalculator.java

diff --git a/core/src/main/java/se/su/dsv/scipro/report/GradeCalculatorServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/report/GradeCalculatorServiceImpl.java
index 569afb79b7..c2a6362c83 100644
--- a/core/src/main/java/se/su/dsv/scipro/report/GradeCalculatorServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/report/GradeCalculatorServiceImpl.java
@@ -1,5 +1,6 @@
 package se.su.dsv.scipro.report;
 
+import jakarta.inject.Inject;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.report.calculators.original.SupervisorBachelorGradeCalculator;
 import se.su.dsv.scipro.report.calculators.original.SupervisorMaster15GradeCalculator;
@@ -8,8 +9,21 @@ import se.su.dsv.scipro.system.DegreeType;
 import se.su.dsv.scipro.system.ProjectType;
 
 public class GradeCalculatorServiceImpl implements GradeCalculatorService {
+
+    private final GradingReportService gradingReportTemplateService;
+
+    @Inject
+    public GradeCalculatorServiceImpl(GradingReportService gradingReportService) {
+        this.gradingReportTemplateService = gradingReportService;
+    }
+
     @Override
     public GradeCalculator getSupervisorCalculator(final Project project) {
+        GradingReportTemplate template = gradingReportTemplateService.getTemplate(project);
+        if (!template.getGradeLimits().isEmpty()) {
+            return new GradingReportTemplateGradeCalculator(template);
+        }
+
         DegreeType degreeType = project.getProjectType().getDegreeType();
         if (degreeType == DegreeType.BACHELOR) {
             if (getYear(project) >= 2017) {
diff --git a/core/src/main/java/se/su/dsv/scipro/report/GradingReport.java b/core/src/main/java/se/su/dsv/scipro/report/GradingReport.java
index 4c4b70397c..0c036ede48 100644
--- a/core/src/main/java/se/su/dsv/scipro/report/GradingReport.java
+++ b/core/src/main/java/se/su/dsv/scipro/report/GradingReport.java
@@ -15,8 +15,14 @@ import java.util.stream.Collectors;
 @Entity
 public abstract class GradingReport extends Report {
 
-    public enum Grade {
-        A, B, C, D, E, F, FX
+    public record Grade(String name) {
+        public static final Grade A = new Grade("A");
+        public static final Grade B = new Grade("B");
+        public static final Grade C = new Grade("C");
+        public static final Grade D = new Grade("D");
+        public static final Grade E = new Grade("E");
+        public static final Grade F = new Grade("F");
+        public static final Grade FX = new Grade("FX");
     }
 
     public enum State { INITIAL, REVIEWING, FINALIZED }
diff --git a/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplateGradeCalculator.java b/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplateGradeCalculator.java
new file mode 100644
index 0000000000..777770eb3b
--- /dev/null
+++ b/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplateGradeCalculator.java
@@ -0,0 +1,39 @@
+package se.su.dsv.scipro.report;
+
+import java.util.Comparator;
+import java.util.Objects;
+
+class GradingReportTemplateGradeCalculator implements GradeCalculator {
+    private final GradingReportTemplate template;
+
+    GradingReportTemplateGradeCalculator(GradingReportTemplate template) {
+        this.template = template;
+    }
+
+    @Override
+    public GradingReport.Grade getGrade(GradingReport gradingReport) {
+        for (GradingCriterion gradingCriterion : gradingReport.getGradingCriteria()) {
+            if (!gradingCriterion.meetsMinimumPointRequirement()) {
+                return new GradingReport.Grade(template.getFailingGrade());
+            }
+        }
+        long points = getPoints(gradingReport);
+        String textualGrade = template.getGradeLimits()
+                .stream()
+                .filter(gradeLimit -> points >= gradeLimit.getLowerLimit())
+                .max(Comparator.comparing(GradeLimit::getLowerLimit))
+                .map(GradeLimit::getGrade)
+                .orElseGet(template::getFailingGrade);
+        return new GradingReport.Grade(textualGrade);
+    }
+
+    @Override
+    public long getPoints(GradingReport gradingReport) {
+        return gradingReport.getGradingCriteria()
+                .stream()
+                .map(GradingCriterion::getPoints)
+                .filter(Objects::nonNull)
+                .mapToInt(Integer::intValue)
+                .sum();
+    }
+}
diff --git a/view/src/main/java/se/su/dsv/scipro/grading/GradingReportPointsPanel.java b/view/src/main/java/se/su/dsv/scipro/grading/GradingReportPointsPanel.java
index 6a0c6c6e71..ad4c49050d 100644
--- a/view/src/main/java/se/su/dsv/scipro/grading/GradingReportPointsPanel.java
+++ b/view/src/main/java/se/su/dsv/scipro/grading/GradingReportPointsPanel.java
@@ -24,7 +24,7 @@ public class GradingReportPointsPanel extends Panel {
                 return gradingReportIModel.getObject().getGrade(gradeCalculator);
             }
         };
-        final Label grade = new Label(GRADE, gradeModel) {
+        final Label grade = new Label(GRADE, gradeModel.map(GradingReport.Grade::name)) {
             @Override
             protected void onConfigure() {
                 super.onConfigure();
diff --git a/view/src/test/java/se/su/dsv/scipro/grading/GradingReportPointsPanelTest.java b/view/src/test/java/se/su/dsv/scipro/grading/GradingReportPointsPanelTest.java
index 6df3e73e79..cc0c6799df 100644
--- a/view/src/test/java/se/su/dsv/scipro/grading/GradingReportPointsPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/grading/GradingReportPointsPanelTest.java
@@ -45,7 +45,7 @@ public class GradingReportPointsPanelTest extends SciProTest {
         when(gradeCalculator.getGrade(any(GradingReport.class))).thenReturn(grade);
         startPanel();
 
-        tester.assertLabel(path(panel, GRADE), grade.toString());
+        tester.assertLabel(path(panel, GRADE), grade.name());
     }
 
     @Test