Skip to content

Commit

Permalink
Merge pull request #95 from redlink-gmbh/FEATURE-Relative_Study_Start
Browse files Browse the repository at this point in the history
Feature relative study start
  • Loading branch information
alireza-dhp authored Nov 28, 2023
2 parents 09d5e36 + 042b2f5 commit 5924d60
Show file tree
Hide file tree
Showing 26 changed files with 834 additions and 93 deletions.
14 changes: 10 additions & 4 deletions .github/workflows/compile-test.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
name: Test and Compile
on:
workflow_dispatch:
inputs:
dockerTag:
description: If set, docker img is built and tagged accordingly
required: false
push:

jobs:
Expand Down Expand Up @@ -37,7 +41,7 @@ jobs:
Build-and-Deploy:
name: "Build and Push Docker Image"
runs-on: ubuntu-latest
if: github.ref_name == 'main'
if: github.ref_name == 'main' || github.event.inputs.dockerTag != ''
needs:
- Compile-and-Test
steps:
Expand All @@ -48,15 +52,17 @@ jobs:
distribution: 'temurin'
java-version: 17
- name: Build JIB container and publish to GitHub Packages
run: ./mvnw -B -U
run:
TAG=${{github.event.inputs.dockerTag}} &&
./mvnw -B -U
--no-transfer-progress
clean verify jib:build
-Drevision=${{github.run_number}}
-Dchangelist=
-Dsha1=.${GITHUB_SHA:0:7}
-Dquick
-Ddocker.namespace=${DOCKER_NAMESPACE,,}
-Djib.to.tags=latest
-Djib.to.tags=${TAG:=latest}
-Djib.to.auth.username=${{ github.actor }}
-Djib.to.auth.password=${{ secrets.GITHUB_TOKEN }}
env:
Expand All @@ -70,4 +76,4 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: Event File
path: ${{ github.event_path }}
path: ${{ github.event_path }}
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<!-- Cache -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>

<!-- Persistence -->
<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.redlink.more.data.configuration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

@Configuration
@EnableCaching
@EnableScheduling
public class CachingConfiguration {

public static final String OBSERVATION_ENDINGS = "observationEndings";
private static final Logger LOGGER = LoggerFactory.getLogger(CachingConfiguration.class);
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager(OBSERVATION_ENDINGS);
}

@CacheEvict(allEntries = true, value = {OBSERVATION_ENDINGS})
@Scheduled(fixedDelay = 60 * 60 * 1000 , initialDelay = 5000)
public void reportCacheEvict() {
LOGGER.info("Flush Cache");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public SecurityFilterChain filterChain(HttpSecurity http,
//External Data Gateway
req.requestMatchers("/api/v1/external/bulk")
.permitAll();
req.requestMatchers("/api/v1/calendar/studies/*/calendar.ics")
.permitAll();
// all other apis require credentials
req.requestMatchers("/api/v1/**")
.authenticated();
Expand Down Expand Up @@ -105,4 +107,4 @@ protected RequestRejectedHandler requestRejectedHandler() {
return new HttpStatusRequestRejectedHandler(HttpStatus.I_AM_A_TEAPOT.value());
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.redlink.more.data.controller;

import io.redlink.more.data.api.app.v1.webservices.CalendarApi;
import io.redlink.more.data.service.CalendarService;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Controller
@RestController
@RequestMapping(value = "/api/v1", produces = MediaType.APPLICATION_JSON_VALUE)
public class CalendarApiV1Controller implements CalendarApi {

private final CalendarService calendarService;

public CalendarApiV1Controller(CalendarService calendarService) {
this.calendarService = calendarService;
}

@Override
public ResponseEntity<String> getStudyCalendar(Long studyId) {
return this.calendarService.getICalendarString(studyId)
.map(ResponseEntity::ok)
.orElseThrow(RuntimeException::new);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
import io.redlink.more.data.api.app.v1.model.ExternalDataDTO;
import io.redlink.more.data.api.app.v1.webservices.ExternalDataApi;
import io.redlink.more.data.controller.transformer.DataTransformer;
import io.redlink.more.data.exception.BadRequestException;
import io.redlink.more.data.model.ApiRoutingInfo;
import io.redlink.more.data.model.RoutingInfo;
import io.redlink.more.data.model.scheduler.Interval;
import io.redlink.more.data.service.ElasticService;
import io.redlink.more.data.service.ExternalService;
import io.redlink.more.data.util.LoggingUtils;
Expand Down Expand Up @@ -63,10 +65,14 @@ public ResponseEntity<Void> storeExternalBulk(String moreApiToken, EndpointDataB
throw new AccessDeniedException("Invalid token");
}

externalService.validateTimeFrame(studyId, observationId,
endpointDataBulkDTO.getDataPoints().stream().map(datapoint ->
datapoint.getTimestamp().toInstant()
).toList());
Interval interval = externalService.getIntervalForObservation(studyId, observationId, participantId);

endpointDataBulkDTO.getDataPoints().stream()
.map(datapoint -> datapoint.getTimestamp().toInstant())
.map(timestamp -> timestamp.isBefore(interval.getStart()) || timestamp.isAfter(interval.getEnd()))
.filter(v -> v)
.findFirst()
.orElseThrow(BadRequestException::TimeFrame);

final RoutingInfo routingInfo = new RoutingInfo(
externalService.validateRoutingInfo(apiRoutingInfo.get(), participantId),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
import io.redlink.more.data.model.Observation;
import io.redlink.more.data.model.SimpleParticipant;
import io.redlink.more.data.model.Study;
import io.redlink.more.data.schedule.ICalendarParser;
import io.redlink.more.data.schedule.SchedulerUtils;
import org.apache.commons.lang3.tuple.Pair;

import java.time.Instant;
import java.time.LocalDateTime;
import java.util.List;

public final class StudyTransformer {
Expand All @@ -30,7 +31,7 @@ public static StudyDTO toDTO(Study study) {
.contact(toDTO(study.contact()))
.start(study.startDate())
.end(study.endDate())
.observations(toDTO(study.observations()))
.observations(toDTO(study.observations(), study.participant().start(), study.participant().end()))
.version(BaseTransformers.toVersionTag(study.modified()))
;
}
Expand All @@ -53,11 +54,11 @@ public static ContactInfoDTO toDTO(Contact contact) {
;
}

public static List<ObservationDTO> toDTO(List<Observation> observations) {
return observations.stream().map(StudyTransformer::toDTO).toList();
public static List<ObservationDTO> toDTO(List<Observation> observations, Instant start, Instant end) {
return observations.stream().map(o -> StudyTransformer.toDTO(o, start, end)).toList();
}

public static ObservationDTO toDTO(Observation observation) {
public static ObservationDTO toDTO(Observation observation, Instant start, Instant end) {
ObservationDTO dto = new ObservationDTO()
.observationId(String.valueOf(observation.observationId()))
.observationType(observation.type())
Expand All @@ -68,9 +69,9 @@ public static ObservationDTO toDTO(Observation observation) {
.hidden(observation.hidden())
.noSchedule(observation.noSchedule())
;
if(observation.observationSchedule() != null) {
dto.schedule(ICalendarParser
.parseToObservationSchedules(observation.observationSchedule())
if(observation.observationSchedule() != null && start != null) {
dto.schedule(SchedulerUtils
.parseToObservationSchedules(observation.observationSchedule(), start, end)
.stream()
.map(StudyTransformer::toObservationScheduleDTO)
.toList());
Expand Down
7 changes: 5 additions & 2 deletions src/main/java/io/redlink/more/data/model/Observation.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,26 @@
*/
package io.redlink.more.data.model;

import io.redlink.more.data.model.scheduler.ScheduleEvent;

import java.time.Instant;

public record Observation(
int observationId,
Integer groupId,
String title,
String type,
String participantInfo,
Object properties,
Event observationSchedule,
ScheduleEvent observationSchedule,
Instant created,
Instant modified,
boolean hidden,
boolean noSchedule
) {
public Observation withProperties(Object properties) {
return new Observation(
observationId, title, type, participantInfo, properties, observationSchedule, created, modified, hidden, noSchedule
observationId, groupId, title, type, participantInfo, properties, observationSchedule, created, modified, hidden, noSchedule
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@
*/
package io.redlink.more.data.model;

import java.time.Instant;

public record SimpleParticipant(
int id,
String alias
String alias,
Instant start,
Instant end
) {
}
3 changes: 1 addition & 2 deletions src/main/java/io/redlink/more/data/model/Study.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
*/
package io.redlink.more.data.model;

import io.redlink.more.data.api.app.v1.model.StudyDTO;

import java.time.Instant;
import java.time.LocalDate;
import java.util.List;
Expand All @@ -24,6 +22,7 @@ public record Study(
String consentInfo,
Contact contact,
LocalDate startDate,
LocalDate plannedStartDate,
LocalDate endDate,
List<Observation> observations,
Instant created,
Expand Down
71 changes: 71 additions & 0 deletions src/main/java/io/redlink/more/data/model/StudyDurationInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package io.redlink.more.data.model;

import io.redlink.more.data.model.scheduler.Duration;
import org.apache.commons.lang3.tuple.Pair;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;

public class StudyDurationInfo{
private LocalDate endDate;
private LocalDate startDate;
private Duration duration;
private final List<Pair<Integer, Duration>> groupDurations = new ArrayList<>();

public LocalDate getEndDate() {
return endDate;
}

public StudyDurationInfo setEndDate(LocalDate endDate) {
this.endDate = endDate;
return this;
}

public LocalDate getStartDate() {
return startDate;
}

public StudyDurationInfo setStartDate(LocalDate startDate) {
this.startDate = startDate;
return this;
}

public Duration getDuration() {
return duration;
}

public StudyDurationInfo setDuration(Duration duration) {
this.duration = duration;
return this;
}

public List<Pair<Integer, Duration>> getGroupDurations() {
return groupDurations;
}

public StudyDurationInfo addGroupDuration(Pair<Integer, Duration> gd) {
this.groupDurations.add(gd);
return this;
}

public Duration getDurationFor(Integer group) {
if(group == null) {
return getDurationFallback();
} else {
return this.groupDurations.stream()
.filter(gd -> gd.getLeft().equals(group))
.findFirst()
.map(Pair::getRight)
.orElse(getDurationFallback());
}
}

private Duration getDurationFallback() {
if(this.duration != null) {
return duration;
} else {
return new Duration().setUnit(Duration.Unit.DAY).setValue((int) startDate.until(endDate, ChronoUnit.DAYS));
}
}
}
Loading

0 comments on commit 5924d60

Please sign in to comment.