Skip to content

Commit

Permalink
Merge pull request #552 from microsoft/personal/duburson/convert-api-…
Browse files Browse the repository at this point in the history
…docs

Initial documentation release FHIR Converter API *Preview*

- Update main readme for new updated container based convert.
- Readme updated with previous version section, which directs to prior versions.
- Create concept documents for resource id generation and validation.
- Add example using built in date filter to Filter and Tags documentation.
- Remove no longer relevant TempleteManagementCLI.md from main.
  • Loading branch information
dustinburson committed May 13, 2024
2 parents b3e36d9 + 077085c commit bc770d0
Show file tree
Hide file tree
Showing 47 changed files with 3,975 additions and 293 deletions.
232 changes: 53 additions & 179 deletions README.md

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions docs/Filters-and-Tags.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Filters and Tags

**This document applies to the Liquid engine. Follow [this](https://github.com/microsoft/FHIR-Converter/tree/handlebars) link for the documentation of Handlebars engine.**

## Filters

By default, Liquid provides a set of [standard filters](https://github.com/Shopify/liquid/wiki/Liquid-for-Designers#standard-filters) to assist template creation.
Expand Down Expand Up @@ -59,6 +57,7 @@ If these filters do not meet your needs, you can also write your own filters.
| format_as_date_time | Converts valid HL7v2 and C-CDA datetime to a valid FHIR datetime format. The input datetime format is datetime or partial datetime without hyphens: YYYY[MM[DD[HH[MM[SS[.S[S[S[S]]]]]]]]][+/-ZZZZ]. For example, the input 20040629175400000 will have the output 2004-06-29T17:54:00.000Z. Provides parameters to handle different time zones: preserve, utc, local. The default method is preserve. | `{{ PID.29.Value \| format_as_date_time: 'utc' }}` |
| now | Provides the current time in a specific format. The default format is yyyy-MM-ddTHH:mm:ss.FFFZ. | `{{ '' \| now: 'dddd, dd MMMM yyyy HH:mm:ss' }}` |
| add_seconds | Adds double seconds on a valid [FHIR datetime](http://hl7.org/fhir/R4/datatypes.html) (e.g.2021-01-01T00:00:00Z). Provides parameters to handle different time zones: preserve, utc, local. The default method is preserve. | `{{ "2021-01-01T00:00:00Z" \| add_seconds:100.1, 'utc' }}` |
| date | This is the standard [Liquid date filter](https://github.com/Shopify/liquid/wiki/Liquid-for-Designers#standard-filters). In the underlying DotLiquid implementation, .NET date formats are used ([standard](https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings) & [custom](https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings)). | `{{ msg.date \| date: "yyyy-MM-ddTHH:mm:ss.fff" }}` |

DateTime filters usage examples:

Expand Down
2 changes: 0 additions & 2 deletions docs/HL7v2-templates.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# HL7v2 Templates

*This document applies to the Liquid engine only. Follow [this](https://github.com/microsoft/FHIR-Converter/tree/handlebars) link for the documentation on Handlebars engine.*

For HL7v2 to FHIR conversion, we provide a total of **57 HL7v2 conversion templates**. Here, you can find a detailed information about each template, such as the mapped FHIR resource types, segments, and extensions.

## Templates
Expand Down
2 changes: 0 additions & 2 deletions docs/SnippetConcept.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Snippet Concept

**This document applies to the Liquid engine. Follow [this](https://github.com/microsoft/FHIR-Converter/tree/handlebars) link for the documentation of Handlebars engine.**

**Snippets** are helpful when creating templates for the FHIR Converter. They are "snippets of templates" that you can reference when writing your own templates, preventing you from having to rewrite the same code over again.
Within the FHIR converter release, there are seven types of snippets: **Resources**, **References**, **Data Type**, **Code Systems**, **Sections**, **Utils** and **Value Set**.
The following sections will describe the purpose of each category of released snippets and give you things to consider when you are creating your own snippets.
Expand Down
108 changes: 0 additions & 108 deletions docs/TemplateManagementCLI.md

This file was deleted.

21 changes: 21 additions & 0 deletions docs/concepts/resource-id-generation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Resource ID generation

The default templates provided with the Converter computes Resource IDs using the input data fields. In order to preserve the generated Resource IDs, the default templates provide create **PUT requests**, instead of POST requests in the generated bundles.

For **HL7v2 to FHIR conversion**, [HL7v2 DotLiquid templates](data/Templates/Hl7v2/ID) help generate FHIR resource IDs from HL7v2 messages. An ID generation template does three things: 1) extract identifiers from the input segment or field; 2) combine the identifers with resource type and base ID (optional) as hash seed; 3) compute hash as output ID.

For **C-CDA to FHIR conversion**, [C-CDA DotLiquid templates](data/Templates/Ccda/Utils) generate FHIR resource IDs in two ways: 1) [ID generation template](data/Templates/Ccda/Utils/_GenerateId.liquid) helps generate Patient ID and Practitioner ID; 2) the resource IDs for other resources are generated from the resource object directly.

For **JSON to FHIR conversion**, there is no standardized JSON input message types unlike HL7v2 messages or C-CDA documents. Therefore, instead of default templates we provide you with some sample JSON DotLiquid templates that you can use as a starting guide for your custom JSON conversion templates. You can decide how to generate the resource IDs according to your own inputs, and use our sample templates as a reference.

For **FHIR STU3 to R4 conversion**, the Resource ID from STU3 resource is copied over to corresponding R4 resource.

The Converter introduces a concept of "base resource/base ID". Base resources are independent entities, like Patient, Organization, Device, etc, whose IDs are defined as base ID. Base IDs could be used to generate IDs for other resources that relate to them. It helps enrich the input for hash and thus reduce ID collision.
For example, a Patient ID is used as part of hash input for an AllergyIntolerance ID, as this resource is closely related with a specific patient.

Below is an example where an AllergyIntolerance ID is generated, using ID/AllergyIntolerance template, AL1 segment and patient ID as its base ID.
The syntax is `{% evaluate [id] using [template] [variables] -%}`.

```liquid
{% evaluate allergyIntoleranceId using 'ID/AllergyIntolerance' AL1: al1Segment, baseId: patientId -%}
```
63 changes: 63 additions & 0 deletions docs/concepts/validation-and-postprocessing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Resource validation and post-processing

The output of converter depends on the templates as well as the quality and richness of input messages. Therefore, it is important that you review and validate the Converter output before using those in production.

In general, you can use [HL7 FHIR validator](https://wiki.hl7.org/Using_the_FHIR_Validator) to validate a FHIR resource. You may be able to fix some of the conversion issues by appropriately changing the templates. For other issues, you may need to have a post-processing step in your pipeline.

In some cases, due to lack of field level data in the incoming messages, the Converter may produce resources without useful information or even without ID. You can use `Hl7.Fhir.R4` .NET library to filter such resources in your pipeline. Here is the sample code for such purpose.

```C#
using Hl7.Fhir.Model;
using Hl7.Fhir.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;

public class PostProcessor
{
private readonly FhirJsonParser _parser = new FhirJsonParser();

public IEnumerable<Resource> FilterResources(IEnumerable<string> fhirResources)
{
return fhirResources
.Select(fhirResource => _parser.Parse<Resource>(fhirResource))
.Where(resource => !IsEmptyResource(resource))
.Where(resource => !IsIdAbsentResource(resource));
}

public bool IsEmptyResource(Resource resource)
{
try
{
var fhirResource = resource.ToJObject();
var properties = fhirResource.Properties().Select(property => property.Name);
// an empty resource contains no properties other than "resourceType" and "id"
return !properties
.Where(property => !property.Equals("resourceType"))
.Where(property => !property.Equals("id"))
.Any();
}
catch (Exception e)
{
Console.Error.WriteLine(e.Message);
// deal with the exception...
}

return false;
}

public bool IsIdAbsentResource(Resource resource)
{
try
{
return string.IsNullOrWhiteSpace(resource.Id);
}
catch (Exception e)
{
Console.Error.WriteLine(e.Message);
// deal with the exception...
}
return false;
}
}
```
124 changes: 124 additions & 0 deletions docs/deploy/Deploy-DependentResources.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
This template deploys the following:
* Storage account (if enableTemplateStoreIntegration is set to true)
* Storage account container (if enableTemplateStoreIntegration is set to true)
* Key vault (if deployKeyVault is set to true)
* User assigned identity with Key Vault Secrets User role on the Key Vault (if deployKeyVault is set to true)
* Role assignment for the user assigned identity to access the Key Vault (if deployKeyVault is set to true)
*/

@description('Location where the storage account is deployed. For list of Azure regions where Blob Storage is available, see [Products available by region](https://azure.microsoft.com/en-us/explore/global-infrastructure/products-by-region/?products=storage).')
@allowed([
'australiacentral'
'australiaeast'
'australiasoutheast'
'brazilsouth'
'canadacentral'
'canadaeast'
'centralindia'
'centralus'
'chinaeast2'
'chinanorth2'
'chinanorth3'
'eastasia'
'eastus'
'eastus2'
'francecentral'
'germanywestcentral'
'italynorth'
'japaneast'
'japanwest'
'koreacentral'
'northcentralus'
'northeurope'
'norwayeast'
'polandcentral'
'qatarcentral'
'southafricanorth'
'southcentralus'
'southeastasia'
'southindia'
'swedencentral'
'switzerlandnorth'
'uaenorth'
'uksouth'
'ukwest'
'westcentralus'
'westeurope'
'westus'
'westus2'
'westus3'
])
param location string

@description('If set to true, a storage account and blob container will be deployed with the specified names for storing custom templates.')
param deployTemplateStore bool

@description('Name of the storage account to be deployed.')
param templateStorageAccountName string = ''

@description('Name of the storage account container to be deployed.')
param templateStorageAccountContainerName string = ''

@description('If set to true, a key vault and user assigned managed identity will be deployed with the specified names.')
param deployKeyVault bool

@description('Name of the key vault to be deployed.')
param keyVaultName string = ''

@description('Name of the user-assigned managed identity to be deployed for accessing the key vault.')
param keyVaultUserAssignedIdentityName string = ''

resource templateStorageAccountCreated 'Microsoft.Storage/storageAccounts@2022-09-01' = if (deployTemplateStore) {
name: deployTemplateStore ? templateStorageAccountName : 'default'
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
properties: {}
}

resource templateStorageAccount 'Microsoft.Storage/storageAccounts/blobServices@2022-09-01' = if (deployTemplateStore) {
name: 'default'
parent: templateStorageAccountCreated
}

resource templateStorageAccountContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2022-09-01' = if (deployTemplateStore) {
name: deployTemplateStore ? templateStorageAccountContainerName : 'default'
parent: templateStorageAccount
}

resource keyVault 'Microsoft.KeyVault/vaults@2021-04-01-preview' = if (deployKeyVault) {
name: deployKeyVault ? keyVaultName : 'default'
location: location
properties: {
sku: {
family: 'A'
name: 'standard'
}
tenantId: subscription().tenantId
enableRbacAuthorization: true
}
}

resource keyVaultUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = if (deployKeyVault) {
name: deployKeyVault ? keyVaultUserAssignedIdentityName : 'default'
location: location
}

var kvSecretUserRole = '4633458b-17de-408a-b874-0445c86b69e6' // Key Vault Secrets User role
resource keyVaultSecretsUserRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (deployKeyVault) {
name: guid(resourceGroup().id, keyVaultUserAssignedIdentity.id, kvSecretUserRole)
scope: keyVault
properties: {
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', kvSecretUserRole)
principalId: deployKeyVault ? keyVaultUserAssignedIdentity.properties.principalId : 'default'
principalType: 'ServicePrincipal'
}
}

output templateStorageAccountName string = deployTemplateStore ? templateStorageAccountCreated.name : ''
output templateStorageAccountContainerName string = deployTemplateStore ? templateStorageAccountContainer.name : ''
output keyVaultName string = deployKeyVault ? keyVault.name : ''
output keyVaultUAMIName string = deployKeyVault ? keyVaultUserAssignedIdentity.name : ''

0 comments on commit bc770d0

Please sign in to comment.