Allows admins to manage grading report templates #14

Merged
niat8586 merged 41 commits from 3482-new-grading-criteria into develop 2024-10-30 10:05:23 +01:00
12 changed files with 168 additions and 14 deletions
Showing only changes of commit b0f6a81a23 - Show all commits

View File

@ -33,4 +33,9 @@ public interface GradingReportTemplateService {
NoSuchTemplateException,
DuplicateDateException,
TemplateLockedException;
GradingReportTemplate create(ProjectType projectType, GradingReportTemplateUpdate update)
throws
ValidDateMustBeInTheFutureException,
DuplicateDateException;
}

View File

@ -269,4 +269,25 @@ public class GradingReportServiceImpl implements GradingReportTemplateService, G
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);
}
}

View File

@ -20,4 +20,6 @@ public interface GradingReportTemplateRepo extends JpaRepository<GradingReportTe
GradingReportTemplate getTemplateById(long templateId);
GradingReportTemplate updateTemplate(long templateId, GradingReportTemplateUpdate update);
GradingReportTemplate createTemplate(ProjectType projectType, GradingReportTemplateUpdate update);
}

View File

@ -65,6 +65,13 @@ public class GradingReportTemplateRepoImpl extends GenericRepo<GradingReportTemp
@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());
@ -111,6 +118,13 @@ public class GradingReportTemplateRepoImpl extends GenericRepo<GradingReportTemp
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())

View File

@ -14,6 +14,7 @@ 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;
@ -288,6 +289,7 @@ public class SciProApplication extends LifecycleManagedWebApplication {
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);

View File

@ -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>

View File

@ -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);
}
});
}
}

View File

@ -66,17 +66,17 @@ public class AdminGradingTemplateEditPage extends AbstractAdminProjectPage imple
add(form);
}
private GradingReportTemplateUpdate toUpdate(EditingGradingTemplate editingGradingTemplate) {
public static GradingReportTemplateUpdate toUpdate(EditingGradingTemplate editingGradingTemplate) {
List<GradingReportTemplateUpdate.GradeLimit> gradeLimits = editingGradingTemplate
.getGradeLimits()
.getGradeLimits()
.stream()
.map(this::toGrade)
.map(AdminGradingTemplateEditPage::toGrade)
.toList();
List<GradingReportTemplateUpdate.Criteria> criteria = editingGradingTemplate
.getCriteria()
.stream()
.map(this::toCriteria)
.map(AdminGradingTemplateEditPage::toCriteria)
.toList();
return new GradingReportTemplateUpdate(
editingGradingTemplate.getValidFrom(),
@ -86,7 +86,7 @@ public class AdminGradingTemplateEditPage extends AbstractAdminProjectPage imple
criteria);
}
private GradingReportTemplateUpdate.Criteria toCriteria(EditingGradingTemplate.Criteria 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++) {
@ -117,7 +117,7 @@ public class AdminGradingTemplateEditPage extends AbstractAdminProjectPage imple
};
}
private GradingReportTemplateUpdate.GradeLimit toGrade(GradeLimits.GradeLimit gradeLimit) {
private static GradingReportTemplateUpdate.GradeLimit toGrade(GradeLimits.GradeLimit gradeLimit) {
return new GradingReportTemplateUpdate.GradeLimit(
gradeLimit.getGrade(),
gradeLimit.getLowerLimit());

View File

@ -32,15 +32,9 @@
</p>
<div class="btn-group mb-3" role="group">
<button type="button" class="btn btn-outline-primary">Create new template</button>
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
</button>
<ul class="dropdown-menu">
<li><button class="dropdown-item" href="#">Create new based on another template</button></li>
</ul>
</div>
<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">

View File

@ -15,6 +15,11 @@ class EditingGradingTemplate implements Serializable {
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();

View File

@ -10,6 +10,10 @@ 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()) {

View File

@ -2,3 +2,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}.
template_created=New template for ${name} created.