Allow authors to re-submit their reflection on supervisor's request #12
@ -20,7 +20,7 @@ public class ProjectEvent extends NotificationEvent {
|
|||||||
ROUGH_DRAFT_APPROVAL_APPROVED, ROUGH_DRAFT_APPROVAL_REJECTED, REVIEWER_GRADING_REPORT_SUBMITTED,
|
ROUGH_DRAFT_APPROVAL_APPROVED, ROUGH_DRAFT_APPROVAL_REJECTED, REVIEWER_GRADING_REPORT_SUBMITTED,
|
||||||
ONE_YEAR_PASSED_FROM_LATEST_ANNUAL_REVIEW, SUPERVISOR_GRADING_INITIAL_ASSESSMENT_DONE,
|
ONE_YEAR_PASSED_FROM_LATEST_ANNUAL_REVIEW, SUPERVISOR_GRADING_INITIAL_ASSESSMENT_DONE,
|
||||||
EXPORTED_SUCCESS, REVIEWER_GRADING_INITIAL_ASSESSMENT_DONE, FIRST_MEETING, OPPOSITION_FAILED, PARTICIPATION_APPROVED, COMPLETED,
|
EXPORTED_SUCCESS, REVIEWER_GRADING_INITIAL_ASSESSMENT_DONE, FIRST_MEETING, OPPOSITION_FAILED, PARTICIPATION_APPROVED, COMPLETED,
|
||||||
PARTICIPATION_FAILED
|
REFLECTION_IMPROVEMENTS_REQUESTED, PARTICIPATION_FAILED
|
||||||
}
|
}
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
|
@ -85,6 +85,10 @@ PROJECT.PARTICIPATION_APPROVED.body = Your active participation on {0} has been
|
|||||||
PROJECT.PARTICIPATION_FAILED.title = Your active participation on {1} did not meet the minimum requirements.
|
PROJECT.PARTICIPATION_FAILED.title = Your active participation on {1} did not meet the minimum requirements.
|
||||||
PROJECT.PARTICIPATION_FAILED.body = Your active participation did not meet the minimum requirements set, and you will \
|
PROJECT.PARTICIPATION_FAILED.body = Your active participation did not meet the minimum requirements set, and you will \
|
||||||
have to be an active participant on a different final seminar to pass this step.
|
have to be an active participant on a different final seminar to pass this step.
|
||||||
|
PROJECT.REFLECTION_IMPROVEMENTS_REQUESTED.title = Reflection improvements requested
|
||||||
|
PROJECT.REFLECTION_IMPROVEMENTS_REQUESTED.body = The supervisor has deemed that the reflection submitted does not meet \
|
||||||
|
the minimum requirements and has requested improvements. Please log into SciPro and submit a new reflection. \
|
||||||
|
Their comments can be seen below:\n\n{0}
|
||||||
|
|
||||||
FORUM.NEW_FORUM_POST.title = Forum post: {2}
|
FORUM.NEW_FORUM_POST.title = Forum post: {2}
|
||||||
FORUM.NEW_FORUM_POST.body = New forum post submitted:<br /><br />{0}
|
FORUM.NEW_FORUM_POST.body = New forum post submitted:<br /><br />{0}
|
||||||
|
@ -39,6 +39,15 @@ public class Author {
|
|||||||
@Basic(optional = true)
|
@Basic(optional = true)
|
||||||
private String reflection;
|
private String reflection;
|
||||||
|
|
||||||
|
@Basic
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(name = "reflection_status")
|
||||||
|
private ReflectionStatus reflectionStatus = ReflectionStatus.NOT_SUBMITTED;
|
||||||
|
|
||||||
|
@Basic
|
||||||
|
@Column(name = "reflection_comment_by_supervisor")
|
||||||
|
private String reflectionSupervisorComment;
|
||||||
|
|
||||||
public Project getProject() {
|
public Project getProject() {
|
||||||
return project;
|
return project;
|
||||||
}
|
}
|
||||||
@ -63,6 +72,22 @@ public class Author {
|
|||||||
this.reflection = reflection;
|
this.reflection = reflection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ReflectionStatus getReflectionStatus() {
|
||||||
|
return reflectionStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReflectionStatus(ReflectionStatus reflectionStatus) {
|
||||||
|
this.reflectionStatus = reflectionStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReflectionSupervisorComment(String reflectionSupervisorComment) {
|
||||||
|
this.reflectionSupervisorComment = reflectionSupervisorComment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReflectionSupervisorComment() {
|
||||||
|
return reflectionSupervisorComment;
|
||||||
|
}
|
||||||
|
|
||||||
@Embeddable
|
@Embeddable
|
||||||
public static class AuthorPK implements Serializable {
|
public static class AuthorPK implements Serializable {
|
||||||
private Long projectId;
|
private Long projectId;
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
package se.su.dsv.scipro.project;
|
||||||
|
|
||||||
|
public enum ReflectionStatus {
|
||||||
|
NOT_SUBMITTED,
|
||||||
|
SUBMITTED,
|
||||||
|
IMPROVEMENTS_NEEDED
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package se.su.dsv.scipro.reflection;
|
||||||
|
|
||||||
|
public sealed interface Reflection {
|
||||||
|
boolean isSubmittable();
|
||||||
|
|
||||||
|
record NotSubmitted() implements Reflection {
|
||||||
|
@Override
|
||||||
|
public boolean isSubmittable() { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
record Submitted(String reflection) implements Reflection {
|
||||||
|
@Override
|
||||||
|
public boolean isSubmittable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record ImprovementsNeeded(String oldReflection, String commentBySupervisor) implements Reflection {
|
||||||
|
@Override
|
||||||
|
public boolean isSubmittable() { return true; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package se.su.dsv.scipro.reflection;
|
||||||
|
|
||||||
|
import se.su.dsv.scipro.project.Project;
|
||||||
|
import se.su.dsv.scipro.system.User;
|
||||||
|
|
||||||
|
public record ReflectionImprovementsRequestedEvent(Project project, User author, String supervisorComment) {
|
||||||
|
}
|
@ -14,4 +14,15 @@ public interface ReflectionService {
|
|||||||
* @return the reflection, or {@code null} if none has been submitted
|
* @return the reflection, or {@code null} if none has been submitted
|
||||||
*/
|
*/
|
||||||
String getSubmittedReflection(Project project, User author);
|
String getSubmittedReflection(Project project, User author);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by the supervisor when the currently submitted reflection does not meet the minimum requirements.
|
||||||
|
* This is done individually by author.
|
||||||
|
*
|
||||||
|
* @param author the author whose reflection does not meet the minimum requirements.
|
||||||
|
* @param supervisorComment feedback provided by the supervisor so the author knows what to improve.
|
||||||
|
*/
|
||||||
|
void requestNewReflection(Project project, User author, String supervisorComment);
|
||||||
|
|
||||||
|
Reflection getReflection(Project project, User author);
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,32 @@
|
|||||||
package se.su.dsv.scipro.reflection;
|
package se.su.dsv.scipro.reflection;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.EventBus;
|
||||||
import com.google.inject.persist.Transactional;
|
import com.google.inject.persist.Transactional;
|
||||||
import se.su.dsv.scipro.finalseminar.AuthorRepository;
|
import se.su.dsv.scipro.finalseminar.AuthorRepository;
|
||||||
import se.su.dsv.scipro.finalseminar.FinalSeminarService;
|
import se.su.dsv.scipro.finalseminar.FinalSeminarService;
|
||||||
import se.su.dsv.scipro.project.Author;
|
import se.su.dsv.scipro.project.Author;
|
||||||
import se.su.dsv.scipro.project.Project;
|
import se.su.dsv.scipro.project.Project;
|
||||||
|
import se.su.dsv.scipro.project.ReflectionStatus;
|
||||||
import se.su.dsv.scipro.system.User;
|
import se.su.dsv.scipro.system.User;
|
||||||
|
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
class ReflectionServiceImpl implements ReflectionService {
|
class ReflectionServiceImpl implements ReflectionService {
|
||||||
private final AuthorRepository authorRepository;
|
private final AuthorRepository authorRepository;
|
||||||
private final FinalSeminarService finalSeminarService;
|
private final FinalSeminarService finalSeminarService;
|
||||||
|
private final EventBus eventBus;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ReflectionServiceImpl(AuthorRepository authorRepository, FinalSeminarService finalSeminarService) {
|
ReflectionServiceImpl(
|
||||||
|
AuthorRepository authorRepository,
|
||||||
|
FinalSeminarService finalSeminarService,
|
||||||
|
EventBus eventBus)
|
||||||
|
{
|
||||||
this.authorRepository = authorRepository;
|
this.authorRepository = authorRepository;
|
||||||
this.finalSeminarService = finalSeminarService;
|
this.finalSeminarService = finalSeminarService;
|
||||||
|
this.eventBus = eventBus;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -25,10 +35,13 @@ class ReflectionServiceImpl implements ReflectionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasToFillInReflection(Project project, User author) {
|
public boolean hasToFillInReflection(Project project, User user) {
|
||||||
boolean noReflectionSubmitted = authorRepository.findByProjectAndUser(project, author)
|
Optional<Author> optionalAuthor = authorRepository.findByProjectAndUser(project, user);
|
||||||
.map(Author::getReflection)
|
if (optionalAuthor.isEmpty()) {
|
||||||
.isEmpty();
|
return false;
|
||||||
|
}
|
||||||
|
Author author = optionalAuthor.get();
|
||||||
|
boolean noReflectionSubmitted = author.getReflectionStatus() != ReflectionStatus.SUBMITTED;
|
||||||
return hasReachedReflectionProcess(project) && noReflectionSubmitted;
|
return hasReachedReflectionProcess(project) && noReflectionSubmitted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +49,10 @@ class ReflectionServiceImpl implements ReflectionService {
|
|||||||
@Transactional
|
@Transactional
|
||||||
public void submitReflection(Project project, User user, String reflection) {
|
public void submitReflection(Project project, User user, String reflection) {
|
||||||
authorRepository.findByProjectAndUser(project, user)
|
authorRepository.findByProjectAndUser(project, user)
|
||||||
.ifPresent(author -> author.setReflection(reflection));
|
.ifPresent(author -> {
|
||||||
|
author.setReflection(reflection);
|
||||||
|
author.setReflectionStatus(ReflectionStatus.SUBMITTED);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -45,4 +61,32 @@ class ReflectionServiceImpl implements ReflectionService {
|
|||||||
.map(Author::getReflection)
|
.map(Author::getReflection)
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void requestNewReflection(Project project, User user, String supervisorComment) {
|
||||||
|
authorRepository.findByProjectAndUser(project, user)
|
||||||
|
.ifPresent(author -> {
|
||||||
|
author.setReflectionStatus(ReflectionStatus.IMPROVEMENTS_NEEDED);
|
||||||
|
author.setReflectionSupervisorComment(supervisorComment);
|
||||||
|
});
|
||||||
|
eventBus.post(new ReflectionImprovementsRequestedEvent(project, user, supervisorComment));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Reflection getReflection(Project project, User author) {
|
||||||
|
return authorRepository.findByProjectAndUser(project, author)
|
||||||
|
.map(this::toReflection)
|
||||||
|
.orElseGet(Reflection.NotSubmitted::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Reflection toReflection(Author author) {
|
||||||
|
return switch (author.getReflectionStatus()) {
|
||||||
|
case SUBMITTED -> new Reflection.Submitted(author.getReflection());
|
||||||
|
case IMPROVEMENTS_NEEDED -> new Reflection.ImprovementsNeeded(
|
||||||
|
author.getReflection(),
|
||||||
|
author.getReflectionSupervisorComment());
|
||||||
|
default -> new Reflection.NotSubmitted();
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
ALTER TABLE `project_user`
|
||||||
|
ADD COLUMN `reflection_status` VARCHAR(32) NOT NULL DEFAULT 'NOT_SUBMITTED';
|
||||||
|
|
||||||
|
UPDATE `project_user` SET `reflection_status` = 'SUBMITTED' WHERE `reflection` IS NOT NULL;
|
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE `project_user` ADD COLUMN `reflection_comment_by_supervisor` TEXT NULL;
|
@ -101,6 +101,26 @@ public class ReflectionServiceTest extends IntegrationTest {
|
|||||||
assertTrue(reflectionService.hasReachedReflectionProcess(project));
|
assertTrue(reflectionService.hasReachedReflectionProcess(project));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void request_resubmission() {
|
||||||
|
LocalDate seminarDate = scheduleSeminar();
|
||||||
|
clock.setDate(seminarDate.plusDays(1));
|
||||||
|
assertTrue(reflectionService.hasToFillInReflection(project, author),
|
||||||
|
"After the final seminar the author should be required to submit a reflection");
|
||||||
|
|
||||||
|
String myReflection = "my reflection";
|
||||||
|
reflectionService.submitReflection(project, author, myReflection);
|
||||||
|
assertEquals(myReflection, reflectionService.getSubmittedReflection(project, author));
|
||||||
|
assertFalse(reflectionService.hasToFillInReflection(project, author),
|
||||||
|
"After submitting the initial reflection it should no longer be required");
|
||||||
|
|
||||||
|
reflectionService.requestNewReflection(project, author, "Very bad reflection");
|
||||||
|
assertTrue(reflectionService.hasToFillInReflection(project, author),
|
||||||
|
"After supervisor requests resubmission the author should now be required to submit a new reflection");
|
||||||
|
assertEquals(myReflection, reflectionService.getSubmittedReflection(project, author),
|
||||||
|
"The old reflection should be saved to make it easier for the student to update it");
|
||||||
|
}
|
||||||
|
|
||||||
private LocalDate scheduleSeminar() {
|
private LocalDate scheduleSeminar() {
|
||||||
project.setFinalSeminarRuleExempted(true); // to bypass rough draft approval
|
project.setFinalSeminarRuleExempted(true); // to bypass rough draft approval
|
||||||
FinalSeminarDetails details = new FinalSeminarDetails("Zoom", false, 1, 1, Language.SWEDISH, Language.ENGLISH, "zoom id 123");
|
FinalSeminarDetails details = new FinalSeminarDetails("Zoom", false, 1, 1, Language.SWEDISH, Language.ENGLISH, "zoom id 123");
|
||||||
|
@ -13,5 +13,6 @@ public class CrosscuttingModule extends AbstractModule {
|
|||||||
bind(ReviewerAssignedNotifications.class).asEagerSingleton();
|
bind(ReviewerAssignedNotifications.class).asEagerSingleton();
|
||||||
bind(ReviewerAssignedDeadline.class).asEagerSingleton();
|
bind(ReviewerAssignedDeadline.class).asEagerSingleton();
|
||||||
bind(ForwardPhase2Feedback.class).asEagerSingleton();
|
bind(ForwardPhase2Feedback.class).asEagerSingleton();
|
||||||
|
bind(NotifyFailedReflection.class).asEagerSingleton();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
package se.su.dsv.scipro.crosscutting;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.EventBus;
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import se.su.dsv.scipro.data.dataobjects.Member;
|
||||||
|
import se.su.dsv.scipro.notifications.NotificationController;
|
||||||
|
import se.su.dsv.scipro.notifications.dataobject.NotificationSource;
|
||||||
|
import se.su.dsv.scipro.notifications.dataobject.ProjectEvent;
|
||||||
|
import se.su.dsv.scipro.reflection.ReflectionImprovementsRequestedEvent;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class NotifyFailedReflection {
|
||||||
|
private final NotificationController notificationController;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public NotifyFailedReflection(NotificationController notificationController, EventBus eventBus) {
|
||||||
|
this.notificationController = notificationController;
|
||||||
|
eventBus.register(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void reflectionImprovementsRequested(ReflectionImprovementsRequestedEvent event) {
|
||||||
|
NotificationSource source = new NotificationSource();
|
||||||
|
source.setMessage(event.supervisorComment());
|
||||||
|
notificationController.notifyCustomProject(
|
||||||
|
event.project(),
|
||||||
|
ProjectEvent.Event.REFLECTION_IMPROVEMENTS_REQUESTED,
|
||||||
|
source,
|
||||||
|
Set.of(new Member(event.author(), Member.Type.AUTHOR)));
|
||||||
|
}
|
||||||
|
}
|
@ -34,6 +34,7 @@ import se.su.dsv.scipro.reflection.ReflectionService;
|
|||||||
import se.su.dsv.scipro.report.AbstractGradingCriterion;
|
import se.su.dsv.scipro.report.AbstractGradingCriterion;
|
||||||
import se.su.dsv.scipro.report.GradingCriterion;
|
import se.su.dsv.scipro.report.GradingCriterion;
|
||||||
import se.su.dsv.scipro.report.GradingCriterionPoint;
|
import se.su.dsv.scipro.report.GradingCriterionPoint;
|
||||||
|
import se.su.dsv.scipro.report.GradingReport;
|
||||||
import se.su.dsv.scipro.report.SupervisorGradingReport;
|
import se.su.dsv.scipro.report.SupervisorGradingReport;
|
||||||
import se.su.dsv.scipro.system.Language;
|
import se.su.dsv.scipro.system.Language;
|
||||||
import se.su.dsv.scipro.system.User;
|
import se.su.dsv.scipro.system.User;
|
||||||
@ -271,7 +272,10 @@ public class CriteriaPanel extends GenericPanel<SupervisorGradingReport> {
|
|||||||
|
|
||||||
modal = new LargeModalWindow("modal");
|
modal = new LargeModalWindow("modal");
|
||||||
modal.setTitle("Reflection");
|
modal.setTitle("Reflection");
|
||||||
modal.setContent(id_ -> new MultiLineLabel(id_, new NullReplacementModel(reflection, "No reflection filled in.")));
|
modal.setContent(modalBodyId -> {
|
||||||
|
IModel<Project> projectModel = CriteriaPanel.this.getModel().map(GradingReport::getProject);
|
||||||
|
return new ReflectionModalBodyPanel(modalBodyId, projectModel, author);
|
||||||
|
});
|
||||||
add(modal);
|
add(modal);
|
||||||
|
|
||||||
WebMarkupContainer showReflection = new WebMarkupContainer("showReflection") {
|
WebMarkupContainer showReflection = new WebMarkupContainer("showReflection") {
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
<?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>
|
||||||
|
<wicket:enclosure>
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<h4 class="alert-heading">
|
||||||
|
<wicket:message key="improvements_requested">
|
||||||
|
You've requested improvements to the submitted version.
|
||||||
|
</wicket:message>
|
||||||
|
</h4>
|
||||||
|
<wicket:container class="mb-0" wicket:id="improvements_needed_supervisor_feedback">
|
||||||
|
[Supervisor feedback on needed improvements]
|
||||||
|
</wicket:container>
|
||||||
|
</div>
|
||||||
|
</wicket:enclosure>
|
||||||
|
|
||||||
|
<wicket:container wicket:id="reflection_text">
|
||||||
|
[Authors submitted reflection]
|
||||||
|
</wicket:container>
|
||||||
|
|
||||||
|
<form wicket:id="request_improvements_form">
|
||||||
|
<hr>
|
||||||
|
<p>
|
||||||
|
<wicket:message key="request_improvements_text">
|
||||||
|
Please provide feedback on what improvements are needed in the submitted version.
|
||||||
|
</wicket:message>
|
||||||
|
</p>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" wicket:for="comment">
|
||||||
|
<wicket:message key="comment">
|
||||||
|
Comment
|
||||||
|
</wicket:message>
|
||||||
|
</label>
|
||||||
|
<textarea class="form-control" wicket:id="comment" rows="5"></textarea>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary" wicket:id="submit">
|
||||||
|
<wicket:message key="request_improvements">
|
||||||
|
Request improvements
|
||||||
|
</wicket:message>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</wicket:panel>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,102 @@
|
|||||||
|
package se.su.dsv.scipro.grading;
|
||||||
|
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import org.apache.wicket.ajax.AjaxRequestTarget;
|
||||||
|
import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink;
|
||||||
|
import org.apache.wicket.markup.html.basic.Label;
|
||||||
|
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.Panel;
|
||||||
|
import org.apache.wicket.model.IModel;
|
||||||
|
import org.apache.wicket.model.Model;
|
||||||
|
import se.su.dsv.scipro.project.Project;
|
||||||
|
import se.su.dsv.scipro.reflection.Reflection;
|
||||||
|
import se.su.dsv.scipro.reflection.ReflectionService;
|
||||||
|
import se.su.dsv.scipro.system.User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is not meant to be a re-usable panel and is made specifically to be used
|
||||||
|
* as the body of the modal dialog that opens when a supervisor views the
|
||||||
|
* author's reflection as they're doing their final individual assessment.
|
||||||
|
*/
|
||||||
|
class ReflectionModalBodyPanel extends Panel {
|
||||||
|
@Inject
|
||||||
|
private ReflectionService reflectionService;
|
||||||
|
|
||||||
|
private final IModel<Project> projectModel;
|
||||||
|
private final IModel<User> authorModel;
|
||||||
|
|
||||||
|
ReflectionModalBodyPanel(String id, IModel<Project> projectModel, IModel<User> authorModel) {
|
||||||
|
super(id);
|
||||||
|
this.projectModel = projectModel;
|
||||||
|
this.authorModel = authorModel;
|
||||||
|
|
||||||
|
setOutputMarkupPlaceholderTag(true); // enable ajax refreshing of the entire body
|
||||||
|
|
||||||
|
IModel<Reflection> reflectionModel = projectModel.combineWith(authorModel, reflectionService::getReflection);
|
||||||
|
|
||||||
|
add(new MultiLineLabel("reflection_text", reflectionModel.map(this::getReflectionText)));
|
||||||
|
|
||||||
|
add(new MultiLineLabel("improvements_needed_supervisor_feedback", reflectionModel
|
||||||
|
.as(Reflection.ImprovementsNeeded.class)
|
||||||
|
.map(Reflection.ImprovementsNeeded::commentBySupervisor)) {
|
||||||
|
@Override
|
||||||
|
protected void onConfigure() {
|
||||||
|
super.onConfigure();
|
||||||
|
setVisible(!getDefaultModelObjectAsString().isBlank());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add(new RequestImprovementsForm("request_improvements_form", reflectionModel));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getReflectionText(Reflection reflection) {
|
||||||
|
if (reflection instanceof Reflection.Submitted submitted) {
|
||||||
|
return submitted.reflection();
|
||||||
|
} else if (reflection instanceof Reflection.ImprovementsNeeded improvementsNeeded) {
|
||||||
|
return improvementsNeeded.oldReflection();
|
||||||
|
} else {
|
||||||
|
return getString("reflection_not_submitted");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDetach() {
|
||||||
|
this.projectModel.detach();
|
||||||
|
this.authorModel.detach();
|
||||||
|
super.onDetach();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RequestImprovementsForm extends Form<Reflection> {
|
||||||
|
public RequestImprovementsForm(String id, IModel<Reflection> reflectionModel) {
|
||||||
|
super(id, reflectionModel);
|
||||||
|
|
||||||
|
IModel<String> commentModel = new Model<>();
|
||||||
|
|
||||||
|
TextArea<String> comment = new TextArea<>("comment", commentModel);
|
||||||
|
comment.setRequired(true);
|
||||||
|
add(comment);
|
||||||
|
|
||||||
|
add(new AjaxSubmitLink("submit") {
|
||||||
|
@Override
|
||||||
|
protected void onSubmit(AjaxRequestTarget target) {
|
||||||
|
super.onSubmit(target);
|
||||||
|
|
||||||
|
reflectionService.requestNewReflection(
|
||||||
|
projectModel.getObject(),
|
||||||
|
authorModel.getObject(),
|
||||||
|
commentModel.getObject());
|
||||||
|
|
||||||
|
target.add(ReflectionModalBodyPanel.this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onConfigure() {
|
||||||
|
super.onConfigure();
|
||||||
|
setVisible(getModelObject() instanceof Reflection.Submitted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
improvements_requested=You've requested improvements to the submitted version.
|
||||||
|
request_improvements=Request improvements
|
||||||
|
comment=Comment
|
||||||
|
reflection_not_submitted=Reflection not submitted yet
|
||||||
|
request_improvements_text=If the submitted reflection does not meet the minimum requirements \
|
||||||
|
you can request improvements from the student. The student will be notified and can submit a new reflection. \
|
||||||
|
Use the comment field to provide feedback to the student.
|
@ -38,6 +38,30 @@
|
|||||||
<div wicket:id="current_thesis_file"></div>
|
<div wicket:id="current_thesis_file"></div>
|
||||||
</div>
|
</div>
|
||||||
</wicket:enclosure>
|
</wicket:enclosure>
|
||||||
|
<wicket:enclosure child="old_reflection">
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<h4 class="alert-heading">
|
||||||
|
<wicket:message key="reflection_improvements_needed_heading">
|
||||||
|
Reflection improvements needed
|
||||||
|
</wicket:message>
|
||||||
|
</h4>
|
||||||
|
<p>
|
||||||
|
<wicket:message key="reflection_improvements_needed">
|
||||||
|
Your supervisor has requested that you improve and resubmit your reflection.
|
||||||
|
See their comments below about what changes are necessary.
|
||||||
|
</wicket:message>
|
||||||
|
</p>
|
||||||
|
<p wicket:id="supervisor_comment">
|
||||||
|
[You need to reflect more on the methods you used.]
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<h4>
|
||||||
|
<wicket:message key="old_reflection">
|
||||||
|
Your previous reflection
|
||||||
|
</wicket:message>
|
||||||
|
</h4>
|
||||||
|
<p wicket:id="old_reflection"></p>
|
||||||
|
</wicket:enclosure>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label" wicket:for="reflection">
|
<label class="form-label" wicket:for="reflection">
|
||||||
<wicket:message key="reflection">[Reflection]</wicket:message>
|
<wicket:message key="reflection">[Reflection]</wicket:message>
|
||||||
|
@ -24,6 +24,7 @@ import se.su.dsv.scipro.grading.PublicationMetadata;
|
|||||||
import se.su.dsv.scipro.grading.PublicationMetadataFormComponentPanel;
|
import se.su.dsv.scipro.grading.PublicationMetadataFormComponentPanel;
|
||||||
import se.su.dsv.scipro.grading.PublicationMetadataService;
|
import se.su.dsv.scipro.grading.PublicationMetadataService;
|
||||||
import se.su.dsv.scipro.project.Project;
|
import se.su.dsv.scipro.project.Project;
|
||||||
|
import se.su.dsv.scipro.reflection.Reflection;
|
||||||
import se.su.dsv.scipro.reflection.ReflectionService;
|
import se.su.dsv.scipro.reflection.ReflectionService;
|
||||||
import se.su.dsv.scipro.session.SciProSession;
|
import se.su.dsv.scipro.session.SciProSession;
|
||||||
|
|
||||||
@ -45,17 +46,20 @@ public class FinalStepsPanel extends GenericPanel<Project> {
|
|||||||
|
|
||||||
add(new FencedFeedbackPanel("feedback", this));
|
add(new FencedFeedbackPanel("feedback", this));
|
||||||
|
|
||||||
IModel<String> reflection = LoadableDetachableModel.of(() ->
|
IModel<Reflection> currentReflection = LoadableDetachableModel.of(() ->
|
||||||
reflectionService.getSubmittedReflection(projectModel.getObject(), SciProSession.get().getUser()));
|
reflectionService.getReflection(projectModel.getObject(), SciProSession.get().getUser()));
|
||||||
add(new MultiLineLabel("reflection", reflection) {
|
IModel<String> reflectionText = currentReflection
|
||||||
|
.as(Reflection.Submitted.class)
|
||||||
|
.map(Reflection.Submitted::reflection);
|
||||||
|
add(new MultiLineLabel("reflection", reflectionText) {
|
||||||
@Override
|
@Override
|
||||||
protected void onConfigure() {
|
protected void onConfigure() {
|
||||||
super.onConfigure();
|
super.onConfigure();
|
||||||
setVisible(getDefaultModelObject() != null);
|
setVisible(!getDefaultModelObjectAsString().isBlank());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
add(new FinalStepsForm("submit_reflection", projectModel));
|
add(new FinalStepsForm("submit_reflection", projectModel, currentReflection));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -67,15 +71,42 @@ public class FinalStepsPanel extends GenericPanel<Project> {
|
|||||||
private class FinalStepsForm extends Form<Project> {
|
private class FinalStepsForm extends Form<Project> {
|
||||||
private final FinalThesisUploadComponent thesisFileUpload;
|
private final FinalThesisUploadComponent thesisFileUpload;
|
||||||
private final IModel<PublicationMetadata> publicationMetadataModel;
|
private final IModel<PublicationMetadata> publicationMetadataModel;
|
||||||
|
private final IModel<Reflection> currentReflection;
|
||||||
private IModel<String> reflectionModel;
|
private IModel<String> reflectionModel;
|
||||||
private IModel<PublishingConsentService.Level> levelModel;
|
private IModel<PublishingConsentService.Level> levelModel;
|
||||||
|
|
||||||
public FinalStepsForm(String id, IModel<Project> projectModel) {
|
public FinalStepsForm(String id, IModel<Project> projectModel, IModel<Reflection> currentReflection) {
|
||||||
super(id, projectModel);
|
super(id, projectModel);
|
||||||
|
|
||||||
|
this.currentReflection = currentReflection;
|
||||||
|
|
||||||
|
IModel<Reflection.ImprovementsNeeded> i = this.currentReflection.as(Reflection.ImprovementsNeeded.class);
|
||||||
|
|
||||||
|
IModel<String> oldReflection = i.map(Reflection.ImprovementsNeeded::oldReflection);
|
||||||
|
add(new MultiLineLabel("old_reflection", oldReflection) {
|
||||||
|
@Override
|
||||||
|
protected void onConfigure() {
|
||||||
|
super.onConfigure();
|
||||||
|
setVisibilityAllowed(!getDefaultModelObjectAsString().isBlank());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add(new MultiLineLabel("supervisor_comment", i.map(Reflection.ImprovementsNeeded::commentBySupervisor)) {
|
||||||
|
@Override
|
||||||
|
protected void onConfigure() {
|
||||||
|
super.onConfigure();
|
||||||
|
setVisibilityAllowed(!getDefaultModelObjectAsString().isBlank());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
reflectionModel = new Model<>();
|
reflectionModel = new Model<>();
|
||||||
TextArea<String> reflectionTextArea = new TextArea<>("reflection", reflectionModel);
|
TextArea<String> reflectionTextArea = new TextArea<>("reflection", reflectionModel) {
|
||||||
|
@Override
|
||||||
|
protected void onConfigure() {
|
||||||
|
super.onConfigure();
|
||||||
|
setVisible(FinalStepsForm.this.currentReflection.getObject().isSubmittable());
|
||||||
|
}
|
||||||
|
};
|
||||||
reflectionTextArea.setRequired(true);
|
reflectionTextArea.setRequired(true);
|
||||||
add(reflectionTextArea);
|
add(reflectionTextArea);
|
||||||
|
|
||||||
@ -111,7 +142,13 @@ public class FinalStepsPanel extends GenericPanel<Project> {
|
|||||||
currentThesisFile.add(new OppositeVisibility(thesisFileUpload));
|
currentThesisFile.add(new OppositeVisibility(thesisFileUpload));
|
||||||
add(currentThesisFile);
|
add(currentThesisFile);
|
||||||
publicationMetadataModel = LoadableDetachableModel.of(() -> publicationMetadataService.getByProject(getModelObject()));
|
publicationMetadataModel = LoadableDetachableModel.of(() -> publicationMetadataService.getByProject(getModelObject()));
|
||||||
WebMarkupContainer publicationMetadata = new WebMarkupContainer("publication_metadata");
|
WebMarkupContainer publicationMetadata = new WebMarkupContainer("publication_metadata") {
|
||||||
|
@Override
|
||||||
|
protected void onConfigure() {
|
||||||
|
super.onConfigure();
|
||||||
|
setEnabled(finalThesisService.isUploadAllowed(FinalStepsPanel.FinalStepsForm.this.getModelObject()));
|
||||||
|
}
|
||||||
|
};
|
||||||
add(publicationMetadata);
|
add(publicationMetadata);
|
||||||
publicationMetadata.add(new PublicationMetadataFormComponentPanel("publication_metadata_components", publicationMetadataModel));
|
publicationMetadata.add(new PublicationMetadataFormComponentPanel("publication_metadata_components", publicationMetadataModel));
|
||||||
}
|
}
|
||||||
@ -119,7 +156,7 @@ public class FinalStepsPanel extends GenericPanel<Project> {
|
|||||||
@Override
|
@Override
|
||||||
protected void onConfigure() {
|
protected void onConfigure() {
|
||||||
super.onConfigure();
|
super.onConfigure();
|
||||||
setVisibilityAllowed(reflectionService.hasToFillInReflection(getModelObject(), SciProSession.get().getUser()));
|
setVisibilityAllowed(currentReflection.getObject().isSubmittable());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -9,3 +9,7 @@ current_final_thesis=Final thesis
|
|||||||
publication_metadata_why=Please provide the following metadata.
|
publication_metadata_why=Please provide the following metadata.
|
||||||
englishTitle=English title
|
englishTitle=English title
|
||||||
swedishTitle=Swedish title
|
swedishTitle=Swedish title
|
||||||
|
reflection_improvements_needed_heading=Reflection improvements needed
|
||||||
|
reflection_improvements_needed=Your supervisor has requested that you improve and resubmit your reflection. \
|
||||||
|
See their comments below about what changes are necessary.
|
||||||
|
old_reflection=Your previous reflection
|
||||||
|
@ -65,6 +65,7 @@ ProjectEvent.FIRST_MEETING = First meeting created. (with date, time, place/room
|
|||||||
ProjectEvent.OPPOSITION_FAILED = An author fails their opposition.
|
ProjectEvent.OPPOSITION_FAILED = An author fails their opposition.
|
||||||
ProjectEvent.PARTICIPATION_APPROVED = An author's active participation is approved.
|
ProjectEvent.PARTICIPATION_APPROVED = An author's active participation is approved.
|
||||||
ProjectEvent.PARTICIPATION_FAILED = An author fails their active participation.
|
ProjectEvent.PARTICIPATION_FAILED = An author fails their active participation.
|
||||||
|
ProjectEvent.REFLECTION_IMPROVEMENTS_REQUESTED = Reflection improvements requested.
|
||||||
|
|
||||||
ProjectForumEvent.NEW_FORUM_POST = Forum thread created.
|
ProjectForumEvent.NEW_FORUM_POST = Forum thread created.
|
||||||
ProjectForumEvent.NEW_FORUM_POST_COMMENT = Comment posted in forum thread.
|
ProjectForumEvent.NEW_FORUM_POST_COMMENT = Comment posted in forum thread.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user