diff --git a/core/src/main/java/modules/CoreModule.java b/core/src/main/java/modules/CoreModule.java
index cb2c500685..075c6e9ed8 100644
--- a/core/src/main/java/modules/CoreModule.java
+++ b/core/src/main/java/modules/CoreModule.java
@@ -48,6 +48,7 @@ import se.su.dsv.scipro.notifications.settings.service.ReceiverConfigurationServ
 import se.su.dsv.scipro.notifications.settings.service.ReceiverConfigurationServiceImpl;
 import se.su.dsv.scipro.peer.*;
 import se.su.dsv.scipro.plagiarism.*;
+import se.su.dsv.scipro.project.ProjectNoteService;
 import se.su.dsv.scipro.project.ProjectPeopleStatisticsService;
 import se.su.dsv.scipro.project.ProjectPeopleStatisticsServiceImpl;
 import se.su.dsv.scipro.project.ProjectService;
@@ -139,6 +140,7 @@ public class CoreModule extends AbstractModule {
         bind(FirstMeetingService.class).to(FirstMeetingServiceImpl.class);
         bind(FinalSeminarCreationSubscribers.class).asEagerSingleton();
         bind(ProjectPartnerRepository.class).to(ProjectPartnerRepositoryImpl.class);
+        bind(ProjectNoteService.class).to(ProjectServiceImpl.class);
 
         install(new PlagiarismModule());
         install(new NotificationModule());
diff --git a/core/src/main/java/modules/RepositoryModule.java b/core/src/main/java/modules/RepositoryModule.java
index 83f7f4539b..2ae13bc0cb 100644
--- a/core/src/main/java/modules/RepositoryModule.java
+++ b/core/src/main/java/modules/RepositoryModule.java
@@ -25,6 +25,8 @@ import se.su.dsv.scipro.report.GradingReportTemplateRepo;
 import se.su.dsv.scipro.report.GradingReportTemplateRepoImpl;
 import se.su.dsv.scipro.report.OppositionReportRepo;
 import se.su.dsv.scipro.report.OppositionReportRepoImpl;
+import se.su.dsv.scipro.report.SupervisorGradingReportRepository;
+import se.su.dsv.scipro.report.SupervisorGradingReportRepositoryImpl;
 import se.su.dsv.scipro.reviewing.DecisionRepository;
 import se.su.dsv.scipro.reviewing.DecisionRepositoryImpl;
 import se.su.dsv.scipro.reviewing.ReviewerTargetRepository;
@@ -59,5 +61,6 @@ public class RepositoryModule extends AbstractModule {
         bind(FinalSeminarRepository.class).to(FinalSeminarRepositoryImpl.class);
         bind(ReviewerTargetRepository.class).to(ReviewerTargetRepositoryImpl.class);
         bind(DecisionRepository.class).to(DecisionRepositoryImpl.class);
+        bind(SupervisorGradingReportRepository.class).to(SupervisorGradingReportRepositoryImpl.class);
     }
 }
\ No newline at end of file
diff --git a/core/src/main/java/se/su/dsv/scipro/DataInitializer.java b/core/src/main/java/se/su/dsv/scipro/DataInitializer.java
index 3a878fc72b..6720ffac7e 100644
--- a/core/src/main/java/se/su/dsv/scipro/DataInitializer.java
+++ b/core/src/main/java/se/su/dsv/scipro/DataInitializer.java
@@ -10,6 +10,7 @@ import se.su.dsv.scipro.milestones.service.MilestoneActivityTemplateService;
 import se.su.dsv.scipro.profiles.CurrentProfile;
 import se.su.dsv.scipro.profiles.Profiles;
 import se.su.dsv.scipro.project.Project;
+import se.su.dsv.scipro.report.AbstractGradingCriterion;
 import se.su.dsv.scipro.report.GradingCriterionPointTemplate;
 import se.su.dsv.scipro.report.GradingReportTemplate;
 import se.su.dsv.scipro.security.auth.roles.Roles;
@@ -20,6 +21,7 @@ import jakarta.inject.Provider;
 import jakarta.persistence.EntityManager;
 import java.time.LocalDate;
 import java.time.LocalTime;
+import java.time.Month;
 import java.util.*;
 
 public class DataInitializer implements Lifecycle {
@@ -232,19 +234,20 @@ public class DataInitializer implements Lifecycle {
     }
 
     private void createGradingCriterionTemplateIfNotDone() {
-        save(getBachelorTemplate(0));
-        save(getBachelorTemplate(ProjectType.HP_15));
-        save(getBachelorTemplate(ProjectType.HP_30));
-        save(getMasterTemplate(0));
-        save(getMasterTemplate(ProjectType.HP_15));
-        save(getMasterTemplate(ProjectType.HP_30));
-        save(getMagisterTemplate(0));
-        save(getMagisterTemplate(ProjectType.HP_15));
-        save(getMagisterTemplate(ProjectType.HP_30));
+        save(getBachelorTemplate());
+        save(getBachelorTemplate());
+        save(getBachelorTemplate());
+        save(getMasterTemplate());
+        save(getMasterTemplate());
+        save(getMasterTemplate());
+        save(getMagisterTemplate());
+        save(getMagisterTemplate());
+        save(getMagisterTemplate());
     }
 
-    private GradingReportTemplate getBachelorTemplate(int credits) {
-        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(bachelorClass, credits);
+    private GradingReportTemplate getBachelorTemplate() {
+        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(bachelorClass,
+                LocalDate.of(2024, Month.JANUARY, 1));
 
         List<GradingCriterionPointTemplate> gradingCriterionPointTemplates = initPointTemplates();
         gradingCriterionPointTemplates.add(new GradingCriterionPointTemplate.Builder()
@@ -401,7 +404,7 @@ public class DataInitializer implements Lifecycle {
                 .description("För 2 poäng krävs dessutom: att oppositionsrapporten ingående och välbalanserat beskriver styrkor och svagheter hos det utvärderade arbetet ur flera aspekter samt att den innehåller tydliga och välmotiverade förslag till förbättringar.")
                 .descriptionEn("For 2 points the following is also required: that the opposition report thoroughly and in a well-balanced way describes from numerous aspects the strengths and weaknesses of the evaluated thesis and that it offers clear and well-motivated suggestions for improvements.")
                 .build());
-        gradingReportTemplate.addIndividualCriterion("Ö1 Oppositionsrapport", "Ö1 Opposition report", 1, gradingCriterionPointTemplates);
+        gradingReportTemplate.addIndividualCriterion("Ö1 Oppositionsrapport", "Ö1 Opposition report", 1, gradingCriterionPointTemplates, AbstractGradingCriterion.Flag.OPPOSITION);
 
         gradingCriterionPointTemplates = initPointTemplates();
         gradingCriterionPointTemplates.add(new GradingCriterionPointTemplate.Builder()
@@ -441,13 +444,14 @@ public class DataInitializer implements Lifecycle {
                 .description("För 1 poäng krävs: att förmåga har uppvisats att reflektera över det genomförda examensarbetet genom individuellt författande av ett reflektionsdokument.")
                 .descriptionEn("Requirement for 1 point: that the ability to reflect about the thesis work has been demonstrated through the individual writing of a reflection document.")
                 .build());
-        gradingReportTemplate.addIndividualCriterion("Ö6 Reflektion", "Ö6 Reflection", 0, gradingCriterionPointTemplates).setFx(false);
+        gradingReportTemplate.addIndividualCriterion("Ö6 Reflektion", "Ö6 Reflection", 0, gradingCriterionPointTemplates, AbstractGradingCriterion.Flag.REFLECTION).setFx(false);
 
         return gradingReportTemplate;
     }
 
-    private GradingReportTemplate getMasterTemplate(int credits) {
-        GradingReportTemplate gradingReportTemplateMaster = new GradingReportTemplate(masterClass, credits);
+    private GradingReportTemplate getMasterTemplate() {
+        GradingReportTemplate gradingReportTemplateMaster = new GradingReportTemplate(masterClass,
+                LocalDate.of(2024, Month.JANUARY, 1));
 
         List<GradingCriterionPointTemplate> gradingCriterionPointTemplates = initPointTemplates();
         gradingCriterionPointTemplates.add(new GradingCriterionPointTemplate.Builder()
@@ -629,7 +633,8 @@ public class DataInitializer implements Lifecycle {
                 .description("För 2 poäng krävs dessutom: att oppositionsrapporten ingående och välbalanserat beskriver styrkor och svagheter hos det utvärderade arbetet ur flera aspekter samt att den innehåller tydliga och välmotiverade förslag till förbättringar.")
                 .descriptionEn("For 2 points the following is also required: that the opposition report thoroughly and in a well-balanced way describes from numerous aspects the strengths and weaknesses of the evaluated thesis and that it offers clear and well-motivated suggestions for improvements.")
                 .build());
-        gradingReportTemplateMaster.addIndividualCriterion("Ö1 Oppositionsrapport", "Ö1 Opposition report", 1, gradingCriterionPointTemplates);
+        gradingReportTemplateMaster.addIndividualCriterion("Ö1 Oppositionsrapport", "Ö1 Opposition report", 1, gradingCriterionPointTemplates,
+                AbstractGradingCriterion.Flag.OPPOSITION);
 
         gradingCriterionPointTemplates = initPointTemplates();
         gradingCriterionPointTemplates.add(new GradingCriterionPointTemplate.Builder()
@@ -669,13 +674,14 @@ public class DataInitializer implements Lifecycle {
                 .description("För 1 poäng krävs: att förmåga har uppvisats att reflektera över det genomförda examensarbetet genom individuellt författande av ett reflektionsdokument.")
                 .descriptionEn("Requirement for 1 point: that the ability to reflect about the thesis work has been demonstrated through the individual writing of a reflection document.")
                 .build());
-        gradingReportTemplateMaster.addIndividualCriterion("Ö6 Reflektion", "Ö6 Reflection", 1, gradingCriterionPointTemplates).setFx(false);
+        gradingReportTemplateMaster.addIndividualCriterion("Ö6 Reflektion", "Ö6 Reflection", 1, gradingCriterionPointTemplates, AbstractGradingCriterion.Flag.REFLECTION).setFx(false);
 
         return gradingReportTemplateMaster;
     }
 
-    private GradingReportTemplate getMagisterTemplate(int credits) {
-        GradingReportTemplate gradingReportTemplateMagister = new GradingReportTemplate(magisterClass, credits);
+    private GradingReportTemplate getMagisterTemplate() {
+        GradingReportTemplate gradingReportTemplateMagister = new GradingReportTemplate(magisterClass,
+                LocalDate.of(2024, Month.JANUARY, 1));
 
         List<GradingCriterionPointTemplate> gradingCriterionPointTemplates = initPointTemplates();
         gradingCriterionPointTemplates.add(new GradingCriterionPointTemplate.Builder()
@@ -837,7 +843,8 @@ public class DataInitializer implements Lifecycle {
                 .description("För 2 poäng krävs dessutom: att oppositionsrapporten ingående och välbalanserat beskriver styrkor och svagheter hos det utvärderade arbetet ur flera aspekter samt att den innehåller tydliga och välmotiverade förslag till förbättringar.")
                 .descriptionEn("For 2 points the following is also required: that the opposition report thoroughly and in a well-balanced way describes from numerous aspects the strengths and weaknesses of the evaluated thesis and that it offers clear and well-motivated suggestions for improvements.")
                 .build());
-        gradingReportTemplateMagister.addIndividualCriterion("Ö1 Oppositionsrapport", "Ö1 Opposition report", 1, gradingCriterionPointTemplates);
+        gradingReportTemplateMagister.addIndividualCriterion("Ö1 Oppositionsrapport", "Ö1 Opposition report", 1, gradingCriterionPointTemplates,
+                AbstractGradingCriterion.Flag.OPPOSITION);
 
         gradingCriterionPointTemplates = initPointTemplates();
         gradingCriterionPointTemplates.add(new GradingCriterionPointTemplate.Builder()
@@ -877,7 +884,7 @@ public class DataInitializer implements Lifecycle {
                 .description("För 1 poäng krävs: att förmåga har uppvisats att reflektera över det genomförda examensarbetet genom individuellt författande av ett reflektionsdokument.")
                 .descriptionEn("Requirement for 1 point: that the ability to reflect about the thesis work has been demonstrated through the individual writing of a reflection document.")
                 .build());
-        gradingReportTemplateMagister.addIndividualCriterion("Ö6 Reflektion", "Ö6 Reflection", 1, gradingCriterionPointTemplates).setFx(false);
+        gradingReportTemplateMagister.addIndividualCriterion("Ö6 Reflektion", "Ö6 Reflection", 1, gradingCriterionPointTemplates, AbstractGradingCriterion.Flag.REFLECTION).setFx(false);
 
         return gradingReportTemplateMagister;
     }
diff --git a/core/src/main/java/se/su/dsv/scipro/activityplan/ActivityPlanFacade.java b/core/src/main/java/se/su/dsv/scipro/activityplan/ActivityPlanFacade.java
index 0dc2b84a36..a4ec749f1a 100644
--- a/core/src/main/java/se/su/dsv/scipro/activityplan/ActivityPlanFacade.java
+++ b/core/src/main/java/se/su/dsv/scipro/activityplan/ActivityPlanFacade.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.activityplan;
 
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.checklist.Checklist;
 import se.su.dsv.scipro.checklist.ChecklistTemplate;
 import se.su.dsv.scipro.file.ProjectFileUpload;
diff --git a/core/src/main/java/se/su/dsv/scipro/activityplan/ActivityPlanFacadeImpl.java b/core/src/main/java/se/su/dsv/scipro/activityplan/ActivityPlanFacadeImpl.java
index 684bfdb7a7..d211c1cbac 100755
--- a/core/src/main/java/se/su/dsv/scipro/activityplan/ActivityPlanFacadeImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/activityplan/ActivityPlanFacadeImpl.java
@@ -4,7 +4,7 @@ import com.google.common.eventbus.EventBus;
 import com.google.inject.persist.Transactional;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.checklist.Checklist;
 import se.su.dsv.scipro.checklist.ChecklistAnswerEnum;
 import se.su.dsv.scipro.checklist.ChecklistCategory;
diff --git a/core/src/main/java/se/su/dsv/scipro/activityplan/ActivityPlanTemplateServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/activityplan/ActivityPlanTemplateServiceImpl.java
index 1ee2963dc9..645f1c9670 100755
--- a/core/src/main/java/se/su/dsv/scipro/activityplan/ActivityPlanTemplateServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/activityplan/ActivityPlanTemplateServiceImpl.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.activityplan;
 
 import com.querydsl.core.BooleanBuilder;
 import com.querydsl.core.types.Predicate;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.system.AbstractServiceImpl;
 import se.su.dsv.scipro.system.User;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistCategoryRepo.java b/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistCategoryRepo.java
index 58d7289f03..9b5883089e 100755
--- a/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistCategoryRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistCategoryRepo.java
@@ -1,8 +1,8 @@
 package se.su.dsv.scipro.checklist;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 
 
 @Transactional
diff --git a/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistQuestionRepo.java b/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistQuestionRepo.java
index 68f39227e1..410c90439a 100755
--- a/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistQuestionRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistQuestionRepo.java
@@ -1,8 +1,8 @@
 package se.su.dsv.scipro.checklist;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 
 
 @Transactional
diff --git a/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistTemplateServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistTemplateServiceImpl.java
index 7676f990a0..0e43b8c13d 100755
--- a/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistTemplateServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistTemplateServiceImpl.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.checklist;
 
 import com.google.inject.persist.Transactional;
 import com.querydsl.core.types.dsl.BooleanExpression;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.AbstractServiceImpl;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/file/FileDescriptionRepo.java b/core/src/main/java/se/su/dsv/scipro/file/FileDescriptionRepo.java
index 86d5d648ed..eccb9da1eb 100755
--- a/core/src/main/java/se/su/dsv/scipro/file/FileDescriptionRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/file/FileDescriptionRepo.java
@@ -1,8 +1,8 @@
 package se.su.dsv.scipro.file;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 
 
 @Transactional
diff --git a/core/src/main/java/se/su/dsv/scipro/file/ProjectFileRepository.java b/core/src/main/java/se/su/dsv/scipro/file/ProjectFileRepository.java
index 6a2a11045d..e33001f8c5 100644
--- a/core/src/main/java/se/su/dsv/scipro/file/ProjectFileRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/file/ProjectFileRepository.java
@@ -1,8 +1,8 @@
 package se.su.dsv.scipro.file;
 
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import se.su.dsv.scipro.system.Pageable;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.project.Project;
 
 import java.util.*;
diff --git a/core/src/main/java/se/su/dsv/scipro/file/ProjectFileRepositoryImpl.java b/core/src/main/java/se/su/dsv/scipro/file/ProjectFileRepositoryImpl.java
index ad3cc70445..a480a8763a 100644
--- a/core/src/main/java/se/su/dsv/scipro/file/ProjectFileRepositoryImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/file/ProjectFileRepositoryImpl.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.file;
 
 import com.querydsl.core.types.dsl.Expressions;
 import com.querydsl.jpa.impl.JPAQuery;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.AbstractServiceImpl;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/file/ProjectFileService.java b/core/src/main/java/se/su/dsv/scipro/file/ProjectFileService.java
index bb67b85b8a..b9dc4a2fb6 100644
--- a/core/src/main/java/se/su/dsv/scipro/file/ProjectFileService.java
+++ b/core/src/main/java/se/su/dsv/scipro/file/ProjectFileService.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.file;
 
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.project.Project;
 
 import java.util.*;
diff --git a/core/src/main/java/se/su/dsv/scipro/file/ProjectFileServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/file/ProjectFileServiceImpl.java
index 7e513b2434..e995fd1831 100644
--- a/core/src/main/java/se/su/dsv/scipro/file/ProjectFileServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/file/ProjectFileServiceImpl.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.file;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.project.Project;
 
 import jakarta.inject.Inject;
diff --git a/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarOppositionRepo.java b/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarOppositionRepo.java
index 16c32a0f2c..7def75b680 100755
--- a/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarOppositionRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarOppositionRepo.java
@@ -1,8 +1,8 @@
 package se.su.dsv.scipro.finalseminar;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.system.ProjectType;
 import se.su.dsv.scipro.system.User;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarService.java b/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarService.java
index 86115973dd..b39387d3c1 100755
--- a/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarService.java
+++ b/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarService.java
@@ -1,7 +1,7 @@
 
 package se.su.dsv.scipro.finalseminar;
 
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.*;
 import se.su.dsv.scipro.util.Either;
diff --git a/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarServiceImpl.java
index 8f1a0a9a85..e3510ddfae 100755
--- a/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarServiceImpl.java
@@ -5,7 +5,7 @@ import com.google.inject.persist.Transactional;
 import com.querydsl.core.BooleanBuilder;
 import com.querydsl.core.types.dsl.BooleanExpression;
 import jakarta.persistence.EntityManager;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.file.FileReference;
 import se.su.dsv.scipro.file.FileService;
 import se.su.dsv.scipro.misc.DaysService;
diff --git a/core/src/main/java/se/su/dsv/scipro/finalthesis/FinalThesisServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/finalthesis/FinalThesisServiceImpl.java
index 00d6898a6f..98b775c07b 100644
--- a/core/src/main/java/se/su/dsv/scipro/finalthesis/FinalThesisServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/finalthesis/FinalThesisServiceImpl.java
@@ -5,7 +5,7 @@ import com.google.inject.persist.Transactional;
 import com.querydsl.core.BooleanBuilder;
 import com.querydsl.core.types.Predicate;
 import jakarta.persistence.EntityManager;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.file.FileDescription;
 import se.su.dsv.scipro.file.FileReference;
 import se.su.dsv.scipro.file.FileService;
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/AbstractThreadRepository.java b/core/src/main/java/se/su/dsv/scipro/forum/AbstractThreadRepository.java
index 2dc9bba97a..0395d492b0 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/AbstractThreadRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/AbstractThreadRepository.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.forum;
 
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.forum.dataobjects.ForumThread;
 
 public interface AbstractThreadRepository extends JpaRepository<ForumThread, Long>, QueryDslPredicateExecutor<ForumThread> {
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostReadStateRepository.java b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostReadStateRepository.java
index d739c2bc0c..4d385c0255 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostReadStateRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostReadStateRepository.java
@@ -1,8 +1,8 @@
 package se.su.dsv.scipro.forum;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.forum.dataobjects.ForumPost;
 import se.su.dsv.scipro.forum.dataobjects.ForumPostReadState;
 import se.su.dsv.scipro.forum.dataobjects.ForumPostReadStateId;
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java
index 7942f329c7..0b39495268 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java
@@ -1,8 +1,8 @@
 package se.su.dsv.scipro.forum;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.forum.dataobjects.ForumPost;
 import se.su.dsv.scipro.forum.dataobjects.ForumThread;
 import se.su.dsv.scipro.forum.dataobjects.ProjectThread;
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/GroupThreadRepository.java b/core/src/main/java/se/su/dsv/scipro/forum/GroupThreadRepository.java
index 679b673a89..37e0a30d31 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/GroupThreadRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/GroupThreadRepository.java
@@ -1,8 +1,8 @@
 package se.su.dsv.scipro.forum;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.forum.dataobjects.GroupThread;
 import se.su.dsv.scipro.group.Group;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ProjectThreadRepository.java b/core/src/main/java/se/su/dsv/scipro/forum/ProjectThreadRepository.java
index 5c0c61e771..b50913b764 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/ProjectThreadRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/ProjectThreadRepository.java
@@ -1,8 +1,8 @@
 package se.su.dsv.scipro.forum;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.forum.dataobjects.ProjectThread;
 import se.su.dsv.scipro.project.Project;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/grading/GradingModule.java b/core/src/main/java/se/su/dsv/scipro/grading/GradingModule.java
index 7f0448f149..e67d559b77 100644
--- a/core/src/main/java/se/su/dsv/scipro/grading/GradingModule.java
+++ b/core/src/main/java/se/su/dsv/scipro/grading/GradingModule.java
@@ -3,6 +3,7 @@ package se.su.dsv.scipro.grading;
 import com.google.inject.Key;
 import com.google.inject.PrivateModule;
 import com.google.inject.name.Names;
+import se.su.dsv.scipro.report.GradingReportServiceImpl;
 
 public class GradingModule extends PrivateModule {
     @Override
@@ -29,5 +30,8 @@ public class GradingModule extends PrivateModule {
         bind(NationalSubjectCategoryRepository.class).to(NationalSubjectCategoryRepositoryImpl.class);
         bind(NationalSubjectCategoryService.class).to(NationalSubjectCategoryServiceImpl.class);
         expose(NationalSubjectCategoryService.class);
+
+        bind(GradingReportTemplateService.class).to(GradingReportServiceImpl.class);
+        expose(GradingReportTemplateService.class);
     }
 }
diff --git a/core/src/main/java/se/su/dsv/scipro/grading/GradingReportTemplateService.java b/core/src/main/java/se/su/dsv/scipro/grading/GradingReportTemplateService.java
new file mode 100644
index 0000000000..1c23b7c0a5
--- /dev/null
+++ b/core/src/main/java/se/su/dsv/scipro/grading/GradingReportTemplateService.java
@@ -0,0 +1,41 @@
+package se.su.dsv.scipro.grading;
+
+import se.su.dsv.scipro.report.DuplicateDateException;
+import se.su.dsv.scipro.report.GradingReportTemplate;
+import se.su.dsv.scipro.report.ValidDateMustBeInTheFutureException;
+import se.su.dsv.scipro.report.NoSuchTemplateException;
+import se.su.dsv.scipro.report.TemplateLockedException;
+import se.su.dsv.scipro.system.ProjectType;
+
+import java.time.LocalDate;
+import java.util.List;
+
+public interface GradingReportTemplateService {
+    List<ProjectType> getProjectTypes();
+
+    GradingReportTemplate getCurrentTemplate(ProjectType projectType);
+
+    GradingReportTemplate getTemplate(long templateId);
+
+    /**
+     * Returns the end date of this grading report template.
+     * End date is specified as the date before another template takes over.
+     *
+     * @return the end date of this grading report template, possibly {@code null}
+     */
+    LocalDate getEndDate(GradingReportTemplate gradingReportTemplate);
+
+    List<GradingReportTemplate> getUpcomingTemplates(ProjectType projectType);
+
+    GradingReportTemplate update(long templateId, GradingReportTemplateUpdate update)
+            throws
+            ValidDateMustBeInTheFutureException,
+            NoSuchTemplateException,
+            DuplicateDateException,
+            TemplateLockedException;
+
+    GradingReportTemplate create(ProjectType projectType, GradingReportTemplateUpdate update)
+            throws
+            ValidDateMustBeInTheFutureException,
+            DuplicateDateException;
+}
diff --git a/core/src/main/java/se/su/dsv/scipro/grading/GradingReportTemplateUpdate.java b/core/src/main/java/se/su/dsv/scipro/grading/GradingReportTemplateUpdate.java
new file mode 100644
index 0000000000..ce7a1b70aa
--- /dev/null
+++ b/core/src/main/java/se/su/dsv/scipro/grading/GradingReportTemplateUpdate.java
@@ -0,0 +1,61 @@
+package se.su.dsv.scipro.grading;
+
+import jakarta.annotation.Nullable;
+
+import java.time.LocalDate;
+import java.util.List;
+import java.util.Objects;
+
+public record GradingReportTemplateUpdate(
+        LocalDate validFrom,
+        @Nullable String note,
+        String failingGrade,
+        List<GradeLimit> gradeLimits,
+        List<Criteria> criteria)
+{
+    public GradingReportTemplateUpdate {
+        Objects.requireNonNull(validFrom, "Valid from must not be null");
+        Objects.requireNonNull(failingGrade, "Failing grade must not be null");
+        Objects.requireNonNull(gradeLimits, "Grades must not be null");
+        Objects.requireNonNull(criteria, "Criteria must not be null");
+
+        for (GradeLimit gradeLimit1 : gradeLimits) {
+            for (GradeLimit gradeLimit2 : gradeLimits) {
+                if (gradeLimit1 != gradeLimit2 && gradeLimit1.minimumPoints() == gradeLimit2.minimumPoints()) {
+                    throw new IllegalArgumentException("Duplicate minimum points on grades: %s and %s".formatted(
+                            gradeLimit1.grade(),
+                            gradeLimit2.grade()));
+                }
+            }
+        }
+    }
+
+    public record GradeLimit(String grade, int minimumPoints) {
+        public GradeLimit {
+            Objects.requireNonNull(grade, "Grade must not be null");
+        }
+    }
+
+    public record Criteria(
+            LocalizedString title,
+            Type type,
+            int minimumPointsRequiredToPass,
+            @Nullable Flag flag,
+            List<Requirement> requirements)
+    {
+        public enum Type {THESIS, INDIVIDUAL}
+        public enum Flag {OPPOSITION, REFLECTION}
+
+        public Criteria {
+            Objects.requireNonNull(title, "Title must not be null");
+            Objects.requireNonNull(type, "Type must not be null");
+            Objects.requireNonNull(requirements, "Requirements must not be null");
+        }
+
+        public record Requirement(int points, LocalizedString description) {
+            public Requirement {
+                Objects.requireNonNull(description, "Description must not be null");
+            }
+        }
+    }
+}
diff --git a/core/src/main/java/se/su/dsv/scipro/grading/LocalizedString.java b/core/src/main/java/se/su/dsv/scipro/grading/LocalizedString.java
new file mode 100644
index 0000000000..cb777b5c65
--- /dev/null
+++ b/core/src/main/java/se/su/dsv/scipro/grading/LocalizedString.java
@@ -0,0 +1,10 @@
+package se.su.dsv.scipro.grading;
+
+import java.util.Objects;
+
+public record LocalizedString(String english, String swedish) {
+    public LocalizedString {
+        Objects.requireNonNull(english, "English must not be null");
+        Objects.requireNonNull(swedish, "Swedish must not be null");
+    }
+}
diff --git a/core/src/main/java/se/su/dsv/scipro/group/GroupServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/group/GroupServiceImpl.java
index d23f909f5e..0eae8d41e7 100755
--- a/core/src/main/java/se/su/dsv/scipro/group/GroupServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/group/GroupServiceImpl.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.group;
 
 import com.querydsl.core.BooleanBuilder;
 import com.querydsl.core.types.dsl.BooleanExpression;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.AbstractServiceImpl;
 import se.su.dsv.scipro.system.FilteredService;
diff --git a/core/src/main/java/se/su/dsv/scipro/integration/activityfinalseminar/ActivityFinalSeminarRepository.java b/core/src/main/java/se/su/dsv/scipro/integration/activityfinalseminar/ActivityFinalSeminarRepository.java
index 0340c3cd97..87191121e6 100644
--- a/core/src/main/java/se/su/dsv/scipro/integration/activityfinalseminar/ActivityFinalSeminarRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/integration/activityfinalseminar/ActivityFinalSeminarRepository.java
@@ -1,8 +1,8 @@
 package se.su.dsv.scipro.integration.activityfinalseminar;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.finalseminar.FinalSeminar;
 
 import java.util.Optional;
diff --git a/core/src/main/java/se/su/dsv/scipro/mail/MailEventServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/mail/MailEventServiceImpl.java
index 651f3965a4..93ffe55a60 100755
--- a/core/src/main/java/se/su/dsv/scipro/mail/MailEventServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/mail/MailEventServiceImpl.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.mail;
 
 import com.querydsl.core.BooleanBuilder;
 import com.querydsl.core.types.Predicate;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.system.AbstractServiceImpl;
 
 import jakarta.inject.Inject;
diff --git a/core/src/main/java/se/su/dsv/scipro/match/ApplicationPeriodServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/match/ApplicationPeriodServiceImpl.java
index b884d5eb91..0156c88bd8 100755
--- a/core/src/main/java/se/su/dsv/scipro/match/ApplicationPeriodServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/match/ApplicationPeriodServiceImpl.java
@@ -3,8 +3,8 @@ package se.su.dsv.scipro.match;
 import com.google.inject.persist.Transactional;
 import com.querydsl.core.types.dsl.BooleanExpression;
 import com.querydsl.core.types.dsl.Expressions;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.PageRequest;
+import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.system.AbstractServiceImpl;
 import se.su.dsv.scipro.system.DegreeType;
 import se.su.dsv.scipro.system.ProjectType;
diff --git a/core/src/main/java/se/su/dsv/scipro/match/FirstMeetingRepository.java b/core/src/main/java/se/su/dsv/scipro/match/FirstMeetingRepository.java
index 03fb11c3f8..7714a267a9 100755
--- a/core/src/main/java/se/su/dsv/scipro/match/FirstMeetingRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/match/FirstMeetingRepository.java
@@ -1,8 +1,8 @@
 package se.su.dsv.scipro.match;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 
 
 @Transactional
diff --git a/core/src/main/java/se/su/dsv/scipro/match/IdeaFacade.java b/core/src/main/java/se/su/dsv/scipro/match/IdeaFacade.java
index 3127261425..d3dc01f86a 100644
--- a/core/src/main/java/se/su/dsv/scipro/match/IdeaFacade.java
+++ b/core/src/main/java/se/su/dsv/scipro/match/IdeaFacade.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.match;
 
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.system.ProjectType;
 import se.su.dsv.scipro.system.User;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/match/IdeaService.java b/core/src/main/java/se/su/dsv/scipro/match/IdeaService.java
index dae917448e..b45a921ea6 100755
--- a/core/src/main/java/se/su/dsv/scipro/match/IdeaService.java
+++ b/core/src/main/java/se/su/dsv/scipro/match/IdeaService.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.match;
 
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.match.Idea.Status;
 import se.su.dsv.scipro.match.Idea.Type;
 import se.su.dsv.scipro.system.*;
diff --git a/core/src/main/java/se/su/dsv/scipro/match/IdeaServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/match/IdeaServiceImpl.java
index fdeae929fa..07a468f894 100755
--- a/core/src/main/java/se/su/dsv/scipro/match/IdeaServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/match/IdeaServiceImpl.java
@@ -9,7 +9,7 @@ import com.querydsl.core.types.dsl.DateTimeExpression;
 import com.querydsl.jpa.JPAExpressions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.data.dataobjects.Member;
 import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettings;
 import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService;
diff --git a/core/src/main/java/se/su/dsv/scipro/match/KeywordService.java b/core/src/main/java/se/su/dsv/scipro/match/KeywordService.java
index 867d5accbb..012726c891 100755
--- a/core/src/main/java/se/su/dsv/scipro/match/KeywordService.java
+++ b/core/src/main/java/se/su/dsv/scipro/match/KeywordService.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.match;
 
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.system.GenericService;
 import se.su.dsv.scipro.system.ResearchArea;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/match/KeywordServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/match/KeywordServiceImpl.java
index d68ddf4ff2..44c702b1aa 100755
--- a/core/src/main/java/se/su/dsv/scipro/match/KeywordServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/match/KeywordServiceImpl.java
@@ -4,9 +4,9 @@ import com.google.inject.persist.Transactional;
 import com.querydsl.core.types.dsl.BooleanExpression;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.PageRequest;
+import se.su.dsv.scipro.system.Pageable;
+import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.system.AbstractServiceImpl;
 import se.su.dsv.scipro.system.ResearchArea;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/match/MatchFollowUpService.java b/core/src/main/java/se/su/dsv/scipro/match/MatchFollowUpService.java
index bbb8bf4e6b..93ab5c5a97 100644
--- a/core/src/main/java/se/su/dsv/scipro/match/MatchFollowUpService.java
+++ b/core/src/main/java/se/su/dsv/scipro/match/MatchFollowUpService.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.match;
 
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.system.User;
 
 import java.util.Set;
diff --git a/core/src/main/java/se/su/dsv/scipro/match/MatchFollowUpServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/match/MatchFollowUpServiceImpl.java
index fb0f451e48..724cbefa0b 100644
--- a/core/src/main/java/se/su/dsv/scipro/match/MatchFollowUpServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/match/MatchFollowUpServiceImpl.java
@@ -8,7 +8,7 @@ import com.querydsl.core.types.dsl.StringPath;
 import com.querydsl.jpa.JPAExpressions;
 import com.querydsl.jpa.JPQLQuery;
 import com.querydsl.jpa.impl.JPAQuery;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.system.User;
 
 import jakarta.inject.Inject;
diff --git a/core/src/main/java/se/su/dsv/scipro/match/MatchService.java b/core/src/main/java/se/su/dsv/scipro/match/MatchService.java
index c36a6d53f5..8710682219 100755
--- a/core/src/main/java/se/su/dsv/scipro/match/MatchService.java
+++ b/core/src/main/java/se/su/dsv/scipro/match/MatchService.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.match;
 
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.system.GenericService;
 
 public interface MatchService extends GenericService<Match, Long> {
diff --git a/core/src/main/java/se/su/dsv/scipro/match/MatchServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/match/MatchServiceImpl.java
index d6d82c98e0..dcdb6cbcbd 100755
--- a/core/src/main/java/se/su/dsv/scipro/match/MatchServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/match/MatchServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.match;
 
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.system.AbstractServiceImpl;
 
 import jakarta.inject.Inject;
diff --git a/core/src/main/java/se/su/dsv/scipro/match/ProgramServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/match/ProgramServiceImpl.java
index ebfccfd272..e759f53f93 100644
--- a/core/src/main/java/se/su/dsv/scipro/match/ProgramServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/match/ProgramServiceImpl.java
@@ -2,8 +2,8 @@ package se.su.dsv.scipro.match;
 
 import com.google.inject.persist.Transactional;
 import com.querydsl.core.types.dsl.BooleanExpression;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.PageRequest;
+import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.system.AbstractServiceImpl;
 import se.su.dsv.scipro.system.Program;
 import se.su.dsv.scipro.system.QProgram;
diff --git a/core/src/main/java/se/su/dsv/scipro/milestones/MilestoneActivityTemplateRepository.java b/core/src/main/java/se/su/dsv/scipro/milestones/MilestoneActivityTemplateRepository.java
index 589b0e5985..e43bb892e8 100644
--- a/core/src/main/java/se/su/dsv/scipro/milestones/MilestoneActivityTemplateRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/milestones/MilestoneActivityTemplateRepository.java
@@ -1,8 +1,8 @@
 package se.su.dsv.scipro.milestones;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.milestones.dataobjects.MilestoneActivityTemplate;
 import se.su.dsv.scipro.milestones.dataobjects.MilestonePhaseTemplate;
 import se.su.dsv.scipro.system.ProjectType;
diff --git a/core/src/main/java/se/su/dsv/scipro/milestones/service/MileStoneService.java b/core/src/main/java/se/su/dsv/scipro/milestones/service/MileStoneService.java
index 9fc24f8c34..3765b1e798 100644
--- a/core/src/main/java/se/su/dsv/scipro/milestones/service/MileStoneService.java
+++ b/core/src/main/java/se/su/dsv/scipro/milestones/service/MileStoneService.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.milestones.service;
 
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.milestones.dataobjects.Milestone;
 import se.su.dsv.scipro.milestones.dataobjects.MilestoneActivityTemplate;
 import se.su.dsv.scipro.project.Project;
diff --git a/core/src/main/java/se/su/dsv/scipro/milestones/service/MilestonePhaseTemplateService.java b/core/src/main/java/se/su/dsv/scipro/milestones/service/MilestonePhaseTemplateService.java
index 271731aa62..78afe052b7 100644
--- a/core/src/main/java/se/su/dsv/scipro/milestones/service/MilestonePhaseTemplateService.java
+++ b/core/src/main/java/se/su/dsv/scipro/milestones/service/MilestonePhaseTemplateService.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.milestones.service;
 
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.milestones.dataobjects.MilestonePhaseTemplate;
 import se.su.dsv.scipro.springdata.SortOrderService;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/milestones/service/MilestoneStatisticsService.java b/core/src/main/java/se/su/dsv/scipro/milestones/service/MilestoneStatisticsService.java
index 2f69457b45..abc4841d51 100644
--- a/core/src/main/java/se/su/dsv/scipro/milestones/service/MilestoneStatisticsService.java
+++ b/core/src/main/java/se/su/dsv/scipro/milestones/service/MilestoneStatisticsService.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.milestones.service;
 
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.milestones.dataobjects.MilestoneActivityTemplate;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.project.ProjectStatus;
diff --git a/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestonePhaseTemplateServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestonePhaseTemplateServiceImpl.java
index b4edc5af32..f5bd91a316 100644
--- a/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestonePhaseTemplateServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestonePhaseTemplateServiceImpl.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.milestones.service.impl;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.milestones.dataobjects.MilestonePhaseTemplate;
 import se.su.dsv.scipro.milestones.dataobjects.QMilestonePhaseTemplate;
 import se.su.dsv.scipro.milestones.service.MilestonePhaseTemplateService;
diff --git a/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestoneServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestoneServiceImpl.java
index 5c335b94b0..70c69ddef2 100644
--- a/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestoneServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestoneServiceImpl.java
@@ -4,7 +4,7 @@ import com.querydsl.core.BooleanBuilder;
 import com.querydsl.core.types.Predicate;
 import com.querydsl.core.types.dsl.BooleanExpression;
 import com.querydsl.jpa.impl.JPAQuery;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.milestones.dataobjects.Milestone;
 import se.su.dsv.scipro.milestones.dataobjects.MilestoneActivityTemplate;
 import se.su.dsv.scipro.milestones.dataobjects.QMilestone;
diff --git a/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestoneStatisticsServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestoneStatisticsServiceImpl.java
index f24e7a37e4..93a1815dce 100644
--- a/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestoneStatisticsServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestoneStatisticsServiceImpl.java
@@ -8,8 +8,8 @@ import com.querydsl.core.types.dsl.BooleanExpression;
 import com.querydsl.core.types.dsl.Expressions;
 import com.querydsl.core.types.dsl.StringPath;
 import com.querydsl.jpa.impl.JPAQuery;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.Pageable;
+import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.milestones.dataobjects.Milestone;
 import se.su.dsv.scipro.milestones.dataobjects.MilestoneActivityTemplate;
 import se.su.dsv.scipro.milestones.dataobjects.QMilestone;
diff --git a/core/src/main/java/se/su/dsv/scipro/notifications/NotificationServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/notifications/NotificationServiceImpl.java
index b8c7cb0d9a..826991801f 100755
--- a/core/src/main/java/se/su/dsv/scipro/notifications/NotificationServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/notifications/NotificationServiceImpl.java
@@ -3,7 +3,7 @@ package se.su.dsv.scipro.notifications;
 import com.google.inject.persist.Transactional;
 import com.querydsl.core.BooleanBuilder;
 import com.querydsl.core.types.Predicate;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.notifications.dataobject.Notification;
 import se.su.dsv.scipro.notifications.dataobject.NotificationEvent;
 import se.su.dsv.scipro.notifications.dataobject.QNotification;
diff --git a/core/src/main/java/se/su/dsv/scipro/peer/CommentThreadRepo.java b/core/src/main/java/se/su/dsv/scipro/peer/CommentThreadRepo.java
index cc3d7b2e91..025bb02da1 100644
--- a/core/src/main/java/se/su/dsv/scipro/peer/CommentThreadRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/peer/CommentThreadRepo.java
@@ -1,8 +1,8 @@
 package se.su.dsv.scipro.peer;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 
 
 @Transactional
diff --git a/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestRepository.java b/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestRepository.java
index 61719ce2f3..d6ca147c68 100755
--- a/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestRepository.java
@@ -1,8 +1,8 @@
 package se.su.dsv.scipro.peer;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 
 @Transactional
 public interface PeerRequestRepository extends JpaRepository<PeerRequest, Long>, QueryDslPredicateExecutor<PeerRequest> {
diff --git a/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestService.java b/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestService.java
index 0a3b89cb2f..5199cdd733 100755
--- a/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestService.java
+++ b/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestService.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.peer;
 
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.*;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestServiceImpl.java
index d4ad19ea87..b79394efd6 100755
--- a/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestServiceImpl.java
@@ -4,7 +4,7 @@ import com.google.common.eventbus.EventBus;
 import com.google.inject.persist.Transactional;
 import com.querydsl.core.BooleanBuilder;
 import com.querydsl.core.types.dsl.BooleanExpression;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.file.FileReference;
 import se.su.dsv.scipro.file.FileService;
 import se.su.dsv.scipro.project.Project;
diff --git a/core/src/main/java/se/su/dsv/scipro/peer/PeerReviewRepository.java b/core/src/main/java/se/su/dsv/scipro/peer/PeerReviewRepository.java
index ee3070c329..8ae4a8cfc9 100755
--- a/core/src/main/java/se/su/dsv/scipro/peer/PeerReviewRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/peer/PeerReviewRepository.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.peer;
 
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.system.ProjectType;
 import se.su.dsv.scipro.system.User;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/peer/PeerReviewService.java b/core/src/main/java/se/su/dsv/scipro/peer/PeerReviewService.java
index 49cfaa8bb3..4434bf4477 100755
--- a/core/src/main/java/se/su/dsv/scipro/peer/PeerReviewService.java
+++ b/core/src/main/java/se/su/dsv/scipro/peer/PeerReviewService.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.peer;
 
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.*;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/peer/PeerReviewServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/peer/PeerReviewServiceImpl.java
index b75239494b..24d9eac359 100755
--- a/core/src/main/java/se/su/dsv/scipro/peer/PeerReviewServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/peer/PeerReviewServiceImpl.java
@@ -3,7 +3,7 @@ package se.su.dsv.scipro.peer;
 import com.querydsl.core.BooleanBuilder;
 import com.querydsl.core.types.Predicate;
 import com.querydsl.core.types.dsl.BooleanExpression;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.AbstractServiceImpl;
 import se.su.dsv.scipro.system.Language;
diff --git a/core/src/main/java/se/su/dsv/scipro/plagiarism/PlagiarismRequestRepository.java b/core/src/main/java/se/su/dsv/scipro/plagiarism/PlagiarismRequestRepository.java
index eb367907dc..cf8334dba7 100644
--- a/core/src/main/java/se/su/dsv/scipro/plagiarism/PlagiarismRequestRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/plagiarism/PlagiarismRequestRepository.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.plagiarism;
 
-import org.springframework.data.jpa.repository.JpaRepository;
+import se.su.dsv.scipro.system.JpaRepository;
 import se.su.dsv.scipro.file.FileDescription;
 
 import java.util.Optional;
diff --git a/core/src/main/java/se/su/dsv/scipro/project/Project.java b/core/src/main/java/se/su/dsv/scipro/project/Project.java
index 92c82a9014..9429d2cbfc 100755
--- a/core/src/main/java/se/su/dsv/scipro/project/Project.java
+++ b/core/src/main/java/se/su/dsv/scipro/project/Project.java
@@ -85,6 +85,13 @@ public class Project extends DomainObject {
     @Enumerated(EnumType.STRING)
     private Language language;
 
+    @ElementCollection(fetch = FetchType.LAZY)
+    @CollectionTable(name = "project_user_note", joinColumns = @JoinColumn(name = "project_id"))
+    @Column(name = "note")
+    @SuppressWarnings("JpaDataSourceORMInspection")  // false warning from IntelliJ for the @MapKeyJoinColumn
+    @MapKeyJoinColumn(name = "user_id")
+    private Map<User, String> userNotes = new HashMap<>();
+
     @PrePersist
     @PreUpdate
     void cleanTitle() {
@@ -94,6 +101,14 @@ public class Project extends DomainObject {
         title = title.trim();
     }
 
+    public Map<User, String> getUserNotes() {
+        return userNotes;
+    }
+
+    public void setUserNotes(Map<User, String> userNotes) {
+        this.userNotes = userNotes;
+    }
+
     public boolean isFinalSeminarRuleExempted() {
         return finalSeminarRuleExempted;
     }
diff --git a/core/src/main/java/se/su/dsv/scipro/project/ProjectNoteService.java b/core/src/main/java/se/su/dsv/scipro/project/ProjectNoteService.java
new file mode 100644
index 0000000000..7ff1657d63
--- /dev/null
+++ b/core/src/main/java/se/su/dsv/scipro/project/ProjectNoteService.java
@@ -0,0 +1,9 @@
+package se.su.dsv.scipro.project;
+
+import se.su.dsv.scipro.system.User;
+
+public interface ProjectNoteService {
+    String getUserNote(Project project, User user);
+
+    void setUserNote(Project project, User user, String note);
+}
diff --git a/core/src/main/java/se/su/dsv/scipro/project/ProjectRepo.java b/core/src/main/java/se/su/dsv/scipro/project/ProjectRepo.java
index 69dcda672d..43892156ca 100755
--- a/core/src/main/java/se/su/dsv/scipro/project/ProjectRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/project/ProjectRepo.java
@@ -1,8 +1,8 @@
 package se.su.dsv.scipro.project;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.system.User;
 
 import java.util.Collection;
@@ -11,4 +11,8 @@ import java.util.List;
 @Transactional
 public interface ProjectRepo extends JpaRepository<Project, Long>, QueryDslPredicateExecutor<Project> {
     List<User> findMultipleAuthors(Collection<Project> projects);
+
+    String getUserNoteForProject(Project project, User user);
+
+    void setUserNoteForProject(Project project, User user, String note);
 }
\ No newline at end of file
diff --git a/core/src/main/java/se/su/dsv/scipro/project/ProjectRepoImpl.java b/core/src/main/java/se/su/dsv/scipro/project/ProjectRepoImpl.java
index 627af554ec..f8dfb462bd 100644
--- a/core/src/main/java/se/su/dsv/scipro/project/ProjectRepoImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/project/ProjectRepoImpl.java
@@ -1,5 +1,6 @@
 package se.su.dsv.scipro.project;
 
+import com.google.inject.persist.Transactional;
 import se.su.dsv.scipro.system.GenericRepo;
 import se.su.dsv.scipro.system.User;
 
@@ -30,4 +31,16 @@ public class ProjectRepoImpl extends GenericRepo<Project, Long> implements Proje
         query.setParameter("projects", projects);
         return query.getResultList();
     }
+
+    @Override
+    public String getUserNoteForProject(Project project, User user) {
+        return project.getUserNotes().get(user);
+    }
+
+    @Override
+    @Transactional
+    public void setUserNoteForProject(Project project, User user, String note) {
+        project.getUserNotes().put(user, note);
+        save(project);
+    }
 }
diff --git a/core/src/main/java/se/su/dsv/scipro/project/ProjectServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/project/ProjectServiceImpl.java
index c2f8e83098..617f39e795 100755
--- a/core/src/main/java/se/su/dsv/scipro/project/ProjectServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/project/ProjectServiceImpl.java
@@ -5,7 +5,7 @@ import com.google.inject.persist.Transactional;
 import com.querydsl.core.BooleanBuilder;
 import com.querydsl.core.types.Predicate;
 import com.querydsl.core.types.dsl.BooleanExpression;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.data.dataobjects.Member;
 import se.su.dsv.scipro.reusable.SciProUtilities;
 import se.su.dsv.scipro.system.AbstractServiceImpl;
@@ -22,7 +22,7 @@ import java.time.Duration;
 import java.time.Instant;
 import java.util.*;
 
-public class ProjectServiceImpl extends AbstractServiceImpl<Project, Long> implements ProjectService {
+public class ProjectServiceImpl extends AbstractServiceImpl<Project, Long> implements ProjectService, ProjectNoteService {
 
     public static final int MIN_TITLE_LENGTH = 3;
     private final ProjectRepo projectRepo;
@@ -175,6 +175,16 @@ public class ProjectServiceImpl extends AbstractServiceImpl<Project, Long> imple
         return completed;
     }
 
+    @Override
+    public String getUserNote(Project project, User user) {
+        return projectRepo.getUserNoteForProject(project, user);
+    }
+
+    @Override
+    public void setUserNote(Project project, User user, String note) {
+        projectRepo.setUserNoteForProject(project, user, note);
+    }
+
     @Override
     public List<Project> findAll(Filter filter, Pageable pageable) {
         return findAll(toPredicate(filter), pageable);
diff --git a/core/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerService.java b/core/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerService.java
index c5341a337f..ce33acbc94 100644
--- a/core/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerService.java
+++ b/core/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerService.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.projectpartner;
 
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.match.ApplicationPeriod;
 import se.su.dsv.scipro.system.GenericService;
 import se.su.dsv.scipro.system.ProjectType;
diff --git a/core/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerServiceImpl.java
index cc4cb5a635..3b39364a33 100644
--- a/core/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerServiceImpl.java
@@ -3,7 +3,7 @@ package se.su.dsv.scipro.projectpartner;
 import com.google.inject.persist.Transactional;
 import com.querydsl.core.types.dsl.Expressions;
 import com.querydsl.jpa.JPAExpressions;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.match.ApplicationPeriod;
 import se.su.dsv.scipro.match.QIdea;
 import se.su.dsv.scipro.match.QIdeaParticipation;
diff --git a/core/src/main/java/se/su/dsv/scipro/report/AbstractGradingCriterion.java b/core/src/main/java/se/su/dsv/scipro/report/AbstractGradingCriterion.java
index cda224a3b7..64d2f0433b 100644
--- a/core/src/main/java/se/su/dsv/scipro/report/AbstractGradingCriterion.java
+++ b/core/src/main/java/se/su/dsv/scipro/report/AbstractGradingCriterion.java
@@ -1,17 +1,42 @@
 package se.su.dsv.scipro.report;
 
 import jakarta.persistence.Basic;
+import jakarta.persistence.Column;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
 import jakarta.persistence.MappedSuperclass;
 
 @MappedSuperclass
 public abstract class AbstractGradingCriterion extends AbstractCriterion {
 
+    public enum Flag {
+        /**
+         * Criterion marked with this flag will add extra functionality related
+         * to the authors submitted reflection. It should only be used on
+         * individual criteria.
+         */
+        REFLECTION,
+        /**
+         * Criterion marked with this flag will add extra functionality related
+         * to the authors performed opposition. For example, it will have its
+         * points and feedback filled in when the seminar supervisor assesses
+         * their opposition and submitted report. It should only be used on
+         * individual criteria.
+         */
+        OPPOSITION
+    }
+
     @Basic(optional = false)
     protected int pointsRequiredToPass;
 
     @Basic
     private boolean fx = true;
 
+    @Basic
+    @Column(name = "flag")
+    @Enumerated(EnumType.STRING)
+    private Flag flag;
+
     protected AbstractGradingCriterion() {
 
     }
@@ -21,6 +46,17 @@ public abstract class AbstractGradingCriterion extends AbstractCriterion {
         this.pointsRequiredToPass = pointsRequiredToPass;
     }
 
+    protected AbstractGradingCriterion(
+            String title,
+            String titleEn,
+            Integer sortOrder,
+            int pointsRequiredToPass,
+            Flag flag)
+    {
+        this(title, titleEn, sortOrder, pointsRequiredToPass);
+        this.flag = flag;
+    }
+
     public abstract boolean isProjectCriterion();
 
     public abstract boolean isIndividualCriterion();
@@ -35,6 +71,14 @@ public abstract class AbstractGradingCriterion extends AbstractCriterion {
         return this.fx;
     }
 
+    public Flag getFlag() {
+        return flag;
+    }
+
+    public void setFlag(Flag flag) {
+        this.flag = flag;
+    }
+
     @Override
     public boolean equals(final Object o) {
         if (o == this) return true;
diff --git a/core/src/main/java/se/su/dsv/scipro/report/DuplicateDateException.java b/core/src/main/java/se/su/dsv/scipro/report/DuplicateDateException.java
new file mode 100644
index 0000000000..1957362903
--- /dev/null
+++ b/core/src/main/java/se/su/dsv/scipro/report/DuplicateDateException.java
@@ -0,0 +1,23 @@
+package se.su.dsv.scipro.report;
+
+import se.su.dsv.scipro.system.ProjectType;
+
+import java.time.LocalDate;
+
+public class DuplicateDateException extends Exception {
+    private final LocalDate validFrom;
+    private final ProjectType projectType;
+
+    public DuplicateDateException(LocalDate validFrom, ProjectType projectType) {
+        this.validFrom = validFrom;
+        this.projectType = projectType;
+    }
+
+    public LocalDate validFrom() {
+        return validFrom;
+    }
+
+    public ProjectType projectType() {
+        return projectType;
+    }
+}
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/GradeLimit.java b/core/src/main/java/se/su/dsv/scipro/report/GradeLimit.java
new file mode 100644
index 0000000000..49d6b60097
--- /dev/null
+++ b/core/src/main/java/se/su/dsv/scipro/report/GradeLimit.java
@@ -0,0 +1,46 @@
+package se.su.dsv.scipro.report;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+
+@Entity
+@Table(name = "grading_report_template_grade_limits")
+public class GradeLimit {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @Column(name = "grade")
+    private String grade;
+
+    @Column(name = "lower_limit")
+    private int lowerLimit;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getGrade() {
+        return grade;
+    }
+
+    public void setGrade(String grade) {
+        this.grade = grade;
+    }
+
+    public int getLowerLimit() {
+        return lowerLimit;
+    }
+
+    public void setLowerLimit(int lowerLimit) {
+        this.lowerLimit = lowerLimit;
+    }
+}
diff --git a/core/src/main/java/se/su/dsv/scipro/report/GradingCriterion.java b/core/src/main/java/se/su/dsv/scipro/report/GradingCriterion.java
index d54ff820ad..aad898db4c 100644
--- a/core/src/main/java/se/su/dsv/scipro/report/GradingCriterion.java
+++ b/core/src/main/java/se/su/dsv/scipro/report/GradingCriterion.java
@@ -30,7 +30,7 @@ public abstract class GradingCriterion extends AbstractGradingCriterion {
     }
 
     GradingCriterion(GradingReport gradingReport, GradingCriterionTemplate gradingCriterionTemplate) {
-        super(gradingCriterionTemplate.getTitle(), gradingCriterionTemplate.getTitleEn(), gradingCriterionTemplate.getSortOrder(), gradingCriterionTemplate.getPointsRequiredToPass());
+        super(gradingCriterionTemplate.getTitle(), gradingCriterionTemplate.getTitleEn(), gradingCriterionTemplate.getSortOrder(), gradingCriterionTemplate.getPointsRequiredToPass(), gradingCriterionTemplate.getFlag());
         this.gradingReport = gradingReport;
         for (GradingCriterionPointTemplate pointTemplate : gradingCriterionTemplate.getGradingCriterionPointTemplates()) {
             gradingCriterionPoints.add(new GradingCriterionPoint(
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/GradingReportService.java b/core/src/main/java/se/su/dsv/scipro/report/GradingReportService.java
index a2c1ac5c4e..c82432c25e 100644
--- a/core/src/main/java/se/su/dsv/scipro/report/GradingReportService.java
+++ b/core/src/main/java/se/su/dsv/scipro/report/GradingReportService.java
@@ -3,14 +3,13 @@ package se.su.dsv.scipro.report;
 import se.su.dsv.scipro.finalseminar.FinalSeminarOpposition;
 import se.su.dsv.scipro.grading.GradingBasis;
 import se.su.dsv.scipro.project.Project;
-import se.su.dsv.scipro.system.GenericService;
 import se.su.dsv.scipro.system.User;
 import se.su.dsv.scipro.util.Either;
 
 import java.time.Instant;
 import java.util.List;
 
-public interface GradingReportService extends GenericService<GradingReport, Long> {
+public interface GradingReportService {
 
     SupervisorGradingReport getSupervisorGradingReport(Project project, User student);
 
diff --git a/core/src/main/java/se/su/dsv/scipro/report/GradingReportServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/report/GradingReportServiceImpl.java
index b57d2bee0c..6f9b1a9824 100644
--- a/core/src/main/java/se/su/dsv/scipro/report/GradingReportServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/report/GradingReportServiceImpl.java
@@ -2,54 +2,60 @@ package se.su.dsv.scipro.report;
 
 import com.google.common.eventbus.EventBus;
 import com.google.inject.persist.Transactional;
-import jakarta.persistence.EntityManager;
 import se.su.dsv.scipro.finalseminar.FinalSeminarOpposition;
 import se.su.dsv.scipro.grading.GradingBasis;
+import se.su.dsv.scipro.grading.GradingReportTemplateService;
+import se.su.dsv.scipro.grading.GradingReportTemplateUpdate;
 import se.su.dsv.scipro.grading.ThesisSubmissionHistoryService;
 import se.su.dsv.scipro.project.Project;
-import se.su.dsv.scipro.system.AbstractServiceImpl;
 import se.su.dsv.scipro.system.Language;
+import se.su.dsv.scipro.system.ProjectModule;
+import se.su.dsv.scipro.system.ProjectType;
+import se.su.dsv.scipro.system.ProjectTypeService;
 import se.su.dsv.scipro.system.User;
 import se.su.dsv.scipro.util.Either;
 
 import jakarta.inject.Inject;
-import jakarta.inject.Named;
-import jakarta.inject.Provider;
 import java.time.Clock;
 import java.time.Instant;
+import java.time.LocalDate;
 import java.util.*;
 
-@Named
-public class GradingReportServiceImpl extends AbstractServiceImpl<GradingReport, Long> implements GradingReportService {
+public class GradingReportServiceImpl implements GradingReportTemplateService, GradingReportService {
 
-    public static final String OPPOSITION_SWEDISH = "Ö1 Oppositionsrapport";
-    public static final String OPPOSITION_ENGLISH = "Ö1 Opposition report";
     private final EventBus eventBus;
     private final ThesisSubmissionHistoryService thesisSubmissionHistoryService;
     private final Clock clock;
+    private final SupervisorGradingReportRepository supervisorGradingReportRepository;
+    private final GradingReportTemplateRepo gradingReportTemplateRepo;
+    private final ProjectTypeService projectTypeService;
 
     @Inject
     public GradingReportServiceImpl(
-            Provider<EntityManager> em,
             EventBus eventBus,
             ThesisSubmissionHistoryService thesisSubmissionHistoryService,
-            Clock clock)
+            Clock clock,
+            SupervisorGradingReportRepository supervisorGradingReportRepository,
+            GradingReportTemplateRepo gradingReportTemplateRepo,
+            ProjectTypeService projectTypeService)
     {
-        super(em, GradingReport.class, QGradingReport.gradingReport);
         this.eventBus = eventBus;
         this.thesisSubmissionHistoryService = thesisSubmissionHistoryService;
         this.clock = clock;
+        this.supervisorGradingReportRepository = supervisorGradingReportRepository;
+        this.gradingReportTemplateRepo = gradingReportTemplateRepo;
+        this.projectTypeService = projectTypeService;
     }
 
     @Override
     public boolean updateOppositionCriteria(SupervisorGradingReport report, FinalSeminarOpposition opposition) {
         for (GradingCriterion gradingCriterion : report.getIndividualCriteria()) {
-            boolean isOppositionCriterion = gradingCriterion.getTitle().equals(OPPOSITION_SWEDISH) || gradingCriterion.getTitle().equals(OPPOSITION_ENGLISH);
+            boolean isOppositionCriterion = gradingCriterion.getFlag() == GradingCriterion.Flag.OPPOSITION;
             boolean betterGrade = gradingCriterion.getPoints() == null || opposition.getPoints() > gradingCriterion.getPoints();
             if (isOppositionCriterion && betterGrade) {
                 gradingCriterion.setFeedback(opposition.getFeedback());
                 gradingCriterion.setPoints(opposition.getPoints());
-                save(report);
+                supervisorGradingReportRepository.save(report);
                 return true;
             }
         }
@@ -88,7 +94,7 @@ public class GradingReportServiceImpl extends AbstractServiceImpl<GradingReport,
             }
             supervisorGradingReport.setMotivation(gradingBasis.getOverallMotivation());
             supervisorGradingReport.setRejectionCommentFeedback(gradingBasis.getRejectionCommentFeedback());
-            save(supervisorGradingReport);
+            supervisorGradingReportRepository.save(supervisorGradingReport);
         }
         return getGradingBasis(project);
     }
@@ -138,22 +144,18 @@ public class GradingReportServiceImpl extends AbstractServiceImpl<GradingReport,
     @Override
     @Transactional
     public SupervisorGradingReport getSupervisorGradingReport(Project project, User user) {
-        SupervisorGradingReport supervisorGradingReport = from(QSupervisorGradingReport.supervisorGradingReport)
-                .where(QSupervisorGradingReport.supervisorGradingReport.user.eq(user).and(
-                        QSupervisorGradingReport.supervisorGradingReport.project.eq(project)))
-                .fetchOne();
+        SupervisorGradingReport supervisorGradingReport = supervisorGradingReportRepository.getReport(project, user);
         if (supervisorGradingReport == null) {
-            supervisorGradingReport = save(getTemplate(project).createSupervisorReport(project, user));
+            GradingReportTemplate template = getTemplate(project);
+            SupervisorGradingReport supervisorReport = template.createSupervisorReport(project, user);
+            supervisorGradingReport = supervisorGradingReportRepository.save(supervisorReport);
         }
         return supervisorGradingReport;
     }
 
     @Override
     public GradingReportTemplate getTemplate(Project project) {
-        QGradingReportTemplate template = QGradingReportTemplate.gradingReportTemplate;
-        return from(template)
-                .where(template.projectType.eq(project.getProjectType()).and(template.credits.eq(project.getCredits())))
-                .fetchOne();
+        return gradingReportTemplateRepo.getTemplate(project);
     }
 
     @Override
@@ -192,11 +194,100 @@ public class GradingReportServiceImpl extends AbstractServiceImpl<GradingReport,
                     rejectionCommentFeedback);
         }
 
-        save(supervisorGradingReport);
+        supervisorGradingReportRepository.save(supervisorGradingReport);
         return Either.right(supervisorGradingReport);
     }
 
     private static boolean isBlank(final String str) {
         return str == null || str.isBlank();
     }
+
+    @Override
+    public List<ProjectType> getProjectTypes() {
+        return projectTypeService.findWithModule(ProjectModule.GRADING);
+    }
+
+    @Override
+    public GradingReportTemplate getCurrentTemplate(ProjectType projectType) {
+        return gradingReportTemplateRepo.getCurrentTemplate(projectType, LocalDate.now(clock));
+    }
+
+    @Override
+    public GradingReportTemplate getTemplate(long templateId) {
+        return gradingReportTemplateRepo.findOne(templateId);
+    }
+
+    @Override
+    public LocalDate getEndDate(GradingReportTemplate gradingReportTemplate) {
+        GradingReportTemplate next = gradingReportTemplateRepo.getNextTemplate(gradingReportTemplate);
+        if (next == null) {
+            return null;
+        } else {
+            return next.getValidFrom().minusDays(1);
+        }
+    }
+
+    @Override
+    public List<GradingReportTemplate> getUpcomingTemplates(ProjectType projectType) {
+        GradingReportTemplate current = getCurrentTemplate(projectType);
+        if (current == null) {
+            return gradingReportTemplateRepo.getTemplatesValidAfter(projectType, LocalDate.now(clock));
+        } else {
+            return gradingReportTemplateRepo.getTemplatesValidAfter(projectType, current.getValidFrom());
+        }
+    }
+
+    @Override
+    public GradingReportTemplate update(long templateId, GradingReportTemplateUpdate update)
+            throws ValidDateMustBeInTheFutureException,
+            NoSuchTemplateException,
+            DuplicateDateException,
+            TemplateLockedException
+    {
+        LocalDate today = LocalDate.now(clock);
+        if (!update.validFrom().isAfter(today)) {
+            throw new ValidDateMustBeInTheFutureException(update.validFrom(), today.plusDays(1));
+        }
+
+        GradingReportTemplate template = gradingReportTemplateRepo.getTemplateById(templateId);
+        if (template == null) {
+            throw new NoSuchTemplateException();
+        }
+
+        GradingReportTemplate currentTemplate = gradingReportTemplateRepo.getCurrentTemplate(
+                template.getProjectType(),
+                update.validFrom());
+        if (currentTemplate.getId() != templateId &&
+            Objects.equals(currentTemplate.getValidFrom(), update.validFrom()))
+        {
+            throw new DuplicateDateException(update.validFrom(), template.getProjectType());
+        }
+
+        if (!template.getValidFrom().isAfter(today)) {
+            throw new TemplateLockedException(template.getValidFrom());
+        }
+
+        return gradingReportTemplateRepo.updateTemplate(templateId, update);
+    }
+
+    @Override
+    public GradingReportTemplate create(ProjectType projectType, GradingReportTemplateUpdate update)
+            throws ValidDateMustBeInTheFutureException, DuplicateDateException
+    {
+        LocalDate today = LocalDate.now(clock);
+        if (!update.validFrom().isAfter(today)) {
+            throw new ValidDateMustBeInTheFutureException(update.validFrom(), today.plusDays(1));
+        }
+
+        GradingReportTemplate currentTemplate = gradingReportTemplateRepo.getCurrentTemplate(
+                projectType,
+                update.validFrom());
+        if (currentTemplate != null &&
+            Objects.equals(currentTemplate.getValidFrom(), update.validFrom()))
+        {
+            throw new DuplicateDateException(update.validFrom(), projectType);
+        }
+
+        return gradingReportTemplateRepo.createTemplate(projectType, update);
+    }
 }
diff --git a/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplate.java b/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplate.java
index 0598728f2c..cc106b6c95 100644
--- a/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplate.java
+++ b/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplate.java
@@ -7,6 +7,9 @@ import se.su.dsv.scipro.system.ProjectType;
 import se.su.dsv.scipro.system.User;
 
 import jakarta.persistence.*;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
@@ -23,22 +26,35 @@ public class GradingReportTemplate extends DomainObject {
     @OneToOne(optional = false)
     private ProjectType projectType;
 
-    @OneToMany(mappedBy = "gradingReportTemplate", cascade = {CascadeType.ALL})
+    @OneToMany(mappedBy = "gradingReportTemplate", cascade = {CascadeType.ALL}, orphanRemoval = true)
     private Collection<GradingCriterionTemplate> criteria = new HashSet<>();
 
+    @Temporal(TemporalType.DATE)
+    @Column(name = "valid_from")
+    private LocalDate validFrom;
+
     @Basic
-    private int credits;
+    @Column(name = "note")
+    private String note;
+
+    @Basic
+    @Column(name = "failing_grade")
+    private String failingGrade;
+
+    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
+    @JoinColumn(name = "grading_report_template_id")
+    private Collection<GradeLimit> gradeLimits = new ArrayList<>();
 
     protected GradingReportTemplate() {
 
     }
 
-    public GradingReportTemplate(ProjectType projectType, int credits) {
+    public GradingReportTemplate(ProjectType projectType, LocalDate validFrom) {
         if (projectType == null) {
             throw new IllegalArgumentException("ProjectType may not be null");
         }
         this.projectType = projectType;
-        this.credits = credits;
+        this.validFrom = validFrom;
     }
 
     public SupervisorGradingReport createSupervisorReport(Project project, User student) {
@@ -53,18 +69,28 @@ public class GradingReportTemplate extends DomainObject {
     }
 
     public GradingCriterionTemplate addProjectCriterion(String title, String titleEn, int pointsRequiredToPass, List<GradingCriterionPointTemplate> gradingCriterionPointTemplates) {
+        return addProjectCriterion(title, titleEn, pointsRequiredToPass, gradingCriterionPointTemplates, null);
+    }
+
+    public GradingCriterionTemplate addProjectCriterion(String title, String titleEn, int pointsRequiredToPass, List<GradingCriterionPointTemplate> gradingCriterionPointTemplates, AbstractGradingCriterion.Flag flag) {
         GradingCriterionTemplate gradingCriterionTemplate = new ProjectGradingCriterionTemplate(this, criteria.size(), title, titleEn, pointsRequiredToPass, gradingCriterionPointTemplates);
+        gradingCriterionTemplate.setFlag(flag);
         criteria.add(gradingCriterionTemplate);
         return gradingCriterionTemplate;
     }
 
     public GradingCriterionTemplate addIndividualCriterion(String title, String titleEn, int pointsRequiredToPass, List<GradingCriterionPointTemplate> gradingCriterionPointTemplates) {
+        return addIndividualCriterion(title, titleEn, pointsRequiredToPass, gradingCriterionPointTemplates, null);
+    }
+
+    public GradingCriterionTemplate addIndividualCriterion(String title, String titleEn, int pointsRequiredToPass, List<GradingCriterionPointTemplate> gradingCriterionPointTemplates, AbstractGradingCriterion.Flag flag) {
         GradingCriterionTemplate gradingCriterionTemplate = new IndividualGradingCriterionTemplate(this, criteria.size(), title, titleEn, pointsRequiredToPass, gradingCriterionPointTemplates);
+        gradingCriterionTemplate.setFlag(flag);
         criteria.add(gradingCriterionTemplate);
         return gradingCriterionTemplate;
     }
 
-    public Iterable<GradingCriterionTemplate> getCriteria() {
+    public Collection<GradingCriterionTemplate> getCriteria() {
         return criteria;
     }
 
@@ -73,6 +99,46 @@ public class GradingReportTemplate extends DomainObject {
         return this.id;
     }
 
+    public LocalDate getValidFrom() {
+        return validFrom;
+    }
+
+    public void setValidFrom(LocalDate validFrom) {
+        this.validFrom = validFrom;
+    }
+
+    public ProjectType getProjectType() {
+        return projectType;
+    }
+
+    public void setProjectType(ProjectType projectType) {
+        this.projectType = projectType;
+    }
+
+    public String getNote() {
+        return note;
+    }
+
+    public void setNote(String note) {
+        this.note = note;
+    }
+
+    public String getFailingGrade() {
+        return failingGrade;
+    }
+
+    public void setFailingGrade(String failingGrade) {
+        this.failingGrade = failingGrade;
+    }
+
+    public Collection<GradeLimit> getGradeLimits() {
+        return gradeLimits;
+    }
+
+    public void setGradeLimits(Collection<GradeLimit> gradeLimits) {
+        this.gradeLimits = gradeLimits;
+    }
+
     @Override
     public boolean equals(final Object o) {
         if (o == this) return true;
@@ -93,6 +159,6 @@ public class GradingReportTemplate extends DomainObject {
 
     @Override
     public String toString() {
-        return "GradingReportTemplate(id=" + this.id + ", projectType=" + this.projectType + ", credits=" + this.credits + ")";
+        return "GradingReportTemplate(id=" + this.id + ", projectType=" + this.projectType + ", validFrom=" + this.validFrom + ")";
     }
 }
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/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplateRepo.java b/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplateRepo.java
index 26bd98075a..9ae1d6560f 100644
--- a/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplateRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplateRepo.java
@@ -1,8 +1,25 @@
 package se.su.dsv.scipro.report;
 
-import org.springframework.data.jpa.repository.JpaRepository;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.grading.GradingReportTemplateUpdate;
+import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.ProjectType;
 
+import java.time.LocalDate;
+import java.util.List;
+
 public interface GradingReportTemplateRepo extends JpaRepository<GradingReportTemplate, Long> {
-    GradingReportTemplate findByProjectTypeAndCredits(ProjectType projectType, int credits);
+    GradingReportTemplate getTemplate(Project project);
+
+    GradingReportTemplate getCurrentTemplate(ProjectType projectType, LocalDate now);
+
+    GradingReportTemplate getNextTemplate(GradingReportTemplate gradingReportTemplate);
+
+    List<GradingReportTemplate> getTemplatesValidAfter(ProjectType projectType, LocalDate date);
+
+    GradingReportTemplate getTemplateById(long templateId);
+
+    GradingReportTemplate updateTemplate(long templateId, GradingReportTemplateUpdate update);
+
+    GradingReportTemplate createTemplate(ProjectType projectType, GradingReportTemplateUpdate update);
 }
diff --git a/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplateRepoImpl.java b/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplateRepoImpl.java
index 170ef05806..8cf96941a1 100644
--- a/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplateRepoImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplateRepoImpl.java
@@ -1,12 +1,19 @@
 package se.su.dsv.scipro.report;
 
+import com.google.inject.persist.Transactional;
+import com.querydsl.jpa.JPAExpressions;
+import com.querydsl.jpa.JPQLQuery;
+import se.su.dsv.scipro.grading.GradingReportTemplateUpdate;
+import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.GenericRepo;
-import se.su.dsv.scipro.system.ProjectType;
 
 import jakarta.inject.Inject;
 import jakarta.inject.Provider;
 import jakarta.persistence.EntityManager;
-import jakarta.persistence.TypedQuery;
+import se.su.dsv.scipro.system.ProjectType;
+
+import java.time.LocalDate;
+import java.util.List;
 
 public class GradingReportTemplateRepoImpl extends GenericRepo<GradingReportTemplate, Long> implements GradingReportTemplateRepo {
     @Inject
@@ -15,10 +22,114 @@ public class GradingReportTemplateRepoImpl extends GenericRepo<GradingReportTemp
     }
 
     @Override
-    public GradingReportTemplate findByProjectTypeAndCredits(ProjectType projectType, int credits) {
-        TypedQuery<GradingReportTemplate> query = em().createQuery("select distinct template from GradingReportTemplate template where template.projectType = :projectType and template.credits = :credits", GradingReportTemplate.class);
-        query.setParameter("projectType", projectType);
-        query.setParameter("credits", credits);
-        return query.getSingleResult();
+    public GradingReportTemplate getTemplate(Project project) {
+        return getCurrentTemplate(project.getProjectType(), project.getStartDate());
+    }
+
+    @Override
+    public GradingReportTemplate getCurrentTemplate(ProjectType projectType, LocalDate now) {
+        QGradingReportTemplate template = QGradingReportTemplate.gradingReportTemplate;
+        // find the latest template that is valid for the project
+        JPQLQuery<LocalDate> validFrom = JPAExpressions
+                .select(template.validFrom.max())
+                .from(template)
+                .where(template.projectType.eq(projectType)
+                        .and(template.validFrom.loe(now)));
+        return findOne(template.projectType.eq(projectType).and(template.validFrom.eq(validFrom)));
+    }
+
+    @Override
+    public GradingReportTemplate getNextTemplate(GradingReportTemplate gradingReportTemplate) {
+        QGradingReportTemplate template = QGradingReportTemplate.gradingReportTemplate;
+        // find the latest template that is valid for the project
+        JPQLQuery<LocalDate> validFrom = JPAExpressions
+                .select(template.validFrom.min())
+                .from(template)
+                .where(template.projectType.eq(gradingReportTemplate.getProjectType())
+                        .and(template.validFrom.gt(gradingReportTemplate.getValidFrom())));
+        return findOne(template.projectType.eq(gradingReportTemplate.getProjectType()).and(template.validFrom.eq(validFrom)));
+    }
+
+    @Override
+    public List<GradingReportTemplate> getTemplatesValidAfter(ProjectType projectType, LocalDate date) {
+        return findAll(QGradingReportTemplate.gradingReportTemplate.projectType.eq(projectType)
+                .and(QGradingReportTemplate.gradingReportTemplate.validFrom.gt(date)));
+    }
+
+    @Override
+    public GradingReportTemplate getTemplateById(long templateId) {
+        return findOne(templateId);
+    }
+
+    @Override
+    @Transactional
+    public GradingReportTemplate updateTemplate(long templateId, GradingReportTemplateUpdate update) {
+        GradingReportTemplate gradingReportTemplate = findOne(templateId);
+        return updateTemplate(gradingReportTemplate, update);
+    }
+
+    private GradingReportTemplate updateTemplate(
+            GradingReportTemplate gradingReportTemplate,
+            GradingReportTemplateUpdate update)
+    {
+        gradingReportTemplate.setValidFrom(update.validFrom());
+        gradingReportTemplate.setNote(update.note());
+        gradingReportTemplate.setFailingGrade(update.failingGrade());
+
+        gradingReportTemplate.getCriteria().clear();
+        for (var criteria : update.criteria()) {
+            final List<GradingCriterionPointTemplate> pointTemplates = criteria.requirements()
+                    .stream()
+                    .map(this::toPointTemplate)
+                    .toList();
+            AbstractGradingCriterion.Flag flag = criteria.flag() == null ? null : switch (criteria.flag()) {
+                case OPPOSITION -> AbstractGradingCriterion.Flag.OPPOSITION;
+                case REFLECTION -> AbstractGradingCriterion.Flag.REFLECTION;
+                //case null -> null; sigh java 17
+            };
+            switch (criteria.type()) {
+                case THESIS -> {
+                    gradingReportTemplate.addProjectCriterion(
+                            criteria.title().swedish(),
+                            criteria.title().english(),
+                            criteria.minimumPointsRequiredToPass(),
+                            pointTemplates,
+                            flag);
+                }
+                case INDIVIDUAL -> {
+                    gradingReportTemplate.addIndividualCriterion(
+                            criteria.title().swedish(),
+                            criteria.title().english(),
+                            criteria.minimumPointsRequiredToPass(),
+                            pointTemplates,
+                            flag);
+                }
+            }
+        }
+
+        gradingReportTemplate.getGradeLimits().clear();
+        for (var grade : update.gradeLimits()) {
+            GradeLimit gradeLimit = new GradeLimit();
+            gradeLimit.setGrade(grade.grade());
+            gradeLimit.setLowerLimit(grade.minimumPoints());
+            gradingReportTemplate.getGradeLimits().add(gradeLimit);
+        }
+
+        return save(gradingReportTemplate);
+    }
+
+    @Override
+    @Transactional
+    public GradingReportTemplate createTemplate(ProjectType projectType, GradingReportTemplateUpdate update) {
+        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(projectType, update.validFrom());
+        return updateTemplate(gradingReportTemplate, update);
+    }
+
+    private GradingCriterionPointTemplate toPointTemplate(GradingReportTemplateUpdate.Criteria.Requirement requirement) {
+        return new GradingCriterionPointTemplate.Builder()
+                .point(requirement.points())
+                .description(requirement.description().swedish())
+                .descriptionEn(requirement.description().english())
+                .build();
     }
 }
\ No newline at end of file
diff --git a/core/src/main/java/se/su/dsv/scipro/report/NoSuchTemplateException.java b/core/src/main/java/se/su/dsv/scipro/report/NoSuchTemplateException.java
new file mode 100644
index 0000000000..0013f826d2
--- /dev/null
+++ b/core/src/main/java/se/su/dsv/scipro/report/NoSuchTemplateException.java
@@ -0,0 +1,4 @@
+package se.su.dsv.scipro.report;
+
+public class NoSuchTemplateException extends Exception {
+}
diff --git a/core/src/main/java/se/su/dsv/scipro/report/OppositionReportRepo.java b/core/src/main/java/se/su/dsv/scipro/report/OppositionReportRepo.java
index 9f5cb39c7c..68fbc4dd66 100644
--- a/core/src/main/java/se/su/dsv/scipro/report/OppositionReportRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/report/OppositionReportRepo.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.report;
 
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.finalseminar.FinalSeminarOpposition;
 
 public interface OppositionReportRepo extends JpaRepository<OppositionReport, Long>, QueryDslPredicateExecutor<OppositionReport> {
diff --git a/core/src/main/java/se/su/dsv/scipro/report/OppositionReportServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/report/OppositionReportServiceImpl.java
index bf1321d744..417e7a15bf 100644
--- a/core/src/main/java/se/su/dsv/scipro/report/OppositionReportServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/report/OppositionReportServiceImpl.java
@@ -33,7 +33,7 @@ public class OppositionReportServiceImpl implements OppositionReportService {
         if (oppositionReport != null) {
             return oppositionReport;
         } else {
-            OppositionReport newReport = gradingReportTemplateRepo.findByProjectTypeAndCredits(finalSeminarOpposition.getProjectType(), finalSeminarOpposition.getProject().getCredits())
+            OppositionReport newReport = gradingReportTemplateRepo.getTemplate(finalSeminarOpposition.getProject())
                     .createOppositionReport(finalSeminarOpposition);
             return oppositionReportRepo.save(newReport);
         }
diff --git a/core/src/main/java/se/su/dsv/scipro/report/SupervisorGradingReportRepository.java b/core/src/main/java/se/su/dsv/scipro/report/SupervisorGradingReportRepository.java
new file mode 100644
index 0000000000..2415d52712
--- /dev/null
+++ b/core/src/main/java/se/su/dsv/scipro/report/SupervisorGradingReportRepository.java
@@ -0,0 +1,10 @@
+package se.su.dsv.scipro.report;
+
+import se.su.dsv.scipro.project.Project;
+import se.su.dsv.scipro.system.User;
+
+public interface SupervisorGradingReportRepository {
+    SupervisorGradingReport save(SupervisorGradingReport report);
+
+    SupervisorGradingReport getReport(Project project, User author);
+}
diff --git a/core/src/main/java/se/su/dsv/scipro/report/SupervisorGradingReportRepositoryImpl.java b/core/src/main/java/se/su/dsv/scipro/report/SupervisorGradingReportRepositoryImpl.java
new file mode 100644
index 0000000000..be7c252236
--- /dev/null
+++ b/core/src/main/java/se/su/dsv/scipro/report/SupervisorGradingReportRepositoryImpl.java
@@ -0,0 +1,37 @@
+package se.su.dsv.scipro.report;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Provider;
+import jakarta.persistence.EntityManager;
+import se.su.dsv.scipro.project.Project;
+import se.su.dsv.scipro.system.AbstractRepository;
+import se.su.dsv.scipro.system.User;
+
+public class SupervisorGradingReportRepositoryImpl extends AbstractRepository
+        implements SupervisorGradingReportRepository
+{
+    @Inject
+    public SupervisorGradingReportRepositoryImpl(Provider<EntityManager> em) {
+        super(em);
+    }
+
+    @Override
+    public SupervisorGradingReport save(SupervisorGradingReport report) {
+        EntityManager entityManager = em();
+        if (entityManager.contains(report)) {
+            return entityManager.merge(report);
+        }
+        else {
+            entityManager.persist(report);
+            return report;
+        }
+    }
+
+    @Override
+    public SupervisorGradingReport getReport(Project project, User author) {
+        return from(QSupervisorGradingReport.supervisorGradingReport)
+                .where(QSupervisorGradingReport.supervisorGradingReport.user.eq(author).and(
+                        QSupervisorGradingReport.supervisorGradingReport.project.eq(project)))
+                .fetchOne();
+    }
+}
diff --git a/core/src/main/java/se/su/dsv/scipro/report/TemplateLockedException.java b/core/src/main/java/se/su/dsv/scipro/report/TemplateLockedException.java
new file mode 100644
index 0000000000..217cdff1da
--- /dev/null
+++ b/core/src/main/java/se/su/dsv/scipro/report/TemplateLockedException.java
@@ -0,0 +1,15 @@
+package se.su.dsv.scipro.report;
+
+import java.time.LocalDate;
+
+public class TemplateLockedException extends Exception {
+    private final LocalDate becameValidAt;
+
+    public TemplateLockedException(LocalDate becameValidAt) {
+        this.becameValidAt = becameValidAt;
+    }
+
+    public LocalDate becameValidAt() {
+        return becameValidAt;
+    }
+}
diff --git a/core/src/main/java/se/su/dsv/scipro/report/ValidDateMustBeInTheFutureException.java b/core/src/main/java/se/su/dsv/scipro/report/ValidDateMustBeInTheFutureException.java
new file mode 100644
index 0000000000..dcaee0092b
--- /dev/null
+++ b/core/src/main/java/se/su/dsv/scipro/report/ValidDateMustBeInTheFutureException.java
@@ -0,0 +1,21 @@
+package se.su.dsv.scipro.report;
+
+import java.time.LocalDate;
+
+public class ValidDateMustBeInTheFutureException extends Exception {
+    private final LocalDate validFrom;
+    private final LocalDate earliestAllowedValidFrom;
+
+    public ValidDateMustBeInTheFutureException(LocalDate validFrom, LocalDate earliestAllowedValidFrom) {
+        this.validFrom = validFrom;
+        this.earliestAllowedValidFrom = earliestAllowedValidFrom;
+    }
+
+    public LocalDate validFrom() {
+        return validFrom;
+    }
+
+    public LocalDate earliestAllowedValidFrom() {
+        return earliestAllowedValidFrom;
+    }
+}
diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/ProjectFinalSeminarStatisticsService.java b/core/src/main/java/se/su/dsv/scipro/reviewing/ProjectFinalSeminarStatisticsService.java
index c49e5beba3..1556cfe7a3 100644
--- a/core/src/main/java/se/su/dsv/scipro/reviewing/ProjectFinalSeminarStatisticsService.java
+++ b/core/src/main/java/se/su/dsv/scipro/reviewing/ProjectFinalSeminarStatisticsService.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.reviewing;
 
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.project.ProjectStatus;
 import se.su.dsv.scipro.system.GenericService;
diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/ProjectFinalSeminarStatisticsServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/reviewing/ProjectFinalSeminarStatisticsServiceImpl.java
index 018b255f9b..736c9d4120 100644
--- a/core/src/main/java/se/su/dsv/scipro/reviewing/ProjectFinalSeminarStatisticsServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/reviewing/ProjectFinalSeminarStatisticsServiceImpl.java
@@ -7,7 +7,7 @@ import com.querydsl.core.types.dsl.Expressions;
 import com.querydsl.core.types.dsl.StringPath;
 import com.querydsl.jpa.JPAExpressions;
 import com.querydsl.jpa.impl.JPAQuery;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.finalseminar.QFinalSeminar;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.project.ProjectStatus;
diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerThreadRepository.java b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerThreadRepository.java
index 3a169cfecb..4dc40f5bb5 100644
--- a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerThreadRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerThreadRepository.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.reviewing;
 
-import org.springframework.data.jpa.repository.JpaRepository;
+import se.su.dsv.scipro.system.JpaRepository;
 import se.su.dsv.scipro.forum.dataobjects.ReviewerThread;
 import se.su.dsv.scipro.project.Project;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/settings/dataobjects/SupervisorProjectNoteDisplay.java b/core/src/main/java/se/su/dsv/scipro/settings/dataobjects/SupervisorProjectNoteDisplay.java
new file mode 100644
index 0000000000..1a6c45d452
--- /dev/null
+++ b/core/src/main/java/se/su/dsv/scipro/settings/dataobjects/SupervisorProjectNoteDisplay.java
@@ -0,0 +1,5 @@
+package se.su.dsv.scipro.settings.dataobjects;
+
+public enum SupervisorProjectNoteDisplay {
+    COMPACT, FULL
+}
diff --git a/core/src/main/java/se/su/dsv/scipro/settings/dataobjects/UserProfile.java b/core/src/main/java/se/su/dsv/scipro/settings/dataobjects/UserProfile.java
index 8cf7d44e31..ac863a2afa 100644
--- a/core/src/main/java/se/su/dsv/scipro/settings/dataobjects/UserProfile.java
+++ b/core/src/main/java/se/su/dsv/scipro/settings/dataobjects/UserProfile.java
@@ -58,6 +58,11 @@ public class UserProfile extends DomainObject {
     @Enumerated(EnumType.STRING)
     private Roles selectedRole;
 
+    @Basic
+    @Enumerated(EnumType.STRING)
+    @Column(name = "supervisor_project_note_display")
+    private SupervisorProjectNoteDisplay supervisorProjectNoteDisplay = SupervisorProjectNoteDisplay.COMPACT;
+
     @Override
     public Long getId() {
         return this.id;
@@ -147,6 +152,14 @@ public class UserProfile extends DomainObject {
         this.selectedRole = selectedRole;
     }
 
+    public SupervisorProjectNoteDisplay getSupervisorProjectNoteDisplay() {
+        return supervisorProjectNoteDisplay;
+    }
+
+    public void setSupervisorProjectNoteDisplay(SupervisorProjectNoteDisplay supervisorProjectNoteDisplay) {
+        this.supervisorProjectNoteDisplay = supervisorProjectNoteDisplay;
+    }
+
     @Override
     public String toString() {
         return "UserProfile(id=" + this.getId() + ", user=" + this.getUser() + ", skypeId=" + this.getSkypeId() + ", phoneNumber=" + this.getPhoneNumber() + ", otherInfo=" + this.getOtherInfo() + ", mailCompilation=" + this.isMailCompilation() + ", defaultProjectStatusFilter=" + this.getDefaultProjectStatusFilter() + ", defaultProjectTeamMemberRolesFilter=" + this.getDefaultProjectTeamMemberRolesFilter() + ", defaultSupervisorFilter=" + this.isDefaultSupervisorFilter() + ", defaultProjectTypeFilter=" + this.getDefaultProjectTypeFilter() + ", selectedRole=" + this.getSelectedRole() + ")";
diff --git a/core/src/main/java/se/su/dsv/scipro/springdata/serviceimpls/SupervisorServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/springdata/serviceimpls/SupervisorServiceImpl.java
index 4110fac926..99f2f30753 100755
--- a/core/src/main/java/se/su/dsv/scipro/springdata/serviceimpls/SupervisorServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/springdata/serviceimpls/SupervisorServiceImpl.java
@@ -3,7 +3,7 @@ package se.su.dsv.scipro.springdata.serviceimpls;
 import com.querydsl.core.BooleanBuilder;
 import com.querydsl.core.types.dsl.BooleanExpression;
 import com.querydsl.jpa.JPAExpressions;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.match.ApplicationPeriod;
 import se.su.dsv.scipro.match.QTarget;
 import se.su.dsv.scipro.security.auth.roles.Roles;
diff --git a/core/src/main/java/se/su/dsv/scipro/springdata/services/SupervisorService.java b/core/src/main/java/se/su/dsv/scipro/springdata/services/SupervisorService.java
index d197dd5b15..01b9638ff7 100755
--- a/core/src/main/java/se/su/dsv/scipro/springdata/services/SupervisorService.java
+++ b/core/src/main/java/se/su/dsv/scipro/springdata/services/SupervisorService.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.springdata.services;
 
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.match.ApplicationPeriod;
 import se.su.dsv.scipro.system.*;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/survey/QuestionRepository.java b/core/src/main/java/se/su/dsv/scipro/survey/QuestionRepository.java
index 3260925acb..90ff58ea5d 100644
--- a/core/src/main/java/se/su/dsv/scipro/survey/QuestionRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/survey/QuestionRepository.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.survey;
 
-import org.springframework.data.jpa.repository.JpaRepository;
+import se.su.dsv.scipro.system.JpaRepository;
 
 public interface QuestionRepository extends JpaRepository<Question, Long> {
 }
diff --git a/core/src/main/java/se/su/dsv/scipro/survey/SurveyRepository.java b/core/src/main/java/se/su/dsv/scipro/survey/SurveyRepository.java
index 1130bc323b..3b4cd92ff3 100644
--- a/core/src/main/java/se/su/dsv/scipro/survey/SurveyRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/survey/SurveyRepository.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.survey;
 
-import org.springframework.data.jpa.repository.JpaRepository;
+import se.su.dsv.scipro.system.JpaRepository;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.User;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/system/AbstractServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/system/AbstractServiceImpl.java
index 22e22d9613..748940900e 100755
--- a/core/src/main/java/se/su/dsv/scipro/system/AbstractServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/AbstractServiceImpl.java
@@ -6,8 +6,6 @@ import com.querydsl.core.types.dsl.EntityPathBase;
 import com.querydsl.core.types.dsl.Expressions;
 import com.querydsl.core.types.dsl.StringPath;
 import com.querydsl.jpa.impl.JPAQuery;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Sort;
 
 import jakarta.inject.Provider;
 import jakarta.persistence.EntityManager;
diff --git a/core/src/main/java/se/su/dsv/scipro/system/FilteredService.java b/core/src/main/java/se/su/dsv/scipro/system/FilteredService.java
index 596a055941..c407cd1d8e 100644
--- a/core/src/main/java/se/su/dsv/scipro/system/FilteredService.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/FilteredService.java
@@ -1,7 +1,5 @@
 package se.su.dsv.scipro.system;
 
-import org.springframework.data.domain.Pageable;
-
 import java.io.Serializable;
 import java.util.List;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/system/FooterAddressRepo.java b/core/src/main/java/se/su/dsv/scipro/system/FooterAddressRepo.java
index 2980b857f5..ad0f13494d 100644
--- a/core/src/main/java/se/su/dsv/scipro/system/FooterAddressRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/FooterAddressRepo.java
@@ -1,8 +1,6 @@
 package se.su.dsv.scipro.system;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
 
 @Transactional
 public interface FooterAddressRepo extends JpaRepository<FooterAddress, Long>, QueryDslPredicateExecutor<FooterAddress> {
diff --git a/core/src/main/java/se/su/dsv/scipro/system/FooterLinkRepo.java b/core/src/main/java/se/su/dsv/scipro/system/FooterLinkRepo.java
index 3c9e2f0e62..bd0c349289 100755
--- a/core/src/main/java/se/su/dsv/scipro/system/FooterLinkRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/FooterLinkRepo.java
@@ -1,8 +1,6 @@
 package se.su.dsv.scipro.system;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
 
 import java.util.List;
 import java.util.Optional;
diff --git a/core/src/main/java/se/su/dsv/scipro/system/GenericRepo.java b/core/src/main/java/se/su/dsv/scipro/system/GenericRepo.java
index 36c735a306..2a42a60a0e 100644
--- a/core/src/main/java/se/su/dsv/scipro/system/GenericRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/GenericRepo.java
@@ -6,10 +6,6 @@ import com.querydsl.core.types.dsl.EntityPathBase;
 import com.querydsl.core.types.dsl.Expressions;
 import com.querydsl.core.types.dsl.StringPath;
 import com.querydsl.jpa.impl.JPAQuery;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Sort;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
 
 import jakarta.inject.Provider;
 import jakarta.persistence.EntityManager;
diff --git a/core/src/main/java/se/su/dsv/scipro/system/GenericService.java b/core/src/main/java/se/su/dsv/scipro/system/GenericService.java
index ef89d6da75..31efa33888 100755
--- a/core/src/main/java/se/su/dsv/scipro/system/GenericService.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/GenericService.java
@@ -1,7 +1,6 @@
 package se.su.dsv.scipro.system;
 
 import com.querydsl.core.types.Predicate;
-import org.springframework.data.domain.Pageable;
 
 import java.io.Serializable;
 import java.util.List;
diff --git a/core/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java b/core/src/main/java/se/su/dsv/scipro/system/JpaRepository.java
similarity index 96%
rename from core/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java
rename to core/src/main/java/se/su/dsv/scipro/system/JpaRepository.java
index d6d6671e89..1d6165771b 100644
--- a/core/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/JpaRepository.java
@@ -1,4 +1,4 @@
-package org.springframework.data.jpa.repository;
+package se.su.dsv.scipro.system;
 
 import java.io.Serializable;
 import java.util.List;
diff --git a/core/src/main/java/se/su/dsv/scipro/system/LocalUserSearch.java b/core/src/main/java/se/su/dsv/scipro/system/LocalUserSearch.java
index 197886adc0..90a2cc05f8 100644
--- a/core/src/main/java/se/su/dsv/scipro/system/LocalUserSearch.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/LocalUserSearch.java
@@ -1,6 +1,5 @@
 package se.su.dsv.scipro.system;
 
-import org.springframework.data.domain.PageRequest;
 import se.su.dsv.scipro.security.auth.roles.Roles;
 
 import jakarta.inject.Inject;
diff --git a/core/src/main/java/org/springframework/data/domain/PageRequest.java b/core/src/main/java/se/su/dsv/scipro/system/PageRequest.java
similarity index 96%
rename from core/src/main/java/org/springframework/data/domain/PageRequest.java
rename to core/src/main/java/se/su/dsv/scipro/system/PageRequest.java
index e91d1135c7..6d7ca6d6c2 100644
--- a/core/src/main/java/org/springframework/data/domain/PageRequest.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/PageRequest.java
@@ -1,4 +1,4 @@
-package org.springframework.data.domain;
+package se.su.dsv.scipro.system;
 
 import java.util.Objects;
 
diff --git a/core/src/main/java/org/springframework/data/domain/Pageable.java b/core/src/main/java/se/su/dsv/scipro/system/Pageable.java
similarity index 69%
rename from core/src/main/java/org/springframework/data/domain/Pageable.java
rename to core/src/main/java/se/su/dsv/scipro/system/Pageable.java
index 9974901541..288468868b 100644
--- a/core/src/main/java/org/springframework/data/domain/Pageable.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/Pageable.java
@@ -1,4 +1,4 @@
-package org.springframework.data.domain;
+package se.su.dsv.scipro.system;
 
 public interface Pageable {
     long getOffset();
diff --git a/core/src/main/java/se/su/dsv/scipro/system/PasswordRepo.java b/core/src/main/java/se/su/dsv/scipro/system/PasswordRepo.java
index 9e8a814d6f..32d5c4c4f9 100755
--- a/core/src/main/java/se/su/dsv/scipro/system/PasswordRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/PasswordRepo.java
@@ -1,8 +1,6 @@
 package se.su.dsv.scipro.system;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
 
 
 @Transactional
diff --git a/core/src/main/java/org/springframework/data/querydsl/QueryDslPredicateExecutor.java b/core/src/main/java/se/su/dsv/scipro/system/QueryDslPredicateExecutor.java
similarity index 76%
rename from core/src/main/java/org/springframework/data/querydsl/QueryDslPredicateExecutor.java
rename to core/src/main/java/se/su/dsv/scipro/system/QueryDslPredicateExecutor.java
index 72abfe041e..b85aef63c1 100644
--- a/core/src/main/java/org/springframework/data/querydsl/QueryDslPredicateExecutor.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/QueryDslPredicateExecutor.java
@@ -1,7 +1,6 @@
-package org.springframework.data.querydsl;
+package se.su.dsv.scipro.system;
 
 import com.querydsl.core.types.Predicate;
-import org.springframework.data.domain.Pageable;
 
 import java.util.List;
 
diff --git a/core/src/main/java/org/springframework/data/domain/Sort.java b/core/src/main/java/se/su/dsv/scipro/system/Sort.java
similarity index 96%
rename from core/src/main/java/org/springframework/data/domain/Sort.java
rename to core/src/main/java/se/su/dsv/scipro/system/Sort.java
index 0b49a02911..8ed0486e92 100644
--- a/core/src/main/java/org/springframework/data/domain/Sort.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/Sort.java
@@ -1,4 +1,4 @@
-package org.springframework.data.domain;
+package se.su.dsv.scipro.system;
 
 import java.util.Objects;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/system/UserRepo.java b/core/src/main/java/se/su/dsv/scipro/system/UserRepo.java
index 53d2c021f4..1a227078e1 100755
--- a/core/src/main/java/se/su/dsv/scipro/system/UserRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/UserRepo.java
@@ -1,9 +1,6 @@
 package se.su.dsv.scipro.system;
 
 import com.google.inject.persist.Transactional;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.security.auth.roles.Roles;
 
 import java.util.Collection;
diff --git a/core/src/main/java/se/su/dsv/scipro/system/UserRepoImpl.java b/core/src/main/java/se/su/dsv/scipro/system/UserRepoImpl.java
index 80a0a28ee4..35c53085f8 100644
--- a/core/src/main/java/se/su/dsv/scipro/system/UserRepoImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/UserRepoImpl.java
@@ -1,7 +1,6 @@
 package se.su.dsv.scipro.system;
 
 import com.querydsl.jpa.JPAExpressions;
-import org.springframework.data.domain.Pageable;
 import se.su.dsv.scipro.security.auth.roles.Roles;
 
 import jakarta.inject.Inject;
diff --git a/core/src/main/java/se/su/dsv/scipro/system/UserServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/system/UserServiceImpl.java
index 5fd3aaa5fb..ee1392b194 100755
--- a/core/src/main/java/se/su/dsv/scipro/system/UserServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/UserServiceImpl.java
@@ -2,7 +2,6 @@ package se.su.dsv.scipro.system;
 
 import com.querydsl.core.BooleanBuilder;
 import com.querydsl.core.types.Predicate;
-import org.springframework.data.domain.Pageable;
 
 import jakarta.inject.Inject;
 import jakarta.inject.Provider;
diff --git a/core/src/main/java/se/su/dsv/scipro/workerthreads/GradeFinalSeminarParticipantReminderWorker.java b/core/src/main/java/se/su/dsv/scipro/workerthreads/GradeFinalSeminarParticipantReminderWorker.java
index f5debaf3f4..b5603d84f0 100644
--- a/core/src/main/java/se/su/dsv/scipro/workerthreads/GradeFinalSeminarParticipantReminderWorker.java
+++ b/core/src/main/java/se/su/dsv/scipro/workerthreads/GradeFinalSeminarParticipantReminderWorker.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.workerthreads;
 
-import org.springframework.data.domain.PageRequest;
+import se.su.dsv.scipro.system.PageRequest;
 import se.su.dsv.scipro.finalseminar.FinalSeminar;
 import se.su.dsv.scipro.finalseminar.FinalSeminarService;
 import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettings;
diff --git a/core/src/main/resources/db/migration/V388__user_notes_for_projects.sql b/core/src/main/resources/db/migration/V388__user_notes_for_projects.sql
new file mode 100644
index 0000000000..334caad59b
--- /dev/null
+++ b/core/src/main/resources/db/migration/V388__user_notes_for_projects.sql
@@ -0,0 +1,11 @@
+CREATE TABLE IF NOT EXISTS `project_user_note` (
+    `project_id` bigint NOT NULL,
+    `user_id` bigint NOT NULL,
+    `note` text NULL,
+    PRIMARY KEY (`project_id`, `user_id`),
+    CONSTRAINT `FK_project_user_note_project` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+    CONSTRAINT `FK_project_user_note_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+);
+
+ALTER TABLE `user_profile`
+    ADD COLUMN `supervisor_project_note_display` VARCHAR(15) NOT NULL DEFAULT 'COMPACT';
diff --git a/core/src/main/resources/db/migration/V389_1__grading_report_template_valid_timespans.sql b/core/src/main/resources/db/migration/V389_1__grading_report_template_valid_timespans.sql
new file mode 100644
index 0000000000..04e804ea63
--- /dev/null
+++ b/core/src/main/resources/db/migration/V389_1__grading_report_template_valid_timespans.sql
@@ -0,0 +1,46 @@
+# For any given date there is only ever one valid template for a project type
+# That template is valid until the date a new template is valid from
+ALTER TABLE `grading_report_template`
+    ADD COLUMN `valid_from` DATE NOT NULL DEFAULT '2001-01-01';
+
+CREATE TEMPORARY TABLE pt_points
+(
+    pt_id   INT,
+    credits INT,
+    count   INT
+);
+
+# Count how many projects/credits each project type has
+INSERT INTO pt_points
+SELECT pt.id, credits, COUNT(*)
+FROM ProjectType pt
+         INNER JOIN project p ON pt.id = p.projectType_id
+WHERE pt.id IN (SELECT projectType_id FROM grading_report_template)
+GROUP BY pt.id, credits;
+
+CREATE TEMPORARY TABLE grading_templates_to_keep
+(
+    id INT
+);
+
+# Keep the most used grading template for each project type (based on projects/credits)
+INSERT INTO grading_templates_to_keep (id)
+SELECT t.id
+FROM grading_report_template t
+WHERE credits = (SELECT credits
+                 FROM pt_points m
+                 WHERE m.pt_id = t.projectType_id
+                   AND m.`count` = (SELECT MAX(count)
+                                    FROM pt_points n
+                                    WHERE n.pt_id = t.projectType_id));
+
+DELETE FROM grading_criterion_point_template WHERE gradingCriterionTemplate_id IN (SELECT id FROM grading_criterion_template WHERE gradingReportTemplate_id NOT IN (SELECT id FROM grading_templates_to_keep));
+DELETE FROM grading_criterion_template WHERE gradingReportTemplate_id NOT IN (SELECT id FROM grading_templates_to_keep);
+DELETE FROM grading_report_template WHERE id NOT IN (SELECT id FROM grading_templates_to_keep);
+
+DROP TABLE grading_templates_to_keep;
+DROP TABLE pt_points;
+
+ALTER TABLE `grading_report_template`
+    DROP COLUMN `credits`,
+    ADD UNIQUE `UK_only_one_template_per_date_and_type` (`projectType_id`, `valid_from`);
diff --git a/core/src/main/resources/db/migration/V389_2__failing_grade_and_note_on_templates.sql b/core/src/main/resources/db/migration/V389_2__failing_grade_and_note_on_templates.sql
new file mode 100644
index 0000000000..31d85e8edf
--- /dev/null
+++ b/core/src/main/resources/db/migration/V389_2__failing_grade_and_note_on_templates.sql
@@ -0,0 +1,3 @@
+ALTER TABLE `grading_report_template`
+    ADD COLUMN `note` TEXT,
+    ADD COLUMN `failing_grade` VARCHAR(32);
diff --git a/core/src/main/resources/db/migration/V389_3__grade_limits_on_grading_templates.sql b/core/src/main/resources/db/migration/V389_3__grade_limits_on_grading_templates.sql
new file mode 100644
index 0000000000..80a2cc6439
--- /dev/null
+++ b/core/src/main/resources/db/migration/V389_3__grade_limits_on_grading_templates.sql
@@ -0,0 +1,10 @@
+CREATE TABLE `grading_report_template_grade_limits` (
+    `id` BIGINT NOT NULL AUTO_INCREMENT,
+    `grading_report_template_id` BIGINT, -- can't be NOT NULL because of Hibernate using an INSERT followed by an UPDATE
+    `grade` VARCHAR(32) NOT NULL,
+    `lower_limit` INT NOT NULL,
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `UK_one_grade_per_template` (`grading_report_template_id`, `grade`),
+    FOREIGN KEY `FK_grade_limit_grading_report_template `(`grading_report_template_id`)
+        REFERENCES `grading_report_template` (`id`) ON DELETE CASCADE
+);
diff --git a/core/src/main/resources/db/migration/V389_4__grading_criterion_flags.sql b/core/src/main/resources/db/migration/V389_4__grading_criterion_flags.sql
new file mode 100644
index 0000000000..9faf8c121e
--- /dev/null
+++ b/core/src/main/resources/db/migration/V389_4__grading_criterion_flags.sql
@@ -0,0 +1,4 @@
+ALTER TABLE `grading_criterion_template`
+    ADD COLUMN `flag` VARCHAR(64);
+ALTER TABLE `GradingCriterion`
+    ADD COLUMN `flag` VARCHAR(64);
diff --git a/core/src/main/resources/db/migration/V389_5__migrate_opposition_criteria_to_flag.sql b/core/src/main/resources/db/migration/V389_5__migrate_opposition_criteria_to_flag.sql
new file mode 100644
index 0000000000..d4ffffc008
--- /dev/null
+++ b/core/src/main/resources/db/migration/V389_5__migrate_opposition_criteria_to_flag.sql
@@ -0,0 +1,2 @@
+UPDATE `GradingCriterion` SET `flag` = 'OPPOSITION' WHERE title like 'Ö1 %';
+UPDATE `grading_criterion_template` SET `flag` = 'OPPOSITION' WHERE title like 'Ö1 %';
diff --git a/core/src/main/resources/db/migration/V389_6__migrate_reflection_criteria_to_flag.sql b/core/src/main/resources/db/migration/V389_6__migrate_reflection_criteria_to_flag.sql
new file mode 100644
index 0000000000..0ad60ce957
--- /dev/null
+++ b/core/src/main/resources/db/migration/V389_6__migrate_reflection_criteria_to_flag.sql
@@ -0,0 +1,2 @@
+UPDATE `GradingCriterion` SET `flag` = 'REFLECTION' WHERE title like 'Ö6 %';
+UPDATE `grading_criterion_template` SET `flag` = 'REFLECTION' WHERE title like 'Ö6 %';
diff --git a/core/src/test/java/se/su/dsv/scipro/activityplan/ActivityPlanFacadeImplIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/activityplan/ActivityPlanFacadeImplIntegrationTest.java
index 1fcbe09d16..0ccd31f5a2 100644
--- a/core/src/test/java/se/su/dsv/scipro/activityplan/ActivityPlanFacadeImplIntegrationTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/activityplan/ActivityPlanFacadeImplIntegrationTest.java
@@ -4,8 +4,8 @@ import com.google.common.eventbus.EventBus;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mock;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.PageRequest;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.checklist.ChecklistTemplateService;
 import se.su.dsv.scipro.file.FileDescription;
 import se.su.dsv.scipro.file.FileReference;
diff --git a/core/src/test/java/se/su/dsv/scipro/checklist/ChecklistTemplateServiceImplTest.java b/core/src/test/java/se/su/dsv/scipro/checklist/ChecklistTemplateServiceImplTest.java
index ec10a6909a..82907edc00 100644
--- a/core/src/test/java/se/su/dsv/scipro/checklist/ChecklistTemplateServiceImplTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/checklist/ChecklistTemplateServiceImplTest.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.checklist;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.PageRequest;
+import se.su.dsv.scipro.system.PageRequest;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.reusable.SciProUtilities;
 import se.su.dsv.scipro.system.DegreeType;
diff --git a/core/src/test/java/se/su/dsv/scipro/finalseminar/FinalSeminarOppositionServiceImplIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/finalseminar/FinalSeminarOppositionServiceImplIntegrationTest.java
index 07fdc37c8c..97bdd1b8e2 100644
--- a/core/src/test/java/se/su/dsv/scipro/finalseminar/FinalSeminarOppositionServiceImplIntegrationTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/finalseminar/FinalSeminarOppositionServiceImplIntegrationTest.java
@@ -13,6 +13,7 @@ import se.su.dsv.scipro.test.IntegrationTest;
 
 import jakarta.inject.Inject;
 import java.time.LocalDate;
+import java.time.Month;
 import java.util.Date;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -52,7 +53,8 @@ public class FinalSeminarOppositionServiceImplIntegrationTest extends Integratio
     }
 
     private GradingReportTemplate createGradingReportTemplate() {
-        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(projectType, 30);
+        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(projectType,
+                LocalDate.of(2024, Month.JANUARY, 1));
         return save(gradingReportTemplate);
     }
 
diff --git a/core/src/test/java/se/su/dsv/scipro/finalseminar/FinalSeminarServiceImplIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/finalseminar/FinalSeminarServiceImplIntegrationTest.java
index b11c3af291..a3fc78016c 100644
--- a/core/src/test/java/se/su/dsv/scipro/finalseminar/FinalSeminarServiceImplIntegrationTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/finalseminar/FinalSeminarServiceImplIntegrationTest.java
@@ -3,7 +3,7 @@ package se.su.dsv.scipro.finalseminar;
 import com.google.common.collect.Lists;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.PageRequest;
+import se.su.dsv.scipro.system.PageRequest;
 import se.su.dsv.scipro.file.FileDescription;
 import se.su.dsv.scipro.file.FileReference;
 import se.su.dsv.scipro.project.Project;
@@ -15,6 +15,7 @@ import se.su.dsv.scipro.test.IntegrationTest;
 
 import jakarta.inject.Inject;
 import java.time.LocalDate;
+import java.time.Month;
 import java.time.ZonedDateTime;
 import java.util.Date;
 import java.util.List;
@@ -316,7 +317,8 @@ public class FinalSeminarServiceImplIntegrationTest extends IntegrationTest {
     }
 
     private GradingReportTemplate createGradingReportTemplate(ProjectType projectType) {
-        GradingReportTemplate template = new GradingReportTemplate(projectType, 30);
+        GradingReportTemplate template = new GradingReportTemplate(projectType,
+                LocalDate.of(2024, Month.JANUARY, 1));
         return save(template);
     }
 
diff --git a/core/src/test/java/se/su/dsv/scipro/finalseminar/FinalSeminarServiceImplTest.java b/core/src/test/java/se/su/dsv/scipro/finalseminar/FinalSeminarServiceImplTest.java
index 89d7aefc1a..a0f5b8c592 100755
--- a/core/src/test/java/se/su/dsv/scipro/finalseminar/FinalSeminarServiceImplTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/finalseminar/FinalSeminarServiceImplTest.java
@@ -29,6 +29,8 @@ import se.su.dsv.scipro.test.InstanceProvider;
 import jakarta.persistence.EntityManager;
 import java.lang.reflect.Field;
 import java.time.Clock;
+import java.time.LocalDate;
+import java.time.Month;
 import java.time.ZonedDateTime;
 import java.util.*;
 
@@ -137,7 +139,8 @@ public class FinalSeminarServiceImplTest {
 
         FinalSeminarOpposition finalSeminarOpposition = new FinalSeminarOpposition();
         DomainObjects.injectId(finalSeminarOpposition, 1L);
-        finalSeminarOpposition.setOppositionReport(new OppositionReport(new GradingReportTemplate(new ProjectType(DegreeType.BACHELOR, "bachelor", "bachelor"), 1), finalSeminarOpposition));
+        finalSeminarOpposition.setOppositionReport(new OppositionReport(new GradingReportTemplate(new ProjectType(DegreeType.BACHELOR, "bachelor", "bachelor"),
+                LocalDate.of(2024, Month.JANUARY, 1)), finalSeminarOpposition));
         finalSeminar.setOppositions(Collections.singletonList(finalSeminarOpposition));
 
         seminarService.delete(finalSeminar);
diff --git a/core/src/test/java/se/su/dsv/scipro/finalthesis/FinalThesisServiceImplTest.java b/core/src/test/java/se/su/dsv/scipro/finalthesis/FinalThesisServiceImplTest.java
index a60b2d2ab6..97eee113b0 100644
--- a/core/src/test/java/se/su/dsv/scipro/finalthesis/FinalThesisServiceImplTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/finalthesis/FinalThesisServiceImplTest.java
@@ -5,7 +5,7 @@ import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.PageRequest;
+import se.su.dsv.scipro.system.PageRequest;
 import se.su.dsv.scipro.file.FileReference;
 import se.su.dsv.scipro.file.FileService;
 import se.su.dsv.scipro.file.ProjectFileUpload;
diff --git a/core/src/test/java/se/su/dsv/scipro/match/IdeaFacadeTest.java b/core/src/test/java/se/su/dsv/scipro/match/IdeaFacadeTest.java
index d8d88d34ba..45d747b6b8 100644
--- a/core/src/test/java/se/su/dsv/scipro/match/IdeaFacadeTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/match/IdeaFacadeTest.java
@@ -6,8 +6,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.PageRequest;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.system.DegreeType;
 import se.su.dsv.scipro.system.ProjectType;
 import se.su.dsv.scipro.system.User;
diff --git a/core/src/test/java/se/su/dsv/scipro/match/IdeaServiceImplIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/match/IdeaServiceImplIntegrationTest.java
index c200ff22c5..1b334f8802 100644
--- a/core/src/test/java/se/su/dsv/scipro/match/IdeaServiceImplIntegrationTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/match/IdeaServiceImplIntegrationTest.java
@@ -8,7 +8,7 @@ import org.hamcrest.core.Every;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.PageRequest;
+import se.su.dsv.scipro.system.PageRequest;
 import se.su.dsv.scipro.system.*;
 import se.su.dsv.scipro.test.IntegrationTest;
 
diff --git a/core/src/test/java/se/su/dsv/scipro/match/MatchFollowUpServiceTest.java b/core/src/test/java/se/su/dsv/scipro/match/MatchFollowUpServiceTest.java
index d3e1aa499a..ee84b2d14a 100644
--- a/core/src/test/java/se/su/dsv/scipro/match/MatchFollowUpServiceTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/match/MatchFollowUpServiceTest.java
@@ -6,8 +6,8 @@ import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.PageRequest;
+import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.security.auth.roles.Roles;
 import se.su.dsv.scipro.system.DegreeType;
 import se.su.dsv.scipro.system.ProjectType;
diff --git a/core/src/test/java/se/su/dsv/scipro/milestones/service/impl/MilestonePhaseTemplateServiceImplTest.java b/core/src/test/java/se/su/dsv/scipro/milestones/service/impl/MilestonePhaseTemplateServiceImplTest.java
index a09823fa35..348754d2d6 100644
--- a/core/src/test/java/se/su/dsv/scipro/milestones/service/impl/MilestonePhaseTemplateServiceImplTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/milestones/service/impl/MilestonePhaseTemplateServiceImplTest.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.milestones.service.impl;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.milestones.dataobjects.MilestonePhaseTemplate;
 import se.su.dsv.scipro.test.IntegrationTest;
 
diff --git a/core/src/test/java/se/su/dsv/scipro/milestones/service/impl/MilestoneServiceImplIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/milestones/service/impl/MilestoneServiceImplIntegrationTest.java
index 83dab95c61..8a3a74f6fa 100644
--- a/core/src/test/java/se/su/dsv/scipro/milestones/service/impl/MilestoneServiceImplIntegrationTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/milestones/service/impl/MilestoneServiceImplIntegrationTest.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.milestones.service.impl;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.PageRequest;
+import se.su.dsv.scipro.system.PageRequest;
 import se.su.dsv.scipro.milestones.dataobjects.Milestone;
 import se.su.dsv.scipro.milestones.dataobjects.MilestoneActivityTemplate;
 import se.su.dsv.scipro.milestones.dataobjects.MilestonePhaseTemplate;
diff --git a/core/src/test/java/se/su/dsv/scipro/milestones/service/impl/MilestoneStatisticsServiceImplTest.java b/core/src/test/java/se/su/dsv/scipro/milestones/service/impl/MilestoneStatisticsServiceImplTest.java
index 7d4283e986..400261587a 100644
--- a/core/src/test/java/se/su/dsv/scipro/milestones/service/impl/MilestoneStatisticsServiceImplTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/milestones/service/impl/MilestoneStatisticsServiceImplTest.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.milestones.service.impl;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.PageRequest;
+import se.su.dsv.scipro.system.PageRequest;
 import se.su.dsv.scipro.milestones.dataobjects.Milestone;
 import se.su.dsv.scipro.milestones.dataobjects.MilestoneActivityTemplate;
 import se.su.dsv.scipro.milestones.dataobjects.MilestonePhaseTemplate;
diff --git a/core/src/test/java/se/su/dsv/scipro/notifications/NotificationServiceImplIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/notifications/NotificationServiceImplIntegrationTest.java
index 1c496ff1f1..a6e232e644 100644
--- a/core/src/test/java/se/su/dsv/scipro/notifications/NotificationServiceImplIntegrationTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/notifications/NotificationServiceImplIntegrationTest.java
@@ -4,7 +4,7 @@ import org.hamcrest.Description;
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.PageRequest;
+import se.su.dsv.scipro.system.PageRequest;
 import se.su.dsv.scipro.notifications.dataobject.Notification;
 import se.su.dsv.scipro.notifications.dataobject.ProjectEvent;
 import se.su.dsv.scipro.project.Project;
diff --git a/core/src/test/java/se/su/dsv/scipro/peer/PeerRequestServiceImplTest.java b/core/src/test/java/se/su/dsv/scipro/peer/PeerRequestServiceImplTest.java
index d06d0b63b7..05a2437313 100644
--- a/core/src/test/java/se/su/dsv/scipro/peer/PeerRequestServiceImplTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/peer/PeerRequestServiceImplTest.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.peer;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.PageRequest;
+import se.su.dsv.scipro.system.PageRequest;
 import se.su.dsv.scipro.file.FileDescription;
 import se.su.dsv.scipro.file.FileReference;
 import se.su.dsv.scipro.project.Project;
diff --git a/core/src/test/java/se/su/dsv/scipro/peer/PeerReviewServiceImplIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/peer/PeerReviewServiceImplIntegrationTest.java
index 7d7b8fa320..4aca6ed7c2 100644
--- a/core/src/test/java/se/su/dsv/scipro/peer/PeerReviewServiceImplIntegrationTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/peer/PeerReviewServiceImplIntegrationTest.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.peer;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.PageRequest;
+import se.su.dsv.scipro.system.PageRequest;
 import se.su.dsv.scipro.file.FileDescription;
 import se.su.dsv.scipro.file.FileReference;
 import se.su.dsv.scipro.project.Project;
diff --git a/core/src/test/java/se/su/dsv/scipro/peer/PeerReviewServiceImplTest.java b/core/src/test/java/se/su/dsv/scipro/peer/PeerReviewServiceImplTest.java
index c35daaff5e..d36f73b25e 100644
--- a/core/src/test/java/se/su/dsv/scipro/peer/PeerReviewServiceImplTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/peer/PeerReviewServiceImplTest.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.peer;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.PageRequest;
+import se.su.dsv.scipro.system.PageRequest;
 import se.su.dsv.scipro.file.FileDescription;
 import se.su.dsv.scipro.file.FileReference;
 import se.su.dsv.scipro.project.Project;
diff --git a/core/src/test/java/se/su/dsv/scipro/peer/TestPeerReview.java b/core/src/test/java/se/su/dsv/scipro/peer/TestPeerReview.java
index 48838b35f3..93a6feb96b 100755
--- a/core/src/test/java/se/su/dsv/scipro/peer/TestPeerReview.java
+++ b/core/src/test/java/se/su/dsv/scipro/peer/TestPeerReview.java
@@ -3,7 +3,7 @@ package se.su.dsv.scipro.peer;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.PageRequest;
+import se.su.dsv.scipro.system.PageRequest;
 import se.su.dsv.scipro.file.FileDescription;
 import se.su.dsv.scipro.file.FileReference;
 import se.su.dsv.scipro.project.Project;
diff --git a/core/src/test/java/se/su/dsv/scipro/project/ProjectServiceImplIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/project/ProjectServiceImplIntegrationTest.java
index 0028849f94..a5945c7c91 100644
--- a/core/src/test/java/se/su/dsv/scipro/project/ProjectServiceImplIntegrationTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/project/ProjectServiceImplIntegrationTest.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.project;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.PageRequest;
+import se.su.dsv.scipro.system.PageRequest;
 import se.su.dsv.scipro.system.DegreeType;
 import se.su.dsv.scipro.system.ProjectType;
 import se.su.dsv.scipro.system.Unit;
diff --git a/core/src/test/java/se/su/dsv/scipro/projectpartner/ProjectPartnerServiceImplTest.java b/core/src/test/java/se/su/dsv/scipro/projectpartner/ProjectPartnerServiceImplTest.java
index d758673608..3dfeddaf19 100644
--- a/core/src/test/java/se/su/dsv/scipro/projectpartner/ProjectPartnerServiceImplTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/projectpartner/ProjectPartnerServiceImplTest.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.projectpartner;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.PageRequest;
+import se.su.dsv.scipro.system.PageRequest;
 import se.su.dsv.scipro.match.ApplicationPeriod;
 import se.su.dsv.scipro.system.DegreeType;
 import se.su.dsv.scipro.system.ProjectType;
diff --git a/core/src/test/java/se/su/dsv/scipro/report/GradingReportServiceImplIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/report/GradingReportServiceImplIntegrationTest.java
index 6bd8a3f8a8..765513cc4e 100644
--- a/core/src/test/java/se/su/dsv/scipro/report/GradingReportServiceImplIntegrationTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/report/GradingReportServiceImplIntegrationTest.java
@@ -15,14 +15,13 @@ import se.su.dsv.scipro.util.Either;
 
 import jakarta.inject.Inject;
 import java.time.LocalDate;
+import java.time.Month;
 import java.util.*;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static se.su.dsv.scipro.report.GradingReportServiceImpl.OPPOSITION_ENGLISH;
-import static se.su.dsv.scipro.report.GradingReportServiceImpl.OPPOSITION_SWEDISH;
 
 public class GradingReportServiceImplIntegrationTest extends IntegrationTest {
 
@@ -82,7 +81,7 @@ public class GradingReportServiceImplIntegrationTest extends IntegrationTest {
         addOppositionCriterion();
         boolean updated = updateOppositionCriterion();
 
-        GradingCriterion oppositionCriterion = findOppositionCriterion(OPPOSITION_SWEDISH);
+        GradingCriterion oppositionCriterion = findOppositionCriterion();
         assert oppositionCriterion != null;
         assertEquals(FEEDBACK_ON_OPPOSITION, oppositionCriterion.getFeedback());
         assertEquals((Integer) OPPOSITION_CRITERION_POINTS, oppositionCriterion.getPoints());
@@ -94,7 +93,7 @@ public class GradingReportServiceImplIntegrationTest extends IntegrationTest {
         addOppositionCriterion();
         boolean updated = updateOppositionCriterion();
 
-        GradingCriterion oppositionCriterion = findEnglishOppositionCriterion(OPPOSITION_ENGLISH);
+        GradingCriterion oppositionCriterion = findEnglishOppositionCriterion("Ö1 Opposition report");
         assert oppositionCriterion != null;
         assertEquals(FEEDBACK_ON_OPPOSITION, oppositionCriterion.getFeedback());
         assertEquals((Integer) OPPOSITION_CRITERION_POINTS, oppositionCriterion.getPoints());
@@ -107,7 +106,7 @@ public class GradingReportServiceImplIntegrationTest extends IntegrationTest {
         assessAllCriteria(gradingReport);
         boolean updated = updateOppositionCriterion();
 
-        GradingCriterion oppositionCriterion = findOppositionCriterion(OPPOSITION_SWEDISH);
+        GradingCriterion oppositionCriterion = findOppositionCriterion();
         assert oppositionCriterion != null;
         assertEquals(FEEDBACK, oppositionCriterion.getFeedback());
         assertEquals((Integer) oppositionCriterion.getMaxPoints(), oppositionCriterion.getPoints());
@@ -121,7 +120,7 @@ public class GradingReportServiceImplIntegrationTest extends IntegrationTest {
         setPointsOnAllCriteria(gradingReport, 1);
         updateOppositionCriterion();
 
-        GradingCriterion oppositionCriterion = findOppositionCriterion(OPPOSITION_SWEDISH);
+        GradingCriterion oppositionCriterion = findOppositionCriterion();
         assert oppositionCriterion != null;
         assertEquals(FEEDBACK_ON_OPPOSITION, oppositionCriterion.getFeedback());
         assertEquals(OPPOSITION_CRITERION_POINTS, oppositionCriterion.getPoints());
@@ -134,7 +133,7 @@ public class GradingReportServiceImplIntegrationTest extends IntegrationTest {
         setPointsOnAllCriteria(gradingReport, points);
         updateOppositionCriterion();
 
-        GradingCriterion oppositionCriterion = findOppositionCriterion(OPPOSITION_SWEDISH);
+        GradingCriterion oppositionCriterion = findOppositionCriterion();
         assert oppositionCriterion != null;
         assertEquals((Integer) points, oppositionCriterion.getPoints());
         assertNull(oppositionCriterion.getFeedback());
@@ -150,9 +149,9 @@ public class GradingReportServiceImplIntegrationTest extends IntegrationTest {
         return gradingReportService.updateOppositionCriteria(gradingReport, opposition);
     }
 
-    private GradingCriterion findOppositionCriterion(String title) {
+    private GradingCriterion findOppositionCriterion() {
         for (GradingCriterion gradingCriterion : gradingReport.getIndividualCriteria()) {
-            if (gradingCriterion.getTitle().equals(title)) {
+            if (gradingCriterion.getFlag() == AbstractGradingCriterion.Flag.OPPOSITION) {
                 return gradingCriterion;
             }
         }
@@ -246,12 +245,13 @@ public class GradingReportServiceImplIntegrationTest extends IntegrationTest {
 
     private GradingReportTemplate createOppositionCriteria(GradingReportTemplate gradingReportTemplate, int maxPoints) {
         List<GradingCriterionPointTemplate> gradingCriterionPointTemplates = getPointTemplates(maxPoints, "Opposition");
-        gradingReportTemplate.addIndividualCriterion(OPPOSITION_SWEDISH, OPPOSITION_ENGLISH, 0, gradingCriterionPointTemplates);
+        gradingReportTemplate.addIndividualCriterion("Ö1 Oppositionsrapport", "Ö1 Opposition report", 0, gradingCriterionPointTemplates, AbstractGradingCriterion.Flag.OPPOSITION);
         return save(gradingReportTemplate);
     }
 
     private GradingReportTemplate createGradingReportTemplate(ProjectType projectType) {
-        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(projectType, 30);
+        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(projectType,
+                LocalDate.of(2024, Month.JANUARY, 1));
         return save(gradingReportTemplate);
     }
 
diff --git a/core/src/test/java/se/su/dsv/scipro/report/GradingReportTemplateTest.java b/core/src/test/java/se/su/dsv/scipro/report/GradingReportTemplateTest.java
index 3a5f0fde9b..4f6b4adc27 100644
--- a/core/src/test/java/se/su/dsv/scipro/report/GradingReportTemplateTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/report/GradingReportTemplateTest.java
@@ -11,6 +11,7 @@ import se.su.dsv.scipro.system.ProjectType;
 import se.su.dsv.scipro.system.User;
 
 import java.time.LocalDate;
+import java.time.Month;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -34,7 +35,7 @@ public class GradingReportTemplateTest {
     @Test
     public void creating_a_grading_report_template_with_null_project_type_should_fail() {
         assertThrows(IllegalArgumentException.class, () ->
-                new GradingReportTemplate(null, 30));
+                new GradingReportTemplate(null, LocalDate.of(2024, Month.JANUARY, 1)));
     }
 
     @Test
@@ -100,6 +101,6 @@ public class GradingReportTemplateTest {
     }
 
     private GradingReportTemplate createBachelorTemplate() {
-        return new GradingReportTemplate(bachelor, 30);
+        return new GradingReportTemplate(bachelor, LocalDate.of(2024, Month.JANUARY, 1));
     }
 }
diff --git a/core/src/test/java/se/su/dsv/scipro/report/OppositionReportServiceImplTest.java b/core/src/test/java/se/su/dsv/scipro/report/OppositionReportServiceImplTest.java
index 2e74c37739..ebf8e6d827 100644
--- a/core/src/test/java/se/su/dsv/scipro/report/OppositionReportServiceImplTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/report/OppositionReportServiceImplTest.java
@@ -13,6 +13,7 @@ import se.su.dsv.scipro.file.FileService;
 import se.su.dsv.scipro.finalseminar.FinalSeminar;
 import se.su.dsv.scipro.finalseminar.FinalSeminarOpposition;
 import se.su.dsv.scipro.finalseminar.FinalSeminarOppositionRepo;
+import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.DegreeType;
 import se.su.dsv.scipro.system.ProjectType;
 import se.su.dsv.scipro.system.User;
@@ -20,12 +21,13 @@ import se.su.dsv.scipro.test.DomainObjects;
 import se.su.dsv.scipro.test.ObjectMother;
 import se.su.dsv.scipro.test.UserBuilder;
 
+import java.time.LocalDate;
+import java.time.Month;
 import java.time.ZonedDateTime;
 import java.util.*;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
 
 @ExtendWith(MockitoExtension.class)
 public class OppositionReportServiceImplTest {
@@ -49,7 +51,7 @@ public class OppositionReportServiceImplTest {
 
     @BeforeEach
     public void setUp() throws Exception {
-        GradingReportTemplate template = new GradingReportTemplate(BACHELOR, 30);
+        GradingReportTemplate template = new GradingReportTemplate(BACHELOR, LocalDate.of(2024, Month.JANUARY, 1));
         List<GradingCriterionPointTemplate> gradingCriterionPointTemplates = new ArrayList<>();
         gradingCriterionPointTemplates.add(new GradingCriterionPointTemplate.Builder()
                 .point(1)
@@ -73,7 +75,7 @@ public class OppositionReportServiceImplTest {
     public void create_report_if_not_existing() {
         GradingReportTemplate template = Mockito.mock(GradingReportTemplate.class);
         Mockito.when(template.createOppositionReport(any(FinalSeminarOpposition.class))).thenReturn(oppositionReport);
-        Mockito.when(gradingReportTemplateRepo.findByProjectTypeAndCredits(any(ProjectType.class), anyInt())).thenReturn(template);
+        Mockito.when(gradingReportTemplateRepo.getTemplate(any(Project.class))).thenReturn(template);
         Mockito.when(oppositionReportRepo.save(oppositionReport)).thenReturn(oppositionReport);
         assertEquals(oppositionReport, oppositionReportService.findOrCreateReport(createFinalSeminarOpposition()));
     }
diff --git a/core/src/test/java/se/su/dsv/scipro/report/OppositionReportTest.java b/core/src/test/java/se/su/dsv/scipro/report/OppositionReportTest.java
index 344f6cf502..6cdae1e826 100644
--- a/core/src/test/java/se/su/dsv/scipro/report/OppositionReportTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/report/OppositionReportTest.java
@@ -6,6 +6,8 @@ import se.su.dsv.scipro.finalseminar.FinalSeminarOpposition;
 import se.su.dsv.scipro.system.DegreeType;
 import se.su.dsv.scipro.system.ProjectType;
 
+import java.time.LocalDate;
+import java.time.Month;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -20,7 +22,8 @@ public class OppositionReportTest {
     public void prepareTemplate() {
         ProjectType bachelor = new ProjectType(DegreeType.BACHELOR, "bachelor", "bachelor");
 
-        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(bachelor, 30);
+        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(bachelor,
+                LocalDate.of(2024, Month.JANUARY, 1));
         addCriteria(gradingReportTemplate);
 
         oppositionReport = gradingReportTemplate.createOppositionReport(new FinalSeminarOpposition());
diff --git a/core/src/test/java/se/su/dsv/scipro/report/SupervisorGradingReportFactoryTest.java b/core/src/test/java/se/su/dsv/scipro/report/SupervisorGradingReportFactoryTest.java
index 383e39a53d..46f39e4a7d 100644
--- a/core/src/test/java/se/su/dsv/scipro/report/SupervisorGradingReportFactoryTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/report/SupervisorGradingReportFactoryTest.java
@@ -3,6 +3,8 @@ package se.su.dsv.scipro.report;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import java.time.LocalDate;
+import java.time.Month;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -16,7 +18,7 @@ public class SupervisorGradingReportFactoryTest {
 
     @BeforeEach
     public void setUp() throws Exception {
-        gradingReportTemplate = new GradingReportTemplate(BACHELOR, 30);
+        gradingReportTemplate = new GradingReportTemplate(BACHELOR, LocalDate.of(2024, Month.JANUARY, 1));
         List<GradingCriterionPointTemplate> gradingCriterionPointTemplates = new ArrayList<>();
         gradingCriterionPointTemplates.add(new GradingCriterionPointTemplate.Builder()
                 .point(1)
diff --git a/core/src/test/java/se/su/dsv/scipro/report/SupervisorGradingReportTest.java b/core/src/test/java/se/su/dsv/scipro/report/SupervisorGradingReportTest.java
index 4e618dc351..2cf70d275e 100644
--- a/core/src/test/java/se/su/dsv/scipro/report/SupervisorGradingReportTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/report/SupervisorGradingReportTest.java
@@ -8,6 +8,7 @@ import se.su.dsv.scipro.system.ProjectType;
 import se.su.dsv.scipro.system.User;
 
 import java.time.LocalDate;
+import java.time.Month;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -25,7 +26,7 @@ public class SupervisorGradingReportTest {
         ProjectType bachelor = new ProjectType(DegreeType.BACHELOR, "bachelor", "bachelor");
         project = Project.builder().title("Foo").projectType(bachelor).startDate(LocalDate.now()).build();
 
-        gradingReportTemplate = new GradingReportTemplate(bachelor, 30);
+        gradingReportTemplate = new GradingReportTemplate(bachelor, LocalDate.of(2024, Month.JANUARY, 1));
         addCriteria(gradingReportTemplate);
 
         gradingReport = gradingReportTemplate.createSupervisorReport(project, new User());
@@ -73,12 +74,12 @@ public class SupervisorGradingReportTest {
         gradingReportTemplate.addProjectCriterion("U12 Källhänvisningar och dokumentation", "U12 References and documentation", 1, getPointTemplates(1)).setFx(false);
         gradingReportTemplate.addProjectCriterion("U13 Originalitet och signifikans", "U13 Originality and significance", 0, getPointTemplates(3)).setFx(false);
 
-        gradingReportTemplate.addIndividualCriterion("Ö1 Oppositionsrapport", "Ö1 Opposition report", 1, getPointTemplates(2));
+        gradingReportTemplate.addIndividualCriterion("Ö1 Oppositionsrapport", "Ö1 Opposition report", 1, getPointTemplates(2), AbstractGradingCriterion.Flag.OPPOSITION);
         gradingReportTemplate.addIndividualCriterion("Ö2 Presentationer", "Ö2 Presentations", 1, getPointTemplates(1));
         gradingReportTemplate.addIndividualCriterion("Ö3 Aktivitet vid seminarier och möten", "Ö3 Participation in seminars and meetings", 1, getPointTemplates(1));
         gradingReportTemplate.addIndividualCriterion("Ö4 Deadlines", "Ö4 Deadlines", 0, getPointTemplates(1)).setFx(false);
         gradingReportTemplate.addIndividualCriterion("Ö5 Revision efter slutseminarium", "Ö5 Revisions after the final seminar", 0, getPointTemplates(1)).setFx(false);
-        gradingReportTemplate.addIndividualCriterion("Ö6 Reflektion", "Ö6 Reflection", 0, getPointTemplates(1)).setFx(false);
+        gradingReportTemplate.addIndividualCriterion("Ö6 Reflektion", "Ö6 Reflection", 0, getPointTemplates(1), AbstractGradingCriterion.Flag.REFLECTION).setFx(false);
     }
 
     private List<GradingCriterionPointTemplate> getPointTemplates(int maxPoint) {
diff --git a/core/src/test/java/se/su/dsv/scipro/report/calculators/original/SupervisorBachelorGradeCalculatorTest.java b/core/src/test/java/se/su/dsv/scipro/report/calculators/original/SupervisorBachelorGradeCalculatorTest.java
index b3a06857d3..f0c024be16 100644
--- a/core/src/test/java/se/su/dsv/scipro/report/calculators/original/SupervisorBachelorGradeCalculatorTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/report/calculators/original/SupervisorBachelorGradeCalculatorTest.java
@@ -6,6 +6,8 @@ import se.su.dsv.scipro.report.*;
 import se.su.dsv.scipro.system.User;
 import se.su.dsv.scipro.test.ObjectMother;
 
+import java.time.LocalDate;
+import java.time.Month;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -85,7 +87,8 @@ public class SupervisorBachelorGradeCalculatorTest extends GradeCalculatorTest {
 
     @Override
     protected GradingReportTemplate prepareTemplate() {
-        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(ObjectMother.BACHELOR, 30);
+        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(ObjectMother.BACHELOR,
+                LocalDate.of(2024, Month.JANUARY, 1));
 
         gradingReportTemplate.addProjectCriterion("U1 Sammanfattning", "U1 Abstract", 1, getPointTemplates(1));
         gradingReportTemplate.addProjectCriterion("U2 Introduktion", "U2 Introduction", 1, getPointTemplates(1));
@@ -101,12 +104,12 @@ public class SupervisorBachelorGradeCalculatorTest extends GradeCalculatorTest {
         gradingReportTemplate.addProjectCriterion("U12 Källhänvisningar och dokumentation", "U12 References and documentation", 1, getPointTemplates(1));
         gradingReportTemplate.addProjectCriterion("U13 Originalitet och signifikans", "U13 Originality and significance", 0, getPointTemplates(3)).setFx(false);
 
-        gradingReportTemplate.addIndividualCriterion("Ö1 Oppositionsrapport", "Ö1 Opposition report", 1, getPointTemplates(2));
+        gradingReportTemplate.addIndividualCriterion("Ö1 Oppositionsrapport", "Ö1 Opposition report", 1, getPointTemplates(2), AbstractGradingCriterion.Flag.OPPOSITION);
         gradingReportTemplate.addIndividualCriterion("Ö2 Presentationer", "Ö2 Presentations", 1, getPointTemplates(1));
         gradingReportTemplate.addIndividualCriterion("Ö3 Aktivitet vid seminarier och möten", "Ö3 Participation in seminars and meetings", 1, getPointTemplates(1));
         gradingReportTemplate.addIndividualCriterion("Ö4 Deadlines", "Ö4 Deadlines", 0, getPointTemplates(1)).setFx(false);
         gradingReportTemplate.addIndividualCriterion("Ö5 Revision efter slutseminarium", "Ö5 Revisions after the final seminar", 0, getPointTemplates(1)).setFx(false);
-        gradingReportTemplate.addIndividualCriterion("Ö6 Reflektion", "Ö6 Reflection", 0, getPointTemplates(1)).setFx(false);
+        gradingReportTemplate.addIndividualCriterion("Ö6 Reflektion", "Ö6 Reflection", 0, getPointTemplates(1), AbstractGradingCriterion.Flag.REFLECTION).setFx(false);
 
         return gradingReportTemplate;
     }
diff --git a/core/src/test/java/se/su/dsv/scipro/report/calculators/original/SupervisorMaster15GradeCalculatorTest.java b/core/src/test/java/se/su/dsv/scipro/report/calculators/original/SupervisorMaster15GradeCalculatorTest.java
index 18981669cb..da7d4ea369 100644
--- a/core/src/test/java/se/su/dsv/scipro/report/calculators/original/SupervisorMaster15GradeCalculatorTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/report/calculators/original/SupervisorMaster15GradeCalculatorTest.java
@@ -6,6 +6,8 @@ import se.su.dsv.scipro.report.*;
 import se.su.dsv.scipro.system.User;
 import se.su.dsv.scipro.test.ObjectMother;
 
+import java.time.LocalDate;
+import java.time.Month;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -72,7 +74,8 @@ public class SupervisorMaster15GradeCalculatorTest extends GradeCalculatorTest {
 
     @Override
     protected GradingReportTemplate prepareTemplate() {
-        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(ObjectMother.MASTER, 15);
+        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(ObjectMother.MASTER,
+                LocalDate.of(2024, Month.JANUARY, 1));
 
         gradingReportTemplate.addProjectCriterion("U1 Sammanfattning", "U1 Abstract", 1, getPointTemplates(1));
         gradingReportTemplate.addProjectCriterion("U2 Introduktion", "U2 Introduction", 1, getPointTemplates(1));
@@ -88,12 +91,12 @@ public class SupervisorMaster15GradeCalculatorTest extends GradeCalculatorTest {
         gradingReportTemplate.addProjectCriterion("U12 Källhänvisningar och dokumentation", "U12 References and documentation", 1, getPointTemplates(1));
         gradingReportTemplate.addProjectCriterion("U13 Originalitet och signifikans", "U13 Originality and significance", 1, getPointTemplates(3));
 
-        gradingReportTemplate.addIndividualCriterion("Ö1 Oppositionsrapport", "Ö1 Opposition report", 1, getPointTemplates(2));
+        gradingReportTemplate.addIndividualCriterion("Ö1 Oppositionsrapport", "Ö1 Opposition report", 1, getPointTemplates(2), AbstractGradingCriterion.Flag.OPPOSITION);
         gradingReportTemplate.addIndividualCriterion("Ö2 Presentationer", "Ö2 Presentations", 1, getPointTemplates(1));
         gradingReportTemplate.addIndividualCriterion("Ö3 Aktivitet vid seminarier och möten", "Ö3 Participation in seminars and meetings", 1, getPointTemplates(1));
         gradingReportTemplate.addIndividualCriterion("Ö4 Deadlines", "Ö4 Deadlines", 0, getPointTemplates(1)).setFx(false);
         gradingReportTemplate.addIndividualCriterion("Ö5 Revision efter slutseminarium", "Ö5 Revisions after the final seminar", 0, getPointTemplates(1)).setFx(false);
-        gradingReportTemplate.addIndividualCriterion("Ö6 Reflektion", "Ö6 Reflection", 0, getPointTemplates(1)).setFx(false);
+        gradingReportTemplate.addIndividualCriterion("Ö6 Reflektion", "Ö6 Reflection", 0, getPointTemplates(1), AbstractGradingCriterion.Flag.REFLECTION).setFx(false);
 
         return gradingReportTemplate;
     }
diff --git a/core/src/test/java/se/su/dsv/scipro/report/calculators/original/SupervisorMaster30GradeCalculatorTest.java b/core/src/test/java/se/su/dsv/scipro/report/calculators/original/SupervisorMaster30GradeCalculatorTest.java
index 70bf7076cc..1f21c29600 100644
--- a/core/src/test/java/se/su/dsv/scipro/report/calculators/original/SupervisorMaster30GradeCalculatorTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/report/calculators/original/SupervisorMaster30GradeCalculatorTest.java
@@ -6,6 +6,8 @@ import se.su.dsv.scipro.report.*;
 import se.su.dsv.scipro.system.User;
 import se.su.dsv.scipro.test.ObjectMother;
 
+import java.time.LocalDate;
+import java.time.Month;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -72,7 +74,8 @@ public class SupervisorMaster30GradeCalculatorTest extends GradeCalculatorTest {
 
     @Override
     protected GradingReportTemplate prepareTemplate() {
-        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(ObjectMother.MASTER, 30);
+        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(ObjectMother.MASTER,
+                LocalDate.of(2024, Month.JANUARY, 1));
 
         gradingReportTemplate.addProjectCriterion("U1 Sammanfattning", "U1 Abstract", 1, getPointTemplates(1));
         gradingReportTemplate.addProjectCriterion("U2 Introduktion", "U2 Introduction", 1, getPointTemplates(1));
@@ -88,12 +91,12 @@ public class SupervisorMaster30GradeCalculatorTest extends GradeCalculatorTest {
         gradingReportTemplate.addProjectCriterion("U12 Källhänvisningar och dokumentation", "U12 References and documentation", 1, getPointTemplates(1));
         gradingReportTemplate.addProjectCriterion("U13 Originalitet och signifikans", "U13 Originality and significance", 2, getPointTemplates(4));
 
-        gradingReportTemplate.addIndividualCriterion("Ö1 Oppositionsrapport", "Ö1 Opposition report", 1, getPointTemplates(2));
+        gradingReportTemplate.addIndividualCriterion("Ö1 Oppositionsrapport", "Ö1 Opposition report", 1, getPointTemplates(2), AbstractGradingCriterion.Flag.OPPOSITION);
         gradingReportTemplate.addIndividualCriterion("Ö2 Presentationer", "Ö2 Presentations", 1, getPointTemplates(1));
         gradingReportTemplate.addIndividualCriterion("Ö3 Aktivitet vid seminarier och möten", "Ö3 Participation in seminars and meetings", 1, getPointTemplates(1));
         gradingReportTemplate.addIndividualCriterion("Ö4 Deadlines", "Ö4 Deadlines", 0, getPointTemplates(1));
         gradingReportTemplate.addIndividualCriterion("Ö5 Revision efter slutseminarium", "Ö5 Revisions after the final seminar", 0, getPointTemplates(1));
-        gradingReportTemplate.addIndividualCriterion("Ö6 Reflektion", "Ö6 Reflection", 0, getPointTemplates(1)).setFx(false);
+        gradingReportTemplate.addIndividualCriterion("Ö6 Reflektion", "Ö6 Reflection", 0, getPointTemplates(1), AbstractGradingCriterion.Flag.REFLECTION).setFx(false);
 
         return gradingReportTemplate;
     }
diff --git a/core/src/test/java/se/su/dsv/scipro/springdata/serviceimpls/SupervisorServiceImplTest.java b/core/src/test/java/se/su/dsv/scipro/springdata/serviceimpls/SupervisorServiceImplTest.java
index 2b6ac80265..50594a1acd 100644
--- a/core/src/test/java/se/su/dsv/scipro/springdata/serviceimpls/SupervisorServiceImplTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/springdata/serviceimpls/SupervisorServiceImplTest.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.springdata.serviceimpls;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.PageRequest;
+import se.su.dsv.scipro.system.PageRequest;
 import se.su.dsv.scipro.security.auth.roles.Roles;
 import se.su.dsv.scipro.springdata.services.SupervisorService;
 import se.su.dsv.scipro.system.ResearchArea;
diff --git a/core/src/test/java/se/su/dsv/scipro/system/UserServiceImplIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/system/UserServiceImplIntegrationTest.java
index 8a8d7bd372..506e739ca0 100644
--- a/core/src/test/java/se/su/dsv/scipro/system/UserServiceImplIntegrationTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/system/UserServiceImplIntegrationTest.java
@@ -2,7 +2,6 @@ package se.su.dsv.scipro.system;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.PageRequest;
 import se.su.dsv.scipro.security.auth.roles.Roles;
 import se.su.dsv.scipro.test.IntegrationTest;
 
diff --git a/daisy-integration/src/main/java/se/su/dsv/scipro/daisyExternal/impl/ExternalImporterDaisyImpl.java b/daisy-integration/src/main/java/se/su/dsv/scipro/daisyExternal/impl/ExternalImporterDaisyImpl.java
index b73c8bb2e1..3d4acbe581 100755
--- a/daisy-integration/src/main/java/se/su/dsv/scipro/daisyExternal/impl/ExternalImporterDaisyImpl.java
+++ b/daisy-integration/src/main/java/se/su/dsv/scipro/daisyExternal/impl/ExternalImporterDaisyImpl.java
@@ -3,7 +3,7 @@ package se.su.dsv.scipro.daisyExternal.impl;
 import com.google.inject.persist.Transactional;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.data.domain.PageRequest;
+import se.su.dsv.scipro.system.PageRequest;
 import se.su.dsv.scipro.daisyExternal.ExternalImporter;
 import se.su.dsv.scipro.daisyExternal.ImporterTransactions;
 import se.su.dsv.scipro.daisyExternal.exceptions.ExternalImportException;
diff --git a/pom.xml b/pom.xml
index 271b096252..d222069a72 100755
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
         <!-- Dependency versions -->
         <slf4j.version>2.0.7</slf4j.version>
         <log4j2.version>2.20.0</log4j2.version>
-        <wicket.version>10.0.0</wicket.version>
+        <wicket.version>10.1.0</wicket.version>
 
         <!-- See https://hibernate.org/orm/releases/ for which version Hibernate implements -->
         <jakarta.persistence-api.version>3.1.0</jakarta.persistence-api.version>
diff --git a/view/src/main/java/se/su/dsv/scipro/SciProApplication.java b/view/src/main/java/se/su/dsv/scipro/SciProApplication.java
index e02ddcfc74..ebbc21afa8 100755
--- a/view/src/main/java/se/su/dsv/scipro/SciProApplication.java
+++ b/view/src/main/java/se/su/dsv/scipro/SciProApplication.java
@@ -14,6 +14,10 @@ import org.apache.wicket.util.convert.converter.LocalDateConverter;
 import org.apache.wicket.util.convert.converter.LocalDateTimeConverter;
 import se.su.dsv.scipro.activityplan.*;
 import se.su.dsv.scipro.admin.pages.*;
+import se.su.dsv.scipro.admin.pages.grading.AdminGradingTemplateCreationPage;
+import se.su.dsv.scipro.admin.pages.grading.AdminGradingTemplateEditPage;
+import se.su.dsv.scipro.admin.pages.grading.AdminGradingTemplatePage;
+import se.su.dsv.scipro.admin.pages.grading.AdminGradingTemplatesOverviewPage;
 import se.su.dsv.scipro.admin.pages.settings.*;
 import se.su.dsv.scipro.applicationperiod.AdminEditApplicationPeriodExemptionsPage;
 import se.su.dsv.scipro.applicationperiod.AdminEditApplicationPeriodPage;
@@ -282,6 +286,10 @@ public class SciProApplication extends LifecycleManagedWebApplication {
         mountPage("admin/project/survey", AdminSurveyPage.class);
         mountPage("admin/project/reviewer", AdminAssignReviewerPage.class);
         mountPage("admin/project/reviewer/capacity", AdminReviewerCapacityManagementPage.class);
+        mountPage("admin/project/grading/template", AdminGradingTemplatesOverviewPage.class);
+        mountPage("admin/project/grading/template/view", AdminGradingTemplatePage.class);
+        mountPage("admin/project/grading/template/edit", AdminGradingTemplateEditPage.class);
+        mountPage("admin/project/grading/template/create", AdminGradingTemplateCreationPage.class);
         mountPage("admin/edit", AdminEditProjectPage.class);
         mountPage("admin/finalseminars", AdminFinalSeminarPage.class);
         mountPage("admin/finalseminars/exemptions", AdminFinalSeminarExemptionPage.class);
diff --git a/view/src/main/java/se/su/dsv/scipro/activityplan/UpcomingActivitiesPanel.java b/view/src/main/java/se/su/dsv/scipro/activityplan/UpcomingActivitiesPanel.java
index fee31e277a..d3d400648c 100644
--- a/view/src/main/java/se/su/dsv/scipro/activityplan/UpcomingActivitiesPanel.java
+++ b/view/src/main/java/se/su/dsv/scipro/activityplan/UpcomingActivitiesPanel.java
@@ -8,8 +8,8 @@ import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.LambdaModel;
 import org.apache.wicket.model.StringResourceModel;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.PageRequest;
+import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.components.DateLabel;
 import se.su.dsv.scipro.data.enums.DateStyle;
 import se.su.dsv.scipro.project.Project;
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AbstractAdminProjectPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AbstractAdminProjectPage.java
index cb15d8e777..8fa344c822 100644
--- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AbstractAdminProjectPage.java
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AbstractAdminProjectPage.java
@@ -1,6 +1,8 @@
 package se.su.dsv.scipro.admin.pages;
 
 import se.su.dsv.scipro.activityplan.AdminActivityPlanTemplatesPage;
+import se.su.dsv.scipro.admin.pages.grading.AdminGradingTemplatesOverviewPage;
+import se.su.dsv.scipro.admin.pages.grading.MenuHighlightGradingTemplates;
 import se.su.dsv.scipro.checklists.AdminChecklistPage;
 import se.su.dsv.scipro.components.AbstractMenuPanel;
 import se.su.dsv.scipro.components.menuhighlighting.MenuHighlightAdminActivityPlanTemplates;
@@ -28,7 +30,8 @@ public class AbstractAdminProjectPage extends AbstractAdminPage {
                         new MenuItem("Checklist templates", AdminChecklistPage.class, MenuHighlightAdminChecklist.class),
                         new MenuItem("Non work days", NonWorkDaysPage.class),
                         new MenuItem("Survey", AdminSurveyPage.class),
-                        new MenuItem("Reviewer targets", AdminReviewerCapacityManagementPage.class)
+                        new MenuItem("Reviewer targets", AdminReviewerCapacityManagementPage.class),
+                        new MenuItem("Grading templates", AdminGradingTemplatesOverviewPage.class, MenuHighlightGradingTemplates.class)
                 );
             }
 
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateCreationPage.html b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateCreationPage.html
new file mode 100644
index 0000000000..8fd44e66ab
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateCreationPage.html
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+<body>
+<wicket:extend>
+    <h1>Create a new grading report template</h1>
+
+    <div wicket:id="feedback"></div>
+
+    <form wicket:id="form" class="line-length-limit">
+        <div class="mb-3">
+            <label class="form-label" wicket:for="project_type">Project type</label>
+            <select wicket:id="project_type" class="form-select">
+                <option value="bachelor">Bachelor</option>
+                <option value="master">Master</option>
+            </select>
+        </div>
+
+        <div wicket:id="grading_template_component_panel"></div>
+
+        <div class="position-sticky bottom-0 bg-white p-3 border line-length-limit">
+            <button type="submit" class="btn btn-primary">Create</button>
+        </div>
+    </form>
+</wicket:extend>
+</body>
+</html>
\ No newline at end of file
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateCreationPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateCreationPage.java
new file mode 100644
index 0000000000..c14d4925db
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateCreationPage.java
@@ -0,0 +1,80 @@
+package se.su.dsv.scipro.admin.pages.grading;
+
+import jakarta.inject.Inject;
+import org.apache.wicket.RestartResponseException;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.LambdaChoiceRenderer;
+import org.apache.wicket.markup.html.panel.FeedbackPanel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import se.su.dsv.scipro.admin.pages.AbstractAdminProjectPage;
+import se.su.dsv.scipro.components.AjaxDropDownChoice;
+import se.su.dsv.scipro.data.DetachableServiceModel;
+import se.su.dsv.scipro.grading.GradingReportTemplateService;
+import se.su.dsv.scipro.grading.GradingReportTemplateUpdate;
+import se.su.dsv.scipro.report.DuplicateDateException;
+import se.su.dsv.scipro.report.GradingReportTemplate;
+import se.su.dsv.scipro.report.ValidDateMustBeInTheFutureException;
+import se.su.dsv.scipro.system.ProjectType;
+import se.su.dsv.scipro.system.ProjectTypeService;
+
+public class AdminGradingTemplateCreationPage extends AbstractAdminProjectPage implements MenuHighlightGradingTemplates {
+
+    @Inject
+    GradingReportTemplateService gradingReportTemplateService;
+    @Inject
+    ProjectTypeService projectTypeService;
+
+    private final IModel<ProjectType> projectTypeModel;
+    private EditingGradingTemplate editingGradingTemplateModel;
+
+    public AdminGradingTemplateCreationPage() {
+        projectTypeModel = new DetachableServiceModel<>(projectTypeService);
+
+        add(new FeedbackPanel("feedback"));
+
+        Form<?> form = new Form<>("form") {
+            @Override
+            protected void onSubmit() {
+                super.onSubmit();
+                GradingReportTemplateUpdate update = AdminGradingTemplateEditPage.toUpdate(editingGradingTemplateModel);
+                try {
+                    GradingReportTemplate gradingReportTemplate = gradingReportTemplateService.create(
+                            projectTypeModel.getObject(),
+                            update);
+                    getSession().success(getString("template_created", projectTypeModel));
+                    PageParameters pageParameters = AdminGradingTemplateEditPage.getPageParameters(gradingReportTemplate);
+                    throw new RestartResponseException(AdminGradingTemplateEditPage.class, pageParameters);
+                } catch (ValidDateMustBeInTheFutureException e) {
+                    error(getString("valid_from_must_be_in_the_future", () -> e));
+                } catch (DuplicateDateException e) {
+                    error(getString("another_template_exists_for_the_given_date_date", () -> e));
+                }
+            }
+        };
+        form.setOutputMarkupId(true);
+        add(form);
+
+        form.add(new AjaxDropDownChoice<>(
+                "project_type",
+                projectTypeModel,
+                gradingReportTemplateService.getProjectTypes(),
+                new LambdaChoiceRenderer<>(ProjectType::getName, ProjectType::getId)) {
+            @Override
+            public void onNewSelection(AjaxRequestTarget target, ProjectType objectSelected) {
+                target.add(form);
+            }
+        });
+
+        editingGradingTemplateModel = new EditingGradingTemplate();
+        form.add(new EditingGradingTemplateComponentPanel("grading_template_component_panel", Model.of(editingGradingTemplateModel)) {
+            @Override
+            protected void onConfigure() {
+                super.onConfigure();
+                setVisible(projectTypeModel.getObject() != null);
+            }
+        });
+    }
+}
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateEditPage.html b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateEditPage.html
new file mode 100644
index 0000000000..9e28766880
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateEditPage.html
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+<body>
+<wicket:extend>
+    <div wicket:id="feedback"></div>
+    <form wicket:id="form">
+        <div wicket:id="editing" class="mb-3"></div>
+        <div class="position-sticky bottom-0 bg-white p-3 border line-length-limit">
+            <button type="submit" class="btn btn-primary">Save</button>
+        </div>
+    </form>
+</wicket:extend>
+</body>
+</html>
\ No newline at end of file
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateEditPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateEditPage.java
new file mode 100644
index 0000000000..da4a0b42a7
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateEditPage.java
@@ -0,0 +1,130 @@
+package se.su.dsv.scipro.admin.pages.grading;
+
+import jakarta.inject.Inject;
+import org.apache.wicket.RestartResponseException;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.panel.FeedbackPanel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import se.su.dsv.scipro.admin.pages.AbstractAdminProjectPage;
+import se.su.dsv.scipro.grading.GradingReportTemplateService;
+import se.su.dsv.scipro.grading.GradingReportTemplateUpdate;
+import se.su.dsv.scipro.grading.LocalizedString;
+import se.su.dsv.scipro.report.DuplicateDateException;
+import se.su.dsv.scipro.report.GradingReportTemplate;
+import se.su.dsv.scipro.report.ValidDateMustBeInTheFutureException;
+import se.su.dsv.scipro.report.NoSuchTemplateException;
+import se.su.dsv.scipro.report.TemplateLockedException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AdminGradingTemplateEditPage extends AbstractAdminProjectPage implements MenuHighlightGradingTemplates {
+    private static final String GRADING_REPORT_TEMPLATE_ID_PARAMETER = "id";
+    public static final GradingReportTemplateUpdate.Criteria.Requirement ZERO_REQUIREMENT = new GradingReportTemplateUpdate.Criteria.Requirement(
+            0,
+            new LocalizedString("", ""));
+
+    @Inject
+    GradingReportTemplateService gradingReportTemplateService;
+
+    private EditingGradingTemplate editingGradingTemplate;
+
+    public AdminGradingTemplateEditPage(PageParameters pageParameters) {
+        long id = pageParameters.get(GRADING_REPORT_TEMPLATE_ID_PARAMETER).toLong();
+
+        GradingReportTemplate template = gradingReportTemplateService.getTemplate(id);
+
+        editingGradingTemplate = new EditingGradingTemplate(template);
+
+        add(new FeedbackPanel("feedback"));
+
+        Form<EditingGradingTemplate> form = new Form<>("form") {
+            @Override
+            protected void onSubmit() {
+                super.onSubmit();
+                try {
+                    GradingReportTemplateUpdate update = toUpdate(
+                            editingGradingTemplate);
+
+                    gradingReportTemplateService.update(id, update);
+                    success(getString("template_updated"));
+                } catch (ValidDateMustBeInTheFutureException e) {
+                    error(getString("valid_from_must_be_in_the_future", () -> e));
+                } catch (NoSuchTemplateException e) {
+                    throw new RestartResponseException(AdminGradingTemplatesOverviewPage.class);
+                } catch (DuplicateDateException e) {
+                    error(getString("another_template_exists_for_the_given_date_date", () -> e));
+                } catch (TemplateLockedException e) {
+                    error(getString("template_is_locked", () -> e));
+                }
+            }
+        };
+
+        form.add(new EditingGradingTemplateComponentPanel("editing", Model.of(editingGradingTemplate)));
+        add(form);
+    }
+
+    static GradingReportTemplateUpdate toUpdate(EditingGradingTemplate editingGradingTemplate) {
+        List<GradingReportTemplateUpdate.GradeLimit> gradeLimits = editingGradingTemplate
+                .getGradeLimits()
+                .getGradeLimits()
+                .stream()
+                .map(AdminGradingTemplateEditPage::toGrade)
+                .toList();
+        List<GradingReportTemplateUpdate.Criteria> criteria = editingGradingTemplate
+                .getCriteria()
+                .stream()
+                .map(AdminGradingTemplateEditPage::toCriteria)
+                .toList();
+        return new GradingReportTemplateUpdate(
+                editingGradingTemplate.getValidFrom(),
+                editingGradingTemplate.getNote(),
+                editingGradingTemplate.getGradeLimits().getFailingGrade(),
+                gradeLimits,
+                criteria);
+    }
+
+    private static GradingReportTemplateUpdate.Criteria toCriteria(EditingGradingTemplate.Criteria criteria) {
+        ArrayList<GradingReportTemplateUpdate.Criteria.Requirement> requirements = new ArrayList<>();
+        requirements.add(ZERO_REQUIREMENT);
+        for (int i = 0; i < criteria.getPoints().size(); i++) {
+            EditingGradingTemplate.Criteria.Point point = criteria.getPoints().get(i);
+            requirements.add(new GradingReportTemplateUpdate.Criteria.Requirement(
+                    i + 1,
+                    new LocalizedString(point.getRequirementEn(), point.getRequirementSv())));
+        }
+        return new GradingReportTemplateUpdate.Criteria(
+                new LocalizedString(criteria.getTitleEn(), criteria.getTitleSv()),
+                switch (criteria.getType()) {
+                    case PROJECT -> GradingReportTemplateUpdate.Criteria.Type.THESIS;
+                    case INDIVIDUAL -> GradingReportTemplateUpdate.Criteria.Type.INDIVIDUAL;
+                },
+                criteria.getPointsRequiredToPass(),
+                getFlag(criteria),
+                requirements);
+    }
+
+    private static GradingReportTemplateUpdate.Criteria.Flag getFlag(EditingGradingTemplate.Criteria criteria) {
+        if (criteria.getFlag() == null) {
+            return null;
+        }
+        return switch (criteria.getFlag()) {
+            case OPPOSITION -> GradingReportTemplateUpdate.Criteria.Flag.OPPOSITION;
+            case REFLECTION -> GradingReportTemplateUpdate.Criteria.Flag.REFLECTION;
+            //case null -> null; sigh old versions of Java
+        };
+    }
+
+    private static GradingReportTemplateUpdate.GradeLimit toGrade(GradeLimits.GradeLimit gradeLimit) {
+        return new GradingReportTemplateUpdate.GradeLimit(
+                gradeLimit.getGrade(),
+                gradeLimit.getLowerLimit());
+    }
+
+    public static PageParameters getPageParameters(GradingReportTemplate gradingReportTemplate) {
+        PageParameters pageParameters = new PageParameters();
+        pageParameters.add(GRADING_REPORT_TEMPLATE_ID_PARAMETER, gradingReportTemplate.getId());
+        return pageParameters;
+    }
+}
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplatePage.html b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplatePage.html
new file mode 100644
index 0000000000..970e9229d9
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplatePage.html
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+<head>
+    <title>View a specific grading report template</title>
+    <wicket:remove>
+        <link rel="stylesheet" type="text/css" href="../../../../../../../../webapp/css/scipro_m.css">
+        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
+        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
+    </wicket:remove>
+</head>
+<body>
+<wicket:extend>
+    <div class="line-length-limit" wicket:id="details">
+        <h1>
+            Grading report template for
+            <wicket:container wicket:id="project_type"/>
+            <small>
+                (<span wicket:id="valid_period"></span>)
+            </small>
+        </h1>
+        <h2>Criteria</h2>
+        <ol class="list-unstyled">
+            <li wicket:id="criteria" class="mb-3">
+                <h3 wicket:id="title"></h3>
+                <ol class="list-unstyled">
+                    <li wicket:id="points">
+                        <span wicket:id="point"></span> - <span wicket:id="description"></span>
+                    </li>
+                </ol>
+            </li>
+        </ol>
+    </div>
+</wicket:extend>
+</body>
+</html>
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplatePage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplatePage.java
new file mode 100644
index 0000000000..f445f860ee
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplatePage.java
@@ -0,0 +1,95 @@
+package se.su.dsv.scipro.admin.pages.grading;
+
+import jakarta.inject.Inject;
+import org.apache.wicket.RestartResponseException;
+import org.apache.wicket.markup.html.GenericWebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.LoadableDetachableModel;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import se.su.dsv.scipro.admin.pages.AbstractAdminProjectPage;
+import se.su.dsv.scipro.grading.GradingReportTemplateService;
+import se.su.dsv.scipro.report.GradingCriterionPointTemplate;
+import se.su.dsv.scipro.report.GradingCriterionTemplate;
+import se.su.dsv.scipro.report.GradingReportTemplate;
+import se.su.dsv.scipro.system.ProjectType;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class AdminGradingTemplatePage extends AbstractAdminProjectPage implements MenuHighlightGradingTemplates {
+    private static final String TEMPLATE_ID_PARAMETER = "id";
+
+    @Inject
+    private GradingReportTemplateService gradingReportTemplateService;
+
+    public static PageParameters getPageParameters(GradingReportTemplate gradingReportTemplate) {
+        PageParameters pageParameters = new PageParameters();
+        pageParameters.add(TEMPLATE_ID_PARAMETER, gradingReportTemplate.getId());
+        return pageParameters;
+    }
+
+    public AdminGradingTemplatePage(PageParameters parameters) {
+        long templateId = parameters.get(TEMPLATE_ID_PARAMETER).toLong();
+
+        IModel<GradingReportTemplate> template = LoadableDetachableModel.of(() ->
+                gradingReportTemplateService.getTemplate(templateId));
+
+        if (template.getObject() == null) {
+            throw new RestartResponseException(AdminGradingTemplatesOverviewPage.class);
+        }
+
+        add(new TemplateDetailsPanel("details", template));
+    }
+
+    private class TemplateDetailsPanel extends GenericWebMarkupContainer<GradingReportTemplate> {
+        public TemplateDetailsPanel(String id, IModel<GradingReportTemplate> model) {
+            super(id, model);
+
+            add(new Label("project_type", model.map(GradingReportTemplate::getProjectType).map(ProjectType::getName)));
+
+            IModel<String> validPeriod = () -> {
+                GradingReportTemplate template = model.getObject();
+                LocalDate validFrom = template.getValidFrom();
+                LocalDate endDate = gradingReportTemplateService.getEndDate(template);
+                if (endDate == null) {
+                    return "from " + validFrom + " and onwards";
+                } else {
+                    return "from " + validFrom + " to " + endDate;
+                }
+            };
+
+            add(new Label("valid_period", validPeriod));
+
+            add(new ListView<>("criteria", model.map(this::getCriteria)) {
+                @Override
+                protected void populateItem(ListItem<GradingCriterionTemplate> item) {
+                    item.add(new Label("title", item.getModel().map(GradingCriterionTemplate::getTitle)));
+                    item.add(new ListView<>(
+                            "points",
+                            item.getModel().map(GradingCriterionTemplate::getGradingCriterionPointTemplates))
+                    {
+                        @Override
+                        protected void populateItem(ListItem<GradingCriterionPointTemplate> item) {
+                            item.add(new Label("point", item.getModel().map(GradingCriterionPointTemplate::getPoint)));
+                            item.add(new Label(
+                                    "description",
+                                    item.getModel().map(GradingCriterionPointTemplate::getDescription)));
+                        }
+                    });
+                }
+            });
+        }
+
+        private List<GradingCriterionTemplate> getCriteria(GradingReportTemplate gradingReportTemplate) {
+            ArrayList<GradingCriterionTemplate> criteria = new ArrayList<>(gradingReportTemplate.getCriteria());
+            criteria.sort(Comparator.comparing(GradingCriterionTemplate::getSortOrder));
+            return Collections.unmodifiableList(criteria);
+        }
+    }
+}
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplatesOverviewPage.html b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplatesOverviewPage.html
new file mode 100644
index 0000000000..91b7896e26
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplatesOverviewPage.html
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+<head>
+    <wicket:remove>
+    <link rel="stylesheet" type="text/css" href="../../../../../../../../webapp/css/scipro_m.css">
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
+    </wicket:remove>
+</head>
+<body>
+<div class="container-fluid">
+<wicket:extend>
+<div class="line-length-limit">
+    <h1>Grading report template management</h1>
+    <p>
+        Here you can manage the grading templates that are used to grade projects.
+    </p>
+    <p>
+        A template consists of multiple criteria, each with a description and the maximum points that can be scored on it.
+
+        The point sum total conversion to grade letters is also defined in the template.
+    </p>
+    <p>
+        Each template has a period during which it is in use. The period is defined by a start date,
+        and it remains current until a new template takes over. There will only ever be one template
+        current for any given date.
+
+        The template that is selected to be used for grading a project is the one that was current the date
+        the project started.
+    </p>
+    <p>
+        Once a template has become current it is <strong><em>no longer possible</em></strong> to edit it.
+    </p>
+
+    <div class="btn-group mb-3" role="group">
+        <wicket:link>
+            <a href="AdminGradingTemplateCreationPage.html" class="btn btn-outline-primary">Create new template</a>
+        </wicket:link>
+    </div>
+
+    <div class="mb-3" wicket:id="project_types">
+        <h2 wicket:id="name">Bachelor</h2>
+
+        <div class="hstack gap-3 flex-wrap align-items-start" wicket:id="templates">
+            <div class="card" wicket:id="upcoming_templates">
+                <div class="card-header bg-info-subtle">
+                    Upcoming template
+                </div>
+                <div class="card-body">
+                    <dl>
+                        <dt>Valid from</dt>
+                        <dd wicket:id="valid_from">2024-10-31</dd>
+
+                        <wicket:enclosure>
+                            <dt>Note</dt>
+                            <dd wicket:id="note">Added a new criteria U14</dd>
+                        </wicket:enclosure>
+                    </dl>
+                    <span class="card-text">
+                        <a href="#" wicket:id="edit_template">View / Edit</a>
+                    </span>
+                </div>
+            </div>
+
+            <div class="card">
+                <div class="card-header bg-success-subtle">
+                    Current template
+                </div>
+                <div class="card-body">
+                    <dl>
+                        <dt>Valid from</dt>
+                        <dd wicket:id="valid_from">2024-01-01</dd>
+
+                        <wicket:enclosure>
+                            <dt>Note</dt>
+                            <dd wicket:id="note">Change ethics criteria to include AI</dd>
+                        </wicket:enclosure>
+                    </dl>
+                    <span class="card-text">
+                        <a href="#" wicket:id="view_template">View</a>
+                    </span>
+                </div>
+            </div>
+
+            <wicket:remove>
+            <div class="card">
+                <div class="card-header">
+                    Older templates
+                </div>
+                <div class="card-body hstack gap-2">
+                    <div class="mw-100">
+                        <dl>
+                            <dt>Valid</dt>
+                            <dd>2023-01-01 to 2023-12-41</dd>
+
+                            <dt>Note</dt>
+                            <dd>Change ethics criteria to include AI</dd>
+                        </dl>
+                        <span class="card-text">
+                            <a href="#">View</a>
+                        </span>
+                    </div>
+                    <div class="vr"></div>
+                    <div>
+                        <dl>
+                            <dt>Valid</dt>
+                            <dd>2001-01-01 to 2022-12-31</dd>
+
+                            <dt>Note</dt>
+                            <dd>Change ethics criteria to include AI</dd>
+                        </dl>
+                        <span class="card-text">
+                            <a href="#">View</a>
+                        </span>
+                    </div>
+                </div>
+            </div>
+            </wicket:remove>
+        </div>
+    </div>
+</div>
+</wicket:extend>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplatesOverviewPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplatesOverviewPage.java
new file mode 100644
index 0000000000..7d994d340f
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplatesOverviewPage.java
@@ -0,0 +1,57 @@
+package se.su.dsv.scipro.admin.pages.grading;
+
+import jakarta.inject.Inject;
+import org.apache.wicket.markup.html.GenericWebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.LoadableDetachableModel;
+import se.su.dsv.scipro.admin.pages.AbstractAdminProjectPage;
+import se.su.dsv.scipro.components.NonEmptyLabel;
+import se.su.dsv.scipro.grading.GradingReportTemplateService;
+import se.su.dsv.scipro.report.GradingReportTemplate;
+import se.su.dsv.scipro.system.ProjectType;
+
+import java.util.List;
+
+public class AdminGradingTemplatesOverviewPage extends AbstractAdminProjectPage {
+    @Inject
+    private GradingReportTemplateService gradingReportTemplateService;
+
+    public AdminGradingTemplatesOverviewPage() {
+        LoadableDetachableModel<List<ProjectType>> projectTypes =
+                LoadableDetachableModel.of(gradingReportTemplateService::getProjectTypes);
+
+        add(new ListView<>("project_types", projectTypes) {
+            @Override
+            protected void populateItem(ListItem<ProjectType> item) {
+                item.add(new Label("name", item.getModel().map(ProjectType::getName)));
+                item.add(new TemplatesPanel("templates", item.getModel()));
+            }
+        });
+    }
+
+    private class TemplatesPanel extends GenericWebMarkupContainer<ProjectType> {
+        public TemplatesPanel(String id, IModel<ProjectType> model) {
+            super(id, model);
+
+            IModel<GradingReportTemplate> currentTemplate = model.map(gradingReportTemplateService::getCurrentTemplate);
+
+            add(new Label("valid_from", currentTemplate.map(GradingReportTemplate::getValidFrom)));
+            add(new NonEmptyLabel("note", currentTemplate.map(GradingReportTemplate::getNote)));
+            add(new BookmarkablePageLink<>("view_template", AdminGradingTemplatePage.class, AdminGradingTemplatePage.getPageParameters(currentTemplate.getObject())));
+
+            IModel<List<GradingReportTemplate>> upcomingTemplates = model.map(gradingReportTemplateService::getUpcomingTemplates);
+            add(new ListView<>("upcoming_templates", upcomingTemplates) {
+                @Override
+                protected void populateItem(ListItem<GradingReportTemplate> item) {
+                    item.add(new Label("valid_from", item.getModel().map(GradingReportTemplate::getValidFrom)));
+                    item.add(new NonEmptyLabel("note", item.getModel().map(GradingReportTemplate::getNote)));
+                    item.add(new BookmarkablePageLink<>("edit_template", AdminGradingTemplateEditPage.class, AdminGradingTemplateEditPage.getPageParameters(item.getModelObject())));
+                }
+            });
+        }
+    }
+}
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplate.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplate.java
new file mode 100644
index 0000000000..7e4e935ad5
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplate.java
@@ -0,0 +1,184 @@
+package se.su.dsv.scipro.admin.pages.grading;
+
+import se.su.dsv.scipro.report.GradingCriterionPointTemplate;
+import se.su.dsv.scipro.report.GradingCriterionTemplate;
+import se.su.dsv.scipro.report.GradingReportTemplate;
+
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+
+class EditingGradingTemplate implements Serializable {
+    private String note;
+    private LocalDate validFrom;
+    private List<Criteria> criteria;
+    private GradeLimits gradeLimits;
+
+    public EditingGradingTemplate() {
+        this.gradeLimits = new GradeLimits();
+        this.criteria = new ArrayList<>();
+    }
+
+    EditingGradingTemplate(GradingReportTemplate template) {
+        this.note = template.getNote();
+        this.validFrom = template.getValidFrom();
+        this.gradeLimits = new GradeLimits(template);
+        this.criteria = new ArrayList<>();
+        for (var criteria : template.getCriteria()) {
+            Criteria editingCriteria = new Criteria(criteria);
+            this.criteria.add(editingCriteria);
+        }
+    }
+
+    public String getNote() {
+        return note;
+    }
+
+    public void setNote(String note) {
+        this.note = note;
+    }
+
+    public LocalDate getValidFrom() {
+        return validFrom;
+    }
+
+    public void setValidFrom(LocalDate validFrom) {
+        this.validFrom = validFrom;
+    }
+
+    public GradeLimits getGradeLimits() {
+        return gradeLimits;
+    }
+
+    public List<Criteria> getCriteria() {
+        return criteria;
+    }
+
+    public int getMaxPointsAvailable() {
+        return criteria.stream()
+                .mapToInt(Criteria::getMaxPoints)
+                .sum();
+    }
+
+    public void addCriteria() {
+        this.criteria.add(new Criteria());
+    }
+
+    class Criteria implements Serializable {
+        enum Flag {
+            OPPOSITION, REFLECTION
+        }
+
+        enum Type {
+            PROJECT, INDIVIDUAL
+        }
+
+        private String titleSv;
+        private String titleEn;
+        private List<Point> points = new ArrayList<>();
+        private Flag flag;
+        private Type type = Type.PROJECT;
+        private int pointsRequiredToPass;
+
+        Criteria(GradingCriterionTemplate criteria) {
+            this.titleSv = criteria.getTitle();
+            this.titleEn = criteria.getTitleEn();
+            this.pointsRequiredToPass = criteria.getPointsRequiredToPass();
+            for (var point : criteria.getGradingCriterionPointTemplates()) {
+                if (point.getPoint() == 0) continue;
+                Point editingPoint = new Point(point);
+                this.points.add(editingPoint);
+            }
+            this.flag = criteria.getFlag() == null ? null : switch (criteria.getFlag()) {
+                case OPPOSITION -> Flag.OPPOSITION;
+                case REFLECTION -> Flag.REFLECTION;
+            };
+        }
+
+        private Criteria() {}
+
+        public Type getType() {
+            return type;
+        }
+
+        public void setType(Type type) {
+            this.type = type;
+        }
+
+        public String getTitleSv() {
+            return titleSv;
+        }
+
+        public void setTitleSv(String titleSv) {
+            this.titleSv = titleSv;
+        }
+
+        public String getTitleEn() {
+            return titleEn;
+        }
+
+        public void setTitleEn(String titleEn) {
+            this.titleEn = titleEn;
+        }
+
+        public Flag getFlag() {
+            return flag;
+        }
+
+        public void setFlag(Flag flag) {
+            this.flag = flag;
+        }
+
+        public int getMaxPoints() {
+            return points.size();
+        }
+
+        public List<Point> getPoints() {
+            return points;
+        }
+
+        public void setPoints(List<Point> points) {
+            this.points = points;
+        }
+
+        public int getPointsRequiredToPass() {
+            return pointsRequiredToPass;
+        }
+
+        public void setPointsRequiredToPass(int pointsRequiredToPass) {
+            this.pointsRequiredToPass = pointsRequiredToPass;
+        }
+
+        class Point implements Serializable {
+            private String requirementEn;
+            private String requirementSv;
+
+            Point() {
+                this.requirementEn = "";
+                this.requirementSv = "";
+            }
+
+            Point(GradingCriterionPointTemplate point) {
+                this.requirementEn = point.getDescriptionEn();
+                this.requirementSv = point.getDescription();
+            }
+
+            public String getRequirementEn() {
+                return requirementEn;
+            }
+
+            public void setRequirementEn(String requirement) {
+                this.requirementEn = requirement;
+            }
+
+            public String getRequirementSv() {
+                return requirementSv;
+            }
+
+            public void setRequirementSv(String requirementSv) {
+                this.requirementSv = requirementSv;
+            }
+        }
+    }
+}
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplateComponentPanel.html b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplateComponentPanel.html
new file mode 100644
index 0000000000..80952a8f02
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplateComponentPanel.html
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+<head>
+    <wicket:remove>
+        <link rel="stylesheet" type="text/css" href="../../../../../../../../webapp/css/scipro_m.css">
+        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
+        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
+    </wicket:remove>
+</head>
+<body>
+<wicket:panel>
+    <div class="mb-3 line-length-limit">
+        <label class="form-label" wicket:for="valid_from">
+            Valid from
+        </label>
+        <input required type="text" class="form-control" wicket:id="valid_from">
+        <small class="text-muted">
+            When does this grading template take effect.
+        </small>
+    </div>
+
+    <div class="mb-3 line-length-limit">
+        <label class="form-label" wicket:for="note">
+            Note
+        </label>
+        <textarea class="form-control" wicket:id="note" rows="3"></textarea>
+        <small class="text-muted">
+            This note can be used to describe the template, for example, what changes have been made from the previous version.
+            It is purely an aid for administrators if necessary.
+        </small>
+    </div>
+
+    <fieldset wicket:id="grade_limits" class="line-length-limit">
+        <legend>Grade limits</legend>
+        <p>
+            A grade limit is a point requirement to get another grade, e.g. at least 30 points to receive an A.
+            The order in which you specify the grade limits does not matter.
+            The authors will receive the grade associated with the highest minimum point requirement they met.
+            If they do not meet any of the requirements, they will receive the failing grade.
+        </p>
+        <div class="mb-3">
+            <label class="form-label" wicket:for="failing_grade">Failing grade</label>
+            <input required type="text" class="form-control" wicket:id="failing_grade">
+            <small class="text-muted">The grade they receive if they do not meet the minimum points for any other grade.</small>
+        </div>
+
+        <wicket:container wicket:id="grade_limits">
+            <div class="row align-items-center mb-3" wicket:id="grade_limit">
+                <div class="col"><label class="form-label" wicket:for="minimum">Minimum points</label></div>
+                <div class="col"><input type="number" required class="form-control" min="1" wicket:id="minimum"></div>
+                <div class="col"><label class="form-label">Grade</label></div>
+                <div class="col"><input type="text" required class="form-control" wicket:id="grade"></div>
+                <div class="col-auto"><button class="btn btn-sm btn-outline-danger" wicket:id="remove">Remove</button></div>
+            </div>
+        </wicket:container>
+        <div class="mb-3">
+            <button class="btn btn-sm btn-outline-success" wicket:id="add">Add new grade limit</button>
+        </div>
+    </fieldset>
+
+    <p class="mb-3">
+        <wicket:message key="max_points_available">
+            The maximum number of points available is <strong wicket:id="max_points_available">8</strong> with the below criteria.
+        </wicket:message>
+    </p>
+
+    <div class="mb-3 line-length-limit card" wicket:id="criteria">
+        <div class="card-header text-bg-info text-white hstack justify-content-between">
+            <h3 class="text-white mb-0">Criterion</h3>
+            <button class="btn btn-sm btn-outline-danger" wicket:id="remove">Remove</button>
+        </div>
+        <div wicket:id="criteria" class="card-body">
+            <div class="row mb-3">
+                <div class="col">
+                    <label class="form-label" wicket:for="title_en">
+                        Title (english)
+                    </label>
+                    <input required class="form-control" wicket:id="title_en" type="text">
+                </div>
+                <div class="col">
+                    <label class="form-label" wicket:for="title_sv">
+                        Title (swedish)
+                    </label>
+                    <input required class="form-control" wicket:id="title_sv" type="text">
+                </div>
+            </div>
+
+            <div class="mb-3">
+                <label class="form-label" wicket:for="type">Type</label>
+                <select wicket:id="type" class="form-control"></select>
+                <small class="text-muted">Whether this criterion is assessed once for the thesis or per author.</small>
+            </div>
+
+            <div class="mb-3">
+                <label class="form-label" wicket:for="flag">Special flag</label>
+                <select class="form-control" wicket:id="flag"></select>
+                <small class="text-muted">You can flag a criteria if it is connected to some other part of the system</small>
+            </div>
+
+            <div class="mb-3">
+                <label class="form-label" wicket:for="points_required_to_pass">
+                    Points required to pass
+                </label>
+                <input required type="number" class="form-control" wicket:id="points_required_to_pass">
+                <small class="text-muted">
+                    If the author does not get at least this many points on this criterion,
+                    they get the default grade.
+                </small>
+            </div>
+
+            <fieldset>
+                <legend>Points</legend>
+
+                <ol class="list-unstyled">
+                    <li wicket:id="points">
+                        <fieldset wicket:id="point" class="card mb-3">
+                            <legend class="card-header hstack justify-content-between">
+                                <span>Requirement for <span wicket:id="point"></span> point(s)</span>
+                                <button class="btn btn-sm btn-outline-danger" wicket:id="remove">Remove</button>
+                            </legend>
+                            <div class="card-body">
+                                <div class="mb-3">
+                                    <label wicket:for="requirement_en">
+                                        Requirement (english)
+                                    </label>
+                                    <textarea required rows="4" class="form-control" wicket:id="requirement_en"></textarea>
+                                </div>
+                                <div>
+                                    <label wicket:for="requirement_sv">
+                                        Requirement (swedish)
+                                    </label>
+                                    <textarea required rows="4" class="form-control" wicket:id="requirement_sv"></textarea>
+                                </div>
+                            </div>
+                        </fieldset>
+                    </li>
+                    <li>
+                        <div class="row align-items-center">
+                            <label class="col-auto">
+                                <wicket:container wicket:id="new_point"/>
+                            </label>
+                            <div class="col-auto">
+                                <div class="btn-group">
+                                    <button class="btn btn-sm btn-outline-success" wicket:id="add_new_point">Add</button>
+                                </div>
+                            </div>
+                        </div>
+                    </li>
+                </ol>
+            </fieldset>
+        </div>
+    </div>
+
+    <div class="mb-3">
+        <button class="btn btn-sm btn-outline-success" wicket:id="add_criteria">Add new criteria</button>
+    </div>
+</wicket:panel>
+</body>
+</html>
\ No newline at end of file
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplateComponentPanel.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplateComponentPanel.java
new file mode 100644
index 0000000000..fb6b0af006
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplateComponentPanel.java
@@ -0,0 +1,297 @@
+package se.su.dsv.scipro.admin.pages.grading;
+
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.markup.html.GenericWebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.EnumChoiceRenderer;
+import org.apache.wicket.markup.html.form.NumberTextField;
+import org.apache.wicket.markup.html.form.RequiredTextField;
+import org.apache.wicket.markup.html.form.TextArea;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.markup.html.panel.GenericPanel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.LambdaModel;
+import se.su.dsv.scipro.components.AjaxDropDownChoice;
+import se.su.dsv.scipro.components.BootstrapDatePicker;
+
+import java.time.LocalDate;
+import java.util.List;
+
+class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTemplate> {
+    EditingGradingTemplateComponentPanel(
+            String id,
+            IModel<EditingGradingTemplate> editingGradingTemplateModel)
+    {
+        super(id, editingGradingTemplateModel);
+
+        setOutputMarkupId(true);
+
+        TextField<LocalDate> validFromField = new RequiredTextField<>("valid_from",
+                LambdaModel.of(editingGradingTemplateModel,
+                        EditingGradingTemplate::getValidFrom,
+                        EditingGradingTemplate::setValidFrom),
+                LocalDate.class);
+        validFromField.add(new BootstrapDatePicker());
+        validFromField.add(new AutoSave());
+        add(validFromField);
+
+        add(new TextArea<>("note", LambdaModel.of(
+                editingGradingTemplateModel,
+                EditingGradingTemplate::getNote,
+                EditingGradingTemplate::setNote)) {
+            {
+                add(new AutoSave());
+            }
+        });
+
+        add(new GradeLimitsPanel("grade_limits", editingGradingTemplateModel.map(EditingGradingTemplate::getGradeLimits)));
+
+        add(new Label("max_points_available", editingGradingTemplateModel.map(EditingGradingTemplate::getMaxPointsAvailable)));
+
+        add(new ListView<>("criteria", editingGradingTemplateModel.map(EditingGradingTemplate::getCriteria)) {
+            {
+                setReuseItems(true);
+            }
+
+            @Override
+            protected void populateItem(ListItem<EditingGradingTemplate.Criteria> item) {
+                item.add(new AjaxLink<>("remove") {
+                    @Override
+                    public void onClick(AjaxRequestTarget target) {
+                        editingGradingTemplateModel.getObject().getCriteria().remove(item.getModelObject());
+                        target.add(EditingGradingTemplateComponentPanel.this);
+                    }
+                });
+                item.add(new CriteriaEditingPanel("criteria", item.getModel()));
+            }
+        });
+
+        add(new AjaxLink<>("add_criteria") {
+            @Override
+            public void onClick(AjaxRequestTarget target) {
+                editingGradingTemplateModel.getObject().addCriteria();
+                target.add(EditingGradingTemplateComponentPanel.this);
+            }
+        });
+    }
+
+    class CriteriaEditingPanel extends GenericWebMarkupContainer<EditingGradingTemplate.Criteria> {
+        CriteriaEditingPanel(String id, IModel<EditingGradingTemplate.Criteria> model) {
+            super(id, model);
+
+            setOutputMarkupId(true);
+
+            add(new RequiredTextField<>("title_sv", LambdaModel.of(
+                    model,
+                    EditingGradingTemplate.Criteria::getTitleSv,
+                    EditingGradingTemplate.Criteria::setTitleSv)) {
+                {
+                    add(new AutoSave());
+                }
+            });
+            add(new RequiredTextField<>("title_en", LambdaModel.of(
+                    model,
+                    EditingGradingTemplate.Criteria::getTitleEn,
+                    EditingGradingTemplate.Criteria::setTitleEn)) {
+                {
+                    add(new AutoSave());
+                }
+            });
+
+            AjaxDropDownChoice<EditingGradingTemplate.Criteria.Type> typeChoice = new AjaxDropDownChoice<>(
+                    "type",
+                    LambdaModel.of(
+                            model,
+                            EditingGradingTemplate.Criteria::getType,
+                            EditingGradingTemplate.Criteria::setType),
+                    List.of(EditingGradingTemplate.Criteria.Type.values()),
+                    new EnumChoiceRenderer<>(this))
+            {
+                @Override
+                public void onNewSelection(
+                        AjaxRequestTarget target,
+                        EditingGradingTemplate.Criteria.Type objectSelected)
+                {
+                    // auto save
+                }
+            };
+            typeChoice.setRequired(true);
+            add(typeChoice);
+
+            NumberTextField<Integer> pointsRequiredToPass = new NumberTextField<>(
+                    "points_required_to_pass",
+                    LambdaModel.of(
+                            model,
+                            EditingGradingTemplate.Criteria::getPointsRequiredToPass,
+                            EditingGradingTemplate.Criteria::setPointsRequiredToPass),
+                    Integer.class);
+            pointsRequiredToPass.setMinimum(0);
+            pointsRequiredToPass.setRequired(true);
+            pointsRequiredToPass.add(new AutoSave());
+            add(pointsRequiredToPass);
+
+            AjaxDropDownChoice<EditingGradingTemplate.Criteria.Flag> flagChoice = new AjaxDropDownChoice<>(
+                    "flag",
+                    LambdaModel.of(
+                            model,
+                            EditingGradingTemplate.Criteria::getFlag,
+                            EditingGradingTemplate.Criteria::setFlag),
+                    List.of(EditingGradingTemplate.Criteria.Flag.values()),
+                    new EnumChoiceRenderer<>(this))
+            {
+                @Override
+                public void onNewSelection(
+                        AjaxRequestTarget target,
+                        EditingGradingTemplate.Criteria.Flag objectSelected)
+                {
+                    // auto save
+                }
+            };
+            flagChoice.setNullValid(true);
+            add(flagChoice);
+
+            add(new ListView<>("points", model.map(EditingGradingTemplate.Criteria::getPoints)) {
+                {
+                    setReuseItems(true);
+                }
+
+                @Override
+                protected void populateItem(ListItem<EditingGradingTemplate.Criteria.Point> item) {
+                    item.add(new PointEditingPanel("point", item));
+                }
+            });
+
+            add(new Label("new_point", model
+                    .map(criteria -> criteria.getPoints().size())
+                    .map(size -> "Requirement for " + (size + 1) + " points")));
+
+            add(new AjaxLink<>("add_new_point") {
+                @Override
+                public void onClick(AjaxRequestTarget target) {
+                    EditingGradingTemplate.Criteria criteria = model.getObject();
+                    EditingGradingTemplate.Criteria.Point newPoint = criteria.new Point();
+                    criteria.getPoints().add(newPoint);
+                    target.add(CriteriaEditingPanel.this);
+                }
+            });
+        }
+
+        private class PointEditingPanel extends GenericWebMarkupContainer<EditingGradingTemplate.Criteria.Point> {
+            public PointEditingPanel(String id, ListItem<EditingGradingTemplate.Criteria.Point> item) {
+                super(id, item.getModel());
+
+                IModel<EditingGradingTemplate.Criteria.Point> model = item.getModel();
+
+                add(new Label("point", () -> item.getIndex() + 1));
+
+                TextArea<String> englishRequirement = new TextArea<>("requirement_en", LambdaModel.of(
+                        model,
+                        EditingGradingTemplate.Criteria.Point::getRequirementEn,
+                        EditingGradingTemplate.Criteria.Point::setRequirementEn));
+                englishRequirement.setRequired(true);
+                englishRequirement.add(new AutoSave());
+                add(englishRequirement);
+
+                TextArea<String> swedishRequirement = new TextArea<>("requirement_sv", LambdaModel.of(
+                        model,
+                        EditingGradingTemplate.Criteria.Point::getRequirementSv,
+                        EditingGradingTemplate.Criteria.Point::setRequirementSv));
+                swedishRequirement.setRequired(true);
+                swedishRequirement.add(new AutoSave());
+                add(swedishRequirement);
+
+                add(new AjaxLink<>("remove") {
+                    @Override
+                    public void onClick(AjaxRequestTarget target) {
+                        EditingGradingTemplate.Criteria criteria = CriteriaEditingPanel.this.getModelObject();
+                        criteria.getPoints().remove(model.getObject());
+                        target.add(CriteriaEditingPanel.this);
+                    }
+                });
+            }
+        }
+    }
+
+    private static class GradeLimitsPanel extends GenericWebMarkupContainer<GradeLimits> {
+        public GradeLimitsPanel(String id, IModel<GradeLimits> model) {
+            super(id, model);
+
+            setOutputMarkupId(true);
+
+            add(new RequiredTextField<>("failing_grade", LambdaModel.of(
+                    model,
+                    GradeLimits::getFailingGrade,
+                    GradeLimits::setFailingGrade)) {
+                {
+                    add(new AutoSave());
+                }
+            });
+
+            add(new ListView<>("grade_limits", model.map(GradeLimits::getGradeLimits)) {
+                {
+                    setReuseItems(true);
+                }
+
+                @Override
+                protected void populateItem(ListItem<GradeLimits.GradeLimit> item) {
+                    item.add(new GradeLimitEditingPanel("grade_limit", item.getModel()));
+                }
+            });
+
+            add(new AjaxLink<>("add") {
+                @Override
+                public void onClick(AjaxRequestTarget target) {
+                    GradeLimits gradeLimits = GradeLimitsPanel.this.getModelObject();
+                    gradeLimits.addNewLimit();
+                    target.add(GradeLimitsPanel.this);
+                }
+            });
+        }
+
+        private class GradeLimitEditingPanel extends GenericWebMarkupContainer<GradeLimits.GradeLimit> {
+            public GradeLimitEditingPanel(String id, IModel<GradeLimits.GradeLimit> model) {
+                super(id, model);
+
+                NumberTextField<Integer> minimum = new NumberTextField<>("minimum", LambdaModel.of(
+                        model,
+                        GradeLimits.GradeLimit::getLowerLimit,
+                        GradeLimits.GradeLimit::setLowerLimit), Integer.class);
+                minimum.setRequired(true);
+                minimum.add(new AutoSave());
+                add(minimum);
+
+                TextField<String> grade = new TextField<>("grade", LambdaModel.of(
+                        model,
+                        GradeLimits.GradeLimit::getGrade,
+                        GradeLimits.GradeLimit::setGrade));
+                grade.setRequired(true);
+                grade.add(new AutoSave());
+                add(grade);
+
+                add(new AjaxLink<>("remove") {
+                    @Override
+                    public void onClick(AjaxRequestTarget target) {
+                        GradeLimits gradeLimits = GradeLimitsPanel.this.getModelObject();
+                        gradeLimits.getGradeLimits().remove(model.getObject());
+                        target.add(GradeLimitsPanel.this);
+                    }
+                });
+            }
+        }
+    }
+
+    private static class AutoSave extends AjaxFormComponentUpdatingBehavior {
+        public AutoSave() {
+            super("input");
+        }
+
+        @Override
+        protected void onUpdate(AjaxRequestTarget target) {
+            // just trigger the ajax call is enough to update the model object
+        }
+    }
+}
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplateComponentPanel.utf8.properties b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplateComponentPanel.utf8.properties
new file mode 100644
index 0000000000..6315499c53
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplateComponentPanel.utf8.properties
@@ -0,0 +1,13 @@
+Flag.OPPOSITION=Final seminar opposition
+Flag.REFLECTION=Reflection
+max_points_available=The maximum number of points available is ${max_points_available} with the below criteria.
+failing_grade.Required=You must set the failing grade.
+title_sv.Required=You must set the swedish title for all criteria
+title_en.Required=You must set the english title for all criteria
+requirement_sv.Required=You must set the swedish requirement for every criteria point
+requirement_en.Required=You must set the english requirement for every criteria point
+Type.PROJECT=Once per thesis
+Type.INDIVIDUAL=Individually for each author
+minimum.Required=You must set the minimum number of points for each grade
+grade.Required=You must set the letter for each grade
+flag.nullValid=None
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/GradeLimits.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/GradeLimits.java
new file mode 100644
index 0000000000..29bd5877d2
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/GradeLimits.java
@@ -0,0 +1,64 @@
+package se.su.dsv.scipro.admin.pages.grading;
+
+import se.su.dsv.scipro.report.GradingReportTemplate;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+class GradeLimits implements Serializable {
+    private List<GradeLimit> gradeLimits;
+    private String failingGrade;
+
+    public GradeLimits() {
+        this.gradeLimits = new ArrayList<>();
+    }
+
+    GradeLimits(GradingReportTemplate template) {
+        this.gradeLimits = new ArrayList<>();
+        for (var gradeLimit : template.getGradeLimits()) {
+            GradeLimit editableGradeLimit = new GradeLimit();
+            editableGradeLimit.setGrade(gradeLimit.getGrade());
+            editableGradeLimit.setLowerLimit(gradeLimit.getLowerLimit());
+            gradeLimits.add(editableGradeLimit);
+        }
+        this.failingGrade = template.getFailingGrade();
+    }
+
+    void addNewLimit() {
+        getGradeLimits().add(new GradeLimit());
+    }
+
+    public String getFailingGrade() {
+        return failingGrade;
+    }
+
+    public void setFailingGrade(String failingGrade) {
+        this.failingGrade = failingGrade;
+    }
+
+    public List<GradeLimit> getGradeLimits() {
+        return gradeLimits;
+    }
+
+    class GradeLimit implements Serializable {
+        private String grade;
+        private int lowerLimit;
+
+        public String getGrade() {
+            return grade;
+        }
+
+        public void setGrade(String grade) {
+            this.grade = grade;
+        }
+
+        public int getLowerLimit() {
+            return lowerLimit;
+        }
+
+        public void setLowerLimit(int lowerLimit) {
+            this.lowerLimit = lowerLimit;
+        }
+    }
+}
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/MenuHighlightGradingTemplates.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/MenuHighlightGradingTemplates.java
new file mode 100644
index 0000000000..0ff8cfc851
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/MenuHighlightGradingTemplates.java
@@ -0,0 +1,6 @@
+package se.su.dsv.scipro.admin.pages.grading;
+
+import se.su.dsv.scipro.components.menuhighlighting.MenuHighlight;
+
+public interface MenuHighlightGradingTemplates extends MenuHighlight {
+}
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/wicket-package.utf8.properties b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/wicket-package.utf8.properties
new file mode 100644
index 0000000000..f761f20e4e
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/wicket-package.utf8.properties
@@ -0,0 +1,5 @@
+template_updated=Template updated
+valid_from_must_be_in_the_future=The templates valid date must be in the future. The given date was ${validFrom} but it must be at least ${earliestAllowedValidFrom}.
+another_template_exists_for_the_given_date_date=There is already another ${projectType.name} template that becomes valid at ${validFrom}, please pick another date.
+template_is_locked=You can not edit templates that have become current. The template you are trying to edit became current at ${validFrom}.
+template_created=New template for ${name} created.
diff --git a/view/src/main/java/se/su/dsv/scipro/components/MaxLengthLabel.java b/view/src/main/java/se/su/dsv/scipro/components/MaxLengthLabel.java
new file mode 100644
index 0000000000..edfdd48ab9
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/components/MaxLengthLabel.java
@@ -0,0 +1,37 @@
+package se.su.dsv.scipro.components;
+
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.util.convert.IConverter;
+
+import java.util.Locale;
+
+public class MaxLengthLabel extends Label {
+    private final IModel<Integer> maxLengthModel;
+
+    public MaxLengthLabel(String id, IModel<String> dataModel, IModel<Integer> maxLength) {
+        super(id, dataModel);
+        this.maxLengthModel = maxLength;
+    }
+
+    @Override
+    protected IConverter<?> createConverter(Class<?> type) {
+        return new MaxLengthConverter();
+    }
+
+    private class MaxLengthConverter implements IConverter<String> {
+        @Override
+        public String convertToObject(String s, Locale locale) {
+            return s;
+        }
+
+        @Override
+        public String convertToString(String o, Locale locale) {
+            Integer maxLength = maxLengthModel.getObject();
+            if (o.length() > maxLength) {
+                return o.substring(0, maxLength) + "...";
+            }
+            return o;
+        }
+    }
+}
diff --git a/view/src/main/java/se/su/dsv/scipro/components/ModalWindowPlus.java b/view/src/main/java/se/su/dsv/scipro/components/ModalWindowPlus.java
index 9faa92158d..0eca546462 100644
--- a/view/src/main/java/se/su/dsv/scipro/components/ModalWindowPlus.java
+++ b/view/src/main/java/se/su/dsv/scipro/components/ModalWindowPlus.java
@@ -1,6 +1,7 @@
 package se.su.dsv.scipro.components;
 
 import org.apache.wicket.Component;
+import org.apache.wicket.ajax.AjaxEventBehavior;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.behavior.AttributeAppender;
 import org.apache.wicket.markup.ComponentTag;
@@ -9,6 +10,7 @@ import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.Model;
+import org.danekja.java.util.function.serializable.SerializableConsumer;
 
 import java.util.function.Function;
 
@@ -83,4 +85,13 @@ public class ModalWindowPlus extends Panel {
         component.setOutputMarkupPlaceholderTag(true);
         replace(component);
     }
+
+    public void onClose(SerializableConsumer<AjaxRequestTarget> onClose) {
+        add(new AjaxEventBehavior("hidden.bs.modal") {
+            @Override
+            protected void onEvent(AjaxRequestTarget target) {
+                onClose.accept(target);
+            }
+        });
+    }
 }
diff --git a/view/src/main/java/se/su/dsv/scipro/components/NonEmptyLabel.java b/view/src/main/java/se/su/dsv/scipro/components/NonEmptyLabel.java
new file mode 100644
index 0000000000..27218979e7
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/components/NonEmptyLabel.java
@@ -0,0 +1,19 @@
+package se.su.dsv.scipro.components;
+
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.model.IModel;
+
+/**
+ * A basic {@link Label} that hides itself if the rendered model object {@link String#isBlank() is blank}
+ */
+public class NonEmptyLabel extends Label {
+    public NonEmptyLabel(String id, IModel<?> model) {
+        super(id, model);
+    }
+
+    @Override
+    protected void onConfigure() {
+        super.onConfigure();
+        setVisible(!getDefaultModelObjectAsString().isBlank());
+    }
+}
diff --git a/view/src/main/java/se/su/dsv/scipro/dataproviders/PageAdapter.java b/view/src/main/java/se/su/dsv/scipro/dataproviders/PageAdapter.java
index 195ca6098b..61b7977786 100644
--- a/view/src/main/java/se/su/dsv/scipro/dataproviders/PageAdapter.java
+++ b/view/src/main/java/se/su/dsv/scipro/dataproviders/PageAdapter.java
@@ -1,8 +1,8 @@
 package se.su.dsv.scipro.dataproviders;
 
 import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.PageRequest;
+import se.su.dsv.scipro.system.Sort;
 
 import java.util.Objects;
 
diff --git a/view/src/main/java/se/su/dsv/scipro/grading/CriteriaPanel.java b/view/src/main/java/se/su/dsv/scipro/grading/CriteriaPanel.java
index 2dcfa3420a..e5475c42ed 100644
--- a/view/src/main/java/se/su/dsv/scipro/grading/CriteriaPanel.java
+++ b/view/src/main/java/se/su/dsv/scipro/grading/CriteriaPanel.java
@@ -121,7 +121,7 @@ public class CriteriaPanel extends GenericPanel<SupervisorGradingReport> {
                 grading.add(pf);
                 grading.add(new Label("pfCriteria", item.getModel().combineWith(language, (gc, lang) -> gc.getGradingCriterionPoints().get(1).getDescription(lang))));
 
-                if (item.getModelObject().getTitle().startsWith("Ö1 ")) {
+                if (item.getModelObject().getFlag() == AbstractGradingCriterion.Flag.OPPOSITION) {
                     grading.setEnabled(false);
                 }
                 item.add(new AjaxConfirmationLink<>("enable", "Are you sure you want to grade this criteria? It is usually filled in by the seminar supervisor") {
@@ -194,7 +194,7 @@ public class CriteriaPanel extends GenericPanel<SupervisorGradingReport> {
         @Override
         protected void onConfigure() {
             super.onConfigure();
-            final boolean isOppositionCriteria = gradingCriterion.getObject().getTitle().startsWith("Ö1 ");
+            final boolean isOppositionCriteria = gradingCriterion.getObject().getFlag() == AbstractGradingCriterion.Flag.OPPOSITION;
             setVisibilityAllowed(isOppositionCriteria && isEnabledInHierarchy());
         }
 
@@ -296,7 +296,7 @@ public class CriteriaPanel extends GenericPanel<SupervisorGradingReport> {
         @Override
         protected void onConfigure() {
             super.onConfigure();
-            boolean isReflectionCriteria = gradingCriterion.getObject().getTitle().startsWith("Ö6 ");
+            boolean isReflectionCriteria = gradingCriterion.getObject().getFlag() == AbstractGradingCriterion.Flag.REFLECTION;
             setVisibilityAllowed(isReflectionCriteria);
         }
     }
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/main/java/se/su/dsv/scipro/match/ProjectMyIdeasPanel.java b/view/src/main/java/se/su/dsv/scipro/match/ProjectMyIdeasPanel.java
index 9841cbf78a..3e9e7f12b5 100755
--- a/view/src/main/java/se/su/dsv/scipro/match/ProjectMyIdeasPanel.java
+++ b/view/src/main/java/se/su/dsv/scipro/match/ProjectMyIdeasPanel.java
@@ -16,8 +16,8 @@ import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.LambdaModel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.PageRequest;
+import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.components.*;
 import se.su.dsv.scipro.data.DetachableServiceModel;
 import se.su.dsv.scipro.firstmeeting.FirstMeetingColumnPanel;
diff --git a/view/src/main/java/se/su/dsv/scipro/milestones/EditMilestoneActivityPanel.java b/view/src/main/java/se/su/dsv/scipro/milestones/EditMilestoneActivityPanel.java
index 3bb5d88ea7..3e84423325 100644
--- a/view/src/main/java/se/su/dsv/scipro/milestones/EditMilestoneActivityPanel.java
+++ b/view/src/main/java/se/su/dsv/scipro/milestones/EditMilestoneActivityPanel.java
@@ -7,7 +7,7 @@ import org.apache.wicket.markup.html.panel.FeedbackPanel;
 import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.LambdaModel;
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.components.BootstrapCheckBoxMultipleChoice;
 import se.su.dsv.scipro.components.BootstrapRadioChoice;
 import se.su.dsv.scipro.milestones.dataobjects.MilestoneActivityTemplate;
diff --git a/view/src/main/java/se/su/dsv/scipro/milestones/MileStoneActivityAdminPanel.java b/view/src/main/java/se/su/dsv/scipro/milestones/MileStoneActivityAdminPanel.java
index c82b6581d5..5631a9dcc9 100644
--- a/view/src/main/java/se/su/dsv/scipro/milestones/MileStoneActivityAdminPanel.java
+++ b/view/src/main/java/se/su/dsv/scipro/milestones/MileStoneActivityAdminPanel.java
@@ -14,7 +14,7 @@ import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.LambdaModel;
 import org.apache.wicket.model.LoadableDetachableModel;
 import org.apache.wicket.model.Model;
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.components.DisplayMultiplesPanel;
 import se.su.dsv.scipro.components.ListAdapterModel;
 import se.su.dsv.scipro.components.ModalWindowPlus;
diff --git a/view/src/main/java/se/su/dsv/scipro/milestones/MileStonePhaseAdminPanel.java b/view/src/main/java/se/su/dsv/scipro/milestones/MileStonePhaseAdminPanel.java
index 490ba486d1..1c52a1364b 100644
--- a/view/src/main/java/se/su/dsv/scipro/milestones/MileStonePhaseAdminPanel.java
+++ b/view/src/main/java/se/su/dsv/scipro/milestones/MileStonePhaseAdminPanel.java
@@ -12,7 +12,7 @@ import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.LoadableDetachableModel;
 import org.apache.wicket.model.Model;
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.components.ModalWindowPlus;
 import se.su.dsv.scipro.components.SortOrderPanel;
 import se.su.dsv.scipro.milestones.dataobjects.MilestonePhaseTemplate;
diff --git a/view/src/main/java/se/su/dsv/scipro/peer/LatestReviewPanel.java b/view/src/main/java/se/su/dsv/scipro/peer/LatestReviewPanel.java
index d84996354a..c8611fc665 100644
--- a/view/src/main/java/se/su/dsv/scipro/peer/LatestReviewPanel.java
+++ b/view/src/main/java/se/su/dsv/scipro/peer/LatestReviewPanel.java
@@ -8,8 +8,8 @@ import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.LoadableDetachableModel;
 import org.apache.wicket.model.ResourceModel;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.PageRequest;
+import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.components.DateLabel;
 import se.su.dsv.scipro.data.enums.DateStyle;
 import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettings;
diff --git a/view/src/main/java/se/su/dsv/scipro/peer/PeerRequestSelectionPanel.java b/view/src/main/java/se/su/dsv/scipro/peer/PeerRequestSelectionPanel.java
index 0fe3233fea..c45bd486a8 100644
--- a/view/src/main/java/se/su/dsv/scipro/peer/PeerRequestSelectionPanel.java
+++ b/view/src/main/java/se/su/dsv/scipro/peer/PeerRequestSelectionPanel.java
@@ -11,7 +11,7 @@ import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.StringResourceModel;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.data.domain.PageRequest;
+import se.su.dsv.scipro.system.PageRequest;
 import se.su.dsv.scipro.components.ConfirmationLink;
 import se.su.dsv.scipro.data.DetachableServiceModel;
 import se.su.dsv.scipro.notifications.NotificationController;
diff --git a/view/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerDataProvider.java b/view/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerDataProvider.java
index 5656231623..14be7e30b7 100755
--- a/view/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerDataProvider.java
+++ b/view/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerDataProvider.java
@@ -2,8 +2,8 @@ package se.su.dsv.scipro.projectpartner;
 
 import org.apache.wicket.markup.repeater.data.IDataProvider;
 import org.apache.wicket.model.IModel;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.PageRequest;
+import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.data.DetachableServiceModel;
 import se.su.dsv.scipro.match.ApplicationPeriod;
 import se.su.dsv.scipro.system.ProjectType;
diff --git a/view/src/main/java/se/su/dsv/scipro/statistics/AdminFinalSeminarStatisticsPage.java b/view/src/main/java/se/su/dsv/scipro/statistics/AdminFinalSeminarStatisticsPage.java
index c2cd216a39..c6615908ab 100644
--- a/view/src/main/java/se/su/dsv/scipro/statistics/AdminFinalSeminarStatisticsPage.java
+++ b/view/src/main/java/se/su/dsv/scipro/statistics/AdminFinalSeminarStatisticsPage.java
@@ -17,7 +17,7 @@ import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.LambdaModel;
 import org.apache.wicket.model.LoadableDetachableModel;
 import org.apache.wicket.model.Model;
-import org.springframework.data.domain.PageRequest;
+import se.su.dsv.scipro.system.PageRequest;
 import se.su.dsv.scipro.components.AjaxCheckBoxMultipleChoice;
 import se.su.dsv.scipro.components.EnumLambdaColumn;
 import se.su.dsv.scipro.components.ExportableDataPanel;
diff --git a/view/src/main/java/se/su/dsv/scipro/statistics/AdminUnmetTargetsStatisticsPage.java b/view/src/main/java/se/su/dsv/scipro/statistics/AdminUnmetTargetsStatisticsPage.java
index 16bd405add..4a443d7c32 100644
--- a/view/src/main/java/se/su/dsv/scipro/statistics/AdminUnmetTargetsStatisticsPage.java
+++ b/view/src/main/java/se/su/dsv/scipro/statistics/AdminUnmetTargetsStatisticsPage.java
@@ -13,8 +13,8 @@ import org.apache.wicket.markup.html.form.StatelessForm;
 import org.apache.wicket.markup.html.panel.FeedbackPanel;
 import org.apache.wicket.markup.repeater.Item;
 import org.apache.wicket.model.*;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.PageRequest;
+import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.admin.pages.AdminMailPage;
 import se.su.dsv.scipro.components.AjaxDropDownChoice;
 import se.su.dsv.scipro.components.ExportableDataPanel;
diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.html b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.html
new file mode 100644
index 0000000000..7a5e924096
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.html
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+<body>
+<wicket:panel>
+    <div wicket:id="edit_note_modal"></div>
+    <span wicket:id="shortened_note"></span>
+    <wicket:container wicket:id="full_note"></wicket:container>
+    <a wicket:id="view_note"><wicket:message key="note.view.edit"/></a>
+</wicket:panel>
+</body>
+</html>
\ No newline at end of file
diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.utf8.properties b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.utf8.properties
new file mode 100644
index 0000000000..5f31ea7d1c
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.utf8.properties
@@ -0,0 +1,2 @@
+note.modal.title=View/edit note for ${title}
+note.view.edit=View/edit note
diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteForm.html b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteForm.html
new file mode 100644
index 0000000000..bf546f6a76
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteForm.html
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+<body>
+<wicket:panel>
+    <form wicket:id="form">
+        <div wicket:id="feedback"></div>
+        <div class="mb-3">
+            <label wicket:for="note" class="sr-only">Note</label>
+            <textarea wicket:id="note" rows="20" class="form-control"></textarea>
+        </div>
+        <button wicket:id="save" type="submit" class="btn btn-primary">Save</button>
+    </form>
+</wicket:panel>
+</body>
+</html>
\ No newline at end of file
diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java
new file mode 100644
index 0000000000..b35962f891
--- /dev/null
+++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java
@@ -0,0 +1,129 @@
+package se.su.dsv.scipro.supervisor.panels;
+
+import jakarta.inject.Inject;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink;
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.export.AbstractExportableColumn;
+import org.apache.wicket.feedback.FencedFeedbackPanel;
+import org.apache.wicket.injection.Injector;
+import org.apache.wicket.markup.html.basic.MultiLineLabel;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.TextArea;
+import org.apache.wicket.markup.html.panel.GenericPanel;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.LoadableDetachableModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.StringResourceModel;
+import se.su.dsv.scipro.components.LargeModalWindow;
+import se.su.dsv.scipro.components.MaxLengthLabel;
+import se.su.dsv.scipro.components.ModalWindowPlus;
+import se.su.dsv.scipro.project.Project;
+import se.su.dsv.scipro.project.ProjectNoteService;
+import se.su.dsv.scipro.settings.dataobjects.SupervisorProjectNoteDisplay;
+import se.su.dsv.scipro.system.User;
+
+import java.time.LocalTime;
+import java.time.temporal.ChronoUnit;
+
+public class ProjectNoteColumn extends AbstractExportableColumn<Project, String> {
+    @Inject
+    private ProjectNoteService projectNoteService;
+
+    private final IModel<User> user;
+    private final IModel<SupervisorProjectNoteDisplay> supervisorProjectNoteDisplayModel;
+
+    public ProjectNoteColumn(IModel<String> displayModel, IModel<User> user,
+            IModel<SupervisorProjectNoteDisplay> supervisorProjectNoteDisplayModel) {
+        super(displayModel);
+        Injector.get().inject(this);
+        this.supervisorProjectNoteDisplayModel = supervisorProjectNoteDisplayModel;
+        this.user = user;
+    }
+
+    @Override
+    public IModel<String> getDataModel(IModel<Project> rowModel) {
+        return LoadableDetachableModel.of(() -> projectNoteService.getUserNote(
+                rowModel.getObject(),
+                user.getObject()));
+    }
+
+    @Override
+    public void populateItem(
+            Item<ICellPopulator<Project>> cellItem,
+            String componentId,
+            IModel<Project> rowModel)
+    {
+        cellItem.add(new ViewAndEditNoteCellPanel(componentId, rowModel));
+    }
+
+    private class ViewAndEditNoteCellPanel extends GenericPanel<Project> {
+        public ViewAndEditNoteCellPanel(String id, IModel<Project> model) {
+            super(id, model);
+
+            ModalWindowPlus modal = new LargeModalWindow("edit_note_modal");
+            modal.setTitle(new StringResourceModel("note.modal.title", this, model));
+            modal.setContent(componentId -> new ViewAndEditNoteForm(componentId, model));
+            add(modal);
+
+            setOutputMarkupId(true);
+            modal.onClose(target -> target.add(this));
+
+            add(new MaxLengthLabel("shortened_note", getDataModel(model), Model.of(100)) {
+                @Override
+                protected void onConfigure() {
+                    super.onConfigure();
+                    setVisibilityAllowed(supervisorProjectNoteDisplayModel.getObject() == SupervisorProjectNoteDisplay.COMPACT);
+                }
+            });
+            add(new MultiLineLabel("full_note", getDataModel(model)) {
+                @Override
+                protected void onConfigure() {
+                    super.onConfigure();
+                    setVisibilityAllowed(supervisorProjectNoteDisplayModel.getObject() == SupervisorProjectNoteDisplay.FULL);
+                }
+            });
+
+            AjaxLink<Object> noteLink = new AjaxLink<>("view_note") {
+                @Override
+                public void onClick(AjaxRequestTarget target) {
+                    modal.show(target);
+                }
+            };
+            add(noteLink);
+        }
+    }
+
+    private class ViewAndEditNoteForm extends GenericPanel<Project> {
+        public ViewAndEditNoteForm(String id, IModel<Project> project) {
+            super(id, project);
+
+            IModel<String> note = getDataModel(project);
+
+            Form<Project> form = new Form<>("form", project) {
+                @Override
+                protected void onSubmit() {
+                    projectNoteService.setUserNote(
+                            project.getObject(),
+                            user.getObject(),
+                            note.getObject()
+                    );
+                    success("Note saved at " + LocalTime.now().truncatedTo(ChronoUnit.SECONDS));
+                }
+            };
+            add(form);
+
+            form.add(new FencedFeedbackPanel("feedback", form));
+
+            form.add(new TextArea<>("note", note));
+            form.add(new AjaxSubmitLink("save") {
+                @Override
+                protected void onAfterSubmit(AjaxRequestTarget target) {
+                    target.add(form);
+                }
+            });
+        }
+    }
+}
diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.html b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.html
index 9b68bf8d79..73ab571f7f 100755
--- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.html
+++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.html
@@ -31,6 +31,10 @@
                         </strong>
                         <div wicket:id="projectTypes"></div>
                     </div>
+                    <fieldset class="col-6 col-md-3 col-lg-2">
+                        <legend>Note</legend>
+                        <div wicket:id="note_display"></div>
+                    </fieldset>
                 </div>
             </form>
         </div>
diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.java b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.java
index e13137a359..e3cab053eb 100755
--- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.java
+++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.java
@@ -1,6 +1,7 @@
 package se.su.dsv.scipro.supervisor.panels;
 
 import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior;
 import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox;
 import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
 import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
@@ -15,6 +16,7 @@ import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.markup.repeater.Item;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.LambdaModel;
+import org.apache.wicket.model.LoadableDetachableModel;
 import org.apache.wicket.model.Model;
 import se.su.dsv.scipro.components.*;
 import se.su.dsv.scipro.components.datatables.MultipleUsersColumn;
@@ -30,6 +32,7 @@ import se.su.dsv.scipro.project.ProjectService;
 import se.su.dsv.scipro.project.ProjectStatus;
 import se.su.dsv.scipro.project.ProjectTeamMemberRoles;
 import se.su.dsv.scipro.session.SciProSession;
+import se.su.dsv.scipro.settings.dataobjects.SupervisorProjectNoteDisplay;
 import se.su.dsv.scipro.settings.dataobjects.UserProfile;
 import se.su.dsv.scipro.springdata.services.UserProfileService;
 import se.su.dsv.scipro.system.ProjectType;
@@ -62,6 +65,7 @@ public class SupervisorMyProjectsPanel extends Panel {
 
     private ExportableDataPanel dataPanel;
     private ProjectService.Filter filter = new ProjectService.Filter();
+    private IModel<SupervisorProjectNoteDisplay> supervisorProjectNoteDisplayModel = new Model<>();
 
     public SupervisorMyProjectsPanel(String id) {
         super(id);
@@ -90,6 +94,7 @@ public class SupervisorMyProjectsPanel extends Panel {
                 return new ListAdapterModel<>(rowModel.map(Project::getProjectParticipants));
             }
         });
+        columns.add(new ProjectNoteColumn(Model.of("Note"), LoadableDetachableModel.of(this::currentUser), supervisorProjectNoteDisplayModel));
         columns.add(new UserColumn<>(Model.of("Head supervisor"), "headSupervisor.fullName", Project::getHeadSupervisor));
         return columns;
     }
@@ -112,6 +117,7 @@ public class SupervisorMyProjectsPanel extends Panel {
         filter.setRoles(userProfile.getDefaultProjectTeamMemberRolesFilter());
         filter.setFilterSupervisor(userProfile.isDefaultSupervisorFilter());
         filter.setProjectTypes(userProfile.getDefaultProjectTypeFilter());
+        supervisorProjectNoteDisplayModel.setObject(userProfile.getSupervisorProjectNoteDisplay());
     }
 
     private User currentUser() {
@@ -155,6 +161,19 @@ public class SupervisorMyProjectsPanel extends Panel {
                     updateProfileWithCurrentFilter();
                 }
             });
+            BootstrapRadioChoice<SupervisorProjectNoteDisplay> noteDisplay = new BootstrapRadioChoice<>(
+                    "note_display",
+                    supervisorProjectNoteDisplayModel,
+                    List.of(SupervisorProjectNoteDisplay.values()),
+                    new EnumChoiceRenderer<>(this));
+            noteDisplay.add(new AjaxFormChoiceComponentUpdatingBehavior() {
+                @Override
+                public void onUpdate(AjaxRequestTarget target) {
+                    target.add(dataPanel);
+                    updateProfileWithCurrentFilter();
+                }
+            });
+            add(noteDisplay);
         }
 
         private void updateProfileWithCurrentFilter() {
@@ -163,6 +182,7 @@ public class SupervisorMyProjectsPanel extends Panel {
             userProfile.setDefaultProjectTeamMemberRolesFilter(filter.getRoles());
             userProfile.setDefaultSupervisorFilter(filter.isFilterSupervisor());
             userProfile.setDefaultProjectTypeFilter(filter.getProjectTypes());
+            userProfile.setSupervisorProjectNoteDisplay(supervisorProjectNoteDisplayModel.getObject());
             profileService.save(userProfile);
         }
     }
diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.utf8.properties b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.utf8.properties
index 8c2d03baf2..17f454ad8c 100644
--- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.utf8.properties
+++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.utf8.properties
@@ -9,3 +9,6 @@ ProjectTeamMemberRoles.CO_SUPERVISOR= Co-Supervisor
 ProjectStatus.ACTIVE= Active
 ProjectStatus.INACTIVE= Inactive
 ProjectStatus.COMPLETED= Completed
+
+SupervisorProjectNoteDisplay.COMPACT=Compact
+SupervisorProjectNoteDisplay.FULL=Full
diff --git a/view/src/main/webapp/css/scipro_m.css b/view/src/main/webapp/css/scipro_m.css
index 46944aaf54..5c4184a9c0 100755
--- a/view/src/main/webapp/css/scipro_m.css
+++ b/view/src/main/webapp/css/scipro_m.css
@@ -597,4 +597,7 @@ th.wicket_orderUp, th.sorting_asc {
 
 .bg-su-primary {
     background-color: #002f5f;
-}
\ No newline at end of file
+}
+.line-length-limit {
+    max-width: 80em;
+}
diff --git a/view/src/test/java/se/su/dsv/scipro/SciProTest.java b/view/src/test/java/se/su/dsv/scipro/SciProTest.java
index 9a400e0086..ee39fc8b41 100755
--- a/view/src/test/java/se/su/dsv/scipro/SciProTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/SciProTest.java
@@ -87,6 +87,7 @@ import se.su.dsv.scipro.peer.PerformReviewService;
 import se.su.dsv.scipro.plagiarism.PlagiarismControl;
 import se.su.dsv.scipro.plagiarism.urkund.UrkundService;
 import se.su.dsv.scipro.profiles.CurrentProfile;
+import se.su.dsv.scipro.project.ProjectNoteService;
 import se.su.dsv.scipro.project.ProjectPeopleStatisticsService;
 import se.su.dsv.scipro.project.ProjectService;
 import se.su.dsv.scipro.project.pages.ProjectStartPage;
@@ -355,6 +356,8 @@ public abstract class SciProTest {
     protected ExaminerTimelineService examinerTimelineService;
     @Mock
     protected NationalSubjectCategoryService nationalSubjectCategoryService;
+    @Mock
+    protected ProjectNoteService projectNoteService;
 
     protected WicketTester tester;
 
diff --git a/view/src/test/java/se/su/dsv/scipro/activityplan/ActivityPlanPanelTest.java b/view/src/test/java/se/su/dsv/scipro/activityplan/ActivityPlanPanelTest.java
index dbe7825763..455f4d6ed2 100644
--- a/view/src/test/java/se/su/dsv/scipro/activityplan/ActivityPlanPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/activityplan/ActivityPlanPanelTest.java
@@ -6,7 +6,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentMatchers;
 import org.mockito.Mockito;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.checklist.Checklist;
 import se.su.dsv.scipro.checklists.ProjectViewChecklistPage;
diff --git a/view/src/test/java/se/su/dsv/scipro/activityplan/UpcomingActivitiesPanelTest.java b/view/src/test/java/se/su/dsv/scipro/activityplan/UpcomingActivitiesPanelTest.java
index b31c093b0b..6af79a01c0 100644
--- a/view/src/test/java/se/su/dsv/scipro/activityplan/UpcomingActivitiesPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/activityplan/UpcomingActivitiesPanelTest.java
@@ -12,7 +12,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentMatchers;
 import org.mockito.Mockito;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.DegreeType;
diff --git a/view/src/test/java/se/su/dsv/scipro/applicationperiod/AdminApplicationPeriodsPanelTest.java b/view/src/test/java/se/su/dsv/scipro/applicationperiod/AdminApplicationPeriodsPanelTest.java
index 93e0d7bf14..31e231936e 100644
--- a/view/src/test/java/se/su/dsv/scipro/applicationperiod/AdminApplicationPeriodsPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/applicationperiod/AdminApplicationPeriodsPanelTest.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.applicationperiod;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.match.ApplicationPeriod;
 
diff --git a/view/src/test/java/se/su/dsv/scipro/applicationperiod/AdminEditTargetsPageTest.java b/view/src/test/java/se/su/dsv/scipro/applicationperiod/AdminEditTargetsPageTest.java
index a3eff4971a..c5d3add07b 100644
--- a/view/src/test/java/se/su/dsv/scipro/applicationperiod/AdminEditTargetsPageTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/applicationperiod/AdminEditTargetsPageTest.java
@@ -4,7 +4,7 @@ import org.apache.wicket.markup.html.form.FormComponent;
 import org.apache.wicket.model.Model;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.match.ApplicationPeriod;
 import se.su.dsv.scipro.match.Target;
diff --git a/view/src/test/java/se/su/dsv/scipro/dataproviders/FilteredDataProviderTest.java b/view/src/test/java/se/su/dsv/scipro/dataproviders/FilteredDataProviderTest.java
index 1c4712b422..68a4903be7 100644
--- a/view/src/test/java/se/su/dsv/scipro/dataproviders/FilteredDataProviderTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/dataproviders/FilteredDataProviderTest.java
@@ -6,7 +6,7 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.system.DomainObject;
 import se.su.dsv.scipro.system.FilteredService;
 
diff --git a/view/src/test/java/se/su/dsv/scipro/dataproviders/GenericDataProviderTest.java b/view/src/test/java/se/su/dsv/scipro/dataproviders/GenericDataProviderTest.java
index d137472789..0d3f908801 100644
--- a/view/src/test/java/se/su/dsv/scipro/dataproviders/GenericDataProviderTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/dataproviders/GenericDataProviderTest.java
@@ -6,7 +6,7 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.system.DomainObject;
 import se.su.dsv.scipro.system.GenericService;
 
diff --git a/view/src/test/java/se/su/dsv/scipro/datatables/project/ProjectDataPanelTest.java b/view/src/test/java/se/su/dsv/scipro/datatables/project/ProjectDataPanelTest.java
index ed95068673..4c1961be0c 100644
--- a/view/src/test/java/se/su/dsv/scipro/datatables/project/ProjectDataPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/datatables/project/ProjectDataPanelTest.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.datatables.project;
 
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.datatables.AjaxCheckboxWrapper;
 import se.su.dsv.scipro.project.Project;
diff --git a/view/src/test/java/se/su/dsv/scipro/finalseminar/AdminFinalSeminarExemptionPageTest.java b/view/src/test/java/se/su/dsv/scipro/finalseminar/AdminFinalSeminarExemptionPageTest.java
index a818cb6e6e..79147e7456 100644
--- a/view/src/test/java/se/su/dsv/scipro/finalseminar/AdminFinalSeminarExemptionPageTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/finalseminar/AdminFinalSeminarExemptionPageTest.java
@@ -7,7 +7,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.ArgumentMatchers;
 import org.mockito.Mockito;
 import org.mockito.junit.jupiter.MockitoExtension;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.components.ExportableDataPanel;
 import se.su.dsv.scipro.project.Project;
diff --git a/view/src/test/java/se/su/dsv/scipro/finalseminar/DownloadPdfReportPanelTest.java b/view/src/test/java/se/su/dsv/scipro/finalseminar/DownloadPdfReportPanelTest.java
index 5d8ecbdfe5..70c92fb5cb 100644
--- a/view/src/test/java/se/su/dsv/scipro/finalseminar/DownloadPdfReportPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/finalseminar/DownloadPdfReportPanelTest.java
@@ -18,6 +18,8 @@ import se.su.dsv.scipro.test.ObjectMother;
 import se.su.dsv.scipro.test.SeminarBuilder;
 import se.su.dsv.scipro.test.UserBuilder;
 
+import java.time.LocalDate;
+import java.time.Month;
 import java.time.ZonedDateTime;
 import java.util.*;
 
@@ -76,7 +78,8 @@ public class DownloadPdfReportPanelTest extends SciProTest {
     }
 
     private void prepareReports() {
-        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(BACHELOR, 30);
+        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(BACHELOR,
+                LocalDate.of(2024, Month.JANUARY, 1));
         List<GradingCriterionPointTemplate> gradingCriterionPointTemplates = new ArrayList<>();
         gradingCriterionPointTemplates.add(new GradingCriterionPointTemplate.Builder()
                 .point(0)
diff --git a/view/src/test/java/se/su/dsv/scipro/finalseminar/OppositionReportPageTest.java b/view/src/test/java/se/su/dsv/scipro/finalseminar/OppositionReportPageTest.java
index c446887a68..cafe8f9928 100644
--- a/view/src/test/java/se/su/dsv/scipro/finalseminar/OppositionReportPageTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/finalseminar/OppositionReportPageTest.java
@@ -29,6 +29,7 @@ import se.su.dsv.scipro.test.UserBuilder;
 import se.su.dsv.scipro.util.PageParameterKeys;
 
 import java.time.LocalDate;
+import java.time.Month;
 import java.time.ZonedDateTime;
 import java.util.*;
 
@@ -152,7 +153,8 @@ public class OppositionReportPageTest extends SciProTest {
     }
 
     private GradingReportTemplate createTemplate(ProjectType bachelor) {
-        GradingReportTemplate reportTemplate = new GradingReportTemplate(bachelor, 30);
+        GradingReportTemplate reportTemplate = new GradingReportTemplate(bachelor,
+                LocalDate.of(2024, Month.JANUARY, 1));
         List<GradingCriterionPointTemplate> gradingCriterionPointTemplates = new ArrayList<>();
         gradingCriterionPointTemplates.add(new GradingCriterionPointTemplate.Builder()
                 .point(1)
diff --git a/view/src/test/java/se/su/dsv/scipro/finalseminar/ProjectOppositionPageTest.java b/view/src/test/java/se/su/dsv/scipro/finalseminar/ProjectOppositionPageTest.java
index 48bd1c756b..fc977d606a 100644
--- a/view/src/test/java/se/su/dsv/scipro/finalseminar/ProjectOppositionPageTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/finalseminar/ProjectOppositionPageTest.java
@@ -13,7 +13,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentMatchers;
 import org.mockito.Mockito;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.PageTest;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.DegreeType;
diff --git a/view/src/test/java/se/su/dsv/scipro/finalthesis/SupervisorFinalThesisListingPageTest.java b/view/src/test/java/se/su/dsv/scipro/finalthesis/SupervisorFinalThesisListingPageTest.java
index c1ed2d955a..8d8625563a 100644
--- a/view/src/test/java/se/su/dsv/scipro/finalthesis/SupervisorFinalThesisListingPageTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/finalthesis/SupervisorFinalThesisListingPageTest.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.finalthesis;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.ProjectType;
diff --git a/view/src/test/java/se/su/dsv/scipro/grading/CriteriaPanelTest.java b/view/src/test/java/se/su/dsv/scipro/grading/CriteriaPanelTest.java
index 9577a21d84..608659db2f 100644
--- a/view/src/test/java/se/su/dsv/scipro/grading/CriteriaPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/grading/CriteriaPanelTest.java
@@ -17,6 +17,7 @@ import se.su.dsv.scipro.system.User;
 import se.su.dsv.scipro.test.UserBuilder;
 
 import java.time.LocalDate;
+import java.time.Month;
 import java.util.*;
 
 import static se.su.dsv.scipro.grading.CriteriaPanel.*;
@@ -110,7 +111,8 @@ public class CriteriaPanelTest extends SciProTest {
     }
 
     private void prepareReports() {
-        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(BACHELOR, 30);
+        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(BACHELOR,
+                LocalDate.of(2024, Month.JANUARY, 1));
         gradingReportTemplate.addIndividualCriterion("Projektkriterium", "Project criterion", 1, getPointTemplates(2));
         gradingReportTemplate.addIndividualCriterion("Individuellt kriterium", "Individual criterion", 0, getPointTemplates(2));
         gradingReport = gradingReportTemplate.createSupervisorReport(SOME_PROJECT, SOME_USER);
diff --git a/view/src/test/java/se/su/dsv/scipro/grading/FillOutReportPanelTest.java b/view/src/test/java/se/su/dsv/scipro/grading/FillOutReportPanelTest.java
index 2ccff71344..209a783033 100644
--- a/view/src/test/java/se/su/dsv/scipro/grading/FillOutReportPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/grading/FillOutReportPanelTest.java
@@ -20,6 +20,7 @@ import se.su.dsv.scipro.system.User;
 import se.su.dsv.scipro.test.UserBuilder;
 
 import java.time.LocalDate;
+import java.time.Month;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
@@ -44,7 +45,8 @@ public class FillOutReportPanelTest extends SciProTest {
         User headSupervisorUser = new UserBuilder().create();
         Project project = Project.builder().title("title").projectType(projectType).startDate(LocalDate.now()).headSupervisor(headSupervisorUser).build();
         finalSeminar.setProject(project);
-        GradingReportTemplate template = new GradingReportTemplate(projectType, 30);
+        GradingReportTemplate template = new GradingReportTemplate(projectType,
+                LocalDate.of(2024, Month.JANUARY, 1));
         template.addProjectCriterion("U1", "U1", 1, getPointTemplates(1));
         FinalSeminarOpposition finalSeminarOpposition = new FinalSeminarOpposition();
         finalSeminarOpposition.setFinalSeminar(finalSeminar);
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 a7edf1d14b..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
@@ -13,6 +13,7 @@ import se.su.dsv.scipro.system.User;
 import se.su.dsv.scipro.test.UserBuilder;
 
 import java.time.LocalDate;
+import java.time.Month;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -44,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
@@ -61,7 +62,8 @@ public class GradingReportPointsPanelTest extends SciProTest {
     }
 
     private void prepareReports() {
-        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(BACHELOR, 30);
+        GradingReportTemplate gradingReportTemplate = new GradingReportTemplate(BACHELOR,
+                LocalDate.of(2024, Month.JANUARY, 1));
         gradingReportTemplate.addProjectCriterion("title", "titleEn", 0, getPointTemplates(2));
         gradingReport = gradingReportTemplate.createSupervisorReport(SOME_PROJECT, SOME_USER);
         for (GradingCriterion gradingCriterion : gradingReport.getGradingCriteria()) {
diff --git a/view/src/test/java/se/su/dsv/scipro/grading/ThesisApprovedPanelTest.java b/view/src/test/java/se/su/dsv/scipro/grading/ThesisApprovedPanelTest.java
index 46d7ef2a76..5a13e8e940 100644
--- a/view/src/test/java/se/su/dsv/scipro/grading/ThesisApprovedPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/grading/ThesisApprovedPanelTest.java
@@ -11,6 +11,7 @@ import se.su.dsv.scipro.system.DegreeType;
 import se.su.dsv.scipro.system.ProjectType;
 
 import java.time.LocalDate;
+import java.time.Month;
 import java.util.Date;
 
 public class ThesisApprovedPanelTest extends SciProTest {
@@ -31,7 +32,8 @@ public class ThesisApprovedPanelTest extends SciProTest {
         ProjectType projectType = new ProjectType(ProjectType.MASTER, "Master", "Master");
         projectType.setId(1L);
 
-        Mockito.when(gradingReportService.getTemplate(SOME_PROJECT)).thenReturn(new GradingReportTemplate(projectType, 30));
+        Mockito.when(gradingReportService.getTemplate(SOME_PROJECT)).thenReturn(new GradingReportTemplate(projectType,
+                LocalDate.of(2024, Month.JANUARY, 1)));
         panel = tester.startComponentInPage(new ThesisApprovedPanel("panel", Model.of(SOME_PROJECT)));
     }
 
diff --git a/view/src/test/java/se/su/dsv/scipro/match/AdminIdeaOverviewPanelTest.java b/view/src/test/java/se/su/dsv/scipro/match/AdminIdeaOverviewPanelTest.java
index 2b59fce915..b0897b0e92 100644
--- a/view/src/test/java/se/su/dsv/scipro/match/AdminIdeaOverviewPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/match/AdminIdeaOverviewPanelTest.java
@@ -7,7 +7,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentMatchers;
 import org.mockito.Mockito;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.system.ProjectType;
 import se.su.dsv.scipro.system.User;
diff --git a/view/src/test/java/se/su/dsv/scipro/match/AdminIdeaStatisticsPageTest.java b/view/src/test/java/se/su/dsv/scipro/match/AdminIdeaStatisticsPageTest.java
index e624dafa45..0aa07c583d 100644
--- a/view/src/test/java/se/su/dsv/scipro/match/AdminIdeaStatisticsPageTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/match/AdminIdeaStatisticsPageTest.java
@@ -9,7 +9,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentMatchers;
 import org.mockito.Mockito;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.components.ExportableDataPanel;
 
diff --git a/view/src/test/java/se/su/dsv/scipro/match/AdminKeywordCrudPanelTest.java b/view/src/test/java/se/su/dsv/scipro/match/AdminKeywordCrudPanelTest.java
index 0a1eb20d09..470b12fab5 100644
--- a/view/src/test/java/se/su/dsv/scipro/match/AdminKeywordCrudPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/match/AdminKeywordCrudPanelTest.java
@@ -9,7 +9,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentMatchers;
 import org.mockito.Mockito;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.components.ExportableDataPanel;
 import se.su.dsv.scipro.test.DomainObjects;
diff --git a/view/src/test/java/se/su/dsv/scipro/match/AdminProgramPageTest.java b/view/src/test/java/se/su/dsv/scipro/match/AdminProgramPageTest.java
index e663380dfa..15936b511d 100644
--- a/view/src/test/java/se/su/dsv/scipro/match/AdminProgramPageTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/match/AdminProgramPageTest.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.match;
 
 import org.apache.wicket.Page;
 import org.junit.jupiter.api.BeforeEach;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.PageTest;
 import se.su.dsv.scipro.system.Program;
 
diff --git a/view/src/test/java/se/su/dsv/scipro/match/AdminUnitPageTest.java b/view/src/test/java/se/su/dsv/scipro/match/AdminUnitPageTest.java
index 6564baefae..e215bb5869 100644
--- a/view/src/test/java/se/su/dsv/scipro/match/AdminUnitPageTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/match/AdminUnitPageTest.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.match;
 
 import org.apache.wicket.Page;
 import org.junit.jupiter.api.BeforeEach;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.PageTest;
 import se.su.dsv.scipro.system.Unit;
 
diff --git a/view/src/test/java/se/su/dsv/scipro/match/SupervisorAllStudentIdeasPanelTest.java b/view/src/test/java/se/su/dsv/scipro/match/SupervisorAllStudentIdeasPanelTest.java
index 7e2de887e2..4b65de31dd 100644
--- a/view/src/test/java/se/su/dsv/scipro/match/SupervisorAllStudentIdeasPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/match/SupervisorAllStudentIdeasPanelTest.java
@@ -3,7 +3,7 @@ package se.su.dsv.scipro.match;
 import org.apache.wicket.model.Model;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.system.ProjectType;
 import se.su.dsv.scipro.system.User;
diff --git a/view/src/test/java/se/su/dsv/scipro/match/SupervisorMyIdeasPanelTest.java b/view/src/test/java/se/su/dsv/scipro/match/SupervisorMyIdeasPanelTest.java
index b1eee0b76d..ea61feda65 100644
--- a/view/src/test/java/se/su/dsv/scipro/match/SupervisorMyIdeasPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/match/SupervisorMyIdeasPanelTest.java
@@ -5,7 +5,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentMatchers;
 import org.mockito.Mockito;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.system.ResearchArea;
 import se.su.dsv.scipro.system.User;
diff --git a/view/src/test/java/se/su/dsv/scipro/milestones/EditMilestoneActivityPanelTest.java b/view/src/test/java/se/su/dsv/scipro/milestones/EditMilestoneActivityPanelTest.java
index 0cc114b103..81ecc068ed 100644
--- a/view/src/test/java/se/su/dsv/scipro/milestones/EditMilestoneActivityPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/milestones/EditMilestoneActivityPanelTest.java
@@ -14,7 +14,7 @@ import org.mockito.ArgumentMatchers;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.internal.verification.VerificationModeFactory;
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.milestones.dataobjects.MilestoneActivityTemplate;
 import se.su.dsv.scipro.milestones.dataobjects.MilestonePhaseTemplate;
diff --git a/view/src/test/java/se/su/dsv/scipro/milestones/MilestoneActivityAdminPanelTest.java b/view/src/test/java/se/su/dsv/scipro/milestones/MilestoneActivityAdminPanelTest.java
index 233e7bdf30..dd503603e4 100644
--- a/view/src/test/java/se/su/dsv/scipro/milestones/MilestoneActivityAdminPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/milestones/MilestoneActivityAdminPanelTest.java
@@ -9,7 +9,7 @@ import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentMatchers;
 import org.mockito.Mock;
 import org.mockito.Mockito;
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.components.DisplayMultiplesPanel;
 import se.su.dsv.scipro.milestones.dataobjects.MilestoneActivityTemplate;
diff --git a/view/src/test/java/se/su/dsv/scipro/milestones/MilestonePhaseAdminPanelTest.java b/view/src/test/java/se/su/dsv/scipro/milestones/MilestonePhaseAdminPanelTest.java
index 8b79b8caff..3c32df6acd 100644
--- a/view/src/test/java/se/su/dsv/scipro/milestones/MilestonePhaseAdminPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/milestones/MilestonePhaseAdminPanelTest.java
@@ -8,7 +8,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentMatchers;
 import org.mockito.Mockito;
-import org.springframework.data.domain.Sort;
+import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.components.SortOrderPanel;
 import se.su.dsv.scipro.milestones.dataobjects.MilestonePhaseTemplate;
diff --git a/view/src/test/java/se/su/dsv/scipro/notifications/panels/NotificationDataPanelTest.java b/view/src/test/java/se/su/dsv/scipro/notifications/panels/NotificationDataPanelTest.java
index 99f8a03cec..df03f0a9fe 100644
--- a/view/src/test/java/se/su/dsv/scipro/notifications/panels/NotificationDataPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/notifications/panels/NotificationDataPanelTest.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.notifications.panels;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.data.dataobjects.Member;
 import se.su.dsv.scipro.notifications.NotificationService;
diff --git a/view/src/test/java/se/su/dsv/scipro/peer/LatestReviewPanelTest.java b/view/src/test/java/se/su/dsv/scipro/peer/LatestReviewPanelTest.java
index 1456406ce8..8fb5f53ca5 100644
--- a/view/src/test/java/se/su/dsv/scipro/peer/LatestReviewPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/peer/LatestReviewPanelTest.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.peer;
 
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettings;
 import se.su.dsv.scipro.util.PageParameterKeys;
diff --git a/view/src/test/java/se/su/dsv/scipro/peer/PeerRequestSelectionPanelTest.java b/view/src/test/java/se/su/dsv/scipro/peer/PeerRequestSelectionPanelTest.java
index cde069c24d..913b5552a1 100644
--- a/view/src/test/java/se/su/dsv/scipro/peer/PeerRequestSelectionPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/peer/PeerRequestSelectionPanelTest.java
@@ -4,7 +4,7 @@ import org.apache.wicket.model.Model;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentMatchers;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.notifications.dataobject.NotificationSource;
 import se.su.dsv.scipro.notifications.dataobject.PeerEvent;
diff --git a/view/src/test/java/se/su/dsv/scipro/projectpartner/ProjectPartnerPageTest.java b/view/src/test/java/se/su/dsv/scipro/projectpartner/ProjectPartnerPageTest.java
index 20aa3c60a2..42b6763e8d 100644
--- a/view/src/test/java/se/su/dsv/scipro/projectpartner/ProjectPartnerPageTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/projectpartner/ProjectPartnerPageTest.java
@@ -5,7 +5,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentMatchers;
 import org.mockito.Mockito;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettings;
 import se.su.dsv.scipro.match.ApplicationPeriod;
diff --git a/view/src/test/java/se/su/dsv/scipro/repository/panels/ProjectFilePanelTest.java b/view/src/test/java/se/su/dsv/scipro/repository/panels/ProjectFilePanelTest.java
index 879bb37b19..23fe60979f 100644
--- a/view/src/test/java/se/su/dsv/scipro/repository/panels/ProjectFilePanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/repository/panels/ProjectFilePanelTest.java
@@ -3,7 +3,7 @@ package se.su.dsv.scipro.repository.panels;
 import org.apache.wicket.model.Model;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.file.FileDescription;
 import se.su.dsv.scipro.file.FileReference;
diff --git a/view/src/test/java/se/su/dsv/scipro/statistics/AdminUnfinishedFinalSeminarsPageTest.java b/view/src/test/java/se/su/dsv/scipro/statistics/AdminUnfinishedFinalSeminarsPageTest.java
index 6c6c80c635..ff4ced4682 100644
--- a/view/src/test/java/se/su/dsv/scipro/statistics/AdminUnfinishedFinalSeminarsPageTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/statistics/AdminUnfinishedFinalSeminarsPageTest.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.statistics;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.finalseminar.FinalSeminar;
 import se.su.dsv.scipro.finalseminar.FinalSeminarOpposition;
diff --git a/view/src/test/java/se/su/dsv/scipro/statistics/ProjectMilestoneDetailsPanelTest.java b/view/src/test/java/se/su/dsv/scipro/statistics/ProjectMilestoneDetailsPanelTest.java
index 930d16838d..42678ee12d 100644
--- a/view/src/test/java/se/su/dsv/scipro/statistics/ProjectMilestoneDetailsPanelTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/statistics/ProjectMilestoneDetailsPanelTest.java
@@ -5,7 +5,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentMatchers;
 import org.mockito.Mockito;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.milestones.dataobjects.Milestone;
 import se.su.dsv.scipro.milestones.dataobjects.MilestoneActivityTemplate;
diff --git a/view/src/test/java/se/su/dsv/scipro/supervisor/pages/SupervisorStartPageTest.java b/view/src/test/java/se/su/dsv/scipro/supervisor/pages/SupervisorStartPageTest.java
index 23fa9eed8d..5177f3dfbc 100644
--- a/view/src/test/java/se/su/dsv/scipro/supervisor/pages/SupervisorStartPageTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/supervisor/pages/SupervisorStartPageTest.java
@@ -4,7 +4,7 @@ import org.apache.wicket.util.tester.FormTester;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentCaptor;
-import org.springframework.data.domain.Pageable;
+import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.SciProTest;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.project.ProjectService;