Report Generation

Introduction

Reporting is implemented using the onbrand package which is a templated interface to 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 may wan to use that package directly. However the templated workflow in onbrand facilitates switching between organizational templates. Currently ubiquity has support for generating both PowerPoint and Word reports, and this vignette will go over example scripts for both types of reports.

Implenting onbrand in ubiquity

One objective of reporting in ubiquity is to allow the management of several reports simultaneously. This is done by creating wrapper functions for functions in onbrand. Where the onbrand functions take in an onbrand object, the wrapper functions take in a ubiquity system object and an optional report name (rptname). The other inputs to the ubiquty wrapper functions are passed through directly to the wrapped onbrand function. At the bottom of this vignette, the ubiquity function name is listed along with the onbrand name it wraps around.

Reporting workshop files

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_rpt_read_template().
  2. Use the functions that add content to reports (system_rpt_add_slide() for PowerPoint and system_rpt_add_doc_content() for Word), or append analysis results using integrated workflow reporting (e.g. system_rpt_estimation() to add the results of parameter estimation).
  3. Lastly, save the output using system_rpt_save_report().

The examples below are intended to work with a ubiquity system object. So at the top of any script you will need to build the system. If you don’t have a system file, this command will create an empty template in the current directory:

cfg = build_system("system.txt")

PowerPoint reports (make_report_PowerPoint.R)

Creating some content

Before we get started we’ll create some content for the presentation. Since we’re wrapping around onbrand functions, the content is expected in a format consistent with those functions. For details on the format of different content types, see the help for onbrand::add_pptx_ph_content().

First we’ll create some figures. First a ggplot object (p) and an image file (imgfile).

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

Next we’ll create tabular data (tdata):

tdata =  data.frame(Parameters = c("Vp", "Cl", "Q", "Vt"),
                    Values     = 1:4,
                    Units      = c("L", "L/hr", "L/hr", "L") )

Tables can be displayed in three different ways: as an Office table, an onbrand abstraction of a flextable, or a user defined flextable object. These are lists with specific elements. You should see the content specifications found in onbrand::add_pptx_ph_content() for details on how these lists should be formatted. The examples below should be able to get you started.

Office table

tco  = list(table     = tdata,  
            header    = TRUE,   
            first_row = FALSE)

flextable using onbrand abstraction

tcf = list(table       = tdata,             # This element contains the table data
           header_top  = list(              # Defining the table headers
             Parameters = "Name",  
             Values     = "Value",
             Units      = "Units"),
           cwidth         = 0.8,            # Column width
           table_autofit  = TRUE,           # Making the tables automatically fit
           table_theme    = "theme_zebra",  # Selecting the table theme
           first_row = FALSE)

User-defined flextable object

tfo = flextable::flextable(tdata)
tfo = flextable::autofit(tfo)

Lastly list content is specified in a paired vector. The first element indicates the indention level, and the second indicates the content. Note: This will only work for placeholders that contain list content.

lcontent = c(1, "First major item",
             2, "first sub bullet",
             2, "second sub bullet",
             3, "sub sub bullet",
             1, "Second major item",
             2, "first sub bullet",
             2, "second sub bullet")

Creating a new report

First we initialize a new report. If the template option is set to "PowerPoint" it will create an empty presentation using the internal ubiquity template. Below we discuss creating custom templates for your organization.

cfg = system_rpt_read_template(cfg, "PowerPoint")

Adding slides to a report

Slides are added using ubiquity::system_rpt_add_slide() with the slide template to be used and the elements identifying the placeholders and the placeholder content. Here we’re adding slides for a title ("title_slide") and a section ("section_side").

cfg = system_rpt_add_slide(cfg, 
   template = "title_slide",
   elements = list(
      title=list(content = "Reporting in ubiquity",
                 type    = "text")))
cfg = system_rpt_add_slide(cfg, 
   template = "section_slide",
   elements = list(
      title=list(content = "Content Types",
                 type    = "text")))

For each element there is a list element (e.g. title) that contains content and content type. The type specifies whether the content is a figure, table, etc. In this example we’re using text. There are two types of text content. One is plain text and the other is lists. This is determined by the placeholder type in PowerPoint. We’ll go through examples below for different types of content and available templates. For details on the format of the template and elements inputs see the documentation for onbrand::report_add_slide(). To show the available templates, placeholder element names in each template, and the type of text they can hold you can do the following:

fr = system_rpt_template_details(cfg)

This will produce output in the console like the following:

## Mapping:     /Library/Frameworks/R.framework/Versions/4.0/Resources/library/ubiquity/ubinc/templates/report.yaml
## Report Type: PowerPoint
## title_slide (master/template)
##   > title (text)
##   > sub_title (text)
## section_slide (master/template)
##   > title (text)
##   > sub_title (text)
## title_only (master/template)
##   > title (text)
## content_text (master/template)
##   > title (text)
##   > sub_title (text)
##   > content_body (text)
## content_list (master/template)
##   > title (text)
##   > sub_title (text)
##   > content_body (list)

Adding lists

In the previous example we added text content, here we will add the list content we created above (lcontent). Notice that we simply specify the content type is "list". Note: You can only do this if the content type is list in the slide master.

cfg = system_rpt_add_slide(cfg, 
   template = "content_list",
   elements = list(
      title=
        list(content = "Lists",
             type    = "text"),
      sub_title=
        list(content = "For placholders that contain lists.",
             type    = "text"),
      content_body=
        list(content = lcontent,
             type    = "list")))

Adding figures

If you specify the content type is "ggplot", then you can just provide the ggplot object (p).

cfg = system_rpt_add_slide(cfg, 
   template = "content_text",
   elements = list(
      title=
        list(content = "Figures: ggplot object",
             type    = "text"),
      sub_title=
        list(content = "Using ggplot objects directly",
             type    = "text"),
      content_body=
        list(content = p,
             type    = "ggplot")))

If you have an image in a file you want to insert you can use the "imagefile" content type. One was created above and the location is stored in the imgfile object.

cfg = system_rpt_add_slide(cfg, 
   template = "content_text",
   elements = list(
      title=
        list(content = "Figures: image file",
             type    = "text"),
      sub_title=
        list(content = "Inserting figures from files",
             type    = "text"),
      content_body=
        list(content = imgfile,
             type    = "imagefile")))

Adding tables

First we’re going to add a table using the built in Office tables. The content is stored in tco and can be added to any placeholder as long as we specify the type as "table".

cfg = system_rpt_add_slide(cfg, 
   template = "content_text",
   elements = list(
      title=
        list(content = "Tables: Office",
             type    = "text"),
      sub_title=
        list(content = "Table in native Office format",
             type    = "text"),
      content_body=
        list(content = tco,
             type    = "table")))

Similarly, you can add a flextable using the onbrand abstraction by specifying the type as "flextable" and supply the correctly formatted content created above (tcf).

cfg = system_rpt_add_slide(cfg, 
   template = "content_text",
   elements = list(
      title=
        list(content = "Tables: flextable",
             type    = "text"),
      sub_title=
        list(content = "Flextables using onbrand abstraction",
             type    = "text"),
      content_body=
        list(content = tcf,
             type    = "flextable")))

Lastly, you can create a flextable directly. This gives you a lot of control with regards to formatting. You simply need to specify the type as "flextable_object" and provide an object created with flextable (tfo).

cfg = system_rpt_add_slide(cfg, 
   template = "content_text",
   elements = list(
      title=
        list(content = "Tables: flextable object",
             type    = "text"),
      sub_title=
        list(content = "Flextables using a user-created flextable object",
             type    = "text"),
      content_body=
        list(content = tfo,
             type    = "flextable_object")))

The rest of the reporting script (make_report_PowerPoint.R) simply provides examples of the other available slide templates. You can look through those to see if they are suitable for your needs. If they are not and you need other templates or wish to create a template for your school or organization, jump down to the section on custom organizational templates.

Saving the report

Once you’re done you can then save the presentation to a file:

system_rpt_save_report(cfg, output_file = "example.pptx")

Word reports (make_report_Word.R)

Creating some content

Just like the PowerPoint example above, we need to create some content for Word reporting. We’re using wrappers here for onbrand functions so document content needs to be provided in a specific format documented in the help for onbrand::report_add_doc_content()

Text formatting can be plain text, formatted as Markdown, or formatted using the officer::fpar() command.

plain_text_content = paste(rep("The quick brown fox jumped over the lazy dog.", 70), collapse= " ")
md_text_content    = paste(rep("The *quick* <color:brown>brown</color> fox **jumped** over the ~lazy dog~.", 70), collapse=" ")
fpar_text_content  = officer::fpar(
   officer::ftext("The quick ", prop=NULL),
   officer::ftext("brown", prop=officer::fp_text(color="brown")),
   officer::ftext(" fox jumped over the lazy dog.", prop=NULL))

For figures we can use two formats a ggplot object (p) or an image file (imgfile) these are packed into content lists gpc and ifc, respectively.

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

gpc = list(image   = p,
           caption = "This is an example of an image from a ggplot object.")

ifc  = list(image   = imgfile,
            caption = "This is an example of an image from a file.")

The same tabular data from before is used here as well:

tdata =  data.frame(Parameters = c("Vp", "Cl", "Q", "Vt"),
                    Values     = 1:4,
                    Units      = c("L", "L/hr", "L/hr", "L") )

Office table

The content for tables in Office format is similar but it has other elements as well such as a figure caption:

tco  = list(table     = tdata,    # This element contains the table data
            header    = TRUE,     # These two lines control the header
            first_row = FALSE,
            caption   = "This creates a table using an Office theme/format.")

flextable using onbrand abstraction

Similarly we can create a flextable using the onbrand abstraction. In this case we’re supplying a caption and specifying the format of the caption is Markdown ("md").

tcf = list(table       = tdata,             # This element contains the table data
           caption_format = "md",
           caption     = "This creates a <ff:courier>flextable</ff> using the <ff:courier>onbrand</ff> abstraction",
           header_top  = list(              # Defining the table headers
             Parameters = "Name",  
             Values     = "Value",
             Units      = "Units"),
           cwidth         = 0.8,            # Column width
           table_autofit  = TRUE,           # Making the tables automatically fit
           table_theme    = "theme_zebra",  # Making the tables automatically fit
           first_row = FALSE)

User-defined flextable object

tfo = flextable::flextable(tdata)
tfo = flextable::autofit(tfo)

tcfo = list(ft = tfo,
            caption  = "This inserts a flextable object created by the user")

Creating a new report

We create a new Word report by defining the template option as "Word".

cfg = system_rpt_read_template(cfg, "Word")

Adding content to the report

Content is added using ubiquity::system_rpt_add_doc_content() with the content and type of content specified. This wraps around the onbrand::report_add_doc_content() function. See the help for that function for allowed types and the expected format of the content. Below we will cover some examples of common content to be added.

Adding text

Text content contains a style element where you can specify the onbrand style.

cfg = system_rpt_add_doc_content(cfg, 
        type="text",
        content = list(
          style = "Heading_1",
          text  = "Formatting Text"))

To get a list of available styles for a given template you can use system_rpt_template_details():

fr = system_rpt_template_details(cfg)

This will produce output in the console like the following:

## Mapping:     /Library/Frameworks/R.framework/Versions/4.0/Resources/library/ubiquity/ubinc/templates/report.yaml
## Report Type: Word
##   onbrand style (word style, style type)
##   --------------------------------------
##   Code (Code, paragraph)
##   Figure_Caption (graphic title, paragraph)
##   Heading_1 (heading 1, paragraph)
##   Heading_2 (heading 2, paragraph)
##   Heading_3 (heading 3, paragraph)
##   Normal (Normal, paragraph)
##   Table_Caption (table title, paragraph)
##   TOC (toc 1, paragraph)
##   Table (Table Grid, table)

Text can be added in three different formats: plain text, as Markdown, and using the officer::fpar() command from the officer package.

Plain text

If no style is specified a template default style will be used.

cfg = system_rpt_add_doc_content(cfg, 
  type="text",
  content = list(
    text  = plain_text_content))

Markdown

To add text content other than plain text you need to add a format element. For example you can supply text in Markdown format and then define the format as "md". For details on Markdown supported see the help for the onbrand::md_to_officer() function.

cfg = system_rpt_add_doc_content(cfg, 
        type="text",
        content = list(
          style  = "Normal",    
          format = "md",
          text   = md_text_content))

Using officer::fpar()

Officer formats text using the officer::fpar() command. If you have an fpar object you can use that as the text element for the content. You just need to specify the format as "fpar".

cfg = system_rpt_add_doc_content(cfg, 
        type="text",
        content = list(
          style  = "Normal",    
          format = "fpar",
          text   = fpar_text_content))

Adding figures

To add figures you can specify the type as either an "imagefile" or "ggplot":

cfg = system_rpt_add_doc_content(cfg,
  type     = "imagefile",
  content  = ifc)
cfg = system_rpt_add_doc_content(cfg,
  type     = "ggplot",
  content  = gpc)

Adding tables

Tables can be added in Office table, onbrand abstraction of flextables, and as flextable objects. This is done by using the type "table", "flextable", and "flextable_object" respectively:

cfg = system_rpt_add_doc_content(cfg, 
  type     = "table",
  content  = tco)
cfg = system_rpt_add_doc_content(cfg, 
  type     = "flextable",
  content  = tcf)
cfg = system_rpt_add_doc_content(cfg, 
  type     = "flextable_object",
  content  = tcfo)

Other content and formatting

For information on adding other content (such as using placeholder text within a document) and formatting (number of columns, page orientation, etc) see the function onbrand::report_add_doc_content() for a list of allowed type values and the expected content format.

Saving the report

Once you’re done you can then save the document to a file:

system_rpt_save_report(cfg, output_file = "example.docx")

Using custom organizational templates

Internally ubiquity uses the onbrand package for customizable templates. To create a template for your organization you should use the onbrand vignette for Custom Templates. This will instruct you to create a yaml mapping file for your templates. You can use the built in organizational template as a starting point for that:

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

In order to use the ubiquity workflows, you will need to have specific placeholders for PowerPoint and styles for Word. You can have others, but you will at least need those listed below. These should already be defined in the organizational template. You simply need to ensure that the placeholder names and content types from PowerPoint are correct and that the style names in Word you create are also assigned to the correct onbrand styles.

PowerPoint

In PowerPoint you will need to have a document template with the following slide masters/templates. You will need to name the slide Master the name shown in the table below. For each slide master you will need to create placeholders for the specified content. Make sure the content type matches that shown below. When you create the onbrand mapping file you will need to use the placeholder names shown below.

Word

For Word reporting you will need to have a Word template with the following styles defined. When you create the onbrand mapping file you will need to use the onbrand Style name listed.

Using the custom templates

To use the custom templates you would use system_rpt_read_template() and specify the mapping file and the Office document containing your template. For example if you wanted to load a PowerPoint template called myOrg.pptx and your yaml mapping file was called myOrg.yaml you would use the following:

cfg = system_rpt_read_template(cfg, 
                               mapping  = "myOrg.yaml",
                               template = "myOrg.pptx")

After that you can just use all the normal reporting functions to add content to the report.

Integration with ubiquity workflows

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_rpt_estimation(). This function supports both PowerPoint and Word reports.

cfg = system_rpt_estimation(cfg=cfg, analysis_name="analysis_name")

Non-compartmental analysis (NCA)

The results of NCA can be appended to a report using the system_rpt_nca() function and supplying the NCA analysis name. This function supports both PowerPoint and Word reports.

cfg = system_rpt_nca(cfg=cfg, analysis_name="analysis_name")

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 out of the ubiquity system object using system_fetch_rpt_officer_object():

rpt = system_fetch_rpt_officer_object(cfg)

Now rpt is an officer object. If it contains a PowerPoint presentation you can use all of the officer 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_set_rpt_officer_object():

cfg  = system_set_rpt_officer_object(cfg, rpt)

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

Wrapper mapping between ubiquity and onbrand functions

ubiquity onbrand
system_rpt_read_template() onbrand::read_template()
system_rpt_read_template() onbrand::read_template()
system_rpt_add_slide() onbrand::report_add_slide()
system_rpt_add_doc_content() onbrand::report_add_doc_content()
system_rpt_save_report() onbrand::save_report()
system_rpt_template_details() onbrand::template_details()
system_fetch_rpt_officer_object() onbrand::fetch_officer_object()
system_set_rpt_officer_object() onbrand::set_officer_object()