private SubjectDosingSummary toSubjectDosingSummary()

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;
    }