mozzy11 / pyfhirsdc

Python scripts to help with the mapping of L2 layer to L3

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Python FHIR SDC generator from XLS/ODS files

The goal of the project is to have tool like pyxform but for fhir structure data capture

Installation

Clone repository

Clone the repository from the GitHub repositroy to your local computer

git clone https://github.com/SwissTPH/pyfhirsdc

Create and activate a python virtual environment

Create a python virtual environment to install your dependencies

cd pyfhirsdc
python -m venv venv
bash venv/bin/activate.sh

Install necessary dependencies

Install the python dependencies needed to run pyfhirsdc

python -m pip install .

Running pyfhirsdc

python -m main -[options]

input file

Config file

The config file is a json with two main section, the data within the JSON file will be used to generate the IG using pyFHIR.

processor;

"processor":{
        "inputFile":"/home/<>/WHO DAK/merged/xls_form_iraq.xlsx",
        "manual_content":"/home/<>/smart-emcare/manual",
        "outputPath":"/home/<>/smart-emcare/input",
        "cql_translator": "https://fhir.cql-translator.dk.swisstph-mis.ch/cql/translator",
        "mapping_translator": "https://fhir.dk.swisstph-mis.ch/matchbox/fhir/StructureMap",
        "default_resource_path":"./default_resources",
        "excudedWorksheets":[
        ],
        "scope":"EmCare",
        "encoding":"json",
        "generateElm" : false,
        "environment": "dev",
        "layoutMode": "DIRECTORY"
    },

Parameter Description
inputFile Path to the the input file, it is an xlsx file with several mandatory sheets
manual_content Path to manual content to be integrated into the IG that is not automatically generated by pyFHIR
outputPath Output path of the generated IG content
cql_translator URL to cql translator
mapping_translator URL to mapping translator
environment Whether the current environment is development or production, accepted values dev or prod

FHIR:

"fhir":{
        "version": "4.0.1",
        "lib_version": "0.99.99",
        "canonicalBase" : "https://fhir.dk.swisstph-mis.ch/matchbox/fhir/",
        "guideBase":"http://fhir.org/guides/who/emc-cds/",
        "activity":{
            "CodeSystem": "http://fhir.org/guides/who/anc-cds/CodeSystem/activity-codes"
        },
        "external_libraries" : {
            "FHIRHelpers" : "http://fhir.org/guides/who/anc-cds/Library/FHIRHelpers"
        }
        ,
        "usageContext":{
            "CodeSystem": "http://terminology.hl7.org/CodeSystem/usage-context-type",
            "Code": "task",
            "Display": "Workflow Task"
        }
    }

pd.

this sheet defined how the questionnaires are sequences using multiple plan definition, in the cql-tooling it was based on Decision Tables. Here each row will be, an action, that belongs to a main action with a decision ID. If an ID is provided in one row, this will be assumed as main action and the following rows will be assumed as sub actions of this action. If two following rows have the same action, they will be merged into one, joining the inputs with "OR". The delimiter to separate inputs in one action (row) is the pipe (|).

id

id of the main action

description

- Describe the action to be taken
annotation
- Will be mapped to the textEquivalent in the FHIR resource.

title

- The title of the action.

applicabilityCondition

- If this column is filled, the condition mapped with have applicability as type

startCondition

- If this column is filled, the condition mapped with have start as type

stopCondition

- If this column is filled, the condition mapped with have stop as type

constraintDescription

use to create http://hl7.org/fhir/StructureDefinition/questionnaire-constraint with constraintExpression if not MinMax
must have this structure

human::requirements

* human: string: A free text expression of the rule to display to the user if the rule is violated.
* requirements: string: An explanation of why this extension is required (for documentation purposes).

constraintExpression

can be MinMax::min value::max value 
    https://hl7.org/fhir/extensions/StructureDefinition-minValue.html
    Simple Extension of type date, dateTime, time, decimal, integer, Quantity: The inclusive lower bound on the range of allowed values for the data element.
    https://hl7.org/fhir/extensions/StructureDefinition-maxValue.html
    Simple Extension of type date, dateTime, time, decimal, integer, Quantity: The inclusive upper bound on the range of allowed values for the data element.     
else it must have this structure:

    expression::severity
    
    * severity: code: Indicates how serious violating the invariant is. (error or warning: http://hl7.org/fhir/R5/valueset-constraint-severity.html )
    * expression: string: The FHIRPath expression of the rule for computable interpretation.

    content in constraintDescription is mandatory 


    it will create http://hl7.org/fhir/StructureDefinition/questionnaire-constraint

    Complex Extension: An invariant that must be satisfied before responses to the questionnaire can be considered "complete".

message

- The action that will result

trigger

- What will trigger this decision. Must only be specified at the mainAction level

triggerType

- Type of the trigger (named-event | periodic | data-changed | data-accessed | data-access-ended )

businessRule

- Business Rule of the decision

reference

- Reference for the specific action

output

- The outcome of performing such an action

l. libraries

will create a library, same way and fields as the questionnaire

c. Condition

will work like a questionnaire with those diference

question type condition is added, thise create the activity definition based on the enableWhenCode

question type postcoordination is created, MUST be a child item of a condition, enableWhen logic is use to generate CQL that will populate the postcoordination extension in the task generated by the activityDefinition

TODO: THIS should create a questionnaire as fallback activity

q.

thoses sheets are containing the questionnaires, and the required information to de the fihr mapping via structureMap (to be confirmed) and the CQL to find back the answers based on their "label" the format is inpired by the pyxform 'survey' sheet but addapted to fhir SDC questionnaires

id

Mandatory, used as linkid

can be set to {{library}} to speficy the questionnaires libraries dependencies

type

will follow the structure [type] [option]

type
  • all fhir type but choice : use one of the basic type
  • note, equivalen to text
  • select_one option : choice when only one selection is possible
  • select_multile option : choice when multiple selections are possible
  • select_condition create a list with all the classification mapped in the questionnaire
  • select_boolean quesiton with only one checkbox
  • mapping : will not apprear on the questionnaire, just to document mapping information
  • group will start a question group, an ID is mandatory, several levels are possible
  • variable : add a variable in the questionnaire, on the questionnaire level if no parentId is specified, else on the question where id == parentId, the expression MUST be in calcualtedExpression column
option
  • [valueSet] valueSet defined in the valueSet tab
  • url::[valueSetUrl] link to a remote value set
  • candidateExpression:: will fetch the result via the , then will display the result based on the data attached to the in the choiceColum sheet

required

set to True to make sure the question is required

display

can be a list separated by || (double pipe)

parentId

used to add subItem or/and cql details

help

content for the help extension

expression

Expression can be written on several lines to clarify the sub line must refer to the parent line through the parentId column

several line with the same parentId will be joined with an OR each set of subline is joined to the parent with an AND only the first line may not have expression but then it must have '{{cql}}' in it in order to triger the conversion to library

the type of the subline should be '{{cql}}'

initialExpression (optionnal)

fhirpath/CQL code that will be executed by the api with the $populate opearation

When writing the CQL please note:

  • all observations (i.e. questions mapped to Observation) can be access via their label or id

Cql identifier (label) must only use ascii

enableWhenExpression (optionnal)

fhirpath code that will be added on the SDC enableWhenExpression extension

calculatedExpression (optionnal)

fhirpath code that will be added on the SDC calculatedExpression extension

map_extension

Will be used to implicitly flag a necessary extension. Additional information will be used to create the extension structure definition that will be referenced in the resource profile. Has the format:

Path :: min :: max
  • The id of the extension will be created by concatenating the path of the extension with the slice name
  • The value type of the extension will be derived from the type of the question
  • The reference will be derived from the map_profile column
  • For the slicing name, the question label will be used
  • Min will default to 0 and max will default * unless defined otherwise

map_resource

List of Map rules,

Some usefull complex mapping are defined on the code level

SetObservation -> for observation code or quantity SetObservationBoolean ->

example

to generate

src.a2 as a where a2.length <= 20 -> tgt.a2 = a 'rulename1'; // ignore it  if it's longer than 20 characters
src.a2 as a check a2.length <= 20 -> tgt.a2 = a 'rulename2' ; // error if it's longer than 20 characters

this will be needed

where a2.length <= 20 -> tgt.a2 = a||check a2.length <= 20 -> tgt.a2 = a

map_profile

use the create custom profiles and to create the structure map Questionnaire - Profile the details of the profile to the mapped will be in the profile tab

the based profile will be deducted from the profile, this means that the profile name MUST include the base profile name

if the base profile is Observation then the question code will be defined from the label in the {{scope}}observation library

if the base profile is condition then the question code will be defined from the label in the {{scope}}condition library

to avoid complex code in the XLSX, snippet/function could be defined in mapHelpers/custom Mapping function

add library:

id = {{library}} description = [name]::[alias]::[version] e.g FHIRHelper::FHIRHelper::4.01 version can be {{LIB_VERSION}} or {{FHIR_VERSION}} this will ne updated base on the configuration file type = mapping

valueSet

maturitiy:2

this sheet define the the valuset concept that need to be defined in the project scope the format is inpired by the pyxform 'choice' sheet

the minimal definiton is scope , valueSet and display (a code system URL), all of the concept from that codesystem will be option with {{include}} in code (to be developped, should replace url::[valueSetUrl] in the quesiton type)

the simple definition is one row per concept (those concept will be added in the custom codesystem)

Only

but "additionnal" inforamation can be added when keyword are used in the code

  • {{title}} : will set the valuesset title [display] and description [definition]
  • {{exclude}}: define the code system to be excluded [definition] , the [display] can be used to set a name to link the element to be exculde (concepts to exclude will share this name in the valueSet column)
  • {{include}}: define the code system to be include [definition] , the [display] can be used to set a name to link the element to be exculde (concepts to exclude will share this name in the valueSet column)
  • {{choiceColumn}} : Only for candidteExpression, the choice column details will be defined as a json [definition] : {"path":".last_name", "width": "30", "forDisplay":"1"}
  • {{choiceColumn}} : Only for candidteExpression, will define the URL including the query parameters

library

this sheet list the additionnal CQL required

profile

for the IG, two different profile type might be usefull

  • FHIR or WHO existing profiles
  • [scope] profile in case there is extension to be added to an existing profile

Defining a profile can be used also to create event, for example a QuestionnaireResponse profile can be created for a specific quesitonnaire.

Different profile will need to be generated:

  • [scope] Patient
  • [scope] Encounter
  • [scope] Measure: to define in which conditions some measure must be done
  • [scope] Conditions: either one per condition or per group of condition like "Emergency Conditions", "Mild Condition", "chronic conditon" etc
  • [scope] Activity: to trigger a questionnaire (CPG collectWhith) with a task (TBC)

This approach have limits,

  • an extension cannot yet be reuse in 2 profiles
  • Cardinality of the values is set to 1:1

definitionType

resource
create a recipient for extention
Extenstion
extention to add on a profile

profile

only use for Extention, define the resource on which this extenion need to be added

baseProfile

cannonical url of the based profile

cardinality

cardinalliy of the extention in the profile

map_path

THis column requires the same information that extensions need but for non extension elements.

Path :: min :: max
  • The id of the element
  • The value type of the element will be derived from the type of the question
  • The reference will be derived from the map_profile column
  • Min will default to 0 and max will default * unless defined otherwise

output files

the output file structure should follow the cqf-tooling structure

Context

this tool was started to answer WHO EmCare project needs

Credits

pyxform project cqf-tooling project

About

Python scripts to help with the mapping of L2 layer to L3

License:GNU Affero General Public License v3.0


Languages

Language:Python 99.9%Language:Shell 0.1%