Skip to content

Commit

Permalink
implement tutorial_package_dependencies() (#329)
Browse files Browse the repository at this point in the history
* implement tutorial_dependencies()

* add unit test

* rename file

* Move files into one place and unify renv call to a single function

* allow for tutorial_package_dependencies to work like available_packages

* document available tutorials. Add new column package_dependencies. Have tutorial_package_dependencies call available_tutorials if needed. Skip tests if learnr isn't installed

* return NULL for test and older R versions

* Correct the news item and add the PR

* Remove unneeded skip check

* Trim white space

* Reflow tutorial_package_dependencies() docs. Fix incorrect word

* Preempt if there are no deps found. R <= 3.4 can not `sort(NULL)`

Co-authored-by: Barret Schloerke <barret@rstudio.com>
  • Loading branch information
kevinushey and schloerke committed Feb 12, 2020
1 parent 6620c67 commit 89c1d14
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 46 deletions.
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export(safe_env)
export(tutorial)
export(tutorial_html_dependency)
export(tutorial_options)
export(tutorial_package_dependencies)
import(rmarkdown)
import(shiny)
importFrom(evaluate,evaluate)
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ learnr 0.10.0.9000 (unreleased)

## Minor new features and improvements

* `learnr` gained the function `learnr::tutorial_package_dependencies()`, used to enumerate a tutorial's R package dependencies. Front-ends can use this to ensure a tutorial's dependencies are satisfied before attempting to run that tutorial. `learnr::available_tutorials()` gained the column `package_dependencies` containing the required packages to run the document. ([#329](https://github.com/rstudio/learnr/pull/329))

* Include vignette about publishing learnr tutorials on shinyapps.io

* `learnr`'s built-in tutorials now come with a description as part of the YAML header, with the intention of this being used in front-end software that catalogues available `learnr` tutorials on the system. ([#312](https://github.com/rstudio/learnr/issues/312))
Expand Down
4 changes: 3 additions & 1 deletion R/available_tutorials.R
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#' development of the package (i.e. the corresponding tutorial .html file for
#' the .Rmd file must exist).
#'
#' @return \code{available_tutorials} will return a \code{data.frame} containing "package", "name", and "title".
#' @return \code{available_tutorials} will return a \code{data.frame} containing "package", "name", "title", "description", "package_dependencies", "private", and "yaml_front_matter".
#' @rdname available_tutorials
#' @export
available_tutorials <- function(package = NULL) {
Expand Down Expand Up @@ -39,6 +39,7 @@ available_tutorials <- function(package = NULL) {
#' "name": Tutorial directory. (can be passed in as `run_tutorial(NAME, PKG)`; string
#' "title": Tutorial title from yaml header; [NA]
#' "description": Tutorial description from yaml header; [NA]
#' "package_dependencies": Packages needed to run tutorial; [lsit()]
#' "private": Boolean describing if tutorial should be indexed / displayed; [FALSE]
#' "yaml_front_matter": list column of all yaml header info; [list()]
#' @noRd
Expand Down Expand Up @@ -87,6 +88,7 @@ available_tutorials_for_package <- function(package) {
title = yaml_front_matter$title %||% NA,
description = yaml_front_matter$description %||% NA,
private = yaml_front_matter$private %||% FALSE,
package_dependencies = I(list(tutorial_dir_package_dependencies(tut_dir))),
yaml_front_matter = I(list(yaml_front_matter)),
stringsAsFactors = FALSE,
row.names = FALSE
Expand Down
43 changes: 0 additions & 43 deletions R/install_tutorial_dependencies.R

This file was deleted.

81 changes: 81 additions & 0 deletions R/tutorial_package_dependencies.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
get_needed_pkgs <- function(dir) {

pkgs <- tutorial_dir_package_dependencies(dir)

pkgs[!pkgs %in% utils::installed.packages()]
}

format_needed_pkgs <- function(needed_pkgs) {
paste(" -", needed_pkgs, collapse = "\n")
}

ask_pkgs_install <- function(needed_pkgs) {
question <- sprintf("Would you like to install the following packages?\n%s",
format_needed_pkgs(needed_pkgs))

utils::menu(choices = c("yes", "no"),
title = question)
}

install_tutorial_dependencies <- function(dir) {
needed_pkgs <- get_needed_pkgs(dir)

if(length(needed_pkgs) == 0) {
return(invisible(NULL))
}

if(!interactive()) {
stop("The following packages need to be installed:\n",
format_needed_pkgs(needed_pkgs))
}

answer <- ask_pkgs_install(needed_pkgs)

if(answer == 2) {
stop("The tutorial is missing required packages and cannot be rendered.")
}

utils::install.packages(needed_pkgs)
}




#' List Tutorial Dependencies
#'
#' List the \R packages required to run a particular tutorial.
#'
#' @param name The tutorial name. If \code{name} is \code{NULL}, then all
#' tutorials within \code{package} will be searched.
#' @param package The \R package providing the tutorial. If \code{package} is
#' \code{NULL}, then all tutorials will be searched.
#'
#' @export
#' @return A character vector of package names that are required for execution.
#' @examples
#' tutorial_package_dependencies(package = "learnr")
tutorial_package_dependencies <- function(name = NULL, package = NULL) {
if (identical(name, NULL)) {
info <- available_tutorials(package = package)
return(
sort(unique(unlist(info$package_dependencies)))
)
}

tutorial_dir_package_dependencies(
get_tutorial_path(name = name, package = package)
)
}

tutorial_dir_package_dependencies <- function(dir) {
# enumerate tutorial package dependencies
deps <- renv::dependencies(dir, quiet = TRUE)

# R <= 3.4 can not sort(NULL)
# if no deps are found, renv::dependencies returns NULL
if (is.null(deps)) {
return(NULL)
}

sort(unique(deps$Package))
}
2 changes: 1 addition & 1 deletion man/available_tutorials.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions man/tutorial_package_dependencies.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions tests/testthat/test-dependency.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,11 @@ test_that("tutor html dependencies can be retreived", {
expect_equal(dep$name, "tutorial")
})

test_that("tutorial package dependencies can be enumerated", {
packages <- tutorial_package_dependencies("ex-data-summarise", "learnr")
expect_true("tidyverse" %in% packages)
})
test_that("Per package, tutorial package dependencies can be enumerated", {
packages <- tutorial_package_dependencies(package = "learnr")
expect_true("tidyverse" %in% packages)
})
1 change: 0 additions & 1 deletion tests/testthat/test-install-dependencies.R
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,3 @@ test_that("tutorial dependency check works (not interactive)", {

expect_error(install_tutorial_dependencies(tutorial_dir))
})

0 comments on commit 89c1d14

Please sign in to comment.