From 1176ab6a84e1048387e5901ace89cc24bff55b19 Mon Sep 17 00:00:00 2001 From: Giorgio Comai Date: Thu, 25 Jan 2024 12:18:44 +0100 Subject: [PATCH] more checks on inputs, provides meaningful error messages, fixes #11 --- DESCRIPTION | 4 +- NAMESPACE | 1 + R/gantt_verify.R | 78 ++++++++++++++++++++++++++++++ R/ganttrify.R | 6 +++ man/gantt_verify.Rd | 29 +++++++++++ tests/testthat.R | 12 +++++ tests/testthat/test-gantt_verify.R | 47 ++++++++++++++++++ 7 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 R/gantt_verify.R create mode 100644 man/gantt_verify.Rd create mode 100644 tests/testthat.R create mode 100644 tests/testthat/test-gantt_verify.R diff --git a/DESCRIPTION b/DESCRIPTION index 4421fd6..d94e877 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: ganttrify Title: Create beautiful Gantt charts with ggplot2 -Version: 0.0.0.9014 +Version: 0.0.0.9015 Authors@R: person(given = "Giorgio", family = "Comai (OBCT/CCI)", @@ -11,7 +11,7 @@ Description: 'ganttrify' facilitates the creation of nice-looking Gantt charts, License: GPL-3 Encoding: UTF-8 LazyData: true -RoxygenNote: 7.3.0 +RoxygenNote: 7.3.1 Imports: magrittr, tibble, diff --git a/NAMESPACE b/NAMESPACE index 2f4ecc4..3db902b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,6 +1,7 @@ # Generated by roxygen2: do not edit by hand export("%>%") +export(gantt_verify) export(ganttrify) export(shiny_ganttrify) importFrom(magrittr,"%>%") diff --git a/R/gantt_verify.R b/R/gantt_verify.R new file mode 100644 index 0000000..92b71e2 --- /dev/null +++ b/R/gantt_verify.R @@ -0,0 +1,78 @@ +#' Check the consistency of the input project data frame +#' +#' Check the consistency of the input project data frame, return meaningful errors or warnings if something is not quite right +#' +#' @inheritParams ganttrify +#' +#' @return A data frame (a tibble) that is consistent with the format expected by [ganttrify()]. +#' @export +#' +#' @examples +#' gantt_verify(project = ganttrify::test_project) +gantt_verify <- function(project, + by_date = FALSE, + exact_date = FALSE) { + if (is.data.frame(project) == FALSE) { + cli::cli_abort("{.arg project} must be a data frame.") + } + + if (ncol(project) < 4) { + cli::cli_abort("{.arg project} must must have (at least) four columns.") + } + + project <- tibble::as_tibble(project) + + if (identical(colnames(project)[1:4], colnames(ganttrify::test_project)) == FALSE) { + cli::cli_warn(c( + x = "{.arg project} is expected to have (at least) four columns, in this order: {stringr::str_flatten_comma(string = colnames(ganttrify::test_project))}.", + i = "The first four columns of this data frame will be treated as such, even if column names are different." + )) + colnames(project)[1:4] <- colnames(ganttrify::test_project) + } + + + if (by_date) { + project <- project %>% + dplyr::mutate( + wp = as.character(wp), + activity = as.character(activity), + start_date = as.character(start_date), + end_date = as.character(end_date) + ) + } else { + project <- project %>% + dplyr::mutate( + wp = as.character(wp), + activity = as.character(activity), + start_date = as.numeric(start_date), + end_date = as.numeric(end_date) + ) + } + + if (exact_date) { + project <- project %>% + dplyr::mutate( + wp = as.character(wp), + activity = as.character(activity), + start_date = lubridate::as_date(start_date), + end_date = lubridate::as_date(end_date) + ) + } + + na_count_v <- sapply(X = project[1:4], FUN = function(x) sum(is.na(x))) + + if (sum(na_count_v) > 0) { + project_pre_nrow_v <- nrow(project) + project <- tidyr::drop_na(project) + project_post_nrow_v <- nrow(project) + + effective_na_v <- na_count_v[na_count_v > 0] + + cli::cli_warn(message = c( + x = "{.val {sum(effective_na_v)}} missing values or wrong format found in the following column{?s}: {.field {stringr::str_flatten_comma(names(effective_na_v))}}", + i = "{.val {project_pre_nrow_v-project_post_nrow_v}} rows with invalid values have been dropped." + )) + } + + project +} diff --git a/R/ganttrify.R b/R/ganttrify.R index 4a5f21a..cd84d76 100644 --- a/R/ganttrify.R +++ b/R/ganttrify.R @@ -139,6 +139,12 @@ ganttrify <- function(project, month_breaks = 1, show_vertical_lines = TRUE, axis_text_align = "right") { + project <- gantt_verify( + project = project, + by_date = by_date, + exact_date = exact_date + ) + # arguments consistency check if (hide_wp & hide_activities) { cli::cli_abort("At least one of {.arg hide_wp} or {.arg hide_activities} must be {.code TRUE}, otherwise there's nothing left to show.") diff --git a/man/gantt_verify.Rd b/man/gantt_verify.Rd new file mode 100644 index 0000000..eeda3ed --- /dev/null +++ b/man/gantt_verify.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/gantt_verify.R +\name{gantt_verify} +\alias{gantt_verify} +\title{Check the consistency of the input project data frame} +\usage{ +gantt_verify(project, by_date = FALSE, exact_date = FALSE) +} +\arguments{ +\item{project}{A data frame. See `ganttrify::test_project` for an example.} + +\item{by_date}{Logical, defaults to FALSE If FALSE, the the start and end +columns in the data frame should correspond to month numbers from the +beginning of the project. If TRUE, dates in the format ("2020-10" or +"2020-10-01") should be given.} + +\item{exact_date}{Logical, defaults to FALSE. If FALSE, then periods are +always understood to include full months. If FALSE, then exact dates can be +given.} +} +\value{ +A data frame (a tibble) that is consistent with the format expected by [ganttrify()]. +} +\description{ +Check the consistency of the input project data frame, return meaningful errors or warnings if something is not quite right +} +\examples{ +gantt_verify(project = ganttrify::test_project) +} diff --git a/tests/testthat.R b/tests/testthat.R new file mode 100644 index 0000000..bfe63b0 --- /dev/null +++ b/tests/testthat.R @@ -0,0 +1,12 @@ +# This file is part of the standard setup for testthat. +# It is recommended that you do not modify it. +# +# Where should you do additional test configuration? +# Learn more about the roles of various files in: +# * https://r-pkgs.org/testing-design.html#sec-tests-files-overview +# * https://testthat.r-lib.org/articles/special-files.html + +library(testthat) +library(ganttrify) + +test_check("ganttrify") diff --git a/tests/testthat/test-gantt_verify.R b/tests/testthat/test-gantt_verify.R new file mode 100644 index 0000000..55d24d5 --- /dev/null +++ b/tests/testthat/test-gantt_verify.R @@ -0,0 +1,47 @@ +test_that("NAs are caught by gantt_verify", { + expect_warning(object = { + project <- data.frame( + wp = letters[1:3], + activity = month.name[1:3], + start_date = 1:3, + end_date = 4:6 + ) + + project[2, 2] <- NA_character_ + gantt_verify(project) + }) + + expect_identical( + object = { + project <- data.frame( + wp = letters[1:3], + activity = month.name[1:3], + start_date = 1:3, + end_date = 4:6 + ) + + project[2, 2] <- NA_character_ + + suppressWarnings(nrow(gantt_verify(project))) + }, + expected = 2L + ) + + + + expect_identical( + object = { + project <- data.frame( + wp = letters[1:3], + activity = month.name[1:3], + start_date = 1:3, + end_date = 4:6 + ) + + project[2:3, 2] <- NA_character_ + + suppressWarnings(nrow(gantt_verify(project))) + }, + expected = 1L + ) +})