Skip to content

Commit

Permalink
first implementation for observation timeline
Browse files Browse the repository at this point in the history
  • Loading branch information
drtyyj committed May 3, 2024
1 parent ed9f0b1 commit f91c85c
Show file tree
Hide file tree
Showing 8 changed files with 357 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
package io.redlink.more.studymanager.controller.studymanager;

import io.redlink.more.studymanager.api.v1.model.StudyTimelineDTO;
import io.redlink.more.studymanager.api.v1.webservices.CalendarApi;
import io.redlink.more.studymanager.model.transformer.TimelineTransformer;
import io.redlink.more.studymanager.properties.GatewayProperties;
import io.redlink.more.studymanager.service.CalendarService;
import io.redlink.more.studymanager.service.OAuth2AuthenticationService;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDate;
import java.time.OffsetDateTime;

@RestController
@RequestMapping(value = "/api/v1", produces = MediaType.APPLICATION_JSON_VALUE)
@EnableConfigurationProperties(GatewayProperties.class)
public class CalendarApiV1Controller implements CalendarApi {

private final CalendarService service;
OAuth2AuthenticationService auth2AuthenticationService;
private final GatewayProperties properties;

public CalendarApiV1Controller(GatewayProperties properties) {
public CalendarApiV1Controller(CalendarService service, OAuth2AuthenticationService auth2AuthenticationService, GatewayProperties properties) {
this.service = service;
this.auth2AuthenticationService = auth2AuthenticationService;
this.properties = properties;
}

Expand All @@ -26,4 +37,14 @@ public ResponseEntity<String> getStudyCalendar(Long studyId) {
.header("Location", properties.getBaseUrl() + "/api/v1/calendar/studies/" + studyId + "/calendar.ics")
.build();
}

@Override
public ResponseEntity<StudyTimelineDTO> getStudyTimeline(Long studyId, Integer participant, Integer studyGroup, OffsetDateTime referenceDate, LocalDate from, LocalDate to) {
final var currentUser = auth2AuthenticationService.getCurrentUser();
return ResponseEntity.ok(
TimelineTransformer.toStudyTimelineDTO(
service.getTimeline(studyId, participant, studyGroup, referenceDate, from, to, currentUser)
)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.redlink.more.studymanager.model.timeline;

import java.time.Instant;

public record InterventionTimelineEvent(
Integer interventionId,
Integer studyGroupId,
String title,
String purpose,
String type,
Instant start,
Instant end,
String scheduleType
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.redlink.more.studymanager.model.timeline;

import java.time.Instant;

public record ObservationTimelineEvent(
Integer observationId,
Integer studyGroupId,
String title,
String purpose,
String type,
Instant start,
Instant end,
Boolean hidden,
String scheduleType
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.redlink.more.studymanager.model.timeline;

import java.util.ArrayList;
import java.util.List;

public class StudyTimeline {
List<ObservationTimelineEvent> observationTimelineEvents;
List<InterventionTimelineEvent> interventionTimelineEvents;

public StudyTimeline() {
observationTimelineEvents = new ArrayList<>();
interventionTimelineEvents = new ArrayList<>();
}

public void addObservationTimelineEvent(ObservationTimelineEvent event) {
observationTimelineEvents.add(event);
}

public void addAllObservations(List<ObservationTimelineEvent> events) { observationTimelineEvents.addAll(events); }

public List<ObservationTimelineEvent> getObservationTimelineEvents() {
return observationTimelineEvents;
}

public void addInterventionTimelineEvent(InterventionTimelineEvent event) {
interventionTimelineEvents.add(event);
}

public void addAllInterventions(List<InterventionTimelineEvent> events) { interventionTimelineEvents.addAll(events); }

public List<InterventionTimelineEvent> getInterventionTimelineEvents() {
return interventionTimelineEvents;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.redlink.more.studymanager.model.timeline;

public record TimelineFilter (
Integer studyGroupId,
Integer participantId
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package io.redlink.more.studymanager.model.transformer;


import io.redlink.more.studymanager.api.v1.model.InterventionTimelineEventDTO;
import io.redlink.more.studymanager.api.v1.model.ObservationTimelineEventDTO;
import io.redlink.more.studymanager.api.v1.model.StudyTimelineDTO;
import io.redlink.more.studymanager.model.timeline.InterventionTimelineEvent;
import io.redlink.more.studymanager.model.timeline.ObservationTimelineEvent;
import io.redlink.more.studymanager.model.timeline.StudyTimeline;


public class TimelineTransformer {
private TimelineTransformer() {}

public static StudyTimelineDTO toStudyTimelineDTO(StudyTimeline studyTimeline) {
return new StudyTimelineDTO()
.observations(studyTimeline.getObservationTimelineEvents().stream().map(
TimelineTransformer::toObservationTimelineDTO
).toList())
.interventions(studyTimeline.getInterventionTimelineEvents().stream().map(
TimelineTransformer::toInterventionTimelineEventDTO
).toList());
}

public static ObservationTimelineEventDTO toObservationTimelineDTO(ObservationTimelineEvent observationTimelineEvent) {
return new ObservationTimelineEventDTO()
.observationId(observationTimelineEvent.observationId())
.studyGroupId(observationTimelineEvent.studyGroupId())
.title(observationTimelineEvent.title())
.purpose(observationTimelineEvent.purpose())
.type(observationTimelineEvent.type())
.start(Transformers.toOffsetDateTime(observationTimelineEvent.start()))
.end(Transformers.toOffsetDateTime(observationTimelineEvent.end()))
.hidden(observationTimelineEvent.hidden())
.scheduleType(observationTimelineEvent.scheduleType());
}

public static InterventionTimelineEventDTO toInterventionTimelineEventDTO(InterventionTimelineEvent interventionTimelineEvent) {
return new InterventionTimelineEventDTO()
.interventionId(interventionTimelineEvent.interventionId())
.studyGroupId(interventionTimelineEvent.studyGroupId())
.title(interventionTimelineEvent.title())
.purpose(interventionTimelineEvent.purpose())
.type(interventionTimelineEvent.type())
.start(Transformers.toOffsetDateTime(interventionTimelineEvent.start()))
.end(Transformers.toOffsetDateTime(interventionTimelineEvent.end()))
.scheduleType(interventionTimelineEvent.scheduleType());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package io.redlink.more.studymanager.service;

import io.redlink.more.studymanager.model.Participant;
import io.redlink.more.studymanager.model.Study;
import io.redlink.more.studymanager.model.User;
import io.redlink.more.studymanager.model.scheduler.Event;
import io.redlink.more.studymanager.model.scheduler.RelativeEvent;
import io.redlink.more.studymanager.model.scheduler.ScheduleEvent;
import io.redlink.more.studymanager.model.timeline.ObservationTimelineEvent;
import io.redlink.more.studymanager.model.timeline.StudyTimeline;
import io.redlink.more.studymanager.model.transformer.Transformers;
import org.springframework.stereotype.Service;

import java.time.*;
import java.time.temporal.ChronoUnit;
import java.util.Objects;


@Service
public class CalendarService {

private final StudyService studyService;
private final ObservationService observationService;
private final InterventionService interventionService;
private final ParticipantService participantService;
public CalendarService(StudyService studyService, ObservationService observationService, InterventionService interventionService,
ParticipantService participantService) {
this.studyService = studyService;
this.observationService = observationService;
this.interventionService = interventionService;
this.participantService = participantService;
}

public StudyTimeline getTimeline(Long studyId, Integer participantId, Integer studyGroupId, OffsetDateTime referenceDate, LocalDate from, LocalDate to, User currentUser) {
Study study = studyService.getStudy(studyId, currentUser).orElse(null);
if(study == null)
return null;

StudyTimeline studyTimeline = new StudyTimeline();
Integer actualStudyGroupId;
Instant relativeDate = null;

if(participantId != null) {
Participant participant = participantService.getParticipant(studyId, participantId);
if(participant == null)
return null;
actualStudyGroupId = participant.getStudyGroupId();
if (participant.getStart() != null) {
relativeDate = participant.getStart();
}
} else {
actualStudyGroupId = studyGroupId;
}

if(relativeDate == null) {
if (referenceDate != null) {
relativeDate = referenceDate.toInstant();
} else if (study.getStartDate() != null) {
relativeDate = study.getStartDate().atStartOfDay().toInstant(ZoneId.systemDefault().getRules().getOffset(Instant.now()));
} else {
relativeDate = study.getPlannedStartDate().atStartOfDay().toInstant(ZoneId.systemDefault().getRules().getOffset(Instant.now()));
}
}
Instant finalRelativeDate = relativeDate;

studyTimeline.addAllObservations(observationService.listObservations(studyId)
.stream()
.map(observation -> {
ScheduleEvent e = observation.getSchedule();
boolean isWithinTimeframe = true;
Instant start = null;
Instant end = null;

if(Event.TYPE.equals(e.getType())) {
Event event = (Event) e;
start = event.getDateStart();
end = event.getDateEnd();
isWithinTimeframe = start.isAfter(Transformers.toInstant(OffsetDateTime.from(from.atStartOfDay())))
&& end.isBefore(Transformers.toInstant(OffsetDateTime.from(to.atStartOfDay().plusDays(1))));
} else if(RelativeEvent.TYPE.equals(e.getType())) {
RelativeEvent event = (RelativeEvent) e;
start = finalRelativeDate.plus(
event.getDtstart().getOffset().getValue(),
ChronoUnit.valueOf(event.getDtstart().getOffset().getUnit().getValue())
);
end = finalRelativeDate.plus(
event.getDtend().getOffset().getValue(),
ChronoUnit.valueOf(event.getDtend().getOffset().getUnit().getValue())
);

isWithinTimeframe = start.isAfter(Transformers.toInstant(OffsetDateTime.from(from.atStartOfDay())))
&& end.isBefore(Transformers.toInstant(OffsetDateTime.from(to.atStartOfDay().plusDays(1))));
}
if (Objects.equals(observation.getStudyGroupId(), actualStudyGroupId) && isWithinTimeframe)
return new ObservationTimelineEvent(
observation.getObservationId(),
observation.getStudyGroupId(),
observation.getTitle(),
observation.getPurpose(),
observation.getType(),
start,
end,
observation.getHidden(),
observation.getSchedule().getType()
);
return null;
}).filter(Objects::nonNull)
.toList()
);

return studyTimeline;
}


}
Loading

0 comments on commit f91c85c

Please sign in to comment.