-
Notifications
You must be signed in to change notification settings - Fork 40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[16143] Implement Convert Step Updates #16710
base: main
Are you sure you want to change the base?
Changes from 9 commits
e5d7a5a
2891d0c
f3e19c0
12aaeb2
48cf73b
63d3649
87e3c7a
466e4d7
7224ee4
270faef
98368f9
d8a5314
44d4b60
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,6 +44,7 @@ import gov.cdc.prime.router.azure.observability.event.AzureEventServiceImpl | |
import gov.cdc.prime.router.azure.observability.event.IReportStreamEventService | ||
import gov.cdc.prime.router.azure.observability.event.ReportStreamEventName | ||
import gov.cdc.prime.router.azure.observability.event.ReportStreamEventProperties | ||
import gov.cdc.prime.router.common.BaseEngine | ||
import gov.cdc.prime.router.fhirengine.translation.HL7toFhirTranslator | ||
import gov.cdc.prime.router.fhirengine.translation.hl7.FhirTransformer | ||
import gov.cdc.prime.router.fhirengine.translation.hl7.utils.CustomContext | ||
|
@@ -52,6 +53,8 @@ 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 | ||
|
@@ -258,7 +261,7 @@ class FHIRConverter( | |
// TODO: https://github.com/CDCgov/prime-reportstream/issues/14287 | ||
FhirPathUtils | ||
|
||
val processedItems = process(format, input.blobURL, input.blobDigest, input.topic, actionLogger) | ||
val processedItems = process(format, input, actionLogger) | ||
|
||
// processedItems can be empty in three scenarios: | ||
// - the blob had no contents, i.e. an empty file was submitted | ||
|
@@ -323,6 +326,10 @@ class FHIRConverter( | |
// We know from the null check above that this cannot be null | ||
val bundle = processedItem.bundle!! | ||
transformer?.process(bundle) | ||
logger.info( | ||
"Applied transform - parentReportId=[${input.reportId}]" + | ||
", childReportId=[], schemaName=[${input.schemaName}]" | ||
) | ||
|
||
// make a 'report' | ||
val report = Report( | ||
|
@@ -382,7 +389,8 @@ class FHIRConverter( | |
mapOf( | ||
ReportStreamEventProperties.BUNDLE_DIGEST | ||
to bundleDigestExtractor.generateDigest(processedItem.bundle!!), | ||
ReportStreamEventProperties.ITEM_FORMAT to format | ||
ReportStreamEventProperties.ITEM_FORMAT to format, | ||
ReportStreamEventProperties.ENRICHMENTS to input.schemaName | ||
) | ||
) | ||
} | ||
|
@@ -450,14 +458,12 @@ class FHIRConverter( | |
*/ | ||
internal fun process( | ||
format: MimeFormat, | ||
blobURL: String, | ||
blobDigest: String, | ||
topic: Topic, | ||
input: FHIRConvertInput, | ||
actionLogger: ActionLogger, | ||
routeReportWithInvalidItems: Boolean = true, | ||
): List<IProcessedItem<*>> { | ||
val validator = topic.validator | ||
val rawReport = BlobAccess.downloadBlob(blobURL, blobDigest) | ||
val validator = input.topic.validator | ||
val rawReport = BlobAccess.downloadBlob(input.blobURL, input.blobDigest) | ||
return if (rawReport.isBlank()) { | ||
actionLogger.error(InvalidReportMessage("Provided raw data is empty.")) | ||
emptyList() | ||
|
@@ -471,7 +477,7 @@ class FHIRConverter( | |
"format" to format.name | ||
) | ||
) { | ||
getBundlesFromRawHL7(rawReport, validator, topic.hl7ParseConfiguration) | ||
getBundlesFromRawHL7(rawReport, validator, input.topic.hl7ParseConfiguration) | ||
} | ||
} catch (ex: ParseFailureError) { | ||
actionLogger.error( | ||
|
@@ -508,24 +514,32 @@ class FHIRConverter( | |
} | ||
// 'stamp' observations with their condition code | ||
if (item.bundle != null) { | ||
val isElr = if (item.bundle!!.getRSMessageType() == RSMessageType.LAB_RESULT) true else false | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be simplified to: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup. |
||
item.bundle!!.getObservations().forEach { observation -> | ||
val result = stamper.stampObservation(observation) | ||
if (!result.success) { | ||
val logger = actionLogger.getItemLogger(item.index + 1, observation.id) | ||
if (result.failures.isEmpty()) { | ||
logger.warn(UnmappableConditionMessage()) | ||
} else { | ||
logger.warn( | ||
result.failures.map { | ||
UnmappableConditionMessage( | ||
it.failures.map { it.code }, | ||
it.source | ||
// Only do this if it is an ELR item. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. comment not needed, the code is clear enough. Try to use comments to explain the "why" not the "what". In this case, no comment is needed imo. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cleaned it up. |
||
if (isElr) { | ||
val result = stamper.stampObservation(observation) | ||
if (!result.success) { | ||
val logger = actionLogger.getItemLogger(item.index + 1, observation.id) | ||
if (result.failures.isEmpty()) { | ||
logger.warn(UnmappableConditionMessage()) | ||
} else { | ||
logger.warn( | ||
result.failures.map { | ||
UnmappableConditionMessage( | ||
it.failures.map { it.code }, | ||
it.source | ||
) | ||
} | ||
) | ||
} | ||
) | ||
} | ||
} | ||
} | ||
logger.info( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need this exact log message again? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This came out of the conversation we had when we were reviewing the code previously. The gist of the conversation was that you thought we should capture every time the message was modified in some way. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But we already have a log for when a transform is applied? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed. |
||
"Applied transform - parentReportId=[${input.reportId}]" + | ||
", childReportId=[], schemaName=[${input.schemaName}]" | ||
) | ||
} | ||
item | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package gov.cdc.prime.router.fhirengine.engine | ||
|
||
/** | ||
* This class represents a way to group message types from an RS perspective. As we add additional logical | ||
* groupings, FHIRBundleHelpers.getRSMessageType will need to be updated. | ||
* | ||
*/ | ||
enum class RSMessageType { | ||
LAB_RESULT, | ||
UNKNOWN, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ import gov.cdc.prime.router.azure.ConditionStamper.Companion.BUNDLE_CODE_IDENTIF | |
import gov.cdc.prime.router.azure.ConditionStamper.Companion.BUNDLE_VALUE_IDENTIFIER | ||
import gov.cdc.prime.router.azure.ConditionStamper.Companion.conditionCodeExtensionURL | ||
import gov.cdc.prime.router.codes | ||
import gov.cdc.prime.router.fhirengine.engine.RSMessageType | ||
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.FHIRBundleHelpers.Companion.getChildProperties | ||
|
@@ -20,6 +21,7 @@ import org.hl7.fhir.r4.model.Coding | |
import org.hl7.fhir.r4.model.DateTimeType | ||
import org.hl7.fhir.r4.model.DiagnosticReport | ||
import org.hl7.fhir.r4.model.Extension | ||
import org.hl7.fhir.r4.model.MessageHeader | ||
import org.hl7.fhir.r4.model.Observation | ||
import org.hl7.fhir.r4.model.Patient | ||
import org.hl7.fhir.r4.model.Property | ||
|
@@ -116,6 +118,38 @@ fun Bundle.addProvenanceReference() { | |
} | ||
} | ||
|
||
/** | ||
* Return true if Bundle contains an ELR in the MessageHeader. | ||
* | ||
* @return true if has a MesssageHeader that contains an R01 or ORU_R01, otherwise false. | ||
*/ | ||
fun Bundle.isElr(): Boolean { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. RS currently receives test orders and results. Instead of Implementing detection for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added some code for this with only LAB_RESULT or UNKNOWN as options. Future features will need to add additional values to the enum. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have we tried moving the logic here into There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My thought on this is that, if we intend to add several potential groupings (i.e., enums in RSMessageType), then getRSMessageType would become quite large. Keeping just meaningful function names related to the message grouping type makes the switch statement clearer in my opinion. Additionally, there is an old programming rule of thumb that goes something like "no subroutine should be longer than one screen in length". This, obviously, is rather vague but speaks to keeping code segments small to increase "understandability". You'll often see this argument backed by references like The Magic Number Seven, Plus or Minus Two. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, I just think in practice in this case this won't be longer than a page etc and the downside is you'll be copy pasting this function most likely and just changing the if check in it (violating the DRY principal). That said, there is no repeated code right now, so I'll leave this as a "nit" and we can refactor if needed when we expand if you'd like. |
||
var isElr = false | ||
if (this.type == Bundle.BundleType.MESSAGE && this.entry.isNotEmpty()) { | ||
// By rule, the first entry must be a MessageHeader | ||
arnejduranovic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
val resource = this.entry[0].resource | ||
if (resource is MessageHeader) { | ||
val event = resource.event | ||
if (event is Coding && ((event.code == "R01") || (event.code == "ORU_R01"))) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are there other codes that could be ELR in the future? Throwing those codes into a set to check against would make this a bit more readable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's unknown at this point. I suspect not right now, though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unlikely |
||
isElr = true | ||
} | ||
} | ||
} | ||
return isElr | ||
} | ||
|
||
/** | ||
* Return RSMessageType based on grouping logic. | ||
* | ||
* @return RSMessageType of this Bundle. | ||
*/ | ||
fun Bundle.getRSMessageType(): RSMessageType { | ||
when { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. kotlin tip: return when {
isElr() -> RSMessageType.LAB_RESULT
else -> RSMessageType.UNKNOWN
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Gradle build fails when your suggested change is made.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops my mistake, apologies. Made the change as requested. |
||
isElr() -> return RSMessageType.LAB_RESULT | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you update this method per my suggestion above, you can just do this instead:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See my comment about your suggestion above. |
||
else -> return RSMessageType.UNKNOWN | ||
} | ||
} | ||
|
||
/** | ||
* Gets all properties for a [Base] resource recursively and filters only its references | ||
* | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some suggestions to make this log message more detailed:
childReportId
to an actual valueprocessedItem
var here, so add the tracking id of the item to this log message:processedItem.getTrackingId()
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. Changes made.