Allows admins to manage grading report templates #14
|
@ -1,6 +1,10 @@
|
|||
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;
|
||||
|
@ -22,4 +26,11 @@ public interface GradingReportTemplateService {
|
|||
LocalDate getEndDate(GradingReportTemplate gradingReportTemplate);
|
||||
|
||||
List<GradingReportTemplate> getUpcomingTemplates(ProjectType projectType);
|
||||
|
||||
GradingReportTemplate update(long templateId, GradingReportTemplateUpdate update)
|
||||
throws
|
||||
ValidDateMustBeInTheFutureException,
|
||||
NoSuchTemplateException,
|
||||
DuplicateDateException,
|
||||
TemplateLockedException;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
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 defaultGrade,
|
||||
List<Grade> grades,
|
||||
List<Criteria> criteria)
|
||||
{
|
||||
public GradingReportTemplateUpdate {
|
||||
Objects.requireNonNull(validFrom, "Valid from must not be null");
|
||||
Objects.requireNonNull(defaultGrade, "Default grade must not be null");
|
||||
Objects.requireNonNull(grades, "Grades must not be null");
|
||||
Objects.requireNonNull(criteria, "Criteria must not be null");
|
||||
|
||||
for (Grade grade1 : grades) {
|
||||
for (Grade grade2 : grades) {
|
||||
if (grade1 != grade2 && grade1.minimumPoints() == grade2.minimumPoints()) {
|
||||
throw new IllegalArgumentException("Duplicate minimum points on grades: %s and %s".formatted(
|
||||
grade1.grade(),
|
||||
grade2.grade()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public record Grade(String grade, int minimumPoints) {
|
||||
public Grade {
|
||||
Objects.requireNonNull(grade, "Grade must not be null");
|
||||
}
|
||||
}
|
||||
|
||||
public record Criteria(LocalizedString title, @Nullable Flag flag, List<Requirement> requirements)
|
||||
{
|
||||
public enum Flag {OPPOSITION, REFLECTION}
|
||||
|
||||
public Criteria {
|
||||
Objects.requireNonNull(title, "Title 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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 Throwable {
|
||||
ansv7779 marked this conversation as resolved
Outdated
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import com.google.inject.persist.Transactional;
|
|||
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.Language;
|
||||
|
@ -237,4 +238,37 @@ public class GradingReportServiceImpl implements GradingReportTemplateService, G
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ public class GradingReportTemplate extends DomainObject {
|
|||
return gradingCriterionTemplate;
|
||||
}
|
||||
|
||||
public Iterable<GradingCriterionTemplate> getCriteria() {
|
||||
public Collection<GradingCriterionTemplate> getCriteria() {
|
||||
return criteria;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package se.su.dsv.scipro.report;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import se.su.dsv.scipro.grading.GradingReportTemplateUpdate;
|
||||
import se.su.dsv.scipro.project.Project;
|
||||
import se.su.dsv.scipro.system.ProjectType;
|
||||
|
||||
|
@ -15,4 +16,8 @@ public interface GradingReportTemplateRepo extends JpaRepository<GradingReportTe
|
|||
GradingReportTemplate getNextTemplate(GradingReportTemplate gradingReportTemplate);
|
||||
|
||||
List<GradingReportTemplate> getTemplatesValidAfter(ProjectType projectType, LocalDate date);
|
||||
|
||||
GradingReportTemplate getTemplateById(long templateId);
|
||||
|
||||
GradingReportTemplate updateTemplate(long templateId, GradingReportTemplateUpdate update);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
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;
|
||||
|
||||
|
@ -53,4 +55,16 @@ public class GradingReportTemplateRepoImpl extends GenericRepo<GradingReportTemp
|
|||
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 gradingReportTemplate;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package se.su.dsv.scipro.report;
|
||||
|
||||
public class NoSuchTemplateException extends Exception {
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
<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">
|
||||
|
|
|
@ -1,15 +1,29 @@
|
|||
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;
|
||||
|
@ -23,10 +37,27 @@ public class AdminGradingTemplateEditPage extends AbstractAdminProjectPage imple
|
|||
|
||||
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));
|
||||
}
|
||||
System.out.println(editingGradingTemplate);
|
||||
}
|
||||
};
|
||||
|
@ -35,6 +66,58 @@ public class AdminGradingTemplateEditPage extends AbstractAdminProjectPage imple
|
|||
add(form);
|
||||
}
|
||||
|
||||
private GradingReportTemplateUpdate toUpdate(EditingGradingTemplate editingGradingTemplate) {
|
||||
List<GradingReportTemplateUpdate.Grade> grades = editingGradingTemplate
|
||||
.getGradeLimits()
|
||||
.getGradeLimits()
|
||||
.stream()
|
||||
.map(this::toGrade)
|
||||
.toList();
|
||||
List<GradingReportTemplateUpdate.Criteria> criteria = editingGradingTemplate
|
||||
.getCriteria()
|
||||
.stream()
|
||||
.map(this::toCriteria)
|
||||
.toList();
|
||||
return new GradingReportTemplateUpdate(
|
||||
editingGradingTemplate.getValidFrom(),
|
||||
editingGradingTemplate.getNote(),
|
||||
editingGradingTemplate.getGradeLimits().getDefaultGrade(),
|
||||
grades,
|
||||
criteria);
|
||||
}
|
||||
|
||||
private 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()),
|
||||
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 GradingReportTemplateUpdate.Grade toGrade(GradeLimits.GradeLimit gradeLimit) {
|
||||
return new GradingReportTemplateUpdate.Grade(
|
||||
gradeLimit.getGrade(),
|
||||
gradeLimit.getLowerLimit());
|
||||
}
|
||||
|
||||
public static PageParameters getPageParameters(GradingReportTemplate gradingReportTemplate) {
|
||||
PageParameters pageParameters = new PageParameters();
|
||||
pageParameters.add(GRADING_REPORT_TEMPLATE_ID_PARAMETER, gradingReportTemplate.getId());
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
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}.
|
Loading…
Reference in New Issue
Block a user
Should extend Exception instead of Throwable