In the HL7v2 to FHIR conversion CodeSystem templates, their default, i.e., "else" part in the if-then-else rule, is not working.
For example, for Patient.communication.language in the Resource/_Patient.liquid template, it has the following line to map a language code to a dictionary containing "code", "display", and "system".
{% include 'DataType/CWECodeableConcept' mapping: 'CodeSystem/Language', CWE: PID.15 -%}
In the CodeSystem/_Language.liquid template, if no matching code, it would output the input values.
{% else -%}
"code" : "{{ inCode }}",
"display" : "{{ inCode }}",
"system" : "",
{% endif -%}
My case has PID.15="English" and it has no matching code. So, I expect the following output.
{
"code" : "English",
"display" : "English",
"system" : "",
}
What happens is no output at all.
I also tried to add the following rule with no success.
{% elsif inCode == 'English' -%}
"code" : "{{ inCode }}",
"display" : "{{ inCode }}",
"system" : "",
I appears that CodeSystem/_Language.liquid does not take changes, nor does it handle no-matched codes with "else" condition. I tested the changes with the "FHIR Converter" extension in Visual Studio Code. What can I do to make the "else" condition in CodeSystem/_Language.liquid working?
Try this:
Make required changes in the /CodeSystem/CodeSystem.json file and that should work for you.
Also, update the VS Code extension to the latest version.
"CodeSystem/Language": {
"A": {
"code": "A's code",
"display": "A's display",
"system": "A's system"
},
"__default__": {
"code": "English",
"display": "English",
"system": "English"
}
},
Explanation:
The following statement is calling _DataType/CWECodeableConcept and passing 'CodeSystem/Language' to mapping parameter, and the value of PID.15 to the CWE parameter.
{% include 'DataType/CWECodeableConcept' mapping: 'CodeSystem/Language', CWE: PID.15 -%}
Inside the _CWECodeableConcept.liquid, since mapping is populated it executes the if blocks, and not the else blocks.
{% if mapping -%}
"code":"{{ CWE.1.Value | get_property: mapping, 'code' }}",
"display":"{{ CWE.1.Value | get_property: mapping, 'display' }}",
"system":"{{ CWE.1.Value | get_property: mapping, 'system' }}",
"version":"{{ CWE.1.Value | get_property: mapping, 'version' }}",
{% else -%}
"code":"{{ CWE.1.Value }}",
"display":"{{ CWE.2.Value }}",
"system":"{{ CWE.3.Value }}",
"version":"{{ CWE.7.Value }}",
{% endif -%}
Inside the if block, get_property is used to fetch the code, display, system, and version for the mapping CodeSystem/Language corresponding to the value in CWE.(1|4|10).Value.
get_property uses the /CodeSystem/CodeSystem.json file, and not the individual files under the CodeSystem folder.
The behavior of get_property is documented here. However, the documentation does not mention the name of the config file it uses. We are fixing the documentation in the next release.
For quicker turnaround, we recommend raising an issue on the FHIR converter github repo.
Related
I have a jinja template in salt that every time apply.state is ran, salt thinks it has changed when it hasn't.
The issue is that a reconfigure command executes when the file "changes", and this reconfigure would change the file from a single lined JSON to a lined JSON. For example:
salt creates the file like:
{ "key1": { "sub-key1": "sub-value1", "sub-key2": "sub-value2"}, "key2": "value2" }
but when the reconfigure command executes, it changes the file to:
{
"key1": {
"sub-key1": "sub-value1",
"sub-key2": "sub-value2"
},
"key2": "value2"
}
Is there a way to have salt create the file as a formatted JSON to begin with?
This is what I have:
{{ gb_server.secrets_file }}:
file.managed:
- source: {{ gb_server.secrets_tmpl }}
- user: {{ gb_server.secure_user }}
- group: {{ gb_server.secure_user }}
- mode: 600
- template: jinja
the template is as follow:
{%- import_yaml 'gb-server/defaults.yaml' as defaultmap -%}
{%- set secrets = gb_server_config['secrets'] -%}
{{ secrets|tojson|replace('\\\\n','\\n') }}
The values are in pillar in yaml format.
Ideally, the resulted JSON would have its key sorted alphabetically, that way the content of the resulted file wouldn't "change" and the reconfigure command wouldn't run every single time.
Is there a way to make this happen with salt?
Thank you.
It looks like file.serialize will do what you want here
{{ gb_server.secrets_file }}:
file.serialize:
- user: {{ gb_server.secure_user }}
- group: {{ gb_server.secure_user }}
- mode: 600
- data: {{ gb_server.secrets }}
- serializer: json
- serializer_opts:
- indent: 2
- sort_keys: true
i got the following and stuck by getting the right answer. i got a dict that i want to template with item.key in file name and all the values in the template.
my_dict:
name1:
{ path=/x/y/z, action=all, filter=no },
{ path=/a/b/c, action=some, filter=yes }
name2:
{ path=/z/y/x, action=nothing, filter=no },
{ path=/c/b/a, action=all, filter=yes }
tasks:
- name: generate check config
template:
src: check.j2
dest: "{{ config_dir }}/{{ item.key }}-directories.json"
owner: Own
group: Wheel
mode: 0644
with_dict:
- "{{ my_dict }}"
when:
- my_dict is defined
become: true
My template looks like
{
"configs": [
{% for value in my_dict %}
{
"path": "{{ value.path }}",
"action": "{{ value.action }}",
{% if value.filter is defined %}
"filter": "{{ value.filter }}"
{% endif %}
}{% if !loop.last %},{% endif %}
{% endfor %}
]
}
So i tested so much that now i dont see any forest cause of too many trees.
Above should result in 2 files.
File name = name1-directories.json
Content:
{
"configs": [
{
"path": /x/y/z,
"action": all,
"filter": no
},
{
"path": /a/b/c,
"action": some,
"filter": yes
}
]
}
Thx in Advance
Let me start with the following. I see some problems with your current solution.
You're template references the value of the array items with value.<key> when it should instead read item.value.<key>.
with_dict expects a dict, but you're passing an array containing a dict as the only element. In yaml, - denotes array elemtents. To use that correctly you just write: with_dict: "{{ my_dict }}"
Using the shorthand yaml syntax is discouraged in ansible as it makes the playbooks harder to read.
I would suggest you do the following:
There is a jinja2 Filter that just converts your dict to json:
{{ dict_variable | to_json }} # or
{{ dict_variable | to_nice_json }}
The second one makes it human readable. What you're currently trying to do may work ( haven't looked into it so thoroughly) but it's not pretty and error prone.
To make it work with the jinja2 filter restructure your variables at the top the following way:
my_dict:
- name1:
configs:
- path: /x/y/z
action: all
filter: no
- path: /a/b/c
action: some
filter: yes
- name2:
configs:...
When the vars are formatted like this, you can just use the copy module to print the configs to the files like this:
- name: Print the configs to the files
copy:
content: "{{ item.value | to_nice_json }}"
dest: "{{ config_dir }}/{{ item.key }}-directories.json"
with_dict: "{{ my_dict }}"
I have a variable that is an array [{'foo':1},{'bar':2}].
I want to combine it with the following hash: {'baz':3} using a set fact (?) such as my output registered variable is:
[{'foo':1, 'baz':3},{'bar':2, 'baz':3}]
I've looked into the combine filter, but it only works when I already have an hash to work with. In my case I have an array.
Is there a way to achieve that using ansible?
Actually, I have found a way. map can be used with any filters, and arguments have to be passed after a comma
- name: test
set_fact:
_test: "{{ [{'foo':1}, {'bar':2}] | map('combine', {'baz':3}) | list }}"
produces:
ok: [localhost] => {
"_test": [
{
"baz": 3,
"foo": 1
},
{
"bar": 2,
"baz": 3
}
]
}
Jinja2 doesn't have list comprehension, but I think you can use set and for loop to achieve it:
{% set outputarray = [] -%}
{% for d in inputarray -%}
{% set r = d|combine({'baz': 3}) -%}
{{ ouputarray.append(r) and '' }}
{%- endfor %}
I have a Jinja set directive as follows:
{% set mylist = [
"item 1",
"another item",
"yet another item",
] %}
I would like to add a comment to the second list item. Does Jinja support that? I've tried the following:
{% set mylist = [
"item 1",
"another item", # My comment
"yet another item",
] %}
and
{% set mylist = [
"item 1",
"another item", ## My comment
"yet another item",
] %}
, but none of them work. I'm using Jinja 2.6.
Since you are using Jinja 2.2+ then you can use whatever your environment configures for line_comment_prefix (part of the line statements feature). This feature must be enabled by the application that Jinja is embedded in (for example, in Flask, this is done by setting app.jinja_options['line_comment_prefix'] = "whatever#you$want").
app = Flask(__name__)
app.jinja_options['line_statement_prefix'] = '#'
app.jinja_options['line_comment_prefix'] = '#::'
Then you can write a template that uses line comments:
{% set mylist = [
"first item",
"another item", #:: needed for raisins - see #12345
"a third item"
] %}
If you are using Jinja 2.1 or lower then those versions do not support inline comments. However, you can use a comment block:
{#
BUG: Added "another item" because of raisins.
Don't remove it until #12345 is fixed
#}
{% set mylist = [
"item 1",
"another item",
"yet another item",
] %}
I guess everyone's going to find that out via documentation but just in case, I'll leave it here as well:
Since Jinja 2.2, line-based comments are available as well. For
example, if the line-comment prefix is configured to be ##, everything
from ## to the end of the line is ignored (excluding the newline
sign):
# for item in seq:
<li>{{ item }}</li> ## this comment is ignored
# endfor
I have variable named "network" registered in Ansible:
{
"addresses": {
"private_ext": [
{
"type": "fixed",
"addr": "172.16.2.100"
}
],
"private_man": [
{
"type": "fixed",
"addr": "172.16.1.100"
},
{
"type": "floating",
"addr": "10.90.80.10"
}
]
}
}
Is it possible to get the IP address ("addr") with type="floating" doing something like this?
- debug: var={{ network.addresses.private_man | filter type="fixed" | get "addr" }}
I know the syntax is wrong but you get the idea.
To filter a list of dicts you can use the selectattr filter together with the equalto test:
network.addresses.private_man | selectattr("type", "equalto", "fixed")
The above requires Jinja2 v2.8 or later (regardless of Ansible version).
Ansible also has the tests match and search, which take regular expressions:
match will require a complete match in the string, while search will require a match inside of the string.
network.addresses.private_man | selectattr("type", "match", "^fixed$")
To reduce the list of dicts to a list of strings, so you only get a list of the addr fields, you can use the map filter:
... | map(attribute='addr') | list
Or if you want a comma separated string:
... | map(attribute='addr') | join(',')
Combined, it would look like this.
- debug: msg={{ network.addresses.private_man | selectattr("type", "equalto", "fixed") | map(attribute='addr') | join(',') }}
I've submitted a pull request (available in Ansible 2.2+) that will make this kinds of situations easier by adding jmespath query support on Ansible. In your case it would work like:
- debug: msg="{{ addresses | json_query(\"private_man[?type=='fixed'].addr\") }}"
would return:
ok: [localhost] => {
"msg": [
"172.16.1.100"
]
}
Not necessarily better, but since it's nice to have options here's how to do it using Jinja statements:
- debug:
msg: "{% for address in network.addresses.private_man %}\
{% if address.type == 'fixed' %}\
{{ address.addr }}\
{% endif %}\
{% endfor %}"
Or if you prefer to put it all on one line:
- debug:
msg: "{% for address in network.addresses.private_man if address.type == 'fixed' %}{{ address.addr }}{% endfor %}"
Which returns:
ok: [localhost] => {
"msg": "172.16.1.100"
}