Skip to content

Commit

Permalink
Merge pull request #734 from lantanagroup/remove-imrs-from-context
Browse files Browse the repository at this point in the history
Remove IMRs from report/measure context
  • Loading branch information
smailliwcs committed Mar 22, 2024
2 parents db6ceb9 + 7d07583 commit e73c421
Show file tree
Hide file tree
Showing 10 changed files with 106 additions and 70 deletions.
11 changes: 6 additions & 5 deletions api/src/main/java/com/lantanagroup/link/api/ReportGenerator.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.lantanagroup.link.api;

import com.lantanagroup.link.Constants;
import com.lantanagroup.link.FhirHelper;
import com.lantanagroup.link.IReportAggregator;
import com.lantanagroup.link.ReportIdHelper;
import com.lantanagroup.link.config.api.ApiConfig;
Expand Down Expand Up @@ -73,15 +74,15 @@ public void generate(QueryPhase queryPhase) throws ExecutionException, Interrupt
}
try {
MeasureReport measureReport = generate(measureServiceWrapper, patient);
synchronized (this) {
measureContext.getPatientReportsByPatientId().put(patient.getId(), measureReport);
if (queryPhase == QueryPhase.INITIAL && FhirHelper.hasNonzeroPopulationCount(measureReport)) {
measureContext.getSupplementalPatientsOfInterest().add(patient);
}
} catch (Exception e) {
logger.error("Error generating measure report for patient {}", patient.getId(), e);
} finally {
int completed = progress.incrementAndGet();
double percent = Math.round((completed * 100.0) / pois.size());
logger.info("Progress ({}%) for report {} is {} of {}", String.format("%.2f", percent), reportContext.getMasterIdentifierValue(), completed, pois.size());
double percent = (completed * 100.0) / pois.size();
logger.info("Progress ({}%) for report {} is {} of {}", String.format("%.1f", percent), reportContext.getMasterIdentifierValue(), completed, pois.size());
}
}))
.get();
Expand Down Expand Up @@ -111,7 +112,7 @@ private MeasureReport generate(MeasureServiceWrapper measureServiceWrapper, Pati
}

public void aggregate() throws ParseException {
MeasureReport masterMeasureReport = this.reportAggregator.generate(this.criteria, this.measureContext);
MeasureReport masterMeasureReport = this.reportAggregator.generate(this.tenantService, this.criteria, this.measureContext);
this.measureContext.setMeasureReport(masterMeasureReport);

Aggregate aggregateReport = new Aggregate();
Expand Down
22 changes: 17 additions & 5 deletions core/src/main/java/com/lantanagroup/link/GenericAggregator.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.lantanagroup.link;

import com.lantanagroup.link.config.api.ApiConfig;
import com.lantanagroup.link.db.TenantService;
import com.lantanagroup.link.db.model.PatientMeasureReport;
import com.lantanagroup.link.model.PatientOfInterestModel;
import com.lantanagroup.link.model.ReportContext;
import com.lantanagroup.link.model.ReportCriteria;
import org.apache.commons.lang3.StringUtils;
Expand All @@ -11,7 +14,6 @@
import org.springframework.beans.factory.annotation.Autowired;

import java.text.ParseException;
import java.util.Collection;
import java.util.Optional;

public abstract class GenericAggregator implements IReportAggregator {
Expand All @@ -20,10 +22,12 @@ public abstract class GenericAggregator implements IReportAggregator {
@Autowired
private ApiConfig config;

protected abstract void aggregatePatientReports(MeasureReport masterMeasureReport, Collection<MeasureReport> measureReports);
protected abstract void aggregatePatientReport(MeasureReport masterMeasureReport, MeasureReport measureReport);

protected abstract void finishAggregation(MeasureReport masterMeasureReport);

@Override
public MeasureReport generate(ReportCriteria criteria, ReportContext.MeasureContext measureContext) throws ParseException {
public MeasureReport generate(TenantService tenantService, ReportCriteria criteria, ReportContext.MeasureContext measureContext) throws ParseException {
// Create the master measure report
MeasureReport masterMeasureReport = new MeasureReport();
masterMeasureReport.setId(measureContext.getReportId());
Expand All @@ -39,8 +43,16 @@ public MeasureReport generate(ReportCriteria criteria, ReportContext.MeasureCont
masterMeasureReport.setMeasure(measureContext.getMeasure().getUrl());
}

// TODO: Swap the order of aggregatePatientReports and createGroupsFromMeasure?
this.aggregatePatientReports(masterMeasureReport, measureContext.getPatientReports());
for (PatientOfInterestModel poi : measureContext.getPatientsOfInterest()) {
String pmrId = ReportIdHelper.getPatientMeasureReportId(measureContext.getReportId(), poi.getId());
PatientMeasureReport pmr = tenantService.getPatientMeasureReport(pmrId);
if (pmr == null) {
logger.warn("Patient measure report not found in database: {}", pmrId);
continue;
}
this.aggregatePatientReport(masterMeasureReport, pmr.getMeasureReport());
}
this.finishAggregation(masterMeasureReport);

this.createGroupsFromMeasure(masterMeasureReport, measureContext);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.lantanagroup.link;

import com.lantanagroup.link.db.TenantService;
import com.lantanagroup.link.model.ReportContext;
import com.lantanagroup.link.model.ReportCriteria;
import org.hl7.fhir.r4.model.MeasureReport;

import java.text.ParseException;

public interface IReportAggregator {
MeasureReport generate(ReportCriteria criteria, ReportContext.MeasureContext measureContext) throws ParseException;
MeasureReport generate(TenantService tenantService, ReportCriteria criteria, ReportContext.MeasureContext measureContext) throws ParseException;
}
57 changes: 27 additions & 30 deletions core/src/main/java/com/lantanagroup/link/model/ReportContext.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.lantanagroup.link.model;

import ca.uhn.fhir.rest.client.api.IGenericClient;
import com.lantanagroup.link.FhirHelper;
import com.lantanagroup.link.auth.LinkCredentials;
import com.lantanagroup.link.db.model.PatientList;
import com.lantanagroup.link.db.model.tenant.QueryPlan;
Expand All @@ -19,15 +18,15 @@
@Getter
@Setter
public class ReportContext {
private HttpServletRequest request;
private LinkCredentials user;
private String masterIdentifierValue;
private List<PatientList> patientLists = new ArrayList<>();
private List<PatientOfInterestModel> patientsOfInterest = new ArrayList<>();
private List<MeasureContext> measureContexts = new ArrayList<>();
private QueryPlan queryPlan;
private IGenericClient client;
private List<String> debugPatients = new ArrayList<>();
private volatile HttpServletRequest request;
private volatile LinkCredentials user;
private volatile String masterIdentifierValue;
private volatile List<PatientList> patientLists = new ArrayList<>();
private volatile List<PatientOfInterestModel> initialPatientsOfInterest = new ArrayList<>();
private volatile List<MeasureContext> measureContexts = new ArrayList<>();
private volatile QueryPlan queryPlan;
private volatile IGenericClient client;
private volatile List<String> debugPatients = new ArrayList<>();

public ReportContext() {
}
Expand All @@ -37,10 +36,14 @@ public ReportContext(HttpServletRequest request, LinkCredentials user) {
this.user = user;
}

public List<PatientOfInterestModel> getPatientsOfInterest() {
return initialPatientsOfInterest;
}

public List<PatientOfInterestModel> getPatientsOfInterest(QueryPhase queryPhase) {
switch (queryPhase) {
case INITIAL:
return patientsOfInterest;
return initialPatientsOfInterest;
case SUPPLEMENTAL:
return measureContexts.stream()
.flatMap(measureContext -> measureContext.getPatientsOfInterest(queryPhase).stream())
Expand All @@ -54,33 +57,27 @@ public List<PatientOfInterestModel> getPatientsOfInterest(QueryPhase queryPhase)
@Getter
@Setter
public static class MeasureContext {
private String bundleId;
private Bundle reportDefBundle;
private Measure measure;
private String reportId;
private List<PatientOfInterestModel> patientsOfInterest = new ArrayList<>();
private Map<String, MeasureReport> patientReportsByPatientId = new HashMap<>();
private MeasureReport measureReport;
private volatile String bundleId;
private volatile Bundle reportDefBundle;
private volatile Measure measure;
private volatile String reportId;
private volatile List<PatientOfInterestModel> initialPatientsOfInterest = new ArrayList<>();
private volatile List<PatientOfInterestModel> supplementalPatientsOfInterest = Collections.synchronizedList(new ArrayList<>());
private volatile MeasureReport measureReport;

public List<PatientOfInterestModel> getPatientsOfInterest() {
return initialPatientsOfInterest;
}

public List<PatientOfInterestModel> getPatientsOfInterest(QueryPhase queryPhase) {
switch (queryPhase) {
case INITIAL:
return patientsOfInterest;
return initialPatientsOfInterest;
case SUPPLEMENTAL:
Set<String> patientIds = patientReportsByPatientId.entrySet().stream()
.filter(reportById -> FhirHelper.hasNonzeroPopulationCount(reportById.getValue()))
.map(Map.Entry::getKey)
.collect(Collectors.toSet());
return patientsOfInterest.stream()
.filter(poi -> patientIds.contains(poi.getId()))
.collect(Collectors.toList());
return supplementalPatientsOfInterest;
default:
throw new IllegalArgumentException(queryPhase.toString());
}
}

public Collection<MeasureReport> getPatientReports() {
return patientReportsByPatientId.values();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public void testExecute() {
model.setId("10000");
patientOfInterestModel.add(model);

context.setPatientsOfInterest(patientOfInterestModel);
context.setInitialPatientsOfInterest(patientOfInterestModel);

List<Coding> codes = new ArrayList<>();
codes.add(getLocation().getType().get(0).getCoding().get(0));
Expand Down
29 changes: 14 additions & 15 deletions nhsn/src/main/java/com/lantanagroup/link/nhsn/ReportAggregator.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Optional;

@Component
Expand Down Expand Up @@ -38,23 +37,23 @@ protected void addSubjectResult(
"MeasureReport/" + individualMeasureReport.getIdElement().getIdPart());
}

public void aggregatePatientReports(MeasureReport masterMeasureReport, Collection<MeasureReport> measureReports) {
// aggregate all individual reports in ones
for (MeasureReport patientMeasureReportResource : measureReports) {
for (MeasureReport.MeasureReportGroupComponent group : patientMeasureReportResource.getGroup()) {
for (MeasureReport.MeasureReportGroupPopulationComponent population : group.getPopulation()) {
// Check if group and population code exist in master, if not create
MeasureReport.MeasureReportGroupPopulationComponent measureGroupPopulation = getOrCreateGroupAndPopulation(masterMeasureReport, population, group);
// Add population.count to the master group/population count
measureGroupPopulation.setCount(measureGroupPopulation.getCount() + population.getCount());
// If this population incremented the master
if (population.getCount() > 0) {
// add subject results
addSubjectResult(patientMeasureReportResource, measureGroupPopulation);
}
public void aggregatePatientReport(MeasureReport masterMeasureReport, MeasureReport measureReport) {
for (MeasureReport.MeasureReportGroupComponent group : measureReport.getGroup()) {
for (MeasureReport.MeasureReportGroupPopulationComponent population : group.getPopulation()) {
// Check if group and population code exist in master, if not create
MeasureReport.MeasureReportGroupPopulationComponent measureGroupPopulation = getOrCreateGroupAndPopulation(masterMeasureReport, population, group);
// Add population.count to the master group/population count
measureGroupPopulation.setCount(measureGroupPopulation.getCount() + population.getCount());
// If this population incremented the master
if (population.getCount() > 0) {
// add subject results
addSubjectResult(measureReport, measureGroupPopulation);
}
}
}
}

public void finishAggregation(MeasureReport masterMeasureReport) {
for (MeasureReport.MeasureReportGroupComponent group : masterMeasureReport.getGroup()) {
for (MeasureReport.MeasureReportGroupPopulationComponent population : group.getPopulation()) {
logger.debug(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ public void loadPatientsOfInterest(TenantService tenantService, ReportCriteria c
Collector<PatientOfInterestModel, ?, Map<String, PatientOfInterestModel>> deduplicator =
Collectors.toMap(PatientOfInterestModel::toString, Function.identity(), (poi1, poi2) -> poi1);
Map<String, PatientOfInterestModel> poiMap = context.getPatientsOfInterest().stream().collect(deduplicator);
context.setPatientsOfInterest(new ArrayList<>(poiMap.values()));
context.setInitialPatientsOfInterest(new ArrayList<>(poiMap.values()));
for (ReportContext.MeasureContext measureContext : context.getMeasureContexts()) {
measureContext.setPatientsOfInterest(measureContext.getPatientsOfInterest().stream()
measureContext.setInitialPatientsOfInterest(measureContext.getPatientsOfInterest().stream()
.collect(deduplicator)
.values().stream()
.map(poi -> poiMap.get(poi.toString()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public void execute() throws ParseException {
patientOfInterest.setId(patientID);
List<PatientOfInterestModel> patientsOfInterest = new ArrayList<>();
patientsOfInterest.add(patientOfInterest);
context.setPatientsOfInterest(patientsOfInterest);
context.setInitialPatientsOfInterest(patientsOfInterest);
context.setMasterIdentifierValue(reportID);
String bundleId = ReportIdHelper.getPatientDataBundleId(context.getMasterIdentifierValue(), patientOfInterest.getId());

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.lantanagroup.link.nhsn;

import com.lantanagroup.link.ReportIdHelper;
import com.lantanagroup.link.db.TenantService;
import com.lantanagroup.link.db.model.PatientMeasureReport;
import com.lantanagroup.link.model.PatientOfInterestModel;
import com.lantanagroup.link.model.ReportContext;
import com.lantanagroup.link.model.ReportCriteria;
import org.hl7.fhir.r4.model.*;
Expand All @@ -9,6 +13,9 @@

import java.text.ParseException;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class ReportAggregatorTests {
private Bundle getReportDefBundle() {
Bundle measureDefBundle = new Bundle();
Expand Down Expand Up @@ -36,12 +43,21 @@ private ReportContext.MeasureContext getMeasureContext(String version) {
context.getMeasure().setUrl("http://test.com/fhir/Measure/SomeMeasure");
context.getMeasure().setVersion(version);
context.setReportDefBundle(this.getReportDefBundle());
context.getPatientReportsByPatientId().put("test-patient1", this.getPatientMeasureReport("test-patient1", 1));
context.getPatientReportsByPatientId().put("test-patient2", this.getPatientMeasureReport("test-patient2", 2));
context.getPatientReportsByPatientId().put("test-patient3", this.getPatientMeasureReport("test-patient3", 0));
context.setReportId("abc123");
return context;
}

private void addPatientOfInterest(ReportContext.MeasureContext context, TenantService tenantService, String patientId, int populationCount) {
PatientOfInterestModel poi = new PatientOfInterestModel();
poi.setReference("Patient/" + patientId);
poi.setId(patientId);
context.getPatientsOfInterest().add(poi);
String pmrId = ReportIdHelper.getPatientMeasureReportId(context.getReportId(), patientId);
PatientMeasureReport pmr = new PatientMeasureReport();
pmr.setMeasureReport(this.getPatientMeasureReport(patientId, populationCount));
when(tenantService.getPatientMeasureReport(pmrId)).thenReturn(pmr);
}

@Test
public void aggregationWithoutMeasureVersionTest() throws ParseException {
ReportContext.MeasureContext context = this.getMeasureContext(null);
Expand All @@ -50,7 +66,7 @@ public void aggregationWithoutMeasureVersionTest() throws ParseException {
"2023-08-22T00:00:00Z",
"2023-08-22T23:59:59Z");
ReportAggregator aggregator = new ReportAggregator();
MeasureReport aggregate = aggregator.generate(criteria, context);
MeasureReport aggregate = aggregator.generate(mock(TenantService.class), criteria, context);
Assert.assertEquals("http://test.com/fhir/Measure/SomeMeasure", aggregate.getMeasure());
}

Expand All @@ -62,7 +78,7 @@ public void aggregationWithMeasureVersionTest() throws ParseException {
"2023-08-22T00:00:00Z",
"2023-08-22T23:59:59Z");
ReportAggregator aggregator = new ReportAggregator();
MeasureReport aggregate = aggregator.generate(criteria, context);
MeasureReport aggregate = aggregator.generate(mock(TenantService.class), criteria, context);
Assert.assertEquals("http://test.com/fhir/Measure/SomeMeasure|1.0.1", aggregate.getMeasure());
}

Expand All @@ -74,7 +90,11 @@ public void aggregationCountTest() throws ParseException {
"2023-08-22T00:00:00Z",
"2023-08-22T23:59:59Z");
ReportAggregator aggregator = new ReportAggregator();
MeasureReport aggregate = aggregator.generate(criteria, context);
TenantService tenantService = mock(TenantService.class);
addPatientOfInterest(context, tenantService, "test-patient1", 1);
addPatientOfInterest(context, tenantService, "test-patient2", 2);
addPatientOfInterest(context, tenantService, "test-patient3", 0);
MeasureReport aggregate = aggregator.generate(tenantService, criteria, context);
Assert.assertEquals(MeasurePopulation.INITIALPOPULATION.toCode(), aggregate.getGroupFirstRep().getPopulationFirstRep().getCode().getCodingFirstRep().getCode());
Assert.assertEquals(3, aggregate.getGroupFirstRep().getPopulationFirstRep().getCount());
}
Expand Down

0 comments on commit e73c421

Please sign in to comment.