diff --git a/src/main/java/io/redlink/more/data/repository/StudyRepository.java b/src/main/java/io/redlink/more/data/repository/StudyRepository.java index 2ec8f0c..e05efe5 100644 --- a/src/main/java/io/redlink/more/data/repository/StudyRepository.java +++ b/src/main/java/io/redlink/more/data/repository/StudyRepository.java @@ -5,8 +5,6 @@ import io.redlink.more.data.exception.BadRequestException; import io.redlink.more.data.model.*; -import io.redlink.more.data.model.scheduler.Interval; -import io.redlink.more.data.model.scheduler.RelativeEvent; import io.redlink.more.data.model.scheduler.ScheduleEvent; import io.redlink.more.data.schedule.SchedulerUtils; import org.apache.commons.lang3.tuple.Pair; @@ -27,8 +25,6 @@ import java.util.Optional; import java.util.OptionalInt; import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.Stream; import static io.redlink.more.data.repository.DbUtils.toInstant; import static io.redlink.more.data.repository.DbUtils.toLocalDate; @@ -169,8 +165,8 @@ public Optional getApiRoutingInfo(Long studyId, Integer observat } } - public Stream getObservationSchedule(Long studyId, Integer observationId) { - return jdbcTemplate.queryForStream( + public ScheduleEvent getObservationSchedule(Long studyId, Integer observationId) { + return jdbcTemplate.queryForObject( GET_OBSERVATION_SCHEDULE, getObservationScheduleRowMapper(), studyId, observationId @@ -435,20 +431,13 @@ private static MapSqlParameterSource toParameterSource(long studyId, int partici } - public List getIntervals(Long studyId, Integer participantId, RelativeEvent event) { - try (var stream = jdbcTemplate.queryForStream( - GET_PARTICIPANT_INFO_AND_START_DURATION_END_FOR_STUDY_AND_PARTICIPANT, - (rs, rowNum) -> { - Instant start = rs.getTimestamp("start").toInstant(); - return Interval.fromRanges(SchedulerUtils.parseToObservationSchedulesForRelativeEvent(event, start)); - }, - studyId, participantId - )) { - var intervalList = stream - .flatMap(List::stream) - .collect(Collectors.toList()); - stream.close(); - return intervalList; + public Instant getStudyStartFor(Long studyId, Integer participantId) { + try { + return jdbcTemplate.queryForObject( + GET_PARTICIPANT_INFO_AND_START_DURATION_END_FOR_STUDY_AND_PARTICIPANT, + (rs, rowNum) -> rs.getTimestamp("start").toInstant(), + studyId, participantId + ); } catch (Exception e) { throw new BadRequestException("Failed to retrieve intervals for studyId: " + studyId + " and participantId: " + participantId); } diff --git a/src/main/java/io/redlink/more/data/service/ExternalService.java b/src/main/java/io/redlink/more/data/service/ExternalService.java index 71b04ba..78f8cac 100644 --- a/src/main/java/io/redlink/more/data/service/ExternalService.java +++ b/src/main/java/io/redlink/more/data/service/ExternalService.java @@ -22,16 +22,14 @@ import io.redlink.more.data.model.scheduler.RelativeEvent; import io.redlink.more.data.model.scheduler.ScheduleEvent; import io.redlink.more.data.repository.StudyRepository; +import io.redlink.more.data.schedule.SchedulerUtils; import org.springframework.cache.annotation.Cacheable; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; -import java.util.Base64; -import java.util.List; -import java.util.Optional; -import java.util.OptionalInt; -import java.util.stream.Stream; +import java.time.Instant; +import java.util.*; @Service public class ExternalService { @@ -84,37 +82,43 @@ public RoutingInfo validateAndCreateRoutingInfo(ApiRoutingInfo apiRoutingInfo, I @Cacheable(CachingConfiguration.OBSERVATION_ENDINGS) public void assertTimestampsInBulk(Long studyId, Integer observationId, Integer participantId, EndpointDataBulkDTO dataBulkDTO) { - Stream scheduleEvents = Optional.ofNullable(repository.getObservationSchedule(studyId, observationId)) - .orElseThrow(() -> BadRequestException.NotFound(studyId, observationId)); - - List intervalList = scheduleEvents - .flatMap(scheduleEvent -> { - if (scheduleEvent instanceof Event) { - return Stream.of(Interval.from((Event) scheduleEvent)); - } else if (scheduleEvent instanceof RelativeEvent) { - return repository.getIntervals(studyId, participantId, (RelativeEvent) scheduleEvent).stream(); - } else { - throw new BadRequestException("Unsupported ScheduleEvent type: " + scheduleEvent.getClass()); - } - }) - .toList(); - scheduleEvents.close(); - - if (intervalList.isEmpty()) { - throw BadRequestException.NotFound(studyId, observationId); - } + try { + ScheduleEvent scheduleEvent = repository.getObservationSchedule(studyId, observationId); + + List intervalList = new ArrayList<>(); + if (scheduleEvent instanceof Event) { + intervalList.add(Interval.from((Event) scheduleEvent)); + } else if (scheduleEvent instanceof RelativeEvent) { + Instant studyStart = repository.getStudyStartFor(studyId, participantId); + intervalList.addAll(createSchedulesFromRelativeEvent((RelativeEvent) scheduleEvent, studyStart)); + } else { + throw new BadRequestException("Unsupported ScheduleEvent type: " + scheduleEvent.getClass()); + } + + if (intervalList.isEmpty()) { + throw BadRequestException.NotFound(studyId, observationId); + } - boolean allValid = dataBulkDTO.getDataPoints().stream() - .map(ExternalDataDTO::getTimestamp) - .allMatch(timestamp -> intervalList.stream() - .anyMatch(interval -> interval.contains(timestamp)) - ); - if (!allValid) { - throw TimeFrameException.InvalidDataPointInterval(dataBulkDTO.getParticipantId(), intervalList); + boolean allValid = dataBulkDTO.getDataPoints().stream() + .map(ExternalDataDTO::getTimestamp) + .allMatch(timestamp -> intervalList.stream() + .anyMatch(interval -> interval.contains(timestamp)) + ); + if (!allValid) { + throw TimeFrameException.InvalidDataPointInterval(dataBulkDTO.getParticipantId(), intervalList); + } + } catch (BadRequestException e) { + throw e; + } catch (Exception e) { + throw BadRequestException.NotFound(studyId, observationId); } } public List listParticipants(Long studyId, OptionalInt studyGroupId) { return repository.listParticipants(studyId, studyGroupId); } + + private List createSchedulesFromRelativeEvent(RelativeEvent event, Instant start) { + return Interval.fromRanges(SchedulerUtils.parseToObservationSchedulesForRelativeEvent(event, start)); + } }