I have some test demographic data in the following form (truncated for demo purposes):
TreatmentArm Site-Subject Gender Age
Placebo 000001-000002 M 42
Placebo 000001-000043 F 23
Placebo 000003-000076 F 45
.
.
Active 000001-000003 M 56
Active 000003-000098 F 34
I can produce a PDF with headers, footers and a table showing the data in the above structure. However, the treatment arm repeats are unnecessary and would normally be handled by sub-titles:
Treatment Arm: Placebo
Site-Subject Gender Age
000001-000002 M 42
000001-000043 F 23
000003-000076 F 45
<page-break>
Treatment Arm: Active
Site-Subject Gender Age
000001-000003 M 56
000003-000098 F 34
So, the two values of treatment arm are controlling the text in the sub-title and the change of the treatment arm value is triggering a page-throw.
The language I'm trying here is Julia, which has, so far, acquitted itself very well. In order to write a simple report to a PDF I need to use a package called Taro, which also uses a port of Mustache.js to Julia. Here, Julia is calling Java, which uses Apache FOP to produce the PDF. Julia calls an XSL-FO template and the Mustache render function marries the data to the template.
Hence, there are 2 source files: the Julia program and the XSL-FO template files. First, the Julia source, abridged as far as possible:
using Taro
# init() once per session to set the Java classpath
Taro.init()
using Mustache
using DataFrames
# get the xsl-fo template
tmpl = Mustache.template_from_file("tables.fo.tmpl")
# get the data, process, sort and select columns
df = readtable("DM1.csv")
df[:sitesubj] = map(x->x[8:end], df[:usubjid])
df2 = sort(df[:, [:armcd, :arm, :sitesubj, :age, :sex]], cols = [:armcd, :sitesubj])
# Write the data to an Array of Dictionaries
d=Array(Dict, nrow(df2));
for i in 1:length(d)
d[i] = Dict{ASCIIString,Any}(
"armcd"=>df2[i, :armcd],
"arm"=>df2[i, :arm],
"sitesubj"=>df2[i, :sitesubj],
"age"=>df2[i, :age],
"sex"=>df2[i, :sex],
)
end
# Some Mustache magic. Render adds the data to the report template
# tn is a String, to is an IOStream
tn, to=mktemp()
fo=render(tmpl, D=d)
write(to, fo)
close(to)
Taro.fo(tn, "test_listing.pdf")
And now the template, abridged as far as possible, but leaving a working example, albeit without the sub-title I need:
<?xml version="1.0" encoding="UTF-8"?>
<fo:root font-family="Courier" font-size="10pt" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="A4-landscape"
margin-right="0.5cm"
margin-left="0.5cm"
margin-bottom="0.5cm"
margin-top="0.5cm"
page-width="29.7cm"
page-height="21cm">
<fo:region-body margin-top="4cm" margin-bottom="3cm"/>
<fo:region-before extent="8cm"/>
<fo:region-after extent="3cm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="A4-landscape">
<!-- Headers -->
<fo:static-content flow-name="xsl-region-before">
<fo:block line-height="14pt" font-size="8pt" text-align-last="justify">ACME Corp
<fo:leader leader-pattern="space" />
CONFIDENTIAL
</fo:block>
<fo:block line-height="14pt" font-size="8pt" text-align-last="justify">XYZ123 / Anti-Hypertensive
<fo:leader leader-pattern="space" />
Draft
</fo:block>
<fo:block line-height="14pt" font-size="8pt" text-align="left">Protocol XYZ123</fo:block>
<fo:block line-height="14pt" font-size="8pt" text-align="center">Study XYZ123</fo:block>
<fo:block line-height="14pt" font-size="8pt" text-align="center">Listing of Demographic Data by Treatment Arm</fo:block>
<fo:block line-height="14pt" font-size="8pt" text-align="center">All Subjects</fo:block>
<fo:block text-align="left">
<!-- Here is where I need to add the current ARM value in a sub-title -->
<!-- fo:retrieve-marker ?? -->
</fo:block>
</fo:static-content>
<!-- Footers -->
<fo:static-content flow-name="xsl-region-after">
<fo:block line-height="14pt" font-size="8pt" text-align="left">A long explanatory text</fo:block>
<fo:block line-height="14pt" font-size="8pt" text-align="left">All subjects are included in the listing including the screen failures</fo:block>
<fo:block line-height="14pt" font-size="8pt" text-align="left">All measurements were taken at the screening visit</fo:block>
<fo:block line-height="14pt" font-size="8pt" text-align-last="left"> Page <fo:page-number/> of <fo:page-number-citation ref-id="end"/>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
<!-- Here I need to capture the value of the current arm -->
<!-- Set a marker ?? -->
<!-- Cannot use {{#:D}} to {{/:D}} as this captures values across all rows -->
<fo:table table-layout="fixed" width="100%" >
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="6cm"/>
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-header border-bottom-style="solid" border-top-style="solid">
<fo:table-row space-after="10px">
<fo:table-cell>
<fo:block>Arm</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>Site ID - Subject ID</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>Age</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>Gender</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-header>
<fo:table-body border-bottom-style="solid">
{{#:D}}
<fo:table-row keep-together.within-page="always">
<fo:table-cell>
<fo:block>{{arm}}</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>{{sitesubj}}</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>{{age}}</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>{{sex}}</fo:block>
</fo:table-cell>
</fo:table-row>
{{/:D}}
</fo:table-body>
</fo:table>
<fo:block id="end"/>
</fo:flow>
</fo:page-sequence>
</fo:root>
One of the problems here is the mixture of technologies and where to address the problem. Do I need to pre-summarize in Julia and pass another dictionary with just the 'Placebo' and 'Active' values to the template. Even so, there must be some mechanism to recognise position within the template. I don't think it possible to add XSL directives to the mix, so logic within the template eludes me. As the comments in the XSL-FO file suggest, maybe the way is to set and retrieve markers, but the recognition of the 'first row in a BY group' does not seem to be present. I hope I am wrong and this is possible.
The paging issue seems to have been solved by
<fo:table-row keep-together.within-page="always">
But this is not like saying 'when this condition is reached, throw a page.'
So, if anybody has suggestions, I'm more than happy to test them. Many thanks.