use of org.hl7.davinci.r4.crdhook.CrdPrefetch in project CRD by HL7-DaVinci.
the class OrderSelectService method createCqlExecutionContexts.
@Override
public List<CoverageRequirementRuleResult> createCqlExecutionContexts(OrderSelectRequest orderSelectRequest, FileStore fileStore, String baseUrl) {
List<String> selections = Arrays.asList(orderSelectRequest.getContext().getSelections());
FhirBundleProcessor fhirBundleProcessor = new FhirBundleProcessor(fileStore, baseUrl, selections);
CrdPrefetch prefetch = orderSelectRequest.getPrefetch();
fhirBundleProcessor.processOrderSelectMedicationStatements(prefetch.getMedicationRequestBundle(), prefetch.getMedicationStatementBundle());
List<CoverageRequirementRuleResult> results = fhirBundleProcessor.getResults();
if (results.isEmpty()) {
throw RequestIncompleteException.NoSupportedBundlesFound();
}
return results;
}
use of org.hl7.davinci.r4.crdhook.CrdPrefetch in project CRD by HL7-DaVinci.
the class QueryBatchRequest method performQueryBatchRequest.
/**
* Backfills the missing required values of the response that prefetch may have missed.
* This implementation pulls the IDs of the required references from the request object's draft
* orders, checks which of those values are missing from the current CRD response, builds the
* Query Batch JSON request using
* http://build.fhir.org/ig/HL7/davinci-crd/hooks.html#fhir-resource-access,
* then populates the CRD response with the response from the Query Batch.
*/
public void performQueryBatchRequest(CdsRequest<?, ?> cdsRequest, CrdPrefetch crdPrefetch) {
logger.info("***** ***** Performing Query Batch Request.");
CrdPrefetch crdResponse = crdPrefetch;
// The list of references that should be queried in the batch request.
List<String> requiredReferences = new ArrayList<String>();
// Get the IDs of references in the request's draft orders.
Bundle draftOrdersBundle = cdsRequest.getContext().getDraftOrders();
// This assumes that only the first draft order is relevant.
Resource initialRequestResource = draftOrdersBundle.getEntry().get(0).getResource();
ResourceType requestType = initialRequestResource.getResourceType();
// Extract the references by iterating through the JSON.
Gson gson = new Gson();
final JsonObject jsonObject = gson.toJsonTree(initialRequestResource).getAsJsonObject();
for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
FhirRequestProcessor.extractReferenceIds(requiredReferences, entry.getValue());
}
// Filter out references that already exist in the CRD Response.
requiredReferences = requiredReferences.stream().filter(referenceId -> !crdResponse.containsRequestResourceId(referenceId)).collect(Collectors.toList());
logger.info("References to query: " + requiredReferences);
if (requiredReferences.isEmpty()) {
logger.info("A Query Batch Request is not needed: all references have already already fetched.");
return;
}
// Build the Query Batch Request JSON.
Bundle queryBatchRequestBundle = buildQueryBatchRequestBundle(requiredReferences);
String queryBatchRequestBody = FhirContext.forR4().newJsonParser().encodeResourceToString(queryBatchRequestBundle);
// Make the query batch request to the EHR server.
Bundle queryResponseBundle = null;
try {
logger.info("Executing Query Batch Request: " + queryBatchRequestBody);
queryResponseBundle = (Bundle) FhirRequestProcessor.executeFhirQueryBody(queryBatchRequestBody, cdsRequest, this.fhirComponents, HttpMethod.POST);
queryResponseBundle = extractNestedBundledResources(queryResponseBundle);
logger.info("Extracted Query Batch Resources: " + (queryResponseBundle).getEntry().stream().map(entry -> entry.getResource()).collect(Collectors.toList()));
} catch (Exception e) {
logger.error("Failed to backfill prefetch with Query Batch Request " + queryBatchRequestBody, e);
}
if (queryResponseBundle == null) {
logger.error("No response recieved from the Query Batch Request.");
return;
}
// Add the request resource to the query batch response as it may be missing.
// Coverage and Subject are not automatically being
// linked to the request object. It seems to somehow automatically link during
// standard prefetch, but not here so we're doing it manually.
List<Coverage> coverages = FhirRequestProcessor.extractCoverageFromBundle(queryResponseBundle);
List<Patient> patients = FhirRequestProcessor.extractPatientsFromBundle(queryResponseBundle);
FhirRequestProcessor.addInsuranceAndSubject(initialRequestResource, patients, coverages);
BundleEntryComponent newEntry = new BundleEntryComponent();
newEntry.setResource(initialRequestResource);
queryResponseBundle.addEntry(newEntry);
// Add the query batch response resources to the CRD Prefetch request.
logger.info("Query Batch Response Entries: " + queryResponseBundle.getEntry());
FhirRequestProcessor.addToCrdPrefetchRequest(crdResponse, requestType, queryResponseBundle.getEntry());
logger.info("Post-Query Batch CRDResponse: " + crdResponse);
}
use of org.hl7.davinci.r4.crdhook.CrdPrefetch in project CRD by HL7-DaVinci.
the class CrdRequestCreator method createOrderSignRequest.
/**
* Generate a order sign request that contains a ServiceRequest.
*
* @param patientGender Desired gender of the patient in the request
* @param patientBirthdate Desired birth date of the patient in the request
* @return Fully populated CdsRequest
*/
public static OrderSignRequest createOrderSignRequest(Enumerations.AdministrativeGender patientGender, Date patientBirthdate, String patientAddressState, String providerAddressState) {
OrderSignRequest request = new OrderSignRequest();
request.setHook(Hook.ORDER_SIGN);
request.setHookInstance(UUID.randomUUID().toString());
OrderSignContext context = new OrderSignContext();
request.setContext(context);
context.setUserId("Practitioner/1234");
Patient patient = createPatient(patientGender, patientBirthdate, patientAddressState);
context.setPatientId(patient.getId());
ServiceRequest sr = new ServiceRequest();
sr.setStatus(ServiceRequest.ServiceRequestStatus.DRAFT);
sr.setId("DeviceRequest/123");
sr.setIntent(ServiceRequest.ServiceRequestIntent.ORDER);
PrefetchCallback callback = (p, c) -> {
sr.addPerformer(new Reference(p));
sr.addInsurance(new Reference(c));
};
sr.setSubject(new Reference(patient));
Practitioner provider = createPractitioner();
Bundle prefetchBundle = createPrefetchBundle(patient, provider, callback, providerAddressState);
Coding oxygen = new Coding().setCode("A0426").setSystem("https://bluebutton.cms.gov/resources/codesystem/hcpcs").setDisplay("Ambulance service, advanced life support, non-emergency transport, level 1 (als 1)");
sr.setCode(new CodeableConcept().addCoding(oxygen).setText("Ambulance service Non-Emergency Transport"));
Bundle orderBundle = new Bundle();
Bundle.BundleEntryComponent bec = new Bundle.BundleEntryComponent();
bec.setResource(sr);
orderBundle.addEntry(bec);
Bundle.BundleEntryComponent pfDrBec = new Bundle.BundleEntryComponent();
pfDrBec.setResource(sr);
prefetchBundle.addEntry(pfDrBec);
context.setDraftOrders(orderBundle);
Device device = new Device();
device.setType(new CodeableConcept().addCoding(oxygen));
bec = new Bundle.BundleEntryComponent();
bec.setResource(device);
prefetchBundle.addEntry(bec);
CrdPrefetch prefetch = new CrdPrefetch();
prefetch.setServiceRequestBundle(prefetchBundle);
request.setPrefetch(prefetch);
return request;
}
use of org.hl7.davinci.r4.crdhook.CrdPrefetch in project CRD by HL7-DaVinci.
the class CdsService method handleRequest.
/**
* Performs generic operations for incoming requests of any type.
*
* @param request the generically typed incoming request
* @return The response from the server
*/
public CdsResponse handleRequest(@Valid @RequestBody requestTypeT request, URL applicationBaseUrl) {
// create the RequestLog
RequestLog requestLog = new RequestLog(request, new Date().getTime(), this.fhirComponents.getFhirVersion().toString(), this.id, requestService, 5);
// Parsed request
requestLog.advanceTimeline(requestService);
PrefetchHydrator prefetchHydrator = new PrefetchHydrator(this, request, this.fhirComponents);
prefetchHydrator.hydrate();
// hydrated
requestLog.advanceTimeline(requestService);
// Attempt a Query Batch Request to backfill missing attributes.
if (myConfig.isQueryBatchRequest()) {
QueryBatchRequest qbr = new QueryBatchRequest(this.fhirComponents);
this.attempQueryBatchRequest(request, qbr);
}
logger.info("***** ***** request from requestLog: " + requestLog.toString());
CdsResponse response = new CdsResponse();
Gson gson = new Gson();
final String jsonObject = gson.toJson(request.getPrefetch());
logger.info("Final populated CRDPrefetch: " + jsonObject);
// CQL Fetched
List<CoverageRequirementRuleResult> lookupResults;
try {
lookupResults = this.createCqlExecutionContexts(request, fileStore, applicationBaseUrl.toString() + "/");
requestLog.advanceTimeline(requestService);
} catch (RequestIncompleteException e) {
logger.warn("RequestIncompleteException " + request);
logger.warn(e.getMessage() + "; summary card sent to client");
response.addCard(CardBuilder.summaryCard(CardTypes.COVERAGE, e.getMessage()));
requestLog.setCardListFromCards(response.getCards());
requestLog.setResults(e.getMessage());
requestService.edit(requestLog);
return response;
}
// process the extension for the configuration
// load hook configuration with default values
Configuration hookConfiguration = new Configuration();
Extension extension = request.getExtension();
if (extension != null) {
if (extension.getConfiguration() != null) {
hookConfiguration = extension.getConfiguration();
}
}
boolean errorCardOnEmpty = !(request instanceof OrderSelectRequest);
// no error cards on empty when order-select request
boolean foundApplicableRule = false;
for (CoverageRequirementRuleResult lookupResult : lookupResults) {
requestLog.addTopic(requestService, lookupResult.getTopic());
CqlResultsForCard results = executeCqlAndGetRelevantResults(lookupResult.getContext(), lookupResult.getTopic());
CoverageRequirements coverageRequirements = results.getCoverageRequirements();
if (results.ruleApplies()) {
foundApplicableRule = true;
if (results.getCoverageRequirements().getApplies()) {
// if prior auth already approved
if (coverageRequirements.isPriorAuthApproved()) {
response.addCard(CardBuilder.priorAuthCard(results, results.getRequest(), fhirComponents, coverageRequirements.getPriorAuthId(), request.getContext().getPatientId(), lookupResult.getCriteria().getPayorId(), request.getContext().getUserId(), applicationBaseUrl.toString() + "/fhir/" + fhirComponents.getFhirVersion().toString(), fhirResourceRepository));
} else if (coverageRequirements.isDocumentationRequired() || coverageRequirements.isPriorAuthRequired()) {
if (StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireOrderUri()) || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireFaceToFaceUri()) || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireLabUri()) || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireProgressNoteUri()) || StringUtils.isNotEmpty(coverageRequirements.getQuestionnairePARequestUri()) || StringUtils.isNotEmpty(coverageRequirements.getQuestionnairePlanOfCareUri()) || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireDispenseUri()) || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireAdditionalUri())) {
List<Link> smartAppLinks = createQuestionnaireLinks(request, applicationBaseUrl, lookupResult, results);
if (coverageRequirements.isPriorAuthRequired()) {
Card card = CardBuilder.transform(CardTypes.PRIOR_AUTH, results, smartAppLinks);
card.addSuggestionsItem(CardBuilder.createSuggestionWithNote(card, results.getRequest(), fhirComponents, "Save Update To EHR", "Update original " + results.getRequest().fhirType() + " to add note", true, CoverageGuidance.ADMIN));
response.addCard(card);
} else if (coverageRequirements.isDocumentationRequired()) {
Card card = CardBuilder.transform(CardTypes.DTR_CLIN, results, smartAppLinks);
card.addSuggestionsItem(CardBuilder.createSuggestionWithNote(card, results.getRequest(), fhirComponents, "Save Update To EHR", "Update original " + results.getRequest().fhirType() + " to add note", true, CoverageGuidance.CLINICAL));
response.addCard(card);
}
// add a card for an alternative therapy if there is one
if (results.getAlternativeTherapy().getApplies() && hookConfiguration.getAlternativeTherapy()) {
try {
response.addCard(CardBuilder.alternativeTherapyCard(results.getAlternativeTherapy(), results.getRequest(), fhirComponents));
} catch (RuntimeException e) {
logger.warn("Failed to process alternative therapy: " + e.getMessage());
}
}
} else {
logger.warn("Unspecified Questionnaire URI; summary card sent to client");
response.addCard(CardBuilder.transform(CardTypes.COVERAGE, results));
}
} else {
// no prior auth or documentation required
logger.info("Add the no doc or prior auth required card");
Card card = CardBuilder.transform(CardTypes.COVERAGE, results);
card.addSuggestionsItem(CardBuilder.createSuggestionWithNote(card, results.getRequest(), fhirComponents, "Save Update To EHR", "Update original " + results.getRequest().fhirType() + " to add note", true, CoverageGuidance.COVERED));
card.setSelectionBehavior(Card.SelectionBehaviorEnum.ANY);
response.addCard(card);
}
}
// apply the DrugInteractions
if (results.getDrugInteraction().getApplies()) {
response.addCard(CardBuilder.drugInteractionCard(results.getDrugInteraction(), results.getRequest()));
}
}
}
// CQL Executed
requestLog.advanceTimeline(requestService);
if (errorCardOnEmpty) {
if (!foundApplicableRule) {
String msg = "No documentation rules found";
logger.warn(msg + "; summary card sent to client");
response.addCard(CardBuilder.summaryCard(CardTypes.COVERAGE, msg));
}
CardBuilder.errorCardIfNonePresent(CardTypes.COVERAGE, response);
}
// Ading card to requestLog
requestLog.setCardListFromCards(response.getCards());
requestService.edit(requestLog);
return response;
}
use of org.hl7.davinci.r4.crdhook.CrdPrefetch in project CRD by HL7-DaVinci.
the class OrderSignService method createCqlExecutionContexts.
@Override
public List<CoverageRequirementRuleResult> createCqlExecutionContexts(OrderSignRequest orderSignRequest, FileStore fileStore, String baseUrl) {
FhirBundleProcessor fhirBundleProcessor = new FhirBundleProcessor(fileStore, baseUrl);
CrdPrefetch prefetch = orderSignRequest.getPrefetch();
fhirBundleProcessor.processDeviceRequests(prefetch.getDeviceRequestBundle());
fhirBundleProcessor.processMedicationRequests(prefetch.getMedicationRequestBundle());
fhirBundleProcessor.processServiceRequests(prefetch.getServiceRequestBundle());
fhirBundleProcessor.processMedicationDispenses(prefetch.getMedicationDispenseBundle());
List<CoverageRequirementRuleResult> results = fhirBundleProcessor.getResults();
if (results.isEmpty()) {
throw RequestIncompleteException.NoSupportedBundlesFound();
}
return results;
}
Aggregations