From b55ba0d9cbbfa8f1e9b612dc2511164e5cda33e2 Mon Sep 17 00:00:00 2001 From: Jan Cortiel Date: Thu, 12 Dec 2024 14:21:11 +0100 Subject: [PATCH] #350: Refactor schedule handling and streamline interval processing Replaced stream-based processing with object-based handling for schedules to improve readability and maintainability. Removed unused imports and methods, simplifying the codebase. Added error handling for unsupported event types and encapsulated relative event scheduling logic into a helper method. --- .../more/data/repository/StudyRepository.java | 29 +++----- .../more/data/service/ExternalService.java | 66 ++++++++++--------- 2 files changed, 44 insertions(+), 51 deletions(-) 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)); + } }