Introduction

Imagine that you are a healthcare worker, and you are working on automating the next clinic day or the last clinic day of a patient based on:

  1. the number of days of medications prescribed, or
  2. the number of days the clinician has decided before the next check-up (if not the same as the number of pill days)

You’d like to get a response that looks like this:

“Your next clinic day is Saturday, 04 September 2021” or “Your last clinic day was Monday, 30th August 2021”

You have worked out the codes required and captured them into three functions shown below:

## Print date stamp as desired
date_stamp <- function(date) {
  date <- as.Date(date)
  format(date, "%A, %d %B %Y")
}


## last clinic appointment
appt_last <- function(c_date = Sys.Date(), d_pills, days = NULL) {
  c_date <- as.Date(c_date)

  dt <- if (is.null(days)) {
    date_stamp(c_date - as.integer(d_pills))
  } else {
    date_stamp(c_date - as.integer(days))
  }

  cat("Your last clinic appointment was on", dt, sep = " ")
}

## next clinic appointment
appt_next <- function(c_date = Sys.Date(), d_pills, days = NULL) {
  c_date <- as.Date(c_date)

  dt <- if (is.null(days)) {
    date_stamp(c_date + as.integer(d_pills))
  } else {
    date_stamp(c_date + as.integer(days))
  }

  cat("Your next clinic appointment is on", dt, sep = " ")
}

Create a package project

Having being part of the satuRday NairobiR conference, you have decided to develop these functions into a package. You have a colleague who has decided to walk you through the process of the package development. The steps involved are as below:

Step 1: Decide on the name you’d like to call your package (keep it simple and short)

Step 2: Check that the name is not already in used (on CRAN, GitHub and Bioconductor), and also the sentiment

available::available("my-pkg-name")

Step 3: When you have decided on a package name that is not already in use, create a package directory

library(devtools)
path <- "path-to-my-pkg/my-pkg-name"
create_package(path)

A new RStudio containing your project will open. The package folder should have the followings (you might have to click on “more” option in the “file” browser, then toggle on “show Hidden files”):

  • .gitignore

  • .Rbuildignore

  • pkg-name.Rproj

  • DESCRIPTION

  • NAMESPACE

  • R

  • .Rproj.user/

Your R codes

Step 4: Put your first function in new R scripts.

use_r("function-name")

The function above automatically creates a script in the R directory with the function name you have assigned.

Step 5: optimize your function for package development

## Print date stamp as desired
date_stamp <- function(date) {
  
  date <- lubridate::as_date(date)
  
  base::format(date, "%A, %d %B %Y")
}

Next, we will be iterating through load_all() and check() functions. load() makes our function available to be used in the console, while check() “checks” the status of our package development.

Step 6: load your function, and try it out

load_all()

date_stamp("2021-09-03")

date_stamp(Sys.Date())

We are making steady progress…

Step 7: Check package status

check()

Notice that you have two “warnings” already being displayed. But what did you do wrong? RStudio gives you more info by letting you know that one of the warnings is from licensing while the other one relates to the {lubridate} package. We will address these in a bit.

Step 8: modify the DESCRIPTION metadata. We will show example of two authors.

Title: Appointment Scheduling System for Patients # one line, sentence format
Authors@R: 
    c(person(given = "Stephen",
           family = "Balogun",
           role = c("aut", "cre"),
           email = "stephentaiyebalogun@gmail.com",
           comment = c(ORCID = "0000-0002-9928-3703")),
      person(given = "Shelmith",
           family = "Kariuki",
           role = "aut",
           email = "shelmith.kariuki@gmail.com",
           comment = c(ORCID = "0000-0002-2444-4226")))
Description: An easy, concise and reliable way of scheduling appointment for patients based on their clinic date,
            number of pills dispensed and/or number of days of appointment given.

Step 9: Pick a license for your package. Common ones are MIT + file license, GPL (GPL-2, GPL-3), CCO, CCBY. You can also use proprietary license.

use_mit_license("firstname Lastname")

Step 10: check your package again

check()

Notice that one of the “warnings” previously received has been addressed.

Step 11: write the documentation for your function. These are stored in the “man” folder (man/)

  • click anywhere in your script, then click on the “Code” tab, “Insert roxygen skeleton” and update accordingly

  • convert your roxygen code to function documentation

document() ## this also exports your function to the NAMESPACE file

Observe the changes in the NAMESPACE file.

Step 12: import all the functions from other packages used in creating your own function (aside pre-shipped R packages)

use_package("lubridate") ## run 'check()' to be sure that the second warning has been removed

Also include the packages as imports in the function. “@import pkg-name” or “@importFrom pkg-name pkg-function”. Observe the changes in the DESCRIPTION file.

Step 13: Now, we can install our package locally with the “install()” or using the “install and restart” button from the “build” pane

install()

Step 14: next is unit testing. First, we declare our intent to write unit tests and to use the testthat package for this, thereafter, we write the “test” for our function(s)

use_testthat()

## write the unit test
use_test("function-name")

## in the R script, insert

test_that("date_stamp is consistent", {
  expect_identical(
    date_stamp(Sys.Date()),
    format(Sys.Date(), format = "%A, %d %B %Y")
  )
})

Deploy your package

Step 15: It’s time to connect to git to manage your package development. Run “check()” again to be sure that all is set for deployment.

use_git() ## this creates a ".git" folder

You will be asked if you’d like to commit your codes and restart the computer.

Step 16: now that you have a (basic) functional package, deploy this to GitHub

use_github()

Step 17: document your package in a markdown document

use_readme_rmd()

## edit your markdown, then build
build_readme()

Step 18: Commit and push your files to GitHub

Step 19: Iterate through the processes from step 4 to step 18 using the other functions.

Step 20: Install your package from GitHub

  • restart your session to clear your environment
remotes::install_github("github-acct/package-name")

Optionals

Write supporting documents - vignettes

Step 21: write your vignette(s)

use_vignette("vignette-name", "Vignette Title")

Modify the template appropriately, then build your vignette.

build_vignettes()

Iterate if you are writing more than one vignette.

Step 22: “check” your package again

check()

Notice that this introduces a “warning” on Windows OS - “‘qpdf’ is needed for checks on size reduction of PDFs”. You need a application to compress the pdf vignette generated. Follow the steps below to resolve this:

  1. Download qpdf for windows from https://sourceforge.net/projects/qpdf/?source=typ_redirect

  2. Unzip the downloaded document

  3. Copy the extracted qpdf (qpdf-major-minor-patch) to a directory in C:/Program Files

  4. In R, run Sys.setenv(‘PATH’ = paste0(‘C:/Program Files/qpdf-version_numer/bin;’, Sys.getenv(‘PATH’))). One further step may be required to convince Windows that pqdf is safe to run. Navigate to “C:/Program Files/qpdf-version_numer/bin” and execute qpdf.exe (by double-clicking). You’ll need to use the more options link to find the button to run the program.

  5. Run “check()” again.

Submitting to CRAN

  1. In your DESCRIPTION file, update your package version to reflect three digits - major.minor.patch.
  2. Check that your licensing conforms to CRAN policies (any of the licenses described above is fine).
  3. Ensure that you have a valid email address that is open to receiving email from external sources.
  4. Include documentation about your package changes - use use_news_md() and modify appropriately.
  5. Fix all errors, warnings. Make efforts to address as many notes as possible. For first CRAN submission, there will be at least one “note 1” notify CRAN that it is a first submission.
  6. Follow the steps created by the release() .
  7. If revisions were made following feedback from CRAN, use submit_cran() to avoid walking through the other steps.