Skip to content

Commit

Permalink
#350: Refactor schedule handling and streamline interval processing
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
janoliver20 committed Dec 12, 2024
1 parent db40020 commit b55ba0d
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 51 deletions.
29 changes: 9 additions & 20 deletions src/main/java/io/redlink/more/data/repository/StudyRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -169,8 +165,8 @@ public Optional<ApiRoutingInfo> getApiRoutingInfo(Long studyId, Integer observat
}
}

public Stream<ScheduleEvent> getObservationSchedule(Long studyId, Integer observationId) {
return jdbcTemplate.queryForStream(
public ScheduleEvent getObservationSchedule(Long studyId, Integer observationId) {
return jdbcTemplate.queryForObject(
GET_OBSERVATION_SCHEDULE,
getObservationScheduleRowMapper(),
studyId, observationId
Expand Down Expand Up @@ -435,20 +431,13 @@ private static MapSqlParameterSource toParameterSource(long studyId, int partici
}


public List<Interval> 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);
}
Expand Down
66 changes: 35 additions & 31 deletions src/main/java/io/redlink/more/data/service/ExternalService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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<ScheduleEvent> scheduleEvents = Optional.ofNullable(repository.getObservationSchedule(studyId, observationId))
.orElseThrow(() -> BadRequestException.NotFound(studyId, observationId));

List<Interval> 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<Interval> 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<Participant> listParticipants(Long studyId, OptionalInt studyGroupId) {
return repository.listParticipants(studyId, studyGroupId);
}

private List<Interval> createSchedulesFromRelativeEvent(RelativeEvent event, Instant start) {
return Interval.fromRanges(SchedulerUtils.parseToObservationSchedulesForRelativeEvent(event, start));
}
}

0 comments on commit b55ba0d

Please sign in to comment.