Stream: IG creation
Topic: Using liquid
Grahame Grieve (Apr 14 2020 at 06:22):
@Keith Boone asked for more documentation about how to use liquid. So here goes:
Grahame Grieve (Apr 14 2020 at 06:23):
in your IG you define a parameter path-liquid
that points to a directory where you have liquid templates. E.g. path-liquid
=liquid
Grahame Grieve (Apr 14 2020 at 06:24):
then, in that directory, you have a series of files like Patient.liquid
- .e.g with the name of the resource that they are the template for
Grahame Grieve (Apr 14 2020 at 06:25):
the liquid template will be executed automatically when generating the narrative for any resource of that type.
Grahame Grieve (Apr 14 2020 at 06:25):
the liquid spec is found here: https://confluence.hl7.org/display/FHIR/FHIR+Liquid+Profile
Grahame Grieve (Apr 14 2020 at 06:25):
here's an example:
Grahame Grieve (Apr 14 2020 at 06:34):
<div xmlns="http://www.w3.org/1999/xhtml"> <table> <tbody> <tr> <td> Name</td> <td> {%loop name in Patient.name%} <b>{{name.family}}</b>, {{name.given}} </br> {%endloop%} </td> </tr> <tr> <td> Address</td> <td>{{Patient.address.text}}</td> </tr> <tr> <td> Contacts</td> <td> Home: unknown. Work: (03) 5555 6473</td> </tr> {%if Patient.identifier.exists()%} <tr> <td> Id</td> <td> {{identifier[0].type.text}}: {{identifier[0].value}}</td> </tr> {%endif%} </tbody> </table> </div>
Grahame Grieve (Apr 14 2020 at 06:38):
there's a rather useful FHIRPath function defined in the IG publisher called .render()
which applies to data types which takes a lot of the finickity work out of this - you can use it on any data type, and it will generate the text that it think best represents the data type
Grahame Grieve (Apr 14 2020 at 06:39):
e.g. the fragment above, you want to render and address... well, you can write liquid for all the possible combinations of Address... you can just do .render() and let my code do it.. and mostly my code is pretty robust
Grahame Grieve (Apr 14 2020 at 06:40):
Keith - let me know if you want more... and it would be lovely if someone turned this into doco somewhere,,,?
Jose Costa Teixeira (Apr 14 2020 at 07:47):
Somewhere = the guidance IG?
Grahame Grieve (Apr 14 2020 at 09:59):
probably confluence for thta
Grahame Grieve (Apr 14 2020 at 09:59):
though an example in the guidance IG would also be excellent
Eric Haas (Jun 04 2020 at 21:13):
@Grahame Grieve will .render() work for references and url so that you get a link? It currently causes an error when I try to use it like so...
<ul>
{% for var in Subscription.modifierExtension %}
<li>(<strong>url:</strong> {{var.url.render()}}, <strong>value: </strong>{{var.valueUri.render()}})</li>
{% endfor %}
</ul>
Eric Haas (Jun 04 2020 at 21:14):
there is a " not yet implemented error" error
Grahame Grieve (Jun 04 2020 at 21:19):
have you got a full stack?
Eric Haas (Jun 04 2020 at 21:30):
Load Content (00:56.0332)
Processing Conformance Resources (00:56.0852)
Publishing Content Failed: Not done yet (IGPublisherHostServices.resolveFunction) (00:58.0736)
(00:58.0737)
Use -? to get command line help (00:58.0737)
(00:58.0737)
Stack Dump (for debugging): (00:58.0738)
org.apache.commons.lang3.NotImplementedException: Not done yet (IGPublisherHostServices.resolveFunction)
at org.hl7.fhir.igtools.publisher.Publisher$IGPublisherHostServices.resolveFunction(Publisher.java:377)
at org.hl7.fhir.r5.utils.LiquidEngine.resolveFunction(LiquidEngine.java:623)
at org.hl7.fhir.r5.utils.FHIRPathEngine.parseExpression(FHIRPathEngine.java:902)
at org.hl7.fhir.r5.utils.FHIRPathEngine.parseExpression(FHIRPathEngine.java:938)
at org.hl7.fhir.r5.utils.FHIRPathEngine.parseExpression(FHIRPathEngine.java:938)
at org.hl7.fhir.r5.utils.FHIRPathEngine.parse(FHIRPathEngine.java:369)
at org.hl7.fhir.r5.utils.FHIRPathEngine.parse(FHIRPathEngine.java:362)
at org.hl7.fhir.r5.utils.LiquidEngine$LiquidStatement.evaluate(LiquidEngine.java:141)
at org.hl7.fhir.r5.utils.LiquidEngine$LiquidFor.evaluate(LiquidEngine.java:268)
at org.hl7.fhir.r5.utils.LiquidEngine.evaluate(LiquidEngine.java:102)
at org.hl7.fhir.r5.renderers.LiquidRenderer.render(LiquidRenderer.java:65)
at org.hl7.fhir.r5.renderers.ResourceRenderer.render(ResourceRenderer.java:76)
at org.hl7.fhir.igtools.publisher.Publisher.generateNarratives(Publisher.java:1032)
at org.hl7.fhir.igtools.publisher.Publisher.loadConformance(Publisher.java:3605)
at org.hl7.fhir.igtools.publisher.Publisher.createIg(Publisher.java:834)
at org.hl7.fhir.igtools.publisher.Publisher.execute(Publisher.java:696)
at org.hl7.fhir.igtools.publisher.Publisher.main(Publisher.java:7430)
(venv37) Erics-MacBook-Air-2:subscription-backport-ig ehaas$
Eric Haas (Jun 04 2020 at 21:31):
it could be user error or misuse. i am not sure what it would do for a url.
Eric Haas (Jun 05 2020 at 02:50):
I got this to work on Patient and Subscription but not on Bundle. Is there a reason why can't use on Bundles?
Eric Haas (Jun 05 2020 at 02:53):
@Grahame Grieve ?
Grahame Grieve (Jun 05 2020 at 03:25):
narrative generation won't work for bundles since they don't have narrative
Eric Haas (Jun 05 2020 at 18:44):
Ah stupid me...the Ig publisher is generating a display for them nonetheless, is that a template used by the publisher and can that be customized?
Grahame Grieve (Jun 05 2020 at 20:48):
to some degree - some bundles are rendered differently, but other than the special cases, the rendering is a series of html fragments separated by <hr/>, and where the default renderer is used for each entry. So liquid would apply
Jose Costa Teixeira (Feb 20 2021 at 16:40):
I need to use this to render PlanDefinitions, but only a specific profile of PlanDefinitions.
Jose Costa Teixeira (Feb 20 2021 at 16:43):
Is this a big change to ask?
Jose Costa Teixeira (Feb 23 2021 at 22:19):
@Grahame Grieve the reason for me to ask this is:
I want to render some resources using some liquid templates. For this, I need to tell the publisher:
- resource type A, profile 1 - liquid template X
- resource type A, profile 2 - liquid template Y
- resource type B, profile 1 - liquid template Z
For this I need to tell the template what to do for each. Currently I'd have to do a big IF THEN ELSE.
Jose Costa Teixeira (Feb 23 2021 at 22:19):
Some of these resources are expected to be custom resources, so i need to differentiate the flavours
Grahame Grieve (Feb 24 2021 at 03:23):
at the moment you can't have different liquid templates for different profiles
Jose Costa Teixeira (Feb 24 2021 at 05:48):
Is that something we could expose? Or too much work?
Grahame Grieve (Feb 24 2021 at 06:03):
I don't know. I haven't really thought about it
Jose Costa Teixeira (Feb 24 2021 at 06:38):
If in my liquid folder I have not only plandefinition.liquid
but also plandefinition-homecare.liquid
and plandefinition-medicationtreatment.liquid
and if the publisher would know the difference (i.e. homecare
and medicationtreatment
are the profile ids) that would be helpful
Richard Townley-O'Neill (Feb 25 2021 at 04:57):
Is there a way to discover which liquid variables are defined in an IG?
I have found https://confluence.hl7.org/display/FHIR/FHIR+Liquid+Profile but values like {{ page.title }} are not mentioned.
Grahame Grieve (Feb 25 2021 at 04:58):
you can read the jekyll documentation ,and look in the _data directory
Grahame Grieve (Feb 25 2021 at 04:58):
because that's what jekyll gets access too,
Grahame Grieve (Feb 25 2021 at 04:58):
if that's what you mean by liquid.
Richard Townley-O'Neill (Feb 25 2021 at 05:01):
Thanks
I found some stuff in temp\pages\_data
Where is the FHIR IG docco?
Grahame Grieve (Feb 25 2021 at 05:13):
actually, I'm not sure what you're asking about. there's 2 different contexts in which liquid is used - in jekyll, when building an IG, and in the IG publisher, when creating resource narratives. The work differnetly
Richard Townley-O'Neill (Feb 25 2021 at 05:14):
Thanks. I did not know that. I'm looking at variable for creating resource narratives.
Richard Townley-O'Neill (Feb 25 2021 at 05:15):
I'm after a variable for the title of a resource. I've found one for the name, but I want to put the title in documentation.
Richard Townley-O'Neill (Feb 25 2021 at 05:16):
I thought it would be easy. Now it is a rabbit hole. :smile:
Richard Townley-O'Neill (Feb 25 2021 at 05:18):
I can get the name from {{ site.data.structuredefinitions.<RESOURCE ID>.name }}, but there is no title.
I'll do it with text instead of a variable.
Jose Costa Teixeira (Feb 25 2021 at 05:21):
@Richard Townley-O'Neill I'm in the rabbit hole (it's nice and cozy and it's dangerous out there). What do you need?
Richard Townley-O'Neill (Feb 25 2021 at 05:41):
We have an IG not using the template approach built using the ig.json approach.
On -intro pages for profiles we put the title of the profile at the start. I thought it would be nice to use a variable instead of remembering to change it when the resource gets a new title during development. I found a variable for the ig's title : {{ site.data.fhir.ig.title }}
but for the resource all I could find was a name: {{ site.data.structuredefinitions.<RESOURCE ID>.name }}
I expect there is no such vartiable.
Jose Costa Teixeira (Feb 25 2021 at 05:44):
what about site.data.pages?
Jose Costa Teixeira (Feb 25 2021 at 05:45):
if you have a pages.json file, does it contain the info?
Jose Costa Teixeira (Feb 25 2021 at 05:46):
also site.data.resources should have that info
Richard Townley-O'Neill (Feb 25 2021 at 05:50):
pages.json has almost nothing. It has what is in ImplementationGuide.definition.page, and that is just a stub.
Grahame Grieve (Feb 25 2021 at 06:02):
if you're generating narrative, you only have what's in the resource that is being generated. If theres's no title in the resource, you can't use it when generating narrative
Richard Townley-O'Neill (Feb 25 2021 at 06:05):
The structure definition has a title, which I can see in site.data.resources
With site.data.resources I tried {{ site.data.resources.StructureDefinition/sra-match-record.title}}
and got nothing.
Maybe I need to escape the "/".
Grahame Grieve (Feb 25 2021 at 06:11):
I think you're confused. Are you writing liquid for jekyll to process, or for generating narrative?
Grahame Grieve (Feb 25 2021 at 06:12):
but if you're writing for a structure definition, the liquid will (or should) never be called - the IG publisher renders that itself
Richard Townley-O'Neill (Feb 25 2021 at 06:21):
I am writing for the narrative. I want the circled bit to be a variable
image.png
from https://sra.digitalhealth.gov.au/fhir/currentdraft/StructureDefinition-sra-organization.html
Richard Townley-O'Neill (Feb 25 2021 at 06:21):
Gotta go.
Grahame Grieve (Feb 25 2021 at 06:38):
there's no variables - you just have access to what's in the resource
Max Masnick (Feb 25 2021 at 12:46):
If anyone is trying to get this to work in a SUSHI project, this worked for me:
1) Add the following to sushi-config.yaml:
parameters:
"liquid-path": "liquid"
2) Create a folder input/liquid/
and add a file like Patient.liquid
as Grahame describes above
Max Masnick (Feb 25 2021 at 12:47):
When I add my own Patient.liquid
file, is there a place I can see the template I'm overriding?
Jose Costa Teixeira (Feb 25 2021 at 12:55):
I believe that when you add a Patient.liquid, you are not overriding the template, you are telling the Java code to let it go
Max Masnick (Feb 25 2021 at 13:00):
Ah ok, that makes sense
Max Masnick (Feb 25 2021 at 13:07):
I have yet to find an object that render()
actually works on. For example, Observation.value.render()
, Observation.value.coding.render()
, Observation.value.coding.code.render()
all produce the following exception:
org.apache.commons.lang3.NotImplementedException: Not done yet (IGPublisherHostServices.resolveFunction)
at org.hl7.fhir.igtools.publisher.Publisher$IGPublisherHostServices.resolveFunction(Publisher.java:385)
at org.hl7.fhir.r5.utils.LiquidEngine.resolveFunction(LiquidEngine.java:623)
at org.hl7.fhir.r5.utils.FHIRPathEngine.parseExpression(FHIRPathEngine.java:1038)
at org.hl7.fhir.r5.utils.FHIRPathEngine.parseExpression(FHIRPathEngine.java:1079)
at org.hl7.fhir.r5.utils.FHIRPathEngine.parseExpression(FHIRPathEngine.java:1079)
at org.hl7.fhir.r5.utils.FHIRPathEngine.parse(FHIRPathEngine.java:431)
at org.hl7.fhir.r5.utils.FHIRPathEngine.parse(FHIRPathEngine.java:423)
at org.hl7.fhir.r5.utils.LiquidEngine$LiquidStatement.evaluate(LiquidEngine.java:141)
at org.hl7.fhir.r5.utils.LiquidEngine.evaluate(LiquidEngine.java:102)
at org.hl7.fhir.r5.renderers.LiquidRenderer.render(LiquidRenderer.java:75)
at org.hl7.fhir.r5.renderers.ResourceRenderer.render(ResourceRenderer.java:84)
at org.hl7.fhir.igtools.publisher.Publisher.generateNarratives(Publisher.java:1152)
at org.hl7.fhir.igtools.publisher.Publisher.loadConformance(Publisher.java:3826)
at org.hl7.fhir.igtools.publisher.Publisher.createIg(Publisher.java:877)
at org.hl7.fhir.igtools.publisher.Publisher.execute(Publisher.java:732)
at org.hl7.fhir.igtools.publisher.Publisher.main(Publisher.java:8417)
Is this truly not implemented, or am I just doing something silly?
Grahame Grieve (Feb 25 2021 at 17:45):
truly not implemented.
Grahame Grieve (Feb 25 2021 at 17:45):
apparently I overlooked hooking something up
Max Masnick (Feb 25 2021 at 17:49):
Well having briefly attempted to replicate the behavior of render()
in Liquid, I can definitely see the value of render()
:grinning:
Grahame Grieve (Feb 25 2021 at 17:49):
y.
Grahame Grieve (Feb 25 2021 at 17:50):
it's now on my list
Max Masnick (Feb 25 2021 at 17:50):
Thanks!!
Brian Kaney (Feb 17 2022 at 23:32):
I have a similar setup and having problems with include
.
I would like to create a partial and include it in Patient.liquid. However any attempt at {% include 'test' %}
fails in the rendered output with an inline error
Exception generating Narrative: Script template: Error reading include: "test"
I think it is happening here https://github.com/hapifhir/org.hl7.fhir.core/blob/master/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/LiquidEngine.java#L494-L521, but really can't make any sense of what that code is doing.
Any suggestions? Is there an example somewhere of a successful include
?
Bryn Rhodes (Feb 21 2022 at 19:40):
There is this testcase in the fhir-test-cases repository: https://github.com/FHIR/fhir-test-cases/blob/master/r5/liquid/liquid-tests.json#L46
Bryn Rhodes (Feb 21 2022 at 22:13):
It looks like the LiquidEngine that is being used at that point for the narrative generation doesn't have an implementation of the IncludeResolver, so it doesn't know how to get the templates. I've sketched what I _think_ will work here:
Bryn Rhodes (Feb 21 2022 at 22:13):
https://github.com/cqframework/org.hl7.fhir.core/pull/2
Bryn Rhodes (Feb 21 2022 at 22:14):
But it's a change down in core to the LiquidRenderer
Bryn Rhodes (Feb 21 2022 at 22:15):
But I can't get a good build locally due to some seemingly unrelated package cache tests failing
Grahame Grieve (Feb 23 2022 at 23:58):
@Bryn Rhodes I don't understand this - the test passes?
Bryn Rhodes (Feb 24 2022 at 00:31):
It does, but that's because it sets a includeTemplateResolver in the test setup. When the Liquid Engine is used in the publisher to generate the narratives, it doesn't have an includeTemplateResolver set. We can see in the debug that the templates are loaded in the template provider, but there's no glue from the LiquidEngine to be able to resolve the templates there. So that's what this proposed PR does, just adds a class that implements the template resolver interface using the template provider in the context, and sets the LiquidEngine's include resolver to that instance.
Grahame Grieve (Feb 24 2022 at 00:41):
ok well make it as a PR then
Bryn Rhodes (Feb 24 2022 at 02:57):
https://github.com/hapifhir/org.hl7.fhir.core/pull/750
Last updated: Apr 12 2022 at 19:14 UTC