Allows admins to manage grading report templates #14
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
package se.su.dsv.scipro.grading;
|
||||
|
||||
import se.su.dsv.scipro.report.GradingReportTemplate;
|
||||
import se.su.dsv.scipro.system.ProjectType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface GradingReportTemplateService {
|
||||
List<ProjectType> getProjectTypes();
|
||||
|
||||
GradingReportTemplate getCurrentTemplate(ProjectType projectType);
|
||||
}
|
@ -4,18 +4,23 @@ import com.google.common.eventbus.EventBus;
|
||||
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.ThesisSubmissionHistoryService;
|
||||
import se.su.dsv.scipro.project.Project;
|
||||
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 java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.util.*;
|
||||
|
||||
public class GradingReportServiceImpl 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";
|
||||
@ -24,6 +29,7 @@ public class GradingReportServiceImpl implements GradingReportService {
|
||||
private final Clock clock;
|
||||
private final SupervisorGradingReportRepository supervisorGradingReportRepository;
|
||||
private final GradingReportTemplateRepo gradingReportTemplateRepo;
|
||||
private final ProjectTypeService projectTypeService;
|
||||
|
||||
@Inject
|
||||
public GradingReportServiceImpl(
|
||||
@ -31,13 +37,15 @@ public class GradingReportServiceImpl implements GradingReportService {
|
||||
ThesisSubmissionHistoryService thesisSubmissionHistoryService,
|
||||
Clock clock,
|
||||
SupervisorGradingReportRepository supervisorGradingReportRepository,
|
||||
GradingReportTemplateRepo gradingReportTemplateRepo)
|
||||
GradingReportTemplateRepo gradingReportTemplateRepo,
|
||||
ProjectTypeService projectTypeService)
|
||||
{
|
||||
this.eventBus = eventBus;
|
||||
this.thesisSubmissionHistoryService = thesisSubmissionHistoryService;
|
||||
this.clock = clock;
|
||||
this.supervisorGradingReportRepository = supervisorGradingReportRepository;
|
||||
this.gradingReportTemplateRepo = gradingReportTemplateRepo;
|
||||
this.projectTypeService = projectTypeService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -194,4 +202,14 @@ public class GradingReportServiceImpl implements GradingReportService {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +76,14 @@ public class GradingReportTemplate extends DomainObject {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public LocalDate getValidFrom() {
|
||||
return validFrom;
|
||||
}
|
||||
|
||||
public void setValidFrom(LocalDate validFrom) {
|
||||
this.validFrom = validFrom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (o == this) return true;
|
||||
|
@ -2,7 +2,12 @@ package se.su.dsv.scipro.report;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import se.su.dsv.scipro.project.Project;
|
||||
import se.su.dsv.scipro.system.ProjectType;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public interface GradingReportTemplateRepo extends JpaRepository<GradingReportTemplate, Long> {
|
||||
GradingReportTemplate getTemplate(Project project);
|
||||
|
||||
GradingReportTemplate getCurrentTemplate(ProjectType projectType, LocalDate now);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import se.su.dsv.scipro.system.GenericRepo;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Provider;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import se.su.dsv.scipro.system.ProjectType;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@ -19,13 +20,18 @@ public class GradingReportTemplateRepoImpl extends GenericRepo<GradingReportTemp
|
||||
|
||||
@Override
|
||||
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(project.getProjectType())
|
||||
.and(template.validFrom.loe(project.getStartDate())));
|
||||
return findOne(template.projectType.eq(project.getProjectType()).and(template.validFrom.eq(validFrom)));
|
||||
.where(template.projectType.eq(projectType)
|
||||
.and(template.validFrom.loe(now)));
|
||||
return findOne(template.projectType.eq(projectType).and(template.validFrom.eq(validFrom)));
|
||||
}
|
||||
}
|
@ -14,6 +14,8 @@ 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.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 +284,8 @@ 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/edit", AdminEditProjectPage.class);
|
||||
mountPage("admin/finalseminars", AdminFinalSeminarPage.class);
|
||||
mountPage("admin/finalseminars/exemptions", AdminFinalSeminarExemptionPage.class);
|
||||
|
@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,19 @@
|
||||
package se.su.dsv.scipro.admin.pages.grading;
|
||||
|
||||
import org.apache.wicket.request.mapper.parameter.PageParameters;
|
||||
import se.su.dsv.scipro.admin.pages.AbstractAdminProjectPage;
|
||||
import se.su.dsv.scipro.report.GradingReportTemplate;
|
||||
|
||||
public class AdminGradingTemplatePage extends AbstractAdminProjectPage implements MenuHighlightGradingTemplates {
|
||||
private static final String TEMPLATE_ID_PARAMETER = "id";
|
||||
|
||||
public AdminGradingTemplatePage(PageParameters parameters) {
|
||||
long templateId = parameters.get(TEMPLATE_ID_PARAMETER).toLong();
|
||||
}
|
||||
|
||||
public static PageParameters getPageParameters(GradingReportTemplate gradingReportTemplate) {
|
||||
PageParameters pageParameters = new PageParameters();
|
||||
pageParameters.add(TEMPLATE_ID_PARAMETER, gradingReportTemplate.getId());
|
||||
return pageParameters;
|
||||
}
|
||||
}
|
@ -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>
|
||||
<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">
|
||||
<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>
|
||||
</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">
|
||||
<div class="card-header bg-info-subtle">
|
||||
Upcoming template
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl>
|
||||
<dt>Valid from</dt>
|
||||
<dd>2024-10-31</dd>
|
||||
|
||||
<dt>Note</dt>
|
||||
<dd>Added a new criteria U14</dd>
|
||||
</dl>
|
||||
<span class="card-text">
|
||||
<a href="#">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>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</wicket:extend>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,48 @@
|
||||
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 org.apache.wicket.model.Model;
|
||||
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", Model.of("")));
|
||||
add(new BookmarkablePageLink<>("view_template", AdminGradingTemplatePage.class, AdminGradingTemplatePage.getPageParameters(currentTemplate.getObject())));
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user