Report Generation

John Harrold & Anson Abraham

2020-09-12

Introduction

Reporting is implemented using the officer package. Officer provides a lot of control over the generation of both Word and PowerPoint documents. If you feel comfortable programming in R, you can simply use that package directly. Currently ubiquity has support for generating both PowerPoint and Word reports, and this vingette will go over example scripts for both types of reports. To make a copy of the example scripts in the current working directory run the following:

library(ubiquity)
fr = workshop_fetch(section="Reporting", overwrite=TRUE)

This should create the following scripts

Then general process for creating a report is:

  1. Initialize a report using the function system_report_init
  2. Use the functions that begin with system_report to add content to the report
  3. Lastly, save the output using system_report_save

PowerPoint reports (make_report_PowerPoint.R)

Trying to keep track of different slide layouts and their indices can be cumbersome. The following functions have been created to allow slides to be created easily:

The first five of these functions are general reporting functions and those that begin with system_report_slide are used to add slides to a PowerPoint report. Examples of how to use these functions can be found in the file make_report_PowerPoint.R, and the text below is intended to document their use in more detail.

Creating a report

The first step is to initialize a report:

system_new(file_name="system.txt", system_file="mab_pk", overwrite = TRUE)
cfg = build_system(system_file = "system.txt")
cfg = system_report_init(cfg, rpttype="PowerPoint")

This will create a report named "default". To create multiple reports you can use the rptname input and pass that report name to the other system_report commands as well. The rpttype input indicates that we want to create a PowerPoint report. Default document templates will be used, but custom templates can be used as described below.

Content type

With the report created we can begin adding slides. There are several diffent slide formats with different placeholders for content. Each placehodler can contian a different type of content (text, list, image file, etc). The following list provides the available content types followed by the format of the content:

For more details on the expected format of slide content see ?system_report_ph_content.

Adding slides to the presentation

First you may want to add a title slide to the presentation:

cfg = system_report_slide_title(cfg, 
           title     = "Generating Inline Reports",
           sub_title = "A Working Example")

Next you may want to add an overview of your analysis. The content slide provides one placeholder that you can fill with any of the above content types.

cfg = system_report_slide_content(cfg, 
           title     = "Single text area",   
           content   = "This vignette provides examples of how to add different types of slide content" )

Lists

The default content type is text, but you may want to add a list. Lists are formatted as vectors with paired elements. The first element contains the indention level and the second element contains the actual text:

lcont = c(1, "Top level item", 
          2, "This is a sub bullet",
          2, "This is another sub bullet")
cfg = system_report_slide_content(cfg, 
           title        = "Lists are pretty straight forward",      
           content_type = "list",
           content      = lcont)

Figures

If you are running simulations you may want to put ggplot figures directly into a report. Consider simulating a single dose in the mab_pk system:

parameters = system_fetch_parameters(cfg)
cfg = system_zero_inputs(cfg)
cfg = system_set_bolus(cfg, state = "At",
                           times  = c(  0.0),
                           values = c(400.0))

cfg=system_set_option(cfg, group  = "simulation",
                           option = "output_times",
                           value  = seq(0,60,.1))

som = run_simulation_ubiquity(parameters, cfg)

Then we can plot the response in ggplot:

myfig = ggplot() + 
        geom_line(data=som$simout, aes(x=ts.days,   y=C_ng_ml), color="red")  +
        xlab("Time (days)")+
        ylab("C (ng/ml) (units)")
myfig = gg_log10_yaxis(myfig, ylim_min=1e3, ylim_max=1e5)
myfig = prepare_figure("present", myfig)

Then that figure can be added to a slide:

cfg = system_report_slide_content(cfg, 
           title        = "ggplot objects can be inserted directly", 
           content_type = "ggplot",
           content      = myfig)

You can also pull image content from files as well using the imagefile content type.

cfg = system_report_slide_content(cfg, 
           title        = "Images can be inserted from files", 
           sub_title    = "But the image should have the same aspect ratio as the placeholder", 
           content_type = "imagefile",
           content      = system.file("ubinc", "images", "report_image.png", package="ubiquity"))

The benefit of using ggplot is that it will automatically size the image to the dimensions of the placeholder. To use an image file the image file needs to have the same aspect ratio as the placeholder you want to put it in.

Tables

Tabular data can be include too. The simplest method is to use the table content type and supply a list with an element named table containing a data frame with the tabular information you want to display:

tcont = list()
tcont$table = parameters
cfg = system_report_slide_content(cfg, 
           title        = "Simple Tables", 
           content_type = "table",    
           content      = tcont)

This may not be the most attractive way to display tabular information, and for smaller tables it may be a bit poorly formatted. As an alternative the flextable package can be used by specifying that content type:

tcont = list()
tcont$table = parameters
cfg = system_report_slide_content(cfg, 
           title        = "Flextables", 
           content_type = "flextable",    
           content      = tcont)

To provide a little more formatting, two column masters are provided. The content_type argument indicates the type of text that can be included. It should be either "list" or "text". If the left or right content is to contain a table or figure it can be overwritten.

cfg = system_report_slide_two_col(cfg,
       title                  = "Two columns of lists",      
       sub_title              = NULL, 
       content_type           = "list", 
       left_content           = lcont,
       right_content_type     = "flextable",
       right_content          = tcont)

You can specify column headers if you like. These can be text, imagefile, ggplot, table or flextable.

cfg = system_report_slide_two_col(cfg,
       title                  = "ggplot vs imagefile",      
       sub_title              = NULL, 
       content_type           = "list", 
       left_content_header    = "Image file",
       left_content_type      = 'imagefile',
       left_content           = system.file("ubinc", "images", "report_image.png", package="ubiquity"),
       right_content_header   = "ggplot object",
       right_content_type     = "ggplot",
       right_content          = myfig)

Saving slides

Finally, after you have added all of your sldies, you will want to save them to a file:

system_report_save(cfg, output_file = "report_vignette.pptx")

Word reports (make_report_Word.R)

To generate word reports the following functions are used. The first five are the same general reporting function from above and the remaining three are specifically used for Word documents:

Creating a report

Like with PowerPoint reports above, we begin by initializing a report. The only difference is that we are using the "Word" report type.

cfg = system_report_init(cfg, rpttype="Word")

Content type

Similar to the slide functions above, content is added to Word reports using the function system_report_doc_add_content. When calling this function we must specify a content_type and corresponding content. The following list provides the available content types followed by a description of the format of the content.

Text can be specified in different formats: "text" indicates plain text, "fpar" is formatted text defined by the fpar command from the officer package, and "md" is text formatted in markdown format (?md_to_officer for markdown details).

Adding content to the document

Next we can start by adding the table of contents to the document:

cfg = system_report_doc_add_content(cfg, 
  content_type  = "toc",
  content       = list(level=1))

Different types of text

We can begin by creating a header. You can add three levels of headers using the style element in the content ("h1", "h2", and "h3").

cfg = system_report_doc_add_content(cfg, 
  content_type  = "text",
  content       = list(style   = "h1",
                       text    = "This will insert a level 1 header"))

You can also add text using the "normal" and "code" styles. The content contains the optional format. If you do not specify a format, it will assume you’re using plain text.

cfg = system_report_doc_add_content(cfg, 
  content_type  = "text",
  content       = list(style   = "normal",
                       text    = "This is plain text"))

You can also format text using two methods.First you can use the fpar command from officer:

fpartext = fpar(
ftext("Formatted text can be created using the ", prop=prop=officer::fp_text()),
ftext("fpar ", prop=officer::fp_text(color="green")),
ftext("command from the officer package.", prop=prop=officer::fp_text()))

cfg = system_report_doc_add_content(cfg, 
  content_type  = "text",
  content       = list(style   = "normal",
                       format  = "fpar",
                       text    = fpartext))

Lastly you can specify formatting use a type of markdown. This also includes options for font colors and font families. For more information see ?md_to_officer.

mdtext = "Text can be specified in markdown format as well. You can specify
*bold text*, **italicized text**, ^superscripts^ and ~subscripts~. These can
be combined as well *H*~*2*~*0*.

You can change colors to  <color:red>red</color>, <color:blue>blue</color>, etc and
change the <shade:#33ff33>shading</shade>. Again these can be combined
<shade:orange><color:green>both color and shading</color></shade>. You can also
change the font to things like <ff:symbol>*symbol*</ff>."

cfg = system_report_doc_add_content(cfg, 
  content_type  = "text",
  content       = list(style  = "normal",
                       format = "md",
                       text   = mdtext))

Including figures

First we create an image (ggplot object and file):

p = ggplot() + annotate("text", x=0, y=0, label = "picture example")
imagefile = tempfile(pattern="image", fileext=".png")
ggsave(filename=imagefile, plot=p, height=5.15, width=9, units="in")

Figures can be added by either referencing them from a file or inserting a ggplot object directly. This is done by specifying the content type "imagefile" or "ggplot". The content contains the file name or ggplot object and other information such as the image width, caption text, etc.

cfg = system_report_doc_add_content(cfg, 
  content_type  = "imagefile",
  content       = list(image   = imagefile,
                       caption = "This is an image file"))

cfg = system_report_doc_add_content(cfg, 
  content_type  = "ggplot",
  content       = list(image   = p,
                       caption = "This is a ggplot image"))

Tables

Tables can be added in two ways. The simplest is to create a table in Word using a predefined style in the template file. First a list is created to contain the table and metadata about that table. This list is then passed into system_report_doc_add_content with the "table" content type specified:

tc = list()
tc$table = data.frame(Parameters = c("Vp", "Cl", "Q", "Vt"),
                      Values     = 1:4,
                      Units      = c("L", "L/hr", "L/hr", "L") )
tc$header    = TRUE 
tc$first_row = TRUE 
tc$caption = "This is a table"

cfg = system_report_doc_add_content(cfg, 
  content_type  = "table",
  content       = tc)

Tables can also be created using the flextable package. A list can be created to contain the tabular data and other information as before. The benefit here is that you will have more control over the table formatting:

tcf = list()
tcf$caption = "This is a flextable"
tcf$table = data.frame(Parameters = c("Vp", "Cl", "Q", "Vt"),
                       Values     = 1:4,
                       Units      = c("L", "L/hr", "L/hr", "L") )
tcf$header_top   = 
     list(Parameters     = "Name", 
          Values         = "Value",
          Units          = "Units")

tcf$cwidth        = 0.8 
tcf$table_autofit = TRUE
tcf$table_theme   ='theme_zebra'

cfg = system_report_doc_add_content(cfg, 
  content_type  = "flextable",
  content       = tcf)

Placeholders

You can include placeholders in your documents. These can be in the template document itself or in the text you’re adding to the document. For example you can create placeholder text called: :::PHName::: in your document and it can be replaced by the desired text in the document. A placeholder is a unique string of text surrounded by :::. If the placeholder is in your template document, it may not work correctly if you type the text into word. This is because while a string may appear to be a contiguous in Word, it may not be so in the underlying XML code. To ensure the string is contiguous you should type the placeholder text into a text editor, copy and paste it into template word. Next you can define the replacement text using system_report_doc_set_ph:

cfg = system_report_doc_set_ph(cfg, 
      ph_content  = "Jill Smith" ,
      ph_name     = "PHName", 
      ph_location = "header")

The ph_location can be either "header", "footer", or "body".

Page orientation and columns

To alter the orientation of the page (portrait vs. landscape) or the number of columns. This is done using the system_report_doc_format_section. When this function is called it applies to all of the added content since the last time this function was called. For example to switch to landscape for a figure we must end the current section:

cfg = system_report_doc_format_section(cfg, section_type="continuous")

Then we can add our figure

p = ggplot() + annotate("text", x=0, y=0, label = "picture example")

cfg = system_report_doc_add_content(cfg, 
  content_type  = "ggplot",
  content       = list(image   = p,
                       height  = 2.5,
                       width   = 9,
                       caption = "This is a landscape figure"))

Next we can apply the apply the landscape orientation to the figure.

cfg = system_report_doc_format_section(cfg, section_type="landscape", h=8, w=10)

The following section types are available. "columns", "continuous", "landscape", "portrait", "columns", or "columns_landscape".

Finally, after you have added all of content to your Word report, you will want to save it to a file.

system_report_save(cfg, output_file = "report_vignette.docx")

Using custom organizational templates

To use a custom template for your organization you need to do the following:

  1. Create a template with the appropriate slide masters (PowerPoint) or styles (Word)
  2. Generate a layout file with mapping information for your template
  3. Customize the functions in the organizational template script
  4. Initialize a report indicating that you want to use this new template and add elements to the report

Creating a PowerPoint template for your organization

Using your organizational template create slide masters below with the content elements to the right. The title and subtitle elements of the title_slide and section_slide should be of the type ctrTitle and subTitle, respecitvely. For the rest of the slides the title elements should be of the type title, and the other elements should simply be of type body. The format of the body elements should be plain text unless identified parenthetically below.

Master layout name Content elements
title_slide title, subtitle
section_slide title, subtitle
content_text title, subtitle, main content
content_list title, subtitle, main content (list)
two_content_list title, subtitle, left, right, left body (list), right body (list)
two_content_text title, subtitle, left, right, left body, right body
two_content_header_list title, subtitle, left header, right header, left body (list), right body (list)
two_content_header_text title, subtitle, left header, right header, left body, right body
title_only title

Creating a Word template for your organization

In your word organizational template you will need to create a file with the following styles:

Creating the layout file for the PowerPoint template

Each element in a master is identified with an index number (index) and placeholder label (ph_label). These values are asigned as the master slide is built, so it can be difficult to find this information through PowerPoint. If you named your template mytemplate.pptx, then you can have ubiquity produce a slide deck with the masters annotated so you can identify these values:

cfg = build_system()
cfg = system_report_init(cfg, template="mytemplate.pptx")
cfg = system_report_view_layout(cfg)

This should create a file called layout.pptx that has the layout name of each slide master in the title. Both the ph_label and index of each element will be identified. These slides should look something like:

Master slide layouts

Creating the layout file for the Word template

After you create your Word template you can create a layout file for it. If you named your template mytemplate.docx, then you can create the layout file using the following:

cfg = build_system()
cfg = system_report_init(cfg, template="mytemplate.docx")
cfg = system_report_view_layout(cfg)

This should create a file called layout.docx that has an example of each style in the template. It will have the names of the styles as well that will be used to define mapping information in the organizational slide.

Mapping your layout to ubiquity functions

Next you need to map information in your document templates. This will be done with the myOrg template:

tr = system_fetch_template(cfg, template="myOrg")

This should create the file myOrg.R in the current directory. In this file modify the function named org_pptx_meta for PowerPoint templates. This returns the variable meta which contains this mapping information. You need to look at the information in layout.pptx and make sure it matches the information returned in meta.

Similarly you can modify the function org_docx_meta for Word templates. Make sure that the styles in this function point to the correct styles in your Word template.

Using your own template

To use your own template you simply need to source the myOrg.R file at the top of your script. For PowerPoint templates, initialize the report using your own template (mytemplate.pptx) and the meta information returned from org_pptx_meta:

source("myOrg.R")
cfg = system_report_init(cfg  = cfg, 
            meta     = org_pptx_meta(),
            template = "mytemplate.pptx")

For word reports you can use the word equivalents:

source("myOrg.R")
cfg = system_report_init(cfg  = cfg, 
            meta     = org_docx_meta(),
            template = "mytemplate.docx")

Now you can use the functions outlined above to add content to these reports.

Integration with ubiquity workflows

Ubiquity provides reporting functionality for the different workflows by allowing the results of those workflows to be appended to open reports.

Parameter estimation

After performing a parameter estimation and archiving the estimation with a specified analysis name, that analysis name can be used to retrieve the results and append them to an open report using system_report_estimation. This function supports both PowerPoint and Word reports.

cfg = system_report_estimation(cfg=cfg, analysis_name="analysis_name")

Non-compartmental analysis (NCA)

The results of NCA can be appended to a report using the system_report_nca function. This function supports both PowerPoint and Word reports.

cfg = system_report_nca(cfg, analysis_name = "default")

Modifying reports directly with officer

Sometimes the functions provided above are not sufficient to get what you want done. It may be more convenient to directly use the officer functions to add content or modify your report. If you have report initialized, you can pull that report of the ubiquity system object using system_report_fetch:

rpt = system_report_fetch(cfg)

Now rpt is an officer object. If contains a PowerPoint presentation you can use all of theofficer functions for PowerPoint to modify that object. If it’s a Word document you can use the Word functions from officer to modify/add content. Once you’re done making changes you can put the object back using system_report_set:

cfg = system_report_set(cfg, rpt = rpt) 

Then you can continue using the ubiquity functions above or save the document.