2984 PO 4) Save (re-)submissions of authors for examination

This commit is contained in:
Andreas Svanberg 2023-10-02 11:58:46 +02:00
parent c615ffea56
commit b2c8d94d2f
9 changed files with 224 additions and 16 deletions

@ -4,14 +4,18 @@ 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, ThesisApprovedHistoryService {
public class GradingHistory implements
ExaminerTimelineService,
ThesisRejectionHistoryService,
ThesisApprovedHistoryService,
ThesisSubmissionHistoryService
{
private final GradingHistoryEventRepository gradingHistoryEventRepository;
@Inject
@ -21,27 +25,29 @@ public class GradingHistory implements ExaminerTimelineService, ThesisRejectionH
@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<SubmissionEvent> submissions = gradingHistoryEventRepository.findSubmissions(project);
for (SubmissionEvent submission : submissions) {
if (submission.getCorrections() == null || submission.getCorrections().isBlank()) {
events.add(new Event.InitialSubmission(submission.getWhen(), submission.getAuthor()));
} else {
events.add(new Event.Resubmitted(
submission.getWhen(),
submission.getAuthor(),
submission.getCorrections()));
}
}
Collection<RejectionEvent> rejections = gradingHistoryEventRepository.findRejections(project);
rejections.forEach(rejection -> {
for (RejectionEvent rejection : rejections) {
events.add(new Event.Rejected(rejection.getWhen(), rejection.getReason()));
});
}
Collection<ApprovedEvent> approvals = gradingHistoryEventRepository.findApprovals(project);
approvals.forEach(approval -> {
for (ApprovedEvent approval : approvals) {
events.add(new Event.Approved(approval.getWhen()));
});
events.add(new Event.Approved(morning.plus(Duration.ofHours(8))));
}
events.sort(Comparator.comparing(Event::when).reversed());
@ -64,4 +70,23 @@ public class GradingHistory implements ExaminerTimelineService, ThesisRejectionH
approvedEvent.setWhen(when);
gradingHistoryEventRepository.save(approvedEvent);
}
@Override
public void addInitialSubmission(Project project, User author, Instant when) {
SubmissionEvent submissionEvent = new SubmissionEvent();
submissionEvent.setProject(project);
submissionEvent.setAuthor(author);
submissionEvent.setWhen(when);
gradingHistoryEventRepository.save(submissionEvent);
}
@Override
public void addResubmission(Project project, User author, Instant when, String corrections) {
SubmissionEvent submissionEvent = new SubmissionEvent();
submissionEvent.setProject(project);
submissionEvent.setAuthor(author);
submissionEvent.setWhen(when);
submissionEvent.setCorrections(corrections);
gradingHistoryEventRepository.save(submissionEvent);
}
}

@ -12,4 +12,8 @@ public interface GradingHistoryEventRepository {
void save(ApprovedEvent approvedEvent);
Collection<ApprovedEvent> findApprovals(Project project);
void save(SubmissionEvent submissionEvent);
Collection<SubmissionEvent> findSubmissions(Project project);
}

@ -40,4 +40,17 @@ public class GradingHistoryEventRepositoryImpl extends AbstractRepository implem
.where(QApprovedEvent.approvedEvent.project.eq(project))
.fetch();
}
@Override
@Transactional
public void save(SubmissionEvent submissionEvent) {
em().persist(submissionEvent);
}
@Override
public Collection<SubmissionEvent> findSubmissions(Project project) {
return from(QSubmissionEvent.submissionEvent)
.where(QSubmissionEvent.submissionEvent.project.eq(project))
.fetch();
}
}

@ -23,5 +23,7 @@ public class GradingModule extends PrivateModule {
expose(ThesisRejectionHistoryService.class);
bind(ThesisApprovedHistoryService.class).to(GradingHistory.class);
expose(ThesisApprovedHistoryService.class);
bind(ThesisSubmissionHistoryService.class).to(GradingHistory.class);
expose(ThesisSubmissionHistoryService.class);
}
}

@ -0,0 +1,108 @@
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 se.su.dsv.scipro.system.User;
import java.time.Instant;
import java.util.Objects;
@Entity
@Table(name = "grading_history_submissions")
public class SubmissionEvent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "project_id")
private Project project;
@ManyToOne
@JoinColumn(name = "author_id")
private User author;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "`when`")
private Instant when;
@Basic
private String corrections;
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 User getAuthor() {
return author;
}
public void setAuthor(User user) {
this.author = user;
}
public Instant getWhen() {
return when;
}
public void setWhen(Instant when) {
this.when = when;
}
public String getCorrections() {
return corrections;
}
public void setCorrections(String corrections) {
this.corrections = corrections;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
return o instanceof SubmissionEvent that
&& Objects.equals(id, that.id)
&& Objects.equals(project, that.project)
&& Objects.equals(author, that.author)
&& Objects.equals(when, that.when)
&& Objects.equals(corrections, that.corrections);
}
@Override
public int hashCode() {
return Objects.hash(id, project, author, when, corrections);
}
@Override
public String toString() {
return "RejectionEvent{" +
"id=" + id +
", project=" + project +
", author=" + author +
", when=" + when +
", corrections='" + corrections + '\'' +
'}';
}
}

@ -0,0 +1,12 @@
package se.su.dsv.scipro.grading;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.system.User;
import java.time.Instant;
public interface ThesisSubmissionHistoryService {
void addInitialSubmission(Project project, User author, Instant when);
void addResubmission(Project project, User author, Instant when, String corrections);
}

@ -5,6 +5,7 @@ import com.google.inject.persist.Transactional;
import jakarta.persistence.EntityManager;
import se.su.dsv.scipro.finalseminar.FinalSeminarOpposition;
import se.su.dsv.scipro.grading.GradingBasis;
import se.su.dsv.scipro.grading.ThesisSubmissionHistoryService;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.system.AbstractServiceImpl;
import se.su.dsv.scipro.system.User;
@ -13,6 +14,7 @@ import se.su.dsv.scipro.util.Either;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import java.time.Clock;
import java.util.*;
@Named
@ -21,11 +23,20 @@ public class GradingReportServiceImpl extends AbstractServiceImpl<GradingReport,
public static final String OPPOSITION_SWEDISH = "Ö1 Oppositionsrapport";
public static final String OPPOSITION_ENGLISH = "Ö1 Opposition report";
private final EventBus eventBus;
private final ThesisSubmissionHistoryService thesisSubmissionHistoryService;
private final Clock clock;
@Inject
public GradingReportServiceImpl(Provider<EntityManager> em, EventBus eventBus) {
public GradingReportServiceImpl(
Provider<EntityManager> em,
EventBus eventBus,
ThesisSubmissionHistoryService thesisSubmissionHistoryService,
Clock clock)
{
super(em, GradingReport.class, QGradingReport.gradingReport);
this.eventBus = eventBus;
this.thesisSubmissionHistoryService = thesisSubmissionHistoryService;
this.clock = clock;
}
@Override
@ -133,6 +144,7 @@ public class GradingReportServiceImpl extends AbstractServiceImpl<GradingReport,
}
@Override
@Transactional
public Either<List<SubmissionError>, SupervisorGradingReport> submitReport(SupervisorGradingReport supervisorGradingReport) {
final ArrayList<SubmissionError> errors = new ArrayList<>();
for (GradingCriterion gradingCriterion : supervisorGradingReport.getGradingCriteria()) {
@ -141,7 +153,8 @@ public class GradingReportServiceImpl extends AbstractServiceImpl<GradingReport,
}
}
if (!isBlank(supervisorGradingReport.getRejectionComment()) && isBlank(supervisorGradingReport.getRejectionCommentFeedback())) {
String rejectionCommentFeedback = supervisorGradingReport.getRejectionCommentFeedback();
if (!isBlank(supervisorGradingReport.getRejectionComment()) && isBlank(rejectionCommentFeedback)) {
errors.add(new SubmissionError.NoRejectionCommentFeedback());
}
@ -152,6 +165,19 @@ public class GradingReportServiceImpl extends AbstractServiceImpl<GradingReport,
supervisorGradingReport.submit();
eventBus.post(new SupervisorGradingReportSubmittedEvent(supervisorGradingReport));
if (isBlank(rejectionCommentFeedback)) {
thesisSubmissionHistoryService.addInitialSubmission(
supervisorGradingReport.getProject(),
supervisorGradingReport.getUser(),
clock.instant());
}
else {
thesisSubmissionHistoryService.addResubmission(
supervisorGradingReport.getProject(),
supervisorGradingReport.getUser(),
clock.instant(),
rejectionCommentFeedback);
}
save(supervisorGradingReport);
return Either.right(supervisorGradingReport);

@ -0,0 +1,11 @@
CREATE TABLE IF NOT EXISTS `grading_history_submissions`
(
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`when` TIMESTAMP NOT NULL DEFAULT 0, -- disable automatic CURRENT_TIMESTAMP behaviour
`author_id` BIGINT NOT NULL,
`corrections` TEXT NULL,
PRIMARY KEY (id),
CONSTRAINT `FK_grading_history_submissions_project` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`),
CONSTRAINT `FK_grading_history_submissions_author` FOREIGN KEY (`author_id`) REFERENCES `user` (`id`)
) collate utf8mb4_swedish_ci;

@ -6,6 +6,11 @@ import com.google.inject.name.Names;
import modules.CoreModule;
import se.su.dsv.scipro.file.FileModule;
import se.su.dsv.scipro.file.FileStore;
import se.su.dsv.scipro.grading.GradingHistory;
import se.su.dsv.scipro.grading.GradingHistoryEventRepository;
import se.su.dsv.scipro.grading.GradingHistoryEventRepositoryImpl;
import se.su.dsv.scipro.grading.GradingModule;
import se.su.dsv.scipro.grading.ThesisSubmissionHistoryService;
import se.su.dsv.scipro.plagiarism.urkund.UrkundModule;
import se.su.dsv.scipro.reviewing.ReviewingModule;
import se.su.dsv.scipro.sukat.SukatModule;
@ -27,6 +32,8 @@ public abstract class IntegrationTest extends GuiceTest {
install(new ReviewingModule());
install(new SukatModule());
install(new UrkundModule());
bind(ThesisSubmissionHistoryService.class).to(GradingHistory.class);
bind(GradingHistoryEventRepository.class).to(GradingHistoryEventRepositoryImpl.class);
bind(CurrentUser.class).to(TestUser.class);
bind(FileStore.class).to(InMemoryFileStore.class);
}