diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index ffca4661..14f0badf 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -14,33 +14,37 @@ jobs: R-CMD-check: runs-on: ubuntu-latest container: - image: jhudsl/base_ottr + image: jhudsl/base_ottr:main steps: - uses: actions/checkout@v4 + - name: Query dependencies + run: | + install.packages('remotes') + saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) + writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") + shell: Rscript {0} + - name: Install dependencies run: | remotes::install_deps(dependencies = TRUE) - remotes::install_cran("devtools") - sessionInfo() + remotes::install_cran("rcmdcheck") shell: Rscript {0} - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - name: Check + env: + GH_PAT: ${{ secrets.GH_PAT }} run: | options(crayon.enabled = TRUE) - devtools::document() - devtools::load_all() - rcmdcheck::rcmdcheck(args = c("--no-manual"), check_dir = "check") + rcmdcheck::rcmdcheck(args = c("--no-manual", "--as-cran"), check_dir = "check") shell: Rscript {0} - env: - GH_PAT: ${{ secrets.GH_PAT }} - name: Check testthat id: check_check run: | + Rscript -e "install.packages('tidyr', repos = 'http://cran.us.r-project.org')" + Rscript -e "devtools::test()" error_num=$(Rscript --vanilla '.github/workflows/check_testthat.R') echo "error_num=$error_num" >> $GITHUB_OUTPUT @@ -52,5 +56,4 @@ jobs: if: failure() uses: actions/upload-artifact@main with: - name: ${{ runner.os }}-r${{ matrix.config.r }}-results path: check diff --git a/.github/workflows/check_testthat.R b/.github/workflows/check_testthat.R index 831d1d2b..ecef234e 100644 --- a/.github/workflows/check_testthat.R +++ b/.github/workflows/check_testthat.R @@ -8,38 +8,21 @@ library(magrittr) # Find .git root directory root_dir <- rprojroot::find_root(rprojroot::has_dir(".git")) -# Read in the testthat results -out_file <- list.files(pattern = "testthat.Rout$|Rout.fail$", file.path(root_dir, "check"), +out_file <- list.files(pattern = ".Rout$|Rout.fail$", file.path(root_dir, "check"), recursive = TRUE, full.names = TRUE) -# Extract testhat results -testthat_check_content <- readLines(out_file) -testthat_result <- grep("\\[ FAIL", testthat_check_content, value = TRUE)[1] -testthat_result <- unlist(strsplit(testthat_result, "\\||\\[|\\]")) - -# Read in standard check results -check_content <- readLines(file.path(root_dir, "check", "ottrpal.Rcheck", "00check.log")) - -# Extract standard check results -check_result <- grep("Status\\:", check_content, value = TRUE) -check_result <- unlist(strsplit(check_result, ",")) -check_result <- stringr::str_remove(check_result, "Status:| ") +check_content <- readLines(out_file) +test_result <- grep("\\[ FAIL", check_content, value = TRUE)[1] +test_result <- unlist(strsplit(test_result, "\\||\\[|\\]")) # Format the data into a dataframe -testthat_result_df <- data.frame(result = trimws(testthat_result)) %>% +test_result_df <- data.frame(result = trimws(test_result)) %>% dplyr::filter(result != "") %>% tidyr::separate(result, sep = " ", into = c("test_name", "num")) %>% dplyr::mutate(num = as.numeric(num)) -# Do the same for the check results -check_result_df <- data.frame(result = trimws(check_result)) %>% - tidyr::separate(result, sep = " ", into = c("num", "test_name")) %>% - dplyr::mutate(num = as.numeric(num)) %>% - dplyr::select("test_name", "num") - -# We only want warnings or errors or fails -fail_num <- dplyr::bind_rows(check_result_df, testthat_result_df) %>% - dplyr::filter(test_name %in% c("FAIL", "WARN", "WARNINGs", "ERROR")) %>% +fail_num <- test_result_df %>% + dplyr::filter(test_name %in% c("FAIL", "WARN")) %>% dplyr::summarize(total = sum(num)) fail_num <- as.character(fail_num$total) diff --git a/DESCRIPTION b/DESCRIPTION index 5b05f496..69d3c57d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -25,12 +25,13 @@ Imports: dplyr, fs, httr, - janitor, + gitcreds, jsonlite, knitr (>= 1.33), magrittr, openssl, purrr, + quarto, R.utils, readr, rmarkdown (>= 2.10), @@ -44,12 +45,10 @@ Suggests: testthat (>= 3.0.0), tibble, utils -Remotes: - jhudsl/cow VignetteBuilder: knitr ByteCompile: true Encoding: UTF-8 LazyData: true -RoxygenNote: 7.3.1 +RoxygenNote: 7.3.2 Config/testthat/edition: 3 diff --git a/NAMESPACE b/NAMESPACE index b7fda99b..cdcc59c8 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -5,28 +5,30 @@ export(auth_from_secret) export(authorize) export(bad_quiz_path) export(check_all_questions) +export(check_git_repo) export(check_question) export(check_quiz) export(check_quiz_attributes) export(check_quiz_question_attributes) export(check_quizzes) +export(clean_up) export(convert_coursera_quizzes) export(convert_quiz) export(course_path) export(course_to_book_txt) -export(download_ottr_template) export(extract_meta) export(extract_object_id) export(extract_quiz) export(get_chapters) +export(get_git_auth) export(get_gs_pptx) export(get_image_from_slide) export(get_image_link_from_slide) export(get_object_id_notes) export(get_pages_url) +export(get_repo_info) export(get_slide_id) export(get_slide_page) -export(get_yaml_spec) export(good_quiz_path) export(gs_id_from_slide) export(gs_png_download) @@ -34,29 +36,27 @@ export(gs_png_url) export(include_slide) export(make_embed_markdown) export(make_screenshots) -export(output_destination) export(parse_q_tag) export(parse_quiz) export(parse_quiz_df) export(pptx_notes) export(pptx_slide_note_df) export(pptx_slide_text_df) -export(qrmd_files) export(render_without_toc) -export(set_knitr_image_path) -export(set_up_leanpub) +export(setup_ottr_template) export(unzip_pptx) export(website_to_embed_leanpub) export(xml_notes) import(dplyr) +importFrom(gitcreds,gitcreds_get) importFrom(httr,GET) importFrom(httr,accept_json) +importFrom(httr,authenticate) importFrom(httr,config) importFrom(httr,content) importFrom(httr,oauth2.0_token) importFrom(httr,oauth_app) importFrom(httr,oauth_endpoints) -importFrom(janitor,make_clean_names) importFrom(jsonlite,fromJSON) importFrom(magrittr,"%>%") importFrom(openssl,aes_cbc_decrypt) diff --git a/R/book_txt.R b/R/book_txt.R new file mode 100644 index 00000000..ee8144f9 --- /dev/null +++ b/R/book_txt.R @@ -0,0 +1,113 @@ + +#' Create Book.txt file from files existing in quiz directory +#' +#' @param path path to the bookdown or quarto course repository, must have a `_bookdown.yml` or `_quarto.yml` file +#' @param md_files vector of file path of the md's to be included +#' @param output_dir output directory to put files. It should likely be +#' relative to path +#' @param quiz_dir Where are the quizzes stored? Default looks for folder called "quizzes". +#' @param verbose print diagnostic messages +#' +#' @return A list of quiz and chapter files in order in a file called Book.txt -- How Leanpub wants it. +#' @export +#' +course_to_book_txt <- function(path = ".", + md_files = NULL, + output_dir = "manuscript", + quiz_dir = "quizzes", + verbose = TRUE) { + # If md_files are not specified, then try to get them + if (is.null(md_files)) { + # Establish path + root_dir <- course_path(path = path) + + rmd_regex <- "[.][q|R|r]md$" + + # Extract the names of the Rmd files (the chapters) + md_files <- qrmd_files(path = root_dir) + } + + if (!is.null(quiz_dir)) { + # Find the quiz files in the quiz directory + quiz_files <- list.files(pattern = "\\.md$", quiz_dir) + + # Put files in one vector + all_files <- c(md_files, quiz_files) + + # Make a vector specifying the file type: quiz or not + file_type <- c( + rep("non-quiz", length(md_files)), + rep("quiz", length(quiz_files)) + ) + } else { + all_files <- md_files + file_type <- rep("non-quiz", length(md_files)) + } + # Put all files in one data.frame + all_files <- data.frame( + file_name = all_files, + file_type + ) %>% + dplyr::mutate( + # Use this so we don't have to fiddle with case senstivity for the next step + lower_filename = tolower(file_name), + # Get the number from the file name and that will be the order + num = stringr::str_extract(file_name, "([0-9]+)"), + num = dplyr::case_when( + # Put index file first and about file last + lower_filename == "index.rmd" ~ "0", + lower_filename == "about.rmd" ~ as.character(length(all_files)), + TRUE ~ num + ), + num = as.numeric(num), + file_name = gsub(".Rmd$", ".md", file_name) + ) %>% + # Put quizzes in order! + dplyr::arrange(num, file_type) %>% + dplyr::pull(file_name) + + # Declare output file name + book_txt <- file.path(output_dir, "Book.txt") + + if (verbose) { + message(paste0("Autogenerated Book.txt saved to: ", book_txt)) + } + # need to fix about quiz + writeLines(all_files, con = book_txt) +} + +#' Get file paths to all qmds or rmds in the course website directory +#' +#' @param path Where to look for the _bookdown.yml or _quarto.yml file. Passes to get_yaml_spec() function. By default looks in current directory +#' +#' @return The file paths to rmds or wmds listed in the _bookdown.yml or _quarto.yml file. +#' @export +#' +qrmd_files <- function(path = ".") { + spec <- get_yaml_spec(file.path(path)) + + rmd_files <- spec$rmd_files + qmd_files <- grep(".qmd", unlist(spec$book$chapters), value = TRUE) + + if (length(rmd_files) > 0 && length(qmd_files) > 0) stop("Both qmd and rmd files are found. Not sure what format to expect") + + # Make files whichever ones exist here + if (length(rmd_files) > 0) files <- rmd_files + if (length(qmd_files) > 0) files <- qmd_files + + if (length(rmd_files) == 0 && length(qmd_files) == 0) { + warning( + "No rmd or qmd files found specified in the _quarto.yml or _bookdown.yml file. Going to try to find files in the repo based on suffix" + ) + root_dir <- course_path(path = file.path(path)) + files <- list.files( + pattern = "[.]Rmd$|[.]qmd$", ignore.case = TRUE, + path = root_dir, full.names = FALSE + ) + } + + # If we don't find files STOP + if (length(files) == 0) stop("No rmd/qmd files found in the repo") + + return(files) +} diff --git a/R/coursera_and_leanpub.R b/R/coursera_and_leanpub.R deleted file mode 100644 index 2cb7c4dc..00000000 --- a/R/coursera_and_leanpub.R +++ /dev/null @@ -1,358 +0,0 @@ -# C. Savonen 2021 - -# Need magrittr -`%>%` <- dplyr::`%>%` - -find_end_of_prompt <- function(start_prompt_index, type_vector) { - # We want to see if the next line is where the answers start - end_prompt_index <- start_prompt_index + 1 - - # See if the end of the prompt is in the same line - end_prompt <- grepl("answer", type_vector[end_prompt_index]) - - # Keep looking in each next line until we find it. - if (end_prompt == FALSE) { - while (end_prompt == FALSE) { - # Add one - end_prompt_index <- end_prompt_index + 1 - - # Look in next line - end_prompt <- grepl("answer", type_vector[end_prompt_index]) - - if (end_prompt_index == length(type_vector) && end_prompt == FALSE) { - stop(paste("Searched end of file and could not find end of prompt that starts at line:", start_prompt_index)) - } - } - } else { - end_prompt_index <- start_prompt_index - } - return(end_prompt_index) -} - -#' Convert Leanpub md quiz to Coursera yaml quiz -#' -#' Convert a Leanpub-formatted md quiz file to a Coursera-formatted yaml quiz file in preparation for uploading to Coursera. -#' -#' @param quiz_path A path to a quiz .md file to be converted. -#' @param output_quiz_dir An existing folder where you would like the new version of the quiz to be saved. -#' Default is the directory of the quiz_path provided -#' @param verbose Would you like the progress messages? -#' -#' @return A Coursera-ready quiz file saved to the output directory specified as a yaml. -#' @export convert_quiz -#' -#' @examples \dontrun{ -#' -#' quiz_path <- good_quiz_path() -#' -#' # Provide path to quiz to convert -#' convert_quiz(quiz_path) -#' } -convert_quiz <- function(quiz_path, - output_quiz_dir = dirname(quiz_path), - verbose = TRUE) { - # Print out which quiz we're converting - message(paste("Converting quiz:", quiz_path)) - - output_filename <- file.path(output_quiz_dir, paste0(basename(quiz_path), ".yml")) - - ### First read lines for each quiz - # Put it as a data.frame: - quiz_lines_df <- parse_quiz_df(readLines(quiz_path), remove_tags = TRUE) - - # Add in proper Coursera yaml mappings based on the parsing - quiz_lines_df <- quiz_lines_df %>% - # Now for updating based on type! - dplyr::mutate(updated_line = dplyr::case_when( - type %in% c("prompt", "single_line_prompt") ~ stringr::str_replace(original, "^\\?", " prompt:"), - type %in% c("extended_prompt", "end_prompt") ~ paste0(" ", original), - grepl("answer", type) ~ stringr::str_replace(original, "^[[:alpha:]]\\)", " - answer:"), - TRUE ~ original - )) - - # Declare the correct answers by which ones are the first ones listed - correct_answers <- quiz_lines_df %>% - dplyr::filter(type == "correct_answer") %>% - dplyr::distinct(question, .keep_all = TRUE) - - # Identify other correct answers that we don't want to keep - remove_answers <- setdiff( - dplyr::filter(quiz_lines_df, type == "correct_answer")$index, - correct_answers$index - ) - - # Remove any correct answers that aren't those - quiz_lines_df <- quiz_lines_df %>% - dplyr::filter(!(index %in% remove_answers)) - - # Turn updated lines into a named vector - updated_quiz_lines <- quiz_lines_df$updated_line - names(updated_quiz_lines) <- quiz_lines_df$type - - ### Add specs for coursera - # Add typeName before prompt starts: - updated_quiz_lines <- R.utils::insert(updated_quiz_lines, - ats = which(names(updated_quiz_lines) %in% c("prompt", "single_line_prompt")), - values = "- typeName: multipleChoice" - ) - - ### Add " options:" before beginning of answer options - updated_quiz_lines <- R.utils::insert(updated_quiz_lines, - ats = which(names(updated_quiz_lines) %in% c("end_prompt", "single_line_prompt")) + 1, - values = " options:" - ) - - # Add shuffleoptions: true after prompt ends - updated_quiz_lines <- R.utils::insert(updated_quiz_lines, - ats = which(names(updated_quiz_lines) %in% c("end_prompt", "single_line_prompt")) + 1, - values = " shuffleOptions: true" - ) - - # Need to add "isCorrect: true" one line below correct value lines - updated_quiz_lines <- R.utils::insert(updated_quiz_lines, - ats = which(names(updated_quiz_lines) == "correct_answer") + 1, - values = " isCorrect: true" - ) - - # Need to add "isCorrect: false" one line below incorrect value lines - updated_quiz_lines <- R.utils::insert(updated_quiz_lines, - ats = which(names(updated_quiz_lines) == "wrong_answer") + 1, - values = " isCorrect: false" - ) - - # Remove other lines - updated_quiz_lines <- updated_quiz_lines[-grep("other", names(updated_quiz_lines))] - - # Add extra line in between each question - updated_quiz_lines <- R.utils::insert(updated_quiz_lines, - ats = grep("typeName:", updated_quiz_lines), - values = "" - ) - - # Trim trailing space - updated_quiz_lines <- trimws(updated_quiz_lines, which = "right") - - # Add extra line in between each question - updated_quiz_lines <- stringr::str_remove(updated_quiz_lines, ":$") - - # Return the options : though - updated_quiz_lines <- stringr::str_replace(updated_quiz_lines, " options", " options:") - - ### Write new file with .yml at end of file name and put in coursera dir - writeLines(updated_quiz_lines, con = output_filename) - - # Put message - message(paste("Converted quiz saved to:", output_filename)) -} - -#' Convert Leanpub md quiz to Coursera yaml quiz -#' -#' @param input_quiz_dir A path to a directory of leanpub formatted quiz md files. By default assumes "quizzes" and looks in current directory. -#' @param output_quiz_dir A folder (existing or not) that the new coursera converted quizzes should be saved to. By default saves to "coursera_quizzes". -#' @param verbose Would you like the progress messages: TRUE/FALSE? -#' -#' @return A folder of coursera ready quiz files saved to the output directory specified as a yamls. -#' @export -#' -#' @examples -#' -#' # Set up a directory with a quiz in it for this example -#' tdir <- tempfile() -#' dir.create(tdir, showWarnings = FALSE, recursive = TRUE) -#' -#' file.copy( -#' from = good_quiz_path(), -#' to = file.path(tdir, basename(good_quiz_path())) -#' ) -#' -#' # Provide path to directory of quizzes -#' convert_coursera_quizzes(tdir) -#' -#' system("rm -r coursera_quizzes") -convert_coursera_quizzes <- function(input_quiz_dir = "quizzes", - output_quiz_dir = "coursera_quizzes", - verbose = TRUE) { - # Create directory if it is not yet created - if (!dir.exists(output_quiz_dir)) { - dir.create(output_quiz_dir, recursive = TRUE) - } - - # List quiz paths - leanpub_quizzes <- list.files( - pattern = (".md"), - ignore.case = TRUE, - path = input_quiz_dir, - full.names = TRUE - ) - - if (length(leanpub_quizzes) < 1) { - stop(paste0("No quiz .md files found in your specified path dir of: ", quiz_path)) - } - - # Run the thing! - lapply(leanpub_quizzes, - convert_quiz, - verbose = verbose, - output_quiz_dir = output_quiz_dir - ) -} - -#' Create TOC-less course website for use in Coursera or Leanpub -#' -#' Create a version of the course that does not have a TOC and has quizzes in the Coursera yaml format. -#' This is only needed to be used on Bookdown courses. Quarto has a simple command for this. -#' -#' @param output_dir A folder (existing or not) that the TOC-less Bookdown for Coursera files should be saved. By default is file.path("docs", "coursera") -#' @param output_yaml A output.yml file to be provided to bookdown. By default is "_output.yml" -#' @param convert_quizzes TRUE/FALSE whether or not to convert quizzes. Default is TRUE -#' @param input_quiz_dir A path to a directory of Leanpub-formatted quiz md files. By default assumes "quizzes" and looks in current directory. -#' @param output_quiz_dir A folder (existing or not) where the coursera quizzes should be saved. By default is "coursera_quizzes". -#' @param verbose Would you like the progress messages? TRUE/FALSE -#' -#' @return A folder of coursera ready quiz files and html chapter files saved to output directories specified. -#' @export -#' @rdname coursera -#' -#' @importFrom utils download.file -#' -render_without_toc <- function(output_dir = file.path("docs", "no_toc"), - output_yaml = "_output.yml", - convert_quizzes = FALSE, - input_quiz_dir = "quizzes", - output_quiz_dir = "coursera_quizzes", - verbose = TRUE) { - # Find root directory by finding `.git` folder - root_dir <- course_path() - - # Output files: - output_dir <- file.path(root_dir, output_dir) - - ###### Check we have the files we need ###### - # Create output folder if it does not exist - if (!dir.exists(output_dir)) { - message(paste0("Creating output folder: ", output_dir)) - dir.create(output_dir, recursive = TRUE, showWarnings = FALSE) - } - - ###### Declare all the file paths relative to root directory ###### - # Input files: - toc_close_css <- file.path(root_dir, "assets", "toc_close.css") - - if (!file.exists(toc_close_css)) { - download.file("https://raw.githubusercontent.com/jhudsl/ottrpal/master/inst/extdata/toc_close.css", - destfile = toc_close_css - ) - } - output_yaml_file <- file.path(root_dir, output_yaml) - - # Make sure we have that file - if (!file.exists(toc_close_css)) { - stop(paste0("Could not find: ", toc_close_css)) - } - # Make sure we know where the output yaml is - if (!file.exists(output_yaml_file)) { - stop(paste0("Could not find: ", output_yaml_file)) - } - - # Clean out old files if they exist - old_files <- list.files(output_dir, pattern = c("html$", "md$"), full.names = TRUE) - if (length(old_files) > 0) { - file.remove(old_files) - } - - ###### Copy over needed directories ###### - # Copy these directories over if they don't exist in the output folder - needed_directories <- c("assets", "resources") - - if (verbose) { - message(paste0(c("Needed directories being copied:"), collapse = "\n")) - } - - # Do the copying - lapply(needed_directories, function(needed_dir) { - if (verbose) { - message(needed_dir) - } - if (!dir.exists(needed_dir)) { - stop(paste0("Needed directory:", needed_dir, "does not exist in the current path.")) - } - if (!dir.exists(file.path(output_dir, needed_dir))) { - fs::dir_copy(needed_dir, file.path(output_dir, needed_dir), overwrite = TRUE) - } - }) - - # Slightly different path for the libs folder - libs_path <- file.path("docs", "libs") - if (!dir.exists(file.path(output_dir, "libs"))) { - if (verbose) { - message(file.path("docs", "libs")) - } - fs::dir_copy(libs_path, file.path(output_dir, "libs"), overwrite = TRUE) - } - - ###### Copy over CSS file ###### - # Retrieve yaml file specs - output_yaml_lines <- yaml::yaml.load_file(output_yaml_file) - - # Copy over css file(s) that's specified - org_css_file <- output_yaml_lines$`bookdown::gitbook`$css - - # Check if there are multiple .css - if (length(org_css_file) > 1) { - # Read all .css - css_files_read <- sapply(org_css_file, readLines) - - # Make a "mega .css" and write - if (verbose) { - message("Combining .css files") - } - css_lines_cat <- rbind(unlist(css_files_read)) - css_file <- file.path(output_dir, org_css_file[1]) - writeLines(css_lines_cat, css_file) - } else { - css_file <- file.path(output_dir, org_css_file) - - # Write it as "style.css" - fs::file_copy(org_css_file, - css_file, - overwrite = TRUE - ) - } - - ###### Now do the rendering! ###### - message("Render bookdown without TOC") - - # Do the render - bookdown::render_book( - input = "index.Rmd", - output_yaml = output_yaml_file, - output_dir = output_dir - ) - - # Read in TOC closing CSS lines - toc_close_css_lines <- readLines(toc_close_css) - - # Using suppressWarnings() because "incomplete final line" - full_css <- suppressWarnings( - readLines(css_file) - ) - - # Write to "style.css" - writeLines(append(full_css, toc_close_css_lines), css_file) - - # Only convert the quizzes if set to TRUE - if (convert_quizzes) { - if (!dir.exists(input_quiz_dir)) { - stop( - "convert_quizzes = TRUE but the specified input_quiz_dir: ", - input_quiz_dir, - " cannot be found." - ) - } - convert_coursera_quizzes( - input_quiz_dir = input_quiz_dir, - output_quiz_dir = output_quiz_dir, - verbose = verbose - ) - } -} diff --git a/R/data.R b/R/get_data.R similarity index 71% rename from R/data.R rename to R/get_data.R index c0d1c2f0..17ebc38e 100644 --- a/R/data.R +++ b/R/get_data.R @@ -1,11 +1,11 @@ -#' Download files from main OTTR_Template to test +#' Download and render files from main OTTR_Template to test #' #' @param dir What relative file path should the files be downloaded #' @param type Which OTTR repo are we downloading? Options are "rmd", "quarto", "rmd_website", "quarto_website" #' #' @return This downloads the main branch repo files from the respective repo for testing purposes #' @export -download_ottr_template <- function(dir = "inst/extdata", type = "rmd") { +setup_ottr_template <- function(dir = "inst/extdata", type) { if (!dir.exists(dir)) dir.create(dir, recursive = TRUE, showWarnings = FALSE) possible_types <- c("rmd", "quarto", "rmd_website", "quarto_website") @@ -44,9 +44,47 @@ download_ottr_template <- function(dir = "inst/extdata", type = "rmd") { unzip(file_path, exdir = dir) } + ## Render it + if (type == "rmd") bookdown::render_book(output_dir) + if (type == "rmd_website") rmarkdown::render_site(output_dir) + + if (type == "quarto" | type == "quarto_website") { + quarto::quarto_render(output_dir, as_job = FALSE) + } + if (type == "quarto") { + quarto::quarto_render(output_dir, + metadata = list(sidebar = F, toc = F), + quarto_args = c("--output-dir", "docs/no_toc/"), + as_job = FALSE + ) + } return(output_dir) } +#' Clean up OTTR_Template files used for testing +#' +#' @return Looks for dangling zips and directories downloaded for testing and removes them +#' @export +clean_up <- function() { + dirs <- c( + "OTTR_Template-main", + "OTTR_Quarto-main", + "OTTR_Template_Website-main", + "OTTR_Quarto_Website-main" + ) + + zips <- paste0(dirs, ".zip") + + # Remove dirs and their files + sapply(dirs, unlink, recursive = TRUE) + + # Which zips are out there? + existing_zips <- list.files(pattern = paste0(zips, collapse = "|")) + + # Remove any dangling zips + sapply(existing_zips, unlink) +} + #' Path to good example quiz #' diff --git a/R/get_pages_url.R b/R/get_pages_url.R deleted file mode 100644 index 14471259..00000000 --- a/R/get_pages_url.R +++ /dev/null @@ -1,81 +0,0 @@ -#' Retrieve pages url for a repo -#' -#' Given an repository on GitHub, retrieve the pages URL for it. -#' -#' @param repo_name The full name of the repo to get bookdown chapters from. -#' e.g. "jhudsl/OTTR_Template" -#' @param git_pat If private repositories are to be retrieved, a github personal -#' access token needs to be supplied. If none is supplied, then this will attempt to -#' grab from a git pat set in the environment with usethis::create_github_token(). -#' Authorization handled by \link[cow]{get_git_auth} -#' @param verbose TRUE/FALSE do you want more progress messages? -#' @param keep_json verbose TRUE/FALSE keep the json file locally? -#' -#' @return a data frame with the repository with the following columns: -#' data_level, data_path, chapt_name, url, repository name -#' -#' @importFrom magrittr %>% -#' @import dplyr -#' -#' @export -#' -#' @examples \dontrun{ -#' -#' usethis::create_github_token() -#' -#' get_pages_url("jhudsl/Documentation_and_Usability") -#' } -get_pages_url <- function(repo_name, - git_pat = NULL, - verbose = FALSE, - keep_json = FALSE) { - page_url <- NA - - # Try to get credentials other way - auth_arg <- get_git_auth(git_pat = git_pat, quiet = !verbose) - - git_pat <- try(auth_arg$password, silent = TRUE) - - if (grepl("Error", git_pat[1])) { - warning("Cannot retrieve page info without GitHub credentials. Passing an NA.") - } - - # We can only retrieve pages if we have the credentials - if (!grepl("Error", git_pat[1])) { - exists <- check_git_repo( - repo_name = repo_name, - git_pat = git_pat, - verbose = FALSE - ) - - if (exists) { - # Get repo info - repo_info <- get_repo_info( - repo_name = repo_name, - git_pat = git_pat - ) - - # Declare URL - url <- paste0("https://api.github.com/repos/", repo_name, "/pages") - - # Github api get - response <- httr::GET( - url, - httr::add_headers(Authorization = paste0("token ", auth_arg$password)), - httr::accept_json() - ) - - if (httr::http_error(response)) { - if (verbose) { - warning(paste0("url: ", url, " failed")) - } - } else { - # Get content as JSON - page_info <- httr::content(response, as = "parsed") - - page_url <- page_info$html_url - } - } - } - return(page_url) -} diff --git a/R/github_handling.R b/R/github_handling.R new file mode 100644 index 00000000..949d9849 --- /dev/null +++ b/R/github_handling.R @@ -0,0 +1,288 @@ +#' Retrieve pages url for a repo +#' +#' Given an repository on GitHub, retrieve the pages URL for it. +#' +#' @param repo_name The full name of the repo to get bookdown chapters from. +#' e.g. "jhudsl/OTTR_Template" +#' @param git_pat If private repositories are to be retrieved, a github personal +#' access token needs to be supplied. If none is supplied, then this will attempt to +#' grab from a git pat set in the environment with usethis::create_github_token(). +#' Authorization handled by \link[cow]{get_git_auth} +#' @param verbose TRUE/FALSE do you want more progress messages? +#' @param keep_json verbose TRUE/FALSE keep the json file locally? +#' +#' @return a data frame with the repository with the following columns: +#' data_level, data_path, chapt_name, url, repository name +#' +#' @importFrom magrittr %>% +#' @import dplyr +#' +#' @export +#' +#' @examples \dontrun{ +#' +#' usethis::create_github_token() +#' +#' get_pages_url("jhudsl/Documentation_and_Usability") +#' } +get_pages_url <- function(repo_name, + git_pat = NULL, + verbose = FALSE, + keep_json = FALSE) { + page_url <- NA + + # Try to get credentials other way + auth_arg <- get_git_auth(git_pat = git_pat, quiet = !verbose) + + git_pat <- try(auth_arg$password, silent = TRUE) + + if (grepl("Error", git_pat[1])) { + warning("Cannot retrieve page info without GitHub credentials. Passing an NA.") + } + + # We can only retrieve pages if we have the credentials + if (!grepl("Error", git_pat[1])) { + exists <- check_git_repo( + repo_name = repo_name, + git_pat = git_pat, + verbose = FALSE + ) + + if (exists) { + # Get repo info + repo_info <- get_repo_info( + repo_name = repo_name, + git_pat = git_pat + ) + + # Declare URL + url <- paste0("https://api.github.com/repos/", repo_name, "/pages") + + # Github api get + response <- httr::GET( + url, + httr::add_headers(Authorization = paste0("token ", auth_arg$password)), + httr::accept_json() + ) + + if (httr::http_error(response)) { + if (verbose) { + warning(paste0("url: ", url, " failed")) + } + } else { + # Get content as JSON + page_info <- httr::content(response, as = "parsed") + + page_url <- page_info$html_url + } + } + } + return(page_url) +} + +#' Retrieve information about a github repo +#' +#' Given an repository on GitHub, retrieve the information about it from the +#' GitHub API and read it into R. +#' +#' @param repo_name The full name of the repo to get bookdown chapters from. +#' e.g. "jhudsl/OTTR_Template" +#' @param git_pat If private repositories are to be retrieved, a github personal +#' access token needs to be supplied. If none is supplied, then this will attempt to +#' grab from a git pat set in the environment with usethis::create_github_token(). +#' Authorization handled by \link[githubr]{get_git_auth} +#' @param verbose TRUE/FALSE do you want more progress messages? +#' +#' @return a data frame with the repository with the following columns: +#' data_level, data_path, chapt_name, url, repository name +#' +#' @importFrom httr GET +#' @importFrom httr accept_json +#' @importFrom httr authenticate +#' @importFrom gitcreds gitcreds_get +#' @import dplyr +#' +#' @export +#' +#' @examples +#' +#' repo_info <- get_repo_info("jhudsl/Documentation_and_Usability") +get_repo_info <- function(repo_name, + git_pat = NULL, + verbose = FALSE) { + repo_info <- NA + + exists <- check_git_repo( + repo_name = repo_name, + git_pat = git_pat, + verbose = FALSE, + silent = TRUE + ) + + if (exists) { + # Declare URL + url <- paste0("https://api.github.com/repos/", repo_name) + + # Try to get credentials other way + auth_arg <- get_git_auth(git_pat = git_pat) + + git_pat <- try(auth_arg$password, silent = TRUE) + + if (grepl("Error", git_pat[1])) { + # Github api get without authorization + response <- httr::GET( + url, + httr::accept_json() + ) + } else { + # Github api get + response <- httr::GET( + url, + httr::add_headers(Authorization = paste0("token ", git_pat)), + httr::accept_json() + ) + } + + if (httr::http_error(response)) { + warning(paste0("url: ", url, " failed")) + } + + # Get content as JSON + repo_info <- httr::content(response, as = "parsed") + } else { + warning(paste0(repo_name, " could not be found with the given credentials.")) + } + return(repo_info) +} + +#' Check if a repository exists on GitHub +#' +#' Given a repository name, check with git ls-remote whether the repository exists and return a TRUE/FALSE +#' +#' @param repo_name the name of the repository, e.g. jhudsl/OTTR_Template +#' @param git_pat A personal access token from GitHub. Only necessary if the +#' repository being checked is a private repository. +#' @param silent TRUE/FALSE of whether the warning from the git ls-remote +#' command should be echoed back if it does fail. +#' @param verbose TRUE/FALSE do you want more progress messages? +#' @param return_repo TRUE/FALSE of whether or not the output from git ls-remote +#' should be saved to a file (if the repo exists) +#' +#' @return A TRUE/FALSE whether or not the repository exists. Optionally the +#' output from git ls-remote if return_repo = TRUE. +#' +#' @export +#' +#' @examples +#' +#' check_git_repo("jhudsl/OTTR_Template") +check_git_repo <- function(repo_name, + git_pat = NULL, + silent = TRUE, + return_repo = FALSE, + verbose = TRUE) { + if (verbose) { + message(paste("Checking for remote git repository:", repo_name)) + } + # If silent = TRUE don't print out the warning message from the 'try' + report <- ifelse(silent, suppressWarnings, message) + + # Try to get credentials + auth_arg <- get_git_auth(git_pat = git_pat, quiet = !verbose) + + git_pat <- try(auth_arg$password, silent = TRUE) + + # Run git ls-remote + if (!grepl("Error", git_pat[1])) { + # If git_pat is supplied, use it + test_repo <- report( + try(system(paste0("git ls-remote https://", git_pat, "@github.com/", repo_name), + intern = TRUE, ignore.stderr = TRUE + )) + ) + } else { + # Try to git ls-remote the repo_name given + test_repo <- report + try(system(paste0("git ls-remote https://github.com/", repo_name), + intern = TRUE, ignore.stderr = TRUE + )) + } + # If 128 is returned as a status attribute it means it failed + exists <- ifelse(is.null(attr(test_repo, "status")), TRUE, FALSE) + + if (return_repo && exists) { + # Make file name + output_file <- paste0("git_ls_remote_", gsub("/", "_", repo_name)) + + # Tell the user the file was saved + message(paste("Saving output from git ls-remote to file:", output_file)) + + # Write to file + writeLines(exists, file.path(output_file)) + } + + return(exists) +} + +#' Handle GitHub PAT authorization +#' +#' Handle things whether or not a GitHub PAT is supplied. +#' +#' @param git_pat If private repositories are to be retrieved, a github personal +#' access token needs to be supplied. If none is supplied, then this will attempt to +#' grab from a git pat set in the environment with usethis::create_github_token(). +#' @param git_username Optional, can include username for credentials. +#' @param quiet Use TRUE if you don't want the warning about no GitHub credentials. +#' +#' @return Authorization argument to supply to curl OR a blank string if no +#' authorization is found or supplied. +#' +#' @export +#' +get_git_auth <- function(git_pat = NULL, git_username = "PersonalAccessToken", quiet = FALSE) { + auth_arg <- NULL + + # If git pat is not provided, try to get credentials with gitcreds + if (is.null(git_pat)) { + # Try getting credentials + auth_arg <- try(gitcreds::gitcreds_get(), silent = TRUE) + + if (grepl("Could not find any credentials", auth_arg[1])) { + # Only if we're running this interactively + if (interactive()) { + # Set credentials if null + auth_arg <- gitcreds::gitcreds_set() + } else { + if (!quiet) { + message("Could not find git credentials, please set by running usethis::create_github_token(), + or directly providing a personal access token using the git_pat argument") + } + } + } + } else { # If git_pat is given, use it. + # Set to Renviron file temporarily + Sys.setenv(GITHUB_PAT = git_pat) + + # Put it in gitcreds + auth_arg <- gitcreds::gitcreds_get() + + # Delete from Renviron file + Sys.unsetenv("GITHUB_PAT") + + # Set up rest of token + auth_arg$protocol <- "https" + auth_arg$host <- "github.com" + auth_arg$username <- git_username + } + + # Check if we have authentication + git_pat <- try(auth_arg$password, silent = TRUE) + + if (grepl("Error", git_pat[1])) { + if (!quiet) { + message("No github credentials found or provided; only public repositories will be successful.") + } + } + + return(auth_arg) +} diff --git a/R/google_slides.R b/R/google_slides.R new file mode 100644 index 00000000..80e41b60 --- /dev/null +++ b/R/google_slides.R @@ -0,0 +1,230 @@ +#' Get Slide ID from URL +#' +#' @param x URL of slide +#' +#' @return A character vector +#' @export +#' +#' @examples +#' x <- paste0( +#' "https://docs.google.com/presentation/d/", +#' "1Tg-GTGnUPduOtZKYuMoelqUNZnUp3vvg_7TtpUPL7e8", +#' "/edit#slide=id.g154aa4fae2_0_58" +#' ) +#' get_slide_id(x) +get_slide_id <- function(x) { + x <- sub(".*presentation/", "", x) + x <- sub("/d/e", "/d", x) # if you publish by accident + x <- sub("^(d|e)/", "", x) + x <- strsplit(x, "/")[[1]] + x <- x[!grepl("^(edit|pub|export|png)", x)] + x <- x[nchar(x) > 5] + x +} + +#' Get Google Slide PNG URL +#' +#' @param url URL to Google Slide +#' +#' @return A character vector of URLs +#' @export +#' +#' @examples +#' url <- paste0( +#' "https://docs.google.com/presentation/d/", +#' "12DPZgPteQBwgal6kSPP58zhPhjZ7QSPZLe3NkA8M3eo/edit", +#' "#slide=id.gc8648f14c3_0_397&t=4" +#' ) +#' id <- get_slide_id(url) +#' gs_png_url(url) +gs_png_url <- function(url) { + id <- get_slide_id(url) + slide_id <- get_slide_page(url) + gs_png_id(id, slide_id) +} + +gs_png_id <- function(id, slide_id) { + if (any(grepl("^id[.]", slide_id))) { + warning( + "slide ids usually don't have format of id.gc*, ", + "you should likely remove the id." + ) + } + paste0( + "https://docs.google.com/presentation/d/", + id, + "/export/png?id=", id, + "&pageid=", slide_id + ) +} + +#' @export +#' @rdname gs_png_url +get_slide_page <- function(url) { + parsed <- httr::parse_url(url) + slide_id <- parsed$query$pageid + if (length(slide_id) == 0 || nchar(slide_id) == 0) { + fragment <- parsed$fragment + slide_id <- sub("slide=(.*)", "\\1", fragment) + slide_id <- sub("&.*", "", slide_id) + slide_id <- sub("^id[.]", "", slide_id) + } + stopifnot(length(slide_id) > 0 && nchar(slide_id) > 0) + slide_id +} + +#' @export +#' @rdname gs_png_url +#' @param output_dir path to output png +#' @param overwrite should the slide PNG be overwritten? +gs_png_download <- function(url, output_dir = ".", overwrite = TRUE) { + + id <- get_slide_id(url) + slide_id <- get_slide_page(url) + url <- gs_png_url(url) + dir.create(output_dir, recursive = TRUE, showWarnings = FALSE) + outfile <- file.path(output_dir, paste0(id, "_", slide_id, ".png")) + if (!file.exists(outfile) || overwrite) { + curl::curl_download(url, destfile = outfile, quiet = FALSE) + } + stopifnot(file.exists(outfile)) + outfile +} + +#' @export +#' @rdname gs_png_url +#' @param ... for \code{include_slide}, options passed to +#' [knitr::include_graphics()] +include_slide <- function(url, + output_dir = knitr::opts_chunk$get("fig.path"), + overwrite = TRUE, ...) { + outfile <- gs_png_download(url, output_dir, overwrite = overwrite) + knitr::include_graphics(outfile, ...) +} + +#' Google Slides Helper Functions +#' +#' @param file markdown file for manuscript +#' +#' @return A scalar character vector +#' @export +#' @rdname gs_helpers +gs_id_from_slide <- function(file) { + if (!file.exists(file)) { + return(NA_character_) + } + x <- readLines(file, warn = FALSE) + ind <- grep(x, pattern = "\\[(S|s)lides\\]") + if (length(ind) > 0) { + x <- x[ind] + } else { + x <- x[grep(x, pattern = ".*presentation/d/")] + } + if (!any(grepl("http", x))) { + return(NA_character_) + } + x <- sub(".*\\(\\s*(http.*)\\s*\\).*", "\\1", x) + x <- unlist(sapply(x, function(r) httr::parse_url(r)$path)) + x <- sub("/edit$", "", x) + x <- sub("/export/.*", "", x) + x <- basename(x) + x <- stats::na.omit(x) + x <- x[nchar(x) > 5] + ux <- unique(x) + if (length(ux) > 1) { + warning(paste0( + "Multiple sheets identified! Taking most frequent.", + " Please check ", + file + )) + x <- sort(table(x), decreasing = TRUE) + x <- names(x)[1] + # x = x[1] + } else { + x <- ux + } + if (length(x) == 0 || grepl("\\(\\)", x)) { + return(NA_character_) + } + if (nchar(x) < 10) { + warning(paste0("ID extracted is ", x, ", seems short")) + } + return(x) +} + +###################################### +# this returns the actual links in the text +###################################### +#' @export +#' @rdname gs_helpers +get_image_link_from_slide <- function(file) { + x <- readLines(file, warn = FALSE) + x <- grep(x, pattern = "!\\[.*\\]\\(((resources/|)images.*)\\)", value = TRUE) + x <- sub(x, pattern = "!\\[(.*)\\]\\(((resources/|)images.*)\\)", replacement = "\\1") + # if (length(x) == 0) { + # return(NA) + # } + return(x) +} + +###################################### +# this returns the actual image filenames referenced +# we will check to see if all images referenced exist +###################################### +#' @export +#' @rdname gs_helpers +get_image_from_slide <- function(file) { + x <- readLines(file, warn = FALSE) + x <- grep(x, pattern = "!\\[.*\\]\\(((resources/|)images.*)\\)", value = TRUE) + x <- sub(x, pattern = "!\\[.*\\]\\(((resources/|)images.*)\\)", replacement = "\\1") + # if (length(x) == 0) { + # return(NA) + # } + return(x) +} + +is.Token <- function(token) { + inherits(token, "Token") || + (inherits(token, "request") && + inherits(token$auth_token, "Token")) +} + +png_url <- function(id, page_id) { + paste0( + "https://docs.google.com/presentation/d/", + id, "/export/png?id=", id, + "&pageid=", page_id + ) +} + +download_png_urls <- function(urls) { + res <- vapply(urls, function(url) { + tfile <- tempfile(fileext = ".png") + out <- httr::GET( + url, httr::write_disk(tfile), + httr::content_type(".png") + ) + httr::stop_for_status(out) + ctype <- out$headers$`content-type` + ctype <- strsplit(ctype, " ")[[1]] + ctype <- sub(";$", "", ctype) + if (any(ctype == "text/html") && + !any(grepl("png", ctype))) { + stop("Output is not a PNG!") + } + tfile + }, FUN.VALUE = character(1)) + return(res) +} + + +add_footer <- function(rmd_path, footer_text = NULL) { + if (is.null(footer_text)) { + stop("Need character string in footer_text argument to append to end of file.") + } + footer_text <- paste0("\n", footer_text, collapse = "\n") + write(as.character(footer_text), + file = rmd_path, + append = TRUE + ) +} diff --git a/R/gs_png.R b/R/gs_png.R deleted file mode 100644 index da09bba0..00000000 --- a/R/gs_png.R +++ /dev/null @@ -1,102 +0,0 @@ -#' Get Slide ID from URL -#' -#' @param x URL of slide -#' -#' @return A character vector -#' @export -#' -#' @examples -#' x <- paste0( -#' "https://docs.google.com/presentation/d/", -#' "1Tg-GTGnUPduOtZKYuMoelqUNZnUp3vvg_7TtpUPL7e8", -#' "/edit#slide=id.g154aa4fae2_0_58" -#' ) -#' get_slide_id(x) -get_slide_id <- function(x) { - x <- sub(".*presentation/", "", x) - x <- sub("/d/e", "/d", x) # if you publish by accident - x <- sub("^(d|e)/", "", x) - x <- strsplit(x, "/")[[1]] - x <- x[!grepl("^(edit|pub|export|png)", x)] - x <- x[nchar(x) > 5] - x -} - -#' Get Google Slide PNG URL -#' -#' @param url URL to Google Slide -#' -#' @return A character vector of URLs -#' @export -#' -#' @examples -#' url <- paste0( -#' "https://docs.google.com/presentation/d/", -#' "12DPZgPteQBwgal6kSPP58zhPhjZ7QSPZLe3NkA8M3eo/edit", -#' "#slide=id.gc8648f14c3_0_397&t=4" -#' ) -#' id <- get_slide_id(url) -#' gs_png_url(url) -gs_png_url <- function(url) { - id <- get_slide_id(url) - slide_id <- get_slide_page(url) - gs_png_id(id, slide_id) -} - -gs_png_id <- function(id, slide_id) { - if (any(grepl("^id[.]", slide_id))) { - warning( - "slide ids usually don't have format of id.gc*, ", - "you should likely remove the id." - ) - } - paste0( - "https://docs.google.com/presentation/d/", - id, - "/export/png?id=", id, - "&pageid=", slide_id - ) -} - -#' @export -#' @rdname gs_png_url -get_slide_page <- function(url) { - parsed <- httr::parse_url(url) - slide_id <- parsed$query$pageid - if (length(slide_id) == 0 || nchar(slide_id) == 0) { - fragment <- parsed$fragment - slide_id <- sub("slide=(.*)", "\\1", fragment) - slide_id <- sub("&.*", "", slide_id) - slide_id <- sub("^id[.]", "", slide_id) - } - stopifnot(length(slide_id) > 0 && nchar(slide_id) > 0) - slide_id -} - -#' @export -#' @rdname gs_png_url -#' @param output_dir path to output png -#' @param overwrite should the slide PNG be overwritten? -gs_png_download <- function(url, output_dir = ".", overwrite = TRUE) { - id <- get_slide_id(url) - slide_id <- get_slide_page(url) - url <- gs_png_url(url) - dir.create(output_dir, recursive = TRUE, showWarnings = FALSE) - outfile <- file.path(output_dir, paste0(id, "_", slide_id, ".png")) - if (!file.exists(outfile) || overwrite) { - curl::curl_download(url, destfile = outfile, quiet = FALSE) - } - stopifnot(file.exists(outfile)) - outfile -} - -#' @export -#' @rdname gs_png_url -#' @param ... for \code{include_slide}, options passed to -#' [knitr::include_graphics()] -include_slide <- function(url, - output_dir = knitr::opts_chunk$get("fig.path"), - overwrite = TRUE, ...) { - outfile <- gs_png_download(url, output_dir, overwrite = overwrite) - knitr::include_graphics(outfile, ...) -} diff --git a/R/iframe_leanpub.R b/R/leanpub.R similarity index 53% rename from R/iframe_leanpub.R rename to R/leanpub.R index 8f61fe4b..a486f9ab 100644 --- a/R/iframe_leanpub.R +++ b/R/leanpub.R @@ -9,6 +9,7 @@ #' @param html_page The file path of the rendered index.html file #' @param base_url The base url of where the chapters are published -- the url to provide to the iframe in Leanpub #' e.g. https://jhudatascience.org/OTTR_Template/coursera +#' @param html_page The file path of the rendered index.html file #' @param default_img A google slide link to the default image to be used for all chapters #' @param output_dir output directory to put files. It should likely be #' relative to path @@ -37,34 +38,62 @@ #' ) #' } website_to_embed_leanpub <- function(path = ".", - chapt_img_key = NULL, - render = NULL, - html_page = file.path(base_url, "index.html"), - base_url = NULL, - default_img = NULL, - output_dir = "manuscript", - make_book_txt = FALSE, - quiz_dir = "quizzes", - run_quiz_checks = FALSE, - remove_resources_start = FALSE, - verbose = TRUE, - footer_text = "") { - # Run the set up - set_up_leanpub( - path = path, - render = render, - output_dir = output_dir, - make_book_txt = make_book_txt, - quiz_dir = quiz_dir, - run_quiz_checks = run_quiz_checks, - remove_resources_start = remove_resources_start, - verbose = verbose - ) + chapt_img_key = NULL, + render = NULL, + html_page = file.path(base_url, "index.html"), + base_url = NULL, + clean_up = TRUE, + default_img = NULL, + output_dir = "manuscript", + make_book_txt = FALSE, + quiz_dir = "quizzes", + run_quiz_checks = FALSE, + remove_resources_start = FALSE, + verbose = TRUE, + footer_text = "") { + + # Find the OTTR course + root_dir <- course_path(path = path) + + rooted_output_dir <- file.path(root_dir, output_dir) + rooted_quiz_dir <- file.path(root_dir, quiz_dir) + + if (clean_up) { + + if (dir.exists(rooted_output_dir)) { + message(paste("Clearing out old version of output files:", rooted_output_dir)) + + unlink(rooted_output_dir, recursive = TRUE) + } + } + + # If output directory doesn't exist, make it + if (!dir.exists(rooted_output_dir)) { + dir.create(rooted_output_dir, recursive = TRUE, showWarnings = FALSE) + } + + if (!is.null(quiz_dir)) { + #### Run quiz checks + if (run_quiz_checks) { + message("Checking quizzes") + quiz_checks <- check_quizzes( + path = root_dir, + quiz_dir = quiz_dir, + verbose = verbose + ) + } + if (verbose) message("Copying quiz files") + copy_quizzes( + path = root_dir, + quiz_dir = quiz_dir, + output_dir = output_dir + ) + } # If TSV chapter image key file is specified read it in if (!is.null(chapt_img_key)) { - message(paste("Reading in a chapt_img_key TSV file:", chapt_img_key)) - chapt_df <- readr::read_tsv(chapt_img_key) + message(paste("Reading in a chapt_img_key TSV file:", file.path(root_dir, chapt_img_key))) + chapt_df <- readr::read_tsv(file.path(root_dir, chapt_img_key)) } else { # If its not supplied, create it from the get_chapters function message("Creating a chapt_img_key TSV file") @@ -73,6 +102,7 @@ website_to_embed_leanpub <- function(path = ".", stop("No base_url is supplied and no chapt_img_key file was supplied. Need one or the other.") } chapt_df <- get_chapters( + path = root_dir, html_page = paste0(base_url, "index.html"), base_url = file.path(base_url, "no_toc/") ) %>% @@ -86,7 +116,7 @@ website_to_embed_leanpub <- function(path = ".", default_img <- "https://docs.google.com/presentation/d/1jEUxUY1qXDZ3DUtvTU6NCc6ASG5nx4Gwczv5aAglYX4/edit#slide=id.p" } # Set up location to store default image - img_dir <- file.path(output_dir, "embed_chapt_images") + img_dir <- file.path(rooted_output_dir, "embed_chapt_images") if (!dir.exists(img_dir)) { dir.create(img_dir, recursive = TRUE) @@ -100,7 +130,10 @@ website_to_embed_leanpub <- function(path = ".", # Make the data.frame be in the same order dplyr::select(dplyr::any_of("url"), dplyr::any_of("chapt_title"), dplyr::any_of("img_path")) %>% # Run it make_embed_markdown on each row - purrr::pmap(~ make_embed_markdown(url = ..1, chapt_title = ..2, img_path = ..3, footer_text = footer_text)) + purrr::pmap(~ make_embed_markdown( + url = ..1, chapt_title = ..2, img_path = ..3, footer_text = footer_text, + output_dir = output_dir, path = root_dir + )) ####################### Book.txt creation #################################### out <- NULL @@ -109,6 +142,7 @@ website_to_embed_leanpub <- function(path = ".", md_files <- basename(unlist(md_output_files)) course_to_book_txt( + path = root_dir, md_files = md_files, output_dir = output_dir, quiz_dir = quiz_dir, @@ -117,7 +151,7 @@ website_to_embed_leanpub <- function(path = ".", out <- book_txt_file <- file.path(output_dir, "Book.txt") } else { # If false, look for Book.txt file to copy to output folder. - book_txt_file <- file.path(path, "Book.txt") + book_txt_file <- file.path(root_dir, "Book.txt") if (file.exists(book_txt_file)) { # Copy over an existing book.txt file if it exists @@ -142,118 +176,52 @@ website_to_embed_leanpub <- function(path = ".", )) } -#' Create Book.txt file from files existing in quiz directory -#' -#' @param path path to the bookdown or quarto course repository, must have a `_bookdown.yml` or `_quarto.yml` file -#' @param md_files vector of file path of the md's to be included -#' @param output_dir output directory to put files. It should likely be -#' relative to path -#' @param quiz_dir Where are the quizzes stored? Default looks for folder called "quizzes". -#' @param verbose print diagnostic messages -#' -#' @return A list of quiz and chapter files in order in a file called Book.txt -- How Leanpub wants it. -#' @export -#' -course_to_book_txt <- function(path = ".", - md_files = NULL, - output_dir = "manuscript", - quiz_dir = "quizzes", - verbose = TRUE) { - # If md_files are not specified, then try to get them - if (is.null(md_files)) { - # Establish path - path <- course_path(path) - - rmd_regex <- "[.][R|r]md$" - - # Extract the names of the Rmd files (the chapters) - md_files <- qrmd_files(path = path) - } - - if (!is.null(quiz_dir)) { - # Find the quiz files in the quiz directory - quiz_files <- list.files(pattern = "\\.md$", quiz_dir) - - # Put files in one vector - all_files <- c(md_files, quiz_files) - - # Make a vector specifying the file type: quiz or not - file_type <- c( - rep("non-quiz", length(md_files)), - rep("quiz", length(quiz_files)) - ) - } else { - all_files <- md_files - file_type <- rep("non-quiz", length(md_files)) - } - # Put all files in one data.frame - all_files <- data.frame( - file_name = all_files, - file_type - ) %>% - dplyr::mutate( - # Use this so we don't have to fiddle with case senstivity for the next step - lower_filename = tolower(file_name), - # Get the number from the file name and that will be the order - num = stringr::str_extract(file_name, "([0-9]+)"), - num = dplyr::case_when( - # Put index file first and about file last - lower_filename == "index.rmd" ~ "0", - lower_filename == "about.rmd" ~ as.character(length(all_files)), - TRUE ~ num - ), - num = as.numeric(num), - file_name = gsub(".Rmd$", ".md", file_name) - ) %>% - # Put quizzes in order! - dplyr::arrange(num, file_type) %>% - dplyr::pull(file_name) - - # Declare output file name - book_txt <- file.path(output_dir, "Book.txt") - - if (verbose) { - message(paste0("Autogenerated Book.txt saved to: ", book_txt)) - } - # need to fix about quiz - writeLines(all_files, con = book_txt) -} - #' Make Leanpub file that has embed webpage of a chapter #' +#' @param path path to the bookdown or quarto course repository, must have a `_bookdown.yml` or `_quarto.yml` file #' @param url The url to the chapter that is to be embed #' @param chapt_title Title of chapter to be used as file name and printed on iframe -#' @param width_spec How wide should the iframe be in pixels? -#' @param height_spec How high should the iframe be in pixels? #' @param img_path File path to image to use for poster #' @param output_dir output directory to put files. It should likely be #' relative to path -#' @param verbose print diagnostic messages #' @param footer_text Optionally can add a bit of text that will be added to the #' end of each file before the references section. +#' @param width_spec How wide should the iframe be in pixels? +#' @param height_spec How high should the iframe be in pixels? +#' @param verbose print diagnostic messages #' @return A markdown file with an iframe of the provided chapter #' #' @export -make_embed_markdown <- function(url, +make_embed_markdown <- function(path = ".", + url, chapt_title, - width_spec = 800, - height_spec = 600, img_path, output_dir = "manuscript", - verbose = TRUE, - footer_text = "") { + footer_text = "", + width_spec = 800, + height_spec = 600, + verbose = TRUE) { # Arguments: # url: The url to the chapter # chapt_title: The title of the chapter to be used as a header # img: file path to the image to be used in the preview # # Returns: A markdown document ready for Leanpub and the image copied to the manuscript folder + # Find the OTTR course + root_dir <- course_path(path = path) chapt_file_name <- gsub(" ", "-", chapt_title) + # Leanpub hates question marks and exclamation marks in titles + chapt_file_name <- gsub("\\!|\\?", "", chapt_file_name) + # Declare output file - output_file <- file.path(output_dir, paste0(chapt_file_name, ".md")) + output_file <- file.path(root_dir, output_dir, paste0(chapt_file_name, ".md")) + + if (!dir.exists(output_dir)) { + dir.create(output_dir, recursive = TRUE) + } file_contents <- c( paste("#", chapt_title), @@ -272,13 +240,13 @@ make_embed_markdown <- function(url, write(file_contents, file = output_file) - manuscript_dir <- file.path(output_dir, dirname(img_path)) + new_img_path <- file.path(root_dir, output_dir, dirname(img_path)) - if (!dir.exists(manuscript_dir)) { - dir.create(manuscript_dir, recursive = TRUE) + if (!dir.exists(new_img_path)) { + dir.create(new_img_path, recursive = TRUE, showWarnings = TRUE) } - file.copy(from = img_path, to = file.path(output_dir, img_path), overwrite = TRUE) + file.copy(from = file.path(root_dir, img_path), to = file.path(root_dir, output_dir, img_path), overwrite = TRUE) if (verbose) { message(paste0("Output saved to: ", output_file)) @@ -289,6 +257,7 @@ make_embed_markdown <- function(url, #' Make Leanpub file that has embed webpage of a chapter #' +#' @param path path to the bookdown or quarto course repository, must have a `_bookdown.yml` or `_quarto.yml` file #' @param html_page The file path of the rendered index.html file. It can be a url #' @param base_url The base url of where the chapters are published -- the url to provide to the iframe in Leanpub #' e.g. https://jhudatascience.org/OTTR_Template/coursera @@ -298,9 +267,13 @@ make_embed_markdown <- function(url, #' #' @export #' - -get_chapters <- function(html_page = file.path("docs", "index.html"), +get_chapters <- function(path = ".", + html_page = file.path("docs", "index.html"), base_url = ".") { + + # Put this relative to project path + html_page <- file.path(root_dir, html_page) + # Read in html index_html <- suppressWarnings(try(xml2::read_html(html_page))) @@ -313,24 +286,23 @@ get_chapters <- function(html_page = file.path("docs", "index.html"), nodes <- rvest::html_nodes(index_html, xpath = paste0("//", 'div[@class="sidebar-item-container"]')) # We only want chapters - nodes <- nodes[grep("chapter",as.character(nodes))] + nodes <- nodes[grep("chapter", as.character(nodes))] # Extract chapter nodes from the sidebar chapt_title <- nodes %>% - rvest::html_nodes('span.chapter-title') %>% + rvest::html_nodes("span.chapter-title") %>% rvest::html_text() data_level <- nodes %>% - rvest::html_nodes('span.chapter-number') %>% + rvest::html_nodes("span.chapter-number") %>% rvest::html_text() data_path <- nodes %>% - rvest::html_nodes('a.sidebar-item-text.sidebar-link') %>% - rvest::html_attr('href') %>% + rvest::html_nodes("a.sidebar-item-text.sidebar-link") %>% + rvest::html_attr("href") %>% stringr::str_remove("^\\.\\/") chapt_data <- data.frame(chapt_title, data_level, url = paste0(base_url, "/", data_path)) - } else { chapt_data <- rvest::html_attrs(nodes) %>% dplyr::bind_rows() %>% @@ -350,3 +322,107 @@ get_chapters <- function(html_page = file.path("docs", "index.html"), return(chapt_data) } + +#' A function to make screenshots from an OTTR bookdown website +#' @description This function creates screenshots of course chapters that are stored in a created output directory +#' +#' @param path path to the bookdown or quarto course repository, must have a `_bookdown.yml` or `_quarto.yml` file +#' @param git_pat required argument; a Git secret -- see https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens for more info +#' @param repo required argument; GitHub repository name, e.g., jhudsl/OTTR_Template +#' @param output_dir default is "resources/chapt_screen_images"; Output directory where the chapter's screen images should be stored. For OTTR courses, don't change this unless you've changed the downstream functions accordingly. +#' @param base_url default is NULL; rendered bookdown URL where screenshots are taken from, if NULL, the function will use the repo_name and and git_pat to find the base_url +#' @param path default is to look for OTTR files in current directory based on existence of .github. But if you'd like to run this in a different path, you can point to that file path. +#' @return the file path for file where chapter urls are saved +#' +#' @import dplyr +#' @importFrom webshot2 webshot +#' @importFrom magrittr %>% +#' @importFrom rprojroot find_root has_dir +#' +#' @author Candace Savonen +#' +#' @export +#' +#' @examples \dontrun{ +#' +#' make_screenshots( +#' git_pat = Sys.getenv("secrets.GH_PAT"), +#' repo = "jhudsl/OTTR_Template" +#' ) +#' } +make_screenshots <- function(path = ".", + git_pat, + repo, + output_dir = file.path(path, "resources", "chapt_screen_images"), + base_url = NULL) { + # Find .github root directory + root_dir <- course_path(path = path) + + output_folder <- file.path(output_dir) + if (!dir.exists(output_folder)) { + dir.create(output_folder, recursive = TRUE) + } + + if (is.null(base_url)) { + base_url <- ottrpal::get_pages_url(repo_name = repo, git_pat = git_pat) # what if these arguments are still NULL/not supplied? + base_url <- gsub("/$", "", base_url) + } + + # Collect all the chapter pages for the url given + chapt_df <- ottrpal::get_chapters( + path = root_dir, + html_page = file.path("docs", "index.html"), + base_url = base_url + ) + + # Now take screenshots for each + file_names <- lapply(chapt_df$url, function(url) { + file_name <- gsub( + ".html", + ".png", + file.path(output_folder, basename(url)) + ) + + # Get rid of special characters because leanpub no like + file_name <- gsub( + ":|?|!|\\'", + "", + file_name + ) + + # Take the screenshot + webshot(url, file = file_name) + + return(file_name) + }) + + # Save file of chapter urls and file_names + chapt_df <- chapt_df %>% + dplyr::mutate(img_path = unlist(file_names)) + + chapt_df %>% + readr::write_tsv(file.path(output_folder, "chapter_urls.tsv")) + + message(paste("Image Chapter key written to: ", file.path(output_folder, "chapter_urls.tsv"))) + + return(file.path(output_folder, "chapter_urls.tsv")) +} + +copy_quizzes <- function(path = ".", quiz_dir = "quizzes", output_dir = "manuscript") { + quiz_dir <- file.path(quiz_dir) + + if (!is.null(quiz_dir)) { + if (!dir.exists(quiz_dir)) { + warning(paste( + "The quiz directory specified by quiz_dir:", quiz_dir, "does not exist.", + "If you don't have quizzes, set quiz_dir = NULL" + )) + } + quizzes <- list.files(path = file.path(quiz_dir), full.names = TRUE, pattern = "\\.md$") + if (length(quizzes) > 0) { + fs::file_copy(quizzes, file.path(output_dir, basename(quizzes)), + overwrite = TRUE + ) + } + } +} diff --git a/R/quiz.R b/R/quiz_formatting.R similarity index 75% rename from R/quiz.R rename to R/quiz_formatting.R index 11f659ad..7a1956db 100644 --- a/R/quiz.R +++ b/R/quiz_formatting.R @@ -2,6 +2,201 @@ utils::globalVariables(c("question", "original", "n", "metadata_check", "index", "ignore_coursera")) + +find_end_of_prompt <- function(start_prompt_index, type_vector) { + # We want to see if the next line is where the answers start + end_prompt_index <- start_prompt_index + 1 + + # See if the end of the prompt is in the same line + end_prompt <- grepl("answer", type_vector[end_prompt_index]) + + # Keep looking in each next line until we find it. + if (end_prompt == FALSE) { + while (end_prompt == FALSE) { + # Add one + end_prompt_index <- end_prompt_index + 1 + + # Look in next line + end_prompt <- grepl("answer", type_vector[end_prompt_index]) + + if (end_prompt_index == length(type_vector) && end_prompt == FALSE) { + stop(paste("Searched end of file and could not find end of prompt that starts at line:", start_prompt_index)) + } + } + } else { + end_prompt_index <- start_prompt_index + } + return(end_prompt_index) +} + +#' Convert Leanpub md quiz to Coursera yaml quiz +#' +#' Convert a Leanpub-formatted md quiz file to a Coursera-formatted yaml quiz file in preparation for uploading to Coursera. +#' +#' @param quiz_path A path to a quiz .md file to be converted. +#' @param output_quiz_dir An existing folder where you would like the new version of the quiz to be saved. +#' Default is the directory of the quiz_path provided +#' @param verbose Would you like the progress messages? +#' +#' @return A Coursera-ready quiz file saved to the output directory specified as a yaml. +#' @export convert_quiz +#' +#' @examples \dontrun{ +#' +#' quiz_path <- good_quiz_path() +#' +#' # Provide path to quiz to convert +#' convert_quiz(quiz_path) +#' } +convert_quiz <- function(quiz_path, + output_quiz_dir = dirname(quiz_path), + verbose = TRUE) { + # Print out which quiz we're converting + message(paste("Converting quiz:", quiz_path)) + + output_filename <- file.path(output_quiz_dir, paste0(basename(quiz_path), ".yml")) + + ### First read lines for each quiz + # Put it as a data.frame: + quiz_lines_df <- parse_quiz_df(readLines(quiz_path), remove_tags = TRUE) + + # Add in proper Coursera yaml mappings based on the parsing + quiz_lines_df <- quiz_lines_df %>% + # Now for updating based on type! + dplyr::mutate(updated_line = dplyr::case_when( + type %in% c("prompt", "single_line_prompt") ~ stringr::str_replace(original, "^\\?", " prompt:"), + type %in% c("extended_prompt", "end_prompt") ~ paste0(" ", original), + grepl("answer", type) ~ stringr::str_replace(original, "^[[:alpha:]]\\)", " - answer:"), + TRUE ~ original + )) + + # Declare the correct answers by which ones are the first ones listed + correct_answers <- quiz_lines_df %>% + dplyr::filter(type == "correct_answer") %>% + dplyr::distinct(question, .keep_all = TRUE) + + # Identify other correct answers that we don't want to keep + remove_answers <- setdiff( + dplyr::filter(quiz_lines_df, type == "correct_answer")$index, + correct_answers$index + ) + + # Remove any correct answers that aren't those + quiz_lines_df <- quiz_lines_df %>% + dplyr::filter(!(index %in% remove_answers)) + + # Turn updated lines into a named vector + updated_quiz_lines <- quiz_lines_df$updated_line + names(updated_quiz_lines) <- quiz_lines_df$type + + ### Add specs for coursera + # Add typeName before prompt starts: + updated_quiz_lines <- R.utils::insert(updated_quiz_lines, + ats = which(names(updated_quiz_lines) %in% c("prompt", "single_line_prompt")), + values = "- typeName: multipleChoice" + ) + + ### Add " options:" before beginning of answer options + updated_quiz_lines <- R.utils::insert(updated_quiz_lines, + ats = which(names(updated_quiz_lines) %in% c("end_prompt", "single_line_prompt")) + 1, + values = " options:" + ) + + # Add shuffleoptions: true after prompt ends + updated_quiz_lines <- R.utils::insert(updated_quiz_lines, + ats = which(names(updated_quiz_lines) %in% c("end_prompt", "single_line_prompt")) + 1, + values = " shuffleOptions: true" + ) + + # Need to add "isCorrect: true" one line below correct value lines + updated_quiz_lines <- R.utils::insert(updated_quiz_lines, + ats = which(names(updated_quiz_lines) == "correct_answer") + 1, + values = " isCorrect: true" + ) + + # Need to add "isCorrect: false" one line below incorrect value lines + updated_quiz_lines <- R.utils::insert(updated_quiz_lines, + ats = which(names(updated_quiz_lines) == "wrong_answer") + 1, + values = " isCorrect: false" + ) + + # Remove other lines + updated_quiz_lines <- updated_quiz_lines[-grep("other", names(updated_quiz_lines))] + + # Add extra line in between each question + updated_quiz_lines <- R.utils::insert(updated_quiz_lines, + ats = grep("typeName:", updated_quiz_lines), + values = "" + ) + + # Trim trailing space + updated_quiz_lines <- trimws(updated_quiz_lines, which = "right") + + # Add extra line in between each question + updated_quiz_lines <- stringr::str_remove(updated_quiz_lines, ":$") + + # Return the options : though + updated_quiz_lines <- stringr::str_replace(updated_quiz_lines, " options", " options:") + + ### Write new file with .yml at end of file name and put in coursera dir + writeLines(updated_quiz_lines, con = output_filename) + + # Put message + message(paste("Converted quiz saved to:", output_filename)) +} + +#' Convert Leanpub md quiz to Coursera yaml quiz +#' +#' @param input_quiz_dir A path to a directory of leanpub formatted quiz md files. By default assumes "quizzes" and looks in current directory. +#' @param output_quiz_dir A folder (existing or not) that the new coursera converted quizzes should be saved to. By default saves to "coursera_quizzes". +#' @param verbose Would you like the progress messages: TRUE/FALSE? +#' +#' @return A folder of coursera ready quiz files saved to the output directory specified as a yamls. +#' @export +#' +#' @examples +#' +#' # Set up a directory with a quiz in it for this example +#' tdir <- tempfile() +#' dir.create(tdir, showWarnings = FALSE, recursive = TRUE) +#' +#' file.copy( +#' from = good_quiz_path(), +#' to = file.path(tdir, basename(good_quiz_path())) +#' ) +#' +#' # Provide path to directory of quizzes +#' convert_coursera_quizzes(tdir) +#' +#' system("rm -r coursera_quizzes") +convert_coursera_quizzes <- function(input_quiz_dir = "quizzes", + output_quiz_dir = "coursera_quizzes", + verbose = TRUE) { + # Create directory if it is not yet created + if (!dir.exists(output_quiz_dir)) { + dir.create(output_quiz_dir, recursive = TRUE) + } + + # List quiz paths + leanpub_quizzes <- list.files( + pattern = (".md"), + ignore.case = TRUE, + path = input_quiz_dir, + full.names = TRUE + ) + + if (length(leanpub_quizzes) < 1) { + stop(paste0("No quiz .md files found in your specified path dir of: ", quiz_path)) + } + + # Run the thing! + lapply(leanpub_quizzes, + convert_quiz, + verbose = verbose, + output_quiz_dir = output_quiz_dir + ) +} + #' Parse quiz into a data.frame #' #' @param quiz_lines A character vector of the contents of the markdown @@ -624,6 +819,7 @@ check_question <- function(question_df, quiz_name = NA, verbose = TRUE, ignore_c #' #' Check the formatting of all quizzes in a given directory. #' +#' @param path path to the top of course repository (looks for .github folder) #' @param quiz_dir A path to a directory full of quizzes that should all be checked with [ottrpal::check_all_quizzes]. #' @param verbose print diagnostic messages #' @param write_report TRUE/FALSE save warning report to a CSV file? @@ -643,10 +839,12 @@ check_question <- function(question_df, quiz_name = NA, verbose = TRUE, ignore_c #' ## Now check the quizzes in that directory #' all_quiz_results <- check_quizzes(quiz_dir = quiz_dir) #' } -check_quizzes <- function(quiz_dir = "quizzes", +check_quizzes <- function(path = ".", + quiz_dir = "quizzes", write_report = TRUE, verbose = TRUE, ignore_coursera = TRUE) { + files <- list.files( pattern = "\\.md", ignore.case = TRUE, @@ -676,9 +874,9 @@ check_quizzes <- function(quiz_dir = "quizzes", if (write_report) { if (nrow(question_report) > 0) { - message("\n Question error report saved to 'question_error_report.tsv'") + message("\n Question error report saved to:", file.path(quiz_dir, "question_error_report.tsv")) readr::write_tsv(question_report, - file = "question_error_report.tsv" + file = file.path(quiz_dir, "question_error_report.tsv") ) } else { message("\n No question errors to report!") diff --git a/R/render_without_toc.R b/R/render_without_toc.R new file mode 100644 index 00000000..c5ddddbc --- /dev/null +++ b/R/render_without_toc.R @@ -0,0 +1,162 @@ +# C. Savonen 2021 + +#' Create TOC-less course website for use in Coursera or Leanpub +#' +#' Create a version of the course that does not have a TOC and has quizzes in the Coursera yaml format. +#' This is only needed to be used on Bookdown courses. Quarto has a simple command for this. +#' +#' @param output_dir A folder (existing or not) that the TOC-less Bookdown for Coursera files should be saved. By default is file.path("docs", "coursera") +#' @param output_yaml A output.yml file to be provided to bookdown. By default is "_output.yml" +#' @param convert_quizzes TRUE/FALSE whether or not to convert quizzes. Default is TRUE +#' @param input_quiz_dir A path to a directory of Leanpub-formatted quiz md files. By default assumes "quizzes" and looks in current directory. +#' @param output_quiz_dir A folder (existing or not) where the coursera quizzes should be saved. By default is "coursera_quizzes". +#' @param verbose Would you like the progress messages? TRUE/FALSE +#' +#' @return A folder of coursera ready quiz files and html chapter files saved to output directories specified. +#' @export +#' @rdname coursera +#' +#' @importFrom utils download.file +#' +render_without_toc <- function(course_path = ".", + output_dir = file.path("docs", "no_toc"), + output_yaml = "_output.yml", + convert_quizzes = FALSE, + input_quiz_dir = "quizzes", + output_quiz_dir = "coursera_quizzes", + verbose = TRUE) { + # Find root directory by finding `.git` folder + root_dir <- course_path() + + # Output files: + output_dir <- file.path(root_dir, output_dir) + + ###### Check we have the files we need ###### + # Create output folder if it does not exist + if (!dir.exists(output_dir)) { + message(paste0("Creating output folder: ", output_dir)) + dir.create(output_dir, recursive = TRUE, showWarnings = FALSE) + } + + ###### Declare all the file paths relative to root directory ###### + # Input files: + toc_close_css <- file.path(root_dir, "assets", "toc_close.css") + + if (!file.exists(toc_close_css)) { + download.file("https://raw.githubusercontent.com/jhudsl/ottrpal/master/inst/extdata/toc_close.css", + destfile = toc_close_css + ) + } + output_yaml_file <- file.path(root_dir, output_yaml) + + # Make sure we have that file + if (!file.exists(toc_close_css)) { + stop(paste0("Could not find: ", toc_close_css)) + } + # Make sure we know where the output yaml is + if (!file.exists(output_yaml_file)) { + stop(paste0("Could not find: ", output_yaml_file)) + } + + # Clean out old files if they exist + old_files <- list.files(output_dir, pattern = c("html$", "md$"), full.names = TRUE) + if (length(old_files) > 0) { + file.remove(old_files) + } + + ###### Copy over needed directories ###### + # Copy these directories over if they don't exist in the output folder + needed_directories <- c("assets", "resources") + + if (verbose) { + message(paste0(c("Needed directories being copied:"), collapse = "\n")) + } + + # Do the copying + lapply(needed_directories, function(needed_dir) { + if (verbose) { + message(needed_dir) + } + if (!dir.exists(needed_dir)) { + stop(paste0("Needed directory:", needed_dir, "does not exist in the current path.")) + } + if (!dir.exists(file.path(output_dir, needed_dir))) { + fs::dir_copy(needed_dir, file.path(output_dir, needed_dir), overwrite = TRUE) + } + }) + + # Slightly different path for the libs folder + libs_path <- file.path("docs", "libs") + if (!dir.exists(file.path(output_dir, "libs"))) { + if (verbose) { + message(file.path("docs", "libs")) + } + fs::dir_copy(libs_path, file.path(output_dir, "libs"), overwrite = TRUE) + } + + ###### Copy over CSS file ###### + # Retrieve yaml file specs + output_yaml_lines <- yaml::yaml.load_file(output_yaml_file) + + # Copy over css file(s) that's specified + org_css_file <- output_yaml_lines$`bookdown::gitbook`$css + + # Check if there are multiple .css + if (length(org_css_file) > 1) { + # Read all .css + css_files_read <- sapply(org_css_file, readLines) + + # Make a "mega .css" and write + if (verbose) { + message("Combining .css files") + } + css_lines_cat <- rbind(unlist(css_files_read)) + css_file <- file.path(output_dir, org_css_file[1]) + writeLines(css_lines_cat, css_file) + } else { + css_file <- file.path(output_dir, org_css_file) + + # Write it as "style.css" + fs::file_copy(org_css_file, + css_file, + overwrite = TRUE + ) + } + + ###### Now do the rendering! ###### + message("Render bookdown without TOC") + + # Do the render + bookdown::render_book( + input = "index.Rmd", + output_yaml = output_yaml_file, + output_dir = output_dir + ) + + # Read in TOC closing CSS lines + toc_close_css_lines <- readLines(toc_close_css) + + # Using suppressWarnings() because "incomplete final line" + full_css <- suppressWarnings( + readLines(css_file) + ) + + # Write to "style.css" + writeLines(append(full_css, toc_close_css_lines), css_file) + + # Only convert the quizzes if set to TRUE + if (convert_quizzes) { + if (!dir.exists(input_quiz_dir)) { + stop( + "convert_quizzes = TRUE but the specified input_quiz_dir: ", + input_quiz_dir, + " cannot be found." + ) + } + convert_coursera_quizzes( + input_quiz_dir = input_quiz_dir, + output_quiz_dir = output_quiz_dir, + verbose = verbose + ) + } +} diff --git a/R/screenshot.R b/R/screenshot.R deleted file mode 100644 index 2cc02c3e..00000000 --- a/R/screenshot.R +++ /dev/null @@ -1,79 +0,0 @@ -#' A function to make screenshots from an OTTR bookdown website -#' @description This function creates screenshots of course chapters that are stored in a created output directory -#' -#' @param git_pat required argument; a Git secret -- see https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens for more info -#' @param repo required argument; GitHub repository name, e.g., jhudsl/OTTR_Template -#' @param output_dir default is "resources/chapt_screen_images"; Output directory where the chapter's screen images should be stored. For OTTR courses, don't change this unless you've changed the downstream functions accordingly. -#' @param base_url default is NULL; rendered bookdown URL where screenshots are taken from, if NULL, the function will use the repo_name and and git_pat to find the base_url -#' -#' @return the file path for file where chapter urls are saved -#' -#' @import dplyr -#' @importFrom webshot2 webshot -#' @importFrom magrittr %>% -#' @importFrom rprojroot find_root has_dir -#' @importFrom janitor make_clean_names -#' -#' @author Candace Savonen -#' -#' @export -#' -#' @examples \dontrun{ -#' -#' make_screenshots(Sys.getenv("secrets.GH_PAT"), "jhudsl/OTTR_Template") -#' -#' } -make_screenshots <- function(git_pat, repo, output_dir = "resources/chapt_screen_images", base_url = NULL){ - - # Find .git root directory - root_dir <- find_root(has_dir(".git")) - - output_folder <- file.path(output_dir) - if (!dir.exists(output_folder)) { - dir.create(output_folder, recursive = TRUE) - } - - if (is.null(base_url)){ - base_url <- ottrpal::get_pages_url(repo_name = repo, git_pat = git_pat) #what if these arguments are still NULL/not supplied? - base_url <- gsub("/$", "", base_url) - } - - # Collect all the chapter pages for the url given - chapt_df <- ottrpal::get_chapters(html_page = file.path(root_dir, "docs", "index.html"), - base_url = base_url) - - # Get file names and make unique - file_names <- lapply(chapt_df$url, function(url){ - file_name <- gsub(".html", - ".png", - file.path(output_folder, basename(url)) - ) - - # Get rid of special characters because leanpub no like - file_name <- gsub(":|?|!|\\'", - "", - file_name - ) - return(gsub(".png", "", file_name)) #remove .png so clean_names is adding any numbers before file extension - }) %>% - make_clean_names() %>% #handle repeat chapter names - paste0(".png") #add back .png - - #add cleaned file names as a column in the dataframe with URLs - chapt_df <- chapt_df %>% - dplyr::mutate(img_path = unlist(file_names)) - - # Now take screenshots for each, referencing the dataframe for the URL and desired filename - lapply(1:nrow(chapt_df), - function(x) webshot(chapt_df$url[x], - file = chapt_df$img_path[x])) - - # Save file of chapter urls and file_names - chapt_df %>% - readr::write_tsv(file.path(output_folder, "chapter_urls.tsv")) - - message(paste("Image Chapter key written to: ", file.path(output_folder, "chapter_urls.tsv"))) - - return(file.path(output_folder, "chapter_urls.tsv")) - -} \ No newline at end of file diff --git a/R/set_knitr_image_path.R b/R/set_knitr_image_path.R deleted file mode 100644 index b5a4769a..00000000 --- a/R/set_knitr_image_path.R +++ /dev/null @@ -1,15 +0,0 @@ -#' Set image path for `knitr` -#' -#' @param verbose print out what the figure path is -#' -#' @return When used inside a knitted R Markdown document, will set the image path to a place compatible with 'ottrpal' output folders. -#' @export -set_knitr_image_path <- function(verbose = FALSE) { - fp <- knitr::fig_path() - fp <- dirname(fp) - fp <- paste0("resources/images/", fp, "/") - if (verbose) { - message(paste0("figpath is ", fp)) - } - knitr::opts_chunk$set(fig.path = fp) -} diff --git a/R/set_up.R b/R/set_up.R deleted file mode 100644 index bbadbe2e..00000000 --- a/R/set_up.R +++ /dev/null @@ -1,291 +0,0 @@ -#' Load in yaml specifications from _bookdown.yml or _quarto.yml -#' -#' @param path Where to look for the yaml spec file. By default looks in current directory. -#' -#' @return The yaml contents using yaml::yaml.load_file() -#' @export - -get_yaml_spec <- function(path = ".") { - - root_dir <- course_path(path = file.path(path)) - - file_path <- list.files(pattern = "_bookdown.yml|_quarto.yml", full.names = TRUE) - - # Read in yaml - suppressWarnings({ - yaml_contents <- yaml::yaml.load_file(file_path) - }) - - return(yaml_contents) -} - -#' Find main course git directory -#' -#' @param path Where to look for the file. By default looks in current directory. -#' -#' @return Returns the directory where the .git folder is contained. -#' @export -course_path <- function(path = ".") { - # See what unzip is being used - operating_system <- Sys.info()[1] - - path <- rprojroot::find_root(rprojroot::has_dir(".git"), path = file.path(path)) - - return(path) -} - -#' Get file paths to all qmds or rmds in the course website directory -#' -#' @param path Where to look for the _bookdown.yml or _quarto.yml file. Passes to get_yaml_spec() function. By default looks in current directory -#' -#' @return The file paths to rmds or wmds listed in the _bookdown.yml or _quarto.yml file. -#' @export -#' -qrmd_files <- function(path = ".") { - spec <- get_yaml_spec(file.path(path)) - - rmd_files <- spec$rmd_files - qmd_files <- grep(".qmd", unlist(spec$book$chapters), value = TRUE) - - if (length(rmd_files) > 0 && length(qmd_files) > 0) stop("Both qmd and rmd files are found. Not sure what format to expect") - - # Make files whichever ones exist here - if (length(rmd_files) > 0) files <- rmd_files - if (length(qmd_files) > 0) files <- qmd_files - - if (length(rmd_files) == 0 && length(qmd_files) == 0) { - warning( - "No rmd or qmd files found specified in the _quarto.yml or _bookdown.yml file. Going to try to find files in the repo based on suffix" - ) - root_dir <- course_path(path = file.path(path)) - files <- list.files( - pattern = "[.]Rmd$|[.]qmd$", ignore.case = TRUE, - path = root_dir, full.names = FALSE - ) - } - - # If we don't find files STOP - if (length(files) == 0) stop("No rmd/qmd files found in the repo") - - return(files) -} - -#' Declare file path to docs/ folder -#' -#' @param path Where to look for the _bookdown.yml or _quarto.yml file. Passes to get_yaml_spec() function. By default looks in current directory -#' -#' @return The file paths to rmds or qmds listed in the _bookdown.yml or _quarto.ymlfile. -#' @export -#' -output_destination <- function(path = ".") { - # Find .git folder which indicates the top of the repo - root_dir <- course_path(path = file.path(path)) - - # Get specs from _bookdown.yml or _quarto.yml - spec <- get_yaml_spec(path = file.path(path)) - - # Find output directory declared in the bookdown.yml - output_dir <- spec$output_dir - - # If that didn't work we're working with quarto so look for that - if (is.null(output_dir)) output_dir <- spec$project$`output-dir` - - # If none specified, assume its called docs/ - if (is.null(output_dir)) { - output_dir <- "docs" - } - # Get the full file path - full_output_dir <- file.path(root_dir, output_dir) - - # If the output dir doesn't exist, make it - dir.create(full_output_dir, showWarnings = FALSE, recursive = TRUE) - - # Declare full paths - full_output_dir <- normalizePath(full_output_dir, winslash = "/") - - return(full_output_dir) -} - -copy_resources <- function(path = ".", - images_dir = "resources", - output_dir = "manuscript") { - # Get file path to bookdown.yml - path <- course_path(path) - - # Assume image directory is `resources/images` - res_image_dir <- file.path(path, images_dir) - - # Create the directory if it doesn't exist - dir.create(res_image_dir, showWarnings = FALSE, recursive = TRUE) - - manuscript_image_dir <- file.path(output_dir, images_dir) - - dir.create(manuscript_image_dir, showWarnings = FALSE, recursive = TRUE) - - manuscript_image_dir <- normalizePath(manuscript_image_dir) - - if (dir.exists(res_image_dir)) { - R.utils::copyDirectory(res_image_dir, manuscript_image_dir, recursive = TRUE, overwrite = TRUE) - } -} - -copy_docs <- function(path = ".", output_dir = "manuscript") { - path <- output_destination (path) - R.utils::copyDirectory(path, file.path(output_dir), recursive = TRUE, overwrite = TRUE) -} - -copy_bib <- function(path = ".", output_dir = "manuscript") { - path <- course_path(path) - files <- list.files(path = path, full.names = TRUE, pattern = ".bib$") - if (length(files) > 0) { - file.copy(files, file.path(output_dir), overwrite = TRUE) - } -} - -copy_quizzes <- function(quiz_dir = "quizzes", output_dir = "manuscript") { - quiz_dir <- file.path(quiz_dir) - - if (!is.null(quiz_dir)) { - if (!dir.exists(quiz_dir)) { - warning(paste( - "The quiz directory specified by quiz_dir:", quiz_dir, "does not exist.", - "If you don't have quizzes, set quiz_dir = NULL" - )) - } - quizzes <- list.files(path = file.path(quiz_dir), full.names = TRUE, pattern = "\\.md$") - if (length(quizzes) > 0) { - fs::file_copy(quizzes, file.path(output_dir, basename(quizzes)), - overwrite = TRUE - ) - } - } -} - -#' Set up Manuscript folder for Leanpub publishing -#' -#' @param path path to the top of course repository (looks for .git folder) -#' @param output_dir output directory to put files. It should likely be -#' relative to path -#' @param clean_up TRUE/FALSE the old output directory should be deleted and -#' everything created fresh. -#' @param render If NULL will not be run. If "quarto" or "bookdown" then the respective render type will be run -#' @param verbose print diagnostic messages -#' @param remove_resources_start remove the word `resources/` at the front -#' of any image path. -#' @param run_quiz_checks TRUE/FALSE run quiz checks -#' @param make_book_txt Should [ottrpal::course_to_book_txt()] be run -#' to create a `Book.txt` in the output directory? -#' @param quiz_dir directory that contains the quiz .md files that should be -#' checked and incorporated into the Book.txt file. If you don't have quizzes, -#' set this to NULL -#' @param footer_text Optionally can add a bit of text that will be added to the -#' end of each file before the references section. -#' @return A list of output files and diagnostics -#' @export -#' -set_up_leanpub <- function(path = ".", - clean_up = FALSE, - render = NULL, - output_dir = "manuscript", - make_book_txt = FALSE, - quiz_dir = "quizzes", - run_quiz_checks = FALSE, - remove_resources_start = FALSE, - verbose = TRUE, - footer_text = NULL) { - - - # Check that render is something we can use - if (!is.null(render)) { - if (!render %in% c("quarto", "bookdown")) stop("`render` argument invalid. ") - } - - if (clean_up) { - message(paste("Clearing out old version of output files:", output_dir)) - - file.remove(output_dir, recursive = TRUE, showWarnings = FALSE) - } - - # If output directory doesn't exist, make it - if (!dir.exists(output_dir)) { - dir.create(output_dir, recursive = TRUE, showWarnings = FALSE) - } - - if (!is.null(render)) { - # Declare regex for finding rmd files - rmd_regex <- "[.][q|R|r]md$" - - # Get the path to the _bookdown.yml or _quarto.yml - path <- course_path(path) - - if (verbose) { - message(paste0("Looking for bookdown or quarto md files in ", path)) - } - md_files <- qrmd_files(path = path) - - if (verbose) { - message(paste0(c("Processing these files: ", rmd_files), collapse = "\n")) - } - - if (render == "bookdown") { - if (verbose) { - message("Rendering bookdown Book") - } - # Get the index file path - index_file <- grep("index", rmd_files, ignore.case = TRUE, value = TRUE) - - index_file <- normalizePath(index_file) - - if (length(index_file) == 0 || is.na(index_file)) { - index_file <- md_files[1] - } - message(paste("index_file is", index_file)) - - if (rmarkdown::pandoc_version() >= "2.11") { - output_format <- bookdown::gitbook(pandoc_args = "--citeproc") - output_format$pandoc$args <- c(output_format$pandoc$args, "--citeproc") - } else { - warning("Pandoc version is not greater than 2.11 so citations will not be able to be rendered properly") - output_format <- NULL - } - bookdown::render_book( - input = index_file, - output_format = output_format, - clean_envir = FALSE - ) - } - if (render == "quarto") { - if (verbose) { - message("Rendering Quarto Book") - } - # Get the index file path - index_file <- grep("index", rmd_files, ignore.case = TRUE, value = TRUE) - - index_file <- normalizePath(index_file) - - if (length(index_file) == 0 || is.na(index_file)) { - index_file <- md_files[1] - } - message(paste("index_file is", index_file)) - - quarto::quarto_render('.') - } - - } - - if (!is.null(quiz_dir)) { - #### Run quiz checks - if (run_quiz_checks) { - message("Checking quizzes") - quiz_checks <- check_quizzes( - quiz_dir, - verbose = verbose - ) - } - if (verbose) message("Copying quiz files") - copy_quizzes( - quiz_dir = quiz_dir, - output_dir = output_dir - ) - } -} diff --git a/R/utils.R b/R/utils.R index 6ccb4866..7c6ee358 100644 --- a/R/utils.R +++ b/R/utils.R @@ -6,137 +6,28 @@ utils::globalVariables(c( "num", "quiz_dir", "type_url", "file_name", "trimmed", "quiz", "quiz_path", "type", "q_num", "verbose", "chapt_title", "data_path", "image_dir", - "convert_footnotes", "rmd_files" + "convert_footnotes", "rmd_files", "root_dir" )) - -#' Google Slides Helper Functions -#' -#' @param file markdown file for manuscript -#' -#' @return A scalar character vector -#' @export -#' @rdname gs_helpers -gs_id_from_slide <- function(file) { - if (!file.exists(file)) { - return(NA_character_) - } - x <- readLines(file, warn = FALSE) - ind <- grep(x, pattern = "\\[(S|s)lides\\]") - if (length(ind) > 0) { - x <- x[ind] - } else { - x <- x[grep(x, pattern = ".*presentation/d/")] - } - if (!any(grepl("http", x))) { - return(NA_character_) - } - x <- sub(".*\\(\\s*(http.*)\\s*\\).*", "\\1", x) - x <- unlist(sapply(x, function(r) httr::parse_url(r)$path)) - x <- sub("/edit$", "", x) - x <- sub("/export/.*", "", x) - x <- basename(x) - x <- stats::na.omit(x) - x <- x[nchar(x) > 5] - ux <- unique(x) - if (length(ux) > 1) { - warning(paste0( - "Multiple sheets identified! Taking most frequent.", - " Please check ", - file - )) - x <- sort(table(x), decreasing = TRUE) - x <- names(x)[1] - # x = x[1] - } else { - x <- ux - } - if (length(x) == 0 || grepl("\\(\\)", x)) { - return(NA_character_) - } - if (nchar(x) < 10) { - warning(paste0("ID extracted is ", x, ", seems short")) - } - return(x) -} - -###################################### -# this returns the actual links in the text -###################################### -#' @export -#' @rdname gs_helpers -get_image_link_from_slide <- function(file) { - x <- readLines(file, warn = FALSE) - x <- grep(x, pattern = "!\\[.*\\]\\(((resources/|)images.*)\\)", value = TRUE) - x <- sub(x, pattern = "!\\[(.*)\\]\\(((resources/|)images.*)\\)", replacement = "\\1") - # if (length(x) == 0) { - # return(NA) - # } - return(x) -} - -###################################### -# this returns the actual image filenames referenced -# we will check to see if all images referenced exist -###################################### +#' Find root of OTTR course provided +#' @param path Where should we be looking for a OTTR course. By default will look in current working directory. +#' @return Absolute file path to the course pointed to +#' @importFrom rprojroot find_root has_dir +#' @return Returns a absolute file path to where the course is #' @export -#' @rdname gs_helpers -get_image_from_slide <- function(file) { - x <- readLines(file, warn = FALSE) - x <- grep(x, pattern = "!\\[.*\\]\\(((resources/|)images.*)\\)", value = TRUE) - x <- sub(x, pattern = "!\\[.*\\]\\(((resources/|)images.*)\\)", replacement = "\\1") - # if (length(x) == 0) { - # return(NA) - # } - return(x) -} - -is.Token <- function(token) { - inherits(token, "Token") || - (inherits(token, "request") && - inherits(token$auth_token, "Token")) -} +#' +course_path <- function(path = ".") { -png_url <- function(id, page_id) { - paste0( - "https://docs.google.com/presentation/d/", - id, "/export/png?id=", id, - "&pageid=", page_id - ) -} + # Find .git root directory + root_dir <- rprojroot::find_root(has_dir(".github"), path = path) -download_png_urls <- function(urls) { - res <- vapply(urls, function(url) { - tfile <- tempfile(fileext = ".png") - out <- httr::GET( - url, httr::write_disk(tfile), - httr::content_type(".png") - ) - httr::stop_for_status(out) - ctype <- out$headers$`content-type` - ctype <- strsplit(ctype, " ")[[1]] - ctype <- sub(";$", "", ctype) - if (any(ctype == "text/html") && - !any(grepl("png", ctype))) { - stop("Output is not a PNG!") - } - tfile - }, FUN.VALUE = character(1)) - return(res) -} + bookdown <- file.exists(file.path(root_dir, "_bookdown.yml")) + quarto <- file.exists(file.path(root_dir, "_quarto.yml")) + if (bookdown & quarto) stop("No OTTR course found in this repository. Looking for a _bookdown.yml or _quarto.yml file.") -add_footer <- function(rmd_path, footer_text = NULL) { - if (is.null(footer_text)) { - stop("Need character string in footer_text argument to append to end of file.") - } - footer_text <- paste0("\n", footer_text, collapse = "\n") - write(as.character(footer_text), - file = rmd_path, - append = TRUE - ) + return(root_dir) } - #' Pipe operator #' #' See \code{magrittr::\link[magrittr:pipe]{\%>\%}} for details. diff --git a/man/bad_quiz_path.Rd b/man/bad_quiz_path.Rd index 2655347f..87d60e6e 100644 --- a/man/bad_quiz_path.Rd +++ b/man/bad_quiz_path.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/data.R +% Please edit documentation in R/get_data.R \name{bad_quiz_path} \alias{bad_quiz_path} \title{Path to bad example quiz} diff --git a/man/check_all_questions.Rd b/man/check_all_questions.Rd index f25250bb..4a48f056 100644 --- a/man/check_all_questions.Rd +++ b/man/check_all_questions.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/quiz.R +% Please edit documentation in R/quiz_formatting.R \name{check_all_questions} \alias{check_all_questions} \title{Check all quiz questions} diff --git a/man/check_git_repo.Rd b/man/check_git_repo.Rd new file mode 100644 index 00000000..83278d48 --- /dev/null +++ b/man/check_git_repo.Rd @@ -0,0 +1,39 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/github_handling.R +\name{check_git_repo} +\alias{check_git_repo} +\title{Check if a repository exists on GitHub} +\usage{ +check_git_repo( + repo_name, + git_pat = NULL, + silent = TRUE, + return_repo = FALSE, + verbose = TRUE +) +} +\arguments{ +\item{repo_name}{the name of the repository, e.g. jhudsl/OTTR_Template} + +\item{git_pat}{A personal access token from GitHub. Only necessary if the +repository being checked is a private repository.} + +\item{silent}{TRUE/FALSE of whether the warning from the git ls-remote +command should be echoed back if it does fail.} + +\item{return_repo}{TRUE/FALSE of whether or not the output from git ls-remote +should be saved to a file (if the repo exists)} + +\item{verbose}{TRUE/FALSE do you want more progress messages?} +} +\value{ +A TRUE/FALSE whether or not the repository exists. Optionally the +output from git ls-remote if return_repo = TRUE. +} +\description{ +Given a repository name, check with git ls-remote whether the repository exists and return a TRUE/FALSE +} +\examples{ + +check_git_repo("jhudsl/OTTR_Template") +} diff --git a/man/check_question.Rd b/man/check_question.Rd index 72b46893..ffcb22ce 100644 --- a/man/check_question.Rd +++ b/man/check_question.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/quiz.R +% Please edit documentation in R/quiz_formatting.R \name{check_question} \alias{check_question} \title{Check Quiz Question Set Up} diff --git a/man/check_quiz.Rd b/man/check_quiz.Rd index 1e0c51a4..595f9d00 100644 --- a/man/check_quiz.Rd +++ b/man/check_quiz.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/quiz.R +% Please edit documentation in R/quiz_formatting.R \name{check_quiz} \alias{check_quiz} \title{Check Quiz} diff --git a/man/check_quiz_attributes.Rd b/man/check_quiz_attributes.Rd index b37edc58..cfc80f1c 100644 --- a/man/check_quiz_attributes.Rd +++ b/man/check_quiz_attributes.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/quiz.R +% Please edit documentation in R/quiz_formatting.R \name{check_quiz_attributes} \alias{check_quiz_attributes} \title{Check Quiz Attributes} diff --git a/man/check_quiz_question_attributes.Rd b/man/check_quiz_question_attributes.Rd index 7420dcd0..d4b9f1c3 100644 --- a/man/check_quiz_question_attributes.Rd +++ b/man/check_quiz_question_attributes.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/quiz.R +% Please edit documentation in R/quiz_formatting.R \name{check_quiz_question_attributes} \alias{check_quiz_question_attributes} \title{Check a question's attributes} diff --git a/man/check_quizzes.Rd b/man/check_quizzes.Rd index 687910d2..39b2e4d1 100644 --- a/man/check_quizzes.Rd +++ b/man/check_quizzes.Rd @@ -1,10 +1,11 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/quiz.R +% Please edit documentation in R/quiz_formatting.R \name{check_quizzes} \alias{check_quizzes} \title{Check all quizzes in a directory} \usage{ check_quizzes( + path = ".", quiz_dir = "quizzes", write_report = TRUE, verbose = TRUE, @@ -12,6 +13,8 @@ check_quizzes( ) } \arguments{ +\item{path}{path to the top of course repository (looks for .github folder)} + \item{quiz_dir}{A path to a directory full of quizzes that should all be checked with [ottrpal::check_all_quizzes].} \item{write_report}{TRUE/FALSE save warning report to a CSV file?} diff --git a/man/clean_up.Rd b/man/clean_up.Rd new file mode 100644 index 00000000..2f23e4c6 --- /dev/null +++ b/man/clean_up.Rd @@ -0,0 +1,14 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/get_data.R +\name{clean_up} +\alias{clean_up} +\title{Clean up OTTR_Template files used for testing} +\usage{ +clean_up() +} +\value{ +Looks for dangling zips and directories downloaded for testing and removes them +} +\description{ +Clean up OTTR_Template files used for testing +} diff --git a/man/convert_coursera_quizzes.Rd b/man/convert_coursera_quizzes.Rd index 0e15f5db..6ab3fbb8 100644 --- a/man/convert_coursera_quizzes.Rd +++ b/man/convert_coursera_quizzes.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/coursera_and_leanpub.R +% Please edit documentation in R/quiz_formatting.R \name{convert_coursera_quizzes} \alias{convert_coursera_quizzes} \title{Convert Leanpub md quiz to Coursera yaml quiz} diff --git a/man/convert_quiz.Rd b/man/convert_quiz.Rd index 67520508..3bd35dbe 100644 --- a/man/convert_quiz.Rd +++ b/man/convert_quiz.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/coursera_and_leanpub.R +% Please edit documentation in R/quiz_formatting.R \name{convert_quiz} \alias{convert_quiz} \title{Convert Leanpub md quiz to Coursera yaml quiz} diff --git a/man/course_path.Rd b/man/course_path.Rd index ca1642c1..1f59783b 100644 --- a/man/course_path.Rd +++ b/man/course_path.Rd @@ -1,17 +1,19 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/set_up.R +% Please edit documentation in R/utils.R \name{course_path} \alias{course_path} -\title{Find main course git directory} +\title{Find root of OTTR course provided} \usage{ course_path(path = ".") } \arguments{ -\item{path}{Where to look for the file. By default looks in current directory.} +\item{path}{Where should we be looking for a OTTR course. By default will look in current working directory.} } \value{ -Returns the directory where the .git folder is contained. +Absolute file path to the course pointed to + +Returns a absolute file path to where the course is } \description{ -Find main course git directory +Find root of OTTR course provided } diff --git a/man/course_to_book_txt.Rd b/man/course_to_book_txt.Rd index 70e047c9..49bfd0b9 100644 --- a/man/course_to_book_txt.Rd +++ b/man/course_to_book_txt.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/iframe_leanpub.R +% Please edit documentation in R/leanpub.R \name{course_to_book_txt} \alias{course_to_book_txt} \title{Create Book.txt file from files existing in quiz directory} diff --git a/man/coursera.Rd b/man/coursera.Rd index 81ae5847..b9606027 100644 --- a/man/coursera.Rd +++ b/man/coursera.Rd @@ -1,10 +1,11 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/coursera_and_leanpub.R +% Please edit documentation in R/render_without_toc.R \name{render_without_toc} \alias{render_without_toc} \title{Create TOC-less course website for use in Coursera or Leanpub} \usage{ render_without_toc( + course_path = ".", output_dir = file.path("docs", "no_toc"), output_yaml = "_output.yml", convert_quizzes = FALSE, diff --git a/man/encrypt_creds_path.Rd b/man/encrypt_creds_path.Rd index 64ee34be..7bef23c5 100644 --- a/man/encrypt_creds_path.Rd +++ b/man/encrypt_creds_path.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/data.R +% Please edit documentation in R/get_data.R \name{encrypt_creds_path} \alias{encrypt_creds_path} \title{Get file path to an encrypted credentials RDS} diff --git a/man/encrypt_creds_user_path.Rd b/man/encrypt_creds_user_path.Rd index 116c94ec..7946f0ed 100644 --- a/man/encrypt_creds_user_path.Rd +++ b/man/encrypt_creds_user_path.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/data.R +% Please edit documentation in R/get_data.R \name{encrypt_creds_user_path} \alias{encrypt_creds_user_path} \title{Get file path to an default credentials RDS} diff --git a/man/extract_meta.Rd b/man/extract_meta.Rd index e2edc201..b6a4980e 100644 --- a/man/extract_meta.Rd +++ b/man/extract_meta.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/quiz.R +% Please edit documentation in R/quiz_formatting.R \name{extract_meta} \alias{extract_meta} \title{Extract meta fields from a tag} diff --git a/man/get_chapters.Rd b/man/get_chapters.Rd index 7025182f..2aa7a0d2 100644 --- a/man/get_chapters.Rd +++ b/man/get_chapters.Rd @@ -1,12 +1,18 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/iframe_leanpub.R +% Please edit documentation in R/leanpub.R \name{get_chapters} \alias{get_chapters} \title{Make Leanpub file that has embed webpage of a chapter} \usage{ -get_chapters(html_page = file.path("docs", "index.html"), base_url = ".") +get_chapters( + path = ".", + html_page = file.path("docs", "index.html"), + base_url = "." +) } \arguments{ +\item{path}{path to the bookdown or quarto course repository, must have a `_bookdown.yml` or `_quarto.yml` file} + \item{html_page}{The file path of the rendered index.html file. It can be a url} \item{base_url}{The base url of where the chapters are published -- the url to provide to the iframe in Leanpub diff --git a/man/get_git_auth.Rd b/man/get_git_auth.Rd new file mode 100644 index 00000000..c5662905 --- /dev/null +++ b/man/get_git_auth.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/github_handling.R +\name{get_git_auth} +\alias{get_git_auth} +\title{Handle GitHub PAT authorization} +\usage{ +get_git_auth( + git_pat = NULL, + git_username = "PersonalAccessToken", + quiet = FALSE +) +} +\arguments{ +\item{git_pat}{If private repositories are to be retrieved, a github personal +access token needs to be supplied. If none is supplied, then this will attempt to +grab from a git pat set in the environment with usethis::create_github_token().} + +\item{git_username}{Optional, can include username for credentials.} + +\item{quiet}{Use TRUE if you don't want the warning about no GitHub credentials.} +} +\value{ +Authorization argument to supply to curl OR a blank string if no +authorization is found or supplied. +} +\description{ +Handle things whether or not a GitHub PAT is supplied. +} diff --git a/man/get_pages_url.Rd b/man/get_pages_url.Rd index 8f45facc..40b34963 100644 --- a/man/get_pages_url.Rd +++ b/man/get_pages_url.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/get_pages_url.R +% Please edit documentation in R/github_handling.R \name{get_pages_url} \alias{get_pages_url} \title{Retrieve pages url for a repo} diff --git a/man/get_repo_info.Rd b/man/get_repo_info.Rd new file mode 100644 index 00000000..cd7ae486 --- /dev/null +++ b/man/get_repo_info.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/github_handling.R +\name{get_repo_info} +\alias{get_repo_info} +\title{Retrieve information about a github repo} +\usage{ +get_repo_info(repo_name, git_pat = NULL, verbose = FALSE) +} +\arguments{ +\item{repo_name}{The full name of the repo to get bookdown chapters from. +e.g. "jhudsl/OTTR_Template"} + +\item{git_pat}{If private repositories are to be retrieved, a github personal +access token needs to be supplied. If none is supplied, then this will attempt to +grab from a git pat set in the environment with usethis::create_github_token(). +Authorization handled by \link[githubr]{get_git_auth}} + +\item{verbose}{TRUE/FALSE do you want more progress messages?} +} +\value{ +a data frame with the repository with the following columns: +data_level, data_path, chapt_name, url, repository name +} +\description{ +Given an repository on GitHub, retrieve the information about it from the +GitHub API and read it into R. +} +\examples{ + +repo_info <- get_repo_info("jhudsl/Documentation_and_Usability") +} diff --git a/man/get_slide_id.Rd b/man/get_slide_id.Rd index fc69f905..66abb405 100644 --- a/man/get_slide_id.Rd +++ b/man/get_slide_id.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/gs_png.R +% Please edit documentation in R/google_slides.R \name{get_slide_id} \alias{get_slide_id} \title{Get Slide ID from URL} diff --git a/man/get_yaml_spec.Rd b/man/get_yaml_spec.Rd deleted file mode 100644 index 01167711..00000000 --- a/man/get_yaml_spec.Rd +++ /dev/null @@ -1,17 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/set_up.R -\name{get_yaml_spec} -\alias{get_yaml_spec} -\title{Load in yaml specifications from _bookdown.yml or _quarto.yml} -\usage{ -get_yaml_spec(path = ".") -} -\arguments{ -\item{path}{Where to look for the yaml spec file. By default looks in current directory.} -} -\value{ -The yaml contents using yaml::yaml.load_file() -} -\description{ -Load in yaml specifications from _bookdown.yml or _quarto.yml -} diff --git a/man/good_quiz_path.Rd b/man/good_quiz_path.Rd index a4b6f14b..c72c029c 100644 --- a/man/good_quiz_path.Rd +++ b/man/good_quiz_path.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/data.R +% Please edit documentation in R/get_data.R \name{good_quiz_path} \alias{good_quiz_path} \title{Path to good example quiz} diff --git a/man/gs_helpers.Rd b/man/gs_helpers.Rd index ea529d7d..f07781be 100644 --- a/man/gs_helpers.Rd +++ b/man/gs_helpers.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/utils.R +% Please edit documentation in R/google_slides.R \name{gs_id_from_slide} \alias{gs_id_from_slide} \alias{get_image_link_from_slide} diff --git a/man/gs_png_url.Rd b/man/gs_png_url.Rd index bc7cc104..3ab5e737 100644 --- a/man/gs_png_url.Rd +++ b/man/gs_png_url.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/gs_png.R +% Please edit documentation in R/google_slides.R \name{gs_png_url} \alias{gs_png_url} \alias{get_slide_page} diff --git a/man/key_encrypt_creds_path.Rd b/man/key_encrypt_creds_path.Rd index 66617191..b23cb0f0 100644 --- a/man/key_encrypt_creds_path.Rd +++ b/man/key_encrypt_creds_path.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/data.R +% Please edit documentation in R/get_data.R \name{key_encrypt_creds_path} \alias{key_encrypt_creds_path} \title{Get file path to an key encryption RDS} diff --git a/man/make_embed_markdown.Rd b/man/make_embed_markdown.Rd index 59c2fdd0..12d3b5c2 100644 --- a/man/make_embed_markdown.Rd +++ b/man/make_embed_markdown.Rd @@ -1,38 +1,41 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/iframe_leanpub.R +% Please edit documentation in R/leanpub.R \name{make_embed_markdown} \alias{make_embed_markdown} \title{Make Leanpub file that has embed webpage of a chapter} \usage{ make_embed_markdown( + path = ".", url, chapt_title, - width_spec = 800, - height_spec = 600, img_path, output_dir = "manuscript", - verbose = TRUE, - footer_text = "" + footer_text = "", + width_spec = 800, + height_spec = 600, + verbose = TRUE ) } \arguments{ +\item{path}{path to the bookdown or quarto course repository, must have a `_bookdown.yml` or `_quarto.yml` file} + \item{url}{The url to the chapter that is to be embed} \item{chapt_title}{Title of chapter to be used as file name and printed on iframe} -\item{width_spec}{How wide should the iframe be in pixels?} - -\item{height_spec}{How high should the iframe be in pixels?} - \item{img_path}{File path to image to use for poster} \item{output_dir}{output directory to put files. It should likely be relative to path} -\item{verbose}{print diagnostic messages} - \item{footer_text}{Optionally can add a bit of text that will be added to the end of each file before the references section.} + +\item{width_spec}{How wide should the iframe be in pixels?} + +\item{height_spec}{How high should the iframe be in pixels?} + +\item{verbose}{print diagnostic messages} } \value{ A markdown file with an iframe of the provided chapter diff --git a/man/make_screenshots.Rd b/man/make_screenshots.Rd index d3d4c9dd..6dcb0b2a 100644 --- a/man/make_screenshots.Rd +++ b/man/make_screenshots.Rd @@ -1,17 +1,20 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/screenshot.R +% Please edit documentation in R/leanpub.R \name{make_screenshots} \alias{make_screenshots} \title{A function to make screenshots from an OTTR bookdown website} \usage{ make_screenshots( + path = ".", git_pat, repo, - output_dir = "resources/chapt_screen_images", + output_dir = file.path(path, "resources", "chapt_screen_images"), base_url = NULL ) } \arguments{ +\item{path}{default is to look for OTTR files in current directory based on existence of .github. But if you'd like to run this in a different path, you can point to that file path.} + \item{git_pat}{required argument; a Git secret -- see https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens for more info} \item{repo}{required argument; GitHub repository name, e.g., jhudsl/OTTR_Template} @@ -29,8 +32,10 @@ This function creates screenshots of course chapters that are stored in a create \examples{ \dontrun{ - make_screenshots(Sys.getenv("secrets.GH_PAT"), "jhudsl/OTTR_Template") - +make_screenshots( + git_pat = Sys.getenv("secrets.GH_PAT"), + repo = "jhudsl/OTTR_Template" +) } } \author{ diff --git a/man/output_destination.Rd b/man/output_destination.Rd deleted file mode 100644 index 4b5c6e08..00000000 --- a/man/output_destination.Rd +++ /dev/null @@ -1,17 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/set_up.R -\name{output_destination} -\alias{output_destination} -\title{Declare file path to docs/ folder} -\usage{ -output_destination(path = ".") -} -\arguments{ -\item{path}{Where to look for the _bookdown.yml or _quarto.yml file. Passes to get_yaml_spec() function. By default looks in current directory} -} -\value{ -The file paths to rmds or qmds listed in the _bookdown.yml or _quarto.ymlfile. -} -\description{ -Declare file path to docs/ folder -} diff --git a/man/parse_q_tag.Rd b/man/parse_q_tag.Rd index e90400dc..9ba023d8 100644 --- a/man/parse_q_tag.Rd +++ b/man/parse_q_tag.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/quiz.R +% Please edit documentation in R/quiz_formatting.R \name{parse_q_tag} \alias{parse_q_tag} \title{Parse apart a tag} diff --git a/man/parse_quiz.Rd b/man/parse_quiz.Rd index 3b75cdde..d17ae0a3 100644 --- a/man/parse_quiz.Rd +++ b/man/parse_quiz.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/quiz.R +% Please edit documentation in R/quiz_formatting.R \name{parse_quiz} \alias{parse_quiz} \alias{extract_quiz} diff --git a/man/parse_quiz_df.Rd b/man/parse_quiz_df.Rd index 39f74b0f..51990cff 100644 --- a/man/parse_quiz_df.Rd +++ b/man/parse_quiz_df.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/quiz.R +% Please edit documentation in R/quiz_formatting.R \name{parse_quiz_df} \alias{parse_quiz_df} \title{Parse quiz into a data.frame} diff --git a/man/qrmd_files.Rd b/man/qrmd_files.Rd deleted file mode 100644 index e4057b91..00000000 --- a/man/qrmd_files.Rd +++ /dev/null @@ -1,17 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/set_up.R -\name{qrmd_files} -\alias{qrmd_files} -\title{Get file paths to all qmds or rmds in the course website directory} -\usage{ -qrmd_files(path = ".") -} -\arguments{ -\item{path}{Where to look for the _bookdown.yml or _quarto.yml file. Passes to get_yaml_spec() function. By default looks in current directory} -} -\value{ -The file paths to rmds or wmds listed in the _bookdown.yml or _quarto.yml file. -} -\description{ -Get file paths to all qmds or rmds in the course website directory -} diff --git a/man/set_knitr_image_path.Rd b/man/set_knitr_image_path.Rd deleted file mode 100644 index 655c7ea9..00000000 --- a/man/set_knitr_image_path.Rd +++ /dev/null @@ -1,17 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/set_knitr_image_path.R -\name{set_knitr_image_path} -\alias{set_knitr_image_path} -\title{Set image path for `knitr`} -\usage{ -set_knitr_image_path(verbose = FALSE) -} -\arguments{ -\item{verbose}{print out what the figure path is} -} -\value{ -When used inside a knitted R Markdown document, will set the image path to a place compatible with 'ottrpal' output folders. -} -\description{ -Set image path for `knitr` -} diff --git a/man/set_up_leanpub.Rd b/man/set_up_leanpub.Rd deleted file mode 100644 index ad5feefb..00000000 --- a/man/set_up_leanpub.Rd +++ /dev/null @@ -1,53 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/set_up.R -\name{set_up_leanpub} -\alias{set_up_leanpub} -\title{Set up Manuscript folder for Leanpub publishing} -\usage{ -set_up_leanpub( - path = ".", - clean_up = FALSE, - render = NULL, - output_dir = "manuscript", - make_book_txt = FALSE, - quiz_dir = "quizzes", - run_quiz_checks = FALSE, - remove_resources_start = FALSE, - verbose = TRUE, - footer_text = NULL -) -} -\arguments{ -\item{path}{path to the top of course repository (looks for .git folder)} - -\item{clean_up}{TRUE/FALSE the old output directory should be deleted and -everything created fresh.} - -\item{render}{If NULL will not be run. If "quarto" or "bookdown" then the respective render type will be run} - -\item{output_dir}{output directory to put files. It should likely be -relative to path} - -\item{make_book_txt}{Should [ottrpal::course_to_book_txt()] be run -to create a `Book.txt` in the output directory?} - -\item{quiz_dir}{directory that contains the quiz .md files that should be -checked and incorporated into the Book.txt file. If you don't have quizzes, -set this to NULL} - -\item{run_quiz_checks}{TRUE/FALSE run quiz checks} - -\item{remove_resources_start}{remove the word `resources/` at the front -of any image path.} - -\item{verbose}{print diagnostic messages} - -\item{footer_text}{Optionally can add a bit of text that will be added to the -end of each file before the references section.} -} -\value{ -A list of output files and diagnostics -} -\description{ -Set up Manuscript folder for Leanpub publishing -} diff --git a/man/download_ottr_template.Rd b/man/setup_ottr_template.Rd similarity index 57% rename from man/download_ottr_template.Rd rename to man/setup_ottr_template.Rd index aa0e6dad..a0fee5b6 100644 --- a/man/download_ottr_template.Rd +++ b/man/setup_ottr_template.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/data.R -\name{download_ottr_template} -\alias{download_ottr_template} -\title{Download files from main OTTR_Template to test} +% Please edit documentation in R/get_data.R +\name{setup_ottr_template} +\alias{setup_ottr_template} +\title{Download and render files from main OTTR_Template to test} \usage{ -download_ottr_template(dir = "inst/extdata", type = "rmd") +setup_ottr_template(dir = "inst/extdata", type) } \arguments{ \item{dir}{What relative file path should the files be downloaded} @@ -15,5 +15,5 @@ download_ottr_template(dir = "inst/extdata", type = "rmd") This downloads the main branch repo files from the respective repo for testing purposes } \description{ -Download files from main OTTR_Template to test +Download and render files from main OTTR_Template to test } diff --git a/man/website_to_embed_leanpub.Rd b/man/website_to_embed_leanpub.Rd index b9b2a751..095b5dcf 100644 --- a/man/website_to_embed_leanpub.Rd +++ b/man/website_to_embed_leanpub.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/iframe_leanpub.R +% Please edit documentation in R/leanpub.R \name{website_to_embed_leanpub} \alias{website_to_embed_leanpub} \title{Convert Website Course to Leanpub} @@ -10,6 +10,7 @@ website_to_embed_leanpub( render = NULL, html_page = file.path(base_url, "index.html"), base_url = NULL, + clean_up = TRUE, default_img = NULL, output_dir = "manuscript", make_book_txt = FALSE, diff --git a/tests/testthat/test-quarto_leanpub_prep.R b/tests/testthat/test-quarto_leanpub_prep.R index 08d96f5a..24d6a74b 100644 --- a/tests/testthat/test-quarto_leanpub_prep.R +++ b/tests/testthat/test-quarto_leanpub_prep.R @@ -1,10 +1,6 @@ test_that("Create Leanpub IFrames for Quarto", { - dir <- download_ottr_template(dir = ".", type = "quarto") - - quarto::quarto_render(dir, - metadata = list(sidebar = F, toc = F), - quarto_args = c('--output-dir', 'docs/no_toc/')) + dir <- setup_ottr_template(dir = ".", type = "quarto") # TODO: This should be functionalized and incorporated into the package # curl -o make_screenshots.R https://raw.githubusercontent.com/jhudsl/ottr-reports/main/scripts/make_screenshots.R @@ -29,6 +25,5 @@ test_that("Create Leanpub IFrames for Quarto", { # 2. Does each md link to the appropriate sceenshot? # 3. Did the screenshot file path that's in the md lead to the appropriate file path? - unlink(dir, recursive = TRUE) - file.remove(paste0(dir, ".zip")) + clean_up() }) diff --git a/tests/testthat/test-quizzes.R b/tests/testthat/test-quizzes.R index 59821650..dd39a6aa 100644 --- a/tests/testthat/test-quizzes.R +++ b/tests/testthat/test-quizzes.R @@ -1,4 +1,4 @@ -test_that("Quiz checks", { +test_that("Check good quiz", { # Using good quiz md example quiz_path <- good_quiz_path() @@ -6,23 +6,30 @@ test_that("Quiz checks", { good_quiz_specs <- parse_quiz(good_quiz) good_quiz_checks <- check_all_questions(good_quiz_specs) + # It should find no errors with the good quiz + testthat::expect_true(nrow(good_quiz_checks) == 0) +}) + +test_that("Check bad quiz", { # Using bad quiz md example bad_quiz <- readLines(bad_quiz_path()) bad_quiz_specs <- parse_quiz(bad_quiz) - # THe following checks *should fail* because we're giving it a bad quiz. - bad_quiz_checks <- suppressWarnings(check_all_questions(bad_quiz_specs)) + # The following checks *should fail* because we're giving it a bad quiz. + bad_quiz_checks <- try(check_all_questions(bad_quiz_specs), silent = TRUE) + # It should find two errors + testthat::expect_true(nrow(bad_quiz_checks) == 2) +}) + +test_that("Make a quiz report", { ## Make a temporary quiz directory quiz_dir <- dirname(good_quiz_path()) ## Now check the quizzes in that directory # The following checks *should also fail* because the bad quiz is in there - all_quiz_results <- suppressWarnings(check_quizzes(quiz_dir = quiz_dir)) - - ## TEST HERE: - # 1. quiz_error_report.tsv should be made - # 2. bad quiz should have errors - # 3. good quiz should pass! + all_quiz_results <- try(check_quizzes(quiz_dir = quiz_dir), silent = TRUE) + # Should have a report saved to the quiz directory + testthat::expect_true(file.exists(file.path(quiz_dir, "question_error_report.tsv"))) }) diff --git a/tests/testthat/test-rendering.R b/tests/testthat/test-rendering.R index 1c78a4cb..492f4a37 100644 --- a/tests/testthat/test-rendering.R +++ b/tests/testthat/test-rendering.R @@ -1,54 +1,43 @@ +## These tests make sure the infrastructure for setting up test OTTR repos is working +## Makes sure that the repos are downloaded, rendered and then cleaned up. + test_that("Rmd Rendering", { - rmd_dir <- download_ottr_template(dir = ".", type = "rmd") + rmd_dir <- setup_ottr_template(dir = ".", type = "rmd") - dir.exists(rmd_dir) + testthat::expect_true(dir.exists(rmd_dir)) - bookdown::render_book(rmd_dir) + clean_up() - unlink(rmd_dir, recursive = TRUE) - file.remove(paste0(rmd_dir, ".zip")) + testthat::expect_true(!dir.exists(rmd_dir)) }) test_that("Quarto Rendering", { - quarto_dir <- download_ottr_template(dir = ".", type = "quarto") - - dir.exists(quarto_dir) + quarto_dir <- setup_ottr_template(dir = ".", type = "quarto") - # Render it normal - quarto::quarto_render(quarto_dir, as_job = FALSE) + testthat::expect_true(dir.exists(quarto_dir)) - # Render it a different way - quarto::quarto_render(quarto_dir, - metadata = list(sidebar = F, toc = F), - quarto_args = c("--output-dir", "docs/no_toc/"), - as_job = FALSE + clean_up() - ) - unlink(quarto_dir, recursive = TRUE) - file.remove(paste0(quarto_dir, ".zip")) + testthat::expect_true(!dir.exists(quarto_dir)) }) test_that("Rmd Website Rendering", { - rmd_web_dir <- download_ottr_template(dir = ".", type = "rmd_website") - - dir.exists(rmd_web_dir) + rmd_web_dir <- setup_ottr_template(dir = ".", type = "rmd_website") - rmarkdown::clean_site(rmd_web_dir, preview = FALSE) + testthat::expect_true(dir.exists(rmd_web_dir)) - rmarkdown::render_site(rmd_web_dir) + clean_up() - unlink(rmd_web_dir, recursive = TRUE) - file.remove(paste0(rmd_web_dir, ".zip")) + testthat::expect_true(!dir.exists(rmd_web_dir)) }) test_that("Quarto Website Rendering", { - quarto_web_dir <- download_ottr_template(dir = ".", type = "quarto_website") + quarto_web_dir <- setup_ottr_template(dir = ".", type = "quarto_website") - dir.exists(quarto_web_dir) + testthat::expect_true(dir.exists(quarto_web_dir)) - quarto::quarto_render(quarto_web_dir, as_job = FALSE) + clean_up() - unlink(quarto_web_dir, recursive = TRUE) - file.remove(paste0(quarto_web_dir, ".zip")) + testthat::expect_true(!dir.exists(quarto_web_dir)) }) diff --git a/tests/testthat/test-rmd_leanpub_prep.R b/tests/testthat/test-rmd_leanpub_prep.R index 34cd5b1f..0e0e0761 100644 --- a/tests/testthat/test-rmd_leanpub_prep.R +++ b/tests/testthat/test-rmd_leanpub_prep.R @@ -1,39 +1,73 @@ -test_that("Create Leanpub IFrames for Rmd", { +test_that("Get base URL", { - dir <- download_ottr_template(dir = ".", type = "rmd") + ### Now run functions we will test + base_url <- get_pages_url(repo_name = "jhudsl/OTTR_Template", + git_pat = Sys.getenv("secrets.GH_PAT")) - dir.exists(dir) + # TODO: Test that the URL can + testthat::expect_true(base_url == "https://jhudatascience.org/OTTR_Template/") +}) + +test_that("Get chapters", { + ### Set up the OTTR repo + dir <- setup_ottr_template(dir = ".", type = "rmd") + + chapt_df <- get_chapters(html_page = file.path("OTTR_Template-main", "docs", "index.html")) + + testthat::expect_named(chapt_df, c("url", "chapt_title")) + +}) + +test_that("Make screenshots", { + # We want to make screenshots from the course + chapt_df_file <- make_screenshots(git_pat = Sys.getenv("secrets.GH_PAT"), + repo = "jhudsl/OTTR_Template", + path = "OTTR_Template-main") - bookdown::render_book(dir) + testthat::expect_equal(chapt_df_file, "resources/chapt_screen_images/chapter_urls.tsv") - # TODO: This should be functionalized and incorporated into the package - # curl -o make_screenshots.R https://raw.githubusercontent.com/jhudsl/ottr-reports/main/scripts/make_screenshots.R - # Rscript --vanilla make_screenshots.R - # --git_pat sys.getEnv("GH_PAT") - # --repo fhdsl/OTTR_Template - # --output_dir resources/chapt_screen_images) + chapt_df <- readr::read_tsv(file.path("OTTR_Template-main", + "resources", + "chapt_screen_images", + "chapter_urls.tsv")) - ## TEST HERE: - # 1. Does each chapter have screenshot? - # 2. Is the file 'resources/chapt_screen_images/chapter_urls.tsv' made fresh? - # 2. Does chapter_urls.tsv file made have columns with information that are labeled "url", "chapt_title" and "img_path" + # Expect column names should still be the + testthat::expect_names(chapt_df, c("url", "chapt_title", "img_path")) - #set_up_leanpub( - # make_book_txt = TRUE, - # quiz_dir = NULL - #) + testthat::expect_number(nrow(chapt_df), 5) +}) + +test_that("Set Up Leanpub", { + dir <- setup_ottr_template(dir = ".", type = "rmd") + + # We're going to delete this so we can test making it again + unlink(file.path(dir, "manuscript"), recursive = TRUE) + + # Now run the iframe maker bit + website_to_embed_leanpub( + path = dir, + chapt_img_key = file.path('resources', 'chapt_screen_images', 'chapter_urls.tsv'), + make_book_txt = TRUE, + quiz_dir = NULL, + output_dir = "manuscript") - #website_to_embed_leanpub( - #chapt_img_key = 'resources/chapt_screen_images/chapter_urls.tsv', - #make_book_txt = TRUE, - #quiz_dir = NULL) + testthat::expect_true( + file.exists(file.path(dir, + "manuscript", + "resources", + "chapt_screen_images", + "introduction.png"))) - ## TEST HERE: - # 1. Did each chapter get a md in the manuscript folder? - # 2. Does each md link to the appropriate sceenshot? - # 3. Did the screenshot file path that's in the md lead to the appropriate file path? + # Lets check what the file looks like + intro <- readLines(file.path(dir, "manuscript", "1-Introduction.md")) - unlink(dir, recursive = TRUE) - file.remove(paste0(dir, ".zip")) + # Make sure the png is pointed to + testthat::expect_true(any(grepl("poster:resources/chapt_screen_images/introduction.png", intro))) + + # Make sure we link to the page + testthat::expect_true(any(grepl("![](https://jhudatascience.org/OTTR_Template/introduction.html)", intro, fixed = TRUE))) + + clean_up() }) +