Skip to content

Commit

Permalink
CA observation transforms from local code to LOINC or PLT (#1306)
Browse files Browse the repository at this point in the history
* Create 020_CA_ORU_R01_CDPH_OBX_to_LOINC_0_initial_message.hl7

Copied from the previous set of HL7 test data, with added OBX examples of the new mapping

* Update 020_CA_ORU_R01_CDPH_OBX_to_LOINC_0_initial_message.hl7

Fixed typo on some local codes

* Adding translated example file #20

* Addin skeleton for transformation

* WIP - add initial implementation of MapLocalObservationCode transform

* Adding unit test for a single observation

* Adding test for local code mappable to a PLT

* Update MapLocalObservationCodes.java

Add the mapped coding at the beginning of the coding list

* Small code cleanup

* Add tests for when the initial code is a LOINC code

* Refactor tests to remove duplicated code

* Add assertion to evaluateCoding

* Adding test for when primary code is also populated

* Add tests for an unmapped local code and empty observation identifer

* Updates to transformation to pass first 2 test cases

* WIP - multiple observation test

* All curent tests pass, but the code is not pretty and has at least one edge case unnacounted for

* Complete the multiple observations test, add the remaining mappings to the lookup

* Minor cleanup and refactoring

* Replace for() loop with if check for a single coding on the observation

* Add MapLocalObservationCodes to transformation_definitions.json

* refactor:
logging for when local codes not found
added method to extract MSH10 to HapiHelper class

* Update sender in log warning, restore mock logger in unit tests

* Commenting out the PLT codes that haven't been mapped yet so they will cause warnings for now

* refactor helper method for msh10

* Minor naming tweaks and comment updates

* add null check for cwe extension

* Address null check for getSystem method

* Missing paren

* refactor:
added hascodingExtensionWithUrl helper method to HapiHelper
added getCodingExtensionByUrl helper method to HapiHelper

* refactor:
added hasCodingSytem helper method to HapiHelper
added getCodingSystem helper method to HapiHelper

* Removed comments and empty lines

* spotlessApply

* WIP: transform refactoring - flatten conditional blocks

* Refactor transform into simpler methods

* Update minor dependencies to v5.4 (#1339)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Update HapiHelper.java

Replace unused assignment with inline return

* Update dependency com.azure:azure-storage-blob to v12.28.0 (#1344)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Update Terraform azurerm to v4.3.0 (#1345)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Move urlForCodeType to HapiHelper for future reusability

* Adding tests for code coverage and addressing Sonar findings

* Adding tests for code coverage and addressing Sonar findings

* Adding tests for null and empty bundle for resourcesInBundle

* Adding tests for urlForCodeType

* HapiHelper: unit test for when bundle is null

* Updated RS Hurl Script to Replace MSH Header When Not Toggled (#1342)

* Updating hurl scripts
* Fixed typo in readme, create temp folder, write scrambled file into temp folder
* Fixed directory creation, resolved issue with sed command not copying rest of file
* updated readme to include toggle in options, add comments to scramble doing, alerted user to scrambling, removed folder creation for individual file
* replaced msh header for oru and orm message types
* added msh header replacement for oml message type
* switched to msh-9.2 as subfield in conditional
* remove temp directory after hurl script runs

---------

Co-authored-by: jcrichlake <jeff.crichlake@agile6.com>
Co-authored-by: Sylvie <sschuresko@flexion.us>
Co-authored-by: jcrichlake <145698165+jcrichlake@users.noreply.github.com>
Co-authored-by: saquino0827 <saquino@flexion.us>

* Add control id test, update test name

* Fix order of mockLogger registration and injection

* Fixed prod prefix mapping

Co-Authored-By: halprin <halprin@users.noreply.github.com>

* Add post-TI and post-RS example files

* Update dependency gradle to v8.10.2 (#1347)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Add final 5 PLT mappings; update mapping for 99717-50

* Add comment to clarify behavior of adding the mapped code

---------

Co-authored-by: Joel Biskie <jbiskie@flexion.us>
Co-authored-by: jorge Lopez <jlopez@flexion.us>
Co-authored-by: Tiffini Johnson <tjohnson@flexion.us>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Bella L. Quintero <96704946+pluckyswan@users.noreply.github.com>
Co-authored-by: jcrichlake <jeff.crichlake@agile6.com>
Co-authored-by: Sylvie <sschuresko@flexion.us>
Co-authored-by: jcrichlake <145698165+jcrichlake@users.noreply.github.com>
Co-authored-by: saquino0827 <saquino@flexion.us>
Co-authored-by: James Herr <jherr@flexion.us>
Co-authored-by: halprin <halprin@users.noreply.github.com>
Co-authored-by: Jorge Lopez <49923512+jorg3lopez@users.noreply.github.com>
  • Loading branch information
13 people authored Sep 28, 2024
1 parent 3cf9440 commit c8f175b
Show file tree
Hide file tree
Showing 10 changed files with 27,512 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package gov.hhs.cdc.trustedintermediary.etor.messages;

/**
* Represents an identifier triplet, consisting of a code, display value, and coding system.
* Identifiers are used in HL7 fields such as OBR-4 and OBX-3. These fields contain two identifier
* triplets, one in subfields 1/2/3, and an alternate in subfields 4/5/6. For reference, see: <a
* href="https://hl7-definition.caristix.com/v2/HL7v2.5.1/Fields/OBX.3"</a>
*/
public record IdentifierCode(String code, String display, String codingSystem) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package gov.hhs.cdc.trustedintermediary.etor.ruleengine.transformation.custom;

import gov.hhs.cdc.trustedintermediary.context.ApplicationContext;
import gov.hhs.cdc.trustedintermediary.etor.messages.IdentifierCode;
import gov.hhs.cdc.trustedintermediary.etor.ruleengine.FhirResource;
import gov.hhs.cdc.trustedintermediary.etor.ruleengine.transformation.CustomFhirTransformation;
import gov.hhs.cdc.trustedintermediary.external.hapi.HapiHelper;
import gov.hhs.cdc.trustedintermediary.wrappers.Logger;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.StringType;

/**
* Maps local observation codes to LOINC/PLT codes. In order to map a code, the observation must
* have a single local code with cwe-coding extension 'alt-coding'. The mapped LOINC or PLT code is
* added to the observation as an additional observation coding with cwe-coding extension 'coding'.
* When converted to/from HL7, these codings correspond to OBX-3.4/5/6 for the local code, and
* OBX-3.1/2/3 for the LOINC/PLT code.
*/
public class MapLocalObservationCodes implements CustomFhirTransformation {
protected final Logger logger = ApplicationContext.getImplementation(Logger.class);

private HashMap<String, IdentifierCode> codingMap;

public MapLocalObservationCodes() {
initMap();
}

@Override
public void transform(FhirResource<?> resource, Map<String, String> args) {
var bundle = (Bundle) resource.getUnderlyingResource();
var observations = HapiHelper.resourcesInBundle(bundle, Observation.class);

for (Observation obv : observations.toList()) {
var codingList = obv.getCode().getCoding();

if (codingList.size() != 1) {
continue;
}

var coding = codingList.get(0);
if (!hasLocalCodeInAlternateCoding(coding)) {
continue;
}

var identifier = codingMap.get(coding.getCode());
if (identifier == null) {
logUnmappedLocalCode(bundle, coding);
continue;
}

var mappedCoding = getMappedCoding(identifier);

// Add the mapped code as the first in the list, ahead of the existing alternate code
codingList.add(0, mappedCoding);
}
}

private Boolean hasLocalCodeInAlternateCoding(Coding coding) {
if (!HapiHelper.hasCodingExtensionWithUrl(coding, HapiHelper.EXTENSION_CWE_CODING)) {
return false;
}

if (!HapiHelper.hasCodingSystem(coding)) {
return false;
}

var cwe =
HapiHelper.getCodingExtensionByUrl(coding, HapiHelper.EXTENSION_CWE_CODING)
.getValue()
.toString();
var codingSystem = HapiHelper.getCodingSystem(coding);

return Objects.equals(cwe, "alt-coding") && HapiHelper.LOCAL_CODE_URL.equals(codingSystem);
}

private void logUnmappedLocalCode(Bundle bundle, Coding coding) {
var msh41Identifier = HapiHelper.getMSH4_1Identifier(bundle);
var msh41Value = msh41Identifier != null ? msh41Identifier.getValue() : null;

logger.logWarning(
"Unmapped local code detected: '{}', from sender: '{}', message Id: '{}'",
coding.getCode(),
msh41Value,
HapiHelper.getMessageControlId(bundle));
}

private Coding getMappedCoding(IdentifierCode identifierCode) {
var mappedCoding =
new Coding(
HapiHelper.urlForCodeType(identifierCode.codingSystem()),
identifierCode.code(),
identifierCode.display());
mappedCoding.addExtension(HapiHelper.EXTENSION_CWE_CODING, new StringType("coding"));

mappedCoding.addExtension(
HapiHelper.EXTENSION_CODING_SYSTEM, new StringType(identifierCode.codingSystem()));

return mappedCoding;
}

private void initMap() {
this.codingMap = new HashMap<>();
// ALD
codingMap.put(
"99717-32",
new IdentifierCode(
"85269-9",
"X-linked Adrenoleukodystrophy (X- ALD) newborn screen interpretation",
HapiHelper.LOINC_CODE));
codingMap.put(
"99717-33",
new IdentifierCode(
"85268-1",
"X-linked Adrenoleukodystrophy (X- ALD) newborn screening comment-discussion",
HapiHelper.LOINC_CODE));
codingMap.put(
"99717-34",
new IdentifierCode(
"PLT325",
"ABCD1 gene mutation found [Identifier] in DBS by Sequencing",
HapiHelper.PLT_CODE));
// CAH
codingMap.put(
"99717-6",
new IdentifierCode(
"53340-6",
"17-Hydroxyprogesterone [Moles/volume] in DBS",
HapiHelper.LOINC_CODE));
// CF
codingMap.put(
"99717-35",
new IdentifierCode(
"PLT3289",
"CFTR gene mutation found [Interpretation] in DBS by Sequencing",
HapiHelper.PLT_CODE));
codingMap.put(
"99717-36",
new IdentifierCode(
"PLT3290",
"CFTR gene variant found [Identifier] in DBS by Sequencing comments/discussion",
HapiHelper.PLT_CODE));
// MPS I
codingMap.put(
"99717-48",
new IdentifierCode(
"PLT3258",
"IDUA gene mutations found [Identifier] in DBS by Sequencing",
HapiHelper.PLT_CODE));
codingMap.put(
"99717-44",
new IdentifierCode(
"PLT3291",
"IDUA gene variant analysis in DBS by Sequencing comments/discussion",
HapiHelper.PLT_CODE));
// MPS II
codingMap.put(
"99717-50",
new IdentifierCode(
"PLT3294",
"IDS gene mutations found [Identifier] in Dried Bloodspot by Molecular genetics method",
HapiHelper.PLT_CODE));
// Pompe
codingMap.put(
"99717-47",
new IdentifierCode(
"PLT3252",
"GAA gene mutation found [Identifier] in DBS by Sequencing",
HapiHelper.PLT_CODE));
codingMap.put(
"99717-46",
new IdentifierCode(
"PLT3292",
"GAA gene variant analysis in DBS by Sequencing comments/discussion",
HapiHelper.PLT_CODE));
// SMA
codingMap.put(
"99717-60",
new IdentifierCode(
"PLT3293",
"SMN1 exon 7 deletion analysis in DBS by Sequencing",
HapiHelper.PLT_CODE));
}
}
15 changes: 15 additions & 0 deletions etor/src/main/resources/transformation_definitions.json
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,21 @@
}
]
},
{
"name": "ucsdOruMapLocalObservationCodes",
"description": "Maps local observation codes in OBX-3.4/5/6 to LOINC/PLT codes in OBX-3.1/2/3",
"message": "",
"conditions": [
"Bundle.entry.resource.ofType(MessageHeader).destination.receiver.resolve().identifier.where(extension.value = 'HD.1').value in ('R797' | 'R508')",
"Bundle.entry.resource.ofType(MessageHeader).event.code = 'R01'"
],
"rules": [
{
"name": "MapLocalObservationCodes",
"args": {}
}
]
},
{
"name": "ucsdOruCopyOrcOrderProviderToObrOrderProvider",
"description": "Copies the value from ORC12 and uses this value to replace the value in OBR16",
Expand Down
Loading

0 comments on commit c8f175b

Please sign in to comment.