2984 PO 4) Save every rejection by an examiner

This commit is contained in:
Andreas Svanberg 2023-09-12 11:35:43 +02:00
parent 2eafad2b29
commit 3e408d143c
11 changed files with 221 additions and 35 deletions
core/src/main
daisy-integration/src/main/java/se/su/dsv/scipro/integration/daisy/workers
view/src
main/java/se/su/dsv/scipro/grading
test/java/se/su/dsv/scipro

@ -1,33 +0,0 @@
package se.su.dsv.scipro.grading;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.system.User;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class ExaminerTimelineServiceImpl implements ExaminerTimelineService {
@Override
public List<Event> getTimeline(Project project) {
Instant morning = Instant.parse("2023-09-11T08:00:00Z");
ArrayList<Event> events = new ArrayList<>();
for (User author : project.getProjectParticipants()) {
events.add(new Event.InitialSubmission(morning, author));
events.add(new Event.Resubmitted(morning.plus(Duration.ofHours(5)), author, "Ok they improved it"));
}
events.add(new Event.Rejected(morning.plus(Duration.ofHours(3)), "It was not very good"));
events.add(new Event.Approved(morning.plus(Duration.ofHours(8))));
events.sort(Comparator.comparing(Event::when).reversed());
return events;
}
}

@ -0,0 +1,54 @@
package se.su.dsv.scipro.grading;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.system.User;
import javax.inject.Inject;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
public class GradingHistory implements ExaminerTimelineService, ThesisRejectionHistoryService {
private final RejectionEventRepository rejectionEventRepository;
@Inject
public GradingHistory(RejectionEventRepository rejectionEventRepository) {
this.rejectionEventRepository = rejectionEventRepository;
}
@Override
public List<Event> getTimeline(Project project) {
Instant morning = Instant.parse("2023-09-11T08:00:00Z");
ArrayList<Event> events = new ArrayList<>();
for (User author : project.getProjectParticipants()) {
events.add(new Event.InitialSubmission(morning, author));
events.add(new Event.Resubmitted(morning.plus(Duration.ofHours(5)), author, "Ok they improved it"));
}
Collection<RejectionEvent> rejections = rejectionEventRepository.findByProject(project);
rejections.forEach(rejection -> {
events.add(new Event.Rejected(rejection.getWhen(), rejection.getReason()));
});
events.add(new Event.Approved(morning.plus(Duration.ofHours(8))));
events.sort(Comparator.comparing(Event::when).reversed());
return events;
}
@Override
public void addRejection(Project project, String reason, Instant when) {
RejectionEvent rejectionEvent = new RejectionEvent();
rejectionEvent.setProject(project);
rejectionEvent.setReason(reason);
rejectionEvent.setWhen(when);
rejectionEventRepository.save(rejectionEvent);
}
}

@ -16,7 +16,10 @@ public class GradingModule extends PrivateModule {
bind(PublicationMetadataService.class).to(PublicationMetadataServiceImpl.class);
expose(PublicationMetadataService.class);
bind(ExaminerTimelineService.class).to(ExaminerTimelineServiceImpl.class);
bind(ExaminerTimelineService.class).to(GradingHistory.class);
expose(ExaminerTimelineService.class);
bind(RejectionEventRepository.class).to(RejectionEventRepositoryImpl.class);
bind(ThesisRejectionHistoryService.class).to(GradingHistory.class);
expose(ThesisRejectionHistoryService.class);
}
}

@ -0,0 +1,93 @@
package se.su.dsv.scipro.grading;
import jakarta.persistence.Basic;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.Temporal;
import jakarta.persistence.TemporalType;
import se.su.dsv.scipro.project.Project;
import java.time.Instant;
import java.util.Objects;
@Entity
@Table(name = "grading_history_rejections")
public class RejectionEvent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "project_id")
private Project project;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "`when`")
private Instant when;
@Basic
private String reason;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Project getProject() {
return project;
}
public void setProject(Project project) {
this.project = project;
}
public Instant getWhen() {
return when;
}
public void setWhen(Instant when) {
this.when = when;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
return o instanceof RejectionEvent that
&& Objects.equals(id, that.id)
&& Objects.equals(project, that.project)
&& Objects.equals(when, that.when)
&& Objects.equals(reason, that.reason);
}
@Override
public int hashCode() {
return Objects.hash(id, project, when, reason);
}
@Override
public String toString() {
return "RejectionEvent{" +
"id=" + id +
", project=" + project +
", when=" + when +
", reason='" + reason + '\'' +
'}';
}
}

@ -0,0 +1,11 @@
package se.su.dsv.scipro.grading;
import se.su.dsv.scipro.project.Project;
import java.util.Collection;
public interface RejectionEventRepository {
void save(RejectionEvent rejectionEvent);
Collection<RejectionEvent> findByProject(Project project);
}

@ -0,0 +1,30 @@
package se.su.dsv.scipro.grading;
import com.google.inject.persist.Transactional;
import jakarta.persistence.EntityManager;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.system.AbstractRepository;
import javax.inject.Inject;
import javax.inject.Provider;
import java.util.Collection;
public class RejectionEventRepositoryImpl extends AbstractRepository implements RejectionEventRepository {
@Inject
public RejectionEventRepositoryImpl(Provider<EntityManager> em) {
super(em);
}
@Override
@Transactional
public void save(RejectionEvent rejectionEvent) {
em().persist(rejectionEvent);
}
@Override
public Collection<RejectionEvent> findByProject(Project project) {
return from(QRejectionEvent.rejectionEvent)
.where(QRejectionEvent.rejectionEvent.project.eq(project))
.fetch();
}
}

@ -0,0 +1,9 @@
package se.su.dsv.scipro.grading;
import se.su.dsv.scipro.project.Project;
import java.time.Instant;
public interface ThesisRejectionHistoryService {
void addRejection(Project project, String reason, Instant when);
}

@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS `grading_history_rejections` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`when` TIMESTAMP NOT NULL DEFAULT 0, -- disable automatic CURRENT_TIMESTAMP behaviour
`reason` TEXT NOT NULL,
PRIMARY KEY (id),
CONSTRAINT `FK_grading_history_rejections_project` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`)
) collate utf8mb4_swedish_ci;

@ -4,6 +4,7 @@ import se.su.dsv.scipro.daisyExternal.http.DaisyAPI;
import se.su.dsv.scipro.finalthesis.FinalThesis;
import se.su.dsv.scipro.finalthesis.FinalThesisService;
import se.su.dsv.scipro.forum.ProjectForumService;
import se.su.dsv.scipro.grading.ThesisRejectionHistoryService;
import se.su.dsv.scipro.io.dto.ThesisRejection;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.project.ProjectService;
@ -24,6 +25,7 @@ public class RejectedThesisWorker extends AbstractWorker {
private final FinalThesisService finalThesisService;
private final ProjectForumService projectForumService;
private final DaisyAPI daisyAPI;
private final ThesisRejectionHistoryService thesisRejectionHistoryService;
@Inject
public RejectedThesisWorker(
@ -31,6 +33,7 @@ public class RejectedThesisWorker extends AbstractWorker {
final ProjectService projectService,
final FinalThesisService finalThesisService,
final ProjectForumService projectForumService,
final ThesisRejectionHistoryService thesisRejectionHistoryService,
final DaisyAPI daisyAPI)
{
this.gradingReportService = gradingReportService;
@ -38,6 +41,7 @@ public class RejectedThesisWorker extends AbstractWorker {
this.finalThesisService = finalThesisService;
this.daisyAPI = daisyAPI;
this.projectForumService = projectForumService;
this.thesisRejectionHistoryService = thesisRejectionHistoryService;
}
@Override
@ -66,6 +70,9 @@ public class RejectedThesisWorker extends AbstractWorker {
"This is an automated message generated by the system.\n\nThe following comment was provided by the examiner. Consult with your supervisor what actions to take from here.\n\n" + thesisRejection.getMessage(),
Collections.emptySet());
}
if (rejectedDate.after(getLastSuccessfulRun())) {
thesisRejectionHistoryService.addRejection(project, thesisRejection.getMessage(), rejectedDate.toInstant());
}
}
}
}

@ -2,6 +2,7 @@ package se.su.dsv.scipro.grading;
import org.apache.wicket.Component;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.basic.MultiLineLabel;
import org.apache.wicket.markup.html.panel.GenericPanel;
import org.apache.wicket.model.IModel;
import se.su.dsv.scipro.grading.ExaminerTimelineService.Event;
@ -43,7 +44,7 @@ public abstract class EventPanel<T extends Event>
public RejectedPanel(String id, IModel<Event.Rejected> model) {
super(id, model);
add(new Label(
add(new MultiLineLabel(
"feedback",
model.map(Event.Rejected::feedback)));
}

@ -50,6 +50,7 @@ import se.su.dsv.scipro.forummail.ForumMailSettingsService;
import se.su.dsv.scipro.gdpr.Reporter;
import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettings;
import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService;
import se.su.dsv.scipro.grading.ExaminerTimelineService;
import se.su.dsv.scipro.grading.GradingService;
import se.su.dsv.scipro.grading.PublicationMetadata;
import se.su.dsv.scipro.grading.PublicationMetadataService;
@ -353,6 +354,8 @@ public abstract class SciProTest {
protected GradingService gradingService;
@Mock
protected PublicationMetadataService publicationMetadataService;
@Mock
protected ExaminerTimelineService examinerTimelineService;
protected WicketTester tester;