Skip to content

Commit

Permalink
Engagement/jessica/15540 fhirdata specify receiver (#15800)
Browse files Browse the repository at this point in the history
* Added the ability to specify the receiver settings for the fhirdata command
* Made the receiver schema optional if the receiver is specified.
* Checking the receiver for their format and using it. if applicable. Applying enrichment schemas across the board if a receiver is specified and has them
  • Loading branch information
JessicaWNava authored Sep 11, 2024
1 parent 372c5fb commit 84c0de4
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 24 deletions.
175 changes: 154 additions & 21 deletions prime-router/src/main/kotlin/cli/ProcessFhirCommands.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ import gov.cdc.prime.router.ActionLogger
import gov.cdc.prime.router.Hl7Configuration
import gov.cdc.prime.router.Metadata
import gov.cdc.prime.router.MimeFormat
import gov.cdc.prime.router.Receiver
import gov.cdc.prime.router.azure.BlobAccess
import gov.cdc.prime.router.azure.ConditionStamper
import gov.cdc.prime.router.azure.LookupTableConditionMapper
import gov.cdc.prime.router.cli.CommandUtilities.Companion.abort
import gov.cdc.prime.router.cli.helpers.HL7DiffHelper
import gov.cdc.prime.router.common.Environment
import gov.cdc.prime.router.common.JacksonMapperUtilities
Expand Down Expand Up @@ -67,7 +69,7 @@ class ProcessFhirCommands : CliktCommand(
* The format to output the data.
*/
private val outputFormat by option("--output-format", help = "output format")
.choice(MimeFormat.HL7.toString(), MimeFormat.FHIR.toString()).required()
.choice(MimeFormat.HL7.toString(), MimeFormat.FHIR.toString())

/**
* String of file names
Expand All @@ -89,6 +91,27 @@ class ProcessFhirCommands : CliktCommand(
"-r", "--receiver-schema", help = "Receiver schema location. Required for HL7 output."
)

/**
* Name of the receiver settings to use
*/
private val receiverName by option(
"--receiver-name", help = "Name of the receiver settings to use"
)

/**
* Name of the org settings to use
*/
private val orgName by option(
"--org", help = "Name of the org settings to use"
)

/**
* Environment that specifies where to get the receiver settings
*/
private val environment by option(
"--receiver-setting-env", help = "Environment that specifies where to get the receiver settings"
)

/**
* Sender schema location
*/
Expand All @@ -107,29 +130,81 @@ class ProcessFhirCommands : CliktCommand(
val actionLogger = ActionLogger()
// Check on the extension of the file for supported operations
val inputFileType = inputFile.extension.uppercase()
val receiver = getReceiver()

when {
// HL7 to FHIR conversion
inputFileType == "HL7" && outputFormat == MimeFormat.FHIR.toString() -> {
inputFileType == "HL7" && (
outputFormat == MimeFormat.FHIR.toString() ||
(receiver != null && receiver.format == MimeFormat.FHIR)
) -> {
var fhirMessage = convertHl7ToFhir(contents).first
fhirMessage = applyEnrichmentSchemas(fhirMessage)
if (receiver != null && receiver.enrichmentSchemaNames.isNotEmpty()) {
receiver.enrichmentSchemaNames.forEach { currentSchema ->
fhirMessage = FhirTransformer(currentSchema).process(fhirMessage)
}
}
outputResult(
handleSenderAndReceiverTransforms(fhirMessage), actionLogger
)
}

// FHIR to HL7 conversion
(inputFileType == "FHIR" || inputFileType == "JSON") && outputFormat == MimeFormat.HL7.toString() -> {
outputResult(convertFhirToHl7(contents))
(inputFileType == "FHIR" || inputFileType == "JSON") && (
outputFormat == MimeFormat.HL7.toString() ||
(receiver != null && receiver.format == MimeFormat.HL7)
) -> {
if (receiver == null) {
return outputResult(convertFhirToHl7(contents))
}

var bundle = FhirTranscoder.decode(contents)
if (receiver.enrichmentSchemaNames.isNotEmpty()) {
receiver.enrichmentSchemaNames.forEach { currentSchema ->
bundle = FhirTransformer(currentSchema).process(bundle)
}
}
outputResult(
convertFhirToHl7(
FhirTranscoder.encode(bundle),
receiver.translation as Hl7Configuration,
receiver
)
)
}

// FHIR to FHIR conversion
(inputFileType == "FHIR" || inputFileType == "JSON") && outputFormat == MimeFormat.FHIR.toString() -> {
outputResult(convertFhirToFhir(contents), actionLogger)
(inputFileType == "FHIR" || inputFileType == "JSON") && (
outputFormat == MimeFormat.FHIR.toString() ||
(receiver != null && receiver.format == MimeFormat.FHIR)
) -> {
var bundle = FhirTranscoder.decode(contents)
if (receiver != null) {
if (receiver.enrichmentSchemaNames.isNotEmpty()) {
receiver.enrichmentSchemaNames.forEach { currentSchema ->
bundle = FhirTransformer(currentSchema).process(bundle)
}
}
}
outputResult(convertFhirToFhir(FhirTranscoder.encode(bundle)), actionLogger)
}

// HL7 to FHIR to HL7 conversion
inputFileType == "HL7" && outputFormat == MimeFormat.HL7.toString() -> {
val (bundle, inputMessage) = convertHl7ToFhir(contents)
inputFileType == "HL7" && (
outputFormat == MimeFormat.HL7.toString() ||
(receiver != null && receiver.format == MimeFormat.HL7)
) -> {
var (bundle, inputMessage) = convertHl7ToFhir(contents)

if (receiver != null) {
if (receiver.enrichmentSchemaNames.isNotEmpty()) {
receiver.enrichmentSchemaNames.forEach { currentSchema ->
bundle = FhirTransformer(currentSchema).process(bundle)
}
}
}

val output = convertFhirToHl7(FhirTranscoder.encode(bundle))
outputResult(output)
if (diffHl7Output != null) {
Expand All @@ -144,20 +219,67 @@ class ProcessFhirCommands : CliktCommand(
}
}

fun getReceiver(): Receiver? {
if (!environment.isNullOrBlank() && !receiverName.isNullOrBlank() && !orgName.isNullOrBlank()) {
if (!outputFormat.isNullOrBlank()) {
throw CliktError(
"Please specify either a receiver OR an output format. Not both."
)
}
val foundEnvironment = Environment.get(environment!!)
val accessToken = OktaCommand.fetchAccessToken(foundEnvironment.oktaApp)
?: abort(
"Invalid access token. " +
"Run ./prime login to fetch/refresh your access " +
"token for the $foundEnvironment environment."
)
val organizations = GetMultipleSettings().getAll(
environment = foundEnvironment,
accessToken = accessToken,
specificOrg = orgName,
exactMatch = true
)

val receivers = organizations[0].receivers.filter { receiver -> receiver.name == receiverName }
if (receivers.isNotEmpty()) {
return receivers[0]
}
} else if (outputFormat.isNullOrBlank()) {
throw CliktError(
"Output format is required if the environment, receiver, and org " +
"are not specified. "
)
}
return null
}

private val defaultHL7Configuration = Hl7Configuration(
receivingApplicationOID = null,
receivingFacilityOID = null,
messageProfileId = null,
receivingApplicationName = null,
receivingFacilityName = null,
receivingOrganization = null,
)

/**
* Convert a FHIR bundle as a [jsonString] to an HL7 message.
* @return an HL7 message
*/
private fun convertFhirToHl7(jsonString: String): Message {
private fun convertFhirToHl7(
jsonString: String,
hl7Configuration: Hl7Configuration = defaultHL7Configuration,
receiver: Receiver? = null,
): Message {
var fhirMessage = FhirTranscoder.decode(jsonString)
fhirMessage = applyEnrichmentSchemas(fhirMessage)
return when {
receiverSchema == null ->
receiverSchema == null && (receiver == null || receiver.schemaName.isBlank()) ->
// Receiver schema required because if it's coming out as HL7, it would be getting any transform info
// for that from a receiver schema.
throw CliktError(" You must specify a receiver schema using --receiver-schema.")
throw CliktError("You must specify a receiver schema using --receiver-schema.")

else -> {
receiverSchema != null -> {
val bundle = applySenderTransforms(fhirMessage)
val stamper = ConditionStamper(LookupTableConditionMapper(Metadata.getInstance()))
fhirMessage.getObservations().forEach { observation ->
Expand All @@ -169,20 +291,31 @@ class ProcessFhirCommands : CliktCommand(
context = FhirToHl7Context(
CustomFhirPathFunctions(),
config = HL7TranslationConfig(
Hl7Configuration(
receivingApplicationOID = null,
receivingFacilityOID = null,
messageProfileId = null,
receivingApplicationName = null,
receivingFacilityName = null,
receivingOrganization = null,
),
null
hl7Configuration,
receiver
),
translationFunctions = CustomTranslationFunctions(),
)
).process(bundle)
}
receiver != null && receiver.schemaName.isNotBlank() -> {
val bundle = applySenderTransforms(fhirMessage)
FhirToHl7Converter(
receiver.schemaName,
BlobAccess.BlobContainerMetadata.build("metadata", Environment.get().storageEnvVar),
context = FhirToHl7Context(
CustomFhirPathFunctions(),
config = HL7TranslationConfig(
hl7Configuration,
receiver
),
translationFunctions = CustomTranslationFunctions(),
)
).process(bundle)
}
else -> {
throw CliktError("Error state reached when trying to apply the transforms.")
}
}
}

Expand Down
16 changes: 13 additions & 3 deletions prime-router/src/main/kotlin/cli/SettingCommands.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1049,12 +1049,22 @@ class GetMultipleSettings : SettingCommand(
}
}

private fun getAll(environment: Environment, accessToken: String): List<DeepOrganization> {
fun getAll(
environment: Environment,
accessToken: String = oktaAccessToken,
specificOrg: String? = filter,
exactMatch: Boolean = false,
): List<DeepOrganization> {
// get organizations
val organizationJson = getMany(environment, accessToken, SettingType.ORGANIZATION, settingName = "")
var organizations = jsonMapper.readValue(organizationJson, Array<OrganizationAPI>::class.java)
if (filter != null) {
organizations = organizations.filter { it.name.startsWith(filter!!, ignoreCase = true) }.toTypedArray()
if (specificOrg != null) {
if (!exactMatch) {
organizations =
organizations.filter { it.name.startsWith(specificOrg, ignoreCase = true) }.toTypedArray()
} else {
organizations = organizations.filter { it.name.equals(specificOrg, ignoreCase = true) }.toTypedArray()
}
}

// get senders and receivers per org
Expand Down

0 comments on commit 84c0de4

Please sign in to comment.