Commit a66b360e authored by Ananas Orange's avatar Ananas Orange
Browse files

Merge branch 'develop' into fix/pivotDate

parents ec2d6197 08938179
......@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Objects;
import java.util.Comparator;
import java.util.stream.Collectors;
@Service
......@@ -36,29 +37,44 @@ public class ReportService implements IReportService {
private final IDecodedVisitProducerService processService;
private final long exposureTimeUnit;
@Autowired
public ReportService(
@Value("${clea.conf.retentionDurationInDays}") int retentionDuration,
@Value("${clea.conf.duplicateScanThresholdInSeconds}") long duplicateScanThreshold,
LocationSpecificPartDecoder decoder,
@Value("${clea.conf.exposureTimeUnitInSeconds}") long exposureTimeUnit,
LocationSpecificPartDecoder decoder,
IDecodedVisitProducerService processService) {
this.retentionDurationInDays = retentionDuration;
this.duplicateScanThresholdInSeconds = duplicateScanThreshold;
this.exposureTimeUnit = exposureTimeUnit;
this.decoder = decoder;
this.processService = processService;
}
@Override
public List<DecodedVisit> report(ReportRequest reportRequestVo) {
final VisitsInSameUnitCounter closeScanTimeVisits = new VisitsInSameUnitCounter(exposureTimeUnit);
long validatedPivotDate = this.validatePivotDate(reportRequestVo.getPivotDateAsNtpTimestamp());
List<DecodedVisit> verified = reportRequestVo.getVisits().stream()
.filter(visit -> !this.isOutdated(visit))
.filter(visit -> !this.isFuture(visit))
List<Visit> reportVisits = reportRequestVo.getVisits();
List<DecodedVisit> verified = reportVisits.stream()
.filter(visit -> !isOutdated(visit))
.filter(visit -> !isFuture(visit))
.map(it -> this.decode(it, validatedPivotDate))
.filter(Objects::nonNull)
.collect(Collectors.toList());
List<DecodedVisit> pruned = this.pruneDuplicates(verified);
processService.produce(pruned);
// evaluate produced visits and count close scan time visits
pruned.stream().sorted(Comparator.comparing((DecodedVisit::getQrCodeScanTime)))
.forEach(closeScanTimeVisits::incrementIfScannedInSameTimeUnitThanLastScanTime);
log.info("BATCH_REPORT {}#{}#{}#{}#{}", reportVisits.size(), reportVisits.size() - pruned.size(),
pruned.stream().filter(DecodedVisit::isBackward).count(),
pruned.stream().filter(visit -> !visit.isBackward()).count(),
closeScanTimeVisits.getCount());
return pruned;
}
......
package fr.gouv.clea.ws.service.impl;
import fr.gouv.clea.ws.model.DecodedVisit;
import lombok.Data;
import java.time.Duration;
import java.time.Instant;
@Data
public class VisitsInSameUnitCounter {
private final long exposureTimeUnit;
private int count;
private Instant lastScanTime = null;
public int incrementScanInSameUnitCount() {
return ++count;
}
DecodedVisit incrementIfScannedInSameTimeUnitThanLastScanTime(final DecodedVisit decodedVisit) {
final Instant qrCodeScanTime = decodedVisit.getQrCodeScanTime();
if (this.getLastScanTime() == null) {
this.setLastScanTime(qrCodeScanTime);
} else if (visitIsScannedAfterLessThanExposureTime(qrCodeScanTime)) {
this.incrementScanInSameUnitCount();
}
return decodedVisit;
}
private boolean visitIsScannedAfterLessThanExposureTime(Instant qrCodeScanTime) {
return Duration.between(this.getLastScanTime(), qrCodeScanTime).getSeconds() < exposureTimeUnit;
}
}
clea:
conf:
exposureTimeUnitInSeconds: 1800
duplicateScanThresholdInSeconds: 10800
retentionDurationInDays: 14
security:
......
......@@ -31,9 +31,10 @@ class ReportServiceTest {
private final int retentionDuration = 14;
private final long duplicateScanThresholdInSeconds = 10800L;
private final long exposureTimeUnit = 1800L;
private final LocationSpecificPartDecoder decoder = mock(LocationSpecificPartDecoder.class);
private final IDecodedVisitProducerService processService = mock(IDecodedVisitProducerService.class);
private final IReportService reportService = new ReportService(retentionDuration, duplicateScanThresholdInSeconds, decoder, processService);
private final IReportService reportService = new ReportService(retentionDuration, duplicateScanThresholdInSeconds, exposureTimeUnit, decoder, processService);
private Instant now;
@BeforeEach
......
package fr.gouv.clea.ws.service.impl;
import fr.gouv.clea.ws.model.DecodedVisit;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class VisitsInSameCounterTest {
@Mock
private DecodedVisit decodedVisit;
private static final Duration exposureTimeUnit = Duration.ofSeconds(1800);
@Test
void incrementIfScannedInSameTimeUnitThanLastScanTime_increments_counter_when_difference_between_scan_time_is_lower_than_exposureTimeUnit() {
final VisitsInSameUnitCounter counter = new VisitsInSameUnitCounter(exposureTimeUnit.getSeconds());
final int initialCount = 0;
counter.setCount(initialCount);
final Instant lastScanTime = Instant.now().minus(exposureTimeUnit.minus(1, ChronoUnit.SECONDS));
// 1s after
final Instant currentVisitScanTime = Instant.now();
counter.setLastScanTime(lastScanTime);
when(decodedVisit.getQrCodeScanTime()).thenReturn(currentVisitScanTime);
counter.incrementIfScannedInSameTimeUnitThanLastScanTime(decodedVisit);
Assertions.assertThat(counter.getCount()).isEqualTo(initialCount + 1);
}
@Test
void incrementIfScannedInSameTimeUnitThanLastScanTime_does_not_increment_counter_when_difference_between_scan_time_is_greater_than_exposureTimeUnit() {
final VisitsInSameUnitCounter counter = new VisitsInSameUnitCounter(exposureTimeUnit.getSeconds());
final int initialCount = 0;
counter.setCount(initialCount);
final Instant lastScanTime = Instant.now().minus(exposureTimeUnit.plus(1, ChronoUnit.SECONDS));
final Instant currentVisitScanTime = Instant.now();
counter.setLastScanTime(lastScanTime);
when(decodedVisit.getQrCodeScanTime()).thenReturn(currentVisitScanTime);
counter.incrementIfScannedInSameTimeUnitThanLastScanTime(decodedVisit);
Assertions.assertThat(counter.getCount()).isEqualTo(initialCount);
}
@Test
void incrementIfScannedInSameTimeUnitThanLastScanTime_does_not_increment_counter_when_no_lastScanTime_has_already_been_registered() {
final VisitsInSameUnitCounter counter = new VisitsInSameUnitCounter(exposureTimeUnit.getSeconds());
final int initialCount = 0;
counter.setCount(initialCount);
final Instant currentVisitScanTime = Instant.now();
when(decodedVisit.getQrCodeScanTime()).thenReturn(currentVisitScanTime);
counter.incrementIfScannedInSameTimeUnitThanLastScanTime(decodedVisit);
Assertions.assertThat(counter.getLastScanTime()).isEqualTo(currentVisitScanTime);
Assertions.assertThat(counter.getCount()).isEqualTo(initialCount);
}
}
clea:
conf:
exposureTimeUnitInSeconds: 1800
duplicateScanThresholdInSeconds: 10800
retentionDurationInDays: 14
security:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment