diff --git a/studymanager/src/main/java/io/redlink/more/studymanager/repository/OccurredObservationRepository.java b/studymanager/src/main/java/io/redlink/more/studymanager/repository/OccurredObservationRepository.java index d9eb53d9..db013cc0 100644 --- a/studymanager/src/main/java/io/redlink/more/studymanager/repository/OccurredObservationRepository.java +++ b/studymanager/src/main/java/io/redlink/more/studymanager/repository/OccurredObservationRepository.java @@ -69,6 +69,7 @@ ON CONFLICT (study_id,observation_id,participant_id,start) DO UPDATE SET "end" = """; private static final String DELETE_ALL = "DELETE FROM occurred_observation"; + private static final String CLEANUP_BY_STUDY_ID ="DELETE FROM occurred_observation WHERE study_id = :study_id" ; private final JdbcTemplate template; private final NamedParameterJdbcTemplate namedTemplate; @@ -202,4 +203,13 @@ private static RowMapper getInstantRowMapper(String field, boolean with return (rs, rowNum) -> withTimezone ? RepositoryUtils.readInstantUTC(rs, field) : RepositoryUtils.readInstant(rs, field); } + + /** + * Cleans all OccurredOccurrencies for the parsed StudyId + * @param studyId + */ + public void cleanup(Long studyId) { + final var params = toParams(studyId); + namedTemplate.update(CLEANUP_BY_STUDY_ID, params); + } } diff --git a/studymanager/src/main/java/io/redlink/more/studymanager/service/OccurredObservationService.java b/studymanager/src/main/java/io/redlink/more/studymanager/service/OccurredObservationService.java index 4f1aa21c..0b4f870d 100644 --- a/studymanager/src/main/java/io/redlink/more/studymanager/service/OccurredObservationService.java +++ b/studymanager/src/main/java/io/redlink/more/studymanager/service/OccurredObservationService.java @@ -9,8 +9,11 @@ package io.redlink.more.studymanager.service; import io.redlink.more.studymanager.model.OccurredObservation; +import io.redlink.more.studymanager.model.Study; +import io.redlink.more.studymanager.model.generator.RandomTokenGenerator; import io.redlink.more.studymanager.repository.OccurredObservationRepository; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.time.Instant; import java.util.*; @@ -89,4 +92,18 @@ public Stream streamActiveOccurredObservationsForParticipan ACTIVE_DATA_STATES); } + /** + * Aligns OccurredObservations with the study status + * @param study + */ + @Transactional + public void alignParticipantsWithStudyState(Study study) { + //NOTE: for CLOSE this should be a NO-OP, as participants and observations are also deleted and + // OccurredObservations use delete cascade on those FK + if (EnumSet.of(Study.Status.DRAFT, Study.Status.CLOSED).contains(study.getStudyState())) { + repository.cleanup(study.getStudyId()); + } + + } + } diff --git a/studymanager/src/main/java/io/redlink/more/studymanager/service/StudyService.java b/studymanager/src/main/java/io/redlink/more/studymanager/service/StudyService.java index d262dc5c..6c0cc16f 100644 --- a/studymanager/src/main/java/io/redlink/more/studymanager/service/StudyService.java +++ b/studymanager/src/main/java/io/redlink/more/studymanager/service/StudyService.java @@ -59,14 +59,24 @@ public class StudyService { private final StudyStateService studyStateService; private final IntegrationService integrationService; private final ElasticService elasticService; + private final OccurredObservationService occurredObservationService; private final PushNotificationService pushNotificationService; private final StudyGroupRepository studyGroupRepository; - public StudyService(StudyRepository studyRepository, StudyAclRepository aclRepository, UserRepository userRepo, - StudyStateService studyStateService, InterventionService interventionService, ObservationService observationService, - ParticipantService participantService, IntegrationService integrationService, ElasticService elasticService, PushNotificationService pushNotificationService, StudyGroupRepository studyGroupRepository) { + public StudyService(StudyRepository studyRepository, + StudyAclRepository aclRepository, + UserRepository userRepo, + StudyStateService studyStateService, + InterventionService interventionService, + ObservationService observationService, + ParticipantService participantService, + IntegrationService integrationService, + ElasticService elasticService, + OccurredObservationService occurredObservationService, + PushNotificationService pushNotificationService, + StudyGroupRepository studyGroupRepository) { this.studyRepository = studyRepository; this.aclRepository = aclRepository; this.userRepo = userRepo; @@ -76,6 +86,7 @@ public StudyService(StudyRepository studyRepository, StudyAclRepository aclRepos this.participantService = participantService; this.integrationService = integrationService; this.elasticService = elasticService; + this.occurredObservationService = occurredObservationService; this.pushNotificationService = pushNotificationService; this.studyGroupRepository = studyGroupRepository; } @@ -135,6 +146,7 @@ public Optional setStatus(Long studyId, Study.Status newState, User user) pushNotificationService.sendStudyStateUpdate(participant, oldState, s.getStudyState()) ); participantService.alignParticipantsWithStudyState(s); + occurredObservationService.alignParticipantsWithStudyState(s); if (s.getStudyState() == Study.Status.DRAFT) { log.info("Study {} transitioned back to {}, dropping collected observation data", study.getStudyId(), s.getStudyState()); elasticService.deleteIndex(s.getStudyId()); diff --git a/studymanager/src/test/java/io/redlink/more/studymanager/repository/OccurredObservationRepositoryTest.java b/studymanager/src/test/java/io/redlink/more/studymanager/repository/OccurredObservationRepositoryTest.java index 353eefc4..e52e3c68 100644 --- a/studymanager/src/test/java/io/redlink/more/studymanager/repository/OccurredObservationRepositoryTest.java +++ b/studymanager/src/test/java/io/redlink/more/studymanager/repository/OccurredObservationRepositoryTest.java @@ -244,6 +244,56 @@ public void testInsertListUpdateDelete() { //validate that searching for a combination with no entry returns null var lastStartTimeNotFound = occurredObservationRepository.getLatestStartTime(studyId, -1, null, null, null); assertThat(lastStartTimeNotFound).isNull(); + + //Test cleanup by StudyId + + Long study2Id = studyRepository.insert(new Study().setContact(new Contact().setPerson("test2").setEmail("test2"))).getStudyId(); + Integer study2GroupId = studyGroupRepository.insert(new StudyGroup().setStudyId(study2Id)).getStudyGroupId(); + Instant study2StartTime = Instant.now().truncatedTo(ChronoUnit.MINUTES).minus(1, ChronoUnit.DAYS); + Instant study2EndTime = study2StartTime.plus(2, ChronoUnit.HOURS); + + Observation study2Observation = observationRepository.insert(new Observation() + .setStudyId(study2Id) + .setType(type) + .setTitle("Accelerometer Observation") + .setStudyGroupId(study2GroupId) + .setProperties(new ObservationProperties(Map.of("testProperty", "testValue"))) + .setSchedule(new Event() + .setDateStart(startTime) + .setDateEnd(endTime) + .setRRule(new RecurrenceRule().setFreq("DAILY").setCount(7))) + .setHidden(false) + .setNoSchedule(false)); + + Participant study2Participant = participantRepository.insert(new Participant() + .setAlias("participant x") + .setStudyGroupId(study2GroupId) + .setStudyId(study2Id) + .setRegistrationToken("TEST456")); + + var study2OccurredObservation = occurredObservationRepository.upsert(new OccurredObservation( + study2Id, + study2Observation.getObservationId(), + study2Participant.getParticipantId(), + study2StartTime, + study2EndTime)); + + //validate that their exists now an occurred observation for the 2nd study + assertThat(occurredObservationRepository.listOccurredObservations( + study2Id, null, null, null, null).count()).isEqualTo(1); + + //not clean all occurred observations for the first study + occurredObservationRepository.cleanup(studyId); + + //assert that all accurred observations are deleted + assertThat(occurredObservationRepository.listOccurredObservations( + studyId, null, null, null, null).count()).isEqualTo(0); + + //assert that the occurred observations for study2 are still present + assertThat(occurredObservationRepository.listOccurredObservations( + study2Id, null, null, null, null).count()).isEqualTo(1); + + } }