Custom templates

Offcourse you can create your own templates. Basically a template is a yaml file and some jinja templating (https://jinja.palletsprojects.com/) magic to make it configurable. There are two main levels defined for each template.

  1. pipeline

  2. scripts

We will make use of some yaml and jinja features to not have to define some of the variables multiple times. We added those at the top of the file in a key called basic-settings. All values (and keys) at the pipeline level are available when rendering the pipeline config.xml. All key, values at the scripts level are available when rendering script files. Values that need to be defined at both levels could be added at the beginning of the template and referenced to from both pipeline and scripts levels.

Creating a template

To do so we first create a folder ie: templates in the root of our project folder. In this folder we create a new yaml file ie: my_template.yaml. First things to define are:

  1. Version of the template defition file, this should be 1.

  2. Location where CTP can store data like quarantine and root folders.

  3. Define server properties like maxThreads and port number for the web interface.

This should look like this:

basic-settings:
  quarantine: &quarantine {{server.storage}}/quarantine/{{pipeline.name}}
  root: &root {{server.storage}}/root/{{pipeline.name}}
version: 1
pipeline:
  name: {{pipeline.name}}
  version: 1.0
  author: CTP Admin
  server:
    maxthreads: 20
    port: {{server.port}}

After defining the basic settings we can start defining the stages, and script properties that are part of the pipeline. To keep the pipeline flexible we probably want to make the import and export stages flexible like in the default templates. These should look like this:

pipeline:
  stages:
    import:
      type: {{pipeline.import_type}}Import
      {% if ((pipeline.import_type == 'Dicom') or (pipeline.import_type == 'Http')) %}
      port: {{pipeline.import_port}}
      {%endif%}
      {% if ((pipeline.import_type == 'Directory') or (pipeline.import_type == 'Archive')) %}
      path: {{pipeline.import_path}}
      {%endif%}
      quarantine: *quarantine
      root: *root
    export:
      type: {{pipeline.export_type}}Export
      {% if ((pipeline.export_type == 'Dicom') or (pipeline.export_type == 'Http')) %}
      url: {{pipeline.export_url}}
      {% elif ((pipeline.export_type == 'File') or (pipeline.export_type == 'Directory')) %}
      path: {{pipeline.export_path}}
      {%endif%}
      quarantine: *quarantine
      root: *root

This gives the option to define an import and export stage with the following properties:

  1. type: The type of import or export stage.

  2. port: The port number the import stage should listen to. (Only for Dicom and Http)

  3. path: The path where the import or export stage should read or write data to. (Only for Directory and Archive)

  4. url: The url where the export stage should send data to. (Only for Http)

This already gives a working template that can be used to create a CTP configuration. To add to option to enable/disable tracking of DICOM files we could add the following to the template:

{% if pipeline.object_tracker %}
object_input_tracker:
    type: ObjectTracker
    root: *root
{% endif %}

Adding scripts

Some stages requires scripts these can also be templated using CTP builder and just lie the pipeline these can be made configurable using Jinja templating. For example we could add a script to filter out DICOMS with series description we know might contain personal information or are not of interest in this study. So first let’s create a new script file in the scripts folder ie: SeriesFilter.script. The content of this file should look like this:

{%- for series_description in series_descriptions -%}
{%- if loop.first %}
! SeriesDescription.equalsIgnoreCase("{{series_description}}")
{%- else -%}
* !SeriesDescription.equalsIgnoreCase("{{series_description}}")
{%- endif -%}
{%- endfor -%}

This will create a filter for all series_descriptions in the variable series_descriptions. There a couple of acttions we have to do to make sure this end up in the CTP configuration:

  1. Add a stage to the pipeline.

  2. Add the script to make sure it is included in the configuration.

  3. Make sure the series descriptions are configurable.

The final pipeline template should look like:

basic-settings:
  quarantine: &quarantine {{server.storage}}/quarantine/{{pipeline.name}}
  root: &root {{server.storage}}/root/{{pipeline.name}}
version: 1
pipeline:
  name: {{pipeline.name}}
  version: 1.0
  author: CTP Admin
  server:
    maxthreads: 20
    port: {{server.port}}
  stages:
    import:
      type: {{pipeline.import_type}}Import
      {% if ((pipeline.import_type == 'Dicom') or (pipeline.import_type == 'Http')) %}
      port: {{pipeline.import_port}}
      {%endif%}
      {% if ((pipeline.import_type == 'Directory') or (pipeline.import_type == 'Archive')) %}
      path: {{pipeline.import_path}}
      {%endif%}
      quarantine: *quarantine
      root: *root
    {% if pipeline.object_tracker %}
    object_input_tracker:
        type: ObjectTracker
        root: *root
    {% endif %}
    {% if scripts.series_descriptions %}
    series_filter:
      type: Filter
      script: scripts/SeriesFilter.script
      root: *root
      quarantine: *quarantine
    {% endif %}
    export:
      type: {{pipeline.export_type}}Export
      {% if ((pipeline.export_type == 'Dicom') or (pipeline.export_type == 'Http')) %}
      url: {{pipeline.export_url}}
      {% elif ((pipeline.export_type == 'File') or (pipeline.export_type == 'Directory')) %}
      path: {{pipeline.export_path}}
      {%endif%}
      quarantine: *quarantine
      root: *root
{% if scripts.series_descriptions %}
scripts:
  series_descriptions:
    {{ scripts.series_descriptions }}
  include:
    - SeriesFilter.script
{% endif %}

This should finish the new template. Now we need to make a configuration to render the template into a configuration. The configuration looks like:

version: 2
template: my_template
server:
  port: 8080
  storage: /mnt/data
pipeline:
  name: anonymization_project
  import_type: Dicom
  import_port: 1080
  export_type: Dicom
  export_url: https://xnat.example.com:8143
  object_tracker: True
scripts:
  series_descriptions:
    - Report
    - Screenshot
    - Dose
    - Exam