Skip to content

Commit

Permalink
FM2-623: MedicationQuantityCodingTranslator should only use SAME-AS C… (
Browse files Browse the repository at this point in the history
  • Loading branch information
mogoodrich authored Sep 21, 2023
1 parent a252fa3 commit f3f3356
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

Expand Down Expand Up @@ -320,17 +321,17 @@ public void toFhirResource_shouldTranslateMedicationDrug() {

@Test
public void toFhirResource_shouldTranslateDoseQuantityAndUnits() {
Concept openmrsObject = new Concept();
CodeableConcept fhirObject = newCodeableConcept();
when(conceptTranslator.toFhirResource(any())).thenReturn(fhirObject);
String uuid = "b485f97d-3836-4aed-8c90-81b536cc6e3a";
Concept concept = new Concept();
concept.setUuid(uuid);
openmrsDispense.setDose(100d);
openmrsDispense.setDoseUnits(openmrsObject);
openmrsDispense.setDoseUnits(concept);
org.hl7.fhir.r4.model.MedicationDispense dispense = translator.toFhirResource(openmrsDispense);
Quantity quantity = dispense.getDosageInstructionFirstRep().getDoseAndRateFirstRep().getDoseQuantity();
assertThat(quantity, notNullValue());
assertThat(quantity.getValue().doubleValue(), equalTo(openmrsDispense.getDose()));
assertThat(quantity.getSystem(), equalTo("system"));
assertThat(quantity.getCode(), equalTo("code"));
assertNull(quantity.getSystem());
assertThat(quantity.getCode(), equalTo(uuid));
}

@Test
Expand Down Expand Up @@ -375,17 +376,17 @@ public void toFhirResource_shouldTranslateDosingInstructions() {

@Test
public void toFhirResource_shouldTranslateDispenseQuantityAndUnits() {
Concept openmrsObject = new Concept();
CodeableConcept fhirObject = newCodeableConcept();
when(conceptTranslator.toFhirResource(openmrsObject)).thenReturn(fhirObject);
String uuid = "b485f97d-3836-4aed-8c90-81b536cc6e3a";
Concept concept = new Concept();
concept.setUuid(uuid);
openmrsDispense.setQuantity(100d);
openmrsDispense.setQuantityUnits(openmrsObject);
openmrsDispense.setQuantityUnits(concept);
org.hl7.fhir.r4.model.MedicationDispense dispense = translator.toFhirResource(openmrsDispense);
Quantity quantity = dispense.getQuantity();
assertThat(quantity, notNullValue());
assertThat(quantity.getValue().doubleValue(), equalTo(openmrsDispense.getQuantity()));
assertThat(quantity.getSystem(), equalTo("system"));
assertThat(quantity.getCode(), equalTo("code"));
assertNull(quantity.getSystem());
assertThat(quantity.getCode(), equalTo(uuid));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,43 +11,68 @@

import javax.annotation.Nonnull;

import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.openmrs.Concept;
import org.openmrs.ConceptMap;
import org.openmrs.ConceptMapType;
import org.openmrs.module.fhir2.FhirConstants;
import org.openmrs.module.fhir2.api.FhirConceptSourceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
* This is an implementation of Coding Translator that maps a Medication Quantity Concept to/from a
* Coding in FHIR with a preferred set of systems and codes to prioritize. This will first favor
* using RxNorm as the coding system. If this is not present on the OpenMRS concept, it will next
* favor SNOMED-CT. Finally, if neither are present, it will favor the Concept UUID with a null
* system.
* using RxNorm coding with SAME-AS mapping. If this is not present on the OpenMRS concept, it will
* next favor SNOMED-CT, also with SAME-AS mapping. Finally, if neither are present, it will favor
* the Concept UUID with a null system.
*/
@Component
public class MedicationQuantityCodingTranslatorImpl extends BaseCodingTranslator {

@Autowired
private FhirConceptSourceService conceptSourceService;

@Override
public Coding toFhirResource(@Nonnull Concept concept) {
CodeableConcept codeableConcept = conceptTranslator.toFhirResource(concept);
if (codeableConcept == null) {
return null;
}

Coding coding = getCodingForSystem(codeableConcept, FhirConstants.RX_NORM_SYSTEM_URI);
if (coding == null) {
coding = getCodingForSystem(codeableConcept, FhirConstants.SNOMED_SYSTEM_URI);
}
if (coding == null) {
coding = getCodingForSystem(codeableConcept, null);
Coding coding = null;

if (concept.getConceptMappings() != null && !concept.getConceptMappings().isEmpty()) {
coding = getSameAsCodingForSystem(concept, FhirConstants.RX_NORM_SYSTEM_URI);
if (coding == null) {
coding = getSameAsCodingForSystem(concept, FhirConstants.SNOMED_SYSTEM_URI);
}
}

if (coding == null) {
coding = codeableConcept.getCodingFirstRep();
coding = createCoding(null, concept.getUuid(), concept);
}

coding.setDisplay(codeableConcept.getCoding().stream().filter(c -> c.getSystem() == null || c.getSystem().isEmpty())
.findFirst().map(Coding::getDisplay).orElse(null));

return coding;
}

private Coding getSameAsCodingForSystem(Concept concept, String system) {
for (ConceptMap conceptMap : concept.getConceptMappings()) {
String conceptSourceUrl = conceptSourceService
.getUrlForConceptSource(conceptMap.getConceptReferenceTerm().getConceptSource());
if (conceptSourceUrl != null && conceptSourceUrl.equals(system)
&& conceptMap.getConceptMapType().getUuid().equals(ConceptMapType.SAME_AS_MAP_TYPE_UUID)) {
return createCoding(system, conceptMap.getConceptReferenceTerm().getCode(), concept);
}
}
return null;
}

private Coding createCoding(String system, String code, Concept concept) {
Coding coding = new Coding();
coding.setSystem(system);
coding.setCode(code);
coding.setDisplay(concept.getDisplayString());
return coding;
}

public void setConceptSourceService(FhirConceptSourceService conceptSourceService) {
this.conceptSourceService = conceptSourceService;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public void setup() {

quantityCodingTranslator = new MedicationQuantityCodingTranslatorImpl();
quantityCodingTranslator.setConceptTranslator(conceptTranslator);
quantityCodingTranslator.setConceptSourceService(conceptSourceService);

timingTranslator = new MedicationRequestTimingTranslatorImpl();
timingTranslator.setConceptTranslator(conceptTranslator);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public void setup() {

quantityCodingTranslator = new MedicationQuantityCodingTranslatorImpl();
quantityCodingTranslator.setConceptTranslator(conceptTranslator);

quantityCodingTranslator.setConceptSourceService(conceptSourceService);
LocaleUtility.setLocalesAllowedListCache(Arrays.asList(Locale.ENGLISH));
}

Expand Down Expand Up @@ -112,9 +112,19 @@ public void toFhirResource_shouldTranslateMedicationQuantityPreferringSnomedCtIf

@Test
public void toFhirResource_shouldTranslateMedicationQuantityDefaultingToUuid() {

ConceptMapType notSameAs = new ConceptMapType();
notSameAs.setUuid("5d4b9c92-ed26-45ca-8f6e-08c48b00648d"); // this is just a random uuid, so should *not* be a match for SAME_AS_MAP_TYPE_UUID

ConceptSource snomed = new ConceptSource();
snomed.setHl7Code(Duration.SNOMED_CT_CONCEPT_SOURCE_HL7_CODE);
when(conceptSourceService.getUrlForConceptSource(snomed)).thenReturn(FhirConstants.SNOMED_SYSTEM_URI);

Concept mg = new Concept();
mg.setUuid(CONCEPT_UUID);
mg.addName(new ConceptName("mg", Locale.ENGLISH));
mg.addConceptMapping(
new ConceptMap(new ConceptReferenceTerm(snomed, "snomed-ct-mg-code-not-same-as", "snomed"), notSameAs));

Coding result = quantityCodingTranslator.toFhirResource(mg);
assertThat(result, notNullValue());
Expand Down

0 comments on commit f3f3356

Please sign in to comment.