2984 PO 4) Save every approval by the examiners

This commit is contained in:
Andreas Svanberg 2023-09-12 14:04:57 +02:00
parent 3e408d143c
commit 903ade76db
11 changed files with 164 additions and 28 deletions
core/src/main
daisy-integration/src
main/java/se/su/dsv/scipro/integration/daisy/workers
test/java/se/su/dsv/scipro/integration/daisy/workers
view/src/main/java/se/su/dsv/scipro/grading

@ -0,0 +1,79 @@
package se.su.dsv.scipro.grading;
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_approvals")
public class ApprovedEvent {
@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;
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;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
return o instanceof ApprovedEvent that
&& Objects.equals(id, that.id)
&& Objects.equals(project, that.project)
&& Objects.equals(when, that.when);
}
@Override
public int hashCode() {
return Objects.hash(id, project, when);
}
@Override
public String toString() {
return "ApprovedEvent{" +
"id=" + id +
", project=" + project +
", when=" + when +
'}';
}
}

@ -11,12 +11,12 @@ import java.util.Collection;
import java.util.Comparator;
import java.util.List;
public class GradingHistory implements ExaminerTimelineService, ThesisRejectionHistoryService {
private final RejectionEventRepository rejectionEventRepository;
public class GradingHistory implements ExaminerTimelineService, ThesisRejectionHistoryService, ThesisApprovedHistoryService {
private final GradingHistoryEventRepository gradingHistoryEventRepository;
@Inject
public GradingHistory(RejectionEventRepository rejectionEventRepository) {
this.rejectionEventRepository = rejectionEventRepository;
public GradingHistory(GradingHistoryEventRepository gradingHistoryEventRepository) {
this.gradingHistoryEventRepository = gradingHistoryEventRepository;
}
@Override
@ -31,11 +31,16 @@ public class GradingHistory implements ExaminerTimelineService, ThesisRejectionH
events.add(new Event.Resubmitted(morning.plus(Duration.ofHours(5)), author, "Ok they improved it"));
}
Collection<RejectionEvent> rejections = rejectionEventRepository.findByProject(project);
Collection<RejectionEvent> rejections = gradingHistoryEventRepository.findRejections(project);
rejections.forEach(rejection -> {
events.add(new Event.Rejected(rejection.getWhen(), rejection.getReason()));
});
Collection<ApprovedEvent> approvals = gradingHistoryEventRepository.findApprovals(project);
approvals.forEach(approval -> {
events.add(new Event.Approved(approval.getWhen()));
});
events.add(new Event.Approved(morning.plus(Duration.ofHours(8))));
events.sort(Comparator.comparing(Event::when).reversed());
@ -49,6 +54,14 @@ public class GradingHistory implements ExaminerTimelineService, ThesisRejectionH
rejectionEvent.setProject(project);
rejectionEvent.setReason(reason);
rejectionEvent.setWhen(when);
rejectionEventRepository.save(rejectionEvent);
gradingHistoryEventRepository.save(rejectionEvent);
}
@Override
public void addApproved(Project project, Instant when) {
ApprovedEvent approvedEvent = new ApprovedEvent();
approvedEvent.setProject(project);
approvedEvent.setWhen(when);
gradingHistoryEventRepository.save(approvedEvent);
}
}

@ -0,0 +1,15 @@
package se.su.dsv.scipro.grading;
import se.su.dsv.scipro.project.Project;
import java.util.Collection;
public interface GradingHistoryEventRepository {
void save(RejectionEvent rejectionEvent);
Collection<RejectionEvent> findRejections(Project project);
void save(ApprovedEvent approvedEvent);
Collection<ApprovedEvent> findApprovals(Project project);
}

@ -9,9 +9,9 @@ import javax.inject.Inject;
import javax.inject.Provider;
import java.util.Collection;
public class RejectionEventRepositoryImpl extends AbstractRepository implements RejectionEventRepository {
public class GradingHistoryEventRepositoryImpl extends AbstractRepository implements GradingHistoryEventRepository {
@Inject
public RejectionEventRepositoryImpl(Provider<EntityManager> em) {
public GradingHistoryEventRepositoryImpl(Provider<EntityManager> em) {
super(em);
}
@ -22,9 +22,22 @@ public class RejectionEventRepositoryImpl extends AbstractRepository implements
}
@Override
public Collection<RejectionEvent> findByProject(Project project) {
public Collection<RejectionEvent> findRejections(Project project) {
return from(QRejectionEvent.rejectionEvent)
.where(QRejectionEvent.rejectionEvent.project.eq(project))
.fetch();
}
@Override
@Transactional
public void save(ApprovedEvent approvedEvent) {
em().persist(approvedEvent);
}
@Override
public Collection<ApprovedEvent> findApprovals(Project project) {
return from(QApprovedEvent.approvedEvent)
.where(QApprovedEvent.approvedEvent.project.eq(project))
.fetch();
}
}

@ -18,8 +18,10 @@ public class GradingModule extends PrivateModule {
bind(ExaminerTimelineService.class).to(GradingHistory.class);
expose(ExaminerTimelineService.class);
bind(RejectionEventRepository.class).to(RejectionEventRepositoryImpl.class);
bind(GradingHistoryEventRepository.class).to(GradingHistoryEventRepositoryImpl.class);
bind(ThesisRejectionHistoryService.class).to(GradingHistory.class);
expose(ThesisRejectionHistoryService.class);
bind(ThesisApprovedHistoryService.class).to(GradingHistory.class);
expose(ThesisApprovedHistoryService.class);
}
}

@ -1,11 +0,0 @@
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,9 @@
package se.su.dsv.scipro.grading;
import se.su.dsv.scipro.project.Project;
import java.time.Instant;
public interface ThesisApprovedHistoryService {
void addApproved(Project project, Instant when);
}

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

@ -3,6 +3,7 @@ package se.su.dsv.scipro.integration.daisy.workers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import se.su.dsv.scipro.daisyExternal.http.DaisyAPI;
import se.su.dsv.scipro.grading.ThesisApprovedHistoryService;
import se.su.dsv.scipro.io.dto.STATUS;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.project.ProjectService;
@ -12,6 +13,7 @@ import se.su.dsv.scipro.workerthreads.AbstractWorker;
import javax.inject.Inject;
import javax.inject.Named;
import java.time.Instant;
import java.util.Optional;
@Named
@ -21,30 +23,38 @@ public class ProjectFinalizer extends AbstractWorker {
private final ProjectService projectService;
private final DaisyAPI daisyAPI;
private final ThesisApprovedHistoryService thesisApprovedHistoryService;
@Inject
public ProjectFinalizer(ProjectService projectService, DaisyAPI daisyAPI) {
public ProjectFinalizer(
ProjectService projectService,
DaisyAPI daisyAPI,
ThesisApprovedHistoryService thesisApprovedHistoryService)
{
this.projectService = projectService;
this.daisyAPI = daisyAPI;
this.thesisApprovedHistoryService = thesisApprovedHistoryService;
}
@Override
protected void doWork() {
beginTransaction();
Iterable<Project> projects = projectService.findAll(QProject.project.projectStatus.eq(ProjectStatus.ACTIVE));
for (Project project : projects) {
beginTransaction();
try {
Optional.ofNullable(project.getIdentifier())
.flatMap(daisyAPI::getThesis)
.filter(thesis -> thesis.getStatus() == STATUS.FINISHED)
.ifPresent(thesis -> {
projectService.complete(project);
Instant when = thesis.getReadyDate().toInstant();
thesisApprovedHistoryService.addApproved(project, when);
LOG.info("Finalized project ({})", project);
});
} catch (Exception e) {
LOG.info("Failed to finalize project ({})", project, e);
}
commitTransaction();
}
commitTransaction();
}
}

@ -8,6 +8,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import se.su.dsv.scipro.daisyExternal.http.DaisyAPI;
import se.su.dsv.scipro.grading.ThesisApprovedHistoryService;
import se.su.dsv.scipro.io.dto.STATUS;
import se.su.dsv.scipro.io.dto.Thesis;
import se.su.dsv.scipro.project.Project;
@ -41,11 +42,12 @@ public class ProjectFinalizerTest {
private @Mock EntityManager entityManager;
private @Mock EntityTransaction entityTransaction;
private @Mock WorkerDataService workerDataService;
private @Mock ThesisApprovedHistoryService thesisApprovedHistoryService;
@BeforeEach
public void setup() {
projectFinalizer = new ProjectFinalizer(projectService, daisyAPI);
projectFinalizer = new ProjectFinalizer(projectService, daisyAPI, thesisApprovedHistoryService);
projectFinalizer.setUnitOfWork(unitOfWork);
projectFinalizer.setTxManager(Providers.of(entityManager));
projectFinalizer.setWorkerDataService(workerDataService);

@ -6,9 +6,6 @@
<span class="fa fa-thumbs-up text-success"></span>
Approved
</h4>
<div class="forumGrayBackground">
Examiner
</div>
<p>
The thesis has been accepted by the examiner and the authors have received their grade.
</p>