in clns-acuity-vahub/vahub-model/src/main/java/com/acuity/visualisations/rawdatamodel/service/timeline/DrugDoseTimelineService.java [347:448]
private SubjectDosingSummary toSubjectDosingSummary(
Subject subject,
List<DrugDose> doses,
DayZeroType dayZeroType,
String dayZeroOption,
Map<String, DoseAndFrequency> maxDoses,
Map<String, List<Date>> discontinuationDates,
boolean isDosingSummariesByDrug
) {
List<Range<Date>> ranges = isDosingSummariesByDrug
? getRangesForSingleDrug(doses)
: getRangesForMultipleDrugs(doses);
if (log.isTraceEnabled()) {
log.trace("Building events for periods: {}",
ranges.stream()
.map(range -> Arrays.asList(
requireNonNull(extractDaysHours(subject, dayZeroType, dayZeroOption,
range.lowerEndpoint())).getDayHourAsString(),
requireNonNull(extractDaysHours(subject, dayZeroType, dayZeroOption,
range.upperEndpoint())).getDayHourAsString()
))
.collect(toList()));
}
List<DosingSummaryEvent> events = StreamEx.of(ranges)
.mapToEntry(range -> new DosingSummaryEvent())
.peekKeyValue((range, event) -> {
event.setStart(extractDaysHours(subject, dayZeroType, dayZeroOption, range.lowerEndpoint()));
event.setEnd(extractDaysHours(subject, dayZeroType, dayZeroOption, range.upperEndpoint()));
})
.peekKeyValue((range, event) ->
event.setDrugDoses(doses.stream()
.filter(d -> range.isConnected(open(d.getStartDate(), d.getEndDate())))
.map(DrugDose::getEvent)
.sorted(Comparator.comparing(DrugDoseRaw::getDrugName)
.thenComparing(Comparator.comparing(DrugDoseRaw::getDose).reversed()))
.map(this::toDoseAndFrequency)
.distinct()
.collect(toList())))
.peekValues(event -> {
Map<String, DoseAndFrequency> activeDoseAndFrequencies = event.getDrugDoses().stream()
.filter(d -> Double.compare(d.getDose(), 0) > 0)
.collect(toMap(DoseAndFrequency::getDrug, Function.identity(),
(left, right) -> left.getDose() < right.getDose() ? right : left));
event.setPeriodType(activeDoseAndFrequencies.isEmpty() ? INACTIVE : ACTIVE);
event.setPercentChange(
new PercentChange(
calculate(maxDoses, activeDoseAndFrequencies, DoseAndFrequency::getDosePerDay),
calculate(maxDoses, activeDoseAndFrequencies, DoseAndFrequency::getDosePerAdmin)));
})
.peekKeyValue((range, event) -> {
event.setSubsequentPeriodType(INACTIVE);
//The end of dosing event should be calculated as earliest non-null date after event start date stored in db,
//but for inactive periods there is also calculated event start date which is the end date of previous dosing period.
//Correct end date should be calculated based on stored value not calculated
//Here actual start date (stored value) is taken and then earliest date is calculated based on this value
final DrugDose dose = doses.stream().filter(d -> range.isConnected(open(d.getStartDate(), d.getEndDate()))).findFirst().orElse(null);
if (dose != null) {
Range<Date> openClosed = Range.openClosed(dose.getStartDate(), dose.getEndDate());
if (discontinuationDates != null
&& discontinuationDates.values().stream().flatMap(Collection::stream).anyMatch(openClosed::contains)) {
Date lastDate = isDosingSummariesByDrug ? new TreeSet<>(discontinuationDates.values().stream()
.flatMap(Collection::stream).collect(toList())).ceiling(openClosed.lowerEndpoint()) : range.upperEndpoint();
if (doses.stream().noneMatch(d -> Objects.equals(lastDate, d.getStartDate()) && d.getEvent().getDose() > 0)) {
event.setSubsequentPeriodType(DISCONTINUED);
event.setEnd(extractDaysHours(subject, dayZeroType, dayZeroOption, lastDate));
}
}
}
})
.values()
.peek(event -> {
if (log.isTraceEnabled()) {
log.trace("Making event [type={}, start={}, end={}, drugs={}]", event.getPeriodType(),
requireNonNull(event.getStart()).getDayHourAsString(), requireNonNull(event.getEnd()).getDayHourAsString(),
event.getDrugDoses().stream().map(DoseAndFrequency::getDrug).collect(toList()));
}
})
.toList();
SubjectDosingSummary summary = new SubjectDosingSummary();
summary.setSubjectId(subject.getSubjectId());
summary.setSubject(subject.getSubjectCode());
summary.setEvents(events);
if (CollectionUtils.isNotEmpty(events)) {
DosingSummaryEvent lastEvent = events.get(events.size() - 1);
doses.stream()
.filter(d -> Objects.equals(d.getEndDate(), lastEvent.getEnd().getDate()))
.filter(d -> ONGOING.getDbValue().equals(d.getEvent().getSubsequentPeriodType())
|| lastEvent.getEnd().getDate().equals(d.getSubject().getLastEtlDate()))
.findAny()
.ifPresent(d -> lastEvent.setOngoing(true));
summary.setOngoing(lastEvent.isOngoing());
}
return summary;
}