How to customize conversion rules

This page describes how to customize the conversion results.

The converter is a rule-based service so it allows users to define conversion rules using a special Domain-Specific Language (DSL), enabling customization and mapping of data elements between C-CDA and FHIR standards.

We are still working on this page, sorry for the inconvenience.

This part of functionality requires knowledge of zen language.

Setup a service with the service configuration

Here is basic information how to configure zen-project to enable the converter functionality.

More details about zen-configuration.

Default rules

Current conversion rules are stored in .edn files in the zen project.

The core.edn file describes a top-level configuration of the service:

default-config
 {:docdefs
  {:discharge-summary aidbox.ccda.docdefs/discharge-summary}

  :section-aliases
  {:allergies  ["AllergiesandIntolerancesSectioner"
                "AllergiesandIntolerancesSectioneo"]

   :encounters ["EncountersSectionentriesrequiredV3"
                "EncountersSectionentriesoptionalV3"]

    ...
  }

  :section-rules
  {"VitalSignsSectionentriesrequired"    {:entries aidbox.ccda.rules.vital-signs/rules
                                          :narrative aidbox.ccda.rules.vital-signs/narrative}
   "VitalSignsSectionentriesoptional"    {:entries aidbox.ccda.rules.vital-signs/rules
                                          :narrative aidbox.ccda.rules.vital-signs/narrative}
   "GoalsSection"                        {:entries aidbox.ccda.rules.goals/rules}
   "EncountersSectionentriesrequiredV3"  {:entries aidbox.ccda.rules.encounters/rules}
   "EncountersSectionentriesoptionalV3"  {:entries aidbox.ccda.rules.encounters/rules}

    ...

    }}

The :docdefs key defines document templates that will be explaned further.

The :section-aliases key defines the aliases for sections.

The :section-rules key defines the default conversion rules.

Each key inside :section-rules references mappings for the section entries and the narrative part of C-CDA document.

For example, this entry

  {"VitalSignsSectionentriesrequired"    {:entries aidbox.ccda.rules.vital-signs/rules
                                          :narrative aidbox.ccda.rules.vital-signs/narrative}

is related to Vital signs section mapping.

The reference :entries aidbox.ccda.rules.vital-signs/rules points to the file with corresponding mappings:

{ns aidbox.ccda.rules.vital-signs
 import #{aidbox.ccda.rules.shared.observation
          aidbox.ccda.rules.shared.narrative-fns
          aidbox.ccda.core}

 narrative
 {:zen/tags #{aidbox.ccda.core/rules}

  :rules
  [{:cda [:text :table :thead :tr]
    :const {:td ["Vital sign" "Timing" "Value and units"]}}

   {:cda [:text :table :styleCode]
    :const {:border "1" :width "100%"}}

   ...

   {:const "Narrative block"
    :cda [:text :cda/type]}]}

 rules
 {:zen/tags #{aidbox.ccda.core/rules}
  :rules
  [{:cda [:entry.VitalSignsOrganizerV3 :* :component :*
          "VitalSignObservationV2"]
    :apply-rules aidbox.ccda.rules.shared.observation/rules
    :fhir [:entry :**]}

    ...

    }

The rules syntax will be explained in the next part.

Rules DSL syntax

Here is a description of the rules syntax.

Each rule is a map with at least two keys:

{:cda [...]
 :fhir [...]}

Where [...] - is a path in the intermediate tree of an input document. Detailed explanation of the intermediate tree will be provided later.

Use the endpoint /v2/ccda/to-fhir?intermediate=true to get the intermediate tree for input FHIR-bundle and /v2/ccda/to-ccda?intermediate=true to get the one for input C-CDA document.

The path is a combination of nodes of the intermediate tree that leeds to the value of some particulare field.

For example, the path

[:entry.VitalSignsOrganizerV3 :* :component :* "VitalSignObservationV2" :value]

means get a value of the "VitalSignObservationV2" by path in the tree:

- :entry.VitalSignsOrganizerV3
  - [
      :component
      - [
          ...
          "VitalSignObservationV2"
            - value
          ...
          ... (some other components/fields)
          ...
      ]

      ...
    ]

Symbol :* means "for each" expression so [:component :*] means "for each element in :component vector".

The table below describes all kind of special symbols:

SymbolMeaningExample

:*

for each

[:component :*]

0-N number

get N-th element

[:component 0]

{:resourceType "Observation"}

filtering expression, select all maps where there is a key :resourceType with value "Observation"

[:entry.Organizer {:resourceType "Observation"}]

Defining new rules

Overriding existing rules

Sometimes, you do not need to rewrite all set of conversion rules or you just do not want to do it. For this particular purpose we have an option that will make able to rewrite only some of conversion rules.

For that purpose you can open zen-introspector by clicking Profiles in Aidbox UI. CCDA rules are stored in aidbox/ccda/rules folder. Here you will see our default set of conversion rules.

Let it be Vital Signs section narrative. It is available via #aidbox.ccda.rules.vital-signs/narrative. We do not want to display just the most popular observations, but we want to see only those observations that are really present in dataset, without any placeholders.

We create .yaml file where we say:

aidbox.ccda.rules.vital-signs/narrative:
  replace:
    - cda: [text 0]
      const:
        type: table
        header:
          - Vital sign
          - Date
          - Value and units
    - fhir: [entry *]
      cda: [text 0 rows]
      apply-rules:
        - fhir: [code]
          cda: [0]
        - fhir: [effective]
          cda: [1]
        - fhir: [value]
          cda: [2]

aidbox.ccda.rules.vital-signs/narrative - means that we are going to override narrative in Vital Signs namespace replace - this instruction means that we substitute all conversion rules to the given in .yaml file

If you need to substitute just one rule and leave all the rest there is select instruction:

aidbox.ccda.rules.encounters/narrative:
  select:
    - selector:
        cda:  [text 0 cols 1]
        fhir: [entry * type 0]
      override:
        cda:  [text 0 cols 1]
        fhir: [entry * class]

Code here says that you:

  • find the rule in aidbox.ccda.rules.encounters/narrative that is equal to the rule declared in selector

  • substitue the rule that was found to the rule that was declared in override

You can see the example of this config here .

The work with Override DSL is in progress.

DSL description

Last updated