2

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.

Gurnemanz
  • 31
  • 3
  • It isn't implemented, but maybe could be. There is some syntax suggested here http://stackoverflow.com/questions/11147373/only-show-the-first-item-in-list-using-mustache that could possibly be implemented. – jverzani Nov 01 '16 at 22:21

1 Answers1

0

Since Mustache is, by design, 'logic-less', I would implement this by summarising in julia. Pass in an outer array. Each row in that array has two columns. The first column contains the "Arm", and the second column contains another array, containing the data for that "Arm".

In addition, the subheader should not be in static-content. It should be part of the flow content. Am I missing something here?

These changes, combined with keep-together should give you want you need.

aviks
  • 2,087
  • 18
  • 18
  • I agree the sub-header should go in the flow content. I have designed a structure with and Outer array of dicts holding the Arm and the data as a further array of dicts. I hope I have understood the Mustache search paths correctly. Next I shall construct a template to match the data structure. Eventually I shall have to dynamically generate the XSL-FO as the number of sub-headers i.e. number of 'BY' data values needs to be driven by the data values. – Gurnemanz Nov 26 '16 at 11:32
  • If the data for the specific 'BY' group i.e. one data value - in this case 'Placebo' for example - goes over multiple pages, then the sub-header should appear on each page and hence needs to go in the static content section. – Gurnemanz Nov 27 '16 at 10:14