Skip to content

Commit

Permalink
Merge pull request #360 from LinuxForHealth/Example-and-test-of-custo…
Browse files Browse the repository at this point in the history
…m-messages

Example and test of custom messages
  • Loading branch information
cragun47 authored Dec 9, 2021
2 parents cae6e67 + 94db3ab commit 41cbf92
Show file tree
Hide file tree
Showing 4 changed files with 401 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/test/java/custom_packages/2.6
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# This is path to custom HL7 message implementations so HL7 parser can validate them.
org.foo.hl7.custom
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
* (C) Copyright IBM Corp. 2020, 2021
*
* SPDX-License-Identifier: Apache-2.0
*/

package io.github.linuxforhealth.hl7.message;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.File;
import java.io.IOException;
import java.util.List;

import java.io.FileOutputStream;
import java.util.Properties;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.ResourceType;

import io.github.linuxforhealth.core.config.ConverterConfiguration;
import io.github.linuxforhealth.fhir.FHIRContext;
import io.github.linuxforhealth.hl7.ConverterOptions;
import io.github.linuxforhealth.hl7.ConverterOptions.Builder;
import io.github.linuxforhealth.hl7.HL7ToFHIRConverter;
import io.github.linuxforhealth.hl7.resource.ResourceReader;
import io.github.linuxforhealth.hl7.segments.util.ResourceUtils;

// This shows how and tests the ability to create a custom Hl7 class
// Detailed documentation about how this works is found here:
// http://javadox.com/ca.uhn.hapi/hapi-base/2.1/ca/uhn/hl7v2/parser/DefaultModelClassFactory.html#packageList(java.lang.String)
// In this test, the custom class which HL7 uses for validation is placed in src/test/java/org/foo/hl7/custom/message/CUSTOM_PAT.java
// The custom message is placed in src/test/resources/additional_custom_resources/hl7/message/CUSTOM_PAT.yml
// The custom packages class is placed in src/test/java/custom_packages/2.6 and references the custom package /org/foo/hl7/custom/

public class Hl7CustomMessageTest {

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
// NOTE VALIDATION IS INTENTIONALLY NOT USED BECAUSE WE ARE CREATING RESOURCES THAT ARE NOT STANDARD
private static final ConverterOptions OPTIONS = new Builder().withPrettyPrint().build();
// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

private static final String CONF_PROP_HOME = "hl7converter.config.home";

@TempDir
static File folder;

static String originalConfigHome;

@BeforeAll
public static void saveConfigHomeProperty() {
originalConfigHome = System.getProperty(CONF_PROP_HOME);
ConverterConfiguration.reset();
ResourceReader.reset();
}

@AfterEach
public void reset() {
System.clearProperty(CONF_PROP_HOME);
ConverterConfiguration.reset();
ResourceReader.reset();
}

@AfterAll
public static void reloadPreviousConfigurations() {
if (originalConfigHome != null)
System.setProperty(CONF_PROP_HOME, originalConfigHome);
else
System.clearProperty(CONF_PROP_HOME);
}

@Test
public void testCustomPatMessage() throws IOException {

// Set up the config file
commonConfigFileSetup();

String hl7message =
"MSH|^~\\&|||||20211005105125||CUSTOM^PAT|1a3952f1-38fe-4d55-95c6-ce58ebfc7f10|P|2.6\n"
+ "PID|1|100009^^^FAC^MR|100009^^^FAC^MR||DOE^JANE||195001010000|M|||||5734421788|||U\n"
+ "PRB|1|20211005|10281^LYMPHOID LEUKEMIA NEC^ICD9||||201208061011||201208061011|||||||201208061011\n"
+ "PRB|2|20211005|11334^ABNORMALITIES OF HAIR^ICD9||||201208071000||201208071000|||||||201208071000\n"
+ "AL1|50|DA|penicillin|MO||20210629\n"
+ "AL1|50|MA|cat dander|SV|hives\\R\\ difficult breathing|20210629\n"
;

List<BundleEntryComponent> e = getBundleEntryFromHL7Message(hl7message);

// Check for the expected resources 1 patient, 2 conditions, 2 allergies
List<Resource> patientResource = ResourceUtils.getResourceList(e, ResourceType.Patient);
assertThat(patientResource).hasSize(1); // From PID

List<Resource> conditionResource = ResourceUtils.getResourceList(e, ResourceType.Condition);
assertThat(conditionResource).hasSize(2); // From 2x PRB

List<Resource> allergyIntoleranceResource = ResourceUtils.getResourceList(e, ResourceType.AllergyIntolerance);
assertThat(allergyIntoleranceResource).hasSize(2); // From 2x AL1

// Confirm that no extra resources are created
assertThat(e.size()).isEqualTo(5);
}

private static void commonConfigFileSetup() throws IOException {
File configFile = new File(folder, "config.properties");
Properties prop = new Properties();
prop.put("base.path.resource", "src/main/resources");
prop.put("supported.hl7.messages", "*"); // Must use wild card so the custom resources are found.
prop.put("default.zoneid", "+08:00");
prop.put("additional.resources.location", "src/test/resources/additional_custom_resources"); // Location of custom resources
prop.store(new FileOutputStream(configFile), null);
System.setProperty(CONF_PROP_HOME, configFile.getParent());
}

// Need custom convert sequence with options that turn off FHIR validation.
private static List<BundleEntryComponent> getBundleEntryFromHL7Message(String hl7message) {
HL7ToFHIRConverter ftv = new HL7ToFHIRConverter();
String json = ftv.convert(hl7message, OPTIONS); // Need custom options that turn off FHIR validation.
assertThat(json).isNotNull();
FHIRContext context = new FHIRContext();
IBaseResource bundleResource = context.getParser().parseResource(json);
assertThat(bundleResource).isNotNull();
Bundle b = (Bundle) bundleResource;
return b.getEntry();
}

}


220 changes: 220 additions & 0 deletions src/test/java/org/foo/hl7/custom/message/CUSTOM_PAT.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
/*
* (C) Copyright IBM Corp. 2020, 2021
*
* SPDX-License-Identifier: Apache-2.0
*/

//########################################################################
// Used for testing only. Not used in production.
//########################################################################

package org.foo.hl7.custom.message;

import ca.uhn.hl7v2.model.v26.segment.*;

import ca.uhn.hl7v2.HL7Exception;
import ca.uhn.hl7v2.parser.ModelClassFactory;
import ca.uhn.hl7v2.parser.DefaultModelClassFactory;
import ca.uhn.hl7v2.model.*;

/**
* <p>
* Represents a CUSTOM_PAT message structure. This structure
* contains the following elements:
* </p>
* <ul>
* <li>MSH (Message Header) <b> </b></li>
* <li>PID (Patient Identification) <b> </b></li>
* <li>PRB (Problem Details) <b>optional repeating</b></li>
* <li>AL1 (Allergy) <b>optional repeating</b></li>
* </ul>
*
* Sample Message:
* MSH|^~\\&|||||20210709162149||HIST^PAT|20210709162149|P|2.6
* PID|1|100021^^^FAC|100021^^^FAC||DOE^JANE||196705270000|F|||||5733551967|||U
* PRB|1|20210726|11334^ABNORMALITIES OF HAIR^ICD9||||201208070948||201208070948|||||||201208070948
* AL1|90|MA|dog|MO|itch|20210629
*/

public class CUSTOM_PAT extends AbstractMessage {

/**
* Creates a new CUSTOM_PAT message with DefaultModelClassFactory.
*/
public CUSTOM_PAT() {
this(new DefaultModelClassFactory());
}

/**
* Creates a new CUSTOM_PAT message with custom ModelClassFactory.
*/
public CUSTOM_PAT(ModelClassFactory factory) {
super(factory);
init(factory);
}

private void init(ModelClassFactory factory) {
try {
// parms: ClassName, required, repeats
this.add(MSH.class, true, false);
this.add(PID.class, true, false);
this.add(PRB.class, false, true);
this.add(AL1.class, false, true);
} catch (HL7Exception e) {
log.error("Unexpected error creating CUSTOM_PAT - this is probably a bug in the source code generator.");
}
}

/**
* Returns the version.
*/
public String getVersion() {
return "2.6";
}

/**
* Returns MSH (Message Header) - creates it if necessary
*/
public MSH getMSH() {
return getTyped("MSH", MSH.class);
}

/**
* Returns PID (Patient Identification) - creates it if necessary
*/
public PID getPID() {
return getTyped("PID", PID.class);
}

/**
* Returns the first repetition of PRB (Problem Details) - creates it if
* necessary
*/
public PRB getPRB() {
return getTyped("PRB", PRB.class);
}

/**
* Returns a specific repetition of PRB (Problem Details) - creates it if
* necessary
*
* @param rep The repetition index (0-indexed, i.e. the first repetition is at
* index 0)
* @throws HL7Exception if the repetition requested is more than one greater
* than the number of existing repetitions.
*/
public PRB getPRB(int rep) {
return getTyped("PRB", rep, PRB.class);
}

/**
* Returns the number of existing repetitions of PRB
*/
public int getPRBReps() {
return getReps("PRB");
}

/**
* Returns a non-modifiable List containing all current existing repetitions of
* PRB.
* Note that unlike {@link #getPRB()}, this method will not create any reps if
* none are already present, so an empty list may be returned.
*/
public java.util.List<PRB> getPRBAll() throws HL7Exception {
return getAllAsList("PRB", PRB.class);
}

/**
* Inserts a specific repetition of PRB (Problem Details)
* *
* @see AbstractGroup#insertRepetition(Structure, int)
*/
public void insertPRB(PRB structure, int rep) throws HL7Exception {
super.insertRepetition("PRB", structure, rep);
}

/**
* Inserts a specific repetition of PRB (Problem Details)
*
* @see AbstractGroup#insertRepetition(Structure, int)
*/
public PRB insertPRB(int rep) throws HL7Exception {
return (PRB) super.insertRepetition("PRB", rep);
}

/**
* Removes a specific repetition of PRB (Problem Details)
*
* @see AbstractGroup#removeRepetition(String, int)
*/
public PRB removePRB(int rep) throws HL7Exception {
return (PRB) super.removeRepetition("PRB", rep);
}

/**
* Returns the first repetition of AL1 (Patient Allergy Information) - creates
* it if necessary
*/
public AL1 getAL1() {
return getTyped("AL1", AL1.class);
}

/**
* Returns a specific repetition of AL1 (Patient Allergy Information) - creates
* it if necessary
* @param rep The repetition index (0-indexed, i.e. the first repetition is at
* index 0)
* @throws HL7Exception if the repetition requested is more than one greater
* than the number of existing repetitions.
*/
public AL1 getAL1(int rep) {
return getTyped("AL1", rep, AL1.class);
}

/**
* Returns the number of existing repetitions of AL1
*/
public int getAL1Reps() {
return getReps("AL1");
}

/**
* Returns a non-modifiable List containing all current existing repetitions of
* AL1.
*
* Note that unlike {@link #getAL1()}, this method will not create any reps if
* none are already present, so an empty list may be returned.
*
*/
public java.util.List<AL1> getAL1All() throws HL7Exception {
return getAllAsList("AL1", AL1.class);
}

/**
* Inserts a specific repetition of AL1 (Patient Allergy Information)
*
* @see AbstractGroup#insertRepetition(Structure, int)
*/
public void insertAL1(AL1 structure, int rep) throws HL7Exception {
super.insertRepetition("AL1", structure, rep);
}

/**
* Inserts a specific repetition of AL1 (Patient Allergy Information)
*
* @see AbstractGroup#insertRepetition(Structure, int)
*/
public AL1 insertAL1(int rep) throws HL7Exception {
return (AL1) super.insertRepetition("AL1", rep);
}

/**
* Removes a specific repetition of AL1 (Patient Allergy Information)
*
* @see AbstractGroup#removeRepetition(String, int)
*/
public AL1 removeAL1(int rep) throws HL7Exception {
return (AL1) super.removeRepetition("AL1", rep);
}

}
Loading

0 comments on commit 41cbf92

Please sign in to comment.