use of org.hl7.davinci.endpoint.files.FileStore 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.endpoint.files.FileStore in project CRD by HL7-DaVinci.
the class QuestionnaireValueSetProcessor method findAndReplaceValueSetReferences.
/**
* Recursively visits every questionnaire item and replaces every `answerValueSet` that isn't a local
* hash (#) reference into a local reference. This fills the valueSetMap with the loaded valuesets.
*
* @param itemComponents The item components to visit.
* @param valueSetMap A mapping of ValueSet urls to loaded valuesets that should be filled as references are found.
* @param fileStore The file store that is used to load valuesets from.
* @param baseUrl The base url of the server from the request. Used to identify local valuesets.
*/
private void findAndReplaceValueSetReferences(List<QuestionnaireItemComponent> itemComponents, Map<String, ValueSet> valueSetMap, FileStore fileStore, String baseUrl) {
for (QuestionnaireItemComponent itemComponent : itemComponents) {
// If there is an answerValueSet field we need to do some work on this item
if (itemComponent.hasAnswerValueSet()) {
// Only look for a valueset to embed if it does not appear to be a hash reference
if (!itemComponent.getAnswerValueSet().startsWith("#")) {
logger.info("answerValueSet found with url - " + itemComponent.getAnswerValueSet());
String valueSetId = findAndLoadValueSet(itemComponent.getAnswerValueSet(), valueSetMap, fileStore, baseUrl);
if (valueSetId != null) {
itemComponent.getAnswerValueSet();
itemComponent.setAnswerValueSet("#" + valueSetId);
logger.info("answerValueSet replaced with - " + itemComponent.getAnswerValueSet());
} else {
logger.warn("Referenced ValueSet: " + itemComponent.getAnswerValueSet() + " was not found.");
}
}
}
// Recurse down into child items.
if (itemComponent.hasItem()) {
findAndReplaceValueSetReferences(itemComponent.getItem(), valueSetMap, fileStore, baseUrl);
}
}
}
use of org.hl7.davinci.endpoint.files.FileStore in project CRD by HL7-DaVinci.
the class QuestionnaireValueSetProcessor method findAndLoadValueSet.
/**
* Finds a value set, loads it and modifies its id and url fields to work in the contains field.
*
* @param url The canonical url of the valueset to look for.
* @param valueSetMap The map of valuesets that have been loaded already.
* @return The local ID to use for the valueset. null if valueset wasn't found.
*/
private String findAndLoadValueSet(String url, Map<String, ValueSet> valueSetMap, FileStore fileStore, String baseUrl) {
if (valueSetMap.containsKey(url)) {
return valueSetMap.get(url).getId();
}
FileResource valueSetFileResource;
ValueSet valueSet;
// If URL starts with this server's base url, pull out id and search by id
if (url.startsWith(baseUrl)) {
String valueSetId = url.split("ValueSet/")[1];
valueSetFileResource = fileStore.getFhirResourceById("R4", "valueset", valueSetId, baseUrl);
} else {
valueSetFileResource = fileStore.getFhirResourceByUrl("R4", "valueset", url, baseUrl);
}
if (valueSetFileResource != null) {
// parse value set and modify ID and #URL to match.
valueSet = (ValueSet) this.parseFhirFileResource(valueSetFileResource);
String valueSetId = valueSet.getIdElement().getIdPart();
valueSet.setId(valueSetId);
valueSet.setUrl("#" + valueSetId);
// add it to the value set map so it can be reused
valueSetMap.put(url, valueSet);
return valueSetId;
} else {
return null;
}
}
use of org.hl7.davinci.endpoint.files.FileStore in project CRD by HL7-DaVinci.
the class SubQuestionnaireProcessor method processItem.
/**
* Determines if this item is a sub-questionnaire reference and returns the items to replace it with. Returns the same list
* otherwise. Also recursively continues scanning if this is just a grouping item.
*
* @param item The item to check for sub-questionnaire referece.
* @param fileStore The FileStore to be used for fetching sub-questionnaires.
* @param baseUrl The base url from the server.
* @param containedList List of contained resources to put in the assembled Questionnaire. This will be filled while iterating.
* @param extensionList List of extensions to put in the assembled Questionnaire. This will be filled while iterating.
* @return New list of items to replace this item with.
*/
private List<QuestionnaireItemComponent> processItem(QuestionnaireItemComponent item, FileStore fileStore, String baseUrl, Hashtable<String, org.hl7.fhir.r4.model.Resource> containedList, List<Extension> extensionList) {
// find if item has an extension is sub-questionnaire
Extension e = item.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/sub-questionnaire");
if (e != null) {
// read sub questionnaire from file store
CanonicalType value = e.castToCanonical(e.getValue());
logger.info("SubQuestionnaireProcessor::parseItem(): Looking for SubQuestionnaire " + value);
// strip the type off of the id if it is there
String id = value.asStringValue();
String[] parts = id.split("/");
if (parts.length > 1) {
id = parts[1];
}
boolean expandRootItem = false;
Extension expand = item.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/sub-questionnaire-expand");
if (expand != null) {
expandRootItem = expand.castToBoolean(expand.getValue()).booleanValue();
}
FileResource subFileResource = fileStore.getFhirResourceById("R4", "questionnaire", id, baseUrl, false);
if (subFileResource != null) {
Questionnaire subQuestionnaire = (Questionnaire) this.parseFhirFileResource(subFileResource);
if (subQuestionnaire != null) {
// merge extensions
for (Extension subExtension : subQuestionnaire.getExtension()) {
if (extensionList.stream().noneMatch(ext -> ext.equalsDeep(subExtension))) {
extensionList.add(subExtension);
}
}
// merge contained resources
for (org.hl7.fhir.r4.model.Resource r : subQuestionnaire.getContained()) {
containedList.put(r.getId(), r);
}
List<QuestionnaireItemComponent> rootItems = subQuestionnaire.getItem();
// there are more than one root items in sub questionnaire, don't expand
if (!expandRootItem || rootItems.size() > 1) {
return rootItems;
} else {
return rootItems.get(0).getItem();
}
} else {
// SubQuestionnaire could not be loaded
logger.warn("SubQuestionnaireProcessor::parseItem(): Could not load SubQuestionnaire " + value.asStringValue());
return Arrays.asList(item);
}
} else {
// SubQuestionnaire could not be found
logger.warn("SubQuestionnaireProcessor::parseItem(): Could not find SubQuestionnaire " + value.asStringValue());
return Arrays.asList(item);
}
}
// parse sub-items
this.processItemList(item.getItem(), fileStore, baseUrl, containedList, extensionList);
return Arrays.asList(item);
}
use of org.hl7.davinci.endpoint.files.FileStore 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;
}
Aggregations