From 949de4a26041cdfca9e0b7dc9ec1485a2a5d4a5c Mon Sep 17 00:00:00 2001
From: ansv7779 <andreass@dsv.su.se>
Date: Wed, 10 Jul 2024 13:43:27 +0200
Subject: [PATCH 1/4] Allow supervisors to write a note associated with their
 projects (#8)

There is a need among supervisors to maintain some work notes for each project. Where they are in the process, what students need to do, and other such things.

This PR gives the supervisors the ability to write a note for each project on their overview page.

Card 3399

Reviewed-on: https://gitea.dsv.su.se/DSV/scipro/pulls/8
Reviewed-by: niat8586 <nico@dsv.su.se>
---
 core/src/main/java/modules/CoreModule.java    |   2 +
 .../se/su/dsv/scipro/project/Project.java     |  15 ++
 .../scipro/project/ProjectNoteService.java    |   9 ++
 .../se/su/dsv/scipro/project/ProjectRepo.java |   4 +
 .../dsv/scipro/project/ProjectRepoImpl.java   |  13 ++
 .../scipro/project/ProjectServiceImpl.java    |  12 +-
 .../SupervisorProjectNoteDisplay.java         |   5 +
 .../settings/dataobjects/UserProfile.java     |  13 ++
 .../V388__user_notes_for_projects.sql         |  11 ++
 .../dsv/scipro/components/MaxLengthLabel.java |  37 +++++
 .../scipro/components/ModalWindowPlus.java    |  11 ++
 ...ctNoteColumn$ViewAndEditNoteCellPanel.html |  11 ++
 ...n$ViewAndEditNoteCellPanel.utf8.properties |   2 +
 ...ProjectNoteColumn$ViewAndEditNoteForm.html |  15 ++
 .../supervisor/panels/ProjectNoteColumn.java  | 129 ++++++++++++++++++
 .../panels/SupervisorMyProjectsPanel.html     |   4 +
 .../panels/SupervisorMyProjectsPanel.java     |  20 +++
 .../SupervisorMyProjectsPanel.utf8.properties |   3 +
 .../java/se/su/dsv/scipro/SciProTest.java     |   3 +
 19 files changed, 318 insertions(+), 1 deletion(-)
 create mode 100644 core/src/main/java/se/su/dsv/scipro/project/ProjectNoteService.java
 create mode 100644 core/src/main/java/se/su/dsv/scipro/settings/dataobjects/SupervisorProjectNoteDisplay.java
 create mode 100644 core/src/main/resources/db/migration/V388__user_notes_for_projects.sql
 create mode 100644 view/src/main/java/se/su/dsv/scipro/components/MaxLengthLabel.java
 create mode 100644 view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.html
 create mode 100644 view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.utf8.properties
 create mode 100644 view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteForm.html
 create mode 100644 view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java

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/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..f27da7b94a 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
@@ -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..2ba2a6a436 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
@@ -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/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/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/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/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/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;
 

From e3003231357843a9d1f0ec30d88ad98118454b93 Mon Sep 17 00:00:00 2001
From: wwest <wayne@dsv.su.se>
Date: Thu, 25 Jul 2024 14:20:24 +0200
Subject: [PATCH 2/4] Update pom.xml (#10)

changed the wicket version due to security scan

Reviewed-on: https://gitea.dsv.su.se/DMC/scipro/pulls/10
Reviewed-by: niat8586 <nico@dsv.su.se>
Co-authored-by: wwest <wayne@dsv.su.se>
Co-committed-by: wwest <wayne@dsv.su.se>
---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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>

From d8e4f0dd22c50bbe41d560992f93820cb8ac981c Mon Sep 17 00:00:00 2001
From: Andreas Svanberg <andreass@dsv.su.se>
Date: Wed, 15 May 2024 14:23:13 +0200
Subject: [PATCH 3/4] Stop confusing Spring Boot with copied classes

These classes were copied from Spring Data a long time ago. Their existence on the class path confuses Spring Boot into thinking we use Spring Data JPA which we do not.
---
 .../se/su/dsv/scipro/activityplan/ActivityPlanFacade.java   | 2 +-
 .../su/dsv/scipro/activityplan/ActivityPlanFacadeImpl.java  | 2 +-
 .../activityplan/ActivityPlanTemplateServiceImpl.java       | 2 +-
 .../se/su/dsv/scipro/checklist/ChecklistCategoryRepo.java   | 4 ++--
 .../se/su/dsv/scipro/checklist/ChecklistQuestionRepo.java   | 4 ++--
 .../dsv/scipro/checklist/ChecklistTemplateServiceImpl.java  | 2 +-
 .../java/se/su/dsv/scipro/file/FileDescriptionRepo.java     | 4 ++--
 .../java/se/su/dsv/scipro/file/ProjectFileRepository.java   | 6 +++---
 .../se/su/dsv/scipro/file/ProjectFileRepositoryImpl.java    | 2 +-
 .../main/java/se/su/dsv/scipro/file/ProjectFileService.java | 2 +-
 .../java/se/su/dsv/scipro/file/ProjectFileServiceImpl.java  | 2 +-
 .../dsv/scipro/finalseminar/FinalSeminarOppositionRepo.java | 4 ++--
 .../se/su/dsv/scipro/finalseminar/FinalSeminarService.java  | 2 +-
 .../su/dsv/scipro/finalseminar/FinalSeminarServiceImpl.java | 2 +-
 .../su/dsv/scipro/finalthesis/FinalThesisServiceImpl.java   | 2 +-
 .../se/su/dsv/scipro/forum/AbstractThreadRepository.java    | 4 ++--
 .../su/dsv/scipro/forum/ForumPostReadStateRepository.java   | 4 ++--
 .../java/se/su/dsv/scipro/forum/ForumPostRepository.java    | 4 ++--
 .../java/se/su/dsv/scipro/forum/GroupThreadRepository.java  | 4 ++--
 .../se/su/dsv/scipro/forum/ProjectThreadRepository.java     | 4 ++--
 .../main/java/se/su/dsv/scipro/group/GroupServiceImpl.java  | 2 +-
 .../ActivityFinalSeminarRepository.java                     | 4 ++--
 .../java/se/su/dsv/scipro/mail/MailEventServiceImpl.java    | 2 +-
 .../su/dsv/scipro/match/ApplicationPeriodServiceImpl.java   | 4 ++--
 .../java/se/su/dsv/scipro/match/FirstMeetingRepository.java | 4 ++--
 core/src/main/java/se/su/dsv/scipro/match/IdeaFacade.java   | 2 +-
 core/src/main/java/se/su/dsv/scipro/match/IdeaService.java  | 2 +-
 .../main/java/se/su/dsv/scipro/match/IdeaServiceImpl.java   | 2 +-
 .../main/java/se/su/dsv/scipro/match/KeywordService.java    | 2 +-
 .../java/se/su/dsv/scipro/match/KeywordServiceImpl.java     | 6 +++---
 .../java/se/su/dsv/scipro/match/MatchFollowUpService.java   | 2 +-
 .../se/su/dsv/scipro/match/MatchFollowUpServiceImpl.java    | 2 +-
 core/src/main/java/se/su/dsv/scipro/match/MatchService.java | 2 +-
 .../main/java/se/su/dsv/scipro/match/MatchServiceImpl.java  | 2 +-
 .../java/se/su/dsv/scipro/match/ProgramServiceImpl.java     | 4 ++--
 .../milestones/MilestoneActivityTemplateRepository.java     | 4 ++--
 .../su/dsv/scipro/milestones/service/MileStoneService.java  | 2 +-
 .../milestones/service/MilestonePhaseTemplateService.java   | 2 +-
 .../milestones/service/MilestoneStatisticsService.java      | 2 +-
 .../service/impl/MilestonePhaseTemplateServiceImpl.java     | 2 +-
 .../milestones/service/impl/MilestoneServiceImpl.java       | 2 +-
 .../service/impl/MilestoneStatisticsServiceImpl.java        | 4 ++--
 .../dsv/scipro/notifications/NotificationServiceImpl.java   | 2 +-
 .../main/java/se/su/dsv/scipro/peer/CommentThreadRepo.java  | 4 ++--
 .../java/se/su/dsv/scipro/peer/PeerRequestRepository.java   | 4 ++--
 .../main/java/se/su/dsv/scipro/peer/PeerRequestService.java | 2 +-
 .../java/se/su/dsv/scipro/peer/PeerRequestServiceImpl.java  | 2 +-
 .../java/se/su/dsv/scipro/peer/PeerReviewRepository.java    | 4 ++--
 .../main/java/se/su/dsv/scipro/peer/PeerReviewService.java  | 2 +-
 .../java/se/su/dsv/scipro/peer/PeerReviewServiceImpl.java   | 2 +-
 .../dsv/scipro/plagiarism/PlagiarismRequestRepository.java  | 2 +-
 .../src/main/java/se/su/dsv/scipro/project/ProjectRepo.java | 4 ++--
 .../java/se/su/dsv/scipro/project/ProjectServiceImpl.java   | 2 +-
 .../su/dsv/scipro/projectpartner/ProjectPartnerService.java | 2 +-
 .../scipro/projectpartner/ProjectPartnerServiceImpl.java    | 2 +-
 .../se/su/dsv/scipro/report/GradingReportTemplateRepo.java  | 2 +-
 .../java/se/su/dsv/scipro/report/OppositionReportRepo.java  | 4 ++--
 .../reviewing/ProjectFinalSeminarStatisticsService.java     | 2 +-
 .../reviewing/ProjectFinalSeminarStatisticsServiceImpl.java | 2 +-
 .../su/dsv/scipro/reviewing/ReviewerThreadRepository.java   | 2 +-
 .../springdata/serviceimpls/SupervisorServiceImpl.java      | 2 +-
 .../dsv/scipro/springdata/services/SupervisorService.java   | 2 +-
 .../java/se/su/dsv/scipro/survey/QuestionRepository.java    | 2 +-
 .../main/java/se/su/dsv/scipro/survey/SurveyRepository.java | 2 +-
 .../java/se/su/dsv/scipro/system/AbstractServiceImpl.java   | 2 --
 .../main/java/se/su/dsv/scipro/system/FilteredService.java  | 2 --
 .../java/se/su/dsv/scipro/system/FooterAddressRepo.java     | 2 --
 .../main/java/se/su/dsv/scipro/system/FooterLinkRepo.java   | 2 --
 core/src/main/java/se/su/dsv/scipro/system/GenericRepo.java | 4 ----
 .../main/java/se/su/dsv/scipro/system/GenericService.java   | 1 -
 .../su/dsv/scipro/system}/JpaRepository.java                | 2 +-
 .../main/java/se/su/dsv/scipro/system/LocalUserSearch.java  | 1 -
 .../domain => se/su/dsv/scipro/system}/PageRequest.java     | 2 +-
 .../data/domain => se/su/dsv/scipro/system}/Pageable.java   | 2 +-
 .../src/main/java/se/su/dsv/scipro/system/PasswordRepo.java | 2 --
 .../su/dsv/scipro/system}/QueryDslPredicateExecutor.java    | 3 +--
 .../data/domain => se/su/dsv/scipro/system}/Sort.java       | 2 +-
 core/src/main/java/se/su/dsv/scipro/system/UserRepo.java    | 3 ---
 .../src/main/java/se/su/dsv/scipro/system/UserRepoImpl.java | 1 -
 .../main/java/se/su/dsv/scipro/system/UserServiceImpl.java  | 1 -
 .../GradeFinalSeminarParticipantReminderWorker.java         | 2 +-
 .../activityplan/ActivityPlanFacadeImplIntegrationTest.java | 4 ++--
 .../scipro/checklist/ChecklistTemplateServiceImplTest.java  | 2 +-
 .../FinalSeminarServiceImplIntegrationTest.java             | 2 +-
 .../dsv/scipro/finalthesis/FinalThesisServiceImplTest.java  | 2 +-
 .../test/java/se/su/dsv/scipro/match/IdeaFacadeTest.java    | 4 ++--
 .../su/dsv/scipro/match/IdeaServiceImplIntegrationTest.java | 2 +-
 .../se/su/dsv/scipro/match/MatchFollowUpServiceTest.java    | 4 ++--
 .../service/impl/MilestonePhaseTemplateServiceImplTest.java | 2 +-
 .../service/impl/MilestoneServiceImplIntegrationTest.java   | 2 +-
 .../service/impl/MilestoneStatisticsServiceImplTest.java    | 2 +-
 .../NotificationServiceImplIntegrationTest.java             | 2 +-
 .../se/su/dsv/scipro/peer/PeerRequestServiceImplTest.java   | 2 +-
 .../scipro/peer/PeerReviewServiceImplIntegrationTest.java   | 2 +-
 .../se/su/dsv/scipro/peer/PeerReviewServiceImplTest.java    | 2 +-
 .../src/test/java/se/su/dsv/scipro/peer/TestPeerReview.java | 2 +-
 .../scipro/project/ProjectServiceImplIntegrationTest.java   | 2 +-
 .../projectpartner/ProjectPartnerServiceImplTest.java       | 2 +-
 .../springdata/serviceimpls/SupervisorServiceImplTest.java  | 2 +-
 .../dsv/scipro/system/UserServiceImplIntegrationTest.java   | 1 -
 .../daisyExternal/impl/ExternalImporterDaisyImpl.java       | 2 +-
 .../su/dsv/scipro/activityplan/UpcomingActivitiesPanel.java | 4 ++--
 .../java/se/su/dsv/scipro/dataproviders/PageAdapter.java    | 4 ++--
 .../java/se/su/dsv/scipro/match/ProjectMyIdeasPanel.java    | 4 ++--
 .../dsv/scipro/milestones/EditMilestoneActivityPanel.java   | 2 +-
 .../dsv/scipro/milestones/MileStoneActivityAdminPanel.java  | 2 +-
 .../su/dsv/scipro/milestones/MileStonePhaseAdminPanel.java  | 2 +-
 .../main/java/se/su/dsv/scipro/peer/LatestReviewPanel.java  | 4 ++--
 .../se/su/dsv/scipro/peer/PeerRequestSelectionPanel.java    | 2 +-
 .../scipro/projectpartner/ProjectPartnerDataProvider.java   | 4 ++--
 .../scipro/statistics/AdminFinalSeminarStatisticsPage.java  | 2 +-
 .../scipro/statistics/AdminUnmetTargetsStatisticsPage.java  | 4 ++--
 .../su/dsv/scipro/activityplan/ActivityPlanPanelTest.java   | 2 +-
 .../scipro/activityplan/UpcomingActivitiesPanelTest.java    | 2 +-
 .../applicationperiod/AdminApplicationPeriodsPanelTest.java | 2 +-
 .../scipro/applicationperiod/AdminEditTargetsPageTest.java  | 2 +-
 .../dsv/scipro/dataproviders/FilteredDataProviderTest.java  | 2 +-
 .../dsv/scipro/dataproviders/GenericDataProviderTest.java   | 2 +-
 .../dsv/scipro/datatables/project/ProjectDataPanelTest.java | 2 +-
 .../finalseminar/AdminFinalSeminarExemptionPageTest.java    | 2 +-
 .../dsv/scipro/finalseminar/ProjectOppositionPageTest.java  | 2 +-
 .../finalthesis/SupervisorFinalThesisListingPageTest.java   | 2 +-
 .../se/su/dsv/scipro/match/AdminIdeaOverviewPanelTest.java  | 2 +-
 .../se/su/dsv/scipro/match/AdminIdeaStatisticsPageTest.java | 2 +-
 .../se/su/dsv/scipro/match/AdminKeywordCrudPanelTest.java   | 2 +-
 .../java/se/su/dsv/scipro/match/AdminProgramPageTest.java   | 2 +-
 .../test/java/se/su/dsv/scipro/match/AdminUnitPageTest.java | 2 +-
 .../scipro/match/SupervisorAllStudentIdeasPanelTest.java    | 2 +-
 .../se/su/dsv/scipro/match/SupervisorMyIdeasPanelTest.java  | 2 +-
 .../scipro/milestones/EditMilestoneActivityPanelTest.java   | 2 +-
 .../scipro/milestones/MilestoneActivityAdminPanelTest.java  | 2 +-
 .../dsv/scipro/milestones/MilestonePhaseAdminPanelTest.java | 2 +-
 .../notifications/panels/NotificationDataPanelTest.java     | 2 +-
 .../java/se/su/dsv/scipro/peer/LatestReviewPanelTest.java   | 2 +-
 .../su/dsv/scipro/peer/PeerRequestSelectionPanelTest.java   | 2 +-
 .../dsv/scipro/projectpartner/ProjectPartnerPageTest.java   | 2 +-
 .../dsv/scipro/repository/panels/ProjectFilePanelTest.java  | 2 +-
 .../statistics/AdminUnfinishedFinalSeminarsPageTest.java    | 2 +-
 .../scipro/statistics/ProjectMilestoneDetailsPanelTest.java | 2 +-
 .../scipro/supervisor/pages/SupervisorStartPageTest.java    | 2 +-
 140 files changed, 161 insertions(+), 184 deletions(-)
 rename core/src/main/java/{org/springframework/data/jpa/repository => se/su/dsv/scipro/system}/JpaRepository.java (96%)
 rename core/src/main/java/{org/springframework/data/domain => se/su/dsv/scipro/system}/PageRequest.java (96%)
 rename core/src/main/java/{org/springframework/data/domain => se/su/dsv/scipro/system}/Pageable.java (69%)
 rename core/src/main/java/{org/springframework/data/querydsl => se/su/dsv/scipro/system}/QueryDslPredicateExecutor.java (76%)
 rename core/src/main/java/{org/springframework/data/domain => se/su/dsv/scipro/system}/Sort.java (96%)

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/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/ProjectRepo.java b/core/src/main/java/se/su/dsv/scipro/project/ProjectRepo.java
index f27da7b94a..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;
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 2ba2a6a436..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;
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/GradingReportTemplateRepo.java b/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplateRepo.java
index 26bd98075a..9f6f8d1238 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,6 +1,6 @@
 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.system.ProjectType;
 
 public interface GradingReportTemplateRepo extends JpaRepository<GradingReportTemplate, Long> {
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/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/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/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/FinalSeminarServiceImplIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/finalseminar/FinalSeminarServiceImplIntegrationTest.java
index b11c3af291..31b189eaaf 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;
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/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/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/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/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/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/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/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;

From e18af7233669300930afa7c5c4847a292e537128 Mon Sep 17 00:00:00 2001
From: Andreas Svanberg <andreass@dsv.su.se>
Date: Wed, 30 Oct 2024 10:05:22 +0100
Subject: [PATCH 4/4] Allows admins to manage grading report templates (#14)

# Manage grading report templates
Every once in a while the criteria used to asses theses and individual performance changes. When this happens the criteria in SciPro needs to be updated.

Up until now there has not been a GUI to do this and it has instead been done manually, sometimes using database migrations. It was also time-sensitive since there was only ever one template (per project type) in SciPro such that the change had to happen at the correct time when the new template was to take effect.

There was also the question of how to calculate the grade the author should receive based on the point sum from the supervisors assessment of all the criteria and these limits were hard-coded in the system.

These changes address all of the above;
 * There can be multiple templates per type.
 * Each template has a period during which it is are current.
 * Each template has defined the conversion of point sums to grades.

The periods can not overlap and a template remains current from its start date until another template's start date is reached. There can not be two templates (within a project type) that become valid on the same date.

## How to test
 1. Log in as administrator
 2. Go to "Project management"
 3. Go to "Grading templates"
 4. Create/edit new templates
 5. View current template

Card 3482

Co-authored-by: niat8586 <nico@dsv.su.se>
Reviewed-on: https://gitea.dsv.su.se/DMC/scipro/pulls/14
Reviewed-by: niat8586 <nico@dsv.su.se>
Co-authored-by: Andreas Svanberg <andreass@dsv.su.se>
Co-committed-by: Andreas Svanberg <andreass@dsv.su.se>
---
 .../main/java/modules/RepositoryModule.java   |   3 +
 .../se/su/dsv/scipro/DataInitializer.java     |  49 +--
 .../su/dsv/scipro/grading/GradingModule.java  |   4 +
 .../grading/GradingReportTemplateService.java |  41 +++
 .../grading/GradingReportTemplateUpdate.java  |  61 ++++
 .../dsv/scipro/grading/LocalizedString.java   |  10 +
 .../report/AbstractGradingCriterion.java      |  44 +++
 .../scipro/report/DuplicateDateException.java |  23 ++
 .../report/GradeCalculatorServiceImpl.java    |  14 +
 .../se/su/dsv/scipro/report/GradeLimit.java   |  46 +++
 .../dsv/scipro/report/GradingCriterion.java   |   2 +-
 .../su/dsv/scipro/report/GradingReport.java   |  10 +-
 .../scipro/report/GradingReportService.java   |   3 +-
 .../report/GradingReportServiceImpl.java      | 139 ++++++--
 .../scipro/report/GradingReportTemplate.java  |  78 ++++-
 .../GradingReportTemplateGradeCalculator.java |  39 +++
 .../report/GradingReportTemplateRepo.java     |  19 +-
 .../report/GradingReportTemplateRepoImpl.java | 125 +++++++-
 .../report/NoSuchTemplateException.java       |   4 +
 .../report/OppositionReportServiceImpl.java   |   2 +-
 .../SupervisorGradingReportRepository.java    |  10 +
 ...SupervisorGradingReportRepositoryImpl.java |  37 +++
 .../report/TemplateLockedException.java       |  15 +
 .../ValidDateMustBeInTheFutureException.java  |  21 ++
 ...rading_report_template_valid_timespans.sql |  46 +++
 ...2__failing_grade_and_note_on_templates.sql |   3 +
 ...9_3__grade_limits_on_grading_templates.sql |  10 +
 .../V389_4__grading_criterion_flags.sql       |   4 +
 ...5__migrate_opposition_criteria_to_flag.sql |   2 +
 ...6__migrate_reflection_criteria_to_flag.sql |   2 +
 ...rOppositionServiceImplIntegrationTest.java |   4 +-
 ...inalSeminarServiceImplIntegrationTest.java |   4 +-
 .../FinalSeminarServiceImplTest.java          |   5 +-
 ...adingReportServiceImplIntegrationTest.java |  22 +-
 .../report/GradingReportTemplateTest.java     |   5 +-
 .../OppositionReportServiceImplTest.java      |   8 +-
 .../scipro/report/OppositionReportTest.java   |   5 +-
 .../SupervisorGradingReportFactoryTest.java   |   4 +-
 .../report/SupervisorGradingReportTest.java   |   7 +-
 ...SupervisorBachelorGradeCalculatorTest.java |   9 +-
 ...SupervisorMaster15GradeCalculatorTest.java |   9 +-
 ...SupervisorMaster30GradeCalculatorTest.java |   9 +-
 .../se/su/dsv/scipro/SciProApplication.java   |   8 +
 .../admin/pages/AbstractAdminProjectPage.java |   5 +-
 .../AdminGradingTemplateCreationPage.html     |  26 ++
 .../AdminGradingTemplateCreationPage.java     |  80 +++++
 .../grading/AdminGradingTemplateEditPage.html |  14 +
 .../grading/AdminGradingTemplateEditPage.java | 130 ++++++++
 .../grading/AdminGradingTemplatePage.html     |  35 +++
 .../grading/AdminGradingTemplatePage.java     |  95 ++++++
 .../AdminGradingTemplatesOverviewPage.html    | 125 ++++++++
 .../AdminGradingTemplatesOverviewPage.java    |  57 ++++
 .../pages/grading/EditingGradingTemplate.java | 184 +++++++++++
 .../EditingGradingTemplateComponentPanel.html | 159 ++++++++++
 .../EditingGradingTemplateComponentPanel.java | 297 ++++++++++++++++++
 ...dingTemplateComponentPanel.utf8.properties |  13 +
 .../admin/pages/grading/GradeLimits.java      |  64 ++++
 .../MenuHighlightGradingTemplates.java        |   6 +
 .../grading/wicket-package.utf8.properties    |   5 +
 .../dsv/scipro/components/NonEmptyLabel.java  |  19 ++
 .../su/dsv/scipro/grading/CriteriaPanel.java  |   6 +-
 .../grading/GradingReportPointsPanel.java     |   2 +-
 view/src/main/webapp/css/scipro_m.css         |   5 +-
 .../DownloadPdfReportPanelTest.java           |   5 +-
 .../OppositionReportPageTest.java             |   4 +-
 .../dsv/scipro/grading/CriteriaPanelTest.java |   4 +-
 .../grading/FillOutReportPanelTest.java       |   4 +-
 .../grading/GradingReportPointsPanelTest.java |   6 +-
 .../grading/ThesisApprovedPanelTest.java      |   4 +-
 69 files changed, 2208 insertions(+), 111 deletions(-)
 create mode 100644 core/src/main/java/se/su/dsv/scipro/grading/GradingReportTemplateService.java
 create mode 100644 core/src/main/java/se/su/dsv/scipro/grading/GradingReportTemplateUpdate.java
 create mode 100644 core/src/main/java/se/su/dsv/scipro/grading/LocalizedString.java
 create mode 100644 core/src/main/java/se/su/dsv/scipro/report/DuplicateDateException.java
 create mode 100644 core/src/main/java/se/su/dsv/scipro/report/GradeLimit.java
 create mode 100644 core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplateGradeCalculator.java
 create mode 100644 core/src/main/java/se/su/dsv/scipro/report/NoSuchTemplateException.java
 create mode 100644 core/src/main/java/se/su/dsv/scipro/report/SupervisorGradingReportRepository.java
 create mode 100644 core/src/main/java/se/su/dsv/scipro/report/SupervisorGradingReportRepositoryImpl.java
 create mode 100644 core/src/main/java/se/su/dsv/scipro/report/TemplateLockedException.java
 create mode 100644 core/src/main/java/se/su/dsv/scipro/report/ValidDateMustBeInTheFutureException.java
 create mode 100644 core/src/main/resources/db/migration/V389_1__grading_report_template_valid_timespans.sql
 create mode 100644 core/src/main/resources/db/migration/V389_2__failing_grade_and_note_on_templates.sql
 create mode 100644 core/src/main/resources/db/migration/V389_3__grade_limits_on_grading_templates.sql
 create mode 100644 core/src/main/resources/db/migration/V389_4__grading_criterion_flags.sql
 create mode 100644 core/src/main/resources/db/migration/V389_5__migrate_opposition_criteria_to_flag.sql
 create mode 100644 core/src/main/resources/db/migration/V389_6__migrate_reflection_criteria_to_flag.sql
 create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateCreationPage.html
 create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateCreationPage.java
 create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateEditPage.html
 create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateEditPage.java
 create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplatePage.html
 create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplatePage.java
 create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplatesOverviewPage.html
 create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplatesOverviewPage.java
 create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplate.java
 create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplateComponentPanel.html
 create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplateComponentPanel.java
 create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplateComponentPanel.utf8.properties
 create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/grading/GradeLimits.java
 create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/grading/MenuHighlightGradingTemplates.java
 create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/grading/wicket-package.utf8.properties
 create mode 100644 view/src/main/java/se/su/dsv/scipro/components/NonEmptyLabel.java

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/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/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 9f6f8d1238..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 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/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/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/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 31b189eaaf..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
@@ -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/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/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/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/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/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/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/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/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)));
     }