Skip to content

Commit

Permalink
Merge pull request #101 from redlink-gmbh/FEATURE-Relative_Study_Start
Browse files Browse the repository at this point in the history
Bugfixes relative study start
  • Loading branch information
alireza-dhp authored Jan 8, 2024
2 parents 619595c + 414b704 commit b8024b6
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
package io.redlink.more.data.model.scheduler;

import java.time.ZoneId;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RelativeDate {

private static Pattern CLOCK = Pattern.compile("(\\d\\d):(\\d\\d)");
private static Pattern CLOCK = Pattern.compile("(\\d?\\d):(\\d\\d)");

private Duration offset;
private String time;
private String timezone;

public RelativeDate() {
}

public ZoneId getZoneId() {
return timezone != null ? ZoneId.of(timezone) : ZoneId.of("Europe/Berlin");
}

public int getHours() {
return getTimeGroup(1);
}
Expand Down Expand Up @@ -50,4 +56,13 @@ public RelativeDate setTime(String time) {
this.time = time;
return this;
}

public String getTimezone() {
return timezone;
}

public RelativeDate setTimezone(String timezone) {
this.timezone = timezone;
return this;
}
}
19 changes: 13 additions & 6 deletions src/main/java/io/redlink/more/data/repository/StudyRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;
Expand Down Expand Up @@ -250,16 +249,24 @@ public Optional<String> createCredentials(String registrationToken, ParticipantC

if (apiId != null) {
jdbcTemplate.update(SQL_CLEAR_TOKEN, registrationToken);
updateParticipantStatus(routingInfo.studyId(), routingInfo.participantId(), "new", "active");
updateParticipantStatus(routingInfo.studyId(), routingInfo.studyGroupId().orElse(0), routingInfo.participantId(), "new", "active");
return Optional.of(apiId);
}
throw new IllegalStateException("Creating API-Credentials failed!");
}

private void updateParticipantStatus(long studyId, int particpantId, String oldStatus, String newStatus) {
private void updateParticipantStatus(long studyId, int groupId, int participantId, String oldStatus, String newStatus) {
Timestamp start = null;

if ("active".equals(newStatus)) {
start = Timestamp.from(
SchedulerUtils.shiftStartIfObservationAlreadyStarted(Instant.now(), listObservations(studyId, groupId, participantId, true))
);
}

namedTemplate.update(SQL_SET_PARTICIPANT_STATUS,
toParameterSource(studyId, particpantId)
.addValue("start", "active".equals(newStatus) ? Timestamp.valueOf(LocalDateTime.now()) : null)
toParameterSource(studyId, participantId)
.addValue("start", start)
.addValue("oldStatus", oldStatus)
.addValue("newStatus", newStatus)
);
Expand Down Expand Up @@ -288,7 +295,7 @@ public void clearCredentials(String apiId) {
final long studyId = rs.getLong("study_id");
final int participantId = rs.getInt("participant_id");
withdrawConsent(studyId, participantId);
updateParticipantStatus(studyId, participantId,
updateParticipantStatus(studyId, participantId, 0,
"active", "abandoned");
}
);
Expand Down
19 changes: 18 additions & 1 deletion src/main/java/io/redlink/more/data/schedule/SchedulerUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import biweekly.util.Frequency;
import biweekly.util.Recurrence;
import biweekly.util.com.google.ical.compat.javautil.DateIterator;
import io.redlink.more.data.model.Observation;
import io.redlink.more.data.model.scheduler.*;
import org.apache.commons.lang3.tuple.Pair;

Expand Down Expand Up @@ -62,7 +63,7 @@ private static Instant shiftStartIfNecessary(Instant start) {
}

private static Instant toInstant(RelativeDate date, Instant start) {
return ZonedDateTime.ofInstant(start.plus(date.getOffset().getValue() - 1L, date.getOffset().getUnit().toTemporalUnit()), ZoneId.systemDefault())
return ZonedDateTime.ofInstant(start.plus(date.getOffset().getValue() - 1L, date.getOffset().getUnit().toTemporalUnit()), date.getZoneId())
.withHour(date.getHours())
.withMinute(date.getMinutes())
.withSecond(0)
Expand Down Expand Up @@ -97,6 +98,22 @@ public static List<Pair<Instant, Instant>> parseToObservationSchedules(ScheduleE
}
}

public static Instant shiftStartIfObservationAlreadyStarted(Instant start, List<Observation> observations) {
// returns start date, if now event ends before, otherwise start date + 1 day
return observations.stream()
.map(Observation::observationSchedule)
.filter(scheduleEvent -> scheduleEvent.getType().equals(RelativeEvent.TYPE))
.map(r -> ((RelativeEvent) r).getDtend())
.filter(relativeDate -> relativeDate.getOffset().getValue() == 1)
.map(relativeDate -> start.atZone(relativeDate.getZoneId()).withHour(relativeDate.getHours()).withMinute(relativeDate.getMinutes()).withSecond(0).withNano(0).toInstant())
.filter(instant -> {
return instant.isBefore(start.plus(1, ChronoUnit.HOURS));
})
.map(instant -> start.atZone(ZoneId.systemDefault()).withHour(0).withMinute(0).plusDays(1).toInstant())
.findFirst()
.orElse(start);
}

private static long getEventTime(Event event) {
return Duration.between(event.getDateStart(), event.getDateEnd()).getSeconds();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import java.util.Optional;
import java.util.TimeZone;

import static io.redlink.more.data.schedule.SchedulerUtils.shiftStartIfObservationAlreadyStarted;

@Service
public class CalendarService {
private final StudyRepository studyRepository;
Expand All @@ -37,7 +39,10 @@ public Optional<String> getICalendarString(Long studyId) {
iCalEvent.setDateEnd(Date.from(study.endDate().atStartOfDay(TimeZone.getDefault().toZoneId()).toInstant()), false);
ical.addEvent(iCalEvent);

Instant start = study.plannedStartDate().atStartOfDay(ZoneId.systemDefault()).toInstant();
final Instant start = shiftStartIfObservationAlreadyStarted(
study.plannedStartDate().atStartOfDay(ZoneId.systemDefault()).toInstant(),
study.observations()
);

StudyDurationInfo info = studyRepository.getStudyDurationInfo(studyId)
.orElseThrow(() -> new RuntimeException("Cannot create calendar"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/
package io.redlink.more.data.schedule;

import io.redlink.more.data.model.Observation;
import io.redlink.more.data.model.scheduler.*;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.jupiter.api.Assertions;
Expand All @@ -24,7 +25,10 @@
import java.util.Arrays;
import java.util.List;

import static io.redlink.more.data.schedule.SchedulerUtils.shiftStartIfObservationAlreadyStarted;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
public class SchedulerUtilsTest {
Expand Down Expand Up @@ -370,4 +374,32 @@ void testRelativeEventWithRecursionLongRun() {
Assertions.assertEquals(5, events.size());
}

@Test
@DisplayName("Set real start date")
void testRelativeEventShift() {
Instant start = Instant.parse("2023-12-24T11:00:00.000Z");
Observation observationDay1At10 = mock(Observation.class);
Observation observationDay1At12 = mock(Observation.class);
Observation observationDay1At13 = mock(Observation.class);
Observation observationDay2At10 = mock(Observation.class);
ScheduleEvent day1At10 = new RelativeEvent().setDtend(new RelativeDate().setTime("10:00").setTimezone("Europe/Berlin").setOffset(new Duration().setValue(1)));
ScheduleEvent day1At12 = new RelativeEvent().setDtend(new RelativeDate().setTime("12:00").setTimezone("Europe/Berlin").setOffset(new Duration().setValue(1)));
ScheduleEvent day1At13 = new RelativeEvent().setDtend(new RelativeDate().setTime("13:00").setTimezone("Europe/Berlin").setOffset(new Duration().setValue(1)));
ScheduleEvent day2At10 = new RelativeEvent().setDtend(new RelativeDate().setTime("10:00").setTimezone("Europe/Berlin").setOffset(new Duration().setValue(2)));

when(observationDay1At10.observationSchedule()).thenReturn(day1At10);
when(observationDay1At12.observationSchedule()).thenReturn(day1At12);
when(observationDay1At13.observationSchedule()).thenReturn(day1At13);
when(observationDay2At10.observationSchedule()).thenReturn(day2At10);

Instant s1 = shiftStartIfObservationAlreadyStarted(start, List.of(observationDay1At10, observationDay1At12, observationDay2At10));
Assertions.assertNotEquals(s1.toEpochMilli(), start.toEpochMilli());

Instant s2 = shiftStartIfObservationAlreadyStarted(start, List.of(observationDay1At12, observationDay2At10));
Assertions.assertNotEquals(s2.toEpochMilli(), start.toEpochMilli());

Instant s3 = shiftStartIfObservationAlreadyStarted(start, List.of(observationDay1At13, observationDay2At10));
Assertions.assertEquals(s3.toEpochMilli(), start.toEpochMilli());
}

}

0 comments on commit b8024b6

Please sign in to comment.