diff --git a/DESCRIPTION b/DESCRIPTION index bedb0d7..7055c99 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: ghqc.app Title: Create QC Checklists in Github Issues -Version: 0.2.0.9000 +Version: 0.3.0.9000 Authors@R: c( person("Jenna", "Johnson", email = "jenna@a2-ai.com", role = c("aut", "cre")), person("Anne", "Zheng", email = "anne@a2-ai.com", role = c("aut")), diff --git a/NAMESPACE b/NAMESPACE index 02ccf0d..be286af 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,7 +3,7 @@ export(ghqc_assign_app) export(ghqc_record_app) export(ghqc_resolve_app) -export(ghqc_set_info_repo) +export(ghqc_set_config_repo) import(shiny) importFrom(dplyr,"%>%") importFrom(dplyr,case_when) @@ -16,6 +16,7 @@ importFrom(fs,is_file) importFrom(gert,git_ahead_behind) importFrom(gert,git_fetch) importFrom(gert,git_status) +importFrom(gh,gh) importFrom(glue,glue) importFrom(jsTreeR,jstree) importFrom(jsTreeR,jstreeOutput) diff --git a/NEWS.md b/NEWS.md index 8438187..1d7ac03 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,109 +1,117 @@ +# ghqc.app 0.3.0 + +- Updated the custom configuration options repository (now `GHQC_OPTIONS_REPO`) check to reflect the following changes: + - The "note" file within the custom configuration repository is now `prepended_checklist_note` within "options.yaml" + - `checklist_display_name_var` in "options.yaml" provides option to change the name in which the QC checklists are referred to as. + # ghqc.app 0.2.0 Inclusive of 0.1.9, 0.1.10, 0.1.11 changes: -- Adding the branch and a link to the file contents at the start of QC to the Metadata section of Issues created by the Assign app. Also added links to the file contents at the previous and current commits in the metadata section of the resolve app comments. -- Adds "ghqc" label to all GitHub Issues created by ghqc. Only Issues with this label, and the Milestones containing them, will be viewable/selectable in the apps. This change is NOT backward compatible with Issues/Milestones created in previous versions -- Clarifies language in Warning and Error modals for all three apps +- Adding the branch and a link to the file contents at the start of QC to the Metadata section of Issues created by the Assign app. Also added links to the file contents at the previous and current commits in the metadata section of the resolve app comments. +- Adds "ghqc" label to all GitHub Issues created by ghqc. Only Issues with this label, and the Milestones containing them, will be viewable/selectable in the apps. This change is NOT backward compatible with Issues/Milestones created in previous versions +- Clarifies language in Warning and Error modals for all three apps # ghqc.app 0.1.11 -- Adding the branch and a link to the file contents at the start of QC to the Metadata section of Issues created by the Assign app. Also added links to the file contents at the previous and current commits in the metadata section of the resolve app comments. +- Adding the branch and a link to the file contents at the start of QC to the Metadata section of Issues created by the Assign app. Also added links to the file contents at the previous and current commits in the metadata section of the resolve app comments. # ghqc.app 0.1.10 -- Adds "ghqc" label to all GitHub Issues created by ghqc. Only Issues with this label, and the Milestones containing them, will be viewable/selectable in the apps. This change is NOT backward compatible with Issues/Milestones created in previous versions +- Adds "ghqc" label to all GitHub Issues created by ghqc. Only Issues with this label, and the Milestones containing them, will be viewable/selectable in the apps. This change is NOT backward compatible with Issues/Milestones created in previous versions # ghqc.app 0.1.9 -- Clarifies language in Warning and Error modals for all three apps +- Clarifies language in Warning and Error modals for all three apps -# ghqc.app 0.1.8 +# ghqc.app 0.1.8 -- Removes reset button in record app modal for users to click after closing milestones when no closed milestones exist (was buggy in viewer panel) +- Removes reset button in record app modal for users to click after closing milestones when no closed milestones exist (was buggy in viewer panel) # ghqc.app 0.1.7 -- Fixes bug in rendered reports in which a maximum height for logos wasn't set to prevent overlapping header lines and header text. +- Fixes bug in rendered reports in which a maximum height for logos wasn't set to prevent overlapping header lines and header text. # ghqc.app 0.1.6 -- Fixes bug in record app for previewing markdown files (same bug and solution as in 0.1.4) +- Fixes bug in record app for previewing markdown files (same bug and solution as in 0.1.4) # ghqc.app 0.1.5 -- Fixes bug in assign and resolve apps - error checking did not catch remote changes to the directory because checks did not git fetch first +- Fixes bug in assign and resolve apps - error checking did not catch remote changes to the directory because checks did not git fetch first # ghqc.app 0.1.4 -- Fixes bug in resolve app for previewing markdown files +- Fixes bug in resolve app for previewing markdown files # ghqc.app 0.1.3 -- Changes "No Existing Milestones"" to "No Open Milestones" in ghqc_assign_app() Existing Milestones dropdown menu +- Changes "No Existing Milestones"" to "No Open Milestones" in ghqc_assign_app() Existing Milestones dropdown menu # ghqc.app 0.1.2 -- Fixes bug in record app warning modal pop-up for unclosed milestones/issues/checklist items: Milestone link wasn't clickable -- Improves "No closed milestones" modal pop-up warning in record app. Language change to "It is recommended to close milestones" and adds a reset link to modal for users to click after closing milestones. +- Fixes bug in record app warning modal pop-up for unclosed milestones/issues/checklist items: Milestone link wasn't clickable +- Improves "No closed milestones" modal pop-up warning in record app. Language change to "It is recommended to close milestones" and adds a reset link to modal for users to click after closing milestones. # ghqc.app 0.1.1 -- In the case when someone sets the standard gh environment variable GITHUB_API_URL, each app checks if this URL matches the actual set remote URL. The function that gets the GITHUB_API_URL did not explicitly return a value. +- In the case when someone sets the standard gh environment variable GITHUB_API_URL, each app checks if this URL matches the actual set remote URL. The function that gets the GITHUB_API_URL did not explicitly return a value. -- Small grammar fix in error message from apps in the case that the checklists directory isn't in the cloned info repo. +- Small grammar fix in error message from apps in the case that the checklists directory isn't in the cloned info repo. # ghqc.app 0.0.0.9011 -- rename ghqc to ghqc.app and ghqc.launcher to ghqc +- rename ghqc to ghqc.app and ghqc.launcher to ghqc # ghqc 0.0.0.9010 -- terminology changed to match GitHub terminology like "Issue" and "Milestone" +- terminology changed to match GitHub terminology like "Issue" and "Milestone" -- git commit terminology changed from "reference" and "comparator" to "previous" and "current", respectively +- git commit terminology changed from "reference" and "comparator" to "previous" and "current", respectively -- current commit moved to top of metadata section +- current commit moved to top of metadata section -- metadata sections moved to top of issue/comment bodies +- metadata sections moved to top of issue/comment bodies -- "Create" app renamed to "Assign" app +- "Create" app renamed to "Assign" app -- "git sha" in metadata renamed to "initial qc commit" and moved to top of metadata section +- "git sha" in metadata renamed to "initial qc commit" and moved to top of metadata section # ghqc 0.0.0.9009 ## New features -- ghqc pulls client-specific information from a pre-existing repo using the environment variable `GIT_CLIENT_URL`, which is set equal to the https code link to the relevant github repo. +- ghqc pulls client-specific information from a pre-existing repo using the environment variable `GIT_CLIENT_URL`, which is set equal to the https code link to the relevant github repo. # ghqc 0.0.0.9008 ## New features -- ghqcLauncher is now ghqc.launcher to comply with standardized helper package naming conventions. +- ghqcLauncher is now ghqc.launcher to comply with standardized helper package naming conventions. + +- The report function is now a shiny app that can run in ghqc.launcher + +- The git credential authentication function is more robust in each case when + + 1) Git is already authenticated, + 2) Git isn't already authenticated, and + 3) Git is mis-authenticated. + +- Error handling for the git repo and Rproject is now more robust -- The report function is now a shiny app that can run in ghqc.launcher +- The sidebar in `ghqc_create_app()` now scrolls for easy reading of long file and directory names -- The git credential authentication function is more robust in each case when - 1) Git is already authenticated, - 2) Git isn't already authenticated, and - 3) Git is mis-authenticated. +- ghqc can function with multiple remotes set for a given repo, and the app selects the remote in the following hierarchy: -- Error handling for the git repo and Rproject is now more robust + 1) If a single remote exists, it selects it -- The sidebar in `ghqc_create_app()` now scrolls for easy reading of long file and directory names + 2) Else, if multiple remotes exist: -- ghqc can function with multiple remotes set for a given repo, and the app selects the remote in the following hierarchy: - 1) If a single remote exists, it selects it - - 2) Else, if multiple remotes exist: - - - if the environment variable GHQC_REMOTE_NAME exists, it selects the one with that name + - if the environment variable GHQC_REMOTE_NAME exists, it selects the one with that name - - else, if a remote named "origin" exists, it selects it + - else, if a remote named "origin" exists, it selects it - - else, it uses the first remote in the list of remotes + - else, it uses the first remote in the list of remotes # ghqc 0.0.0.9007 diff --git a/R/app_01_assign.R b/R/app_01_assign.R index 985205c..53c9f84 100644 --- a/R/app_01_assign.R +++ b/R/app_01_assign.R @@ -9,7 +9,8 @@ #' @import shiny #' @export ghqc_assign_app <- function() { - if (!exists("info_repo_path", .le)) ghqc_set_info_repo() + if (!exists("config_repo_path", .le)) ghqc_set_config_repo() + get_options() # error handling before starting app root_dir <- rproj_root_dir() diff --git a/R/app_01_assign_server.R b/R/app_01_assign_server.R index a11b940..692dc5c 100644 --- a/R/app_01_assign_server.R +++ b/R/app_01_assign_server.R @@ -11,7 +11,6 @@ NULL ghqc_assign_server <- function(id, remote, root_dir, checklists, org, repo, members, milestone_list) { iv <- shinyvalidate::InputValidator$new() - observe({ req(remote, root_dir) waiter_hide() @@ -205,9 +204,9 @@ return "
" + escape(item.username) + "
" div( style = "display: flex; justify-content: flex-end; padding-bottom: 20px;", actionButton(ns("file_info"), - label = HTML("Preview all available checklists"), + label = HTML(glue::glue("Preview all available {get_checklist_display_name_var(plural = TRUE)}")), class = "preview-button", - style = "min-width: auto; display: inline-block; text-align: center; line-height: 2em; height: 2em;" + style = "min-width: auto; display: inline-block; text-align: right; line-height: 2em; height: 2em;" ) #actionButton ) #div @@ -354,7 +353,7 @@ return "
" + escape(item.username) + "
" } success_note <- { if (custom_checklist_selected()) { - HTML("Issue(s) created successfully.
Remember to manually edit Custom QC checklists on GitHub.") + HTML(glue::glue("Issue(s) created successfully.
Remember to manually edit Custom {get_checklist_display_name_var(plural = TRUE)} on GitHub.")) } else { "Issue(s) created successfully." @@ -382,7 +381,7 @@ return "
" + escape(item.username) + "
" title = tags$div(modalButton("Dismiss"), style = "text-align: right;"), footer = NULL, easyClose = TRUE, - "Each file input will require a checklist type. Each checklist type will have its own items associated with it.", + glue::glue("Each selected file will require a {get_checklist_display_name_var()} type. Each {get_checklist_display_name_var()} type will have its own items associated with it."), "See below for a reference of all types and their items.", br(), br(), @@ -402,7 +401,7 @@ return "
" + escape(item.username) + "
" title = tags$div(modalButton("Dismiss"), style = "text-align: right;"), footer = NULL, easyClose = TRUE, - "Each file input will require a checklist type. Each checklist type will have its own items associated with it.", + glue::glue("Each selected file will require a {get_checklist_display_name_var()} type. Each {get_checklist_display_name_var()} type will have its own items associated with it."), "See below for a reference of all types and their items.", br(), br(), @@ -416,12 +415,12 @@ return "
" + escape(item.username) + "
" output$file_info_panel <- renderUI({ req(checklists) req(input$checklist_info) - debug(.le$logger, glue::glue("Checklist selected for review: {input$checklist_info}")) + debug(.le$logger, glue::glue("{get_checklist_display_name_var(capitalized = TRUE)} selected for review: {input$checklist_info}")) info <- checklists[[input$checklist_info]] log_string <- glue::glue_collapse(info, sep = "\n") - debug(.le$logger, glue::glue("Items found in the checklist: \n{log_string}")) + debug(.le$logger, glue::glue("Items found in the {get_checklist_display_name_var()}: \n{log_string}")) list <- convert_list_to_ui(info) # checklists needs additional formatting for list of named elements diff --git a/R/app_01_assign_utils.R b/R/app_01_assign_utils.R index 8b4019c..86a3477 100644 --- a/R/app_01_assign_utils.R +++ b/R/app_01_assign_utils.R @@ -55,14 +55,15 @@ render_selected_list <- function(input, ns, iv, items = NULL, checklist_choices ) checklist_input <- selectizeInput( - ns(checklist_input_id), - label = NULL, - choices = c("", checklist_choices), #select checklist (required) - width = "100%", - selected = NULL, # Ensures no default selection - options = list(placeholder = "Checklist") + ns(checklist_input_id), + label = NULL, + choices = c("", checklist_choices), + width = "100%", + selected = NULL, + options = list(placeholder = get_checklist_display_name_var(capitalized = TRUE)) ) + button_input <- actionButton( ns(button_input_id), label = HTML("Preview file
contents
"), @@ -73,7 +74,7 @@ render_selected_list <- function(input, ns, iv, items = NULL, checklist_choices preview_input <- actionButton( ns(preview_input_id), - label = HTML("Preview
checklist
"), + label = HTML(glue::glue("Preview
{get_checklist_display_name_var()}
")), style = "height: 34px !important; font-size: 12px !important; padding: 2px 2px 2px 2px !important; color: #5f5f5f !important; line-height: 1.2em", #style = "min-width: auto; display: inline-block; text-align: center; line-height: 2em; height: 2em;", #class = "checklist-preview-button" @@ -226,8 +227,8 @@ convert_list_to_ui <- function(checklists, parent_name = NULL, is_first = TRUE) first_child <- FALSE } } else { - error(.le$logger, glue::glue("Checklist not supported: {checklists}")) - rlang::abort("Unsupported type of checklist") + error(.le$logger, glue::glue("{get_checklist_display_name_var(capitalized = TRUE)} not supported: {checklists}")) + rlang::abort(glue::glue("Unsupported type of {get_checklist_display_name_var()}")) } debug(.le$logger, "Converted list to UI successfully") return(ui_elements) @@ -308,7 +309,7 @@ create_checklist_preview_event <- function(input, iv, ns, name, checklists) { footer = NULL, easyClose = TRUE, renderUI({ - "Select a checklist to preview in the Checklist dropdown." + glue::glue("Select a {get_checklist_display_name_var()} to preview in the {get_checklist_display_name_var(capitalized = TRUE)} dropdown.") }) ) ) diff --git a/R/app_02_resolve.R b/R/app_02_resolve.R index 8086feb..2a54d2c 100644 --- a/R/app_02_resolve.R +++ b/R/app_02_resolve.R @@ -12,7 +12,8 @@ #' @importFrom log4r warn error info debug #' @export ghqc_resolve_app <- function() { - if (!exists("info_repo_path", .le)) ghqc_set_info_repo() + if (!exists("config_repo_path", .le)) ghqc_set_config_repo() + get_options() # error handling before starting app remote <- check_github_credentials() diff --git a/R/app_03_record.R b/R/app_03_record.R index 7f3a7f2..07d837c 100644 --- a/R/app_03_record.R +++ b/R/app_03_record.R @@ -7,7 +7,8 @@ #' @import shiny #' @export ghqc_record_app <- function() { - if (!exists("info_repo_path", .le)) ghqc_set_info_repo() + if (!exists("config_repo_path", .le)) ghqc_set_config_repo() + get_options() # error handling before starting app remote <- check_github_credentials() diff --git a/R/app_03_record_server.R b/R/app_03_record_server.R index bcc2487..af216fc 100644 --- a/R/app_03_record_server.R +++ b/R/app_03_record_server.R @@ -118,7 +118,8 @@ ghqc_record_server <- function(id, remote, org, repo, all_milestones) { actionButton(ns("return"), "Return"), style = "text-align: right;" ), - HTML(paste("It is recommended that relevant GitHub Issues and Milestones are closed upon completion of QC, and checklists within GitHub Issues are checked to indicate QCed items.

You may want to double check the following items for outstanding QC progress:

", modal_check()$message)), + #HTML(paste(glue::glue("It is recommended that all relevant GitHub Issues and Milestones are closed and all {get_checklist_display_name_var()} items within GitHub Issues are checked off to indicate completion of QC.

You may want to double check the following items for outstanding QC progress:

"), modal_check()$message)), + HTML(paste(glue::glue("Upon completion of QC, It is recommended that:
- All selected Milestones are closed
- All Issues within selected Milestones are closed
- All {get_checklist_display_name_var()} items within relevant Issues are completed

You may want to review the following items on GitHub for outstanding QC progress:

"), modal_check()$message)), tags$style(HTML(" .modal-content { word-wrap: break-word; /* Allows long text to break into new lines */ diff --git a/R/app_03_record_utils.R b/R/app_03_record_utils.R index 550100e..aae85cf 100644 --- a/R/app_03_record_utils.R +++ b/R/app_03_record_utils.R @@ -55,8 +55,8 @@ generate_open_issue_message <- function(open_issues, warning_icon_html) { generate_open_checklist_message <- function(issues_with_open_checklists, warning_icon_html) { messages <- c() if (length(issues_with_open_checklists) > 0) { - messages <- c(messages, sprintf( - "%s The selected Milestones contain the following Issues with open checklist items:
    %s

", + messages <- c(messages, sprintf(glue::glue( + "%s The selected Milestones contain the following Issues with open {get_checklist_display_name_var()} items:
    %s

"), warning_icon_html, generate_tiered_html_list_with_hyperlink(issues_with_open_checklists) )) } @@ -79,7 +79,7 @@ determine_modal_message_report <- function(owner, repo, milestone_names) { log_string <- glue::glue("Modal Check Inputs: - Open Milestones: {glue::glue_collapse(open_milestones, sep = ', ')} - Open Issues: {glue::glue_collapse(open_issues, sep = ', ')} - - Issues with unchecked checklist items: {glue::glue_collapse(open_checklists, sep = ', ')} + - Issues with unchecked {get_checklist_display_name_var()} items: {glue::glue_collapse(open_checklists, sep = ', ')} ") log4r::debug(.le$logger, log_string) diff --git a/R/config_repo.R b/R/config_repo.R new file mode 100644 index 0000000..3ee2d65 --- /dev/null +++ b/R/config_repo.R @@ -0,0 +1,50 @@ +#' set the repo that stores the ghqc info +#' @param repo_path path to the git repo storing ghqc information +#' @export +ghqc_set_config_repo <- function(repo_path = file.path("~/.local/share/ghqc", config_repo_name())) { + not_files <- NULL + if (!file.exists(file.path(repo_path, "checklists"))) { + error(.le$logger, glue::glue("The 'checklists' directory is not found in {repo_path}. Please ensure the directory is present before continuing")) + rlang::abort(glue::glue("The 'checklists' directory is not found in {repo_path}. Please ensure the directory is present before continuing")) + } + + checklist_content <- fs::dir_ls(file.path(repo_path, "checklists"), regexp = "[.]yaml$") + if (length(checklist_content) == 0) { + error(.le$logger, glue::glue("The 'checklists' directory in {repo_path} has no .yaml files. Please ensure the directory is populated before continuing")) + rlang::abort(glue::glue("The 'checklists' directory in {repo_path} has no .yaml files. Please ensure the directory is populated before continuing")) + } + + if (all(grepl("INVALID - ", basename(checklist_content)))) { + error(.le$logger, glue::glue("The 'checklists' directory in {repo_path} has no valid checklists. Please ensure the checklists are properly formatted before continuing")) + rlang::abort(glue::glue("The 'checklists' directory in {repo_path} has no valid checklists. Please ensure the checklists are properly formatted before continuing")) + } + + assign("config_repo_path", repo_path, envir = .le) +} + +check_ghqc_config_repo_exists <- function() { + if (!file.exists("~/.Renviron")) config_repo_not_found() + readRenviron("~/.Renviron") + config_repo <- Sys.getenv("GHQC_CONFIG_REPO") + if (config_repo == "") config_repo_not_found() + if (substr(config_repo, 1, 8) != "https://") { + error(.le$logger, glue::glue("GHQC_CONFIG_REPO ({config_repo}) does not start with 'https://'")) + rlang::abort(sprintf("GHQC_CONFIG_REPO (%s) does not start with 'https://'"), config_repo) + } +} + +config_repo_name <- function() { + gsub(".git", "", basename(config_repo_url())) +} + +config_repo_url <- function() { + check_ghqc_config_repo_exists() + Sys.getenv("GHQC_CONFIG_REPO") +} + +config_repo_not_found <- function() { + error(.le$logger, "GHQC_CONFIG_REPO not found. Please set in ~/.Renviron") + rlang::abort(message = "GHQC_CONFIG_REPO not found. Please set in ~/.Renviron") +} + + diff --git a/R/create_yaml.R b/R/create_yaml.R index 26351b2..b2bc5ac 100644 --- a/R/create_yaml.R +++ b/R/create_yaml.R @@ -1,5 +1,5 @@ get_checklists <- function() { - checklists_path <- file.path(.le$info_repo_path, "checklists") + checklists_path <- file.path(.le$config_repo_path, "checklists") yaml_checklists <- list.files(checklists_path, pattern = "\\.ya?ml$", full.names = TRUE) custom_checklist <- system.file("default_checklist", "custom.yaml", package = "ghqc.app") yaml_checklists <- c(yaml_checklists, custom_checklist) @@ -11,7 +11,7 @@ get_checklists <- function() { basename(invalid_checklist) %>% stringr::str_remove("\\.ya?ml$") %>% stringr::str_remove("INVALID - ") }) invalid_checklist_names_col <- glue::glue_collapse(invalid_checklist_names, sep = ", ", last = " and ") - warn(.le$logger, glue::glue("The following checklist(s) are invalid and will therefore not be selectable in the app: {invalid_checklist_names_col}. Run check_ghqc_configuration() for guidance.")) + warn(.le$logger, glue::glue("The following {get_checklist_display_name_var()}(s) are invalid and will therefore not be selectable in the app: {invalid_checklist_names_col}. Run check_ghqc_configuration() for guidance.")) # remove bad checklists yaml_checklists <- yaml_checklists[!invalid_search] diff --git a/R/custom_options.R b/R/custom_options.R new file mode 100644 index 0000000..24d8fea --- /dev/null +++ b/R/custom_options.R @@ -0,0 +1,60 @@ +get_checklist_display_name_var <- function(capitalized = FALSE, plural = FALSE) { + checklist_display_name_var <- .le$checklist_display_name_var + + if (is.null(checklist_display_name_var)) { + checklist_display_name_var <- "checklist" + } + + if (plural) { + checklist_display_name_var <- pluralize(checklist_display_name_var) + } + + if (capitalized) { + checklist_display_name_var <- capitalize(checklist_display_name_var) + } + + return(checklist_display_name_var) +} + +get_prepended_checklist_note <- function() { + prepended_checklist_note <- .le$prepended_checklist_note + + # if not given, make the empty string + if (is.null(prepended_checklist_note)) { + prepended_checklist_note <- "" + } + + # if given, append with a newline + else { + prepended_checklist_note <- paste0(prepended_checklist_note, "\n") + } + + return(prepended_checklist_note) +} + +get_options <- function() { + # put in tryCatch because file may not exist + options_yaml <- file.path(.le$config_repo_path, "options.yaml") + if (!fs::file_exists(options_yaml)) return() + tryCatch({ + options <- unlist(yaml::read_yaml(options_yaml)) + + lapply(names(options), function(option_key) { + option_value <- options[[option_key]] + assign(option_key, option_value, envir = .le) + }) + }, error = function(e) { + error("option.yaml could not be read") + rlang::abort(parent = e$parent) + }) +} + +capitalize <- function(word) { + first_letter <- toupper(substring(word, 1, 1)) + rest_of_word <- substring(word, 2) + glue::glue("{first_letter}{rest_of_word}") +} + +pluralize <- function(word) { + glue::glue("{word}s") +} diff --git a/R/format.R b/R/format.R index 9cab38d..40801e6 100644 --- a/R/format.R +++ b/R/format.R @@ -4,7 +4,7 @@ format_issue_body <- function(checklist_type, file_path) { file_items <- checklists[[checklist_type]] qc_checklist <- format_checklist_items(file_items) metadata <- format_metadata(checklist_type, file_path) - note <- format_note() + note <- get_prepended_checklist_note() issue_body_content <- format_body_content(metadata = metadata, checklist_type = checklist_type, @@ -180,23 +180,6 @@ get_file_contents_url <- function(file_path, git_sha) { file.path(https_url, "blob", substr(git_sha, 1, 6), file_path) } -format_note <- function() { - note <- { - if (file.exists(file.path(.le$info_repo_path, "note"))) { - custom_note <- readr::read_file(file.path(.le$info_repo_path, "note")) - if (stringr::str_sub(custom_note, start =-2) != "\n") { - custom_note <- paste0(custom_note,"\n") - } - custom_note - } - else { - "" - } - } - - return(note) -} - format_body_content <- function(metadata, checklist_type, note, qc_checklist) { glue::glue("## Metadata\n\n {metadata}\n\n diff --git a/R/git_client_info.R b/R/git_client_info.R deleted file mode 100644 index 4bafcbd..0000000 --- a/R/git_client_info.R +++ /dev/null @@ -1,102 +0,0 @@ -.lci <- new.env() - -#' @importFrom log4r warn error info debug -get_client_git_url <- function() { - git_url <- Sys.getenv("GIT_CLIENT_URL") - - # error if CLIENT_INFO_URL not set - if (length(git_url) == 0){ - error(.le$logger, "No client github url found. Please set GIT_CLIENT_URL environmental variable, likely in your ~/.Renviron file.") - rlang::abort(message = "No client github url found. Please set GIT_CLIENT_URL environmental variable, likely in your ~/.Renviron file.") - } - - # error if not https - url_starts_with_https <- stringr::str_starts(git_url, "https://") - if (!url_starts_with_https) { - error(.le$logger, glue::glue("Retrieved GIT_CLIENT_URL: {git_url} does not start with https")) - rlang::abort(message = glue::glue("Retrieved GIT_CLIENT_URL: {git_url} does not start with https")) - } - git_url -} - -#' @importFrom log4r warn error info debug -check_client_local <- function(git_url) { - client_repo_name <- get_remote_name(git_url) - client_repo_path <- file.path("~",client_repo_name) - - if (!file.exists(client_repo_path)) { - # Case 1: Repo not in home dir, cloning down - debug(.le$logger, glue::glue("{client_repo_name} not found in home directory. Attempting to clone {git_url} to {client_repo_path}...")) - tryCatch( - { - gert::git_clone(git_url, path = client_repo_path) - info(.le$logger, glue::glue("Successfully cloned {client_repo_name}")) - }, error = function(e) { - error(.le$logger, glue::glue("Clone of {client_repo_name} was not successful")) - rlang::abort(message = e$message) - } - ) - } # if client repo not cloned - else { - remote_updates <- remote_repo_updates(client_repo_path) - local_updates <- local_repo_updates(client_repo_path) - - # if local changes - if (local_updates) { - stash <- gert::git_stash_save(repo = client_repo_path) - info(.le$logger, glue::glue("Stashed local changes to {client_repo_path}")) - } - - # if remote changes - if (remote_updates) { - # Case 2: Repo in home dir, but local or remote changes - debug(.le$logger, "Most recent remote commit ({remote_commit_id}) does not match local commit ({local_commit_id}). Attempting to pull down update to {client_repo_path}...") - tryCatch( - { - gert::git_pull(repo = client_repo_path) - info(.le$logger, glue::glue("Update has been successfully pulled down to {client_repo_path}")) - }, error = function(e) { - error(.le$logger, glue::glue("Update was unsuccessfully pulled down. Attempted to pull {client_repo_name} remote commit id {remote_commit_id} to {client_repo_path}")) - rlang::abort(.le$logger, message = e$message) - } - ) - } - - if (!remote_updates && !local_updates) { - info(.le$logger, "No local or remote updates to ghqc storage repo") - } - - } # else, client dir has already been cloned - - return(client_repo_path) -} - -#' @importFrom log4r warn error info debug -local_repo_updates <- function(client_repo_path) { - status <- gert::git_status(repo = client_repo_path) - local_repo_updates <- "modified" %in% status$status - if (local_repo_updates) { - info(.le$logger, glue::glue("Detected local changes to {client_repo_path}")) - } - return(local_repo_updates) -} - -#' @importFrom log4r warn error info debug -remote_repo_updates <- function(client_repo_path) { - remote_commit_id <- gert::git_remote_ls(repo = client_repo_path)$oid[1] - local_commit_id <- gert::git_info(repo = client_repo_path)$commit - remote_repo_updates <- remote_commit_id != local_commit_id - return(remote_repo_updates) -} - -#' @importFrom log4r warn error info debug -load_client_info <- function(){ - if (file.exists("~/.Renviron")) readRenviron("~/.Renviron") - - # get client url from ~./Renviron - git_url <- get_client_git_url() - - # check if client local is cloned and most up to date commit - client_repo_path <- check_client_local(git_url) - assign("client_repo_path", client_repo_path, envir = .lci) -} diff --git a/R/info_repo.R b/R/info_repo.R deleted file mode 100644 index 68a403d..0000000 --- a/R/info_repo.R +++ /dev/null @@ -1,40 +0,0 @@ -#' set the repo that stores the ghqc info -#' @param repo_path path to the git repo storing ghqc information -#' @export -ghqc_set_info_repo <- function(repo_path = file.path("~/.local/share/ghqc", info_repo_name())) { - not_files <- NULL - if (!file.exists(file.path(repo_path, "checklists"))) not_files <- append(not_files, "Checklists directory") - if (!file.exists(file.path(repo_path, "logo.png"))) not_files <- append(not_files, "logo.png") - if (!is.null(not_files)) info_repo_files_not_found(not_files, repo_path) - assign("info_repo_path", repo_path, envir = .le) -} - -info_repo_files_not_found <- function(not_files, repo_path) { - error(.le$logger, glue::glue("{paste(not_files, collapse = ' and ')} not found in {repo_path}. Please ensure file(s) are present before continuing")) - rlang::abort(glue::glue("{paste(not_files, collapse = ' and ')} not found in {repo_path}. Please ensure file(s) are present before continuing")) -} - -check_ghqc_info_repo_exists <- function() { - if (!file.exists("~/.Renviron")) info_repo_not_found() - readRenviron("~/.Renviron") - info_repo <- Sys.getenv("GHQC_INFO_REPO") - if (info_repo == "") info_repo_not_found() - if (substr(info_repo, 1, 8) != "https://") { - error(.le$logger, glue::glue("GHQC_INFO_REPO ({info_repo}) does not start with 'https://'")) - rlang::abort(sprintf("GHQC_INFO_REPO (%s) does not start with 'https://'"), info_repo) - } -} - -info_repo_name <- function() { - gsub(".git", "", basename(info_repo_url())) -} - -info_repo_url <- function() { - check_ghqc_info_repo_exists() - Sys.getenv("GHQC_INFO_REPO") -} - -info_repo_not_found <- function() { - error(.le$logger, "GHQC_INFO_REPO not found. Please set in ~/.Renviron") - rlang::abort(message = "GHQC_INFO_REPO not found. Please set in ~/.Renviron") -} diff --git a/R/multi_issue.R b/R/multi_issue.R index a6d5827..1797173 100644 --- a/R/multi_issue.R +++ b/R/multi_issue.R @@ -62,12 +62,12 @@ create_issues <- function(data) { file_names <- glue::glue_collapse(purrr::map(data$files, "name"), sep = ", ", last = " and ") - debug(.le$logger, glue::glue("Creating checklists for files: {file_names}")) + debug(.le$logger, glue::glue("Creating {get_checklist_display_name_var(plural = TRUE)} for files: {file_names}")) # create an issue for each file lapply(data$files, function(file) { issue <- create_issue(file, issue_params) - debug(.le$logger, glue::glue("Created checklist for file: {file$name}")) + debug(.le$logger, glue::glue("Created {get_checklist_display_name_var()} for file: {file$name}")) if (!is.null(data$milestone)) { debug(.le$logger, glue::glue("Milestone: {data$milestone}")) } @@ -77,7 +77,7 @@ create_issues <- function(data) { debug(.le$logger, glue::glue("Issue number: {issue$number}")) return(issue) }) - info(.le$logger, glue::glue("Created checklist(s) for file(s): {file_names}")) + info(.le$logger, glue::glue("Created {get_checklist_display_name_var()}(s) for file(s): {file_names}")) } # create_issues #' @importFrom gh gh diff --git a/R/pre_app_errors.R b/R/pre_app_errors.R index 4c47d97..a23f122 100644 --- a/R/pre_app_errors.R +++ b/R/pre_app_errors.R @@ -18,7 +18,7 @@ get_valid_checklists <- function() { yaml_checklists <- get_checklists() }, error = function(e) { - error(.le$logger, glue::glue("There was an error retrieving checklists: {e$message}")) + error(.le$logger, glue::glue("There was an error retrieving {get_checklist_display_name_var(plural = TRUE)}: {e$message}")) rlang::abort(e$message) } ) diff --git a/R/scrape.R b/R/scrape.R index 6216d8d..b3c6744 100644 --- a/R/scrape.R +++ b/R/scrape.R @@ -355,8 +355,27 @@ create_intro <- function(repo, milestone_names) { author <- Sys.info()[["user"]] date <- format(Sys.Date(), '%B %d, %Y') milestone_names_list <- glue::glue_collapse(milestone_names, sep = ", ") - # - image_path <- file.path(.le$info_repo_path, "logo.png") + + image_path <- file.path(.le$config_repo_path, "logo.png") + + logo_exists_extra_header <- { + if (fs::file_exists(image_path)) { + glue::glue(" + - \\fancyhead[R]{{\\includegraphics[width=2cm, height=1.5cm, keepaspectratio]{{{image_path}}}}} + - \\fancyhead[C]{{}} + - \\fancyhead[L]{{}} + - \"\\\\fancypagestyle{{plain}}{{\" + - \"\\\\renewcommand{{\\\\headrulewidth}}{{0.4pt}}\" + - \"}}\"", .trim = FALSE) + } + else { + glue::glue(" + - \\fancyhead[R]{{}} + - \\fancyhead[C]{{}} + - \\fancyhead[L]{{}}", .trim = FALSE) + } + } + intro <- glue::glue( "--- title: \"QC Record: {milestone_names_list}\" @@ -375,17 +394,10 @@ create_intro <- function(repo, milestone_names) { - \\newcolumntype{{R}}[1]{{>{{\\raggedright\\arraybackslash}}p{{#1}}}} - \\newcommand{{\\blandscape}}{{\\begin{{landscape}}}} - \\newcommand{{\\elandscape}}{{\\end{{landscape}}}} - - \\fancyhead[R]{{\\includegraphics[width=2cm, height=1.5cm, keepaspectratio]{{{image_path}}}}} - - \\fancyhead[C]{{}} - - \\fancyhead[L]{{}} - \\setlength{{\\headheight}}{{30pt}} - \\fancyfoot[C]{{Page \\thepage\\ of \\pageref{{LastPage}}}} - \\usepackage{{lastpage}} - - \\lstset{{breaklines=true}} - - \"\\\\fancypagestyle{{plain}}{{\" - - \"\\\\fancyhead[R]{{\\\\includegraphics[width=2cm, height=1.5cm, keepaspectratio]{{{image_path}}}}}\" - - \"\\\\renewcommand{{\\\\headrulewidth}}{{0.4pt}}\" - - \"}}\" + - \\lstset{{breaklines=true}}{logo_exists_extra_header} output: pdf_document: latex_engine: xelatex @@ -398,6 +410,8 @@ create_intro <- function(repo, milestone_names) { \\newpage ") + + return(intro) } diff --git a/R/validation.R b/R/validation.R index 61493fa..a052342 100644 --- a/R/validation.R +++ b/R/validation.R @@ -55,7 +55,7 @@ validate_description <- function(description) { } # validate description validate_checklist_type <- function(checklist_type, file_name) { - #err_if_invalid_checklist_type(checklist_type, file_name) + } validate_items <- function(items) { diff --git a/R/validation_errors.R b/R/validation_errors.R index da26183..484b501 100644 --- a/R/validation_errors.R +++ b/R/validation_errors.R @@ -86,13 +86,5 @@ err_if_not_list_of_lists <- function(files) { } } -err_if_invalid_checklist_type <- function(checklist_type, file_name) { - valid_checklist_types <- c("R", "mod", "cpp", "vpc") - if (!checklist_type %in% valid_checklist_types) { - rlang::abort(message = glue::glue("{checklist_type} not a valid checklist type for file {file_name}"), - class = "input_error", - x = checklist_type) - } -} diff --git a/inst/css/styles.css b/inst/css/styles.css index 9c3849f..41f9a78 100644 --- a/inst/css/styles.css +++ b/inst/css/styles.css @@ -140,15 +140,16 @@ div[id$="-main_container"] { .grid-items { display: grid; column-gap: 10px; - grid-template-columns: 67px minmax(100px, 1fr) minmax(125px, 1fr) 50px; /*minmax(100px, auto) */ + grid-template-columns: 67px minmax(100px, 1fr) minmax(125px, 1fr) minmax(50px, auto); /*minmax(100px, auto) */ margin-bottom: 15px; + width: 100%; + transition: 0.5s; } -/* -.item-a { + +.item-b { white-space: nowrap; } -*/ .flex-container { display: flex; diff --git a/inst/js/adjust_grid.js b/inst/js/adjust_grid.js index 31c5375..534d25a 100644 --- a/inst/js/adjust_grid.js +++ b/inst/js/adjust_grid.js @@ -49,7 +49,7 @@ export function adjust_grid(ns) { rows.forEach((row, index) => { if (row) { row.style.transition = 'all 0.5s ease'; - row.style.gridTemplateColumns = `67px minmax(125px, 1fr) minmax(125px, 1fr) 50px`; //minmax(100px, ${maxWidth}px) + //row.style.gridTemplateColumns = `67px minmax(125px, 1fr) minmax(125px, 1fr) minmax(50px, auto)`; // ${maxWidth}px } else { console.warn(`Row ${index} is undefined`); } diff --git a/man/ghqc_set_info_repo.Rd b/man/ghqc_set_config_repo.Rd similarity index 65% rename from man/ghqc_set_info_repo.Rd rename to man/ghqc_set_config_repo.Rd index 12c771c..2920c20 100644 --- a/man/ghqc_set_info_repo.Rd +++ b/man/ghqc_set_config_repo.Rd @@ -1,11 +1,11 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/info_repo.R -\name{ghqc_set_info_repo} -\alias{ghqc_set_info_repo} +\name{ghqc_set_config_repo} +\alias{ghqc_set_config_repo} \title{set the repo that stores the ghqc info} \usage{ -ghqc_set_info_repo( - repo_path = file.path("~/.local/share/ghqc", info_repo_name()) +ghqc_set_config_repo( + repo_path = file.path("~/.local/share/ghqc", config_repo_name()) ) } \arguments{