4360 bulk export questionnaireresponses should get picked when author is not empty (#4361)
* added failing test * implemented solution * added more tests * added changelog * changed the implementation, now extended to get all the patient based search params for a given resource instead of 2 in a fixed list of resources * added test for patient bulk export for resources not in patient compartment, fixed implementation to pass test Co-authored-by: Steven Li <steven@smilecdr.com>
This commit is contained in:
parent
360f32f3e4
commit
29ebb950e8
|
@ -59,7 +59,7 @@ public class SearchParameterUtil {
|
|||
* Given the resource type, fetch its patient-based search parameter name
|
||||
* 1. Attempt to find one called 'patient'
|
||||
* 2. If that fails, find one called 'subject'
|
||||
* 3. If that fails, find find by Patient Compartment.
|
||||
* 3. If that fails, find one by Patient Compartment.
|
||||
* 3.1 If that returns >1 result, throw an error
|
||||
* 3.2 If that returns 1 result, return it
|
||||
*/
|
||||
|
@ -76,6 +76,29 @@ public class SearchParameterUtil {
|
|||
return Optional.ofNullable(myPatientSearchParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the resource type, fetch all its patient-based search parameter name that's available
|
||||
*/
|
||||
public static Set<String> getPatientSearchParamsForResourceType(FhirContext theFhirContext, String theResourceType) {
|
||||
RuntimeResourceDefinition runtimeResourceDefinition = theFhirContext.getResourceDefinition(theResourceType);
|
||||
|
||||
List<RuntimeSearchParam> searchParams = new ArrayList<>(runtimeResourceDefinition.getSearchParamsForCompartmentName("Patient"));
|
||||
// add patient search parameter for resources that's not in the compartment
|
||||
RuntimeSearchParam myPatientSearchParam = runtimeResourceDefinition.getSearchParam("patient");
|
||||
if (myPatientSearchParam != null) {
|
||||
searchParams.add(myPatientSearchParam);
|
||||
}
|
||||
RuntimeSearchParam mySubjectSearchParam = runtimeResourceDefinition.getSearchParam("subject");
|
||||
if (mySubjectSearchParam != null) {
|
||||
searchParams.add(mySubjectSearchParam);
|
||||
}
|
||||
if (searchParams == null || searchParams.size() == 0) {
|
||||
String errorMessage = String.format("Resource type [%s] is not eligible for this type of export, as it contains no Patient compartment, and no `patient` or `subject` search parameter", runtimeResourceDefinition.getId());
|
||||
throw new IllegalArgumentException(Msg.code(2222) + errorMessage);
|
||||
}
|
||||
// deduplicate list of searchParams and get their names
|
||||
return searchParams.stream().map(RuntimeSearchParam::getName).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the resource definition for a compartment named 'patient' and return its related Search Parameter.
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 4360
|
||||
title: "Previously, Patient Bulk Export on certain resources with reference to patient only in author or performer did not get exported.
|
||||
This has been fixed."
|
|
@ -146,21 +146,23 @@ public class JpaBulkExportProcessor implements IBulkExportProcessor<JpaPid> {
|
|||
throw new IllegalStateException(Msg.code(797) + errorMessage);
|
||||
}
|
||||
|
||||
List<SearchParameterMap> maps = myBulkExportHelperSvc.createSearchParameterMapsForResourceType(def, theParams);
|
||||
String patientSearchParam = getPatientSearchParamForCurrentResourceType(theParams.getResourceType()).getName();
|
||||
Set<String> patientSearchParams = SearchParameterUtil.getPatientSearchParamsForResourceType(myContext, theParams.getResourceType());
|
||||
|
||||
for (SearchParameterMap map : maps) {
|
||||
//Ensure users did not monkey with the patient compartment search parameter.
|
||||
validateSearchParametersForPatient(map, theParams);
|
||||
for (String patientSearchParam : patientSearchParams) {
|
||||
List<SearchParameterMap> maps = myBulkExportHelperSvc.createSearchParameterMapsForResourceType(def, theParams);
|
||||
for (SearchParameterMap map : maps) {
|
||||
//Ensure users did not monkey with the patient compartment search parameter.
|
||||
validateSearchParametersForPatient(map, theParams);
|
||||
|
||||
ISearchBuilder<JpaPid> searchBuilder = getSearchBuilderForResourceType(theParams.getResourceType());
|
||||
ISearchBuilder<JpaPid> searchBuilder = getSearchBuilderForResourceType(theParams.getResourceType());
|
||||
|
||||
filterBySpecificPatient(theParams, resourceType, patientSearchParam, map);
|
||||
filterBySpecificPatient(theParams, resourceType, patientSearchParam, map);
|
||||
|
||||
SearchRuntimeDetails searchRuntime = new SearchRuntimeDetails(null, jobId);
|
||||
IResultIterator<JpaPid> resultIterator = searchBuilder.createQuery(map, searchRuntime, null, RequestPartitionId.allPartitions());
|
||||
while (resultIterator.hasNext()) {
|
||||
pids.add(resultIterator.next());
|
||||
SearchRuntimeDetails searchRuntime = new SearchRuntimeDetails(null, jobId);
|
||||
IResultIterator<JpaPid> resultIterator = searchBuilder.createQuery(map, searchRuntime, null, RequestPartitionId.allPartitions());
|
||||
while (resultIterator.hasNext()) {
|
||||
pids.add(resultIterator.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
return pids;
|
||||
|
|
|
@ -13,19 +13,25 @@ import com.google.common.collect.Sets;
|
|||
import org.apache.commons.io.LineIterator;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.Basic;
|
||||
import org.hl7.fhir.r4.model.Binary;
|
||||
import org.hl7.fhir.r4.model.CarePlan;
|
||||
import org.hl7.fhir.r4.model.Device;
|
||||
import org.hl7.fhir.r4.model.DocumentReference;
|
||||
import org.hl7.fhir.r4.model.Encounter;
|
||||
import org.hl7.fhir.r4.model.Enumerations;
|
||||
import org.hl7.fhir.r4.model.Group;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.Location;
|
||||
import org.hl7.fhir.r4.model.MedicationAdministration;
|
||||
import org.hl7.fhir.r4.model.Observation;
|
||||
import org.hl7.fhir.r4.model.Organization;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.Practitioner;
|
||||
import org.hl7.fhir.r4.model.Provenance;
|
||||
import org.hl7.fhir.r4.model.QuestionnaireResponse;
|
||||
import org.hl7.fhir.r4.model.Reference;
|
||||
import org.hl7.fhir.r4.model.ServiceRequest;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -460,6 +466,113 @@ public class BulkDataExportTest extends BaseResourceProviderR4Test {
|
|||
verifyBulkExportResults(options, List.of("Patient/P1", obsId, provId, devId, devId2), List.of("Patient/P2", provId2, devId3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPatientBulkExportWithReferenceToAuthor_ShouldShowUp() {
|
||||
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
|
||||
// Create some resources
|
||||
Patient patient = new Patient();
|
||||
patient.setId("P1");
|
||||
patient.setActive(true);
|
||||
myClient.update().resource(patient).execute();
|
||||
|
||||
Basic basic = new Basic();
|
||||
basic.setAuthor(new Reference("Patient/P1"));
|
||||
String basicId = myClient.create().resource(basic).execute().getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
DocumentReference documentReference = new DocumentReference();
|
||||
documentReference.setStatus(Enumerations.DocumentReferenceStatus.CURRENT);
|
||||
documentReference.addAuthor(new Reference("Patient/P1"));
|
||||
String docRefId = myClient.create().resource(documentReference).execute().getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
QuestionnaireResponse questionnaireResponseSub = new QuestionnaireResponse();
|
||||
questionnaireResponseSub.setStatus(QuestionnaireResponse.QuestionnaireResponseStatus.COMPLETED);
|
||||
questionnaireResponseSub.setSubject(new Reference("Patient/P1"));
|
||||
String questRespSubId = myClient.create().resource(questionnaireResponseSub).execute().getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
QuestionnaireResponse questionnaireResponseAuth = new QuestionnaireResponse();
|
||||
questionnaireResponseAuth.setStatus(QuestionnaireResponse.QuestionnaireResponseStatus.COMPLETED);
|
||||
questionnaireResponseAuth.setAuthor(new Reference("Patient/P1"));
|
||||
String questRespAuthId = myClient.create().resource(questionnaireResponseAuth).execute().getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
// set the export options
|
||||
BulkDataExportOptions options = new BulkDataExportOptions();
|
||||
options.setResourceTypes(Sets.newHashSet("Patient", "Basic", "DocumentReference", "QuestionnaireResponse"));
|
||||
options.setExportStyle(BulkDataExportOptions.ExportStyle.PATIENT);
|
||||
options.setOutputFormat(Constants.CT_FHIR_NDJSON);
|
||||
verifyBulkExportResults(options, List.of("Patient/P1", basicId, docRefId, questRespAuthId, questRespSubId), Collections.emptyList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPatientBulkExportWithReferenceToPerformer_ShouldShowUp() {
|
||||
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
|
||||
// Create some resources
|
||||
Patient patient = new Patient();
|
||||
patient.setId("P1");
|
||||
patient.setActive(true);
|
||||
myClient.update().resource(patient).execute();
|
||||
|
||||
CarePlan carePlan = new CarePlan();
|
||||
carePlan.setStatus(CarePlan.CarePlanStatus.COMPLETED);
|
||||
CarePlan.CarePlanActivityComponent carePlanActivityComponent = new CarePlan.CarePlanActivityComponent();
|
||||
CarePlan.CarePlanActivityDetailComponent carePlanActivityDetailComponent = new CarePlan.CarePlanActivityDetailComponent();
|
||||
carePlanActivityDetailComponent.addPerformer(new Reference("Patient/P1"));
|
||||
carePlanActivityComponent.setDetail(carePlanActivityDetailComponent);
|
||||
carePlan.addActivity(carePlanActivityComponent);
|
||||
String carePlanId = myClient.create().resource(carePlan).execute().getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
MedicationAdministration medicationAdministration = new MedicationAdministration();
|
||||
medicationAdministration.setStatus(MedicationAdministration.MedicationAdministrationStatus.COMPLETED);
|
||||
MedicationAdministration.MedicationAdministrationPerformerComponent medicationAdministrationPerformerComponent = new MedicationAdministration.MedicationAdministrationPerformerComponent();
|
||||
medicationAdministrationPerformerComponent.setActor(new Reference("Patient/P1"));
|
||||
medicationAdministration.addPerformer(medicationAdministrationPerformerComponent);
|
||||
String medAdminId = myClient.create().resource(medicationAdministration).execute().getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
ServiceRequest serviceRequest = new ServiceRequest();
|
||||
serviceRequest.setStatus(ServiceRequest.ServiceRequestStatus.COMPLETED);
|
||||
serviceRequest.addPerformer(new Reference("Patient/P1"));
|
||||
String sevReqId = myClient.create().resource(serviceRequest).execute().getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
Observation observationSub = new Observation();
|
||||
observationSub.setStatus(Observation.ObservationStatus.AMENDED);
|
||||
observationSub.setSubject(new Reference("Patient/P1"));
|
||||
String obsSubId = myClient.create().resource(observationSub).execute().getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
Observation observationPer = new Observation();
|
||||
observationPer.setStatus(Observation.ObservationStatus.AMENDED);
|
||||
observationPer.addPerformer(new Reference("Patient/P1"));
|
||||
String obsPerId = myClient.create().resource(observationPer).execute().getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
// set the export options
|
||||
BulkDataExportOptions options = new BulkDataExportOptions();
|
||||
options.setResourceTypes(Sets.newHashSet("Patient", "Observation", "CarePlan", "MedicationAdministration", "ServiceRequest"));
|
||||
options.setExportStyle(BulkDataExportOptions.ExportStyle.PATIENT);
|
||||
options.setOutputFormat(Constants.CT_FHIR_NDJSON);
|
||||
verifyBulkExportResults(options, List.of("Patient/P1", carePlanId, medAdminId, sevReqId, obsSubId, obsPerId), Collections.emptyList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPatientBulkExportWithResourceNotInCompartment_ShouldShowUp() {
|
||||
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
|
||||
// Create some resources
|
||||
Patient patient = new Patient();
|
||||
patient.setId("P1");
|
||||
patient.setActive(true);
|
||||
myClient.update().resource(patient).execute();
|
||||
|
||||
Device device = new Device();
|
||||
device.setStatus(Device.FHIRDeviceStatus.ACTIVE);
|
||||
device.setPatient(new Reference("Patient/P1"));
|
||||
String deviceId = myClient.create().resource(device).execute().getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
|
||||
// set the export options
|
||||
BulkDataExportOptions options = new BulkDataExportOptions();
|
||||
options.setResourceTypes(Sets.newHashSet("Patient", "Device"));
|
||||
options.setExportStyle(BulkDataExportOptions.ExportStyle.PATIENT);
|
||||
options.setOutputFormat(Constants.CT_FHIR_NDJSON);
|
||||
verifyBulkExportResults(options, List.of("Patient/P1", deviceId), Collections.emptyList());
|
||||
}
|
||||
|
||||
private void verifyBulkExportResults(BulkDataExportOptions theOptions, List<String> theContainedList, List<String> theExcludedList) {
|
||||
Batch2JobStartResponse startResponse = myJobRunner.startNewJob(BulkExportUtils.createBulkExportJobParametersFromExportOptions(theOptions));
|
||||
|
||||
|
|
Loading…
Reference in New Issue