Squash and fix migrations so they run against an empty schema #24

Merged
tozh4728 merged 3 commits from empty-database-migration into develop 2024-12-02 10:31:21 +01:00
9 changed files with 181 additions and 19 deletions
Showing only changes of commit 6adf26e9fa - Show all commits

View File

@ -105,11 +105,11 @@ public class Idea extends DomainObject {
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "literature", column = @Column(name = "literature", insertable = false, updatable = false)),
@AttributeOverride(name = "background", column = @Column(name = "background", insertable = false, updatable = false)),
@AttributeOverride(name = "problem", column = @Column(name = "problem", insertable = false, updatable = false)),
@AttributeOverride(name = "method", column = @Column(name = "method", insertable = false, updatable = false)),
@AttributeOverride(name = "interests", column = @Column(name = "interests", insertable = false, updatable = false))
@AttributeOverride(name = "literature", column = @Column(name = "literature")),
@AttributeOverride(name = "background", column = @Column(name = "background")),
@AttributeOverride(name = "problem", column = @Column(name = "problem")),
@AttributeOverride(name = "method", column = @Column(name = "method")),
@AttributeOverride(name = "interests", column = @Column(name = "interests"))
})
private TholanderBox tholanderBox = new TholanderBox();

View File

@ -17,7 +17,7 @@
<div wicket:id="grading_template_component_panel"></div>
<div class="position-sticky bottom-0 bg-white p-3 border line-length-limit">
<div wicket:id="button_container" class="position-sticky bottom-0 bg-white p-3 border line-length-limit">
<button type="submit" class="btn btn-primary">Create</button>
</div>
</form>

View File

@ -3,6 +3,7 @@ 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.WebMarkupContainer;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.LambdaChoiceRenderer;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
@ -29,6 +30,7 @@ public class AdminGradingTemplateCreationPage extends AbstractAdminProjectPage i
private final IModel<ProjectType> projectTypeModel;
private EditingGradingTemplate editingGradingTemplateModel;
private final WebMarkupContainer buttonContainer;
public AdminGradingTemplateCreationPage() {
projectTypeModel = new DetachableServiceModel<>(projectTypeService);
@ -57,6 +59,11 @@ public class AdminGradingTemplateCreationPage extends AbstractAdminProjectPage i
form.setOutputMarkupId(true);
add(form);
buttonContainer = new WebMarkupContainer("button_container");
buttonContainer.setOutputMarkupPlaceholderTag(true);
buttonContainer.setVisible(false);
form.add(buttonContainer);
form.add(new AjaxDropDownChoice<>(
"project_type",
projectTypeModel,
@ -64,7 +71,8 @@ public class AdminGradingTemplateCreationPage extends AbstractAdminProjectPage i
new LambdaChoiceRenderer<>(ProjectType::getName, ProjectType::getId)) {
@Override
public void onNewSelection(AjaxRequestTarget target, ProjectType objectSelected) {
target.add(form);
buttonContainer.setVisible(true);
target.add(form, buttonContainer);
}
});

View File

@ -3,10 +3,18 @@
<body>
<wicket:extend>
<div wicket:id="feedback"></div>
<div class="mb-3 lead">
<wicket:message key="project_type_name_editing">
<span wicket:id="project_type_name"></span>
</wicket:message>
</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">
<div class="position-sticky bottom-0 bg-white p-3 border line-length-limit hstack">
<button type="submit" class="btn btn-primary">Save</button>
<span wicket:id="unsaved_changes_alert" class="text-danger flex-grow-1 text-center" role="alert">
<wicket:message key="unsaved_changes"/>
</span>
</div>
</form>
</wicket:extend>

View File

@ -2,9 +2,13 @@ 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.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
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.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;
@ -12,9 +16,10 @@ 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 se.su.dsv.scipro.report.ValidDateMustBeInTheFutureException;
import se.su.dsv.scipro.system.ProjectType;
import java.util.ArrayList;
import java.util.List;
@ -25,6 +30,8 @@ public class AdminGradingTemplateEditPage extends AbstractAdminProjectPage imple
0,
new LocalizedString("", ""));
private final WebMarkupContainer unsavedChangesAlert;
@Inject
GradingReportTemplateService gradingReportTemplateService;
@ -38,6 +45,8 @@ public class AdminGradingTemplateEditPage extends AbstractAdminProjectPage imple
editingGradingTemplate = new EditingGradingTemplate(template);
add(new FeedbackPanel("feedback"));
IModel<GradingReportTemplate> model = LoadableDetachableModel.of(() -> gradingReportTemplateService.getTemplate(id));
add(new Label("project_type_name", model.map(GradingReportTemplate::getProjectType).map(ProjectType::getName)));
Form<EditingGradingTemplate> form = new Form<>("form") {
@Override
@ -47,7 +56,8 @@ public class AdminGradingTemplateEditPage extends AbstractAdminProjectPage imple
GradingReportTemplateUpdate update = toUpdate(
editingGradingTemplate);
gradingReportTemplateService.update(id, update);
GradingReportTemplate newTemplate = gradingReportTemplateService.update(id, update);
editingGradingTemplate = new EditingGradingTemplate(newTemplate);
success(getString("template_updated"));
} catch (ValidDateMustBeInTheFutureException e) {
error(getString("valid_from_must_be_in_the_future", () -> e));
@ -61,7 +71,23 @@ public class AdminGradingTemplateEditPage extends AbstractAdminProjectPage imple
}
};
form.add(new EditingGradingTemplateComponentPanel("editing", Model.of(editingGradingTemplate)));
unsavedChangesAlert = new WebMarkupContainer("unsaved_changes_alert") {
@Override
protected void onConfigure() {
super.onConfigure();
setVisible(editingGradingTemplate.hasChanges());
}
};
form.add(unsavedChangesAlert);
unsavedChangesAlert.setOutputMarkupPlaceholderTag(true);
form.add(new EditingGradingTemplateComponentPanel("editing", () -> editingGradingTemplate) {
@Override
protected void onTemplateChanged(AjaxRequestTarget target) {
super.onTemplateChanged(target);
target.add(unsavedChangesAlert);
}
});
add(form);
}

View File

@ -8,12 +8,15 @@ import java.io.Serializable;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
class EditingGradingTemplate implements Serializable {
private EditingGradingTemplate original;
private String note;
private LocalDate validFrom;
private List<Criteria> criteria;
private GradeLimits gradeLimits;
private String projectType;
public EditingGradingTemplate() {
this.gradeLimits = new GradeLimits();
@ -21,6 +24,16 @@ class EditingGradingTemplate implements Serializable {
}
EditingGradingTemplate(GradingReportTemplate template) {
this(template, null);
this.original = new EditingGradingTemplate(template, null);
}
/**
* Private constructor for creating a new instance of EditingGradingTemplate
* to be able to track changes made.
* @param doNotCreateOriginal Only exists to differentiate the signature from the public constructor
*/
private EditingGradingTemplate(GradingReportTemplate template, @SuppressWarnings("unused") Void doNotCreateOriginal) {
this.note = template.getNote();
this.validFrom = template.getValidFrom();
this.gradeLimits = new GradeLimits(template);
@ -29,6 +42,7 @@ class EditingGradingTemplate implements Serializable {
Criteria editingCriteria = new Criteria(criteria);
this.criteria.add(editingCriteria);
}
this.projectType = template.getProjectType().getName();
}
public String getNote() {
@ -61,8 +75,34 @@ class EditingGradingTemplate implements Serializable {
.sum();
}
public Boolean hasChanges() {
return !Objects.equals(original, this);
}
public void addCriteria() {
this.criteria.add(new Criteria());
Criteria newCriteria = new Criteria();
newCriteria.points.add(newCriteria.new Point());
this.criteria.add(newCriteria);
}
public String getProjectType() {
return projectType;
}
@Override
public boolean equals(Object o) {
return o instanceof EditingGradingTemplate that
&& Objects.equals(note, that.note)
&& Objects.equals(validFrom, that.validFrom)
&& Objects.equals(criteria, that.criteria)
&& Objects.equals(gradeLimits, that.gradeLimits)
&& Objects.equals(projectType, that.projectType);
}
@Override
public int hashCode() {
return Objects.hash(note, validFrom, criteria, gradeLimits);
}
class Criteria implements Serializable {
@ -79,14 +119,14 @@ class EditingGradingTemplate implements Serializable {
private List<Point> points = new ArrayList<>();
private Flag flag;
private Type type = Type.PROJECT;
private int pointsRequiredToPass;
private int pointsRequiredToPass = 1;
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;
if (point.getPoint() == 0) continue; // This is to hide zero point requirements that never have any text
Point editingPoint = new Point(point);
this.points.add(editingPoint);
}
@ -150,6 +190,22 @@ class EditingGradingTemplate implements Serializable {
this.pointsRequiredToPass = pointsRequiredToPass;
}
@Override
public boolean equals(Object o) {
return o instanceof Criteria criterion
&& pointsRequiredToPass == criterion.pointsRequiredToPass
&& Objects.equals(titleSv, criterion.titleSv)
&& Objects.equals(titleEn, criterion.titleEn)
&& Objects.equals(points, criterion.points)
&& flag == criterion.flag
&& type == criterion.type;
}
@Override
public int hashCode() {
return Objects.hash(titleSv, titleEn, points, flag, type, pointsRequiredToPass);
}
class Point implements Serializable {
private String requirementEn;
private String requirementSv;
@ -179,6 +235,18 @@ class EditingGradingTemplate implements Serializable {
public void setRequirementSv(String requirementSv) {
this.requirementSv = requirementSv;
}
@Override
public boolean equals(Object o) {
return o instanceof Point point
&& Objects.equals(requirementEn, point.requirementEn)
&& Objects.equals(requirementSv, point.requirementSv);
}
@Override
public int hashCode() {
return Objects.hash(requirementEn, requirementSv);
}
}
}
}

View File

@ -22,6 +22,9 @@ import java.time.LocalDate;
import java.util.List;
class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTemplate> {
private final Label maxPointsAvailable;
EditingGradingTemplateComponentPanel(
String id,
IModel<EditingGradingTemplate> editingGradingTemplateModel)
@ -36,7 +39,7 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
EditingGradingTemplate::setValidFrom),
LocalDate.class);
validFromField.add(new BootstrapDatePicker());
validFromField.add(new AutoSave());
validFromField.add(new AutoSave("changeDate"));
add(validFromField);
add(new TextArea<>("note", LambdaModel.of(
@ -50,7 +53,10 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
add(new GradeLimitsPanel("grade_limits", editingGradingTemplateModel.map(EditingGradingTemplate::getGradeLimits)));
add(new Label("max_points_available", editingGradingTemplateModel.map(EditingGradingTemplate::getMaxPointsAvailable)));
maxPointsAvailable = new Label("max_points_available", editingGradingTemplateModel.map(EditingGradingTemplate::getMaxPointsAvailable));
maxPointsAvailable.setOutputMarkupId(true);
add(maxPointsAvailable);
add(new ListView<>("criteria", editingGradingTemplateModel.map(EditingGradingTemplate::getCriteria)) {
{
@ -64,6 +70,7 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
public void onClick(AjaxRequestTarget target) {
editingGradingTemplateModel.getObject().getCriteria().remove(item.getModelObject());
target.add(EditingGradingTemplateComponentPanel.this);
onTemplateChanged(target);
}
});
item.add(new CriteriaEditingPanel("criteria", item.getModel()));
@ -75,6 +82,7 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
public void onClick(AjaxRequestTarget target) {
editingGradingTemplateModel.getObject().addCriteria();
target.add(EditingGradingTemplateComponentPanel.this);
onTemplateChanged(target);
}
});
}
@ -117,6 +125,7 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
EditingGradingTemplate.Criteria.Type objectSelected)
{
// auto save
onTemplateChanged(target);
}
};
typeChoice.setRequired(true);
@ -149,6 +158,7 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
EditingGradingTemplate.Criteria.Flag objectSelected)
{
// auto save
onTemplateChanged(target);
}
};
flagChoice.setNullValid(true);
@ -176,6 +186,8 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
EditingGradingTemplate.Criteria.Point newPoint = criteria.new Point();
criteria.getPoints().add(newPoint);
target.add(CriteriaEditingPanel.this);
target.add(maxPointsAvailable);
onTemplateChanged(target);
}
});
}
@ -210,13 +222,14 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
EditingGradingTemplate.Criteria criteria = CriteriaEditingPanel.this.getModelObject();
criteria.getPoints().remove(model.getObject());
target.add(CriteriaEditingPanel.this);
onTemplateChanged(target);
}
});
}
}
}
private static class GradeLimitsPanel extends GenericWebMarkupContainer<GradeLimits> {
private class GradeLimitsPanel extends GenericWebMarkupContainer<GradeLimits> {
public GradeLimitsPanel(String id, IModel<GradeLimits> model) {
super(id, model);
@ -248,6 +261,7 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
GradeLimits gradeLimits = GradeLimitsPanel.this.getModelObject();
gradeLimits.addNewLimit();
target.add(GradeLimitsPanel.this);
onTemplateChanged(target);
}
});
}
@ -278,20 +292,30 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
GradeLimits gradeLimits = GradeLimitsPanel.this.getModelObject();
gradeLimits.getGradeLimits().remove(model.getObject());
target.add(GradeLimitsPanel.this);
onTemplateChanged(target);
}
});
}
}
}
private static class AutoSave extends AjaxFormComponentUpdatingBehavior {
private class AutoSave extends AjaxFormComponentUpdatingBehavior {
public AutoSave() {
super("input");
}
public AutoSave(String event) {
super(event);
}
@Override
protected void onUpdate(AjaxRequestTarget target) {
// just trigger the ajax call is enough to update the model object
onTemplateChanged(target);
}
}
protected void onTemplateChanged(AjaxRequestTarget target) {
// do nothing
}
}

View File

@ -5,6 +5,7 @@ import se.su.dsv.scipro.report.GradingReportTemplate;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
class GradeLimits implements Serializable {
private List<GradeLimit> gradeLimits;
@ -41,6 +42,19 @@ class GradeLimits implements Serializable {
return gradeLimits;
}
@Override
public boolean equals(Object o) {
return o instanceof GradeLimits that
&& Objects.equals(gradeLimits, that.gradeLimits)
&& Objects.equals(failingGrade, that.failingGrade);
}
@Override
public int hashCode() {
return Objects.hash(gradeLimits, failingGrade);
}
class GradeLimit implements Serializable {
private String grade;
private int lowerLimit;
@ -60,5 +74,17 @@ class GradeLimits implements Serializable {
public void setLowerLimit(int lowerLimit) {
this.lowerLimit = lowerLimit;
}
@Override
public boolean equals(Object o) {
return o instanceof GradeLimit that
&& lowerLimit == that.lowerLimit
&& Objects.equals(grade, that.grade);
}
@Override
public int hashCode() {
return Objects.hash(grade, lowerLimit);
}
}
}

View File

@ -3,3 +3,5 @@ valid_from_must_be_in_the_future=The templates valid date must be in the future.
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.
unsaved_changes=The grading template has been changed, unsaved changes will be lost if you do not save.
project_type_name_editing=You are editing a ${project_type_name} grading template.