Skip to content

Commit

Permalink
Removed HL7MessageParseAndConvertConfiguration
Browse files Browse the repository at this point in the history
  • Loading branch information
adegolier committed Jan 6, 2025
1 parent 306d985 commit f36a90a
Show file tree
Hide file tree
Showing 16 changed files with 69 additions and 108 deletions.
2 changes: 0 additions & 2 deletions prime-router/src/main/kotlin/SettingsProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import com.fasterxml.jackson.annotation.JsonValue
import gov.cdc.prime.router.CustomerStatus.ACTIVE
import gov.cdc.prime.router.CustomerStatus.INACTIVE
import gov.cdc.prime.router.CustomerStatus.TESTING
import gov.cdc.prime.router.fhirengine.utils.HL7Reader
import gov.cdc.prime.router.validation.IItemValidator
import gov.cdc.prime.router.validation.MarsOtcElrOnboardingValidator
import gov.cdc.prime.router.validation.MarsOtcElrValidator
Expand Down Expand Up @@ -55,7 +54,6 @@ enum class Topic(
val isUniversalPipeline: Boolean = true,
val isSendOriginal: Boolean = false,
val validator: IItemValidator = NoopItemValidator(),
val hl7ParseConfiguration: HL7Reader.Companion.HL7MessageParseAndConvertConfiguration? = null,
) {
FULL_ELR("full-elr", true, false),
ETOR_TI("etor-ti", true, false),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ class SubmissionResponseBuilder(
val isBatch = HL7Reader.isBatch(requestBody, messageCount)

if (!isBatch && messageCount == 1) {
val message = HL7Reader.parseHL7Message(requestBody, null)
val message = HL7Reader.parseHL7Message(requestBody)
val acceptAcknowledgementType = HL7Reader.getAcceptAcknowledgmentType(message)
val ackResponseRequired = acceptAcknowledgmentTypeRespondValues.contains(acceptAcknowledgementType)
if (ackResponseRequired) {
Expand Down
6 changes: 2 additions & 4 deletions prime-router/src/main/kotlin/cli/ProcessFhirCommands.kt
Original file line number Diff line number Diff line change
Expand Up @@ -523,10 +523,8 @@ class ProcessFhirCommands : CliktCommand(
// However, the library used to encode the HL7 message throws an error it there are more than 4 encoding
// characters, so this work around exists for that scenario
val stringToEncode = hl7String.replace("MSH|^~\\&#|", "MSH|^~\\&|")
val hl7message = HL7Reader.parseHL7Message(
stringToEncode,
null
)
val hl7message = HL7Reader.parseHL7Message(stringToEncode)

// if a hl7 parsing failure happens, throw error and show the message
if (hl7message.toString().lowercase().contains("failed")) {
throw CliktError("HL7 parser failure. $hl7message")
Expand Down
4 changes: 2 additions & 2 deletions prime-router/src/main/kotlin/cli/ProcessHl7Commands.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ class ProcessHl7Commands : CliktCommand(

val starterMessages = Hl7InputStreamMessageStringIterator(starterFile.byteInputStream()).asSequence()
.map { rawItem ->
HL7Reader.parseHL7Message(rawItem, null)
HL7Reader.parseHL7Message(rawItem)
}.toList()
val comparisonMessages = Hl7InputStreamMessageStringIterator(comparisonFile.byteInputStream()).asSequence()
.map { rawItem ->
HL7Reader.parseHL7Message(rawItem, null)
HL7Reader.parseHL7Message(rawItem)
}.toList()

starterMessages.forEachIndexed { counter, message ->
Expand Down
23 changes: 5 additions & 18 deletions prime-router/src/main/kotlin/fhirengine/engine/FHIRConverter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,9 @@ import gov.cdc.prime.router.fhirengine.translation.hl7.FhirTransformer
import gov.cdc.prime.router.fhirengine.translation.hl7.utils.CustomContext
import gov.cdc.prime.router.fhirengine.translation.hl7.utils.FhirPathUtils
import gov.cdc.prime.router.fhirengine.utils.FhirTranscoder
import gov.cdc.prime.router.fhirengine.utils.HL7Reader
import gov.cdc.prime.router.fhirengine.utils.HL7Reader.Companion.parseHL7Message
import gov.cdc.prime.router.fhirengine.utils.getObservations
import gov.cdc.prime.router.fhirengine.utils.getRSMessageType
import gov.cdc.prime.router.fhirengine.utils.isElr
import gov.cdc.prime.router.logging.LogMeasuredTime
import gov.cdc.prime.router.report.ReportService
import gov.cdc.prime.router.validation.IItemValidator
Expand Down Expand Up @@ -479,7 +477,7 @@ class FHIRConverter(
"format" to format.name
)
) {
getBundlesFromRawHL7(rawReport, validator, input.topic.hl7ParseConfiguration)
getBundlesFromRawHL7(rawReport, validator)
}
} catch (ex: ParseFailureError) {
actionLogger.error(
Expand Down Expand Up @@ -571,7 +569,6 @@ class FHIRConverter(
private fun getBundlesFromRawHL7(
rawReport: String,
validator: IItemValidator,
hL7MessageParseAndConvertConfiguration: HL7Reader.Companion.HL7MessageParseAndConvertConfiguration?,
): List<IProcessedItem<Message>> {
val itemStream =
Hl7InputStreamMessageStringIterator(rawReport.byteInputStream()).asSequence()
Expand All @@ -580,17 +577,16 @@ class FHIRConverter(
}.toList()

return maybeParallelize(itemStream.size, itemStream.stream(), "Generating FHIR bundles in").map { item ->
parseHL7Item(item, hL7MessageParseAndConvertConfiguration)
parseHL7Item(item)
}.map { item ->
validateAndConvertHL7Item(item, validator, hL7MessageParseAndConvertConfiguration)
validateAndConvertHL7Item(item, validator)
}.collect(Collectors.toList())
}

private fun parseHL7Item(
item: ProcessedHL7Item,
hL7MessageParseAndConvertConfiguration: HL7Reader.Companion.HL7MessageParseAndConvertConfiguration?,
) = try {
val message = parseHL7Message(item.rawItem, hL7MessageParseAndConvertConfiguration)
val message = parseHL7Message(item.rawItem)
item.updateParsed(message)
} catch (e: HL7Exception) {
item.updateParsed(
Expand All @@ -605,20 +601,11 @@ class FHIRConverter(
private fun validateAndConvertHL7Item(
item: ProcessedHL7Item,
validator: IItemValidator,
hL7MessageParseAndConvertConfiguration: HL7Reader.Companion.HL7MessageParseAndConvertConfiguration?,
): ProcessedHL7Item = if (item.parsedItem != null) {
val validationResult = validator.validate(item.parsedItem)
if (validationResult.isValid()) {
try {
val bundle = when (hL7MessageParseAndConvertConfiguration) {
null -> HL7toFhirTranslator.getHL7ToFhirTranslatorInstance().translate(item.parsedItem)
else ->
HL7toFhirTranslator
.getHL7ToFhirTranslatorInstance(
hL7MessageParseAndConvertConfiguration.hl7toFHIRMappingLocation
)
.translate(item.parsedItem)
}
val bundle = HL7toFhirTranslator.getHL7ToFhirTranslatorInstance().translate(item.parsedItem)
item.setBundle(bundle)
} catch (ex: Exception) {
item.setConversionError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,8 +442,7 @@ class TranslationSchemaManager : Logging {
HL7Reader.parseHL7Message(
Hl7InputStreamMessageStringIterator(rawValidationInput.output.byteInputStream())
.asSequence()
.first(),
null
.first()
),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ object HL7MessageHelpers : Logging {
// Grab the first message to extract some data if not set in the settings
val firstMessage = if (hl7RawMsgs.isNotEmpty()) {
try {
val message = HL7Reader.parseHL7Message(hl7RawMsgs[0], null)
val message = HL7Reader.parseHL7Message(hl7RawMsgs[0])
Terser(message)
} catch (exception: Hl7InputStreamMessageStringIterator.ParseFailureError) {
logger.warn("Unable to extract batch header values from HL7: ${hl7RawMsgs[0].take(80)} ...")
Expand Down
40 changes: 11 additions & 29 deletions prime-router/src/main/kotlin/fhirengine/utils/HL7Reader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,77 +48,59 @@ class HL7Reader {
*/
data class HL7MessageType(val msh93: String, val msh12: String, val msh213: String)

/**
* Configuration class that contains details on how to parse an HL7 message and then how
* to convert it to FHIR
*
* @param messageModelClass a class that inherits from [Message]
* @param hl7toFHIRMappingLocation the location of the mappings files to convert the message to FHIR
*/
data class HL7MessageParseAndConvertConfiguration(
val messageModelClass: Class<out Message>,
val hl7toFHIRMappingLocation: String,
)

/**
* Accepts a raw HL7 string and uses the MSH segment to detect the [HL7MessageType] which is then used
* to parse the string into an instance of [Message]. If the type is not one that is supported in
* [getHL7ParsingContext] the default HAPI parsing logic is used
*
* @param rawHL7 the HL7 string to convert into a [Message]
*
* @return a [Pair<Message, HL7MessageParseAndConvertConfiguration?>] with parsed message and optional type
* @return a [Message] with parsed message and optional type
*/
fun parseHL7Message(
rawHL7: String,
parseConfiguration: HL7MessageParseAndConvertConfiguration?,
): Message {
// A carriage return is the official segment delimiter; a newline is not recognized so we replace
// them

val carriageReturnFixedHL7 = rawHL7.replace(newLineRegex, "\r")
val hl7MessageType = getMessageType(carriageReturnFixedHL7)
return getHL7ParsingContext(hl7MessageType, parseConfiguration).pipeParser.parse(carriageReturnFixedHL7)
return getHL7ParsingContext(hl7MessageType).pipeParser.parse(carriageReturnFixedHL7)
}

/**
* Creates a HAPI context that can be used to parse an HL7 string. If no configuration is passed, the function
* will return a context with the HAPI defaults which will defer to that library to determine the kind of message
*
* @param hl7MessageParseAndConvertConfiguration optional configuration to use when creating a context
*/
private fun getHL7ParsingContext(
hl7MessageType: HL7MessageType?,
hl7MessageParseAndConvertConfiguration: HL7MessageParseAndConvertConfiguration?,
): HapiContext {
return if (hl7MessageParseAndConvertConfiguration == null) {
if (hl7MessageType?.msh93 == "ORU_R01") {
return when (hl7MessageType?.msh93) {
"ORU_R01" -> {
DefaultHapiContext(
ParserConfiguration(),
ValidationContextFactory.noValidation(),
ReportStreamCanonicalModelClassFactory(ORU_R01::class.java),
)
} else if (hl7MessageType?.msh93 == "OML_O21") {
}
"OML_O21" -> {
DefaultHapiContext(
ParserConfiguration(),
ValidationContextFactory.noValidation(),
ReportStreamCanonicalModelClassFactory(OML_O21::class.java),
)
} else if (hl7MessageType?.msh93 == "ORM_O01") {
}
"ORM_O01" -> {
DefaultHapiContext(
ParserConfiguration(),
ValidationContextFactory.noValidation(),
ReportStreamCanonicalModelClassFactory(ORM_O01::class.java),
)
} else {
}
else -> {
DefaultHapiContext(ValidationContextFactory.noValidation())
}
} else {
DefaultHapiContext(
ParserConfiguration(),
ValidationContextFactory.noValidation(),
ReportStreamCanonicalModelClassFactory(hl7MessageParseAndConvertConfiguration.messageModelClass),
)
}
}

Expand All @@ -136,7 +118,7 @@ class HL7Reader {
*/
@Throws(HL7Exception::class)
internal fun getMessageType(rawHL7: String): HL7MessageType {
val message = getHL7ParsingContext(null, null)
val message = getHL7ParsingContext(null)
.pipeParser
// In order to determine the message configuration, only parse the MSH segment since the type of message
// is required in order to accurately parse the message in its entirety
Expand Down
30 changes: 15 additions & 15 deletions prime-router/src/test/kotlin/cli/helpers/HL7DiffHelperTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ OBR|1||232270000212^ProPhase Diagnostics^2.16.840.1.114222.4.1.238646^ISO|55454-

@Test
fun `diff hl7`() {
val inputMessage = HL7Reader.parseHL7Message(originalMessage, null)
val outputMessage = HL7Reader.parseHL7Message(comparisonMessage, null)
val inputMessage = HL7Reader.parseHL7Message(originalMessage)
val outputMessage = HL7Reader.parseHL7Message(comparisonMessage)
val differences = hl7DiffHelper.diffHl7(inputMessage, outputMessage)
assertThat(differences.size).isEqualTo(15)
val differences2 = hl7DiffHelper.diffHl7(outputMessage, inputMessage)
Expand All @@ -116,7 +116,7 @@ OBR|1||232270000212^ProPhase Diagnostics^2.16.840.1.114222.4.1.238646^ISO|55454-

@Test
fun `test index structure`() {
val outputMessage = HL7Reader.parseHL7Message(comparisonMessage, null)
val outputMessage = HL7Reader.parseHL7Message(comparisonMessage)
val outputNames = outputMessage.names
val outputMap: MutableMap<String, Segment> = mutableMapOf()

Expand All @@ -133,8 +133,8 @@ OBR|1||232270000212^ProPhase Diagnostics^2.16.840.1.114222.4.1.238646^ISO|55454-

@Test
fun `test compareHl7Type primitive`() {
val inputMessage = HL7Reader.parseHL7Message(originalMessage, null)
val outputMessage = HL7Reader.parseHL7Message(comparisonMessage, null)
val inputMessage = HL7Reader.parseHL7Message(originalMessage)
val outputMessage = HL7Reader.parseHL7Message(comparisonMessage)
val inputVal = ST(inputMessage)
inputVal.value = "blah"
val outputVal = ST(outputMessage)
Expand Down Expand Up @@ -168,8 +168,8 @@ OBR|1||232270000212^ProPhase Diagnostics^2.16.840.1.114222.4.1.238646^ISO|55454-

@Test
fun `test compareHl7Type varies`() {
val inputMessage = HL7Reader.parseHL7Message(originalMessage, null)
val outputMessage = HL7Reader.parseHL7Message(comparisonMessage, null)
val inputMessage = HL7Reader.parseHL7Message(originalMessage)
val outputMessage = HL7Reader.parseHL7Message(comparisonMessage)
val inputType = ST(inputMessage)
inputType.value = "blah"
val outputType = ST(outputMessage)
Expand Down Expand Up @@ -208,7 +208,7 @@ OBR|1||232270000212^ProPhase Diagnostics^2.16.840.1.114222.4.1.238646^ISO|55454-

@Test
fun `test compareHl7Type composite`() {
val inputMessage = HL7Reader.parseHL7Message(originalMessage, null)
val inputMessage = HL7Reader.parseHL7Message(originalMessage)
val id = ID(inputMessage)
id.value = "blah"
val nm = NM(inputMessage)
Expand All @@ -224,7 +224,7 @@ OBR|1||232270000212^ProPhase Diagnostics^2.16.840.1.114222.4.1.238646^ISO|55454-
)
assertThat(sameComposite).isEmpty()

val outputMessage = HL7Reader.parseHL7Message(comparisonMessage, null)
val outputMessage = HL7Reader.parseHL7Message(comparisonMessage)
val differentComposite = hl7DiffHelper.compareHl7Type(
"",
inputMessage.msh.getField(4)[0],
Expand All @@ -239,8 +239,8 @@ OBR|1||232270000212^ProPhase Diagnostics^2.16.840.1.114222.4.1.238646^ISO|55454-

@Test
fun `test compareHl7Type different types`() {
val inputMessage = HL7Reader.parseHL7Message(originalMessage, null)
val outputMessage = HL7Reader.parseHL7Message(comparisonMessage, null)
val inputMessage = HL7Reader.parseHL7Message(originalMessage)
val outputMessage = HL7Reader.parseHL7Message(comparisonMessage)
val inputType = ST(inputMessage)
inputType.value = "blah"
val outputType = ST(outputMessage)
Expand All @@ -264,8 +264,8 @@ OBR|1||232270000212^ProPhase Diagnostics^2.16.840.1.114222.4.1.238646^ISO|55454-

@Test
fun `expect no diff messages have blank vs empty MSH 8 (ST), OBR 49 (CWE) respectively`() {
val inputMessage = HL7Reader.parseHL7Message(msgMSH8OBR49Blank, null)
val outputMessage = HL7Reader.parseHL7Message(msgMSH8OBR49Empty, null)
val inputMessage = HL7Reader.parseHL7Message(msgMSH8OBR49Blank)
val outputMessage = HL7Reader.parseHL7Message(msgMSH8OBR49Empty)
val differences = hl7DiffHelper.diffHl7(inputMessage, outputMessage)
assertThat(differences.size).isEqualTo(0)
val differences2 = hl7DiffHelper.diffHl7(outputMessage, inputMessage)
Expand All @@ -276,8 +276,8 @@ OBR|1||232270000212^ProPhase Diagnostics^2.16.840.1.114222.4.1.238646^ISO|55454-
fun `diff output, input missing segments`() {
val msg = originalMessage.split("\n").toMutableList()
msg.removeAt(1)
val inputMessage = HL7Reader.parseHL7Message(msg.joinToString("\n"), null)
val outputMessage = HL7Reader.parseHL7Message(originalMessage, null)
val inputMessage = HL7Reader.parseHL7Message(msg.joinToString("\n"))
val outputMessage = HL7Reader.parseHL7Message(originalMessage)
val differences = hl7DiffHelper.diffHl7(inputMessage, outputMessage)
// input missing seg SFT
assertThat(differences.size).isEqualTo(1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ OBX|1|CWE|94558-4^SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen by

@Test
fun `test get message template`() {
val message = HL7Reader.parseHL7Message(supportedHL7, null)
val message = HL7Reader.parseHL7Message(supportedHL7)
assertThat(
HL7MessageHelpers.messageCount(supportedHL7)
).isEqualTo(1)
Expand All @@ -57,7 +57,7 @@ OBX|1|CWE|94558-4^SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen by

@Test
fun `test get message model`() {
val supportedMessage = HL7Reader.parseHL7Message(supportedHL7, null)
val supportedMessage = HL7Reader.parseHL7Message(supportedHL7)
assertThat(
HL7MessageHelpers.messageCount(supportedHL7)
).isEqualTo(1)
Expand All @@ -74,7 +74,7 @@ ORC|NW|ORD448811^NIST EHR|||||||20120628070100|||5742200012^Radon^Nicholas^^^^^^
OBR|1|ORD448811^NIST EHR||1000^Hepatitis A B C Panel^99USL|||20120628070100|||||||||5742200012^Radon^Nicholas^^^^^^NPI^L^^^NPI
DG1|1||F11.129^Opioid abuse with intoxication,unspecified^I10C|||W|||||||||1
""".trimIndent()
val unsupportedMessage = HL7Reader.parseHL7Message(unsupportedHL7, null)
val unsupportedMessage = HL7Reader.parseHL7Message(unsupportedHL7)
assertThat(
HL7MessageHelpers.messageCount(unsupportedHL7)
).isEqualTo(1)
Expand All @@ -84,7 +84,7 @@ DG1|1||F11.129^Opioid abuse with intoxication,unspecified^I10C|||W|||||||||1
@Test
fun `test a quick translation to FHIR`() {
// Note that FHIR content will be tested as an integration test
val message = HL7Reader.parseHL7Message(supportedHL7, null)
val message = HL7Reader.parseHL7Message(supportedHL7)
assertThat(
HL7MessageHelpers.messageCount(supportedHL7)
).isEqualTo(1)
Expand All @@ -96,7 +96,7 @@ DG1|1||F11.129^Opioid abuse with intoxication,unspecified^I10C|||W|||||||||1

@Test
fun `test birth date extension addition`() {
val message = HL7Reader.parseHL7Message(supportedHL7ORMWithBirthDateTime, null)
val message = HL7Reader.parseHL7Message(supportedHL7ORMWithBirthDateTime)
assertThat(
HL7MessageHelpers.messageCount(supportedHL7ORMWithBirthDateTime)
).isEqualTo(1)
Expand All @@ -121,7 +121,7 @@ DG1|1||F11.129^Opioid abuse with intoxication,unspecified^I10C|||W|||||||||1

@Test
fun `test birth date extension is missing when birthdate is only date`() {
val message = HL7Reader.parseHL7Message(supportedHL7ORMWithBirthDate, null)
val message = HL7Reader.parseHL7Message(supportedHL7ORMWithBirthDate)
assertThat(
HL7MessageHelpers.messageCount(supportedHL7ORMWithBirthDateTime)
).isEqualTo(1)
Expand Down
Loading

0 comments on commit f36a90a

Please sign in to comment.