Skip to content

Commit

Permalink
Merge pull request #226 from DBCG/conceptMap
Browse files Browse the repository at this point in the history
Concept map
  • Loading branch information
JPercival authored Aug 1, 2020
2 parents 4bd6c47 + b702e6f commit 8c838b6
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ public class HapiProperties {
static final String QUESTIONNAIRE_RESPONSE_ENDPOINT = "questionnaireResponseExtract.endpoint";
static final String QUESTIONNAIRE_RESPONSE_USERNAME = "questionnaireResponseExtract.username";
static final String QUESTIONNAIRE_RESPONSE_PASSWORD = "questionnaireResponseExtract.password";

static final String OBSERVATION_TRANSFORM_ENABLED = "observationTransform.enabled";
static final String OBSERVATION_TRANSFORM_USERNAME = "observationTransform.username";
static final String OBSERVATION_TRANSFORM_PASSWORD = "observationTransform.password";
static final String OBSERVATION_TRANSFORM_REPLACE_CODE = "observationTransform.replaceCode";

static final String CDSHOOKS_FHIRSERVER_MAXCODESPERQUERY = "cds_hooks.fhirServer.maxCodesPerQuery";
static final String CDSHOOKS_FHIRSERVER_EXPANDVALUESETS = "cds_hooks.fhirServer.expandValueSets";
Expand Down Expand Up @@ -384,6 +389,11 @@ public static Long getReuseCachedSearchResultsMillis() {
public static String getQuestionnaireResponseExtractUserName(){return HapiProperties.getProperty(QUESTIONNAIRE_RESPONSE_USERNAME);};
public static String getQuestionnaireResponseExtractPassword(){return HapiProperties.getProperty(QUESTIONNAIRE_RESPONSE_PASSWORD);};

public static Boolean getObservationTransformEnabled(){return HapiProperties.getBooleanProperty(OBSERVATION_TRANSFORM_ENABLED, false);}
public static String getObservationTransformUsername(){return HapiProperties.getProperty(OBSERVATION_TRANSFORM_USERNAME);}
public static String getObservationTransformPassword(){return HapiProperties.getProperty(OBSERVATION_TRANSFORM_PASSWORD);}
public static Boolean getObservationTransformReplaceCode(){return HapiProperties.getBooleanProperty(OBSERVATION_TRANSFORM_REPLACE_CODE, false);}

//************************* CDS_HOOKS ****************
public static Integer getCdsHooksFhirServerMaxCodesPerQuery() { return HapiProperties.getIntegerProperty(CDSHOOKS_FHIRSERVER_MAXCODESPERQUERY, 64);}
public static Boolean getCdsHooksFhirServerExpandValueSets() { return HapiProperties.getBooleanProperty(CDSHOOKS_FHIRSERVER_EXPANDVALUESETS, true);}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package org.opencds.cqf.dstu3.providers;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.util.BundleUtil;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.ConceptMap;
import org.hl7.fhir.dstu3.model.Observation;
import org.opencds.cqf.common.config.HapiProperties;

import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

import static org.opencds.cqf.common.helpers.ClientHelper.getClient;

public class ObservationProvider {

private FhirContext fhirContext;

public ObservationProvider(FhirContext fhirContext){
this.fhirContext = fhirContext;
}

@Operation(name = "$transform", idempotent = false, type = Observation.class)
public Bundle transformObservations(
@OperationParam(name = "observations") Bundle observationsBundle,
@OperationParam(name = "conceptMapURL") String conceptMapURL
) {
if(null == observationsBundle) {
throw new IllegalArgumentException("Unable to perform operation Observation$transform. No Observation bundle passed in.");
}
if(null == conceptMapURL) {
throw new IllegalArgumentException("Unable to perform operation Observation$transform. No concept map url specified.");
}
IGenericClient client = getClient(fhirContext, conceptMapURL, HapiProperties.getObservationTransformUsername(), HapiProperties.getObservationTransformPassword() );
ConceptMap transformConceptMap = client.read().resource(ConceptMap.class).withUrl (conceptMapURL).execute();
if(null == transformConceptMap) {
throw new IllegalArgumentException("Unable to perform operation Observation$transform. Unable to get concept map.");
}
List<Observation> observations = BundleUtil.toListOfResources(fhirContext, observationsBundle).stream()
.filter(resource -> resource instanceof Observation)
.map(Observation.class::cast)
.collect(Collectors.toList());
/**
* TODO - There must be a more efficient way to loop through this, but so far I have not come up with it.
*/
transformConceptMap.getGroup().forEach(group -> {
HashMap<String, ConceptMap.TargetElementComponent> codeMappings = new HashMap<>();
String targetSystem = group.getTarget();
group.getElement().forEach(codeElement -> {
codeMappings.put(codeElement.getCode(), codeElement.getTarget().get(0));
});
observations.forEach(observation -> {
if(observation.getValue().fhirType().equalsIgnoreCase("codeableconcept")){
String obsValueCode = observation.getValueCodeableConcept().getCoding().get(0).getCode();
if(obsValueCode != null) {
if (codeMappings.get(observation.getValueCodeableConcept().getCoding().get(0).getCode()) != null) {
if (HapiProperties.getObservationTransformReplaceCode()) {
observation.getValueCodeableConcept().getCoding().get(0).setCode(codeMappings.get(obsValueCode).getCode());
observation.getValueCodeableConcept().getCoding().get(0).setDisplay(codeMappings.get(obsValueCode).getDisplay());
observation.getValueCodeableConcept().getCoding().get(0).setSystem(targetSystem);
} else {
Coding newCoding = new Coding();
newCoding.setSystem(targetSystem);
newCoding.setCode(codeMappings.get(obsValueCode).getCode());
newCoding.setDisplay(codeMappings.get(obsValueCode).getDisplay());
observation.getValueCodeableConcept().getCoding().add(newCoding);
}
}
}
}
});
});
return observationsBundle;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,11 @@ private void resolveProviders(EvaluationProviderFactory providerFactory,
QuestionnaireProvider questionnaireProvider = new QuestionnaireProvider(this.fhirContext);
this.registerProvider(questionnaireProvider);
}
// Observation processing
if(HapiProperties.getObservationTransformEnabled()) {
ObservationProvider observationProvider = new ObservationProvider(this.fhirContext);
this.registerProvider(observationProvider);
}
}

protected <T extends IBaseResource> IFhirResourceDao<T> getDao(Class<T> clazz) {
Expand Down
5 changes: 5 additions & 0 deletions dstu3/src/main/resources/hapi.properties
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ questionnaireResponseExtract.endpoint=https://cds4cpm-develop.sandbox.alphora.co
questionnaireResponseExtract.username=
questionnaireResponseExtract.password=

observationTransform.enabled=true
observationTransform.username=
observationTransform.password=
observationTransform.replaceCode=false

##################################################
# CDS-Hooks Settings
##################################################
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package org.opencds.cqf.r4.providers;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.util.BundleUtil;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.Observation;
import org.opencds.cqf.common.config.HapiProperties;

import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

import static org.opencds.cqf.common.helpers.ClientHelper.getClient;

public class ObservationProvider {

private FhirContext fhirContext;

public ObservationProvider(FhirContext fhirContext){
this.fhirContext = fhirContext;
}

@Operation(name = "$transform", idempotent = false, type = Observation.class)
public Bundle transformObservations(
@OperationParam(name = "observations") Bundle observationsBundle,
@OperationParam(name = "conceptMapURL") String conceptMapURL
) {
if(null == observationsBundle) {
throw new IllegalArgumentException("Unable to perform operation Observation$transform. No Observation bundle passed in.");
}
if(null == conceptMapURL) {
throw new IllegalArgumentException("Unable to perform operation Observation$transform. No concept map url specified.");
}
IGenericClient client = getClient(fhirContext, conceptMapURL, HapiProperties.getObservationTransformUsername(), HapiProperties.getObservationTransformPassword() );
ConceptMap transformConceptMap = client.read().resource(ConceptMap.class).withUrl (conceptMapURL).execute();
if(null == transformConceptMap) {
throw new IllegalArgumentException("Unable to perform operation Observation$transform. Unable to get concept map.");
}
List<Observation> observations = BundleUtil.toListOfResources(fhirContext, observationsBundle).stream()
.filter(resource -> resource instanceof Observation)
.map(Observation.class::cast)
.collect(Collectors.toList());
/**
* TODO - There must be a more efficient way to loop through this, but so far I have not come up with it.
*/
transformConceptMap.getGroup().forEach(group -> {
HashMap<String, ConceptMap.TargetElementComponent> codeMappings = new HashMap<>();
String targetSystem = group.getTarget();
group.getElement().forEach(codeElement -> {
codeMappings.put(codeElement.getCode(), codeElement.getTarget().get(0));
});
observations.forEach(observation -> {
if(observation.getValue().fhirType().equalsIgnoreCase("codeableconcept")){
String obsValueCode = observation.getValueCodeableConcept().getCoding().get(0).getCode();
if(obsValueCode != null) {
if (codeMappings.get(observation.getValueCodeableConcept().getCoding().get(0).getCode()) != null) {
if (HapiProperties.getObservationTransformReplaceCode()) {
observation.getValueCodeableConcept().getCoding().get(0).setCode(codeMappings.get(obsValueCode).getCode());
observation.getValueCodeableConcept().getCoding().get(0).setDisplay(codeMappings.get(obsValueCode).getDisplay());
observation.getValueCodeableConcept().getCoding().get(0).setSystem(targetSystem);
} else {
Coding newCoding = new Coding();
newCoding.setSystem(targetSystem);
newCoding.setCode(codeMappings.get(obsValueCode).getCode());
newCoding.setDisplay(codeMappings.get(obsValueCode).getDisplay());
observation.getValueCodeableConcept().getCoding().add(newCoding);
}
}
}
}
});
});
return observationsBundle;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.hl7.fhir.r4.model.*;
import org.opencds.cqf.common.config.HapiProperties;

import java.util.Collections;
import java.util.Date;

import static org.opencds.cqf.common.helpers.ClientHelper.getClient;
Expand Down Expand Up @@ -48,6 +49,10 @@ private Bundle.BundleEntryComponent extractItem(QuestionnaireResponse.Questionna
obs.setEffective(new DateTimeType(authored));
obs.setStatus(Observation.ObservationStatus.FINAL);
obs.setSubject(questionnaireResponse.getSubject());
Coding qrCategoryCoding = new Coding();
qrCategoryCoding.setCode("survey");
qrCategoryCoding.setSystem("http://hl7.org/fhir/observation-category");
obs.setCategory(Collections.singletonList(new CodeableConcept().addCoding(qrCategoryCoding)));
Coding qrCoding = new Coding();
qrCoding.setCode("74465-6");
qrCoding.setDisplay("Questionnaire response Document");
Expand All @@ -60,7 +65,24 @@ private Bundle.BundleEntryComponent extractItem(QuestionnaireResponse.Questionna
case "Coding":
obs.setValue(new CodeableConcept().addCoding(item.getAnswer().get(0).getValueCoding()));
break;
case "boolean":
obs.setValue(new BooleanType(item.getAnswer().get(0).getValueBooleanType().booleanValue()));
break;
}
Reference questionnaireResponseReference = new Reference();
questionnaireResponseReference.setReference("QuestionnaireResponse" + "/" + questionnaireResponse.getIdElement().getIdPart());
obs.setDerivedFrom(Collections.singletonList(questionnaireResponseReference));
Extension linkIdExtension = new Extension();
linkIdExtension.setUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/derivedFromLinkId");
linkIdExtension.setValue(new StringType(item.getLinkId()));
// Extension refExtension = new Extension();
// refExtension.setUrl(ExtensionEnum.GRCONTAINED.getUrl());
// if(!mReport.hasId()){
// mReport.setId(UUID.randomUUID().toString());
// }
// refExtension.setValue(new Reference("#" + mReport.getId()));
// newGuidanceResponse.addExtension(refExtension); obs.
obs.addExtension(linkIdExtension);
Bundle.BundleEntryRequestComponent berc = new Bundle.BundleEntryRequestComponent();
berc.setMethod(Bundle.HTTPVerb.PUT);
berc.setUrl("Observation/" + obs.getId());
Expand Down
6 changes: 6 additions & 0 deletions r4/src/main/java/org/opencds/cqf/r4/servlet/BaseServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,12 @@ private void resolveProviders(EvaluationProviderFactory providerFactory,
QuestionnaireProvider questionnaireProvider = new QuestionnaireProvider(this.fhirContext);
this.registerProvider(questionnaireProvider);
}
// Observation processing
if(HapiProperties.getObservationTransformEnabled()) {
ObservationProvider observationProvider = new ObservationProvider(this.fhirContext);
this.registerProvider(observationProvider);
}

}

protected <T extends IBaseResource> IFhirResourceDao<T> getDao(Class<T> clazz) {
Expand Down
7 changes: 6 additions & 1 deletion r4/src/main/resources/hapi.properties
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,14 @@ questionnaireResponseExtract.endpoint=https://cds4cpm-develop.sandbox.alphora.co
questionnaireResponseExtract.username=
questionnaireResponseExtract.password=

observationTransform.enabled=true
observationTransform.username=
observationTransform.password=
observationTransform.replaceCode=false

##################################################
# CDS-Hooks Settings
##################################################
cds_hooks.fhirServer.maxCodesPerQuery=
cds_hooks.fhirServer.expandValueSets=
cds_hooks.fhirServer.searchStyle=
cds_hooks.fhirServer.searchStyle=

0 comments on commit 8c838b6

Please sign in to comment.