Generate dynamic form letters with R and Microsoft Word
Matthan Churchill -
If you've ever been tasked with completing a form letter in Microsoft Word, you probably know how tedious it can be. Sometimes letters and documents can be generated with software and tools – like the Mail Merge function within Microsoft Word, for example – but other times, documents with multiple nuanced fields are often just completed when needed.
One solution to completing heavily nuanced form letters could be found in R using the officer package from David Gohel. This brief tutorial creates a template job offer letter and uses R to complete its placeholder fields. You can find the tutorial code and files here if you are interested in reproducing the examples. First, we start with creating the template document.
Step 1: Prepare the Word Document
The method used to change our placeholder text is essentially a "find and replace" function. For this reason, it is important to use placeholders that are unique and are not partially (or fully) used elsewhere in the document. For example, using "appointment_type" and "appointment_type_abbr" as placeholders would not work because the function will incorrectly match the first merge field to the second. For this reason, we use "appointment_type" and "appointment_abbr" to avoid an unintentional mix-up.
For this tutorial, the below offer letter template will be used as an example.
Step 2: Write the R Script
We'll start the script by loading the above four libraries. However, before loading the packages, you'll need to install each with the "install.packages" function. More information on how to install packages can be found here.
Next, we load relevant data tables. In the above code, tables are loaded from an external Excel file. For this example, we'll use fictitious data from the four tables – "position_data", "union_data", "appointment_data", "department_data".
After loading our data tables we'll then write a function for our user inputs. In the above example, 10 inputs are used to return a single row table. The job code and department ID inputs are then used to lookup information from the tables loaded previously using various join and mutate functions. The result is a single row table with 21 inputs/variables – many of which will be later entered into our form letter.
Once we have our input function written, we'll next write our "find and replace" function. In the above example, the Word document is read, and a "body_replace_all_text" function (from the officer package) is written for each placeholder in our letter.
In the above R code, we call the "offerletter_inputs" function we created earlier and supply the necessary inputs. This effectively creates the 21 variable table mentioned before.
In our final step, we run the "find and replace" function and save the offer letter. The code above uses the "glue" function to concatenate fields from our inputs table into the document's file name. The result is a form letter that is dynamically filled and saved with our chosen naming convention.
Beyond the Script
One limitation to putting together an R script that generates dynamic form letters is that running the script requires some working knowledge of R. Providing the script to non-R users would require the addition of detailed instructions on how to install R, the packages, and run the code. However, we can avoid this and include non-R users by working our above code into a Shiny application.
Shiny is an R package that makes it possible to create web applications in a snap. For example, using Shiny, we can create a web application that utilizes our script to allow users to generate dynamic form letters right from their web browser.
The Shiny Package
The Shiny application will utilize much of the R code that we've already written. In particular, we will load the same packages, data, and function, though we will need to make a few changes to our code along the way.
We'll first load the packages, this time also including the Shiny package as shown above.
After our packages are loaded, we then load the relevant data and copy in our look-up function. The above lines of code are for the same data loading and look-up function that we wrote earlier.
The rest of our code will need to be rewritten along with the Shiny application – though much of it will be similar to what was written before. The Shiny application code is typically written in two separate sections – the UI (user interface) section, and the server section. The UI will serve as the application's front end, where information can be seen and/or entered by an end-user. The server-side section will handle all backend calculations. There are more interactive guides and tutorials on the main Shiny website, shared above. We'll start with the UI.
The UI code above consists of 10 inputs, identical to the inputs required by our inputs function. One benefit of using an interface to enter our inputs is that we can dictate how the information is entered. For example, in our code above, we allow our users to enter text for most of our inputs freely but only allow for dropdown selections for others (department, job code, salary step, and appointment type).
The server code above does a few things. The code is wrapped in the "downloadHandler" function and is executed when our end-user clicks the "Download Offer Letter" button. It first calls and runs our inputs function, and then feeds the user's inputs into a now deconstructed find-and-replace function. Lastly, it "prints" the file – effectively saving it with the same naming convention as chosen previously.
Lastly, the Shiny application is initiated (as shown in the above code). You can find a working version of the application through the following link if you want to check it out: https://matthanc.shinyapps.io/dynamicworddemo/.
Closing Thoughts
The examples used in this tutorial are somewhat limited in regards to the capabilities of the packages used. For instance, the officer package includes functions to create Word (.docx) and Powerpoint (.pptx) documents from the script line, without the need for an external document with placeholders. Similarly, there's much more we can do with Shiny to improve and stylize the application's interface.