diff --git a/.Rbuildignore b/.Rbuildignore index 12521c3..2d564f9 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -12,3 +12,5 @@ ^_pkgdown\.yml$ ^docs$ ^pkgdown$ +^data-raw$ +^vignettes/articles$ diff --git a/.gitignore b/.gitignore index 75fff09..7b3f3ad 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ .secrets .quarto docs +inst/doc diff --git a/.lintr b/.lintr index 4c5d639..9f054ae 100644 --- a/.lintr +++ b/.lintr @@ -1,4 +1,13 @@ linters: linters_with_defaults( line_length_linter = line_length_linter(120L), - commented_code_linter = NULL + commented_code_linter = NULL, + object_length_linter(length = 50L) + ) +exclusions: list( + "R/validate-config-utils.R" = list( + line_length_linter = Inf + ), + "tests/testthat/helper-view-val-tbl.R" = list( + object_usage_linter = Inf + ) ) diff --git a/DESCRIPTION b/DESCRIPTION index 0a722d7..2c4a209 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: hubAdmin Title: Utilities for administering hubverse Hubs -Version: 0.0.0.9000 +Version: 0.0.1 Authors@R: c(person("Anna", "Krystalli", , "annakrystalli@googlemail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-2378-4915")), @@ -15,9 +15,38 @@ License: MIT + file LICENSE Encoding: UTF-8 Roxygen: list(markdown = TRUE) RoxygenNote: 7.3.1 -Suggests: - testthat (>= 3.0.0) Config/testthat/edition: 3 URL: https://github.com/Infectious-Disease-Modeling-Hubs/hubAdmin BugReports: https://github.com/Infectious-Disease-Modeling-Hubs/hubAdmin/issues +Imports: + checkmate, + cli, + fs, + glue, + gt (>= 0.10.0), + hubUtils (>= 0.0.1), + jsonlite, + jsonvalidate, + magrittr, + purrr, + rlang, + stringr, + tibble +Suggests: + covr, + curl, + digest, + gh, + hubData, + knitr, + mockery, + rmarkdown, + testthat (>= 3.2.0) +Remotes: + Infectious-Disease-Modeling-Hubs/hubData#1, + Infectious-Disease-Modeling-Hubs/hubUtils@split-hubutils Config/Needs/website: Infectious-Disease-Modeling-Hubs/hubStyle +Depends: + R (>= 2.10) +LazyData: true +VignetteBuilder: knitr diff --git a/NAMESPACE b/NAMESPACE index 6ae9268..6bee1a9 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,2 +1,24 @@ # Generated by roxygen2: do not edit by hand +export("%>%") +export(create_config) +export(create_model_task) +export(create_model_tasks) +export(create_output_type) +export(create_output_type_cdf) +export(create_output_type_mean) +export(create_output_type_median) +export(create_output_type_pmf) +export(create_output_type_quantile) +export(create_output_type_sample) +export(create_round) +export(create_rounds) +export(create_target_metadata) +export(create_target_metadata_item) +export(create_task_id) +export(create_task_ids) +export(validate_config) +export(validate_hub_config) +export(validate_model_metadata_schema) +export(view_config_val_errors) +importFrom(magrittr,"%>%") diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..5658170 --- /dev/null +++ b/NEWS.md @@ -0,0 +1,3 @@ +# hubAdmin 0.0.1 + +* Initial package release resulting from split of `hubUtils` package. See [`hubUtils` NEWS.md](https://github.com/Infectious-Disease-Modeling-Hubs/hubUtils/blob/main/NEWS.md) for details including previous release notes. diff --git a/R/check_input.R b/R/check_input.R new file mode 100644 index 0000000..7765013 --- /dev/null +++ b/R/check_input.R @@ -0,0 +1,438 @@ +#' Check that inputs to create_* functions conform to hubverse schema. +#' Function used primarily to check that properties of inputs to the create_* +#' family of functions, used to create hub config programmatically, conform to +#' expectation defined in the hubverse schema. +#' @param input Vector. The value of the property of the input to be checked. +#' @param property Character string. The name of the property of the input to be checked. +#' @param parent_schema List representation of the schema of the parent object. +#' @param parent_property Character string. The name of the parent object of the property +#' to be checked. +#' @param scalar Logical. whether the property being checked is scalar. +#' @param call Name of the caller fn. For internal error signalling. +#' +#' @return Function used primarily for it's side effects of signalling errors when +#' input does not conform to schema. +#' @noRd +check_input <- function(input, property, parent_schema, # nolint: cyclocomp_linter + parent_property, + scalar = FALSE, + call = rlang::caller_env()) { + property_name <- property # nolint: object_usage_linter + if (!is.null(parent_property) && parent_property == "value") { + property_name <- paste0("value_", property) + } + + property_schema <- parent_schema[[property]] + + if (is.null(input)) { + property_types <- property_schema[["type"]] + if (!"null" %in% property_types) { + cli::cli_abort(c("x" = "{.arg {property_name}} cannot be NULL."), + call = call + ) + } else { + return() + } + } + if (!is.atomic(input) || is.pairlist(input)) { + cli::cli_abort(c("x" = "{.arg {property_name}} must be an atomic vector."), + call = call + ) + } + + if (is.factor(input)) { + cli::cli_abort(c("x" = "{.arg {property_name}} cannot be of class + {.cls factor}."), + call = call + ) + } + + if (scalar) { + if (length(input) != 1L) { + cli::cli_abort( + c( + "x" = "{.arg {property_name}} must be length {.val {1}}, + not {.val {length(input)}}." + ), + call = call + ) + } + } + + if (any(names(property_schema) == "maxItems")) { + if (scalar) { + max_items <- 1 + } else { + max_items <- property_schema[["maxItems"]] + } + is_invalid <- length(input) > max_items + if (is_invalid) { + cli::cli_abort( + c( + "x" = "{.arg {property_name}} must be of length equal to or less than + {.val {max_items}} but is of length {.val {length(input)}}." + ), + call = call + ) + } + } + if (any(names(property_schema) == "minItems")) { + min_items <- property_schema[["minItems"]] + is_invalid <- length(input) < min_items + if (is_invalid) { + cli::cli_abort( + c( + "x" = "{.arg {property_name}} must be of length equal to or greater + than {.val {min_items}} but is of length {.val {length(input)}}." + ), + call = call + ) + } + } + + if (any(names(property_schema) == "uniqueItems")) { + unique_items <- property_schema[["uniqueItems"]] + if (unique_items && any(duplicated(input))) { + duplicates <- input[duplicated(input)] # nolint: object_usage_linter + cli::cli_abort( + c( + "!" = "All values in {.arg {property_name}} must be unique.", + "x" = "{cli::qty(sum(duplicates))} Value{?s} {.val {duplicates}} + {cli::qty(sum(duplicates))} {?is/are} duplicated." + ), + call = call + ) + } + } + + # Array item validation + if (scalar) { + value_schema <- property_schema + } else { + value_schema <- property_schema[["items"]] + } + + input_type <- typeof(input) + value_formats <- value_schema[["format"]] + # Handle situation where type must be missing but can be inferred from `const` or + # `enum` properties. Clunky but included for back compatibility. + if (any(names(value_schema) == "type")) { + value_types <- json_datatypes[value_schema[["type"]]] + } else { + if (any(names(value_schema) == "enum")) { + value_types <- typeof(value_schema[["enum"]]) + } else if (any(names(value_schema) == "const")) { + value_types <- typeof(value_schema[["const"]]) + } else { + value_types <- NULL + cli::cli_warn( + c( + "!" = "Cannot determine appropriate type for argument + {.arg {property_name}}, type validation skipped. + Schema may be invalid. Consult relevant schema and consider opening an issue at + {.url https://github.com/Infectious-Disease-Modeling-Hubs/schemas/issues}" + ), + call = call + ) + } + } + + if (!is.null(value_formats) && value_formats == "date" && typeof(input) != "character") { + cli::cli_abort(c("x" = "{cli::qty(length(input))} {.arg {property_name}} + value{?s} must be character string{?s} of date{?s} in valid + ISO 8601 format (YYYY-MM-DD). Date object format not accepted. + Consider using {.code as.character()} to convert to character."), + call = call + ) + } + + if (!is.null(value_formats) && value_formats == "date" && anyNA(as.Date(input, format = "%Y-%m-%d")) + ) { + cli::cli_abort(c("x" = "{cli::qty(length(input))} {.arg {property_name}} + value{?s} must be character string{?s} of date{?s} in valid + ISO 8601 format (YYYY-MM-DD)."), + call = call + ) + } + + if (!is.null(value_types) && !input_type %in% value_types) { + cli::cli_abort( + c( + "x" = "{.arg {property_name}} is of type {.cls {input_type}}.", + "!" = "Must be {?/one of} {.cls {value_types}}." + ), + call = call + ) + } + + if (any(names(value_schema) == "maximum")) { + value_max <- value_schema[["maximum"]] + is_invalid <- input > value_max + if (any(is_invalid)) { + cli::cli_abort( + c( + "!" = "All values in {.arg {property_name}} must be equal to or less + than {.val {value_max}}.", + "x" = "{cli::qty(sum(is_invalid))} Value{?s} {.val {input[is_invalid]}} + {cli::qty(sum(is_invalid))}{?is/are} greater." + ), + call = call + ) + } + } + + if (any(names(value_schema) == "minimum")) { + value_min <- value_schema[["minimum"]] + is_invalid <- input < value_min + if (any(is_invalid)) { + cli::cli_abort( + c( + "!" = "All values in {.arg {property_name}} must be equal to or greater + than {.val {value_min}}.", + "x" = "{cli::qty(sum(is_invalid))} Value{?s} {.val {input[is_invalid]}} + {cli::qty(sum(is_invalid))}{?is/are} less." + ), + call = call + ) + } + } + + if (any(names(value_schema) == "enum")) { + if (any(!input %in% value_schema[["enum"]])) { + invalid_values <- input[!input %in% value_schema[["enum"]]] # nolint: object_usage_linter + cli::cli_abort( + c( + "x" = "{.arg {property_name}} {cli::qty(length(invalid_values))} + value{?s} {?is/are} invalid.", + "!" = "Must be {cli::qty(if(scalar){1}else{2})} {?one of/member in} + {.val {value_schema[['enum']]}}.", + "i" = "Actual value{?s} {?is/are} {.val {invalid_values}}" + ), + call = call + ) + } + } + + if (any(names(value_schema) == "const")) { + value_const <- value_schema[["const"]] + if (any(input != value_const)) { + cli::cli_abort( + c( + "x" = "{.arg {property_name}} value is invalid.", + "!" = "Must be {.val {value_const}}.", + "i" = "Actual value is {.val {input}}" + ), + call = call + ) + } + } + + if (any(names(value_schema) == "minLength")) { + is_invalid <- stringr::str_length(input) < value_schema[["minLength"]] + if (any(is_invalid)) { + cli::cli_abort( + c( + "!" = "The minimum number of characters allowed for values in + {.arg {property_name}} is {.val {value_schema[['minLength']]}}.", + "x" = "Value{?s} {.val {input[is_invalid]}} {?has/have} + fewer characters than allowed." + ), + call = call + ) + } + } + + if (any(names(value_schema) == "maxLength")) { + is_invalid <- stringr::str_length(input) > value_schema[["maxLength"]] + if (any(is_invalid)) { + cli::cli_abort( + c( + "!" = "The maximum number of characters allowed for values in + {.arg {property_name}} is {.val {value_schema[['maxLength']]}}.", + "x" = "Value{?s} {.val {input[is_invalid]}} {?has/have} + more characters than allowed" + ), + call = call + ) + } + } + + + if (any(names(value_schema) == "multipleOf")) { + is_invalid <- input %% value_schema[["multipleOf"]] != 0L + if (any(is_invalid)) { + cli::cli_abort( + c( + "!" = "Values in {.arg {property_name}} must be multiples of + {.val {value_schema[['multipleOf']]}}.", + "x" = "{cli::qty(sum(is_invalid))} Value{?s} + {.val {input[is_invalid]}} {cli::qty(sum(is_invalid))} {?is/are} not." + ), + call = call + ) + } + } +} + +# Used to check properties that contain oneOf schema properties. +check_oneof_input <- function(input, property = c("required", "optional"), # nolint: cyclocomp_linter + parent_schema, + call = rlang::caller_env()) { + property_schema <- parent_schema[[property]] + + if (is.null(input)) { + property_types <- property_schema[["type"]] + if (!"null" %in% property_types) { + cli::cli_abort(c("x" = "{.arg {property}} cannot be NULL."), + call = call + ) + } else { + return() + } + } + if (!is.atomic(input) || is.pairlist(input)) { + cli::cli_abort(c("x" = "{.arg {property}} must be an atomic vector."), + call = call + ) + } + + if (is.factor(input)) { + cli::cli_abort(c("x" = "{.arg {property}} cannot be of class {.cls factor}."), + call = call + ) + } + + oneof_schema <- property_schema[["items"]][["oneOf"]] + + oneof_schema <- purrr::map( + oneof_schema, + function(x) { + type <- x$type + if (length(type) > 1L) { + x <- rep(list(x), length(type)) + names(x) <- json_datatypes[type] + x + } else { + x <- list(x) + names(x) <- json_datatypes[type] + x + } + } + ) %>% + unlist(recursive = FALSE) + + value_types <- c("character", "double", "integer") + input_type <- typeof(input) + if (!input_type %in% value_types) { + cli::cli_abort( + c( + "x" = "{.arg {property}} is of type {.cls {input_type}}.", + "!" = "Must be {?/one of} {.cls {value_types}}." + ), + call = call + ) + } + + if (typeof(input) == "character") { + value_schema <- oneof_schema[["character"]] + if (!any((grepl(value_schema[["pattern"]], input)))) { + cli::cli_abort( + c( + "x" = "Values of {.arg {property}} must match regex pattern + {.val {value_schema[['pattern']]}}.", + "!" = 'Values {.val {!(grepl(value_schema[["pattern"]], input))}} do not.' + ), + call = call + ) + } + + is_too_long <- stringr::str_length(input) > value_schema[["maxLength"]] + if (any(is_too_long)) { + cli::cli_abort( + c( + "!" = "The maximum number of characters allowed for values in + {.arg {property}} is {.val {value_schema[['maxLength']]}}.", + "x" = "Value{?s} {.val {input[is_too_long]}} {?has/have} + more characters than allowed" + ), + call = call + ) + } + + is_too_short <- stringr::str_length(input) < value_schema[["minLength"]] + if (any(is_too_short)) { + cli::cli_abort( + c( + "!" = "The minimum number of characters allowed for values in + {.arg {property}} is {.val {value_schema[['minLength']]}}.", + "x" = "Value{?s} {.val {input[is_too_short]}} {?has/have} + fewer characters than allowed." + ), + call = call + ) + } + } + + if (typeof(input) %in% c("double", "integer")) { + value_schema <- oneof_schema[["double"]] + value_min <- value_schema[["minimum"]] + is_too_small <- input < value_min + if (any(is_too_small)) { + cli::cli_abort( + c( + "!" = "All values in {.arg {property}} must be greater + than {.val {value_min}}.", + "x" = "{cli::qty(sum(is_invalid))} Value{?s} {.val {input[is_invalid]}} + {cli::qty(sum(is_invalid))}{?is/are} equal to or less." + ), + call = call + ) + } + } +} + +# Subset tasks schema for the schema of a specific output type +get_schema_output_type <- function(schema, output_type) { + purrr::pluck( + schema, + "properties", "rounds", + "items", "properties", "model_tasks", + "items", "properties", "output_type", + "properties", output_type + ) +} + +#' Download hubverse tasks schema from the hubverse schema repository. +#' +#' @param schema_version the version required. Defaults to "latest". +#' @param branch the branch to download the schema from. Defaults to "main". +#' @param format the format to return the schema in. Defaults to "list". Can be "list" or "json". +#' +#' @return The requested version of the tasks hubverse schema in the specified format. +#' @noRd +download_tasks_schema <- function(schema_version = "latest", branch = "main", + format = c("list", "json")) { # nolint: indentation_linter + format <- rlang::arg_match(format) + + # Get the latest version available in our GitHub schema repo + if (schema_version == "latest") { + schema_version <- hubUtils::get_schema_valid_versions(branch = branch) %>% + sort() %>% + utils::tail(1) + } + + schema_url <- hubUtils::get_schema_url( + config = "tasks", + version = schema_version, + branch = branch + ) + + schema_json <- hubUtils::get_schema(schema_url) + + switch(format, + list = jsonlite::fromJSON(schema_json, + simplifyDataFrame = FALSE + ), + json = schema_json + ) +} diff --git a/R/collect_items.R b/R/collect_items.R new file mode 100644 index 0000000..91205e6 --- /dev/null +++ b/R/collect_items.R @@ -0,0 +1,102 @@ +collect_items <- function(..., + item_class = c( + "task_id", + "output_type_item", + "target_metadata_item", + "model_task", + "round" + ), + output_class = c( + "task_ids", + "output_type", + "target_metadata", + "model_tasks", + "rounds" + ), + flatten = TRUE, + call = rlang::caller_env()) { + item_class <- rlang::arg_match(item_class) + output_class <- rlang::arg_match(output_class) + + items <- list(...) + + check_item_classes(items, item_class, call = call) + + schema_id <- check_schema_ids(items, call = call) + + if (flatten) { + items <- purrr::list_flatten(items) + } else { + items <- purrr::map( + items, + function(x) { + attributes(x) <- list(names = names(x)) + return(x) + } + ) + } + if (item_class == "target_metadata_item") { + check_target_metadata_properties_unique(items, + property = "target_id", + call = call + ) + check_target_metadata_properties_unique(items, + property = "target_name", + call = call + ) + check_target_metadata_properties_unique(items, + property = "target_keys", + call = call + ) + } else { + check_property_names_unique(items, call = call) + } + + check_items_unique(items, item_class) + + structure(list(items), + class = c(output_class, "list"), + names = output_class, + n = length(items), + schema_id = schema_id + ) +} + + + +check_items_unique <- function(items, item_class, call = rlang::caller_env()) { + is_duplicate <- duplicated(items) + + if (any(is_duplicate)) { + duplicated_items <- items[is_duplicate] + duplicated_idx <- which(is_duplicate) + + + duplicate_of <- purrr::map2_int( + duplicated_items, + duplicated_idx, + ~ find_duplicate_of(.x, .y, items) + ) + + duplicate_of_msg <- paste0( + "{.cls {item_class}} object ", "{.val {", duplicated_idx, "L}} ", + "is a duplicate of object", " {.val {", duplicate_of, "L}}" + ) %>% + stats::setNames(rep("x", length(duplicate_of))) + + cli::cli_abort( + "!" = "All {.cls {item_class}} objects provided must be unique + but duplicates detected.", + duplicate_of_msg, + call = call + ) + } +} + +find_duplicate_of <- function(duplicated_item, duplicate_idx, items) { + which(purrr::map_lgl( + items, + ~ identical(.x, duplicated_item) + )) %>% + utils::head(1) +} diff --git a/R/config-schema-utils.R b/R/config-schema-utils.R new file mode 100644 index 0000000..b25143e --- /dev/null +++ b/R/config-schema-utils.R @@ -0,0 +1,127 @@ +get_config_file_schema_version <- function(config_path, config) { + config_schema_version <- jsonlite::read_json(config_path)$schema_version + + if (is.null(config_schema_version)) { + cli::cli_abort(c("x" = "Property {.code schema_version} not found in config file.")) + } + + check_config_schema_version(config_schema_version, + config = config + ) + + version <- stringr::str_extract( + config_schema_version, + "v([0-9]\\.){2}[0-9](\\.[0-9]+)?" + ) + + if (length(version) == 0L) { + cli::cli_abort( + c( + "x" = "Valid {.field version} could not be extracted from config + file {.file {config_path}}", + "!" = "Please check property {.val schema_version} is correctly formatted." + ) + ) + } + + version +} + + +check_config_schema_version <- function(schema_version, config = c("tasks", "admin")) { + config <- rlang::arg_match(config) + + check_filename <- grepl( + glue::glue("/{config}-schema.json$"), + schema_version + ) + if (!check_filename) { + cli::cli_abort(c( + "x" = "{.code schema_version} property {.url {schema_version}} + does not point to appropriate schema file.", + "i" = "{.code schema_version} basename should be + {.file {config}-schema.json} but is {.file {basename(schema_version)}}" + )) + } + + check_prefix <- grepl( + "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/", + schema_version, + fixed = TRUE + ) + + if (!check_prefix) { + cli::cli_abort(c( + "x" = "Invalid {.code schema_version} property.", + "i" = "Valid {.code schema_version} properties should start with + {.val https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/} + and resolve to the schema file's raw contents on GitHub." + )) + } +} + + +validate_schema_version_property <- function(validation, config = c("tasks", "admin")) { + config <- rlang::arg_match(config) + schema_version <- jsonlite::read_json(attr(validation, "config_path"), + simplifyVector = TRUE, + simplifyDataFrame = FALSE + )$schema_version + schema <- hubUtils::get_schema(attr(validation, "schema_url")) + + + errors_tbl <- NULL + check_filename <- grepl( + glue::glue("/{config}-schema.json$"), + schema_version + ) + if (!check_filename) { + errors_tbl <- rbind( + errors_tbl, + data.frame( + instancePath = get_error_path(schema, "schema_version", "instance"), + schemaPath = get_error_path(schema, "schema_version", "schema"), + keyword = "schema_version file name", + message = glue::glue( + "'schema_version' property does not point to corresponding schema file for config '{config}'. ", + "Should be '{config}-schema.json' but is '{basename(schema_version)}'" + ), + schema = "", + data = schema_version + ) + ) + } + + check_prefix <- grepl("https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/", + schema_version, + fixed = TRUE + ) + + if (!check_prefix) { + errors_tbl <- rbind( + errors_tbl, + data.frame( + instancePath = get_error_path(schema, "schema_version", "instance"), + schemaPath = get_error_path(schema, "schema_version", "schema"), + keyword = "schema_version prefix", + message = paste( + "Invalid 'schema_version' property. Should start with", + "'https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/'" + ), + schema = "", + data = schema_version + ) + ) + } + + if (!is.null(errors_tbl)) { + # assign FALSE without loosing attributes + validation[] <- FALSE + attr(validation, "errors") <- rbind( + attr(validation, "errors"), + errors_tbl + ) + } + + return(validation) +} diff --git a/R/create_config.R b/R/create_config.R new file mode 100644 index 0000000..4907e7a --- /dev/null +++ b/R/create_config.R @@ -0,0 +1,81 @@ +#' Create a `config` class object. +#' +#' Create a representation of a complete `"tasks"` config file as a list object of +#' class `config`. This can be written out to a `tasks.json` file. +#' @param rounds An object of class `rounds` created using function +#' [`create_rounds()`] +#' +#' @return a named list of class `config`. +#' @export +#' @seealso [create_rounds()] +#' @details For more details consult +#' the [documentation on `tasks.json` Hub config files]( +#' https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file). +#' +#' @examples +#' rounds <- create_rounds( +#' create_round( +#' round_id_from_variable = TRUE, +#' round_id = "origin_date", +#' model_tasks = create_model_tasks( +#' create_model_task( +#' task_ids = create_task_ids( +#' create_task_id("origin_date", +#' required = NULL, +#' optional = c( +#' "2023-01-02", +#' "2023-01-09", +#' "2023-01-16" +#' ) +#' ), +#' create_task_id("location", +#' required = "US", +#' optional = c("01", "02", "04", "05", "06") +#' ), +#' create_task_id("horizon", +#' required = 1L, +#' optional = 2:4 +#' ) +#' ), +#' output_type = create_output_type( +#' create_output_type_mean( +#' is_required = TRUE, +#' value_type = "double", +#' value_minimum = 0L +#' ) +#' ), +#' target_metadata = create_target_metadata( +#' create_target_metadata_item( +#' target_id = "inc hosp", +#' target_name = "Weekly incident influenza hospitalizations", +#' target_units = "rate per 100,000 population", +#' target_keys = NULL, +#' target_type = "discrete", +#' is_step_ahead = TRUE, +#' time_unit = "week" +#' ) +#' ) +#' ) +#' ), +#' submissions_due = list( +#' relative_to = "origin_date", +#' start = -4L, +#' end = 2L +#' ) +#' ) +#' ) +#' create_config(rounds) +create_config <- function(rounds) { + rlang::check_required(rounds) + + check_object_class(rounds, "rounds") + + structure( + list( + schema_version = attr(rounds, "schema_id"), + rounds = rounds$rounds + ), + class = c("config", "list"), + schema_id = attr(rounds, "schema_id") + ) +} diff --git a/R/create_model_task.R b/R/create_model_task.R new file mode 100644 index 0000000..b309f1d --- /dev/null +++ b/R/create_model_task.R @@ -0,0 +1,172 @@ +#' Create an object of class `model_task` +#' +#' Create an object of class `model_task` representing a model task. Multiple +#' model tasks can be combined using function [`create_model_tasks()`]. +#' @param task_ids object of class `model_task`. +#' @param output_type object of class `output_type`. +#' @param target_metadata object of class `target_metadata`. +#' +#' @return a named list of class `model_task`. +#' @export +#' @seealso [create_task_ids()], [create_output_type()], [create_target_metadata()], +#' [`create_model_tasks()`] +#' +#' @examples +#' create_model_task( +#' task_ids = create_task_ids( +#' create_task_id("origin_date", +#' required = NULL, +#' optional = c( +#' "2023-01-02", +#' "2023-01-09", +#' "2023-01-16" +#' ) +#' ), +#' create_task_id("location", +#' required = "US", +#' optional = c("01", "02", "04", "05", "06") +#' ), +#' create_task_id("horizon", +#' required = 1L, +#' optional = 2:4 +#' ) +#' ), +#' output_type = create_output_type( +#' create_output_type_mean( +#' is_required = TRUE, +#' value_type = "double", +#' value_minimum = 0L +#' ) +#' ), +#' target_metadata = create_target_metadata( +#' create_target_metadata_item( +#' target_id = "inc hosp", +#' target_name = "Weekly incident influenza hospitalizations", +#' target_units = "rate per 100,000 population", +#' target_keys = NULL, +#' target_type = "discrete", +#' is_step_ahead = TRUE, +#' time_unit = "week" +#' ) +#' ) +#' ) +create_model_task <- function(task_ids, output_type, target_metadata) { + rlang::check_required(task_ids) + rlang::check_required(output_type) + rlang::check_required(target_metadata) + + call <- rlang::current_call() + + purrr::walk( + c( + "task_ids", + "output_type", + "target_metadata" + ), + ~ check_object_class(get(.x), .x, + call = call + ) + ) + + schema_id <- check_schema_ids( + list( + task_ids, + output_type, + target_metadata + ), + call = call + ) + + check_target_key_valid(target_metadata, task_ids, call) + + structure( + c( + task_ids, + output_type, + target_metadata + ), + class = c("model_task", "list"), + schema_id = schema_id + ) +} + + +check_target_key_valid <- function(target_metadata, task_ids, + call = rlang::caller_env()) { + target_keys <- purrr::map( + target_metadata[[1]], + ~ .x[["target_keys"]] + ) + + if (all(purrr::map_lgl(target_keys, ~ is.null(.x)))) { + return() + } + + target_keys_names <- purrr::map_chr(target_keys, ~ names(.x)) %>% + unique() + + task_id_names <- names(task_ids[[1]]) + + invalid_target_key_names <- !target_keys_names %in% task_id_names + + if (any(invalid_target_key_names)) { + cli::cli_abort( + c( + "!" = "{.arg target_metadata} {.field target_keys names} must + match valid {.arg task_ids} {.field property names}: + {.val {task_id_names}}", + "x" = "{.field target_keys} name{?s} + {.val {target_keys_names[invalid_target_key_names]}} do{?es/} not." + ), + call = call + ) + } + purrr::walk( + target_keys_names, + ~ check_task_id_target_key_values(.x, + task_ids, + target_keys, + call = call + ) + ) +} + + +check_object_class <- function(object, class, call = rlang::caller_env()) { + if (!inherits(object, class)) { + cli::cli_abort( + c("x" = "{.arg {class}} must inherit from class {.cls {class}} but does not"), + call = call + ) + } +} + + +check_task_id_target_key_values <- function(target_key_name, task_ids, # nolint: object_length_linter + target_keys, call = rlang::caller_env()) { + task_id_values <- unlist(task_ids$task_ids[[target_key_name]]) %>% + unique() %>% + sort() + + target_key_values <- purrr::map_chr( + target_keys, + ~ .x[[target_key_name]] + ) %>% + unique() %>% + sort() + + + if (any(task_id_values != target_key_values)) { + cli::cli_abort( + c( + "x" = "{.arg task_ids} {.field {target_key_name}} values must match + {.arg target_metadata} {.arg target_keys} definitions.", + ">" = "{.arg target_keys} {.field {target_key_name}} values: + {.val {target_key_values}}", + ">" = "{.arg task_ids} {.field {target_key_name}} values: + {.val {task_id_values}}" + ), + call = call + ) + } +} diff --git a/R/create_model_tasks.R b/R/create_model_tasks.R new file mode 100644 index 0000000..281cc51 --- /dev/null +++ b/R/create_model_tasks.R @@ -0,0 +1,171 @@ +#' Create a `model_tasks` class object. +#' +#' @param ... objects of class `model_tasks` created using function +#' [`create_model_task()`] +#' +#' @return a named list of class `model_tasks`. +#' @export +#' @seealso [create_model_task()] +#' +#' @examples +#' create_model_tasks( +#' create_model_task( +#' task_ids = create_task_ids( +#' create_task_id("origin_date", +#' required = NULL, +#' optional = c( +#' "2023-01-02", +#' "2023-01-09", +#' "2023-01-16" +#' ) +#' ), +#' create_task_id("location", +#' required = "US", +#' optional = c("01", "02", "04", "05", "06") +#' ), +#' create_task_id("horizon", +#' required = 1L, +#' optional = 2:4 +#' ) +#' ), +#' output_type = create_output_type( +#' create_output_type_mean( +#' is_required = TRUE, +#' value_type = "double", +#' value_minimum = 0L +#' ) +#' ), +#' target_metadata = create_target_metadata( +#' create_target_metadata_item( +#' target_id = "inc hosp", +#' target_name = "Weekly incident influenza hospitalizations", +#' target_units = "rate per 100,000 population", +#' target_keys = NULL, +#' target_type = "discrete", +#' is_step_ahead = TRUE, +#' time_unit = "week" +#' ) +#' ) +#' ) +#' ) +#' create_model_tasks( +#' create_model_task( +#' task_ids = create_task_ids( +#' create_task_id("origin_date", +#' required = NULL, +#' optional = c( +#' "2023-01-02", +#' "2023-01-09", +#' "2023-01-16" +#' ) +#' ), +#' create_task_id("location", +#' required = "US", +#' optional = c("01", "02", "04", "05", "06") +#' ), +#' create_task_id("target", +#' required = NULL, +#' optional = c("inc death", "inc hosp") +#' ), +#' create_task_id("horizon", +#' required = 1L, +#' optional = 2:4 +#' ) +#' ), +#' output_type = create_output_type( +#' create_output_type_mean( +#' is_required = TRUE, +#' value_type = "double", +#' value_minimum = 0L +#' ), +#' create_output_type_median( +#' is_required = FALSE, +#' value_type = "double" +#' ), +#' create_output_type_quantile( +#' required = c(0.25, 0.5, 0.75), +#' optional = c( +#' 0.1, 0.2, 0.3, 0.4, 0.6, +#' 0.7, 0.8, 0.9 +#' ), +#' value_type = "double", +#' value_minimum = 0 +#' ) +#' ), +#' target_metadata = create_target_metadata( +#' create_target_metadata_item( +#' target_id = "inc hosp", +#' target_name = "Weekly incident influenza hospitalizations", +#' target_units = "rate per 100,000 population", +#' target_keys = list(target = "inc hosp"), +#' target_type = "discrete", +#' is_step_ahead = TRUE, +#' time_unit = "week" +#' ), +#' create_target_metadata_item( +#' target_id = "inc death", +#' target_name = "Weekly incident influenza deaths", +#' target_units = "rate per 100,000 population", +#' target_keys = list(target = "inc death"), +#' target_type = "discrete", +#' is_step_ahead = TRUE, +#' time_unit = "week" +#' ) +#' ) +#' ), +#' create_model_task( +#' task_ids = create_task_ids( +#' create_task_id("origin_date", +#' required = NULL, +#' optional = c( +#' "2023-01-02", +#' "2023-01-09", +#' "2023-01-16" +#' ) +#' ), +#' create_task_id("location", +#' required = "US", +#' optional = c("01", "02", "04", "05", "06") +#' ), +#' create_task_id("target", +#' required = "flu hosp rt chng", +#' optional = NULL +#' ), +#' create_task_id("horizon", +#' required = 1L, +#' optional = 2:4 +#' ) +#' ), +#' output_type = create_output_type( +#' create_output_type_pmf( +#' required = c( +#' "large_decrease", +#' "decrease", +#' "stable", +#' "increase", +#' "large_increase" +#' ), +#' optional = NULL, +#' value_type = "double" +#' ) +#' ), +#' target_metadata = create_target_metadata( +#' create_target_metadata_item( +#' target_id = "flu hosp rt chng", +#' target_name = "Weekly influenza hospitalization rate change", +#' target_units = "rate per 100,000 population", +#' target_keys = list(target = "flu hosp rt chng"), +#' target_type = "nominal", +#' is_step_ahead = TRUE, +#' time_unit = "week" +#' ) +#' ) +#' ) +#' ) +create_model_tasks <- function(...) { + collect_items(..., + item_class = "model_task", + output_class = "model_tasks", + flatten = FALSE + ) +} diff --git a/R/create_output_type.R b/R/create_output_type.R new file mode 100644 index 0000000..c92ec9b --- /dev/null +++ b/R/create_output_type.R @@ -0,0 +1,40 @@ +#' Create an `output_type` class object. +#' +#' @param ... objects of class `output_type_item` created using functions from the +#' `create_output_type_*()` family of functions. +#' +#' @return a named list of class `output_type`. +#' @export +#' @seealso [create_output_type_mean()], [create_output_type_median()], +#' [create_output_type_quantile()], [create_output_type_cdf()], +#' [create_output_type_pmf()], [create_output_type_sample()] +#' @details For more details consult +#' the [documentation on `tasks.json` Hub config files]( +#' https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file). +#' @examples +#' create_output_type( +#' create_output_type_mean( +#' is_required = TRUE, +#' value_type = "double", +#' value_minimum = 0L +#' ), +#' create_output_type_median( +#' is_required = FALSE, +#' value_type = "double" +#' ), +#' create_output_type_quantile( +#' required = c(0.25, 0.5, 0.75), +#' optional = c( +#' 0.1, 0.2, 0.3, 0.4, 0.6, +#' 0.7, 0.8, 0.9 +#' ), +#' value_type = "double", +#' value_minimum = 0 +#' ) +#' ) +create_output_type <- function(...) { + collect_items(..., + item_class = "output_type_item", output_class = "output_type", + flatten = TRUE + ) +} diff --git a/R/create_output_type_item.R b/R/create_output_type_item.R new file mode 100644 index 0000000..e9d412d --- /dev/null +++ b/R/create_output_type_item.R @@ -0,0 +1,355 @@ +#' Create a point estimate output type object of class `output_type_item` +#' +#' Create a representation of a `mean` or `median` output type as a list object of +#' class `output_type_item`. This can be combined with +#' additional `output_type_item` objects using function [`create_output_type()`] to +#' create an `output_type` object for a given model_task. +#' This can be combined with other building blocks which can then be written as +#' or appended to `tasks.json` Hub config files. +#' @param is_required Logical. Is the output type required? +#' @param value_type Character string. The data type of the output_type values. +#' @param value_minimum Numeric. The inclusive minimum of output_type values. +#' @param value_maximum Numeric. The inclusive maximum of output_type values. +#' +#' @details For more details consult +#' the [documentation on `tasks.json` Hub config files]( +#' https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file). +#' @return a named list of class `output_type_item` representing a `mean` or +#' `median` output type. +#' @inheritParams create_task_id +#' @export +#' @describeIn create_output_type_mean Create a list representation of a `mean` +#' output type. +#' @seealso [create_output_type()] +#' @examples +#' create_output_type_mean( +#' is_required = TRUE, +#' value_type = "double", +#' value_minimum = 0L +#' ) +#' create_output_type_median( +#' is_required = FALSE, +#' value_type = "integer" +#' ) +create_output_type_mean <- function(is_required, value_type, value_minimum = NULL, + value_maximum = NULL, schema_version = "latest", + branch = "main") { + create_output_type_point( + output_type = "mean", is_required = is_required, + value_type = value_type, value_minimum = value_minimum, + value_maximum = value_maximum, schema_version = schema_version, + branch = branch + ) +} + +#' @export +#' @describeIn create_output_type_mean Create a list representation of a `median` +#' output type. +create_output_type_median <- function(is_required, value_type, value_minimum = NULL, + value_maximum = NULL, schema_version = "latest", + branch = "main") { + create_output_type_point( + output_type = "median", is_required = is_required, + value_type = value_type, value_minimum = value_minimum, + value_maximum = value_maximum, schema_version = schema_version, + branch = branch + ) +} + + +create_output_type_point <- function(output_type = c("mean", "median"), + is_required, value_type, value_minimum = NULL, + value_maximum = NULL, schema_version = "latest", + branch = "main", call = rlang::caller_env()) { + rlang::check_required(value_type) + rlang::check_required(is_required) + + if (!rlang::is_logical(is_required, n = 1L)) { + cli::cli_abort(c( + "x" = "Argument {.arg is_required} must be {.cls logical} and have length 1." + )) + } + output_type <- rlang::arg_match(output_type) + # Get output type id property according to config schema version + # TODO: remove back-compatibility with schema versions < v2.0.0 when support + # retired + config_tid <- hubUtils::get_config_tid( + config_version = hubUtils::get_schema_version_latest(schema_version, branch) + ) + + schema <- download_tasks_schema(schema_version, branch) + + # create output_type_id + if (is_required) { + output_type_id <- list(output_type_id = list( + required = NA_character_, + optional = NULL + )) + } else { + output_type_id <- list(output_type_id = list( + required = NULL, + optional = NA_character_ + )) + } + + # TODO: Remove when support for versions < 2.0.0 retired + names(output_type_id) <- config_tid + + output_type_schema <- get_schema_output_type(schema, + output_type = output_type + ) + + value_schema <- purrr::pluck( + output_type_schema, + "properties", + "value", + "properties" + ) + + value <- list( + type = value_type, + minimum = value_minimum, + maximum = value_maximum + ) %>% + purrr::compact() + + + purrr::walk( + names(value), + function(x) { + check_input( + input = value[[x]], + property = x, + value_schema, + parent_property = "value", + scalar = TRUE, + call = call + ) + } + ) + + structure( + list(c(output_type_id, list(value = value))), + class = c("output_type_item", "list"), + names = output_type, + schema_id = schema$`$id` + ) +} + + +#' Create a distribution output type object of class `output_type_item` +#' +#' Create a representation of a `quantile`, `cdf`, `pmf` or `sample` output +#' type as a list object of class `output_type_item`. This can be combined with +#' additional `output_type_item`s using function [`create_output_type()`] to +#' create an `output_type` object for a given model_task. +#' This can be combined with other building blocks which can then be written as +#' or appended to `tasks.json` Hub config files. +#' output type. +#' This can be combined with other building blocks which can then be written as +#' or appended to `tasks.json` Hub config files. +#' @param required Atomic vector of required `output_type_id` values. Can be NULL if +#' all values are optional. +#' @param optional Atomic vector of optional `output_type_id` values. Can be NULL if +#' all values are required. +#' @inheritParams create_task_id +#' @inheritParams create_output_type_mean +#' @details For more details consult +#' the [documentation on `tasks.json` Hub config files]( +#' https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file). +#' +#' @return a named list of class `output_type_item` representing a `quantile`, +#' `cdf`, `pmf` or `sample` output type. +#' @export +#' @describeIn create_output_type_quantile Create a list representation of a `quantile` +#' output type. +#' @seealso [create_output_type()] +#' @examples +#' create_output_type_quantile( +#' required = c(0.25, 0.5, 0.75), +#' optional = c( +#' 0.1, 0.2, 0.3, 0.4, 0.6, +#' 0.7, 0.8, 0.9 +#' ), +#' value_type = "double", +#' value_minimum = 0 +#' ) +#' create_output_type_cdf( +#' required = c(10, 20), +#' optional = NULL, +#' value_type = "double" +#' ) +#' create_output_type_cdf( +#' required = NULL, +#' optional = c("EW202240", "EW202241", "EW202242"), +#' value_type = "double" +#' ) +#' create_output_type_pmf( +#' required = NULL, +#' optional = c("low", "moderate", "high", "extreme"), +#' value_type = "double" +#' ) +#' create_output_type_sample( +#' required = 1:10, optional = 11:15, +#' value_type = "double" +#' ) +create_output_type_quantile <- function(required, optional, + value_type, value_minimum = NULL, + value_maximum = NULL, + schema_version = "latest", + branch = "main") { + create_output_type_dist( + output_type = "quantile", required = required, optional = optional, + value_type = value_type, value_minimum = value_minimum, + value_maximum = value_maximum, schema_version = schema_version, + branch = branch + ) +} + +#' @describeIn create_output_type_quantile Create a list representation of a `cdf` +#' output type. +#' @export +create_output_type_cdf <- function(required, optional, + value_type, + schema_version = "latest", + branch = "main") { + create_output_type_dist( + output_type = "cdf", required = required, optional = optional, + value_type = value_type, value_minimum = 0L, + value_maximum = 1L, schema_version = schema_version, + branch = branch + ) +} + +#' @describeIn create_output_type_quantile Create a list representation of a `pmf` +#' output type. +#' @export +create_output_type_pmf <- function(required, optional, value_type, + schema_version = "latest", + branch = "main") { + create_output_type_dist( + output_type = "pmf", required = required, optional = optional, + value_type = value_type, value_minimum = 0L, + value_maximum = 1L, schema_version = schema_version, + branch = branch + ) +} + +#' @describeIn create_output_type_quantile Create a list representation of a `sample` +#' output type. +#' @export +create_output_type_sample <- function(required, optional, value_type, + value_minimum = NULL, value_maximum = NULL, + schema_version = "latest", + branch = "main") { + create_output_type_dist( + output_type = "sample", required = required, optional = optional, + value_type = value_type, value_minimum = value_minimum, + value_maximum = value_maximum, schema_version = schema_version, + branch = branch + ) +} + + +create_output_type_dist <- function( + output_type = c("quantile", "cdf", "pmf", "sample"), + required, optional, + value_type, value_minimum = NULL, + value_maximum = NULL, schema_version = "latest", + branch = "main", call = rlang::caller_env()) { + rlang::check_required(value_type) + rlang::check_required(required) + rlang::check_required(optional) + output_type <- rlang::arg_match(output_type) + # Get output type id property according to config schema version + # TODO: remove back-compatibility with schema versions < v2.0.0 when support + # retired + config_tid <- hubUtils::get_config_tid( + config_version = hubUtils::get_schema_version_latest(schema_version, branch) + ) + + schema <- download_tasks_schema(schema_version, branch) + output_type_schema <- get_schema_output_type(schema, output_type) + output_type_id_schema <- purrr::pluck( + output_type_schema, + "properties", + config_tid, + "properties" + ) + + # Check and create output_type_id + if (output_type == "cdf") { + purrr::walk( + c("required", "optional"), + function(x) { + check_oneof_input( + input = get(x), + property = x, + output_type_id_schema, + call = call + ) + } + ) + } else { + purrr::walk( + c("required", "optional"), + function(x) { + check_input( + input = get(x), + property = x, + output_type_id_schema, + parent_property = config_tid, + call = call + ) + } + ) + } + + check_prop_not_all_null(required, optional) + check_prop_type_const(required, optional) + check_prop_dups(required, optional) + + output_type_id <- list(output_type_id = list( + required = required, + optional = optional + )) + + # TODO: Remove when support for versions < 2.0.0 retired + names(output_type_id) <- config_tid + + # Check and create value + value <- list( + type = value_type, + minimum = value_minimum, + maximum = value_maximum + ) %>% + purrr::compact() + + value_schema <- purrr::pluck( + output_type_schema, + "properties", + "value", + "properties" + ) + + purrr::walk( + names(value), + function(x) { + check_input( + input = value[[x]], + property = x, + value_schema, + parent_property = "value", + scalar = TRUE, + call = rlang::caller_env(n = 5) + ) + } + ) + + structure( + list(c(output_type_id, list(value = value))), + class = c("output_type_item", "list"), + names = output_type, + schema_id = schema$`$id` + ) +} diff --git a/R/create_round.R b/R/create_round.R new file mode 100644 index 0000000..18bebb9 --- /dev/null +++ b/R/create_round.R @@ -0,0 +1,282 @@ +#' Create an object of class `round` +#' +#' Create a representation of a round item as a list object of +#' class `round`. This can be combined with +#' additional `round` objects using function [`create_rounds()`]. +#' Such building blocks can ultimately be combined and then written out as or +#' appended to `tasks.json` Hub config files. +#' @param round_id_from_variable logical. Whether `round_id` is inferred from the +#' values of a `task_id` variable within the `model_tasks` `model_task` items. +#' @param round_id character string. The round identifier. +#' If `round_id_from_variable = TRUE`, `round_id` should be the name of a `task_id` +#' variable present in all `model_tasks` `model_task` items. +#' @param round_name character string. An optional round name. This can be useful +#' for internal referencing of rounds, for examples, when a date is used as +#' `round_id` but hub maintainers and teams also refer to rounds as round-1, round-2 etc. +#' @param model_tasks an object of class `model_tasks` created with function +#' [create_model_tasks()]. +#' @param submissions_due named list conforming to one of the two following structures: +#' 1. Submission dates for round is determined relative to an origin date. +#' - `relative_to`: character string of the name of the `task_id` variable containing +#' origin dates in relation to which submission start and end dates are determined. +#' - `start`: integer. Difference in days between start and origin date. +#' - `end`: integer. Difference in days between end and origin date. +#' 2. Submission dates for round are provided explicitly. +#' - `start`: character. Submission start date in ISO 8601 format (YYYY-MM-DD). +#' - `end`: character. Submission end date in ISO 8601 format (YYYY-MM-DD). +#' @param last_data_date character date in ISO 8601 format (YYYY-MM-DD). +#' The last date with recorded data in the data set used as input to a model. +#' Optional. +#' @param file_format character string. An optional specification of a file format for the round. +#' This option in only available for some versions of the schema and is ignored if +#' not allowed in the version of the schema used. It also overrides any specification +#' of file format in `admin.json`. For more details on whether this argument can +#' be used as well as available formats, please consult +#' the [documentation on `tasks.json` Hub config files]( +#' https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file). +#' @return a named list of class `round`. +#' @export +#' @seealso [create_rounds()] +#' @details For more details consult +#' the [documentation on `tasks.json` Hub config files]( +#' https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file). +#' @examples +#' model_tasks <- create_model_tasks( +#' create_model_task( +#' task_ids = create_task_ids( +#' create_task_id("origin_date", +#' required = NULL, +#' optional = c( +#' "2023-01-02", +#' "2023-01-09", +#' "2023-01-16" +#' ) +#' ), +#' create_task_id("location", +#' required = "US", +#' optional = c("01", "02", "04", "05", "06") +#' ), +#' create_task_id("horizon", +#' required = 1L, +#' optional = 2:4 +#' ) +#' ), +#' output_type = create_output_type( +#' create_output_type_mean( +#' is_required = TRUE, +#' value_type = "double", +#' value_minimum = 0L +#' ) +#' ), +#' target_metadata = create_target_metadata( +#' create_target_metadata_item( +#' target_id = "inc hosp", +#' target_name = "Weekly incident influenza hospitalizations", +#' target_units = "rate per 100,000 population", +#' target_keys = NULL, +#' target_type = "discrete", +#' is_step_ahead = TRUE, +#' time_unit = "week" +#' ) +#' ) +#' ) +#' ) +#' create_round( +#' round_id_from_variable = TRUE, +#' round_id = "origin_date", +#' model_tasks = model_tasks, +#' submissions_due = list( +#' relative_to = "origin_date", +#' start = -4L, +#' end = 2L +#' ), +#' last_data_date = "2023-01-02" +#' ) +create_round <- function(round_id_from_variable, + round_id, round_name = NULL, + model_tasks, + submissions_due, last_data_date = NULL, + file_format = NULL) { + rlang::check_required(round_id_from_variable) + rlang::check_required(round_id) + rlang::check_required(model_tasks) + rlang::check_required(submissions_due) + call <- rlang::current_env() + + check_object_class(model_tasks, "model_tasks") + + schema <- hubUtils::get_schema(attr(model_tasks, "schema_id")) %>% + jsonlite::fromJSON(simplifyDataFrame = FALSE) + round_schema <- get_schema_round(schema) + + property_names <- c( + "round_id_from_variable", "round_id", + "round_name", "model_tasks", "submissions_due", + "last_data_date", + "file_format" + ) %>% + purrr::keep(~ .x %in% names(round_schema)) + + model_tasks <- model_tasks$model_tasks + properties <- mget(property_names) %>% + purrr::compact() + + property_names <- names(properties) + + + purrr::walk( + property_names[!property_names %in% c("submissions_due", "model_tasks")], + function(.x) { + check_input( + input = properties[[.x]], + property = .x, + round_schema, + parent_property = NULL, + scalar = TRUE, + call = call + ) + } + ) + check_submission_due(submissions_due, round_schema, model_tasks) + + if (round_id_from_variable) { + check_round_id_variable(model_tasks, round_id) + } + + structure( + properties, + class = c("round", "list"), + round_id = round_id, + schema_id = schema$`$id` + ) +} + +check_round_id_variable <- function(model_tasks, round_id, + call = rlang::caller_env()) { + invalid_round_id <- purrr::map_lgl( + model_tasks, + ~ !round_id %in% names(.x$task_ids) + ) + + if (any(invalid_round_id)) { + cli::cli_abort( + c( + "!" = "{.arg round_id} value must correspond to a valid {.arg task_id} + variable in every {.arg model_task} object.", + "x" = "{.arg round_id} value {.val {round_id}} does not correspond + to a valid variable in provided {cli::qty(sum(invalid_round_id))} + {.arg model_tasks} {.arg model_task} object{?s} + {.val {which(invalid_round_id)}}." + ), + call = call + ) + } +} + +check_submission_due <- function(submissions_due, round_schema, model_tasks, + call = rlang::caller_env()) { + if (!rlang::is_list(submissions_due) || inherits(submissions_due, "data.frame")) { + cli::cli_abort( + c( + "!" = "{.arg submissions_due} must be a {.cls list} not a + {.cls {class(submissions_due)}}" + ), + call = call + ) + } + + schema_valid_names <- purrr::map( + round_schema$submissions_due$oneOf, + ~ names(.x$properties) + ) %>% + unlist() %>% + unique() + invalid_properties <- !names(submissions_due) %in% schema_valid_names + + + if (any(invalid_properties)) { + invalid_property_names <- names(submissions_due)[invalid_properties] # nolint: object_usage_linter + cli::cli_abort( + c( + "x" = "Propert{?y/ies} {.val {invalid_property_names}} in + {.arg submissions_due} {?is/are} invalid.", + "!" = "Valid {.arg submissions_due} properties: + {.val {schema_valid_names}}" + ), + call = call + ) + } + + is_relative_to_schema <- purrr::map_lgl( + round_schema$submissions_due$oneOf, + ~ "relative_to" %in% names(.x$properties) + ) + + if ("relative_to" %in% names(submissions_due)) { + oneof_schema <- purrr::pluck( + round_schema, + "submissions_due", + "oneOf", + which(is_relative_to_schema), + "properties" + ) + check_relative_to_variable(submissions_due, model_tasks, + call = call + ) + } else { + oneof_schema <- purrr::pluck( + round_schema, + "submissions_due", + "oneOf", + which(!is_relative_to_schema), + "properties" + ) + } + + purrr::walk( + names(oneof_schema), + ~ check_input( + input = submissions_due[[.x]], + property = .x, + oneof_schema, + parent_property = NULL, + scalar = TRUE, + call = call + ) + ) +} + +check_relative_to_variable <- function(submissions_due, model_tasks, + call = rlang::caller_env()) { + relative_to_value <- submissions_due$relative_to + invalid_relative_to <- purrr::map_lgl( + model_tasks, + function(.x) { + !relative_to_value %in% names(.x$task_ids) + } + ) + + if (any(invalid_relative_to)) { + cli::cli_abort( + c( + "!" = "{.arg submissions_due} {.arg relative_to} value must + correspond to a valid {.arg task_id} variable in every + {.arg model_tasks} {.arg model_task} object.", + "x" = "{.arg relative_to} value {.val {relative_to_value}} + does not correspond to a valid {.arg task_id} variable in + provided {cli::qty(sum(invalid_relative_to))} + {.arg model_tasks} {.arg model_task} object{?s} + {.val {which(invalid_relative_to)}}." + ), + call = call + ) + } +} + +get_schema_round <- function(schema) { + purrr::pluck( + schema, + "properties", "rounds", + "items", "properties" + ) +} diff --git a/R/create_rounds.R b/R/create_rounds.R new file mode 100644 index 0000000..ca5da0b --- /dev/null +++ b/R/create_rounds.R @@ -0,0 +1,173 @@ +#' Create a `rounds` class object. +#' +#' @param ... objects of class `round` created using function +#' [`create_round()`] +#' +#' @return a named list of class `rounds`. +#' @export +#' @seealso [create_round()] +#' @details For more details consult +#' the [documentation on `tasks.json` Hub config files]( +#' https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file). +#' +#' @examples +#' model_tasks <- create_model_tasks( +#' create_model_task( +#' task_ids = create_task_ids( +#' create_task_id("origin_date", +#' required = NULL, +#' optional = c( +#' "2023-01-02", +#' "2023-01-09", +#' "2023-01-16" +#' ) +#' ), +#' create_task_id("location", +#' required = "US", +#' optional = c("01", "02", "04", "05", "06") +#' ), +#' create_task_id("horizon", +#' required = 1L, +#' optional = 2:4 +#' ) +#' ), +#' output_type = create_output_type( +#' create_output_type_mean( +#' is_required = TRUE, +#' value_type = "double", +#' value_minimum = 0L +#' ) +#' ), +#' target_metadata = create_target_metadata( +#' create_target_metadata_item( +#' target_id = "inc hosp", +#' target_name = "Weekly incident influenza hospitalizations", +#' target_units = "rate per 100,000 population", +#' target_keys = NULL, +#' target_type = "discrete", +#' is_step_ahead = TRUE, +#' time_unit = "week" +#' ) +#' ) +#' ) +#' ) +#' # Create a rounds object with a single rounds where round_id is defined through +#' # the value of a task_id variable. +#' create_rounds( +#' create_round( +#' round_id_from_variable = TRUE, +#' round_id = "origin_date", +#' model_tasks = model_tasks, +#' submissions_due = list( +#' relative_to = "origin_date", +#' start = -4L, +#' end = 2L +#' ) +#' ) +#' ) +#' # Create a rounds object with two rounds and user defined round_ids +#' create_rounds( +#' create_round( +#' round_id_from_variable = FALSE, +#' round_id = "round_1", +#' model_tasks = +#' create_model_tasks( +#' create_model_task( +#' task_ids = create_task_ids( +#' create_task_id("origin_date", +#' required = NULL, +#' optional = c( +#' "2023-01-09" +#' ) +#' ), +#' create_task_id("location", +#' required = "US", +#' optional = c("01", "02", "04", "05", "06") +#' ), +#' create_task_id("horizon", +#' required = 1L, +#' optional = 2:4 +#' ) +#' ), +#' output_type = create_output_type( +#' create_output_type_mean( +#' is_required = TRUE, +#' value_type = "double", +#' value_minimum = 0L +#' ) +#' ), +#' target_metadata = create_target_metadata( +#' create_target_metadata_item( +#' target_id = "inc hosp", +#' target_name = "Weekly incident influenza hospitalizations", +#' target_units = "rate per 100,000 population", +#' target_keys = NULL, +#' target_type = "discrete", +#' is_step_ahead = TRUE, +#' time_unit = "week" +#' ) +#' ) +#' ) +#' ), +#' submissions_due = list( +#' start = "2023-01-05", +#' end = "2023-01-11" +#' ), +#' last_data_date = "2023-01-06" +#' ), +#' create_round( +#' round_id_from_variable = FALSE, +#' round_id = "round_2", +#' model_tasks = +#' create_model_tasks( +#' create_model_task( +#' task_ids = create_task_ids( +#' create_task_id("origin_date", +#' required = NULL, +#' optional = c( +#' "2023-01-16" +#' ) +#' ), +#' create_task_id("location", +#' required = "US", +#' optional = c("01", "02", "04", "05", "06") +#' ), +#' create_task_id("horizon", +#' required = 1L, +#' optional = 2:4 +#' ) +#' ), +#' output_type = create_output_type( +#' create_output_type_mean( +#' is_required = TRUE, +#' value_type = "double", +#' value_minimum = 0L +#' ) +#' ), +#' target_metadata = create_target_metadata( +#' create_target_metadata_item( +#' target_id = "inc hosp", +#' target_name = "Weekly incident influenza hospitalizations", +#' target_units = "rate per 100,000 population", +#' target_keys = NULL, +#' target_type = "discrete", +#' is_step_ahead = TRUE, +#' time_unit = "week" +#' ) +#' ) +#' ) +#' ), +#' submissions_due = list( +#' start = "2023-01-12", +#' end = "2023-01-18" +#' ), +#' last_data_date = "2023-01-13" +#' ) +#' ) +create_rounds <- function(...) { + collect_items(..., + item_class = "round", + output_class = "rounds", + flatten = FALSE + ) +} diff --git a/R/create_target_metadata.R b/R/create_target_metadata.R new file mode 100644 index 0000000..c17a767 --- /dev/null +++ b/R/create_target_metadata.R @@ -0,0 +1,115 @@ +#' Create a `target_metadata` class object. +#' +#' @param ... objects of class `target_metadata_item` created using function +#' [`create_target_metadata_item()`] +#' +#' @return a named list of class `target_metadata`. +#' @export +#' @seealso [create_target_metadata_item()] +#' @details For more details consult +#' the [documentation on `tasks.json` Hub config files]( +#' https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file). +#' +#' @examples +#' create_target_metadata( +#' create_target_metadata_item( +#' target_id = "inc hosp", +#' target_name = "Weekly incident influenza hospitalizations", +#' target_units = "rate per 100,000 population", +#' target_keys = list(target = "inc hosp"), +#' target_type = "discrete", +#' is_step_ahead = TRUE, +#' time_unit = "week" +#' ), +#' create_target_metadata_item( +#' target_id = "inc death", +#' target_name = "Weekly incident influenza deaths", +#' target_units = "rate per 100,000 population", +#' target_keys = list(target = "inc death"), +#' target_type = "discrete", +#' is_step_ahead = TRUE, +#' time_unit = "week" +#' ) +#' ) +create_target_metadata <- function(...) { + collect_items(..., + item_class = "target_metadata_item", + output_class = "target_metadata", + flatten = FALSE + ) +} + + +check_schema_ids <- function(items, call = rlang::caller_env()) { + schema_ids <- purrr::map_chr( + items, + ~ attr(.x, "schema_id") + ) + + unique_n <- schema_ids %>% + unique() %>% + length() + + if (unique_n > 1L) { + if (!purrr::reduce(purrr::map(items, ~ class(.x)), identical)) { + item_names <- purrr::map_chr(items, ~ names(.x)) + schema_ids <- paste(item_names, ":", schema_ids) + obj_ref <- "Argument" # nolint: object_usage_linter + } else { + schema_ids <- paste("Item", seq_along(schema_ids), ":", schema_ids) + obj_ref <- "Item" + } + names(schema_ids) <- rep("*", length(schema_ids)) + + cli::cli_abort(c( + "!" = "All {tolower(obj_ref)}s supplied must be created against the same Hub schema.", + "x" = "{.arg schema_id} attributes are not consistent across all {tolower(obj_ref)}s.", + "{obj_ref} {.arg schema_id} attributes:", + schema_ids + ), call = call) + } + + unique(schema_ids) +} + + +check_item_classes <- function(items, class, call = rlang::caller_env()) { + is_wrong_class <- purrr::map_lgl( + items, + ~ !inherits(.x, class) + ) + + if (any(is_wrong_class)) { + cli::cli_abort( + c( + "!" = "All items supplied must inherit from class {.cls {class}}", + "x" = "{cli::qty(sum(is_wrong_class))} Item{?s} {.val {which(is_wrong_class)}} + {cli::qty(sum(is_wrong_class))} do{?es/} not." + ), + call = call + ) + } +} + +check_target_metadata_properties_unique <- function(items, property, # nolint: object_length_linter + call = rlang::caller_env()) { + item_properties <- purrr::map( + items, + ~ .x[[property]] + ) + + if (any(duplicated(item_properties))) { + duplicate_idx <- which(duplicated(item_properties)) # nolint: object_usage_linter + + cli::cli_abort( + c( + "!" = "{.arg {property}}s must be unique across all + {.arg target_metadata_item}s.", + "x" = "{cli::qty(length(duplicate_idx))} {.arg target_metadata_item}{?s} + {.val {duplicate_idx}} with {.arg {property}} value {.val {item_properties[duplicate_idx]}} + {cli::qty(length(duplicate_idx))} {?is/are} duplicate{?s}." + ), + call = call + ) + } +} diff --git a/R/create_target_metadata_item.R b/R/create_target_metadata_item.R new file mode 100644 index 0000000..8f7c539 --- /dev/null +++ b/R/create_target_metadata_item.R @@ -0,0 +1,168 @@ +#' Create an object of class `target_metadata_item` +#' +#' Create a representation of a target_metadata item as a list object of +#' class `target_metadata_item`. This can be combined with +#' additional target_metadata items using function [`create_target_metadata()`] to +#' create a target_metadata object for a given model_task. +#' Such building blocks can ultimately be combined and then written out as or +#' appended to `tasks.json` Hub config files. +#' @param target_id character string. Short description that uniquely identifies +#' the target. +#' @param target_name character string. A longer human readable target description +#' that could be used, for example, as a visualisation axis label. +#' @param target_units character string. Unit of observation of the target. +#' @param target_keys named list or `NULL`. Should be `NULL`, in the case +#' where the target is not specified as a task_id but is specified solely through +#' the `target_id` argument. Otherwise, should be a named list of one or more +#' character strings. The name of each element should match a task_id variable +#' within the same model_tasks object. Each element should be of length 1. +#' Each value, or the combination of values if multiple keys are specified, +#' define a single target value. +#' @param description character string (optional). An optional verbose description +#' of the target that might include information such as definitions of a 'rate' or similar. +#' @param target_type character string. Target statistical data type. Consult the +#' [appropriate version of the hub schema]( +#' https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#model-tasks-tasks-json-interactive-schema) +#' for potential values. +#' @param is_step_ahead logical. Whether the target is part of a sequence of values +#' @param time_unit character string. If `is_step_ahead` is `TRUE`, then this +#' argument is required and defines the unit of time steps. if `is_step_ahead` is +#' `FALSE`, then this argument is not required and will be ignored if given. +#' @inheritParams create_task_id +#' @seealso [create_target_metadata()] +#' @details For more details consult +#' the [documentation on `tasks.json` Hub config files]( +#' https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file). +#' @return a named list of class `target_metadata_item`. +#' @export +#' +#' @examples +#' create_target_metadata_item( +#' target_id = "inc hosp", +#' target_name = "Weekly incident influenza hospitalizations", +#' target_units = "rate per 100,000 population", +#' target_keys = list(target = "inc hosp"), +#' target_type = "discrete", +#' is_step_ahead = TRUE, +#' time_unit = "week" +#' ) +create_target_metadata_item <- function(target_id, target_name, target_units, + target_keys = NULL, description = NULL, + target_type, is_step_ahead, time_unit = NULL, + schema_version = "latest", branch = "main") { + rlang::check_required(target_id) + rlang::check_required(target_name) + rlang::check_required(target_units) + rlang::check_required(target_keys) + rlang::check_required(target_type) + rlang::check_required(is_step_ahead) + call <- rlang::current_env() + + schema <- download_tasks_schema(schema_version, branch) + target_metadata_schema <- get_schema_target_metadata(schema) + + + if (is.null(description)) { + property_names <- c( + "target_id", "target_name", "target_units", + "target_keys", "target_type", "is_step_ahead" + ) + } else { + property_names <- c( + "target_id", "target_name", "target_units", + "target_keys", "description", "target_type", + "is_step_ahead" + ) + } + + if (is_step_ahead) { + if (is.null(time_unit)) { + cli::cli_abort(c( + "!" = "A value must be provided for {.arg time_unit} when {.arg is_step_ahead} + is {.val {TRUE}}" + )) + } + property_names <- c(property_names, "time_unit") + } + + purrr::walk( + property_names[property_names != "target_keys"], + function(.x) { + check_input( + input = get(.x), + property = .x, + target_metadata_schema, + parent_property = NULL, + scalar = TRUE, + call = call + ) + } + ) + + check_target_keys(target_keys, call = call) + + structure(mget(property_names), + class = c("target_metadata_item", "list"), + names = property_names, + schema_id = schema$`$id` + ) +} + +check_target_keys <- function(target_keys, call = rlang::caller_env()) { + if (is.null(target_keys)) { + return() + } + + if (!rlang::is_list(target_keys) || inherits(target_keys, "data.frame")) { + cli::cli_abort( + c( + "!" = "{.arg target_keys} must be a {.cls list} not a + {.cls {class(target_keys)}}" + ), + call = call + ) + } + if (!rlang::is_named(target_keys)) { + cli::cli_abort( + c( + "!" = "{.arg target_keys} must be a named {.cls list}." + ), + call = call + ) + } + + purrr::walk2( + target_keys, + names(target_keys), + ~ check_target_key_value( + .x, .y, + call = call + ) + ) +} + + +check_target_key_value <- function(target_key, target_key_name, + call = rlang::caller_env()) { + if (!rlang::is_character(target_key, n = 1)) { + cli::cli_abort( + c( + "!" = "{.arg target_keys} element {.field {target_key_name}} must be + a {.cls character} vector of length {.val {1L}} not a + {.cls {class(target_key)}} vector of length + {.val {length(target_key)}}" + ), + call = call + ) + } +} + +get_schema_target_metadata <- function(schema) { + purrr::pluck( + schema, + "properties", "rounds", + "items", "properties", "model_tasks", + "items", "properties", "target_metadata", + "items", "properties" + ) +} diff --git a/R/create_task_id.R b/R/create_task_id.R new file mode 100644 index 0000000..f7d42c8 --- /dev/null +++ b/R/create_task_id.R @@ -0,0 +1,198 @@ +#' #' Create an object of class `task_id` +#' +#' Create a representation of a task ID item as a list object of +#' class `task_id`. This can be combined with +#' additional `task_id` objects using function [`create_task_ids()`] to +#' create a `task_ids` class object for a given model_task. +#' Such building blocks can ultimately be combined and then written out as or +#' appended to `tasks.json` Hub config files. +#' @param name character string, Name of task_id to create. +#' @param required Atomic vector of required task_id values. Can be `NULL` if all +#' values are optional. +#' @param optional Atomic vector of optional task_id values. Can be `NULL` if all +#' values are required. +#' @param schema_version Character string specifying the json schema version to +#' be used for validation. The default value `"latest"` will use the latest version +#' available in the Infectious Disease Modeling Hubs +#' [schemas repository](https://github.com/Infectious-Disease-Modeling-Hubs/schemas). +#' Alternatively, a specific version of a schema (e.g. `"v0.0.1"`) can be +#' specified. +#' @details `required` and `optional` vectors for standard task_ids defined in a Hub schema +#' must match data types and formats specified in the schema. For more details consult +#' the [documentation on `tasks.json` Hub config files]( +#' https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file) +#' +#' JSON schema data type names differ to those in R. Use the following mappings to +#' create vectors of appropriate data types which will correspond to correct JSON +#' schema data types during config file validation. +#' +#' ```{r, echo = FALSE} +#' knitr::kable(data.frame(json = names(json_datatypes), +#' R = json_datatypes, +#' row.names = NULL)) +#' ``` +#' Values across `required` and `optional` arguments must be unique. `required` +#' and `optional` must be of the same type (unless `NULL`) and both cannot be `NULL`. +#' Task_ids that represent dates must be supplied as character strings in ISO 8601 +#' date format (YYYY-MM-DD). If working with date objects, please convert to character +#' (e.g. using `as.character()`) before supplying as arguments. +#' +#' Task_ids not present in the schema are allowed as additional properties but the +#' user is responsible for providing values of the correct data type. +#' +#' @inheritParams validate_config +#' @seealso [create_task_ids()] +#' @return a named list of class `task_id` representing a task ID. +#' @export +#' +#' @examples +#' create_task_id("horizon", required = 1L, optional = 2:4) +create_task_id <- function(name, required, optional, + schema_version = "latest", branch = "main") { + checkmate::assert_character(name, len = 1L) + rlang::check_required(required) + rlang::check_required(optional) + call <- rlang::current_env() + + schema <- download_tasks_schema(schema_version, branch) + + task_ids_schema <- get_schema_task_ids(schema) + schema_task_ids <- names(task_ids_schema$properties) + + name <- match_element_name( + name, + schema_task_ids, + element = "task_id" + ) + + if (name %in% schema_task_ids) { + task_id_schema <- purrr::pluck( + task_ids_schema, + "properties", + name, + "properties" + ) + + purrr::walk( + c("required", "optional"), + function(.x) { + check_input( + input = get(.x), + property = .x, + task_id_schema, + parent_property = NULL, + call = call + ) + } + ) + } + + check_prop_not_all_null(required, optional) + check_prop_type_const(required, optional) + check_prop_dups(required, optional) + + structure( + list(list( + required = required, + optional = optional + )), + class = c("task_id", "list"), + names = name, + schema_id = schema$`$id` + ) +} + +get_schema_task_ids <- function(schema) { + purrr::pluck( + schema, + "properties", "rounds", + "items", "properties", "model_tasks", + "items", "properties", "task_ids" + ) +} + + +match_element_name <- function(name, element_names, + element = c("task_id", "output_type")) { + matched_name <- utils::head(agrep(name, element_names, value = TRUE), 1) + if (length(matched_name) == 1L && matched_name != name) { + ask <- utils::askYesNo(ask_msg(name, matched_name, element), + prompts = "Y/N/C" + ) + + if (is.na(ask)) stop() + if (element != "task_id" && !ask) stop() + + if (ask) { + name <- matched_name + } + } + + name +} + +ask_msg <- function(name, matched_name, element = c("task_id", "output_type")) { + cli::cli_div() + cli::cli_text( + c( + "Argument {.arg name} value {.val {name}} does not match any + {.field {element}} names in schema. ", + "Did you mean {.val {matched_name}}?" + ) + ) + + cli::cli_ul(c( + "Type {.code Y} to use {.val {matched_name}}.", + if (element == "task_id") { + "Type {.code N} to continue with {.val {name}}." + } else { + "Type {.code N} or {.code C} to cancel:" + }, + if (element == "task_id") { + "Type {.code C} to cancel:" + } else { + NULL + } + )) + cli::cli_end() +} + +check_prop_not_all_null <- function(required, optional) { + if (all(is.null(required), is.null(optional))) { + cli::cli_abort(c("x" = "Both arguments {.arg required} and {.arg optional} + cannot be NULL.")) + } +} + +check_prop_dups <- function(required, optional) { + duplicates <- duplicated(c(required, optional)) + + if (any(duplicates)) { + cli::cli_abort( + c( + "x" = "Values across arguments {.arg required} and {.arg optional} + must be unique.", + "!" = "Provided {cli::qty(sum(duplicates))} value{?s} + {.val {c(required, optional)[duplicates]}} + {cli::qty(sum(duplicates))} {?is/are} duplicated." + ) + ) + } +} + +check_prop_type_const <- function(required, optional) { + prop_types <- purrr::map_chr( + c(required, optional), + ~ typeof(.x) + ) %>% + unique() + + if (length(prop_types) != 1L && !"NULL" %in% prop_types) { + cli::cli_abort(c( + "x" = "Arguments {.arg required} and {.arg optional} + must be of same type.", + "i" = "{.arg required} is {.cls {typeof(required)}}", + "i" = "{.arg optional} is {.cls {typeof(optional)}}" + )) + } +} diff --git a/R/create_task_ids.R b/R/create_task_ids.R new file mode 100644 index 0000000..4ff6fa7 --- /dev/null +++ b/R/create_task_ids.R @@ -0,0 +1,65 @@ +#' Create a `task_ids` class object. +#' +#' @param ... objects of class `task_id` created using function [`create_task_id()`] +#' +#' @return a named list of class `task_ids`. +#' @export +#' @seealso [create_task_id()] +#' @details For more details consult +#' the [documentation on `tasks.json` Hub config files]( +#' https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file). +#' +#' @examples +#' create_task_ids( +#' create_task_id("origin_date", +#' required = NULL, +#' optional = c( +#' "2023-01-02", +#' "2023-01-09", +#' "2023-01-16" +#' ) +#' ), +#' create_task_id("scenario_id", +#' required = NULL, +#' optional = c( +#' "A-2021-03-28", +#' "B-2021-03-28" +#' ) +#' ), +#' create_task_id("location", +#' required = "US", +#' optional = c("01", "02", "04", "05", "06") +#' ), +#' create_task_id("target", +#' required = "inc hosp", +#' optional = NULL +#' ), +#' create_task_id("horizon", +#' required = 1L, +#' optional = 2:4 +#' ) +#' ) +create_task_ids <- function(...) { + collect_items(..., + item_class = "task_id", output_class = "task_ids", + flatten = TRUE + ) +} + +check_property_names_unique <- function(x, call = rlang::caller_env()) { + x_names <- names(x) + + if (any(duplicated(x_names))) { + duplicate_idx <- which(duplicated(x_names)) # nolint: object_usage_linter + + cli::cli_abort( + c( + "!" = "{.arg names} must be unique across all items.", + "x" = "{cli::qty(length(duplicate_idx))} Item{?s} + {.val {duplicate_idx}} with {.arg name} {.val {x_names[duplicate_idx]}} + {cli::qty(length(duplicate_idx))} {?is/are} duplicate{?s}." + ), + call = call + ) + } +} diff --git a/R/data.R b/R/data.R new file mode 100644 index 0000000..fe7b0ae --- /dev/null +++ b/R/data.R @@ -0,0 +1,30 @@ +#' Available US locations +#' +#' Data set with available locations for a US hub +#' +#' @format A data frame with 3202 rows and 5 columns: +#' \describe{ +#' \item{fips}{FIPS code} +#' \item{location_name}{Location name} +#' \item{geo_type}{Type of location for compatibility with +#' \href{https://cmu-delphi.github.io/delphi-epidata/api/covidcast_geography.html +#' }{EpiData API geographic codings} +#' } +#' \item{geo_value}{Location abbreviation or FIPS code for compatibility with +#' \href{https://cmu-delphi.github.io/delphi-epidata/api/covidcast_geography.html +#' }{EpiData API geographic codings}} +#' \item{abbreviation}{Corresponding state abbrevaition} +#' } +"hub_locations_us" + + +#' Available European locations +#' +#' Data set with available European locations used in ECDC hubs +#' +#' @format A data frame with 32 rows and 2 columns: +#' \describe{ +#' \item{location_name}{Name of the location} +#' \item{location}{Location abbreviation} +#' } +"hub_locations_eu" diff --git a/R/get_error_path.R b/R/get_error_path.R new file mode 100644 index 0000000..29c6e12 --- /dev/null +++ b/R/get_error_path.R @@ -0,0 +1,75 @@ +get_error_path <- function(schema, element = "target_metadata", + type = c("schema", "instance"), + append_item_n = FALSE) { + type <- rlang::arg_match(type) + + schema_paths <- schema %>% + jsonlite::fromJSON(simplifyDataFrame = FALSE) %>% + unlist(recursive = TRUE, use.names = TRUE) %>% + names() %>% + gsub("\\.", "/", .) %>% + paste0("/", .) + + path <- grep(paste0(".*", element, "/type([0-9])?$"), + schema_paths, + value = TRUE + ) %>% + gsub("/type([0-9])?", "", .) %>% + unique() + + # Instance paths to custom task IDs can be created by appending the element + # name to the task ID properties. + if (length(path) == 0L && type == "instance") { + path <- grep(paste0(".*", "task_ids", "/type([0-9])?$"), + schema_paths, + value = TRUE + ) %>% + gsub("/type([0-9])?", "", .) %>% + unique() %>% + paste("properties", element, sep = "/") + } + + # Schema paths to custom task IDs can be created by pointing to the task_ids + # additionalProperties schema. + # This will only retrun a path with schema v2.0.0 and above, when + # additionalProperties became an object. + if (length(path) == 0L && type == "schema") { + path <- grep(".*task_ids/additionalProperties/type([0-9])?$", + schema_paths, + value = TRUE + ) %>% + gsub("/type([0-9])?", "", .) %>% + unique() + } + + if (append_item_n && any( + grepl( + paste(path, "items", sep = "/"), + schema_paths + ) + ) && + length(path) != 0L + ) { + path <- paste(path, "items", sep = "/") + } + + + switch(type, + schema = if (length(path) == 0L) NA else paste0("#", path), + instance = generate_instance_path_glue(path) + ) +} + +generate_instance_path_glue <- function(path) { + split_path <- gsub("properties/", "", path) %>% + strsplit("/") %>% + unlist() + + is_item <- split_path == "items" + split_path[is_item] <- c( + "{round_i - 1}", + "{model_task_i - 1}", + "{target_key_i - 1}" + )[1:sum(is_item)] + paste(split_path, collapse = "/") +} diff --git a/R/globals.R b/R/globals.R new file mode 100644 index 0000000..323f89a --- /dev/null +++ b/R/globals.R @@ -0,0 +1 @@ +utils::globalVariables(c(".")) diff --git a/R/sysdata.rda b/R/sysdata.rda new file mode 100644 index 0000000..de83123 Binary files /dev/null and b/R/sysdata.rda differ diff --git a/R/utils-pipe.R b/R/utils-pipe.R new file mode 100644 index 0000000..fd0b1d1 --- /dev/null +++ b/R/utils-pipe.R @@ -0,0 +1,14 @@ +#' Pipe operator +#' +#' See \code{magrittr::\link[magrittr:pipe]{\%>\%}} for details. +#' +#' @name %>% +#' @rdname pipe +#' @keywords internal +#' @export +#' @importFrom magrittr %>% +#' @usage lhs \%>\% rhs +#' @param lhs A value or the magrittr placeholder. +#' @param rhs A function call using the magrittr semantics. +#' @return The result of calling `rhs(lhs)`. +NULL diff --git a/R/validate-config-utils.R b/R/validate-config-utils.R new file mode 100644 index 0000000..330691c --- /dev/null +++ b/R/validate-config-utils.R @@ -0,0 +1,491 @@ +# Validate that no task id names match reserved hub variable names +val_task_id_names <- function(model_task_grp, model_task_i, round_i, schema) { + reserved_hub_vars <- c( + "model_id", "output_type", + "output_type_id", "value" + ) + task_id_names <- names(model_task_grp$task_ids) + check_task_id_names <- task_id_names %in% reserved_hub_vars + + if (any(check_task_id_names)) { + invalid_task_id_values <- task_id_names[check_task_id_names] + + error_row <- data.frame( + instancePath = paste0( + glue::glue( + get_error_path(schema, "task_ids", "instance") + ), "/", + names(invalid_task_id_values) + ), + schemaPath = get_error_path(schema, "task_ids", "schema"), + keyword = "task_id names", + message = glue::glue( + "task_id name(s) '{invalid_task_id_values}'", + " must not match reserved hub variable names." + ), + schema = "", + data = glue::glue( + 'task_id names: {glue::glue_collapse(task_id_names, ", ", last = " & ")}; + reserved hub variable names:', + ' {glue::glue_collapse(reserved_hub_vars, ", ", last = " & ")}' + ) + ) + return(error_row) + } + + return(NULL) +} + +val_model_task_grp_target_metadata <- function(model_task_grp, model_task_i, + round_i, schema) { + grp_target_keys <- get_grp_target_keys(model_task_grp) + + # If all target keys are NULL, exit checks + if (all(purrr::map_lgl(grp_target_keys, ~ is.null(.x)))) { + return(NULL) + } + + # Check that target key names across items in target metadata array are consistent. + # If not returns error early as further checks may fail unexpectedly. + errors_check_1 <- val_target_key_names_const( + grp_target_keys, model_task_grp, + model_task_i, round_i, schema + ) + + if (!is.null(errors_check_1)) { + return(errors_check_1) + } + + # Check whether target key names do not correspond to task_id properties + invalid_target_key_names <- purrr::map( + grp_target_keys, + ~ find_invalid_target_keys(.x, model_task_grp) + ) %>% + unlist(use.names = FALSE) + + # If any do not correspond, run validation function to generate errors rows. + # Otherwise assign NULL to errors_check_2 + if (any(invalid_target_key_names)) { + errors_check_2 <- purrr::imap( + grp_target_keys, + ~ val_target_key_names(.x, + target_key_i = .y, + model_task_grp = model_task_grp, + model_task_i = model_task_i, + round_i = round_i, + schema = schema + ) + ) %>% + purrr::list_rbind() + } else { + errors_check_2 <- NULL + } + # If none of the target key names match task id properties, return errors_check_2 + # early as further checks become redundant. + if (all(invalid_target_key_names)) { + return(errors_check_2) + } + + # Check that the values of each target keys have matching values in the corresponding + # task_id required & optional property arrays. + errors_check_3 <- purrr::imap( + grp_target_keys, + ~ val_target_key_values(.x, model_task_grp, + target_key_i = .y, + model_task_i = model_task_i, + round_i = round_i, + schema = schema + ) + ) %>% + purrr::list_rbind() + + # Check that the unique values in the required & optional property arrays + # of each task_id identified as a target key have a matching + # value in the corresponding target key of at least one target metadata item. + errors_check_4 <- val_target_key_task_id_values(grp_target_keys, model_task_grp, + model_task_i = model_task_i, + round_i = round_i, + schema = schema + ) + # Combine all error checks + rbind( + errors_check_2, + errors_check_3, + errors_check_4 + ) +} + +val_target_key_names_const <- function(grp_target_keys, model_task_grp, + model_task_i, round_i, schema) { + target_key_names <- purrr::map(grp_target_keys, ~ names(.x)) %>% + purrr::map_if(~ !is.null(.x), ~.x, .else = ~"null") + + if (length(unique(target_key_names)) > 1) { + error_row <- data.frame( + instancePath = glue::glue(get_error_path(schema, "target_keys", "instance")), + schemaPath = get_error_path(schema, "target_keys", "schema"), + keyword = "target_keys names", + message = glue::glue("target_key names not consistent across target_metadata array items"), + schema = "", + data = glue::glue("target_key_{seq_along(target_key_names)}: {purrr::map_chr(target_key_names, + ~paste(.x, collapse = ','))}") %>% + glue::glue_collapse(sep = "; ") + ) + return(error_row) + } + return(NULL) +} + +val_target_key_names <- function(target_keys, model_task_grp, + target_key_i, model_task_i, + round_i, schema) { + check <- find_invalid_target_keys(target_keys, model_task_grp) + + if (any(check)) { + error_row <- data.frame( + instancePath = paste0( + glue::glue(get_error_path(schema, "target_keys", "instance")), + "/", names(check[check]) + ), + schemaPath = get_error_path(schema, "target_keys", "schema"), + keyword = "target_keys names", + message = glue::glue("target_key(s) '{names(check[check])}' not properties of modeling task group task IDs"), + schema = "", + data = glue::glue("task_id names: {glue::glue_collapse(get_grp_task_ids(model_task_grp), sep = ', ')}; + target_key names: {glue::glue_collapse(names(target_keys), sep = ', ')}") + ) + + return(error_row) + } else { + return(NULL) + } +} + +val_target_key_values <- function(target_keys, model_task_grp, + target_key_i, model_task_i, + round_i, schema) { + check <- !find_invalid_target_keys(target_keys, model_task_grp) + + valid_target_keys <- target_keys[check] + + task_id_values <- purrr::map( + names(valid_target_keys), + ~ model_task_grp[["task_ids"]][[.x]] %>% + unlist(recursive = TRUE, use.names = FALSE) + ) %>% + purrr::set_names(names(valid_target_keys)) + + + is_invalid_target_key <- purrr::map2_lgl( + valid_target_keys, task_id_values, + ~ !.x %in% .y + ) + + + if (any(is_invalid_target_key)) { + error_row <- data.frame( + instancePath = paste0( + glue::glue(get_error_path(schema, "target_keys", "instance")), + "/", names(is_invalid_target_key) + ), + schemaPath = get_error_path(schema, "target_keys", "schema"), + keyword = "target_keys values", + message = glue::glue( + "target_key value '{valid_target_keys[names(is_invalid_target_key[is_invalid_target_key])]}' does not match any values in corresponding modeling task group task_id" + ), + schema = "", + data = glue::glue( + "task_id.{names(is_invalid_target_key)} values: {purrr::map_chr(task_id_values, ~glue::glue_collapse(.x, sep = ', '))}; + target_key.{names(valid_target_keys)} value: {unlist(valid_target_keys)}" + ) + ) + + return(error_row) + } else { + return(NULL) + } +} + +val_target_key_task_id_values <- function(grp_target_keys, + model_task_grp, + model_task_i, + round_i, schema) { + # Get unique values of target key names + target_key_names <- purrr::map(grp_target_keys, ~ names(.x)) %>% + unique() %>% + unlist() + + # Identify target_key_names that are valid task id properties + val_target_key_names <- target_key_names[ + target_key_names %in% names(model_task_grp[["task_ids"]]) + ] + + + # Get list of unique task id values across both required & optional arrays + # for each valid target key. + task_id_values <- model_task_grp[["task_ids"]][val_target_key_names] %>% + purrr::map(~ unlist(.x, use.names = FALSE)) %>% + unique() %>% + purrr::set_names(val_target_key_names) + + # Get list of target key values for each valid target key. + target_key_values <- purrr::map( + purrr::set_names(val_target_key_names), + ~ get_all_grp_target_key_values(.x, grp_target_keys) %>% + unique() + ) + + # Identify task id values that do not have a match in any of the corresponding + # target key definitions. + invalid_task_id_values <- purrr::map2( + .x = task_id_values, + .y = target_key_values, + ~ !.x %in% .y + ) %>% + purrr::map2( + task_id_values, + ~ .y[.x] + ) %>% + purrr::compact() %>% + purrr::map_chr(~ paste(.x, collapse = ", ")) + + + if (length(invalid_task_id_values) != 0) { + nms <- names(invalid_task_id_values) + tk_unique_vals <- purrr::map_chr( # nolint: object_usage_linter + target_key_values[nms], + ~ paste(.x, collapse = ", ") + ) + tid_unique_vals <- purrr::map_chr( # nolint: object_usage_linter + task_id_values[nms], + ~ glue::glue_collapse(.x, sep = ", ") + ) + + error_row <- data.frame( + instancePath = paste0(glue::glue(get_error_path(schema, "task_ids", "instance")), "/", nms), + schemaPath = get_error_path(schema, "task_ids", "schema"), + keyword = "task_id values", + message = glue::glue("task_id value(s) '{invalid_task_id_values}' not defined in any corresponding target_key."), + schema = "", + data = glue::glue("task_id.{nms} unique values: {tid_unique_vals}; + target_key.{nms} unique values: {tk_unique_vals}") + ) + return(error_row) + } + + return(NULL) +} + + +get_all_grp_target_key_values <- function(target, grp_target_keys) { + purrr::map_chr( + grp_target_keys, + ~ .x[[target]] + ) +} + + +get_grp_target_keys <- function(model_task_grp) { + purrr::map( + model_task_grp[["target_metadata"]], + ~ .x[["target_keys"]] + ) +} + +get_grp_task_ids <- function(model_task_grp) { + names(model_task_grp[["task_ids"]]) +} + +find_invalid_target_keys <- function(target_keys, model_task_grp) { + !names(target_keys) %in% get_grp_task_ids(model_task_grp) %>% + stats::setNames(names(target_keys)) +} + + + +validate_mt_property_unique_vals <- function(model_task_grp, + model_task_i, + round_i, + property = c( + "task_ids", + "output_type" + ), + schema) { + property <- rlang::arg_match(property) + property_text <- c( + task_ids = "Task ID", + output_type = "Output type IDs of output type" + )[property] + + + val_properties <- switch(property, + task_ids = model_task_grp[["task_ids"]], + output_type = model_task_grp[["output_type"]][ + c("quantile", "cdf", "pmf", "sample") + ] %>% + purrr::compact() %>% + purrr::map( + ~ if ("type_id" %in% names(.x)) { + .x[["type_id"]] + } else { + .x[["output_type_id"]] + } + ) + ) + + dup_properties <- purrr::map( + val_properties, + ~ { + x <- unlist(.x, use.names = FALSE) + dups <- x[duplicated(x)] + if (length(dups) == 0L) { + NULL + } else { + dups + } + } + ) %>% + purrr::compact() + + if (length(dup_properties) == 0L) { + return(NULL) + } else { + purrr::imap( + dup_properties, + ~ data.frame( + instancePath = glue::glue(get_error_path(schema, .y, "instance")), + schemaPath = get_error_path(schema, .y, "schema"), + keyword = glue::glue("{property} uniqueItems"), + message = glue::glue("must NOT have duplicate items across 'required' and 'optional' properties. {property_text} '{.y}' contains duplicates."), + schema = "", + data = glue::glue("duplicate values: {paste(.x, collapse = ', ')}") + ) + ) %>% purrr::list_rbind() + } +} + +validate_round_ids_consistent <- function(round, round_i, + schema) { + n_mt <- length(round[["model_tasks"]]) + + if (!round[["round_id_from_variable"]] || n_mt == 1L) { + return(NULL) + } + + round_id_var <- round[["round_id"]] + + mt <- purrr::map( + round[["model_tasks"]], + ~ purrr::pluck(.x, "task_ids", round_id_var) + ) + + checks <- purrr::map( + .x = purrr::set_names(seq_along(mt)[-1]), + ~ { + check <- all.equal(mt[[1]], mt[[.x]]) + if (is.logical(check) && check) NULL else check + } + ) %>% purrr::compact() + + if (length(checks) == 0L) { + return(NULL) + } + purrr::imap( + checks, + ~ tibble::tibble( + instancePath = glue::glue_data( + list(model_task_i = as.integer(.y)), + get_error_path(schema, round_id_var, "instance") + ), + schemaPath = get_error_path(schema, round_id_var, "schema"), + keyword = "round_id var", + message = glue::glue( + "round_id var '{round_id_var}' property MUST be", + " consistent across modeling task items" + ), + schema = "", + data = glue::glue("{.x} compared to first model task item") + ) + ) %>% + purrr::list_rbind() %>% + as.data.frame() +} + +# Validate that round IDs are unique across all rounds in config file +validate_round_ids_unique <- function(config_tasks, schema) { + round_ids <- hubUtils::get_round_ids(config_tasks) + + if (!any(duplicated(round_ids))) { + return(NULL) + } + + dup_round_ids <- unique(round_ids[duplicated(round_ids)]) + + purrr::map( + purrr::set_names(dup_round_ids), + ~ dup_round_id_error_df( + .x, + config_tasks = config_tasks, + schema = schema + ) + ) %>% purrr::list_rbind() +} + +dup_round_id_error_df <- function(dup_round_id, + config_tasks, + schema) { + dup_round_idx <- purrr::imap( + hubUtils::get_round_ids(config_tasks, flatten = "model_task"), + ~ { + if (dup_round_id %in% .x) .y else NULL + } + ) %>% + purrr::compact() %>% + unlist() %>% + `[`(-1L) + + dup_mt_idx <- purrr::map( + dup_round_idx, + ~ hubUtils::get_round_ids(config_tasks, flatten = "task_id")[[.x]] %>% + purrr::imap_int(~ { + if (dup_round_id %in% .x) .y else NULL + }) %>% + purrr::compact() + ) + + purrr::map2( + .x = dup_round_idx, + .y = dup_mt_idx, + ~ tibble::tibble( + instancePath = glue::glue_data( + list( + round_i = .x, + model_task_i = .y + ), + get_error_path(schema, get_round_id_var(.x, config_tasks), "instance", + append_item_n = TRUE + ) + ), + schemaPath = get_error_path( + schema, get_round_id_var(.x, config_tasks), + "schema" + ), + keyword = "round_id uniqueItems", + message = glue::glue( + "must NOT contains duplicate round ID values across rounds" + ), + schema = "", + data = glue::glue("duplicate value: {dup_round_id}") + ) + ) %>% + purrr::list_rbind() %>% + as.data.frame() +} + +get_round_id_var <- function(idx, config_tasks) { + if (config_tasks[["rounds"]][[idx]][["round_id_from_variable"]]) { + config_tasks[["rounds"]][[idx]][["round_id"]] + } else { + "rounds" + } +} diff --git a/R/validate_config.R b/R/validate_config.R new file mode 100644 index 0000000..868fb97 --- /dev/null +++ b/R/validate_config.R @@ -0,0 +1,216 @@ +#' Validate a hub config file against a Infectious Disease Modeling Hubs schema +#' +#' @param hub_path Path to a local hub directory. +#' @param config Name of config file to validate. One of `"tasks"` or `"admin"`. +#' @param config_path Defaults to `NULL` which assumes all config files are in +#' the `hub-config` directory in the root of hub directory. Argument +#' `config_path` can be used to override default by providing a path to the +#' config file to be validated. +#' @param schema_version Character string specifying the json schema version to +#' be used for validation. The default value `"from_config"` will use the +#' version specified in the `schema_version` property of the config file. +#' `"latest"` will use the latest version available in the Infectious Disease +#' Modeling Hubs +#' [schemas repository](https://github.com/Infectious-Disease-Modeling-Hubs/schemas). +#' Alternatively, a specific version of a schema (e.g. `"v0.0.1"`) can be +#' specified. +#' @param branch The branch of the Infectious Disease Modeling Hubs +#' [schemas repository](https://github.com/Infectious-Disease-Modeling-Hubs/schemas) +#' from which to fetch schema. Defaults to `"main"`. +#' @return Returns the result of validation. If validation is successful, will +#' return `TRUE`. If any validation errors are detected, returns `FALSE` with +#' details of errors appended as a data.frame to an `errors` attribute. +#' To access +#' the errors table use `attr(x, "errors")` where `x` is the output of the function. +#' +#' You can print a more concise and easier to view version of an errors table with +#' [view_config_val_errors()]. +#' @export +#' @seealso [view_config_val_errors()] +#' @family functions supporting config file validation +#' +#' @examples +#' # Valid config file +#' validate_config( +#' hub_path = system.file( +#' "testhubs/simple/", +#' package = "hubUtils" +#' ), +#' config = "tasks" +#' ) +#' # Config file with errors +#' config_path <- system.file("error-schema/tasks-errors.json", +#' package = "hubUtils" +#' ) +#' validate_config(config_path = config_path, config = "tasks") +validate_config <- function(hub_path = ".", + config = c("tasks", "admin"), + config_path = NULL, schema_version = "from_config", + branch = "main") { + if (!(requireNamespace("jsonvalidate"))) { + cli::cli_abort( + "Package {.pkg jsonvalidate} must be installed to use {.fn validate_config}. Please install it to continue. + Alternatively, to be able to use all packages required for hub maintainer functionality, + re-install {.pkg hubUtils} using argument {.code dependencies = TRUE}" + ) + } + + config <- rlang::arg_match(config) + + if (is.null(config_path)) { + checkmate::assert_directory_exists(hub_path) + config_path <- fs::path(hub_path, "hub-config", config, ext = "json") + } + + # check config file to be checked exists and is correct extension + checkmate::assert_file_exists(config_path, extension = "json") + + if (schema_version == "from_config") { + schema_version <- get_config_file_schema_version(config_path, config) + } + + # Get the latest version available in our GitHub schema repo + if (schema_version == "latest") { + schema_version <- hubUtils::get_schema_valid_versions(branch = branch) %>% + sort() %>% + utils::tail(1) + } + + # TODO: Remove notification when back-compatibility retired + hubUtils::check_deprecated_schema( + config_version = get_config_file_schema_version(config_path, config), + valid_version = "v2.0.0" + ) + + schema_url <- hubUtils::get_schema_url( + config = config, + version = schema_version, + branch = branch + ) + + schema_json <- hubUtils::get_schema(schema_url) + + + validation <- jsonvalidate::json_validate( + json = config_path, + schema = schema_json, + engine = "ajv", + verbose = TRUE + ) + + attr(validation, "config_path") <- config_path + attr(validation, "schema_version") <- schema_version + attr(validation, "schema_url") <- schema_url + + + if (validation) { + validation <- validate_schema_version_property(validation, config) + if (config == "tasks") { + validation <- perform_dynamic_config_validations(validation) + } + } + + if (validation) { + cli::cli_alert_success( + "Successfully validated config file {.file {config_path}} against schema {.url {schema_url}}" + ) + } else { + cli::cli_warn( + "Schema errors detected in config file {.file {config_path}} validated against schema {.url {schema_url}}" + ) + } + return(validation) +} + + + +#' Perform dynamic validation of target keys and schema_ids for internal consistency against +#' task ids. Check only performed once basic jsonvalidate checks pass against +#' schema. +#' +#' @param validation validation object returned by jsonvalidate::json_validate(). +#' +#' @return If validation successful, returns original validation object. If +#' validation fails, value of validation object is set to FALSE and the error +#' table is appended to attribute "errors". +#' @noRd +perform_dynamic_config_validations <- function(validation) { + config_json <- jsonlite::read_json(attr(validation, "config_path"), + simplifyVector = TRUE, + simplifyDataFrame = FALSE + ) + schema <- hubUtils::get_schema(attr(validation, "schema_url")) + + + errors_tbl <- c( + # Map over Round level validations + purrr::imap( + config_json[["rounds"]], + ~ val_round( + round = .x, round_i = .y, + schema = schema + ) + ), + # Perform config level validation + list(validate_round_ids_unique(config_json, schema)) + ) %>% + purrr::list_rbind() + + + if (nrow(errors_tbl) > 1) { + # assign FALSE without loosing attributes + validation[] <- FALSE + attr(validation, "errors") <- rbind( + attr(validation, "errors"), + errors_tbl + ) + } + + return(validation) +} + +## Dynamic schema validation utilities ---- +val_round <- function(round, round_i, schema) { + model_task_grps <- round[["model_tasks"]] + + c( + purrr::imap( + model_task_grps, + ~ val_model_task_grp_target_metadata( + model_task_grp = .x, model_task_i = .y, + round_i = round_i, schema = schema + ) + ), + purrr::imap( + model_task_grps, + ~ val_task_id_names( + model_task_grp = .x, model_task_i = .y, + round_i = round_i, schema = schema + ) + ), + purrr::imap( + model_task_grps, + ~ validate_mt_property_unique_vals( + model_task_grp = .x, model_task_i = .y, + round_i = round_i, property = "task_ids", + schema = schema + ) + ), + purrr::imap( + model_task_grps, + ~ validate_mt_property_unique_vals( + model_task_grp = .x, model_task_i = .y, + round_i = round_i, property = "output_type", + schema = schema + ) + ), + list( + validate_round_ids_consistent( + round = round, + round_i = round_i, + schema = schema + ) + ) + ) %>% + purrr::list_rbind() +} diff --git a/R/validate_hub_config.R b/R/validate_hub_config.R new file mode 100644 index 0000000..4be2d01 --- /dev/null +++ b/R/validate_hub_config.R @@ -0,0 +1,117 @@ +#' Validate Hub config files against Infectious Disease Modeling Hubs schema. +#' +#' Validate the `admin.json` and `tasks.json` Hub config files in a single call. +#' @inheritParams validate_config +#' +#' @return Returns a list of the results of validation, one for each `hub-config` +#' file validated. A value of `TRUE` for a given file indicates that validation +#' was successful. +#' A value of `FALSE` for a given file indicates that validation errors were +#' detected. Details of errors will be appended as a data.frame to an `errors` attribute. +#' To access the errors table for a given element use `attr(x, "errors")` +#' where `x` is the any element of the output of the function that is `FALSE`. +#' You can print a more concise and easier to view version of an errors table with +#' [view_config_val_errors()]. +#' @export +#' @seealso [view_config_val_errors()] +#' @family functions supporting config file validation +#' +#' @examples +#' validate_hub_config( +#' hub_path = system.file( +#' "testhubs/simple/", +#' package = "hubUtils" +#' ) +#' ) +validate_hub_config <- function(hub_path = ".", schema_version = "from_config", + branch = "main") { + configs <- c("tasks", "admin") + + # First only validate config files + validations <- purrr::map( + configs, + ~ validate_config( + hub_path = hub_path, + config = .x, + schema_version = schema_version, + branch = branch + ) + ) %>% + purrr::set_names(configs) %>% + suppressMessages() %>% + suppressWarnings() + + # Throw error if schema urls do not resolve to the same schema version directory. + # No point showing report of errors detected if they are not related to the + # same schema version. + schema_url_dirnames <- purrr::map_chr( + validations, + ~ dirname(attr(.x, "schema_url")) + ) + if (!do.call(`==`, as.list(schema_url_dirnames))) { + msg <- paste0( + "{.field ", + names(schema_url_dirnames), + ".json} schema version: {.url ", + schema_url_dirnames, + "}" + ) %>% + stats::setNames(rep("*", length(schema_url_dirnames))) + + cli::cli_abort(c( + x = "Config files validating against different schema versions.", + msg, + "!" = "Ensure the `schema_version` properties in all config files + refer to the same schema version to proceed." + )) + } + schema_version <- purrr::map_chr( + validations, + ~ attr(.x, "schema_version") + ) %>% + unique() + + # Add model metadata schema validations + validations[[ + "model-metadata-schema" + ]] <- validate_model_metadata_schema( + hub_path + ) %>% + suppressMessages() %>% + suppressWarnings() + + # Issue warning if validation errors detected in any of the config files. + # Else, signal success. + error_idx <- purrr::map_lgl( + validations, + ~ isFALSE(.x) + ) + if (any(error_idx)) { + cli::cli_warn(c( + "x" = "Errors detected in {.path { + fs::path(names(error_idx)[error_idx], ext = 'json')}} + config file{?s}.", + "i" = "Use {.code view_config_val_errors()} on the output of + {.code validate_hub_config} to review errors." + )) + } else { + cli::cli_alert_success( + c( + "Hub correctly configured! \n", + "{.path admin.json}, {.path tasks.json} and {.path model-metadata-schema.json} ", + "all valid." + ) + ) + } + + attr(validations, "config_dir") <- fs::path(hub_path, "hub-config") + attr(validations, "schema_version") <- schema_version + attr(validations, "schema_url") <- gsub( + "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/", + "https://github.com/Infectious-Disease-Modeling-Hubs/schemas/tree/", + unique(schema_url_dirnames), + fixed = TRUE + ) + + return(validations) +} diff --git a/R/validate_model_metadata_schema.R b/R/validate_model_metadata_schema.R new file mode 100644 index 0000000..14c69f4 --- /dev/null +++ b/R/validate_model_metadata_schema.R @@ -0,0 +1,89 @@ +#' Validate model-metadata-schema config file +#' +#' @inherit validate_config params return +#' @details +#' Checks that a `model-metadata-schema.json` config file exists in `hub-config`, +#' can be successfully parsed and contains at least either a `model_id` property or +#' both `team_abbr` and `model_abbr` properties. +#' +#' @export +#' +#' @examples +#' validate_model_metadata_schema( +#' hub_path = system.file( +#' "testhubs/simple/", +#' package = "hubUtils" +#' ) +#' ) +validate_model_metadata_schema <- function(hub_path = ".") { + if (!(requireNamespace("jsonvalidate"))) { + cli::cli_abort( + "Package {.pkg jsonvalidate} must be installed to use {.fn validate_config}. + Please install it to continue. + Alternatively, to be able to use all packages required for hub maintainer functionality, + re-install {.pkg hubUtils} using argument {.code dependencies = TRUE}" + ) + } + checkmate::assert_directory_exists(hub_path) + config_path <- fs::path(hub_path, "hub-config", + "model-metadata-schema", + ext = "json" + ) + # check config file to be checked exists and is correct extension + checkmate::assert_file_exists(config_path, extension = "json") + + schema <- try( + jsonvalidate::json_validator( + config_path, + engine = "ajv" + ), + silent = TRUE + ) + + if (inherits(schema, "try-error")) { + cli::cli_abort( + c( + "x" = "Parsing error detected in {.path model-metadata-schema.json}", + "!" = attr(schema, "condition")$message + ) + ) + } + + validation <- TRUE + attr(validation, "config_path") <- config_path + attr(validation, "schema_version") <- NULL + attr(validation, "schema_url") <- NULL + + properties <- hubUtils::read_config(hub_path, "model-metadata-schema") %>% + purrr::pluck("properties") %>% + names() + + if ( + isFALSE( + "model_id" %in% properties || + all(c("model_abbr", "team_abbr") %in% properties) + ) + ) { + errors_tbl <- data.frame( + instancePath = "", + schemaPath = "properties", + keyword = "required", + message = "must have required properties: either 'model_id' or both 'team_abbr' and 'model_abbr'.", + schema = "", + data = cli::format_inline("{properties}") + ) + + validation[] <- FALSE + attr(validation, "errors") <- errors_tbl + } + if (validation) { + cli::cli_alert_success( + "Successfully validated config file {.file {config_path}}" + ) + } else { + cli::cli_warn( + "Schema errors detected in config file {.file {config_path}}" + ) + } + return(validation) +} diff --git a/R/view_config_val_errors.R b/R/view_config_val_errors.R new file mode 100644 index 0000000..cc72c47 --- /dev/null +++ b/R/view_config_val_errors.R @@ -0,0 +1,310 @@ +#' Print a concise and informative version of validation errors table. +#' +#' @param x output of [validate_config()]. +#' +#' @return prints the errors attribute of x in an informative format to the viewer. Only +#' available in interactive mode. +#' @export +#' @seealso [validate_config()] +#' @family functions supporting config file validation +#' @examples +#' \dontrun{ +#' config_path <- system.file("error-schema/tasks-errors.json", +#' package = "hubUtils" +#' ) +#' validate_config(config_path = config_path, config = "tasks") |> +#' view_config_val_errors() +#' } +view_config_val_errors <- function(x) { + if (all(unlist(x))) { + cli::cli_alert_success(c( + "Validation of {.path {attr(x, 'config_path')}}", + "{.path {attr(x, 'config_dir')}} was successful.", " + No validation errors to display." + )) + return(invisible(NULL)) + } + + if (length(x) > 1L) { + error_df <- purrr::map2( + x, names(x), + ~ compile_errors(.x, .y) %>% + process_error_df() + ) %>% + purrr::list_rbind() + val_path <- attr(x, "config_dir") + val_type <- "directory" + error_loc_columns <- c( + "fileName", + "instancePath", + "schemaPath" + ) + } else { + error_df <- attr(x, "errors") %>% + process_error_df() + val_path <- attr(x, "config_path") + val_type <- "file" + error_loc_columns <- c( + "instancePath", + "schemaPath" + ) + } + + schema_version <- attr(x, "schema_version") + schema_url <- attr(x, "schema_url") + + title <- gt::md("**`hubUtils` config validation error report**") + subtitle <- gt::md( + glue::glue("Report for {val_type} **`{val_path}`** using + schema version [**{schema_version}**]({schema_url})") + ) + + + + # Create table ---- + gt::gt(error_df) %>% + gt::tab_header( + title = title, + subtitle = subtitle + ) %>% + gt::tab_spanner( + label = gt::md("**Error location**"), + columns = error_loc_columns + ) %>% + gt::tab_spanner( + label = gt::md("**Schema details**"), + columns = c( + "keyword", + "message", + "schema" + ) + ) %>% + gt::tab_spanner( + label = gt::md("**Config**"), + columns = "data" + ) %>% + gt::fmt_markdown(columns = c( + "instancePath", + "schemaPath", + "schema" + )) %>% + gt::tab_style( + style = gt::cell_text(whitespace = "pre"), + locations = gt::cells_body(columns = c( + "instancePath", + "schemaPath", + "schema" + )) + ) %>% + gt::tab_style( + style = gt::cell_text(whitespace = "pre-wrap"), + locations = gt::cells_body(columns = "schema") + ) %>% + gt::tab_style( + style = list( + gt::cell_fill(color = "#F9E3D6"), + gt::cell_text(weight = "bold") + ), + locations = gt::cells_body( + columns = c("message", "data") + ) + ) %>% + gt::cols_width( + "schema" ~ gt::pct(1.5 / 6 * 100), + "data" ~ gt::pct(1 / 6 * 100), + "message" ~ gt::pct(1 / 6 * 100) + ) %>% + gt::cols_align( + align = "center", + columns = c( + "keyword", + "message", + "data" + ) + ) %>% + gt::tab_options( + column_labels.font.weight = "bold", + table.margin.left = gt::pct(2), + table.margin.right = gt::pct(2), + data_row.padding = gt::px(5), + heading.background.color = "#F0F3F5", + column_labels.background.color = "#F0F3F5" + ) %>% + gt::tab_source_note( + source_note = gt::md("For more information, please consult the + [**`hubDocs` documentation**.](https://hubdocs.readthedocs.io/en/latest/)") + ) +} + + + +path_to_tree <- function(x) { + # Split up path and remove blank and root elements + paths <- strsplit(x, "/") %>% + unlist() %>% + as.list() + paths <- paths[!(paths == "" | paths == "#")] + + # Highlight property names and convert from 0 to 1 array index + paths <- paths %>% + purrr::map_if( + !is.na(as.numeric(paths)), + ~ as.numeric(.x) + 1 + ) %>% + purrr::map_if( + !paths %in% c("items", "properties"), + ~ paste0("**", .x, "**") + ) %>% + unlist() %>% + suppressWarnings() + + # build path tree + if (length(paths) > 1L) { + for (i in 2:length(paths)) { + paths[i] <- paste0( + "\u2514", + paste(rep("\u2500", times = i - 2), + collapse = "" + ), + paths[i] + ) + } + } + paste(paths, collapse = " \n ") +} + + + +dataframe_to_markdown <- function(x) { + split(x, seq_len(nrow(x))) %>% + purrr::map( + ~ unlist(.x, use.names = TRUE) %>% + stats::setNames(gsub("properties\\.", "", names(.))) %>% + stats::setNames(gsub("\\.", "-", names(.))) %>% + remove_null_properties() %>% + paste0("**", names(.), ":** ", .) %>% + paste(collapse = " \n ") + ) %>% + unlist(use.names = TRUE) %>% + paste0("**", names(.), "** \n ", .) %>% + paste(collapse = "\n\n ") %>% + gsub("[^']NA", "'NA'", .) +} + + + +process_element <- function(x) { + if (inherits(x, "data.frame")) { + return(dataframe_to_markdown(x)) + } + + vector_to_character(x) +} + +vector_to_character <- function(x) { + # unlist and collapse list columns + out <- unlist(x, recursive = TRUE, use.names = TRUE) + + if (length(names(out)) != 0L) { + out <- paste0(names(out), ": ", out) + } + out %>% paste(collapse = ", ") +} + + + +remove_null_properties <- function(x) { + null_maxitem <- names(x[is.na(x) & grepl("maxItems", names(x))]) + x[!names(x) %in% c( + null_maxitem, + gsub( + "maxItems", "const", + null_maxitem + ) + )] +} + + + +remove_superfluous_enum_rows <- function(errors_tbl) { + dup_inst <- duplicated(errors_tbl$instancePath) + + if (any(dup_inst)) { + dup_idx <- errors_tbl$instancePath[dup_inst] %>% + purrr::map(~ which(errors_tbl$instancePath == .x)) + + dup_keywords <- purrr::map(dup_idx, ~ errors_tbl$keyword[.x]) + + dup_unneccessary <- purrr::map_lgl( + dup_keywords, + ~ all(.x == c("type", "enum") | .x == c("type", "const")) + ) + + if (any(dup_unneccessary)) { + remove_idx <- purrr::map_int( + dup_idx[dup_unneccessary], + ~ .x[2] + ) + errors_tbl <- errors_tbl[-remove_idx, ] + } + } + + errors_tbl +} + +compile_errors <- function(x, file_name) { + errors_tbl <- attr(x, "errors") + if (!is.null(errors_tbl)) { + cbind( + fileName = rep(fs::path(file_name, ext = "json"), nrow(errors_tbl)), + errors_tbl + ) + } +} + + +process_error_df <- function(errors_tbl) { + if (is.null(errors_tbl)) { + return(NULL) + } + errors_tbl[c("dataPath", "parentSchema", "params")] <- NULL + errors_tbl <- errors_tbl[!grepl("oneOf.+", errors_tbl$schemaPath), ] + errors_tbl <- remove_superfluous_enum_rows(errors_tbl) + + # Get rid of unnecessarily verbose data entry when a required property is + # missing. Addressing this is dependent on the data column data type. + if (any(errors_tbl$keyword == "required")) { + if (inherits(errors_tbl$data, "data.frame")) { + errors_tbl$data <- "" + } else { + errors_tbl$data[errors_tbl$keyword == "required"] <- "" + } + } + # Get rid of unnecessarily verbose data entry when an additionalProperties property is + # detected Addressing this is dependent on the data column data type. + if (any(errors_tbl$keyword == "additionalProperties")) { + if (inherits(errors_tbl$data, "data.frame")) { + errors_tbl$data <- "" + } else { + errors_tbl$data[errors_tbl$keyword == "additionalProperties"] <- "" + } + } + + error_df <- split(errors_tbl, seq_len(nrow(errors_tbl))) %>% + purrr::map( + ~ unlist(.x, recursive = FALSE) %>% + purrr::map(~ process_element(.x)) %>% + tibble::as_tibble() + ) %>% + purrr::list_rbind() %>% + # split long column names + stats::setNames(gsub("\\.", " ", names(.))) + + + # format path and error message columns + error_df[["schemaPath"]] <- purrr::map_chr(error_df[["schemaPath"]], path_to_tree) + error_df[["instancePath"]] <- purrr::map_chr(error_df[["instancePath"]], path_to_tree) + error_df[["message"]] <- paste("\u274c", error_df[["message"]]) + + error_df +} diff --git a/README.Rmd b/README.Rmd index 43410d0..6b8aa74 100644 --- a/README.Rmd +++ b/README.Rmd @@ -35,6 +35,9 @@ You can install the development version of hubAdmin from [GitHub](https://github remotes::install_github("Infectious-Disease-Modeling-Hubs/hubAdmin") ``` +> ##### **Note:** +> If you have any problems with installation of package `V8` (more likely on Linux), please consult the [following installation instructions](https://github.com/jeroen/V8#installation). + ## Example This is a basic example which shows you how to solve a common problem: diff --git a/README.md b/README.md index 16b2aef..40a7aa7 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,12 @@ You can install the development version of hubAdmin from remotes::install_github("Infectious-Disease-Modeling-Hubs/hubAdmin") ``` +> ##### **Note:** +> +> If you have any problems with installation of package `V8` (more +> likely on Linux), please consult the [following installation +> instructions](https://github.com/jeroen/V8#installation). + ## Example This is a basic example which shows you how to solve a common problem: diff --git a/data-raw/hub_locations.R b/data-raw/hub_locations.R new file mode 100644 index 0000000..b1aed53 --- /dev/null +++ b/data-raw/hub_locations.R @@ -0,0 +1,13 @@ +## code to prepare `hub_locations` dataset goes here +library(dplyr) +hub_locations_us <- covidHubUtils::hub_locations |> + select(-population, -full_location_name) |> + tibble::as_tibble() + +hub_locations_eu <- covidHubUtils::hub_locations_ecdc |> + select(-population) |> + tibble::as_tibble() + +usethis::use_data(hub_locations_us, overwrite = TRUE) + +usethis::use_data(hub_locations_eu, overwrite = TRUE) diff --git a/data-raw/sysdata.R b/data-raw/sysdata.R new file mode 100644 index 0000000..e0e2ba8 --- /dev/null +++ b/data-raw/sysdata.R @@ -0,0 +1,10 @@ +## code to prepare `json_datatypes` dataset goes here +json_datatypes <- c( + string = "character", + boolean = "logical", + integer = "integer", + number = "double" +) +## code to prepare `sysdata` dataset goes here + +usethis::use_data(json_datatypes, overwrite = TRUE, internal = TRUE) diff --git a/data/hub_locations_eu.rda b/data/hub_locations_eu.rda new file mode 100644 index 0000000..1f8a168 Binary files /dev/null and b/data/hub_locations_eu.rda differ diff --git a/data/hub_locations_us.rda b/data/hub_locations_us.rda new file mode 100644 index 0000000..5ca043d Binary files /dev/null and b/data/hub_locations_us.rda differ diff --git a/hubAdmin.Rproj b/hubAdmin.Rproj index fd8dd28..97b9e3f 100644 --- a/hubAdmin.Rproj +++ b/hubAdmin.Rproj @@ -19,4 +19,4 @@ LineEndingConversion: Posix BuildType: Package PackageUseDevtools: Yes PackageInstallArgs: --no-multiarch --with-keep.source -PackageRoxygenize: rd,collate,namespace +PackageRoxygenize: rd,collate,namespace,vignette diff --git a/man/create_config.Rd b/man/create_config.Rd new file mode 100644 index 0000000..2eb088d --- /dev/null +++ b/man/create_config.Rd @@ -0,0 +1,80 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_config.R +\name{create_config} +\alias{create_config} +\title{Create a \code{config} class object.} +\usage{ +create_config(rounds) +} +\arguments{ +\item{rounds}{An object of class \code{rounds} created using function +\code{\link[=create_rounds]{create_rounds()}}} +} +\value{ +a named list of class \code{config}. +} +\description{ +Create a representation of a complete \code{"tasks"} config file as a list object of +class \code{config}. This can be written out to a \code{tasks.json} file. +} +\details{ +For more details consult +the \href{https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file}{documentation on \code{tasks.json} Hub config files}. +} +\examples{ +rounds <- create_rounds( + create_round( + round_id_from_variable = TRUE, + round_id = "origin_date", + model_tasks = create_model_tasks( + create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = NULL, + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ) + ), + submissions_due = list( + relative_to = "origin_date", + start = -4L, + end = 2L + ) + ) +) +create_config(rounds) +} +\seealso{ +\code{\link[=create_rounds]{create_rounds()}} +} diff --git a/man/create_model_task.Rd b/man/create_model_task.Rd new file mode 100644 index 0000000..bc675c7 --- /dev/null +++ b/man/create_model_task.Rd @@ -0,0 +1,66 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_model_task.R +\name{create_model_task} +\alias{create_model_task} +\title{Create an object of class \code{model_task}} +\usage{ +create_model_task(task_ids, output_type, target_metadata) +} +\arguments{ +\item{task_ids}{object of class \code{model_task}.} + +\item{output_type}{object of class \code{output_type}.} + +\item{target_metadata}{object of class \code{target_metadata}.} +} +\value{ +a named list of class \code{model_task}. +} +\description{ +Create an object of class \code{model_task} representing a model task. Multiple +model tasks can be combined using function \code{\link[=create_model_tasks]{create_model_tasks()}}. +} +\examples{ +create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = NULL, + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) +) +} +\seealso{ +\code{\link[=create_task_ids]{create_task_ids()}}, \code{\link[=create_output_type]{create_output_type()}}, \code{\link[=create_target_metadata]{create_target_metadata()}}, +\code{\link[=create_model_tasks]{create_model_tasks()}} +} diff --git a/man/create_model_tasks.Rd b/man/create_model_tasks.Rd new file mode 100644 index 0000000..ec7765e --- /dev/null +++ b/man/create_model_tasks.Rd @@ -0,0 +1,177 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_model_tasks.R +\name{create_model_tasks} +\alias{create_model_tasks} +\title{Create a \code{model_tasks} class object.} +\usage{ +create_model_tasks(...) +} +\arguments{ +\item{...}{objects of class \code{model_tasks} created using function +\code{\link[=create_model_task]{create_model_task()}}} +} +\value{ +a named list of class \code{model_tasks}. +} +\description{ +Create a \code{model_tasks} class object. +} +\examples{ +create_model_tasks( + create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = NULL, + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ) +) +create_model_tasks( + create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("target", + required = NULL, + optional = c("inc death", "inc hosp") + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ), + create_output_type_median( + is_required = FALSE, + value_type = "double" + ), + create_output_type_quantile( + required = c(0.25, 0.5, 0.75), + optional = c( + 0.1, 0.2, 0.3, 0.4, 0.6, + 0.7, 0.8, 0.9 + ), + value_type = "double", + value_minimum = 0 + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ), + create_target_metadata_item( + target_id = "inc death", + target_name = "Weekly incident influenza deaths", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc death"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ), + create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("target", + required = "flu hosp rt chng", + optional = NULL + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_pmf( + required = c( + "large_decrease", + "decrease", + "stable", + "increase", + "large_increase" + ), + optional = NULL, + value_type = "double" + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "flu hosp rt chng", + target_name = "Weekly influenza hospitalization rate change", + target_units = "rate per 100,000 population", + target_keys = list(target = "flu hosp rt chng"), + target_type = "nominal", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ) +) +} +\seealso{ +\code{\link[=create_model_task]{create_model_task()}} +} diff --git a/man/create_output_type.Rd b/man/create_output_type.Rd new file mode 100644 index 0000000..547a3d5 --- /dev/null +++ b/man/create_output_type.Rd @@ -0,0 +1,49 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_output_type.R +\name{create_output_type} +\alias{create_output_type} +\title{Create an \code{output_type} class object.} +\usage{ +create_output_type(...) +} +\arguments{ +\item{...}{objects of class \code{output_type_item} created using functions from the +\verb{create_output_type_*()} family of functions.} +} +\value{ +a named list of class \code{output_type}. +} +\description{ +Create an \code{output_type} class object. +} +\details{ +For more details consult +the \href{https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file}{documentation on \code{tasks.json} Hub config files}. +} +\examples{ +create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ), + create_output_type_median( + is_required = FALSE, + value_type = "double" + ), + create_output_type_quantile( + required = c(0.25, 0.5, 0.75), + optional = c( + 0.1, 0.2, 0.3, 0.4, 0.6, + 0.7, 0.8, 0.9 + ), + value_type = "double", + value_minimum = 0 + ) +) +} +\seealso{ +\code{\link[=create_output_type_mean]{create_output_type_mean()}}, \code{\link[=create_output_type_median]{create_output_type_median()}}, +\code{\link[=create_output_type_quantile]{create_output_type_quantile()}}, \code{\link[=create_output_type_cdf]{create_output_type_cdf()}}, +\code{\link[=create_output_type_pmf]{create_output_type_pmf()}}, \code{\link[=create_output_type_sample]{create_output_type_sample()}} +} diff --git a/man/create_output_type_mean.Rd b/man/create_output_type_mean.Rd new file mode 100644 index 0000000..3e68bc2 --- /dev/null +++ b/man/create_output_type_mean.Rd @@ -0,0 +1,84 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_output_type_item.R +\name{create_output_type_mean} +\alias{create_output_type_mean} +\alias{create_output_type_median} +\title{Create a point estimate output type object of class \code{output_type_item}} +\usage{ +create_output_type_mean( + is_required, + value_type, + value_minimum = NULL, + value_maximum = NULL, + schema_version = "latest", + branch = "main" +) + +create_output_type_median( + is_required, + value_type, + value_minimum = NULL, + value_maximum = NULL, + schema_version = "latest", + branch = "main" +) +} +\arguments{ +\item{is_required}{Logical. Is the output type required?} + +\item{value_type}{Character string. The data type of the output_type values.} + +\item{value_minimum}{Numeric. The inclusive minimum of output_type values.} + +\item{value_maximum}{Numeric. The inclusive maximum of output_type values.} + +\item{schema_version}{Character string specifying the json schema version to +be used for validation. The default value \code{"latest"} will use the latest version +available in the Infectious Disease Modeling Hubs +\href{https://github.com/Infectious-Disease-Modeling-Hubs/schemas}{schemas repository}. +Alternatively, a specific version of a schema (e.g. \code{"v0.0.1"}) can be +specified.} + +\item{branch}{The branch of the Infectious Disease Modeling Hubs +\href{https://github.com/Infectious-Disease-Modeling-Hubs/schemas}{schemas repository} +from which to fetch schema. Defaults to \code{"main"}.} +} +\value{ +a named list of class \code{output_type_item} representing a \code{mean} or +\code{median} output type. +} +\description{ +Create a representation of a \code{mean} or \code{median} output type as a list object of +class \code{output_type_item}. This can be combined with +additional \code{output_type_item} objects using function \code{\link[=create_output_type]{create_output_type()}} to +create an \code{output_type} object for a given model_task. +This can be combined with other building blocks which can then be written as +or appended to \code{tasks.json} Hub config files. +} +\details{ +For more details consult +the \href{https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file}{documentation on \code{tasks.json} Hub config files}. +} +\section{Functions}{ +\itemize{ +\item \code{create_output_type_mean()}: Create a list representation of a \code{mean} +output type. + +\item \code{create_output_type_median()}: Create a list representation of a \code{median} +output type. + +}} +\examples{ +create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L +) +create_output_type_median( + is_required = FALSE, + value_type = "integer" +) +} +\seealso{ +\code{\link[=create_output_type]{create_output_type()}} +} diff --git a/man/create_output_type_quantile.Rd b/man/create_output_type_quantile.Rd new file mode 100644 index 0000000..46d1346 --- /dev/null +++ b/man/create_output_type_quantile.Rd @@ -0,0 +1,136 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_output_type_item.R +\name{create_output_type_quantile} +\alias{create_output_type_quantile} +\alias{create_output_type_cdf} +\alias{create_output_type_pmf} +\alias{create_output_type_sample} +\title{Create a distribution output type object of class \code{output_type_item}} +\usage{ +create_output_type_quantile( + required, + optional, + value_type, + value_minimum = NULL, + value_maximum = NULL, + schema_version = "latest", + branch = "main" +) + +create_output_type_cdf( + required, + optional, + value_type, + schema_version = "latest", + branch = "main" +) + +create_output_type_pmf( + required, + optional, + value_type, + schema_version = "latest", + branch = "main" +) + +create_output_type_sample( + required, + optional, + value_type, + value_minimum = NULL, + value_maximum = NULL, + schema_version = "latest", + branch = "main" +) +} +\arguments{ +\item{required}{Atomic vector of required \code{output_type_id} values. Can be NULL if +all values are optional.} + +\item{optional}{Atomic vector of optional \code{output_type_id} values. Can be NULL if +all values are required.} + +\item{value_type}{Character string. The data type of the output_type values.} + +\item{value_minimum}{Numeric. The inclusive minimum of output_type values.} + +\item{value_maximum}{Numeric. The inclusive maximum of output_type values.} + +\item{schema_version}{Character string specifying the json schema version to +be used for validation. The default value \code{"latest"} will use the latest version +available in the Infectious Disease Modeling Hubs +\href{https://github.com/Infectious-Disease-Modeling-Hubs/schemas}{schemas repository}. +Alternatively, a specific version of a schema (e.g. \code{"v0.0.1"}) can be +specified.} + +\item{branch}{The branch of the Infectious Disease Modeling Hubs +\href{https://github.com/Infectious-Disease-Modeling-Hubs/schemas}{schemas repository} +from which to fetch schema. Defaults to \code{"main"}.} +} +\value{ +a named list of class \code{output_type_item} representing a \code{quantile}, +\code{cdf}, \code{pmf} or \code{sample} output type. +} +\description{ +Create a representation of a \code{quantile}, \code{cdf}, \code{pmf} or \code{sample} output +type as a list object of class \code{output_type_item}. This can be combined with +additional \code{output_type_item}s using function \code{\link[=create_output_type]{create_output_type()}} to +create an \code{output_type} object for a given model_task. +This can be combined with other building blocks which can then be written as +or appended to \code{tasks.json} Hub config files. +output type. +This can be combined with other building blocks which can then be written as +or appended to \code{tasks.json} Hub config files. +} +\details{ +For more details consult +the \href{https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file}{documentation on \code{tasks.json} Hub config files}. +} +\section{Functions}{ +\itemize{ +\item \code{create_output_type_quantile()}: Create a list representation of a \code{quantile} +output type. + +\item \code{create_output_type_cdf()}: Create a list representation of a \code{cdf} +output type. + +\item \code{create_output_type_pmf()}: Create a list representation of a \code{pmf} +output type. + +\item \code{create_output_type_sample()}: Create a list representation of a \code{sample} +output type. + +}} +\examples{ +create_output_type_quantile( + required = c(0.25, 0.5, 0.75), + optional = c( + 0.1, 0.2, 0.3, 0.4, 0.6, + 0.7, 0.8, 0.9 + ), + value_type = "double", + value_minimum = 0 +) +create_output_type_cdf( + required = c(10, 20), + optional = NULL, + value_type = "double" +) +create_output_type_cdf( + required = NULL, + optional = c("EW202240", "EW202241", "EW202242"), + value_type = "double" +) +create_output_type_pmf( + required = NULL, + optional = c("low", "moderate", "high", "extreme"), + value_type = "double" +) +create_output_type_sample( + required = 1:10, optional = 11:15, + value_type = "double" +) +} +\seealso{ +\code{\link[=create_output_type]{create_output_type()}} +} diff --git a/man/create_round.Rd b/man/create_round.Rd new file mode 100644 index 0000000..e5e6a28 --- /dev/null +++ b/man/create_round.Rd @@ -0,0 +1,128 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_round.R +\name{create_round} +\alias{create_round} +\title{Create an object of class \code{round}} +\usage{ +create_round( + round_id_from_variable, + round_id, + round_name = NULL, + model_tasks, + submissions_due, + last_data_date = NULL, + file_format = NULL +) +} +\arguments{ +\item{round_id_from_variable}{logical. Whether \code{round_id} is inferred from the +values of a \code{task_id} variable within the \code{model_tasks} \code{model_task} items.} + +\item{round_id}{character string. The round identifier. +If \code{round_id_from_variable = TRUE}, \code{round_id} should be the name of a \code{task_id} +variable present in all \code{model_tasks} \code{model_task} items.} + +\item{round_name}{character string. An optional round name. This can be useful +for internal referencing of rounds, for examples, when a date is used as +\code{round_id} but hub maintainers and teams also refer to rounds as round-1, round-2 etc.} + +\item{model_tasks}{an object of class \code{model_tasks} created with function +\code{\link[=create_model_tasks]{create_model_tasks()}}.} + +\item{submissions_due}{named list conforming to one of the two following structures: +\enumerate{ +\item Submission dates for round is determined relative to an origin date. +\itemize{ +\item \code{relative_to}: character string of the name of the \code{task_id} variable containing +origin dates in relation to which submission start and end dates are determined. +\item \code{start}: integer. Difference in days between start and origin date. +\item \code{end}: integer. Difference in days between end and origin date. +} +\item Submission dates for round are provided explicitly. +\itemize{ +\item \code{start}: character. Submission start date in ISO 8601 format (YYYY-MM-DD). +\item \code{end}: character. Submission end date in ISO 8601 format (YYYY-MM-DD). +} +}} + +\item{last_data_date}{character date in ISO 8601 format (YYYY-MM-DD). +The last date with recorded data in the data set used as input to a model. +Optional.} + +\item{file_format}{character string. An optional specification of a file format for the round. +This option in only available for some versions of the schema and is ignored if +not allowed in the version of the schema used. It also overrides any specification +of file format in \code{admin.json}. For more details on whether this argument can +be used as well as available formats, please consult +the \href{https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file}{documentation on \code{tasks.json} Hub config files}.} +} +\value{ +a named list of class \code{round}. +} +\description{ +Create a representation of a round item as a list object of +class \code{round}. This can be combined with +additional \code{round} objects using function \code{\link[=create_rounds]{create_rounds()}}. +Such building blocks can ultimately be combined and then written out as or +appended to \code{tasks.json} Hub config files. +} +\details{ +For more details consult +the \href{https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file}{documentation on \code{tasks.json} Hub config files}. +} +\examples{ +model_tasks <- create_model_tasks( + create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = NULL, + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ) +) +create_round( + round_id_from_variable = TRUE, + round_id = "origin_date", + model_tasks = model_tasks, + submissions_due = list( + relative_to = "origin_date", + start = -4L, + end = 2L + ), + last_data_date = "2023-01-02" +) +} +\seealso{ +\code{\link[=create_rounds]{create_rounds()}} +} diff --git a/man/create_rounds.Rd b/man/create_rounds.Rd new file mode 100644 index 0000000..1119f9b --- /dev/null +++ b/man/create_rounds.Rd @@ -0,0 +1,180 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_rounds.R +\name{create_rounds} +\alias{create_rounds} +\title{Create a \code{rounds} class object.} +\usage{ +create_rounds(...) +} +\arguments{ +\item{...}{objects of class \code{round} created using function +\code{\link[=create_round]{create_round()}}} +} +\value{ +a named list of class \code{rounds}. +} +\description{ +Create a \code{rounds} class object. +} +\details{ +For more details consult +the \href{https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file}{documentation on \code{tasks.json} Hub config files}. +} +\examples{ +model_tasks <- create_model_tasks( + create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = NULL, + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ) +) +# Create a rounds object with a single rounds where round_id is defined through +# the value of a task_id variable. +create_rounds( + create_round( + round_id_from_variable = TRUE, + round_id = "origin_date", + model_tasks = model_tasks, + submissions_due = list( + relative_to = "origin_date", + start = -4L, + end = 2L + ) + ) +) +# Create a rounds object with two rounds and user defined round_ids +create_rounds( + create_round( + round_id_from_variable = FALSE, + round_id = "round_1", + model_tasks = + create_model_tasks( + create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-09" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = NULL, + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ) + ), + submissions_due = list( + start = "2023-01-05", + end = "2023-01-11" + ), + last_data_date = "2023-01-06" + ), + create_round( + round_id_from_variable = FALSE, + round_id = "round_2", + model_tasks = + create_model_tasks( + create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-16" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = NULL, + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ) + ), + submissions_due = list( + start = "2023-01-12", + end = "2023-01-18" + ), + last_data_date = "2023-01-13" + ) +) +} +\seealso{ +\code{\link[=create_round]{create_round()}} +} diff --git a/man/create_target_metadata.Rd b/man/create_target_metadata.Rd new file mode 100644 index 0000000..68ec555 --- /dev/null +++ b/man/create_target_metadata.Rd @@ -0,0 +1,47 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_target_metadata.R +\name{create_target_metadata} +\alias{create_target_metadata} +\title{Create a \code{target_metadata} class object.} +\usage{ +create_target_metadata(...) +} +\arguments{ +\item{...}{objects of class \code{target_metadata_item} created using function +\code{\link[=create_target_metadata_item]{create_target_metadata_item()}}} +} +\value{ +a named list of class \code{target_metadata}. +} +\description{ +Create a \code{target_metadata} class object. +} +\details{ +For more details consult +the \href{https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file}{documentation on \code{tasks.json} Hub config files}. +} +\examples{ +create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ), + create_target_metadata_item( + target_id = "inc death", + target_name = "Weekly incident influenza deaths", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc death"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) +) +} +\seealso{ +\code{\link[=create_target_metadata_item]{create_target_metadata_item()}} +} diff --git a/man/create_target_metadata_item.Rd b/man/create_target_metadata_item.Rd new file mode 100644 index 0000000..8ee5d3a --- /dev/null +++ b/man/create_target_metadata_item.Rd @@ -0,0 +1,89 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_target_metadata_item.R +\name{create_target_metadata_item} +\alias{create_target_metadata_item} +\title{Create an object of class \code{target_metadata_item}} +\usage{ +create_target_metadata_item( + target_id, + target_name, + target_units, + target_keys = NULL, + description = NULL, + target_type, + is_step_ahead, + time_unit = NULL, + schema_version = "latest", + branch = "main" +) +} +\arguments{ +\item{target_id}{character string. Short description that uniquely identifies +the target.} + +\item{target_name}{character string. A longer human readable target description +that could be used, for example, as a visualisation axis label.} + +\item{target_units}{character string. Unit of observation of the target.} + +\item{target_keys}{named list or \code{NULL}. Should be \code{NULL}, in the case +where the target is not specified as a task_id but is specified solely through +the \code{target_id} argument. Otherwise, should be a named list of one or more +character strings. The name of each element should match a task_id variable +within the same model_tasks object. Each element should be of length 1. +Each value, or the combination of values if multiple keys are specified, +define a single target value.} + +\item{description}{character string (optional). An optional verbose description +of the target that might include information such as definitions of a 'rate' or similar.} + +\item{target_type}{character string. Target statistical data type. Consult the +\href{https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#model-tasks-tasks-json-interactive-schema}{appropriate version of the hub schema} +for potential values.} + +\item{is_step_ahead}{logical. Whether the target is part of a sequence of values} + +\item{time_unit}{character string. If \code{is_step_ahead} is \code{TRUE}, then this +argument is required and defines the unit of time steps. if \code{is_step_ahead} is +\code{FALSE}, then this argument is not required and will be ignored if given.} + +\item{schema_version}{Character string specifying the json schema version to +be used for validation. The default value \code{"latest"} will use the latest version +available in the Infectious Disease Modeling Hubs +\href{https://github.com/Infectious-Disease-Modeling-Hubs/schemas}{schemas repository}. +Alternatively, a specific version of a schema (e.g. \code{"v0.0.1"}) can be +specified.} + +\item{branch}{The branch of the Infectious Disease Modeling Hubs +\href{https://github.com/Infectious-Disease-Modeling-Hubs/schemas}{schemas repository} +from which to fetch schema. Defaults to \code{"main"}.} +} +\value{ +a named list of class \code{target_metadata_item}. +} +\description{ +Create a representation of a target_metadata item as a list object of +class \code{target_metadata_item}. This can be combined with +additional target_metadata items using function \code{\link[=create_target_metadata]{create_target_metadata()}} to +create a target_metadata object for a given model_task. +Such building blocks can ultimately be combined and then written out as or +appended to \code{tasks.json} Hub config files. +} +\details{ +For more details consult +the \href{https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file}{documentation on \code{tasks.json} Hub config files}. +} +\examples{ +create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" +) +} +\seealso{ +\code{\link[=create_target_metadata]{create_target_metadata()}} +} diff --git a/man/create_task_id.Rd b/man/create_task_id.Rd new file mode 100644 index 0000000..15999dd --- /dev/null +++ b/man/create_task_id.Rd @@ -0,0 +1,76 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_task_id.R +\name{create_task_id} +\alias{create_task_id} +\title{#' Create an object of class \code{task_id}} +\usage{ +create_task_id( + name, + required, + optional, + schema_version = "latest", + branch = "main" +) +} +\arguments{ +\item{name}{character string, Name of task_id to create.} + +\item{required}{Atomic vector of required task_id values. Can be \code{NULL} if all +values are optional.} + +\item{optional}{Atomic vector of optional task_id values. Can be \code{NULL} if all +values are required.} + +\item{schema_version}{Character string specifying the json schema version to +be used for validation. The default value \code{"latest"} will use the latest version +available in the Infectious Disease Modeling Hubs +\href{https://github.com/Infectious-Disease-Modeling-Hubs/schemas}{schemas repository}. +Alternatively, a specific version of a schema (e.g. \code{"v0.0.1"}) can be +specified.} + +\item{branch}{The branch of the Infectious Disease Modeling Hubs +\href{https://github.com/Infectious-Disease-Modeling-Hubs/schemas}{schemas repository} +from which to fetch schema. Defaults to \code{"main"}.} +} +\value{ +a named list of class \code{task_id} representing a task ID. +} +\description{ +Create a representation of a task ID item as a list object of +class \code{task_id}. This can be combined with +additional \code{task_id} objects using function \code{\link[=create_task_ids]{create_task_ids()}} to +create a \code{task_ids} class object for a given model_task. +Such building blocks can ultimately be combined and then written out as or +appended to \code{tasks.json} Hub config files. +} +\details{ +\code{required} and \code{optional} vectors for standard task_ids defined in a Hub schema +must match data types and formats specified in the schema. For more details consult +the \href{https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file}{documentation on \code{tasks.json} Hub config files} + +JSON schema data type names differ to those in R. Use the following mappings to +create vectors of appropriate data types which will correspond to correct JSON +schema data types during config file validation.\tabular{ll}{ + json \tab R \cr + string \tab character \cr + boolean \tab logical \cr + integer \tab integer \cr + number \tab double \cr +} + + +Values across \code{required} and \code{optional} arguments must be unique. \code{required} +and \code{optional} must be of the same type (unless \code{NULL}) and both cannot be \code{NULL}. +Task_ids that represent dates must be supplied as character strings in ISO 8601 +date format (YYYY-MM-DD). If working with date objects, please convert to character +(e.g. using \code{as.character()}) before supplying as arguments. + +Task_ids not present in the schema are allowed as additional properties but the +user is responsible for providing values of the correct data type. +} +\examples{ +create_task_id("horizon", required = 1L, optional = 2:4) +} +\seealso{ +\code{\link[=create_task_ids]{create_task_ids()}} +} diff --git a/man/create_task_ids.Rd b/man/create_task_ids.Rd new file mode 100644 index 0000000..8f06fae --- /dev/null +++ b/man/create_task_ids.Rd @@ -0,0 +1,55 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_task_ids.R +\name{create_task_ids} +\alias{create_task_ids} +\title{Create a \code{task_ids} class object.} +\usage{ +create_task_ids(...) +} +\arguments{ +\item{...}{objects of class \code{task_id} created using function \code{\link[=create_task_id]{create_task_id()}}} +} +\value{ +a named list of class \code{task_ids}. +} +\description{ +Create a \code{task_ids} class object. +} +\details{ +For more details consult +the \href{https://hubdocs.readthedocs.io/en/latest/format/hub-metadata.html#hub-model-task-metadata-tasks-json-file}{documentation on \code{tasks.json} Hub config files}. +} +\examples{ +create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("scenario_id", + required = NULL, + optional = c( + "A-2021-03-28", + "B-2021-03-28" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("target", + required = "inc hosp", + optional = NULL + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) +) +} +\seealso{ +\code{\link[=create_task_id]{create_task_id()}} +} diff --git a/man/hub_locations_eu.Rd b/man/hub_locations_eu.Rd new file mode 100644 index 0000000..625cd37 --- /dev/null +++ b/man/hub_locations_eu.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\docType{data} +\name{hub_locations_eu} +\alias{hub_locations_eu} +\title{Available European locations} +\format{ +A data frame with 32 rows and 2 columns: +\describe{ +\item{location_name}{Name of the location} +\item{location}{Location abbreviation} +} +} +\usage{ +hub_locations_eu +} +\description{ +Data set with available European locations used in ECDC hubs +} +\keyword{datasets} diff --git a/man/hub_locations_us.Rd b/man/hub_locations_us.Rd new file mode 100644 index 0000000..c2a675c --- /dev/null +++ b/man/hub_locations_us.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\docType{data} +\name{hub_locations_us} +\alias{hub_locations_us} +\title{Available US locations} +\format{ +A data frame with 3202 rows and 5 columns: +\describe{ +\item{fips}{FIPS code} +\item{location_name}{Location name} +\item{geo_type}{Type of location for compatibility with +\href{https://cmu-delphi.github.io/delphi-epidata/api/covidcast_geography.html +}{EpiData API geographic codings} +} +\item{geo_value}{Location abbreviation or FIPS code for compatibility with +\href{https://cmu-delphi.github.io/delphi-epidata/api/covidcast_geography.html +}{EpiData API geographic codings}} +\item{abbreviation}{Corresponding state abbrevaition} +} +} +\usage{ +hub_locations_us +} +\description{ +Data set with available locations for a US hub +} +\keyword{datasets} diff --git a/man/pipe.Rd b/man/pipe.Rd new file mode 100644 index 0000000..a648c29 --- /dev/null +++ b/man/pipe.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils-pipe.R +\name{\%>\%} +\alias{\%>\%} +\title{Pipe operator} +\usage{ +lhs \%>\% rhs +} +\arguments{ +\item{lhs}{A value or the magrittr placeholder.} + +\item{rhs}{A function call using the magrittr semantics.} +} +\value{ +The result of calling \code{rhs(lhs)}. +} +\description{ +See \code{magrittr::\link[magrittr:pipe]{\%>\%}} for details. +} +\keyword{internal} diff --git a/man/validate_config.Rd b/man/validate_config.Rd new file mode 100644 index 0000000..7ace935 --- /dev/null +++ b/man/validate_config.Rd @@ -0,0 +1,73 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/validate_config.R +\name{validate_config} +\alias{validate_config} +\title{Validate a hub config file against a Infectious Disease Modeling Hubs schema} +\usage{ +validate_config( + hub_path = ".", + config = c("tasks", "admin"), + config_path = NULL, + schema_version = "from_config", + branch = "main" +) +} +\arguments{ +\item{hub_path}{Path to a local hub directory.} + +\item{config}{Name of config file to validate. One of \code{"tasks"} or \code{"admin"}.} + +\item{config_path}{Defaults to \code{NULL} which assumes all config files are in +the \code{hub-config} directory in the root of hub directory. Argument +\code{config_path} can be used to override default by providing a path to the +config file to be validated.} + +\item{schema_version}{Character string specifying the json schema version to +be used for validation. The default value \code{"from_config"} will use the +version specified in the \code{schema_version} property of the config file. +\code{"latest"} will use the latest version available in the Infectious Disease +Modeling Hubs +\href{https://github.com/Infectious-Disease-Modeling-Hubs/schemas}{schemas repository}. +Alternatively, a specific version of a schema (e.g. \code{"v0.0.1"}) can be +specified.} + +\item{branch}{The branch of the Infectious Disease Modeling Hubs +\href{https://github.com/Infectious-Disease-Modeling-Hubs/schemas}{schemas repository} +from which to fetch schema. Defaults to \code{"main"}.} +} +\value{ +Returns the result of validation. If validation is successful, will +return \code{TRUE}. If any validation errors are detected, returns \code{FALSE} with +details of errors appended as a data.frame to an \code{errors} attribute. +To access +the errors table use \code{attr(x, "errors")} where \code{x} is the output of the function. + +You can print a more concise and easier to view version of an errors table with +\code{\link[=view_config_val_errors]{view_config_val_errors()}}. +} +\description{ +Validate a hub config file against a Infectious Disease Modeling Hubs schema +} +\examples{ +# Valid config file +validate_config( + hub_path = system.file( + "testhubs/simple/", + package = "hubUtils" + ), + config = "tasks" +) +# Config file with errors +config_path <- system.file("error-schema/tasks-errors.json", + package = "hubUtils" +) +validate_config(config_path = config_path, config = "tasks") +} +\seealso{ +\code{\link[=view_config_val_errors]{view_config_val_errors()}} + +Other functions supporting config file validation: +\code{\link{validate_hub_config}()}, +\code{\link{view_config_val_errors}()} +} +\concept{functions supporting config file validation} diff --git a/man/validate_hub_config.Rd b/man/validate_hub_config.Rd new file mode 100644 index 0000000..cd0212c --- /dev/null +++ b/man/validate_hub_config.Rd @@ -0,0 +1,58 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/validate_hub_config.R +\name{validate_hub_config} +\alias{validate_hub_config} +\title{Validate Hub config files against Infectious Disease Modeling Hubs schema.} +\usage{ +validate_hub_config( + hub_path = ".", + schema_version = "from_config", + branch = "main" +) +} +\arguments{ +\item{hub_path}{Path to a local hub directory.} + +\item{schema_version}{Character string specifying the json schema version to +be used for validation. The default value \code{"from_config"} will use the +version specified in the \code{schema_version} property of the config file. +\code{"latest"} will use the latest version available in the Infectious Disease +Modeling Hubs +\href{https://github.com/Infectious-Disease-Modeling-Hubs/schemas}{schemas repository}. +Alternatively, a specific version of a schema (e.g. \code{"v0.0.1"}) can be +specified.} + +\item{branch}{The branch of the Infectious Disease Modeling Hubs +\href{https://github.com/Infectious-Disease-Modeling-Hubs/schemas}{schemas repository} +from which to fetch schema. Defaults to \code{"main"}.} +} +\value{ +Returns a list of the results of validation, one for each \code{hub-config} +file validated. A value of \code{TRUE} for a given file indicates that validation +was successful. +A value of \code{FALSE} for a given file indicates that validation errors were +detected. Details of errors will be appended as a data.frame to an \code{errors} attribute. +To access the errors table for a given element use \code{attr(x, "errors")} +where \code{x} is the any element of the output of the function that is \code{FALSE}. +You can print a more concise and easier to view version of an errors table with +\code{\link[=view_config_val_errors]{view_config_val_errors()}}. +} +\description{ +Validate the \code{admin.json} and \code{tasks.json} Hub config files in a single call. +} +\examples{ +validate_hub_config( + hub_path = system.file( + "testhubs/simple/", + package = "hubUtils" + ) +) +} +\seealso{ +\code{\link[=view_config_val_errors]{view_config_val_errors()}} + +Other functions supporting config file validation: +\code{\link{validate_config}()}, +\code{\link{view_config_val_errors}()} +} +\concept{functions supporting config file validation} diff --git a/man/validate_model_metadata_schema.Rd b/man/validate_model_metadata_schema.Rd new file mode 100644 index 0000000..4edb7ad --- /dev/null +++ b/man/validate_model_metadata_schema.Rd @@ -0,0 +1,37 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/validate_model_metadata_schema.R +\name{validate_model_metadata_schema} +\alias{validate_model_metadata_schema} +\title{Validate model-metadata-schema config file} +\usage{ +validate_model_metadata_schema(hub_path = ".") +} +\arguments{ +\item{hub_path}{Path to a local hub directory.} +} +\value{ +Returns the result of validation. If validation is successful, will +return \code{TRUE}. If any validation errors are detected, returns \code{FALSE} with +details of errors appended as a data.frame to an \code{errors} attribute. +To access +the errors table use \code{attr(x, "errors")} where \code{x} is the output of the function. + +You can print a more concise and easier to view version of an errors table with +\code{\link[=view_config_val_errors]{view_config_val_errors()}}. +} +\description{ +Validate model-metadata-schema config file +} +\details{ +Checks that a \code{model-metadata-schema.json} config file exists in \code{hub-config}, +can be successfully parsed and contains at least either a \code{model_id} property or +both \code{team_abbr} and \code{model_abbr} properties. +} +\examples{ +validate_model_metadata_schema( + hub_path = system.file( + "testhubs/simple/", + package = "hubUtils" + ) +) +} diff --git a/man/view_config_val_errors.Rd b/man/view_config_val_errors.Rd new file mode 100644 index 0000000..5d2dff3 --- /dev/null +++ b/man/view_config_val_errors.Rd @@ -0,0 +1,35 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/view_config_val_errors.R +\name{view_config_val_errors} +\alias{view_config_val_errors} +\title{Print a concise and informative version of validation errors table.} +\usage{ +view_config_val_errors(x) +} +\arguments{ +\item{x}{output of \code{\link[=validate_config]{validate_config()}}.} +} +\value{ +prints the errors attribute of x in an informative format to the viewer. Only +available in interactive mode. +} +\description{ +Print a concise and informative version of validation errors table. +} +\examples{ +\dontrun{ +config_path <- system.file("error-schema/tasks-errors.json", + package = "hubUtils" +) +validate_config(config_path = config_path, config = "tasks") |> + view_config_val_errors() +} +} +\seealso{ +\code{\link[=validate_config]{validate_config()}} + +Other functions supporting config file validation: +\code{\link{validate_config}()}, +\code{\link{validate_hub_config}()} +} +\concept{functions supporting config file validation} diff --git a/tests/_snaps/create_config.md b/tests/_snaps/create_config.md new file mode 100644 index 0000000..5dcf087 --- /dev/null +++ b/tests/_snaps/create_config.md @@ -0,0 +1,117 @@ +# create_config functions work correctly + + Code + create_config(rounds) + Output + $schema_version + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + + $rounds + $rounds[[1]] + $rounds[[1]]$round_id_from_variable + [1] TRUE + + $rounds[[1]]$round_id + [1] "origin_date" + + $rounds[[1]]$model_tasks + $rounds[[1]]$model_tasks[[1]] + $rounds[[1]]$model_tasks[[1]]$task_ids + $rounds[[1]]$model_tasks[[1]]$task_ids$origin_date + $rounds[[1]]$model_tasks[[1]]$task_ids$origin_date$required + NULL + + $rounds[[1]]$model_tasks[[1]]$task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $rounds[[1]]$model_tasks[[1]]$task_ids$location + $rounds[[1]]$model_tasks[[1]]$task_ids$location$required + [1] "US" + + $rounds[[1]]$model_tasks[[1]]$task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $rounds[[1]]$model_tasks[[1]]$task_ids$horizon + $rounds[[1]]$model_tasks[[1]]$task_ids$horizon$required + [1] 1 + + $rounds[[1]]$model_tasks[[1]]$task_ids$horizon$optional + [1] 2 3 4 + + + + $rounds[[1]]$model_tasks[[1]]$output_type + $rounds[[1]]$model_tasks[[1]]$output_type$mean + $rounds[[1]]$model_tasks[[1]]$output_type$mean$output_type_id + $rounds[[1]]$model_tasks[[1]]$output_type$mean$output_type_id$required + [1] NA + + $rounds[[1]]$model_tasks[[1]]$output_type$mean$output_type_id$optional + NULL + + + $rounds[[1]]$model_tasks[[1]]$output_type$mean$value + $rounds[[1]]$model_tasks[[1]]$output_type$mean$value$type + [1] "double" + + $rounds[[1]]$model_tasks[[1]]$output_type$mean$value$minimum + [1] 0 + + + + + $rounds[[1]]$model_tasks[[1]]$target_metadata + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]] + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_id + [1] "inc hosp" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_keys + NULL + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_type + [1] "discrete" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$is_step_ahead + [1] TRUE + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$time_unit + [1] "week" + + + + + + $rounds[[1]]$submissions_due + $rounds[[1]]$submissions_due$relative_to + [1] "origin_date" + + $rounds[[1]]$submissions_due$start + [1] -4 + + $rounds[[1]]$submissions_due$end + [1] 2 + + + + + attr(,"class") + [1] "config" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +# create_config functions error correctly + + Code + create_config(list(a = 10)) + Condition + Error in `create_config()`: + x `rounds` must inherit from class but does not + diff --git a/tests/_snaps/create_model_out_submit_tmpl.md b/tests/_snaps/create_model_out_submit_tmpl.md new file mode 100644 index 0000000..4a1deef --- /dev/null +++ b/tests/_snaps/create_model_out_submit_tmpl.md @@ -0,0 +1,164 @@ +# create_model_out_submit_tmpl works correctly + + Code + str(create_model_out_submit_tmpl(hub_con, round_id = "2023-01-30")) + Output + tibble [3,132 x 7] (S3: tbl_df/tbl/data.frame) + $ forecast_date : Date[1:3132], format: "2023-01-30" "2023-01-30" ... + $ target : chr [1:3132] "wk flu hosp rate change" "wk flu hosp rate change" "wk flu hosp rate change" "wk flu hosp rate change" ... + $ horizon : int [1:3132] 2 1 2 1 2 1 2 1 2 1 ... + $ location : chr [1:3132] "US" "US" "01" "01" ... + $ output_type : chr [1:3132] "pmf" "pmf" "pmf" "pmf" ... + $ output_type_id: chr [1:3132] "large_decrease" "large_decrease" "large_decrease" "large_decrease" ... + $ value : num [1:3132] NA NA NA NA NA NA NA NA NA NA ... + +--- + + Code + str(create_model_out_submit_tmpl(hub_con, round_id = "2023-01-16")) + Output + tibble [3,132 x 7] (S3: tbl_df/tbl/data.frame) + $ forecast_date : Date[1:3132], format: "2023-01-16" "2023-01-16" ... + $ target : chr [1:3132] "wk flu hosp rate change" "wk flu hosp rate change" "wk flu hosp rate change" "wk flu hosp rate change" ... + $ horizon : int [1:3132] 2 1 2 1 2 1 2 1 2 1 ... + $ location : chr [1:3132] "US" "US" "01" "01" ... + $ output_type : chr [1:3132] "pmf" "pmf" "pmf" "pmf" ... + $ output_type_id: chr [1:3132] "large_decrease" "large_decrease" "large_decrease" "large_decrease" ... + $ value : num [1:3132] NA NA NA NA NA NA NA NA NA NA ... + +--- + + Code + str(create_model_out_submit_tmpl(hub_con, round_id = "2023-01-16", + required_vals_only = TRUE)) + Output + tibble [0 x 7] (S3: tbl_df/tbl/data.frame) + $ forecast_date : 'Date' num(0) + $ target : chr(0) + $ horizon : int(0) + $ location : chr(0) + $ output_type : chr(0) + $ output_type_id: chr(0) + $ value : num(0) + +--- + + Code + str(create_model_out_submit_tmpl(hub_con, round_id = "2023-01-16", + required_vals_only = TRUE, complete_cases_only = FALSE)) + Message + ! Column "target" whose values are all optional included as all `NA` column. + ! Round contains more than one modeling task (2) + i See Hub's 'tasks.json' file or attribute "config_tasks" for + details of optional task ID/output_type/output_type ID value combinations. + Output + tibble [28 x 7] (S3: tbl_df/tbl/data.frame) + $ forecast_date : Date[1:28], format: "2023-01-16" "2023-01-16" ... + $ target : chr [1:28] NA NA NA NA ... + $ horizon : int [1:28] 2 2 2 2 2 2 2 2 2 2 ... + $ location : chr [1:28] "US" "US" "US" "US" ... + $ output_type : chr [1:28] "pmf" "pmf" "pmf" "pmf" ... + $ output_type_id: chr [1:28] "large_decrease" "decrease" "stable" "increase" ... + $ value : num [1:28] NA NA NA NA NA NA NA NA NA NA ... + +--- + + Code + str(create_model_out_submit_tmpl(hub_con, round_id = "2022-10-01")) + Output + tibble [5,184 x 7] (S3: tbl_df/tbl/data.frame) + $ origin_date : Date[1:5184], format: "2022-10-01" "2022-10-01" ... + $ target : chr [1:5184] "wk inc flu hosp" "wk inc flu hosp" "wk inc flu hosp" "wk inc flu hosp" ... + $ horizon : int [1:5184] 1 2 3 4 1 2 3 4 1 2 ... + $ location : chr [1:5184] "US" "US" "US" "US" ... + $ output_type : chr [1:5184] "mean" "mean" "mean" "mean" ... + $ output_type_id: num [1:5184] NA NA NA NA NA NA NA NA NA NA ... + $ value : int [1:5184] NA NA NA NA NA NA NA NA NA NA ... + +--- + + Code + str(create_model_out_submit_tmpl(hub_con, round_id = "2022-10-01", + required_vals_only = TRUE, complete_cases_only = FALSE)) + Message + ! Column "location" whose values are all optional included as all `NA` column. + i See Hub's 'tasks.json' file or attribute "config_tasks" for + details of optional task ID/output_type/output_type ID value combinations. + Output + tibble [23 x 7] (S3: tbl_df/tbl/data.frame) + $ origin_date : Date[1:23], format: "2022-10-01" "2022-10-01" ... + $ target : chr [1:23] "wk inc flu hosp" "wk inc flu hosp" "wk inc flu hosp" "wk inc flu hosp" ... + $ horizon : int [1:23] 1 1 1 1 1 1 1 1 1 1 ... + $ location : chr [1:23] NA NA NA NA ... + $ output_type : chr [1:23] "quantile" "quantile" "quantile" "quantile" ... + $ output_type_id: num [1:23] 0.01 0.025 0.05 0.1 0.15 0.2 0.25 0.3 0.35 0.4 ... + $ value : int [1:23] NA NA NA NA NA NA NA NA NA NA ... + +--- + + Code + str(create_model_out_submit_tmpl(hub_con, round_id = "2022-10-29", + required_vals_only = TRUE, complete_cases_only = FALSE)) + Message + ! Column "location" whose values are all optional included as all `NA` column. + i See Hub's 'tasks.json' file or attribute "config_tasks" for + details of optional task ID/output_type/output_type ID value combinations. + Output + tibble [23 x 8] (S3: tbl_df/tbl/data.frame) + $ origin_date : Date[1:23], format: "2022-10-29" "2022-10-29" ... + $ target : chr [1:23] "wk inc flu hosp" "wk inc flu hosp" "wk inc flu hosp" "wk inc flu hosp" ... + $ horizon : int [1:23] 1 1 1 1 1 1 1 1 1 1 ... + $ location : chr [1:23] NA NA NA NA ... + $ age_group : chr [1:23] "65+" "65+" "65+" "65+" ... + $ output_type : chr [1:23] "quantile" "quantile" "quantile" "quantile" ... + $ output_type_id: num [1:23] 0.01 0.025 0.05 0.1 0.15 0.2 0.25 0.3 0.35 0.4 ... + $ value : int [1:23] NA NA NA NA NA NA NA NA NA NA ... + +--- + + Code + str(create_model_out_submit_tmpl(hub_con, round_id = "2022-10-29", + required_vals_only = TRUE)) + Output + tibble [0 x 8] (S3: tbl_df/tbl/data.frame) + $ origin_date : 'Date' num(0) + $ target : chr(0) + $ horizon : int(0) + $ location : chr(0) + $ age_group : chr(0) + $ output_type : chr(0) + $ output_type_id: num(0) + $ value : int(0) + +# create_model_out_submit_tmpl errors correctly + + Code + create_model_out_submit_tmpl(hub_con, round_id = "random_round_id") + Condition + Error in `get_round_idx()`: + ! `round_id` must be one of "2022-10-01", "2022-10-08", "2022-10-15", "2022-10-22", or "2022-10-29", not "random_round_id". + +--- + + Code + create_model_out_submit_tmpl(hub_con) + Condition + Error in `get_round_idx()`: + ! `round_id` must be a character vector, not absent. + +--- + + Code + create_model_out_submit_tmpl(hub_con) + Condition + Error in `get_round_idx()`: + ! `round_id` must be a character vector, not absent. + +--- + + Code + create_model_out_submit_tmpl(list()) + Condition + Error in `create_model_out_submit_tmpl()`: + ! Assertion on 'hub_con' failed: Must inherit from class 'hub_connection', but has class 'list'. + diff --git a/tests/_snaps/create_model_task.md b/tests/_snaps/create_model_task.md new file mode 100644 index 0000000..17104cf --- /dev/null +++ b/tests/_snaps/create_model_task.md @@ -0,0 +1,313 @@ +# create_model_task functions work correctly + + Code + create_model_task(task_ids = create_task_ids(create_task_id("origin_date", + required = NULL, optional = c("2023-01-02", "2023-01-09", "2023-01-16")), + create_task_id("location", required = "US", optional = c("01", "02", "04", "05", + "06")), create_task_id("horizon", required = 1L, optional = 2:4)), + output_type = create_output_type(create_output_type_mean(is_required = TRUE, + value_type = "double", value_minimum = 0L)), target_metadata = create_target_metadata( + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = NULL, + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week"))) + Output + $task_ids + $task_ids$origin_date + $task_ids$origin_date$required + NULL + + $task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $task_ids$location + $task_ids$location$required + [1] "US" + + $task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $task_ids$horizon + $task_ids$horizon$required + [1] 1 + + $task_ids$horizon$optional + [1] 2 3 4 + + + + $output_type + $output_type$mean + $output_type$mean$output_type_id + $output_type$mean$output_type_id$required + [1] NA + + $output_type$mean$output_type_id$optional + NULL + + + $output_type$mean$value + $output_type$mean$value$type + [1] "double" + + $output_type$mean$value$minimum + [1] 0 + + + + + $target_metadata + $target_metadata[[1]] + $target_metadata[[1]]$target_id + [1] "inc hosp" + + $target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $target_metadata[[1]]$target_keys + NULL + + $target_metadata[[1]]$target_type + [1] "discrete" + + $target_metadata[[1]]$is_step_ahead + [1] TRUE + + $target_metadata[[1]]$time_unit + [1] "week" + + + + attr(,"class") + [1] "model_task" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +--- + + Code + create_model_task(task_ids = create_task_ids(create_task_id("origin_date", + required = NULL, optional = c("2023-01-02", "2023-01-09", "2023-01-16")), + create_task_id("location", required = "US", optional = c("01", "02", "04", "05", + "06")), create_task_id("target", required = NULL, optional = c("inc death", + "inc hosp")), create_task_id("horizon", required = 1L, optional = 2:4)), + output_type = create_output_type(create_output_type_mean(is_required = TRUE, + value_type = "double", value_minimum = 0L), create_output_type_median( + is_required = FALSE, value_type = "double"), create_output_type_quantile( + required = c(0.25, 0.5, 0.75), optional = c(0.1, 0.2, 0.3, 0.4, 0.6, 0.7, 0.8, + 0.9), value_type = "double", value_minimum = 0)), target_metadata = create_target_metadata( + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = list(target = "inc hosp"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week"), + create_target_metadata_item(target_id = "inc death", target_name = "Weekly incident influenza deaths", + target_units = "rate per 100,000 population", target_keys = list(target = "inc death"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week"))) + Output + $task_ids + $task_ids$origin_date + $task_ids$origin_date$required + NULL + + $task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $task_ids$location + $task_ids$location$required + [1] "US" + + $task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $task_ids$target + $task_ids$target$required + NULL + + $task_ids$target$optional + [1] "inc death" "inc hosp" + + + $task_ids$horizon + $task_ids$horizon$required + [1] 1 + + $task_ids$horizon$optional + [1] 2 3 4 + + + + $output_type + $output_type$mean + $output_type$mean$output_type_id + $output_type$mean$output_type_id$required + [1] NA + + $output_type$mean$output_type_id$optional + NULL + + + $output_type$mean$value + $output_type$mean$value$type + [1] "double" + + $output_type$mean$value$minimum + [1] 0 + + + + $output_type$median + $output_type$median$output_type_id + $output_type$median$output_type_id$required + NULL + + $output_type$median$output_type_id$optional + [1] NA + + + $output_type$median$value + $output_type$median$value$type + [1] "double" + + + + $output_type$quantile + $output_type$quantile$output_type_id + $output_type$quantile$output_type_id$required + [1] 0.25 0.50 0.75 + + $output_type$quantile$output_type_id$optional + [1] 0.1 0.2 0.3 0.4 0.6 0.7 0.8 0.9 + + + $output_type$quantile$value + $output_type$quantile$value$type + [1] "double" + + $output_type$quantile$value$minimum + [1] 0 + + + + + $target_metadata + $target_metadata[[1]] + $target_metadata[[1]]$target_id + [1] "inc hosp" + + $target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $target_metadata[[1]]$target_keys + $target_metadata[[1]]$target_keys$target + [1] "inc hosp" + + + $target_metadata[[1]]$target_type + [1] "discrete" + + $target_metadata[[1]]$is_step_ahead + [1] TRUE + + $target_metadata[[1]]$time_unit + [1] "week" + + + $target_metadata[[2]] + $target_metadata[[2]]$target_id + [1] "inc death" + + $target_metadata[[2]]$target_name + [1] "Weekly incident influenza deaths" + + $target_metadata[[2]]$target_units + [1] "rate per 100,000 population" + + $target_metadata[[2]]$target_keys + $target_metadata[[2]]$target_keys$target + [1] "inc death" + + + $target_metadata[[2]]$target_type + [1] "discrete" + + $target_metadata[[2]]$is_step_ahead + [1] TRUE + + $target_metadata[[2]]$time_unit + [1] "week" + + + + attr(,"class") + [1] "model_task" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +# create_output_type_point functions error correctly + + Code + create_model_task(task_ids = task_ids, output_type = output_type, + target_metadata = create_target_metadata(create_target_metadata_item( + target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = list(target = "inc hosp"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week"))) + Condition + Error in `map()`: + i In index: 1. + Caused by error in `create_model_task()`: + x `task_ids` target values must match `target_metadata` `target_keys` definitions. + > `target_keys` target values: "inc hosp" + > `task_ids` target values: "inc death" and "inc hosp" + +--- + + Code + create_model_task(task_ids = task_ids, output_type = output_type, + target_metadata = create_target_metadata(create_target_metadata_item( + target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = list(targets = "inc hosp"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week"))) + Condition + Error in `create_model_task()`: + ! `target_metadata` target_keys names must match valid `task_ids` property names: "origin_date", "target", and "horizon" + x target_keys name "targets" does not. + +--- + + Code + create_model_task(task_ids = task_ids, output_type = list(a = 10), + target_metadata = create_target_metadata(create_target_metadata_item(target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", target_units = "rate per 100,000 population", + target_keys = list(targets = "inc hosp"), target_type = "discrete", + is_step_ahead = TRUE, time_unit = "week"))) + Condition + Error in `map()`: + i In index: 2. + Caused by error in `create_model_task()`: + x `output_type` must inherit from class but does not + +--- + + Code + create_model_task(task_ids = task_ids, output_type = output_type, + target_metadata = create_target_metadata(create_target_metadata_item( + target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = NULL, + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week"))) + Condition + Error in `create_model_task()`: + ! All arguments supplied must be created against the same Hub schema. + x `schema_id` attributes are not consistent across all arguments. + Argument `schema_id` attributes: + * task_ids : invalid_schema_id + * output_type : https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json + * target_metadata : https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json + diff --git a/tests/_snaps/create_model_tasks.md b/tests/_snaps/create_model_tasks.md new file mode 100644 index 0000000..bbfe0a3 --- /dev/null +++ b/tests/_snaps/create_model_tasks.md @@ -0,0 +1,397 @@ +# create_model_tasks functions work correctly + + Code + create_model_tasks(create_model_task(task_ids = create_task_ids(create_task_id( + "origin_date", required = NULL, optional = c("2023-01-02", "2023-01-09", + "2023-01-16")), create_task_id("location", required = "US", optional = c( + "01", "02", "04", "05", "06")), create_task_id("horizon", required = 1L, + optional = 2:4)), output_type = create_output_type(create_output_type_mean( + is_required = TRUE, value_type = "double", value_minimum = 0L)), + target_metadata = create_target_metadata(create_target_metadata_item(target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", target_units = "rate per 100,000 population", + target_keys = NULL, target_type = "discrete", is_step_ahead = TRUE, + time_unit = "week")))) + Output + $model_tasks + $model_tasks[[1]] + $model_tasks[[1]]$task_ids + $model_tasks[[1]]$task_ids$origin_date + $model_tasks[[1]]$task_ids$origin_date$required + NULL + + $model_tasks[[1]]$task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $model_tasks[[1]]$task_ids$location + $model_tasks[[1]]$task_ids$location$required + [1] "US" + + $model_tasks[[1]]$task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $model_tasks[[1]]$task_ids$horizon + $model_tasks[[1]]$task_ids$horizon$required + [1] 1 + + $model_tasks[[1]]$task_ids$horizon$optional + [1] 2 3 4 + + + + $model_tasks[[1]]$output_type + $model_tasks[[1]]$output_type$mean + $model_tasks[[1]]$output_type$mean$output_type_id + $model_tasks[[1]]$output_type$mean$output_type_id$required + [1] NA + + $model_tasks[[1]]$output_type$mean$output_type_id$optional + NULL + + + $model_tasks[[1]]$output_type$mean$value + $model_tasks[[1]]$output_type$mean$value$type + [1] "double" + + $model_tasks[[1]]$output_type$mean$value$minimum + [1] 0 + + + + + $model_tasks[[1]]$target_metadata + $model_tasks[[1]]$target_metadata[[1]] + $model_tasks[[1]]$target_metadata[[1]]$target_id + [1] "inc hosp" + + $model_tasks[[1]]$target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $model_tasks[[1]]$target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $model_tasks[[1]]$target_metadata[[1]]$target_keys + NULL + + $model_tasks[[1]]$target_metadata[[1]]$target_type + [1] "discrete" + + $model_tasks[[1]]$target_metadata[[1]]$is_step_ahead + [1] TRUE + + $model_tasks[[1]]$target_metadata[[1]]$time_unit + [1] "week" + + + + + + attr(,"class") + [1] "model_tasks" "list" + attr(,"n") + [1] 1 + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +--- + + Code + create_model_tasks(create_model_task(task_ids = create_task_ids(create_task_id( + "origin_date", required = NULL, optional = c("2023-01-02", "2023-01-09", + "2023-01-16")), create_task_id("location", required = "US", optional = c( + "01", "02", "04", "05", "06")), create_task_id("target", required = NULL, + optional = c("inc death", "inc hosp")), create_task_id("horizon", required = 1L, + optional = 2:4)), output_type = create_output_type(create_output_type_mean( + is_required = TRUE, value_type = "double", value_minimum = 0L), + create_output_type_median(is_required = FALSE, value_type = "double"), + create_output_type_quantile(required = c(0.25, 0.5, 0.75), optional = c(0.1, + 0.2, 0.3, 0.4, 0.6, 0.7, 0.8, 0.9), value_type = "double", value_minimum = 0)), + target_metadata = create_target_metadata(create_target_metadata_item(target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), target_type = "discrete", + is_step_ahead = TRUE, time_unit = "week"), create_target_metadata_item( + target_id = "inc death", target_name = "Weekly incident influenza deaths", + target_units = "rate per 100,000 population", target_keys = list(target = "inc death"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week"))), + create_model_task(task_ids = create_task_ids(create_task_id("origin_date", + required = NULL, optional = c("2023-01-02", "2023-01-09", "2023-01-16")), + create_task_id("location", required = "US", optional = c("01", "02", "04", "05", + "06")), create_task_id("target", required = "flu hosp rt chng", optional = NULL), + create_task_id("horizon", required = 1L, optional = 2:4)), output_type = create_output_type( + create_output_type_pmf(required = c("large_decrease", "decrease", "stable", + "increase", "large_increase"), optional = NULL, value_type = "double")), + target_metadata = create_target_metadata(create_target_metadata_item(target_id = "flu hosp rt chng", + target_name = "Weekly influenza hospitalization rate change", target_units = "rate per 100,000 population", + target_keys = list(target = "flu hosp rt chng"), target_type = "nominal", + is_step_ahead = TRUE, time_unit = "week")))) + Output + $model_tasks + $model_tasks[[1]] + $model_tasks[[1]]$task_ids + $model_tasks[[1]]$task_ids$origin_date + $model_tasks[[1]]$task_ids$origin_date$required + NULL + + $model_tasks[[1]]$task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $model_tasks[[1]]$task_ids$location + $model_tasks[[1]]$task_ids$location$required + [1] "US" + + $model_tasks[[1]]$task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $model_tasks[[1]]$task_ids$target + $model_tasks[[1]]$task_ids$target$required + NULL + + $model_tasks[[1]]$task_ids$target$optional + [1] "inc death" "inc hosp" + + + $model_tasks[[1]]$task_ids$horizon + $model_tasks[[1]]$task_ids$horizon$required + [1] 1 + + $model_tasks[[1]]$task_ids$horizon$optional + [1] 2 3 4 + + + + $model_tasks[[1]]$output_type + $model_tasks[[1]]$output_type$mean + $model_tasks[[1]]$output_type$mean$output_type_id + $model_tasks[[1]]$output_type$mean$output_type_id$required + [1] NA + + $model_tasks[[1]]$output_type$mean$output_type_id$optional + NULL + + + $model_tasks[[1]]$output_type$mean$value + $model_tasks[[1]]$output_type$mean$value$type + [1] "double" + + $model_tasks[[1]]$output_type$mean$value$minimum + [1] 0 + + + + $model_tasks[[1]]$output_type$median + $model_tasks[[1]]$output_type$median$output_type_id + $model_tasks[[1]]$output_type$median$output_type_id$required + NULL + + $model_tasks[[1]]$output_type$median$output_type_id$optional + [1] NA + + + $model_tasks[[1]]$output_type$median$value + $model_tasks[[1]]$output_type$median$value$type + [1] "double" + + + + $model_tasks[[1]]$output_type$quantile + $model_tasks[[1]]$output_type$quantile$output_type_id + $model_tasks[[1]]$output_type$quantile$output_type_id$required + [1] 0.25 0.50 0.75 + + $model_tasks[[1]]$output_type$quantile$output_type_id$optional + [1] 0.1 0.2 0.3 0.4 0.6 0.7 0.8 0.9 + + + $model_tasks[[1]]$output_type$quantile$value + $model_tasks[[1]]$output_type$quantile$value$type + [1] "double" + + $model_tasks[[1]]$output_type$quantile$value$minimum + [1] 0 + + + + + $model_tasks[[1]]$target_metadata + $model_tasks[[1]]$target_metadata[[1]] + $model_tasks[[1]]$target_metadata[[1]]$target_id + [1] "inc hosp" + + $model_tasks[[1]]$target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $model_tasks[[1]]$target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $model_tasks[[1]]$target_metadata[[1]]$target_keys + $model_tasks[[1]]$target_metadata[[1]]$target_keys$target + [1] "inc hosp" + + + $model_tasks[[1]]$target_metadata[[1]]$target_type + [1] "discrete" + + $model_tasks[[1]]$target_metadata[[1]]$is_step_ahead + [1] TRUE + + $model_tasks[[1]]$target_metadata[[1]]$time_unit + [1] "week" + + + $model_tasks[[1]]$target_metadata[[2]] + $model_tasks[[1]]$target_metadata[[2]]$target_id + [1] "inc death" + + $model_tasks[[1]]$target_metadata[[2]]$target_name + [1] "Weekly incident influenza deaths" + + $model_tasks[[1]]$target_metadata[[2]]$target_units + [1] "rate per 100,000 population" + + $model_tasks[[1]]$target_metadata[[2]]$target_keys + $model_tasks[[1]]$target_metadata[[2]]$target_keys$target + [1] "inc death" + + + $model_tasks[[1]]$target_metadata[[2]]$target_type + [1] "discrete" + + $model_tasks[[1]]$target_metadata[[2]]$is_step_ahead + [1] TRUE + + $model_tasks[[1]]$target_metadata[[2]]$time_unit + [1] "week" + + + + + $model_tasks[[2]] + $model_tasks[[2]]$task_ids + $model_tasks[[2]]$task_ids$origin_date + $model_tasks[[2]]$task_ids$origin_date$required + NULL + + $model_tasks[[2]]$task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $model_tasks[[2]]$task_ids$location + $model_tasks[[2]]$task_ids$location$required + [1] "US" + + $model_tasks[[2]]$task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $model_tasks[[2]]$task_ids$target + $model_tasks[[2]]$task_ids$target$required + [1] "flu hosp rt chng" + + $model_tasks[[2]]$task_ids$target$optional + NULL + + + $model_tasks[[2]]$task_ids$horizon + $model_tasks[[2]]$task_ids$horizon$required + [1] 1 + + $model_tasks[[2]]$task_ids$horizon$optional + [1] 2 3 4 + + + + $model_tasks[[2]]$output_type + $model_tasks[[2]]$output_type$pmf + $model_tasks[[2]]$output_type$pmf$output_type_id + $model_tasks[[2]]$output_type$pmf$output_type_id$required + [1] "large_decrease" "decrease" "stable" "increase" + [5] "large_increase" + + $model_tasks[[2]]$output_type$pmf$output_type_id$optional + NULL + + + $model_tasks[[2]]$output_type$pmf$value + $model_tasks[[2]]$output_type$pmf$value$type + [1] "double" + + $model_tasks[[2]]$output_type$pmf$value$minimum + [1] 0 + + $model_tasks[[2]]$output_type$pmf$value$maximum + [1] 1 + + + + + $model_tasks[[2]]$target_metadata + $model_tasks[[2]]$target_metadata[[1]] + $model_tasks[[2]]$target_metadata[[1]]$target_id + [1] "flu hosp rt chng" + + $model_tasks[[2]]$target_metadata[[1]]$target_name + [1] "Weekly influenza hospitalization rate change" + + $model_tasks[[2]]$target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $model_tasks[[2]]$target_metadata[[1]]$target_keys + $model_tasks[[2]]$target_metadata[[1]]$target_keys$target + [1] "flu hosp rt chng" + + + $model_tasks[[2]]$target_metadata[[1]]$target_type + [1] "nominal" + + $model_tasks[[2]]$target_metadata[[1]]$is_step_ahead + [1] TRUE + + $model_tasks[[2]]$target_metadata[[1]]$time_unit + [1] "week" + + + + + + attr(,"class") + [1] "model_tasks" "list" + attr(,"n") + [1] 2 + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +# create_model_tasks functions error correctly + + Code + create_model_tasks(model_task_1, list(a = 10)) + Condition + Error in `create_model_tasks()`: + ! All items supplied must inherit from class + x Item 2 does not. + +--- + + Code + create_model_tasks(model_task_1, create_model_task(task_ids = create_task_ids( + create_task_id("origin_date", required = NULL, optional = c("2023-01-02", + "2023-01-09", "2023-01-16")), create_task_id("location", required = "US", + optional = c("01", "02", "04", "05", "06")), create_task_id("target", + required = "flu hosp rt chng", optional = NULL), create_task_id("horizon", + required = 1L, optional = 2:4)), output_type = create_output_type( + create_output_type_pmf(required = c("large_decrease", "decrease", "stable", + "increase", "large_increase"), optional = NULL, value_type = "double")), + target_metadata = create_target_metadata(create_target_metadata_item(target_id = "flu hosp rt chng", + target_name = "Weekly influenza hospitalization rate change", target_units = "rate per 100,000 population", + target_keys = list(target = "flu hosp rt chng"), target_type = "nominal", + is_step_ahead = TRUE, time_unit = "week")))) + Condition + Error in `create_model_tasks()`: + ! All items supplied must be created against the same Hub schema. + x `schema_id` attributes are not consistent across all items. + Item `schema_id` attributes: + * Item 1 : invalid_schema_id + * Item 2 : https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json + diff --git a/tests/_snaps/create_output_type.md b/tests/_snaps/create_output_type.md new file mode 100644 index 0000000..dd89ad6 --- /dev/null +++ b/tests/_snaps/create_output_type.md @@ -0,0 +1,90 @@ +# create_output_type functions work correctly + + Code + create_output_type(create_output_type_mean(is_required = TRUE, value_type = "double", + value_minimum = 0L), create_output_type_median(is_required = FALSE, + value_type = "double"), create_output_type_quantile(required = c(0.25, 0.5, + 0.75), optional = c(0.1, 0.2, 0.3, 0.4, 0.6, 0.7, 0.8, 0.9), value_type = "double", + value_minimum = 0)) + Output + $output_type + $output_type$mean + $output_type$mean$output_type_id + $output_type$mean$output_type_id$required + [1] NA + + $output_type$mean$output_type_id$optional + NULL + + + $output_type$mean$value + $output_type$mean$value$type + [1] "double" + + $output_type$mean$value$minimum + [1] 0 + + + + $output_type$median + $output_type$median$output_type_id + $output_type$median$output_type_id$required + NULL + + $output_type$median$output_type_id$optional + [1] NA + + + $output_type$median$value + $output_type$median$value$type + [1] "double" + + + + $output_type$quantile + $output_type$quantile$output_type_id + $output_type$quantile$output_type_id$required + [1] 0.25 0.50 0.75 + + $output_type$quantile$output_type_id$optional + [1] 0.1 0.2 0.3 0.4 0.6 0.7 0.8 0.9 + + + $output_type$quantile$value + $output_type$quantile$value$type + [1] "double" + + $output_type$quantile$value$minimum + [1] 0 + + + + + attr(,"class") + [1] "output_type" "list" + attr(,"n") + [1] 3 + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +# create_output_type functions error correctly + + Code + create_output_type(create_output_type_mean(is_required = TRUE, value_type = "double", + value_minimum = 0L), create_output_type_mean(is_required = TRUE, value_type = "double", + value_minimum = 0L)) + Condition + Error in `create_output_type()`: + ! `names` must be unique across all items. + x Item 2 with `name` "mean" is duplicate. + +--- + + Code + create_output_type(create_output_type_mean(is_required = TRUE, value_type = "double", + value_minimum = 0L), list(a = "b")) + Condition + Error in `create_output_type()`: + ! All items supplied must inherit from class + x Item 2 does not. + diff --git a/tests/_snaps/create_output_type_item.md b/tests/_snaps/create_output_type_item.md new file mode 100644 index 0000000..2fcd0c5 --- /dev/null +++ b/tests/_snaps/create_output_type_item.md @@ -0,0 +1,419 @@ +# create_output_type_point functions work correctly + + Code + create_output_type_mean(is_required = TRUE, value_type = "double", + value_minimum = 0L) + Output + $mean + $mean$output_type_id + $mean$output_type_id$required + [1] NA + + $mean$output_type_id$optional + NULL + + + $mean$value + $mean$value$type + [1] "double" + + $mean$value$minimum + [1] 0 + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +--- + + Code + create_output_type_mean(is_required = FALSE, value_type = "integer", + value_maximum = 0L) + Output + $mean + $mean$output_type_id + $mean$output_type_id$required + NULL + + $mean$output_type_id$optional + [1] NA + + + $mean$value + $mean$value$type + [1] "integer" + + $mean$value$maximum + [1] 0 + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +--- + + Code + create_output_type_median(is_required = FALSE, value_type = "double") + Output + $median + $median$output_type_id + $median$output_type_id$required + NULL + + $median$output_type_id$optional + [1] NA + + + $median$value + $median$value$type + [1] "double" + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +--- + + Code + create_output_type_median(is_required = FALSE, value_type = "double", + schema_version = "v1.0.0") + Condition + Warning: + Hub configured using schema version v1.0.0. Support for schema earlier than v2.0.0 was deprecated in hubUtils 0.0.0.9010. + i Please upgrade Hub config files to conform to, at minimum, version v2.0.0 as soon as possible. + Output + $median + $median$type_id + $median$type_id$required + NULL + + $median$type_id$optional + [1] NA + + + $median$value + $median$value$type + [1] "double" + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v1.0.0/tasks-schema.json" + +# create_output_type_point functions error correctly + + Code + create_output_type_mean(is_required = "TRUE", value_type = "double") + Condition + Error in `create_output_type_point()`: + x Argument `is_required` must be and have length 1. + +--- + + Code + create_output_type_mean(is_required = TRUE, value_type = c("double", "integer")) + Condition + Error in `map()`: + i In index: 1. + Caused by error in `create_output_type_mean()`: + x `value_type` must be length 1, not 2. + +--- + + Code + create_output_type_mean(is_required = FALSE, value_type = "character", + value_maximum = 0L) + Condition + Error in `map()`: + i In index: 1. + Caused by error in `create_output_type_mean()`: + x `value_type` value is invalid. + ! Must be one of "double" and "integer". + i Actual value is "character" + +--- + + Code + create_output_type_median(is_required = FALSE) + Condition + Error in `create_output_type_point()`: + ! `value_type` is absent but must be supplied. + +# create_output_type_dist functions work correctly + + Code + create_output_type_quantile(required = c(0.25, 0.5, 0.75), optional = c(0.1, + 0.2, 0.3, 0.4, 0.6, 0.7, 0.8, 0.9), value_type = "double", value_minimum = 0) + Output + $quantile + $quantile$output_type_id + $quantile$output_type_id$required + [1] 0.25 0.50 0.75 + + $quantile$output_type_id$optional + [1] 0.1 0.2 0.3 0.4 0.6 0.7 0.8 0.9 + + + $quantile$value + $quantile$value$type + [1] "double" + + $quantile$value$minimum + [1] 0 + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +--- + + Code + create_output_type_cdf(required = c(10, 20), optional = NULL, value_type = "double") + Output + $cdf + $cdf$output_type_id + $cdf$output_type_id$required + [1] 10 20 + + $cdf$output_type_id$optional + NULL + + + $cdf$value + $cdf$value$type + [1] "double" + + $cdf$value$minimum + [1] 0 + + $cdf$value$maximum + [1] 1 + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +--- + + Code + create_output_type_cdf(required = NULL, optional = c("EW202240", "EW202241", + "EW202242"), value_type = "double") + Output + $cdf + $cdf$output_type_id + $cdf$output_type_id$required + NULL + + $cdf$output_type_id$optional + [1] "EW202240" "EW202241" "EW202242" + + + $cdf$value + $cdf$value$type + [1] "double" + + $cdf$value$minimum + [1] 0 + + $cdf$value$maximum + [1] 1 + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +--- + + Code + create_output_type_pmf(required = NULL, optional = c("low", "moderate", "high", + "extreme"), value_type = "double") + Output + $pmf + $pmf$output_type_id + $pmf$output_type_id$required + NULL + + $pmf$output_type_id$optional + [1] "low" "moderate" "high" "extreme" + + + $pmf$value + $pmf$value$type + [1] "double" + + $pmf$value$minimum + [1] 0 + + $pmf$value$maximum + [1] 1 + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +--- + + Code + create_output_type_sample(required = 1:10, optional = 11:15, value_type = "double") + Output + $sample + $sample$output_type_id + $sample$output_type_id$required + [1] 1 2 3 4 5 6 7 8 9 10 + + $sample$output_type_id$optional + [1] 11 12 13 14 15 + + + $sample$value + $sample$value$type + [1] "double" + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +--- + + Code + create_output_type_quantile(required = c(0.25, 0.5, 0.75), optional = c(0.1, + 0.2, 0.3, 0.4, 0.6, 0.7, 0.8, 0.9), value_type = "double", value_minimum = 0, + schema_version = "v1.0.0") + Condition + Warning: + Hub configured using schema version v1.0.0. Support for schema earlier than v2.0.0 was deprecated in hubUtils 0.0.0.9010. + i Please upgrade Hub config files to conform to, at minimum, version v2.0.0 as soon as possible. + Output + $quantile + $quantile$type_id + $quantile$type_id$required + [1] 0.25 0.50 0.75 + + $quantile$type_id$optional + [1] 0.1 0.2 0.3 0.4 0.6 0.7 0.8 0.9 + + + $quantile$value + $quantile$value$type + [1] "double" + + $quantile$value$minimum + [1] 0 + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v1.0.0/tasks-schema.json" + +# create_output_type_dist functions error correctly + + Code + create_output_type_cdf(required = NULL, optional = c("EW202240", "EW202241", + "EW2022423"), value_type = "double") + Condition + Error in `map()`: + i In index: 2. + Caused by error in `create_output_type_cdf()`: + ! The maximum number of characters allowed for values in `optional` is 8. + x Value "EW2022423" has more characters than allowed + +--- + + Code + create_output_type_quantile(required = c(0.25, 0.5, 0.6, 0.75), optional = c( + 0.1, 0.2, 0.3, 0.4, 0.6, 0.7, 0.8, 0.9), value_type = "double", + value_minimum = 0) + Condition + Error in `check_prop_dups()`: + x Values across arguments `required` and `optional` must be unique. + ! Provided value 0.6 is duplicated. + +--- + + Code + create_output_type_sample(required = 0:10, optional = 11:15, value_type = "double") + Condition + Error in `map()`: + i In index: 1. + Caused by error in `create_output_type_sample()`: + ! All values in `required` must be equal to or greater than 1. + x Value 0 is less. + +--- + + Code + create_output_type_sample(required = 1:10, optional = 11:15, value_type = "character") + Condition + Error in `map()`: + i In index: 1. + Caused by error in `create_output_type_sample()`: + x `value_type` value is invalid. + ! Must be one of "double" and "integer". + i Actual value is "character" + +# create_output_type_dist functions creates expected warnings + + Code + create_output_type_sample(required = 1:50, optional = NULL, value_type = "double", + value_minimum = 0L, value_maximum = 1L) + Condition + Warning in `create_output_type_sample()`: + ! Cannot determine appropriate type for argument `value_minimum`, type validation skipped. Schema may be invalid. Consult relevant schema and consider opening an issue at + Warning in `create_output_type_sample()`: + ! Cannot determine appropriate type for argument `value_maximum`, type validation skipped. Schema may be invalid. Consult relevant schema and consider opening an issue at + Output + $sample + $sample$output_type_id + $sample$output_type_id$required + [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 + [26] 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 + + $sample$output_type_id$optional + NULL + + + $sample$value + $sample$value$type + [1] "double" + + $sample$value$minimum + [1] 0 + + $sample$value$maximum + [1] 1 + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + diff --git a/tests/_snaps/create_round.md b/tests/_snaps/create_round.md new file mode 100644 index 0000000..38c22f2 --- /dev/null +++ b/tests/_snaps/create_round.md @@ -0,0 +1,270 @@ +# create_round functions work correctly + + Code + create_round(round_id_from_variable = FALSE, round_id = "round_1", model_tasks = model_tasks, + submissions_due = list(start = "2023-01-12", end = "2023-01-18"), + last_data_date = "2023-01-02") + Output + $round_id_from_variable + [1] FALSE + + $round_id + [1] "round_1" + + $model_tasks + $model_tasks[[1]] + $model_tasks[[1]]$task_ids + $model_tasks[[1]]$task_ids$origin_date + $model_tasks[[1]]$task_ids$origin_date$required + NULL + + $model_tasks[[1]]$task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $model_tasks[[1]]$task_ids$location + $model_tasks[[1]]$task_ids$location$required + [1] "US" + + $model_tasks[[1]]$task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $model_tasks[[1]]$task_ids$horizon + $model_tasks[[1]]$task_ids$horizon$required + [1] 1 + + $model_tasks[[1]]$task_ids$horizon$optional + [1] 2 3 4 + + + + $model_tasks[[1]]$output_type + $model_tasks[[1]]$output_type$mean + $model_tasks[[1]]$output_type$mean$output_type_id + $model_tasks[[1]]$output_type$mean$output_type_id$required + [1] NA + + $model_tasks[[1]]$output_type$mean$output_type_id$optional + NULL + + + $model_tasks[[1]]$output_type$mean$value + $model_tasks[[1]]$output_type$mean$value$type + [1] "double" + + $model_tasks[[1]]$output_type$mean$value$minimum + [1] 0 + + + + + $model_tasks[[1]]$target_metadata + $model_tasks[[1]]$target_metadata[[1]] + $model_tasks[[1]]$target_metadata[[1]]$target_id + [1] "inc hosp" + + $model_tasks[[1]]$target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $model_tasks[[1]]$target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $model_tasks[[1]]$target_metadata[[1]]$target_keys + NULL + + $model_tasks[[1]]$target_metadata[[1]]$target_type + [1] "discrete" + + $model_tasks[[1]]$target_metadata[[1]]$is_step_ahead + [1] TRUE + + $model_tasks[[1]]$target_metadata[[1]]$time_unit + [1] "week" + + + + + + $submissions_due + $submissions_due$start + [1] "2023-01-12" + + $submissions_due$end + [1] "2023-01-18" + + + $last_data_date + [1] "2023-01-02" + + attr(,"class") + [1] "round" "list" + attr(,"round_id") + [1] "round_1" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +--- + + Code + create_round(round_id_from_variable = TRUE, round_id = "origin_date", + model_tasks = model_tasks, submissions_due = list(relative_to = "origin_date", + start = -4L, end = 2L), last_data_date = "2023-01-02") + Output + $round_id_from_variable + [1] TRUE + + $round_id + [1] "origin_date" + + $model_tasks + $model_tasks[[1]] + $model_tasks[[1]]$task_ids + $model_tasks[[1]]$task_ids$origin_date + $model_tasks[[1]]$task_ids$origin_date$required + NULL + + $model_tasks[[1]]$task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $model_tasks[[1]]$task_ids$location + $model_tasks[[1]]$task_ids$location$required + [1] "US" + + $model_tasks[[1]]$task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $model_tasks[[1]]$task_ids$horizon + $model_tasks[[1]]$task_ids$horizon$required + [1] 1 + + $model_tasks[[1]]$task_ids$horizon$optional + [1] 2 3 4 + + + + $model_tasks[[1]]$output_type + $model_tasks[[1]]$output_type$mean + $model_tasks[[1]]$output_type$mean$output_type_id + $model_tasks[[1]]$output_type$mean$output_type_id$required + [1] NA + + $model_tasks[[1]]$output_type$mean$output_type_id$optional + NULL + + + $model_tasks[[1]]$output_type$mean$value + $model_tasks[[1]]$output_type$mean$value$type + [1] "double" + + $model_tasks[[1]]$output_type$mean$value$minimum + [1] 0 + + + + + $model_tasks[[1]]$target_metadata + $model_tasks[[1]]$target_metadata[[1]] + $model_tasks[[1]]$target_metadata[[1]]$target_id + [1] "inc hosp" + + $model_tasks[[1]]$target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $model_tasks[[1]]$target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $model_tasks[[1]]$target_metadata[[1]]$target_keys + NULL + + $model_tasks[[1]]$target_metadata[[1]]$target_type + [1] "discrete" + + $model_tasks[[1]]$target_metadata[[1]]$is_step_ahead + [1] TRUE + + $model_tasks[[1]]$target_metadata[[1]]$time_unit + [1] "week" + + + + + + $submissions_due + $submissions_due$relative_to + [1] "origin_date" + + $submissions_due$start + [1] -4 + + $submissions_due$end + [1] 2 + + + $last_data_date + [1] "2023-01-02" + + attr(,"class") + [1] "round" "list" + attr(,"round_id") + [1] "origin_date" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +# create_round name matching works correctly + + Code + create_round(round_id_from_variable = FALSE, round_id = "round_1", model_tasks = model_tasks, + submissions_due = list(start = "01/12/2023", end = "2023-01-18"), + last_data_date = "2023-01-02") + Condition + Error in `map()`: + i In index: 1. + Caused by error in `create_round()`: + x `start` value must be character string of date in valid ISO 8601 format (YYYY-MM-DD). + +--- + + Code + create_round(round_id_from_variable = FALSE, round_id = "round_1", model_tasks = model_tasks, + submissions_due = list(start = -4L, end = 2), last_data_date = "2023-01-02") + Condition + Error in `map()`: + i In index: 1. + Caused by error in `create_round()`: + x `start` value must be character string of date in valid ISO 8601 format (YYYY-MM-DD). Date object format not accepted. Consider using `as.character()` to convert to character. + +--- + + Code + create_round(round_id_from_variable = TRUE, round_id = "origin_dates", + model_tasks = model_tasks, submissions_due = list(relative_to = "origin_date", + start = -4L, end = 2L), last_data_date = "2023-01-02") + Condition + Error in `create_round()`: + ! `round_id` value must correspond to a valid `task_id` variable in every `model_task` object. + x `round_id` value "origin_dates" does not correspond to a valid variable in provided `model_tasks` `model_task` object 1. + +--- + + Code + create_round(round_id_from_variable = TRUE, round_id = "origin_date", + model_tasks = model_tasks, submissions_due = list(relative = "origin_date", + start = -4L, end = 2L), last_data_date = "2023-01-02") + Condition + Error in `create_round()`: + x Property "relative" in `submissions_due` is invalid. + ! Valid `submissions_due` properties: "relative_to", "start", and "end" + +--- + + Code + create_round(round_id_from_variable = TRUE, round_id = "origin_date", + model_tasks = "model_tasks", submissions_due = list(relative_to = "origin_date", + start = -4L, end = 2L), last_data_date = "2023-01-02") + Condition + Error in `create_round()`: + x `model_tasks` must inherit from class but does not + diff --git a/tests/_snaps/create_rounds.md b/tests/_snaps/create_rounds.md new file mode 100644 index 0000000..19292a5 --- /dev/null +++ b/tests/_snaps/create_rounds.md @@ -0,0 +1,343 @@ +# create_rounds functions work correctly + + Code + create_rounds(create_round(round_id_from_variable = TRUE, round_id = "origin_date", + model_tasks = model_tasks, submissions_due = list(relative_to = "origin_date", + start = -4L, end = 2L))) + Output + $rounds + $rounds[[1]] + $rounds[[1]]$round_id_from_variable + [1] TRUE + + $rounds[[1]]$round_id + [1] "origin_date" + + $rounds[[1]]$model_tasks + $rounds[[1]]$model_tasks[[1]] + $rounds[[1]]$model_tasks[[1]]$task_ids + $rounds[[1]]$model_tasks[[1]]$task_ids$origin_date + $rounds[[1]]$model_tasks[[1]]$task_ids$origin_date$required + NULL + + $rounds[[1]]$model_tasks[[1]]$task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $rounds[[1]]$model_tasks[[1]]$task_ids$location + $rounds[[1]]$model_tasks[[1]]$task_ids$location$required + [1] "US" + + $rounds[[1]]$model_tasks[[1]]$task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $rounds[[1]]$model_tasks[[1]]$task_ids$horizon + $rounds[[1]]$model_tasks[[1]]$task_ids$horizon$required + [1] 1 + + $rounds[[1]]$model_tasks[[1]]$task_ids$horizon$optional + [1] 2 3 4 + + + + $rounds[[1]]$model_tasks[[1]]$output_type + $rounds[[1]]$model_tasks[[1]]$output_type$mean + $rounds[[1]]$model_tasks[[1]]$output_type$mean$output_type_id + $rounds[[1]]$model_tasks[[1]]$output_type$mean$output_type_id$required + [1] NA + + $rounds[[1]]$model_tasks[[1]]$output_type$mean$output_type_id$optional + NULL + + + $rounds[[1]]$model_tasks[[1]]$output_type$mean$value + $rounds[[1]]$model_tasks[[1]]$output_type$mean$value$type + [1] "double" + + $rounds[[1]]$model_tasks[[1]]$output_type$mean$value$minimum + [1] 0 + + + + + $rounds[[1]]$model_tasks[[1]]$target_metadata + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]] + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_id + [1] "inc hosp" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_keys + NULL + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_type + [1] "discrete" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$is_step_ahead + [1] TRUE + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$time_unit + [1] "week" + + + + + + $rounds[[1]]$submissions_due + $rounds[[1]]$submissions_due$relative_to + [1] "origin_date" + + $rounds[[1]]$submissions_due$start + [1] -4 + + $rounds[[1]]$submissions_due$end + [1] 2 + + + + + attr(,"class") + [1] "rounds" "list" + attr(,"n") + [1] 1 + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +--- + + Code + create_rounds(create_round(round_id_from_variable = FALSE, round_id = "round_1", + model_tasks = create_model_tasks(create_model_task(task_ids = create_task_ids( + create_task_id("origin_date", required = NULL, optional = c("2023-01-09")), + create_task_id("location", required = "US", optional = c("01", "02", "04", + "05", "06")), create_task_id("horizon", required = 1L, optional = 2:4)), + output_type = create_output_type(create_output_type_mean(is_required = TRUE, + value_type = "double", value_minimum = 0L)), target_metadata = create_target_metadata( + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = NULL, + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week")))), + submissions_due = list(start = "2023-01-05", end = "2023-01-11"), + last_data_date = "2023-01-06"), create_round(round_id_from_variable = FALSE, + round_id = "round_2", model_tasks = create_model_tasks(create_model_task( + task_ids = create_task_ids(create_task_id("origin_date", required = NULL, + optional = c("2023-01-16")), create_task_id("location", required = "US", + optional = c("01", "02", "04", "05", "06")), create_task_id("horizon", + required = 1L, optional = 2:4)), output_type = create_output_type( + create_output_type_mean(is_required = TRUE, value_type = "double", + value_minimum = 0L)), target_metadata = create_target_metadata( + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = NULL, + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week")))), + submissions_due = list(start = "2023-01-12", end = "2023-01-18"), + last_data_date = "2023-01-13")) + Output + $rounds + $rounds[[1]] + $rounds[[1]]$round_id_from_variable + [1] FALSE + + $rounds[[1]]$round_id + [1] "round_1" + + $rounds[[1]]$model_tasks + $rounds[[1]]$model_tasks[[1]] + $rounds[[1]]$model_tasks[[1]]$task_ids + $rounds[[1]]$model_tasks[[1]]$task_ids$origin_date + $rounds[[1]]$model_tasks[[1]]$task_ids$origin_date$required + NULL + + $rounds[[1]]$model_tasks[[1]]$task_ids$origin_date$optional + [1] "2023-01-09" + + + $rounds[[1]]$model_tasks[[1]]$task_ids$location + $rounds[[1]]$model_tasks[[1]]$task_ids$location$required + [1] "US" + + $rounds[[1]]$model_tasks[[1]]$task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $rounds[[1]]$model_tasks[[1]]$task_ids$horizon + $rounds[[1]]$model_tasks[[1]]$task_ids$horizon$required + [1] 1 + + $rounds[[1]]$model_tasks[[1]]$task_ids$horizon$optional + [1] 2 3 4 + + + + $rounds[[1]]$model_tasks[[1]]$output_type + $rounds[[1]]$model_tasks[[1]]$output_type$mean + $rounds[[1]]$model_tasks[[1]]$output_type$mean$output_type_id + $rounds[[1]]$model_tasks[[1]]$output_type$mean$output_type_id$required + [1] NA + + $rounds[[1]]$model_tasks[[1]]$output_type$mean$output_type_id$optional + NULL + + + $rounds[[1]]$model_tasks[[1]]$output_type$mean$value + $rounds[[1]]$model_tasks[[1]]$output_type$mean$value$type + [1] "double" + + $rounds[[1]]$model_tasks[[1]]$output_type$mean$value$minimum + [1] 0 + + + + + $rounds[[1]]$model_tasks[[1]]$target_metadata + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]] + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_id + [1] "inc hosp" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_keys + NULL + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_type + [1] "discrete" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$is_step_ahead + [1] TRUE + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$time_unit + [1] "week" + + + + + + $rounds[[1]]$submissions_due + $rounds[[1]]$submissions_due$start + [1] "2023-01-05" + + $rounds[[1]]$submissions_due$end + [1] "2023-01-11" + + + $rounds[[1]]$last_data_date + [1] "2023-01-06" + + + $rounds[[2]] + $rounds[[2]]$round_id_from_variable + [1] FALSE + + $rounds[[2]]$round_id + [1] "round_2" + + $rounds[[2]]$model_tasks + $rounds[[2]]$model_tasks[[1]] + $rounds[[2]]$model_tasks[[1]]$task_ids + $rounds[[2]]$model_tasks[[1]]$task_ids$origin_date + $rounds[[2]]$model_tasks[[1]]$task_ids$origin_date$required + NULL + + $rounds[[2]]$model_tasks[[1]]$task_ids$origin_date$optional + [1] "2023-01-16" + + + $rounds[[2]]$model_tasks[[1]]$task_ids$location + $rounds[[2]]$model_tasks[[1]]$task_ids$location$required + [1] "US" + + $rounds[[2]]$model_tasks[[1]]$task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $rounds[[2]]$model_tasks[[1]]$task_ids$horizon + $rounds[[2]]$model_tasks[[1]]$task_ids$horizon$required + [1] 1 + + $rounds[[2]]$model_tasks[[1]]$task_ids$horizon$optional + [1] 2 3 4 + + + + $rounds[[2]]$model_tasks[[1]]$output_type + $rounds[[2]]$model_tasks[[1]]$output_type$mean + $rounds[[2]]$model_tasks[[1]]$output_type$mean$output_type_id + $rounds[[2]]$model_tasks[[1]]$output_type$mean$output_type_id$required + [1] NA + + $rounds[[2]]$model_tasks[[1]]$output_type$mean$output_type_id$optional + NULL + + + $rounds[[2]]$model_tasks[[1]]$output_type$mean$value + $rounds[[2]]$model_tasks[[1]]$output_type$mean$value$type + [1] "double" + + $rounds[[2]]$model_tasks[[1]]$output_type$mean$value$minimum + [1] 0 + + + + + $rounds[[2]]$model_tasks[[1]]$target_metadata + $rounds[[2]]$model_tasks[[1]]$target_metadata[[1]] + $rounds[[2]]$model_tasks[[1]]$target_metadata[[1]]$target_id + [1] "inc hosp" + + $rounds[[2]]$model_tasks[[1]]$target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $rounds[[2]]$model_tasks[[1]]$target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $rounds[[2]]$model_tasks[[1]]$target_metadata[[1]]$target_keys + NULL + + $rounds[[2]]$model_tasks[[1]]$target_metadata[[1]]$target_type + [1] "discrete" + + $rounds[[2]]$model_tasks[[1]]$target_metadata[[1]]$is_step_ahead + [1] TRUE + + $rounds[[2]]$model_tasks[[1]]$target_metadata[[1]]$time_unit + [1] "week" + + + + + + $rounds[[2]]$submissions_due + $rounds[[2]]$submissions_due$start + [1] "2023-01-12" + + $rounds[[2]]$submissions_due$end + [1] "2023-01-18" + + + $rounds[[2]]$last_data_date + [1] "2023-01-13" + + + + attr(,"class") + [1] "rounds" "list" + attr(,"n") + [1] 2 + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +# create_round errors correctly + + Code + create_rounds(round_1, round_1) + Condition + Error in `collect_items()`: + x object 2 is a duplicate of object 1 + diff --git a/tests/_snaps/create_target_metadata.md b/tests/_snaps/create_target_metadata.md new file mode 100644 index 0000000..750ac2c --- /dev/null +++ b/tests/_snaps/create_target_metadata.md @@ -0,0 +1,127 @@ +# create_target_metadata functions work correctly + + Code + create_target_metadata(create_target_metadata_item(target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), target_type = "discrete", + is_step_ahead = TRUE, time_unit = "week"), create_target_metadata_item( + target_id = "inc death", target_name = "Weekly incident influenza deaths", + target_units = "rate per 100,000 population", target_keys = list(target = "inc death"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week")) + Output + $target_metadata + $target_metadata[[1]] + $target_metadata[[1]]$target_id + [1] "inc hosp" + + $target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $target_metadata[[1]]$target_keys + $target_metadata[[1]]$target_keys$target + [1] "inc hosp" + + + $target_metadata[[1]]$target_type + [1] "discrete" + + $target_metadata[[1]]$is_step_ahead + [1] TRUE + + $target_metadata[[1]]$time_unit + [1] "week" + + + $target_metadata[[2]] + $target_metadata[[2]]$target_id + [1] "inc death" + + $target_metadata[[2]]$target_name + [1] "Weekly incident influenza deaths" + + $target_metadata[[2]]$target_units + [1] "rate per 100,000 population" + + $target_metadata[[2]]$target_keys + $target_metadata[[2]]$target_keys$target + [1] "inc death" + + + $target_metadata[[2]]$target_type + [1] "discrete" + + $target_metadata[[2]]$is_step_ahead + [1] TRUE + + $target_metadata[[2]]$time_unit + [1] "week" + + + + attr(,"class") + [1] "target_metadata" "list" + attr(,"n") + [1] 2 + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +# create_target_metadata functions error correctly + + Code + create_target_metadata(create_target_metadata_item(target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), target_type = "discrete", + is_step_ahead = TRUE, time_unit = "week"), create_target_metadata_item( + target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = list(target = "inc hosp"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week")) + Condition + Error in `create_target_metadata()`: + ! `target_id`s must be unique across all `target_metadata_item`s. + x `target_metadata_item` 2 with `target_id` value inc hosp is duplicate. + +--- + + Code + create_target_metadata(create_target_metadata_item(target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), target_type = "discrete", + is_step_ahead = TRUE, time_unit = "week"), create_target_metadata_item( + target_id = "inc death", target_name = "Weekly incident influenza deaths", + target_units = "rate per 100,000 population", target_keys = list(target = "inc hosp"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week")) + Condition + Error in `create_target_metadata()`: + ! `target_keys`s must be unique across all `target_metadata_item`s. + x `target_metadata_item` 2 with `target_keys` value list(target = "inc hosp") is duplicate. + +--- + + Code + create_target_metadata(create_target_metadata_item(target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), target_type = "discrete", + is_step_ahead = TRUE, time_unit = "week"), list(a = 10), list(b = 10)) + Condition + Error in `create_target_metadata()`: + ! All items supplied must inherit from class + x Items 2 and 3 do not. + +--- + + Code + create_target_metadata(item_1, create_target_metadata_item(target_id = "inc death", + target_name = "Weekly incident influenza deaths", target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), target_type = "discrete", + is_step_ahead = TRUE, time_unit = "week")) + Condition + Error in `create_target_metadata()`: + ! All items supplied must be created against the same Hub schema. + x `schema_id` attributes are not consistent across all items. + Item `schema_id` attributes: + * Item 1 : invalid_schema + * Item 2 : https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json + diff --git a/tests/_snaps/create_target_metadata_item.md b/tests/_snaps/create_target_metadata_item.md new file mode 100644 index 0000000..08677d9 --- /dev/null +++ b/tests/_snaps/create_target_metadata_item.md @@ -0,0 +1,151 @@ +# create_target_metadata_item functions work correctly + + Code + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = list(target = "inc hosp"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week") + Output + $target_id + [1] "inc hosp" + + $target_name + [1] "Weekly incident influenza hospitalizations" + + $target_units + [1] "rate per 100,000 population" + + $target_keys + $target_keys$target + [1] "inc hosp" + + + $target_type + [1] "discrete" + + $is_step_ahead + [1] TRUE + + $time_unit + [1] "week" + + attr(,"class") + [1] "target_metadata_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +--- + + Code + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_type = "discrete", + is_step_ahead = FALSE) + Output + $target_id + [1] "inc hosp" + + $target_name + [1] "Weekly incident influenza hospitalizations" + + $target_units + [1] "rate per 100,000 population" + + $target_keys + NULL + + $target_type + [1] "discrete" + + $is_step_ahead + [1] FALSE + + attr(,"class") + [1] "target_metadata_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +# create_target_metadata_item functions error correctly + + Code + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = list(target = "inc hosp"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "weekly") + Condition + Error in `map()`: + i In index: 6. + Caused by error in `create_target_metadata_item()`: + x `time_unit` value is invalid. + ! Must be one of "day", "week", and "month". + i Actual value is "weekly" + +--- + + Code + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = list(target = c( + "inc hosp", "inc death")), target_type = "discrete", is_step_ahead = TRUE, + time_unit = "week") + Condition + Error in `map2()`: + i In index: 1. + i With name: target. + Caused by error in `create_target_metadata_item()`: + ! `target_keys` element target must be a vector of length 1 not a vector of length 2 + +--- + + Code + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = c(target = "inc hosp"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week") + Condition + Error in `create_target_metadata_item()`: + ! `target_keys` must be a not a + +--- + + Code + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_type = "discrete", + is_step_ahead = TRUE) + Condition + Error in `create_target_metadata_item()`: + ! A value must be provided for `time_unit` when `is_step_ahead` is TRUE + +--- + + Code + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = 1e+05, target_type = "discrete", is_step_ahead = FALSE) + Condition + Error in `map()`: + i In index: 3. + Caused by error in `create_target_metadata_item()`: + x `target_units` is of type . + ! Must be . + +--- + + Code + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_type = "invalid_target_type", + is_step_ahead = FALSE) + Condition + Error in `map()`: + i In index: 4. + Caused by error in `create_target_metadata_item()`: + x `target_type` value is invalid. + ! Must be one of "continuous", "discrete", "date", "binary", "nominal", "ordinal", and "compositional". + i Actual value is "invalid_target_type" + +--- + + Code + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = c("rate per 100,000 population", "count"), target_type = "discrete", + is_step_ahead = FALSE) + Condition + Error in `map()`: + i In index: 3. + Caused by error in `create_target_metadata_item()`: + x `target_units` must be length 1, not 2. + diff --git a/tests/_snaps/create_task_id.md b/tests/_snaps/create_task_id.md new file mode 100644 index 0000000..9ee43ea --- /dev/null +++ b/tests/_snaps/create_task_id.md @@ -0,0 +1,114 @@ +# create_task_id works correctly + + Code + create_task_id("horizon", required = 1L, optional = 2:4) + Output + $horizon + $horizon$required + [1] 1 + + $horizon$optional + [1] 2 3 4 + + + attr(,"class") + [1] "task_id" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +--- + + Code + create_task_id("origin_date", required = NULL, optional = c("2023-01-02", + "2023-01-09", "2023-01-16")) + Output + $origin_date + $origin_date$required + NULL + + $origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + attr(,"class") + [1] "task_id" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +--- + + Code + create_task_id("scenario_id", required = NULL, optional = c("A-2021-03-28", + "B-2021-03-28")) + Output + $scenario_id + $scenario_id$required + NULL + + $scenario_id$optional + [1] "A-2021-03-28" "B-2021-03-28" + + + attr(,"class") + [1] "task_id" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +--- + + Code + create_task_id("scenario_id", required = NULL, optional = c(1L, 2L)) + Output + $scenario_id + $scenario_id$required + NULL + + $scenario_id$optional + [1] 1 2 + + + attr(,"class") + [1] "task_id" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +# create_task_id errors correctly + + Code + create_task_id("horizon", required = NULL, optional = NULL) + Condition + Error in `check_prop_not_all_null()`: + x Both arguments `required` and `optional` cannot be NULL. + +--- + + Code + create_task_id("origin_date", required = NULL, optional = c("01/20/2023")) + Condition + Error in `map()`: + i In index: 2. + Caused by error in `create_task_id()`: + x `optional` value must be character string of date in valid ISO 8601 format (YYYY-MM-DD). + +--- + + Code + create_task_id("scenario_id", required = NULL, optional = c(1L, 1L)) + Condition + Error in `map()`: + i In index: 2. + Caused by error in `create_task_id()`: + ! All values in `optional` must be unique. + x Value 1 is duplicated. + +--- + + Code + create_task_id("horizon", required = c(TRUE, FALSE), optional = NULL) + Condition + Error in `map()`: + i In index: 1. + Caused by error in `create_task_id()`: + x `required` is of type . + ! Must be one of . + diff --git a/tests/_snaps/create_task_ids.md b/tests/_snaps/create_task_ids.md new file mode 100644 index 0000000..084a42e --- /dev/null +++ b/tests/_snaps/create_task_ids.md @@ -0,0 +1,93 @@ +# create_task_ids functions work correctly + + Code + create_task_ids(create_task_id("origin_date", required = NULL, optional = c( + "2023-01-02", "2023-01-09", "2023-01-16")), create_task_id("scenario_id", + required = NULL, optional = c("A-2021-03-28", "B-2021-03-28")), + create_task_id("location", required = "US", optional = c("01", "02", "04", "05", + "06")), create_task_id("target", required = "inc hosp", optional = NULL), + create_task_id("horizon", required = 1L, optional = 2:4)) + Output + $task_ids + $task_ids$origin_date + $task_ids$origin_date$required + NULL + + $task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $task_ids$scenario_id + $task_ids$scenario_id$required + NULL + + $task_ids$scenario_id$optional + [1] "A-2021-03-28" "B-2021-03-28" + + + $task_ids$location + $task_ids$location$required + [1] "US" + + $task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $task_ids$target + $task_ids$target$required + [1] "inc hosp" + + $task_ids$target$optional + NULL + + + $task_ids$horizon + $task_ids$horizon$required + [1] 1 + + $task_ids$horizon$optional + [1] 2 3 4 + + + + attr(,"class") + [1] "task_ids" "list" + attr(,"n") + [1] 5 + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json" + +# create_task_ids functions error correctly + + Code + create_task_ids(create_task_id("origin_date", required = NULL, optional = c( + "2023-01-02", "2023-01-09", "2023-01-16")), create_task_id("origin_date", + required = NULL, optional = c("2023-01-02", "2023-01-09", "2023-01-16"))) + Condition + Error in `create_task_ids()`: + ! `names` must be unique across all items. + x Item 2 with `name` "origin_date" is duplicate. + +--- + + Code + create_task_ids(create_task_id("origin_date", required = NULL, optional = c( + "2023-01-02", "2023-01-09", "2023-01-16")), list(a = 10), list(b = 10)) + Condition + Error in `create_task_ids()`: + ! All items supplied must inherit from class + x Items 2 and 3 do not. + +--- + + Code + create_task_ids(item_1, create_task_id("scenario_id", required = NULL, + optional = c("A-2021-03-28", "B-2021-03-28"))) + Condition + Error in `create_task_ids()`: + ! All items supplied must be created against the same Hub schema. + x `schema_id` attributes are not consistent across all items. + Item `schema_id` attributes: + * Item 1 : invalid_schema + * Item 2 : https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json + diff --git a/tests/_snaps/round_id_utils.md b/tests/_snaps/round_id_utils.md new file mode 100644 index 0000000..cc52c17 --- /dev/null +++ b/tests/_snaps/round_id_utils.md @@ -0,0 +1,189 @@ +# hubUtils::get_round_ids works correctly + + Code + hubUtils::get_round_ids(config_tasks) + Output + [1] "2022-10-01" "2022-10-08" "2022-10-15" "2022-10-22" "2022-10-29" + +--- + + Code + hubUtils::get_round_ids(config_tasks, flatten = "model_task") + Output + [[1]] + [1] "2022-10-01" "2022-10-08" + + [[2]] + [1] "2022-10-15" "2022-10-22" "2022-10-29" + + +--- + + Code + hubUtils::get_round_ids(config_tasks, flatten = "task_id") + Output + [[1]] + [[1]][[1]] + [1] "2022-10-01" "2022-10-08" + + + [[2]] + [[2]][[1]] + [1] "2022-10-15" "2022-10-22" "2022-10-29" + + + +--- + + Code + hubUtils::get_round_ids(config_tasks, flatten = "none") + Output + [[1]] + [[1]][[1]] + [[1]][[1]]$required + NULL + + [[1]][[1]]$optional + [1] "2022-10-01" "2022-10-08" + + + + [[2]] + [[2]][[1]] + [[2]][[1]]$required + NULL + + [[2]][[1]]$optional + [1] "2022-10-15" "2022-10-22" "2022-10-29" + + + + +--- + + Code + hubUtils::get_round_ids(config_tasks, flatten = "random") + Condition + Error in `hubUtils::get_round_ids()`: + ! `flatten` must be one of "all", "model_task", "task_id", or "none", not "random". + +--- + + Code + hubUtils::get_round_ids(config_tasks) + Output + [1] "2022-12-12" "2022-12-19" "2022-12-26" "2023-01-02" "2023-01-09" + [6] "2023-01-16" "2023-01-23" "2023-01-30" "2023-02-06" "2023-02-13" + [11] "2023-02-20" "2023-02-27" "2023-03-06" "2023-03-13" "2023-03-20" + [16] "2023-03-27" "2023-04-03" "2023-04-10" "2023-04-17" "2023-04-24" + [21] "2023-05-01" "2023-05-08" "2023-05-15" "2022-12-12" "2022-12-19" + [26] "2022-12-26" "2023-01-02" "2023-01-09" "2023-01-16" "2023-01-23" + [31] "2023-01-30" "2023-02-06" "2023-02-13" "2023-02-20" "2023-02-27" + [36] "2023-03-06" "2023-03-13" "2023-03-20" "2023-03-27" "2023-04-03" + [41] "2023-04-10" "2023-04-17" "2023-04-24" "2023-05-01" "2023-05-08" + [46] "2023-05-15" + +--- + + Code + hubUtils::get_round_ids(config_tasks, flatten = "model_task") + Output + [[1]] + [1] "2022-12-12" "2022-12-19" "2022-12-26" "2023-01-02" "2023-01-09" + [6] "2023-01-16" "2023-01-23" "2023-01-30" "2023-02-06" "2023-02-13" + [11] "2023-02-20" "2023-02-27" "2023-03-06" "2023-03-13" "2023-03-20" + [16] "2023-03-27" "2023-04-03" "2023-04-10" "2023-04-17" "2023-04-24" + [21] "2023-05-01" "2023-05-08" "2023-05-15" + + +--- + + Code + hubUtils::get_round_ids(config_tasks, flatten = "task_id") + Output + [[1]] + [[1]][[1]] + [1] "2022-12-12" "2022-12-19" "2022-12-26" "2023-01-02" "2023-01-09" + [6] "2023-01-16" "2023-01-23" "2023-01-30" "2023-02-06" "2023-02-13" + [11] "2023-02-20" "2023-02-27" "2023-03-06" "2023-03-13" "2023-03-20" + [16] "2023-03-27" "2023-04-03" "2023-04-10" "2023-04-17" "2023-04-24" + [21] "2023-05-01" "2023-05-08" "2023-05-15" + + [[1]][[2]] + [1] "2022-12-12" "2022-12-19" "2022-12-26" "2023-01-02" "2023-01-09" + [6] "2023-01-16" "2023-01-23" "2023-01-30" "2023-02-06" "2023-02-13" + [11] "2023-02-20" "2023-02-27" "2023-03-06" "2023-03-13" "2023-03-20" + [16] "2023-03-27" "2023-04-03" "2023-04-10" "2023-04-17" "2023-04-24" + [21] "2023-05-01" "2023-05-08" "2023-05-15" + + + +--- + + Code + hubUtils::get_round_ids(config_tasks, flatten = "none") + Output + [[1]] + [[1]][[1]] + [[1]][[1]]$required + NULL + + [[1]][[1]]$optional + [1] "2022-12-12" "2022-12-19" "2022-12-26" "2023-01-02" "2023-01-09" + [6] "2023-01-16" "2023-01-23" "2023-01-30" "2023-02-06" "2023-02-13" + [11] "2023-02-20" "2023-02-27" "2023-03-06" "2023-03-13" "2023-03-20" + [16] "2023-03-27" "2023-04-03" "2023-04-10" "2023-04-17" "2023-04-24" + [21] "2023-05-01" "2023-05-08" "2023-05-15" + + + [[1]][[2]] + [[1]][[2]]$required + NULL + + [[1]][[2]]$optional + [1] "2022-12-12" "2022-12-19" "2022-12-26" "2023-01-02" "2023-01-09" + [6] "2023-01-16" "2023-01-23" "2023-01-30" "2023-02-06" "2023-02-13" + [11] "2023-02-20" "2023-02-27" "2023-03-06" "2023-03-13" "2023-03-20" + [16] "2023-03-27" "2023-04-03" "2023-04-10" "2023-04-17" "2023-04-24" + [21] "2023-05-01" "2023-05-08" "2023-05-15" + + + + +# get_round_idx works correctly + + Code + get_round_idx(config_tasks, "2022-10-01") + Output + [1] 1 + +--- + + Code + get_round_idx(config_tasks, "2022-10-29") + Output + [1] 2 + +--- + + Code + get_round_idx(config_tasks) + Condition + Error in `get_round_idx()`: + ! `round_id` must be a character vector, not absent. + +--- + + Code + get_round_idx(config_tasks, round_id = "2023-01-02") + Output + [1] 1 + +--- + + Code + get_round_idx(config_tasks) + Condition + Error in `get_round_idx()`: + ! `round_id` must be a character vector, not absent. + diff --git a/tests/_snaps/validate_config.md b/tests/_snaps/validate_config.md new file mode 100644 index 0000000..82bbe7b --- /dev/null +++ b/tests/_snaps/validate_config.md @@ -0,0 +1,773 @@ +# Config errors detected successfully + + Code + out + Output + [1] FALSE + attr(,"errors") + instancePath + 1 /rounds/0/model_tasks/0/task_ids/target/required + 2 /rounds/0/model_tasks/0/output_type/mean/type_id/optional + 3 /rounds/0/model_tasks/0/output_type/mean/type_id/required + 4 /rounds/0/model_tasks/0/output_type/mean/type_id + 5 /rounds/0/submissions_due + 6 /rounds/0/submissions_due/start + 7 /rounds/0/submissions_due/end + 8 /rounds/0/submissions_due + schemaPath + 1 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/target/properties/required/type + 2 #/properties/rounds/items/properties/model_tasks/items/properties/output_type/properties/mean/properties/type_id/oneOf/0/properties/optional/type + 3 #/properties/rounds/items/properties/model_tasks/items/properties/output_type/properties/mean/properties/type_id/oneOf/1/properties/required/type + 4 #/properties/rounds/items/properties/model_tasks/items/properties/output_type/properties/mean/properties/type_id/oneOf + 5 #/properties/rounds/items/properties/submissions_due/oneOf/0/required + 6 #/properties/rounds/items/properties/submissions_due/oneOf/1/properties/start/type + 7 #/properties/rounds/items/properties/submissions_due/oneOf/1/properties/end/type + 8 #/properties/rounds/items/properties/submissions_due/oneOf + keyword params.type params.passingSchemas params.missingProperty + 1 type array, null NA + 2 type null NA + 3 type null NA + 4 oneOf NULL NA + 5 required NULL NA relative_to + 6 type string NA + 7 type string NA + 8 oneOf NULL NA + message + 1 must be array,null + 2 must be null + 3 must be null + 4 must match exactly one schema in oneOf + 5 must have required property 'relative_to' + 6 must be string + 7 must be string + 8 must match exactly one schema in oneOf + schema + 1 array, null + 2 null + 3 null + 4 When mean is required, property set to single element 'NA' array, When mean is optional, property set to null, array, null, NA, NA, 1, NA, When mean is required, property set to null, When mean is optional, property set to single element 'NA' array, null, array, NA, NA, NA, 1 + 5 relative_to, start, end + 6 string + 7 string + 8 Name of task id variable in relation to which submission start and end dates are calculated., NA, string, NA, Difference in days between start and origin date., Submission start date., integer, string, NA, date, Difference in days between end and origin date., Submission end date., integer, string, NA, date, relative_to, start, end, start, end + parentSchema.description + 1 Array of target unique identifiers that must be present for submission to be valid. Can be null if no targets are required and all valid targets are specified in the optional property. + 2 When mean is required, property set to null + 3 When mean is optional, property set to null + 4 type_id is not meaningful for a mean output_type. The property is primarily used to determine whether mean is a required or optional output type through properties required and optional. If mean is a required output type, the required property must be an array containing the single string element 'NA' and the optional property must be set to null. If mean is an optional output type, the optional property must be an array containing the single string element 'NA' and the required property must be set to null + 5 + 6 Submission start date. + 7 Submission end date. + 8 Object defining the dates by which model forecasts must be submitted to the hub. + parentSchema.type parentSchema.type + 1 array, null string + 2 null + 3 null + 4 object + 5 NULL + 6 string + 7 string + 8 object + parentSchema.examples + 1 NULL + 2 NULL + 3 NULL + 4 NA, NA + 5 NULL + 6 NULL + 7 NULL + 8 2022-06-07, -4, 2022-07-20, 2, NA, origin_date + parentSchema.oneOf + 1 NULL + 2 NULL + 3 NULL + 4 When mean is required, property set to single element 'NA' array, When mean is optional, property set to null, array, null, NA, NA, 1, NA, When mean is required, property set to null, When mean is optional, property set to single element 'NA' array, null, array, NA, NA, NA, 1 + 5 NULL + 6 NULL + 7 NULL + 8 Name of task id variable in relation to which submission start and end dates are calculated., NA, string, NA, Difference in days between start and origin date., Submission start date., integer, string, NA, date, Difference in days between end and origin date., Submission end date., integer, string, NA, date, relative_to, start, end, start, end + parentSchema.required + 1 NULL + 2 NULL + 3 NULL + 4 required, optional + 5 relative_to, start, end + 6 NULL + 7 NULL + 8 start, end + parentSchema.properties.relative_to.description + 1 + 2 + 3 + 4 + 5 Name of task id variable in relation to which submission start and end dates are calculated. + 6 + 7 + 8 + parentSchema.properties.relative_to.type + 1 + 2 + 3 + 4 + 5 string + 6 + 7 + 8 + parentSchema.properties.start.description + 1 + 2 + 3 + 4 + 5 Difference in days between start and origin date. + 6 + 7 + 8 + parentSchema.properties.start.type + 1 + 2 + 3 + 4 + 5 integer + 6 + 7 + 8 + parentSchema.properties.end.description + 1 + 2 + 3 + 4 + 5 Difference in days between end and origin date. + 6 + 7 + 8 + parentSchema.properties.end.type parentSchema.format data + 1 wk inc flu hosp + 2 NA + 3 NA + 4 NA, NA + 5 integer -6, 1 + 6 date -6 + 7 date 1 + 8 -6, 1 + dataPath + 1 /rounds/0/model_tasks/0/task_ids/target/required + 2 /rounds/0/model_tasks/0/output_type/mean/type_id/optional + 3 /rounds/0/model_tasks/0/output_type/mean/type_id/required + 4 /rounds/0/model_tasks/0/output_type/mean/type_id + 5 /rounds/0/submissions_due + 6 /rounds/0/submissions_due/start + 7 /rounds/0/submissions_due/end + 8 /rounds/0/submissions_due + attr(,"config_path") + [1] "testdata/tasks-errors.json" + attr(,"schema_version") + [1] "v0.0.0.9" + attr(,"schema_url") + https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v0.0.0.9/tasks-schema.json + +# Dynamic config errors detected successfully by custom R validation + + Code + out + Output + [1] FALSE + attr(,"config_path") + [1] "testdata/tasks-errors-rval.json" + attr(,"schema_version") + [1] "v2.0.0" + attr(,"schema_url") + https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json + attr(,"errors") + instancePath + 1 /rounds/0/model_tasks/1/target_metadata/0/target_keys/target_measure + 2 /rounds/0/model_tasks/1/target_metadata/1/target_keys/target_measure + 3 /rounds/0/model_tasks/1/target_metadata/0/target_keys/target_outcome + 4 /rounds/0/model_tasks/1/task_ids/target_outcome + schemaPath + 1 #/properties/rounds/items/properties/model_tasks/items/properties/target_metadata/items/properties/target_keys + 2 #/properties/rounds/items/properties/model_tasks/items/properties/target_metadata/items/properties/target_keys + 3 #/properties/rounds/items/properties/model_tasks/items/properties/target_metadata/items/properties/target_keys + 4 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids + keyword + 1 target_keys names + 2 target_keys names + 3 target_keys values + 4 task_id values + message + 1 target_key(s) 'target_measure' not properties of modeling task group task IDs + 2 target_key(s) 'target_measure' not properties of modeling task group task IDs + 3 target_key value 'flu hospitalisation' does not match any values in corresponding modeling task group task_id + 4 task_id value(s) 'flu hosp, wk inc flu hosp' not defined in any corresponding target_key. + schema + 1 + 2 + 3 + 4 + data + 1 task_id names: origin_date, target_outcome, target_mesures, horizon, location;\ntarget_key names: target_measure, target_outcome + 2 task_id names: origin_date, target_outcome, target_mesures, horizon, location;\ntarget_key names: target_measure, target_outcome + 3 task_id.target_outcome values: flu hosp, wk inc flu hosp, flu case;\ntarget_key.target_outcome value: flu hospitalisation + 4 task_id.target_outcome unique values: flu hosp, wk inc flu hosp, flu case;\ntarget_key.target_outcome unique values: flu hospitalisation, flu case + +# Reserved hub variable task id name detected correctly + + Code + out + Output + [1] FALSE + attr(,"config_path") + [1] "testdata/tasks-errors-rval-reserved.json" + attr(,"schema_version") + [1] "v2.0.0" + attr(,"schema_url") + https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json + attr(,"errors") + instancePath + 1 /rounds/0/model_tasks/1/target_metadata/0/target_keys/target_measure + 2 /rounds/0/model_tasks/1/target_metadata/1/target_keys/target_measure + 3 /rounds/0/model_tasks/1/target_metadata/0/target_keys/target_outcome + 4 /rounds/0/model_tasks/1/task_ids/target_outcome + 5 /rounds/0/model_tasks/0/task_ids/ + 6 /rounds/0/model_tasks/1/task_ids/ + schemaPath + 1 #/properties/rounds/items/properties/model_tasks/items/properties/target_metadata/items/properties/target_keys + 2 #/properties/rounds/items/properties/model_tasks/items/properties/target_metadata/items/properties/target_keys + 3 #/properties/rounds/items/properties/model_tasks/items/properties/target_metadata/items/properties/target_keys + 4 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids + 5 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids + 6 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids + keyword + 1 target_keys names + 2 target_keys names + 3 target_keys values + 4 task_id values + 5 task_id names + 6 task_id names + message + 1 target_key(s) 'target_measure' not properties of modeling task group task IDs + 2 target_key(s) 'target_measure' not properties of modeling task group task IDs + 3 target_key value 'flu hospitalisation' does not match any values in corresponding modeling task group task_id + 4 task_id value(s) 'flu hosp, wk inc flu hosp' not defined in any corresponding target_key. + 5 task_id name(s) 'model_id' must not match reserved hub variable names. + 6 task_id name(s) 'value' must not match reserved hub variable names. + schema + 1 + 2 + 3 + 4 + 5 + 6 + data + 1 task_id names: origin_date, target_outcome, target_mesures, horizon, value;\ntarget_key names: target_measure, target_outcome + 2 task_id names: origin_date, target_outcome, target_mesures, horizon, value;\ntarget_key names: target_measure, target_outcome + 3 task_id.target_outcome values: flu hosp, wk inc flu hosp, flu case;\ntarget_key.target_outcome value: flu hospitalisation + 4 task_id.target_outcome unique values: flu hosp, wk inc flu hosp, flu case;\ntarget_key.target_outcome unique values: flu hospitalisation, flu case + 5 task_id names: origin_date, target, horizon & model_id;\nreserved hub variable names: model_id, output_type, output_type_id & value + 6 task_id names: origin_date, target_outcome, target_mesures, horizon & value;\nreserved hub variable names: model_id, output_type, output_type_id & value + +# Additional properties error successfully + + Code + out + Output + [1] FALSE + attr(,"errors") + instancePath + 1 /rounds/0/model_tasks/0/output_type + schemaPath + 1 #/properties/rounds/items/properties/model_tasks/items/properties/output_type/additionalProperties + keyword additionalProperty message + 1 additionalProperties target_metadata must NOT have additional properties + schema parentSchema.type + 1 FALSE object + parentSchema.description + 1 Object defining valid model output types for a given modeling task. The name of each property corresponds to valid values in column 'output_type' while the 'output_type_id' property of each output type defines the valid values of the 'output_type_id' column and the 'value' property defines the valid values of the 'value' column for a given output type. + parentSchema.properties.mean.type + 1 object + parentSchema.properties.mean.description + 1 Object defining the mean of the predictive distribution output type. + parentSchema.properties.mean.properties.output_type_id.description + 1 output_type_id is not meaningful for a mean output_type. The property is primarily used to determine whether mean is a required or optional output type through properties required and optional. If mean is a required output type, the required property must be an array containing the single string element 'NA' and the optional property must be set to null. If mean is an optional output type, the optional property must be an array containing the single string element 'NA' and the required property must be set to null + parentSchema.properties.mean.properties.output_type_id.examples + 1 NA, NA + parentSchema.properties.mean.properties.output_type_id.type + 1 object + parentSchema.properties.mean.properties.output_type_id.oneOf + 1 When mean is required, property set to single element 'NA' array, When mean is optional, property set to null, array, null, NA, NA, 1, NA, When mean is required, property set to null, When mean is optional, property set to single element 'NA' array, null, array, NA, NA, NA, 1 + parentSchema.properties.mean.properties.output_type_id.required + 1 required, optional + parentSchema.properties.mean.properties.value.type + 1 object + parentSchema.properties.mean.properties.value.description + 1 Object defining the characteristics of valid mean values. + parentSchema.properties.mean.properties.value.examples + 1 double, 0 + parentSchema.properties.mean.properties.value.properties.type.description + 1 Data type of mean values. + parentSchema.properties.mean.properties.value.properties.type.type + 1 string + parentSchema.properties.mean.properties.value.properties.type.enum + 1 double, integer + parentSchema.properties.mean.properties.value.properties.minimum.description + 1 The minimum inclusive valid mean value + parentSchema.properties.mean.properties.value.properties.minimum.type + 1 number, integer + parentSchema.properties.mean.properties.value.properties.maximum.description + 1 the maximum inclusive valid mean value + parentSchema.properties.mean.properties.value.properties.maximum.type + 1 number, integer + parentSchema.properties.mean.properties.value.required + 1 type + parentSchema.properties.mean.required parentSchema.properties.median.type + 1 output_type_id, value object + parentSchema.properties.median.description + 1 Object defining the median of the predictive distribution output type + parentSchema.properties.median.properties.output_type_id.description + 1 output_type_id is not meaningful for a median output_type. The property is primarily used to determine whether median is a required or optional output type through properties required and optional. If median is a required output type, the required property must be an array containing the single string element 'NA' and the optional property must be set to null. If median is an optional output type, the optional property must be an array containing the single string element 'NA' and the required property must be set to null + parentSchema.properties.median.properties.output_type_id.examples + 1 NA, NA + parentSchema.properties.median.properties.output_type_id.type + 1 object + parentSchema.properties.median.properties.output_type_id.oneOf + 1 When median is required, property set to single element 'NA' array, When median is optional, property set to null, array, null, NA, NA, 1, NA, When median is required, property set to null, When median is optional, property set to single element 'NA' array, null, array, NA, NA, NA, 1 + parentSchema.properties.median.properties.output_type_id.required + 1 required, optional + parentSchema.properties.median.properties.value.type + 1 object + parentSchema.properties.median.properties.value.description + 1 Object defining the characteristics of valid median values + parentSchema.properties.median.properties.value.examples + 1 double, 0 + parentSchema.properties.median.properties.value.properties.type.description + 1 Data type of median values + parentSchema.properties.median.properties.value.properties.type.type + 1 string + parentSchema.properties.median.properties.value.properties.type.enum + 1 double, integer + parentSchema.properties.median.properties.value.properties.minimum.description + 1 The minimum inclusive valid median value + parentSchema.properties.median.properties.value.properties.minimum.type + 1 number, integer + parentSchema.properties.median.properties.value.properties.maximum.description + 1 the maximum inclusive valid median value + parentSchema.properties.median.properties.value.properties.maximum.type + 1 number, integer + parentSchema.properties.median.properties.value.required + 1 type + parentSchema.properties.median.required + 1 output_type_id, value + parentSchema.properties.quantile.description + 1 Object defining the quantiles of the predictive distribution output type. + parentSchema.properties.quantile.type + 1 object + parentSchema.properties.quantile.properties.output_type_id.description + 1 Object containing required and optional arrays defining the probability levels at which quantiles of the predictive distribution will be recorded. + parentSchema.properties.quantile.properties.output_type_id.examples + 1 0.25, 0.50, 0.75, 0.10, 0.20, 0.30, 0.40, 0.60, 0.70, 0.80, 0.90 + parentSchema.properties.quantile.properties.output_type_id.type + 1 object + parentSchema.properties.quantile.properties.output_type_id.properties.required.description + 1 Array of unique probability levels between 0 and 1 that must be present for submission to be valid. Can be null if no probability levels are required and all valid probability levels are specified in the optional property. + parentSchema.properties.quantile.properties.output_type_id.properties.required.type + 1 array, null + parentSchema.properties.quantile.properties.output_type_id.properties.required.uniqueItems + 1 TRUE + parentSchema.properties.quantile.properties.output_type_id.properties.required.items.type + 1 number + parentSchema.properties.quantile.properties.output_type_id.properties.required.items.minimum + 1 0 + parentSchema.properties.quantile.properties.output_type_id.properties.required.items.maximum + 1 1 + parentSchema.properties.quantile.properties.output_type_id.properties.optional.description + 1 Array of valid but not required unique probability levels. Can be null if all probability levels are required and are specified in the required property. + parentSchema.properties.quantile.properties.output_type_id.properties.optional.type + 1 array, null + parentSchema.properties.quantile.properties.output_type_id.properties.optional.uniqueItems + 1 TRUE + parentSchema.properties.quantile.properties.output_type_id.properties.optional.items.type + 1 number + parentSchema.properties.quantile.properties.output_type_id.properties.optional.items.minimum + 1 0 + parentSchema.properties.quantile.properties.output_type_id.properties.optional.items.maximum + 1 1 + parentSchema.properties.quantile.properties.output_type_id.required + 1 required, optional + parentSchema.properties.quantile.properties.value.type + 1 object + parentSchema.properties.quantile.properties.value.description + 1 Object defining the characteristics of valid quantiles of the predictive distribution at a given probability level. + parentSchema.properties.quantile.properties.value.properties.type.description + 1 Data type of quantile values. + parentSchema.properties.quantile.properties.value.properties.type.examples + 1 double + parentSchema.properties.quantile.properties.value.properties.type.type + 1 string + parentSchema.properties.quantile.properties.value.properties.type.enum + 1 double, integer + parentSchema.properties.quantile.properties.value.properties.minimum.description + 1 The minimum inclusive valid quantile value (optional). + parentSchema.properties.quantile.properties.value.properties.minimum.examples + 1 0 + parentSchema.properties.quantile.properties.value.properties.minimum.type + 1 number, integer + parentSchema.properties.quantile.properties.value.properties.maximum.description + 1 The maximum inclusive valid quantile value (optional). + parentSchema.properties.quantile.properties.value.properties.maximum.type + 1 number, integer + parentSchema.properties.quantile.properties.value.required + 1 type + parentSchema.properties.quantile.required + 1 output_type_id, value + parentSchema.properties.cdf.description + 1 Object defining the cumulative distribution function of the predictive distribution output type. + parentSchema.properties.cdf.type + 1 object + parentSchema.properties.cdf.properties.output_type_id.description + 1 Object containing required and optional arrays defining possible values of the target variable at which values of the cumulative distribution function of the predictive distribution will be recorded. + parentSchema.properties.cdf.properties.output_type_id.examples + 1 10, 20, EW202240, EW202241, EW202242, EW202243, EW202244, EW202245, EW202246, EW202247, NA, NA + parentSchema.properties.cdf.properties.output_type_id.type + 1 object + parentSchema.properties.cdf.properties.output_type_id.properties.required.description + 1 Array of unique target values that must be present for submission to be valid. Can be null if no target values are required and all valid target values are specified in the optional property. + parentSchema.properties.cdf.properties.output_type_id.properties.required.type + 1 array, null + parentSchema.properties.cdf.properties.output_type_id.properties.required.uniqueItems + 1 TRUE + parentSchema.properties.cdf.properties.output_type_id.properties.required.oneOf + 1 number, integer, string, 0, NA, NA, ^EW[0-9]{6}, NA, 8, NA, 8 + parentSchema.properties.cdf.properties.output_type_id.properties.optional.description + 1 Array of valid but not required unique target values. Can be null if all target values are required and are specified in the required property. + parentSchema.properties.cdf.properties.output_type_id.properties.optional.type + 1 array, null + parentSchema.properties.cdf.properties.output_type_id.properties.optional.uniqueItems + 1 TRUE + parentSchema.properties.cdf.properties.output_type_id.properties.optional.oneOf + 1 number, integer, string, 0, NA, NA, ^EW[0-9]{6}, NA, 8, NA, 8 + parentSchema.properties.cdf.properties.output_type_id.required + 1 required, optional + parentSchema.properties.cdf.properties.value.type + 1 object + parentSchema.properties.cdf.properties.value.description + 1 Object defining the characteristics of valid values of the cumulative distribution function at a given target value. + parentSchema.properties.cdf.properties.value.properties.type.description + 1 Data type of cumulative distribution function values. + parentSchema.properties.cdf.properties.value.properties.type.examples + 1 double + parentSchema.properties.cdf.properties.value.properties.type.const + 1 double + parentSchema.properties.cdf.properties.value.properties.minimum.description + 1 The minimum inclusive valid cumulative distribution function value. Must be 0. + parentSchema.properties.cdf.properties.value.properties.minimum.const + 1 0 + parentSchema.properties.cdf.properties.value.properties.maximum.description + 1 The maximum inclusive valid cumulative distribution function value. Must be 1. + parentSchema.properties.cdf.properties.value.properties.maximum.const + 1 1 + parentSchema.properties.cdf.properties.value.required + 1 type, minimum, maximum + parentSchema.properties.cdf.required + 1 output_type_id, value + parentSchema.properties.pmf.description + 1 Object defining a probability mass function for a discrete variable output type. Includes nominal, binary and ordinal variable types. + parentSchema.properties.pmf.type + 1 object + parentSchema.properties.pmf.properties.output_type_id.description + 1 Object containing required and optional arrays specifying valid categories of a discrete variable. + parentSchema.properties.pmf.properties.output_type_id.examples + 1 NA, low, moderate, high, extreme + parentSchema.properties.pmf.properties.output_type_id.type + 1 object + parentSchema.properties.pmf.properties.output_type_id.properties.required.description + 1 Array of unique categories of a discrete variable that must be present for submission to be valid. Can be null if no categories are required and all valid categories are specified in the optional property. + parentSchema.properties.pmf.properties.output_type_id.properties.required.type + 1 array, null + parentSchema.properties.pmf.properties.output_type_id.properties.required.uniqueItems + 1 TRUE + parentSchema.properties.pmf.properties.output_type_id.properties.required.type + 1 string + parentSchema.properties.pmf.properties.output_type_id.properties.optional.description + 1 Array of valid but not required unique categories of a discrete variable. Can be null if all categories are required and are specified in the required property. + parentSchema.properties.pmf.properties.output_type_id.properties.optional.type + 1 array, null + parentSchema.properties.pmf.properties.output_type_id.properties.optional.uniqueItems + 1 TRUE + parentSchema.properties.pmf.properties.output_type_id.properties.optional.type + 1 string + parentSchema.properties.pmf.properties.output_type_id.required + 1 required, optional + parentSchema.properties.pmf.properties.value.type + 1 object + parentSchema.properties.pmf.properties.value.description + 1 Object defining valid values of the probability mass function of the predictive distribution for a given category of a discrete outcome variable. + parentSchema.properties.pmf.properties.value.examples + 1 double, 0, 1 + parentSchema.properties.pmf.properties.value.properties.type.description + 1 Data type of the probability mass function values. + parentSchema.properties.pmf.properties.value.properties.type.const + 1 double + parentSchema.properties.pmf.properties.value.properties.minimum.description + 1 The minimum inclusive valid probability mass function value. Must be 0. + parentSchema.properties.pmf.properties.value.properties.minimum.const + 1 0 + parentSchema.properties.pmf.properties.value.properties.maximum.description + 1 The maximum inclusive valid probability mass function value. Must be 1. + parentSchema.properties.pmf.properties.value.properties.maximum.const + 1 1 + parentSchema.properties.pmf.properties.value.required + 1 type, minimum, maximum + parentSchema.properties.pmf.required + 1 output_type_id, value + parentSchema.properties.sample.description + 1 Object defining a sample output type. + parentSchema.properties.sample.type + 1 object + parentSchema.properties.sample.properties.output_type_id.description + 1 Object containing required and optional arrays specifying valid sample values. + parentSchema.properties.sample.properties.output_type_id.examples + 1 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + parentSchema.properties.sample.properties.output_type_id.type + 1 object + parentSchema.properties.sample.properties.output_type_id.properties.required.description + 1 Array of unique sample indexes that must be present for submission to be valid. Can be null if no sample indexes are required and all valid sample indexes are specified in the optional property. + parentSchema.properties.sample.properties.output_type_id.properties.required.type + 1 array, null + parentSchema.properties.sample.properties.output_type_id.properties.required.uniqueItems + 1 TRUE + parentSchema.properties.sample.properties.output_type_id.properties.required.items.type + 1 integer + parentSchema.properties.sample.properties.output_type_id.properties.required.items.minimum + 1 1 + parentSchema.properties.sample.properties.output_type_id.properties.optional.description + 1 Array of valid but not required unique sample indexes. Can be null if all sample indexes are required and are specified in the required property. + parentSchema.properties.sample.properties.output_type_id.properties.optional.type + 1 array, null + parentSchema.properties.sample.properties.output_type_id.properties.optional.uniqueItems + 1 TRUE + parentSchema.properties.sample.properties.output_type_id.properties.optional.items.type + 1 integer + parentSchema.properties.sample.properties.output_type_id.properties.optional.items.minimum + 1 1 + parentSchema.properties.sample.properties.output_type_id.required + 1 required, optional + parentSchema.properties.sample.properties.value.type + 1 object + parentSchema.properties.sample.properties.value.description + 1 Object defining valid values of samples from the predictive distribution for a given sample index. Depending on the Hub specification, samples with the same sample index (specified by the output_type_id) may be assumed to correspond to a joint distribution across multiple levels of the task id variables. See Hub documentation for details. + parentSchema.properties.sample.properties.value.properties.type.description + 1 Data type of sample value from the predictive distribution. + parentSchema.properties.sample.properties.value.properties.type.examples + 1 double + parentSchema.properties.sample.properties.value.properties.type.type + 1 string + parentSchema.properties.sample.properties.value.properties.type.enum + 1 double, integer + parentSchema.properties.sample.properties.value.properties.description + 1 The minimum inclusive valid sample value from the predictive distribution + parentSchema.properties.sample.properties.value.properties.description + 1 The maximum inclusive valid sample value from the predictive distribution + parentSchema.properties.sample.properties.value.required + 1 type + parentSchema.properties.sample.required parentSchema.additionalProperties + 1 output_type_id, value FALSE + data.mean.output_type_id.required data.mean.output_type_id.optional + 1 NA NA + data.mean.value.type data.mean.value.minimum + 1 integer 0 + data.quantile.output_type_id.required + 1 0.010, 0.025, 0.050, 0.100, 0.150, 0.200, 0.250, 0.300, 0.350, 0.400, 0.450, 0.500, 0.550, 0.600, 0.650, 0.700, 0.750, 0.800, 0.850, 0.900, 0.950, 0.975, 0.990 + data.quantile.output_type_id.optional data.quantile.value.type + 1 NA integer + data.quantile.value.minimum + 1 0 + data.target_metadata + 1 inc flu hosp, daily incident influenza hospitalizations, count, inc flu hosp, This target represents a count of the number of new hospitalizations per day in a given location., TRUE, day + dataPath + 1 /rounds/0/model_tasks/0/output_type + attr(,"config_path") + [1] "testdata/tasks-addprop.json" + attr(,"schema_version") + [1] "v2.0.0" + attr(,"schema_url") + https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json + +# Duplicate values in individual array error successfully + + Code + out + Output + [1] FALSE + attr(,"errors") + instancePath + 1 /rounds/1/model_tasks/0/task_ids/origin_date/optional + 2 /rounds/1/model_tasks/0/output_type/quantile/output_type_id/required + schemaPath + 1 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/origin_date/properties/optional/uniqueItems + 2 #/properties/rounds/items/properties/model_tasks/items/properties/output_type/properties/quantile/properties/output_type_id/properties/required/uniqueItems + keyword params.i params.j + 1 uniqueItems 2 3 + 2 uniqueItems 15 16 + message schema + 1 must NOT have duplicate items (items ## 3 and 2 are identical) TRUE + 2 must NOT have duplicate items (items ## 16 and 15 are identical) TRUE + parentSchema.description + 1 Array of valid but not required unique origin date identifiers. Can be null if all origin dates are required and are specified in the required property. + 2 Array of unique probability levels between 0 and 1 that must be present for submission to be valid. Can be null if no probability levels are required and all valid probability levels are specified in the optional property. + parentSchema.type parentSchema.uniqueItems parentSchema.items.type + 1 array, null TRUE string + 2 array, null TRUE number + parentSchema.items.format parentSchema.items.minimum + 1 date NA + 2 0 + parentSchema.items.maximum + 1 NA + 2 1 + data + 1 2022-10-15, 2022-10-22, 2022-10-29, 2022-10-29 + 2 0.010, 0.025, 0.050, 0.050, 0.100, 0.150, 0.200, 0.250, 0.300, 0.350, 0.400, 0.450, 0.500, 0.550, 0.600, 0.650, 0.650, 0.700, 0.750, 0.800, 0.850, 0.900, 0.950, 0.975, 0.990 + dataPath + 1 /rounds/1/model_tasks/0/task_ids/origin_date/optional + 2 /rounds/1/model_tasks/0/output_type/quantile/output_type_id/required + attr(,"config_path") + [1] "testdata/dup-in-array.json" + attr(,"schema_version") + [1] "v2.0.0" + attr(,"schema_url") + https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json + +# Duplicate values across property error successfully + + Code + out + Output + [1] FALSE + attr(,"config_path") + [1] "testdata/dup-in-property.json" + attr(,"schema_version") + [1] "v2.0.0" + attr(,"schema_url") + https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json + attr(,"errors") + instancePath + 1 /rounds/0/model_tasks/0/task_ids/horizon + 2 /rounds/1/model_tasks/0/task_ids/age_group + 3 /rounds/1/model_tasks/0/output_type/quantile + schemaPath + 1 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/horizon + 2 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/age_group + 3 #/properties/rounds/items/properties/model_tasks/items/properties/output_type/properties/quantile + keyword + 1 task_ids uniqueItems + 2 task_ids uniqueItems + 3 output_type uniqueItems + message + 1 must NOT have duplicate items across 'required' and 'optional' properties. Task ID 'horizon' contains duplicates. + 2 must NOT have duplicate items across 'required' and 'optional' properties. Task ID 'age_group' contains duplicates. + 3 must NOT have duplicate items across 'required' and 'optional' properties. Output type IDs of output type 'quantile' contains duplicates. + schema data + 1 duplicate values: 2 + 2 duplicate values: 65+ + 3 duplicate values: 0.99 + +# Inconsistent round ID variables across model tasks error successfully + + Code + out + Output + [1] FALSE + attr(,"config_path") + [1] "testdata/round-id-inconsistent.json" + attr(,"schema_version") + [1] "v1.0.0" + attr(,"schema_url") + https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v1.0.0/tasks-schema.json + attr(,"errors") + instancePath schemaPath keyword + 1 /rounds/0/model_tasks/1/task_ids/forecast_date NA round_id var + 2 /rounds/0/model_tasks/1/task_ids/forecast_date NA round_id var + message + 1 round_id var 'forecast_date' property MUST be consistent across modeling task items + 2 round_id var 'forecast_date' property MUST be consistent across modeling task items + schema + 1 + 2 + data + 1 Component "optional": Lengths (23, 22) differ (string compare on first 22) compared to first model task item + 2 Component "optional": 1 string mismatch compared to first model task item + +--- + + Code + out + Output + [1] FALSE + attr(,"config_path") + [1] "testdata/round-id-inconsistent2.json" + attr(,"schema_version") + [1] "v2.0.0" + attr(,"schema_url") + https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json + attr(,"errors") + instancePath + 1 /rounds/0/model_tasks/1/task_ids/forecast_date + 2 /rounds/0/model_tasks/1/task_ids/forecast_date + schemaPath + 1 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/forecast_date + 2 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/forecast_date + keyword + 1 round_id var + 2 round_id var + message + 1 round_id var 'forecast_date' property MUST be consistent across modeling task items + 2 round_id var 'forecast_date' property MUST be consistent across modeling task items + schema + 1 + 2 + data + 1 Component "optional": Lengths (23, 22) differ (string compare on first 22) compared to first model task item + 2 Component "optional": 1 string mismatch compared to first model task item + +# Duplicate round ID values across rounds error successfully + + Code + out + Output + [1] FALSE + attr(,"config_path") + [1] "testdata/dup-in-round-id.json" + attr(,"schema_version") + [1] "v2.0.0" + attr(,"schema_url") + https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json + attr(,"errors") + instancePath + 1 /rounds/1/model_tasks/0/task_ids/origin_date + 2 /rounds/1/model_tasks/0/task_ids/origin_date + 3 /rounds/2/model_tasks/0/task_ids/origin_date + 4 /rounds/2/model_tasks/0/task_ids/origin_date + 5 /rounds/2/model_tasks/0/task_ids/origin_date + schemaPath + 1 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/origin_date + 2 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/origin_date + 3 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/origin_date + 4 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/origin_date + 5 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/origin_date + keyword + 1 round_id uniqueItems + 2 round_id uniqueItems + 3 round_id uniqueItems + 4 round_id uniqueItems + 5 round_id uniqueItems + message schema + 1 must NOT contains duplicate round ID values across rounds + 2 must NOT contains duplicate round ID values across rounds + 3 must NOT contains duplicate round ID values across rounds + 4 must NOT contains duplicate round ID values across rounds + 5 must NOT contains duplicate round ID values across rounds + data + 1 duplicate value: 2022-10-08 + 2 duplicate value: 2022-10-15 + 3 duplicate value: 2022-10-15 + 4 duplicate value: 2022-10-22 + 5 duplicate value: 2022-10-29 + diff --git a/tests/_snaps/validate_model_metadata_schema.md b/tests/_snaps/validate_model_metadata_schema.md new file mode 100644 index 0000000..90eef5e --- /dev/null +++ b/tests/_snaps/validate_model_metadata_schema.md @@ -0,0 +1,13 @@ +# validate_model_metadata_schema works + + Code + str(attr(out_error, "errors")) + Output + 'data.frame': 1 obs. of 6 variables: + $ instancePath: chr "" + $ schemaPath : chr "properties" + $ keyword : chr "required" + $ message : chr "must have required properties: either 'model_id' or both 'team_abbr' and 'model_abbr'." + $ schema : chr "" + $ data : chr "team_name, model_name, model_abbr, model_version, model_contributors, website_url, repo_url, license, designate"| __truncated__ + diff --git a/tests/_snaps/view_config_val_errors.md b/tests/_snaps/view_config_val_errors.md new file mode 100644 index 0000000..96ec664 --- /dev/null +++ b/tests/_snaps/view_config_val_errors.md @@ -0,0 +1,162 @@ +# Errors report launch successful + + Code + tbl$`_source_notes` + Output + [[1]] + [1] "For more information, please consult the\n [**`hubDocs` documentation**.](https://hubdocs.readthedocs.io/en/latest/)" + attr(,"class") + [1] "from_markdown" + + +--- + + Code + tbl$`_heading` + Output + $title + [1] "**`hubUtils` config validation error report**" + attr(,"class") + [1] "from_markdown" + + $subtitle + [1] "Report for file **`testdata/tasks-errors.json`** using\nschema version [**v0.0.0.9**](https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v0.0.0.9/tasks-schema.json)" + attr(,"class") + [1] "from_markdown" + + $preheader + NULL + + +--- + + Code + str(tbl$`_data`) + Output + tibble [3 x 6] (S3: tbl_df/tbl/data.frame) + $ instancePath: chr [1:3] "**rounds** \n └**1** \n └─**model_tasks** \n └──**1** \n └───**task_ids** \n └────**target** \n └─────**required**" "**rounds** \n └**1** \n └─**model_tasks** \n └──**1** \n └───**output_type** \n └────**mean** \n └─────**type_id**" "**rounds** \n └**1** \n └─**submissions_due**" + $ schemaPath : chr [1:3] "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────properties "| __truncated__ "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────properties "| __truncated__ "properties \n └**rounds** \n └─items \n └──properties \n └───**submissions_due** \n └────**oneOf**" + $ keyword : chr [1:3] "type" "oneOf" "oneOf" + $ message : chr [1:3] "❌ must be array,null" "❌ must match exactly one schema in oneOf" "❌ must match exactly one schema in oneOf" + $ schema : chr [1:3] "array, null" "**1** \n **required-description:** When mean is required, property set to single element 'NA' array \n **requir"| __truncated__ "**1** \n **relative_to-description:** Name of task id variable in relation to which submission start and end da"| __truncated__ + $ data : chr [1:3] "wk inc flu hosp" "required: NA, optional: NA" "start: -6, end: 1" + +--- + + Code + tbl$`_styles` + Output + # A tibble: 18 x 7 + locname grpname colname locnum rownum colnum styles + + 1 data instancePath 5 1 NA + 2 data schemaPath 5 1 NA + 3 data schema 5 1 NA + 4 data instancePath 5 2 NA + 5 data schemaPath 5 2 NA + 6 data schema 5 2 NA + 7 data instancePath 5 3 NA + 8 data schemaPath 5 3 NA + 9 data schema 5 3 NA + 10 data schema 5 1 NA + 11 data schema 5 2 NA + 12 data schema 5 3 NA + 13 data message 5 1 NA + 14 data data 5 1 NA + 15 data message 5 2 NA + 16 data data 5 2 NA + 17 data message 5 3 NA + 18 data data 5 3 NA + +# length 1 paths and related type & enum errors handled correctly + + Code + str(tbl$`_data`) + Output + tibble [2 x 6] (S3: tbl_df/tbl/data.frame) + $ instancePath: chr [1:2] "**file_format** \n └**1**" "**timezone**" + $ schemaPath : chr [1:2] "properties \n └**file_format** \n └─items \n └──**enum**" "properties \n └**timezone** \n └─**type**" + $ keyword : chr [1:2] "enum" "type" + $ message : chr [1:2] "❌ must be equal to one of the allowed values" "❌ must be string" + $ schema : chr [1:2] "csv, parquet, arrow" "string" + $ data : chr [1:2] "csvs" "US/Eastern" + +# Data column handled correctly when required property missing + + Code + str(tbl$`_data`) + Output + tibble [2 x 6] (S3: tbl_df/tbl/data.frame) + $ instancePath: chr [1:2] "**rounds** \n └**1** \n └─**model_tasks** \n └──**1**" "**rounds** \n └**1** \n └─**model_tasks** \n └──**1** \n └───**output_type** \n └────**mean** \n └─────**value*"| __truncated__ + $ schemaPath : chr [1:2] "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────**required**" "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────properties "| __truncated__ + $ keyword : chr [1:2] "required" "type" + $ message : chr [1:2] "❌ must have required property 'target_metadata'" "❌ must be number,integer" + $ schema : chr [1:2] "task_ids, output_type, target_metadata" "number, integer" + $ data : chr [1:2] "" "0" + +--- + + Code + str(tbl$`_data`) + Output + tibble [1 x 6] (S3: tbl_df/tbl/data.frame) + $ instancePath: chr "**rounds** \n └**1** \n └─**model_tasks** \n └──**1**" + $ schemaPath : chr "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────**required**" + $ keyword : chr "required" + $ message : chr "❌ must have required property 'target_metadata'" + $ schema : chr "task_ids, output_type, target_metadata" + $ data : chr "" + +--- + + Code + str(tbl$`_data`) + Output + tibble [2 x 6] (S3: tbl_df/tbl/data.frame) + $ instancePath: chr [1:2] "**rounds** \n └**1**" "**rounds** \n └**1** \n └─**model_tasks** \n └──**1**" + $ schemaPath : chr [1:2] "properties \n └**rounds** \n └─items \n └──**required**" "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────**required**" + $ keyword : chr [1:2] "required" "required" + $ message : chr [1:2] "❌ must have required property 'round_id_from_variable'" "❌ must have required property 'target_metadata'" + $ schema : chr [1:2] "round_id_from_variable, round_id, model_tasks, submissions_due" "task_ids, output_type, target_metadata" + $ data : chr [1:2] "" "" + +--- + + Code + str(tbl$`_data`) + Output + tibble [2 x 6] (S3: tbl_df/tbl/data.frame) + $ instancePath: chr [1:2] "**rounds** \n └**1** \n └─**model_tasks** \n └──**1**" "**rounds** \n └**1** \n └─**model_tasks** \n └──**1**" + $ schemaPath : chr [1:2] "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────**required**" "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────**required**" + $ keyword : chr [1:2] "required" "required" + $ message : chr [1:2] "❌ must have required property 'output_type'" "❌ must have required property 'target_metadata'" + $ schema : chr [1:2] "task_ids, output_type, target_metadata" "task_ids, output_type, target_metadata" + $ data : chr [1:2] "" "" + +# Report handles additional property errors successfully + + Code + str(tbl$`_data`) + Output + tibble [1 x 6] (S3: tbl_df/tbl/data.frame) + $ instancePath: chr "**rounds** \n └**1** \n └─**model_tasks** \n └──**1** \n └───**output_type**" + $ schemaPath : chr "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────properties "| __truncated__ + $ keyword : chr "additionalProperties" + $ message : chr "❌ must NOT have additional properties" + $ schema : chr "FALSE" + $ data : chr "" + +# Report works corectly on validate_hub_config output + + Code + str(tbl$`_data`) + Output + tibble [5 x 7] (S3: tbl_df/tbl/data.frame) + $ fileName : chr [1:5] "tasks.json" "tasks.json" "tasks.json" "tasks.json" ... + $ instancePath: chr [1:5] "**rounds** \n └**1** \n └─**model_tasks** \n └──**1**" "**rounds** \n └**1** \n └─**model_tasks** \n └──**1** \n └───**task_ids** \n └────**target** \n └─────**required**" "**rounds** \n └**1** \n └─**model_tasks** \n └──**1** \n └───**output_type** \n └────**mean** \n └─────**output_type_id**" "**rounds** \n └**1** \n └─**submissions_due**" ... + $ schemaPath : chr [1:5] "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────**required**" "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────properties "| __truncated__ "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────properties "| __truncated__ "properties \n └**rounds** \n └─items \n └──properties \n └───**submissions_due** \n └────**oneOf**" ... + $ keyword : chr [1:5] "required" "type" "oneOf" "oneOf" ... + $ message : chr [1:5] "❌ must have required property 'target_metadata'" "❌ must be array,null" "❌ must match exactly one schema in oneOf" "❌ must match exactly one schema in oneOf" ... + $ schema : chr [1:5] "task_ids, output_type, target_metadata" "array, null" "**1** \n **required-description:** When mean is required, property set to single element 'NA' array \n **requir"| __truncated__ "**1** \n **relative_to-description:** Name of task id variable in relation to which submission start and end da"| __truncated__ ... + $ data : chr [1:5] "" "wk inc flu hosp" "required: NA, optional: NA" "start: -6, end: 1" ... + diff --git a/tests/testdata-raw/test_data_dev.R b/tests/testdata-raw/test_data_dev.R new file mode 100644 index 0000000..883360f --- /dev/null +++ b/tests/testdata-raw/test_data_dev.R @@ -0,0 +1 @@ +testthis::use_testdata(test_data_dev) diff --git a/tests/testthat/_snaps/create_config.md b/tests/testthat/_snaps/create_config.md new file mode 100644 index 0000000..5d147af --- /dev/null +++ b/tests/testthat/_snaps/create_config.md @@ -0,0 +1,117 @@ +# create_config functions work correctly + + Code + create_config(rounds) + Output + $schema_version + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + + $rounds + $rounds[[1]] + $rounds[[1]]$round_id_from_variable + [1] TRUE + + $rounds[[1]]$round_id + [1] "origin_date" + + $rounds[[1]]$model_tasks + $rounds[[1]]$model_tasks[[1]] + $rounds[[1]]$model_tasks[[1]]$task_ids + $rounds[[1]]$model_tasks[[1]]$task_ids$origin_date + $rounds[[1]]$model_tasks[[1]]$task_ids$origin_date$required + NULL + + $rounds[[1]]$model_tasks[[1]]$task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $rounds[[1]]$model_tasks[[1]]$task_ids$location + $rounds[[1]]$model_tasks[[1]]$task_ids$location$required + [1] "US" + + $rounds[[1]]$model_tasks[[1]]$task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $rounds[[1]]$model_tasks[[1]]$task_ids$horizon + $rounds[[1]]$model_tasks[[1]]$task_ids$horizon$required + [1] 1 + + $rounds[[1]]$model_tasks[[1]]$task_ids$horizon$optional + [1] 2 3 4 + + + + $rounds[[1]]$model_tasks[[1]]$output_type + $rounds[[1]]$model_tasks[[1]]$output_type$mean + $rounds[[1]]$model_tasks[[1]]$output_type$mean$output_type_id + $rounds[[1]]$model_tasks[[1]]$output_type$mean$output_type_id$required + [1] NA + + $rounds[[1]]$model_tasks[[1]]$output_type$mean$output_type_id$optional + NULL + + + $rounds[[1]]$model_tasks[[1]]$output_type$mean$value + $rounds[[1]]$model_tasks[[1]]$output_type$mean$value$type + [1] "double" + + $rounds[[1]]$model_tasks[[1]]$output_type$mean$value$minimum + [1] 0 + + + + + $rounds[[1]]$model_tasks[[1]]$target_metadata + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]] + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_id + [1] "inc hosp" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_keys + NULL + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_type + [1] "discrete" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$is_step_ahead + [1] TRUE + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$time_unit + [1] "week" + + + + + + $rounds[[1]]$submissions_due + $rounds[[1]]$submissions_due$relative_to + [1] "origin_date" + + $rounds[[1]]$submissions_due$start + [1] -4 + + $rounds[[1]]$submissions_due$end + [1] 2 + + + + + attr(,"class") + [1] "config" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +# create_config functions error correctly + + Code + create_config(list(a = 10)) + Condition + Error in `create_config()`: + x `rounds` must inherit from class but does not + diff --git a/tests/testthat/_snaps/create_model_task.md b/tests/testthat/_snaps/create_model_task.md new file mode 100644 index 0000000..cc7e8f5 --- /dev/null +++ b/tests/testthat/_snaps/create_model_task.md @@ -0,0 +1,313 @@ +# create_model_task functions work correctly + + Code + create_model_task(task_ids = create_task_ids(create_task_id("origin_date", + required = NULL, optional = c("2023-01-02", "2023-01-09", "2023-01-16")), + create_task_id("location", required = "US", optional = c("01", "02", "04", "05", + "06")), create_task_id("horizon", required = 1L, optional = 2:4)), + output_type = create_output_type(create_output_type_mean(is_required = TRUE, + value_type = "double", value_minimum = 0L)), target_metadata = create_target_metadata( + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = NULL, + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week"))) + Output + $task_ids + $task_ids$origin_date + $task_ids$origin_date$required + NULL + + $task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $task_ids$location + $task_ids$location$required + [1] "US" + + $task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $task_ids$horizon + $task_ids$horizon$required + [1] 1 + + $task_ids$horizon$optional + [1] 2 3 4 + + + + $output_type + $output_type$mean + $output_type$mean$output_type_id + $output_type$mean$output_type_id$required + [1] NA + + $output_type$mean$output_type_id$optional + NULL + + + $output_type$mean$value + $output_type$mean$value$type + [1] "double" + + $output_type$mean$value$minimum + [1] 0 + + + + + $target_metadata + $target_metadata[[1]] + $target_metadata[[1]]$target_id + [1] "inc hosp" + + $target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $target_metadata[[1]]$target_keys + NULL + + $target_metadata[[1]]$target_type + [1] "discrete" + + $target_metadata[[1]]$is_step_ahead + [1] TRUE + + $target_metadata[[1]]$time_unit + [1] "week" + + + + attr(,"class") + [1] "model_task" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +--- + + Code + create_model_task(task_ids = create_task_ids(create_task_id("origin_date", + required = NULL, optional = c("2023-01-02", "2023-01-09", "2023-01-16")), + create_task_id("location", required = "US", optional = c("01", "02", "04", "05", + "06")), create_task_id("target", required = NULL, optional = c("inc death", + "inc hosp")), create_task_id("horizon", required = 1L, optional = 2:4)), + output_type = create_output_type(create_output_type_mean(is_required = TRUE, + value_type = "double", value_minimum = 0L), create_output_type_median( + is_required = FALSE, value_type = "double"), create_output_type_quantile( + required = c(0.25, 0.5, 0.75), optional = c(0.1, 0.2, 0.3, 0.4, 0.6, 0.7, 0.8, + 0.9), value_type = "double", value_minimum = 0)), target_metadata = create_target_metadata( + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = list(target = "inc hosp"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week"), + create_target_metadata_item(target_id = "inc death", target_name = "Weekly incident influenza deaths", + target_units = "rate per 100,000 population", target_keys = list(target = "inc death"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week"))) + Output + $task_ids + $task_ids$origin_date + $task_ids$origin_date$required + NULL + + $task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $task_ids$location + $task_ids$location$required + [1] "US" + + $task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $task_ids$target + $task_ids$target$required + NULL + + $task_ids$target$optional + [1] "inc death" "inc hosp" + + + $task_ids$horizon + $task_ids$horizon$required + [1] 1 + + $task_ids$horizon$optional + [1] 2 3 4 + + + + $output_type + $output_type$mean + $output_type$mean$output_type_id + $output_type$mean$output_type_id$required + [1] NA + + $output_type$mean$output_type_id$optional + NULL + + + $output_type$mean$value + $output_type$mean$value$type + [1] "double" + + $output_type$mean$value$minimum + [1] 0 + + + + $output_type$median + $output_type$median$output_type_id + $output_type$median$output_type_id$required + NULL + + $output_type$median$output_type_id$optional + [1] NA + + + $output_type$median$value + $output_type$median$value$type + [1] "double" + + + + $output_type$quantile + $output_type$quantile$output_type_id + $output_type$quantile$output_type_id$required + [1] 0.25 0.50 0.75 + + $output_type$quantile$output_type_id$optional + [1] 0.1 0.2 0.3 0.4 0.6 0.7 0.8 0.9 + + + $output_type$quantile$value + $output_type$quantile$value$type + [1] "double" + + $output_type$quantile$value$minimum + [1] 0 + + + + + $target_metadata + $target_metadata[[1]] + $target_metadata[[1]]$target_id + [1] "inc hosp" + + $target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $target_metadata[[1]]$target_keys + $target_metadata[[1]]$target_keys$target + [1] "inc hosp" + + + $target_metadata[[1]]$target_type + [1] "discrete" + + $target_metadata[[1]]$is_step_ahead + [1] TRUE + + $target_metadata[[1]]$time_unit + [1] "week" + + + $target_metadata[[2]] + $target_metadata[[2]]$target_id + [1] "inc death" + + $target_metadata[[2]]$target_name + [1] "Weekly incident influenza deaths" + + $target_metadata[[2]]$target_units + [1] "rate per 100,000 population" + + $target_metadata[[2]]$target_keys + $target_metadata[[2]]$target_keys$target + [1] "inc death" + + + $target_metadata[[2]]$target_type + [1] "discrete" + + $target_metadata[[2]]$is_step_ahead + [1] TRUE + + $target_metadata[[2]]$time_unit + [1] "week" + + + + attr(,"class") + [1] "model_task" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +# create_output_type_point functions error correctly + + Code + create_model_task(task_ids = task_ids, output_type = output_type, + target_metadata = create_target_metadata(create_target_metadata_item( + target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = list(target = "inc hosp"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week"))) + Condition + Error in `map()`: + i In index: 1. + Caused by error in `create_model_task()`: + x `task_ids` target values must match `target_metadata` `target_keys` definitions. + > `target_keys` target values: "inc hosp" + > `task_ids` target values: "inc death" and "inc hosp" + +--- + + Code + create_model_task(task_ids = task_ids, output_type = output_type, + target_metadata = create_target_metadata(create_target_metadata_item( + target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = list(targets = "inc hosp"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week"))) + Condition + Error in `create_model_task()`: + ! `target_metadata` target_keys names must match valid `task_ids` property names: "origin_date", "target", and "horizon" + x target_keys name "targets" does not. + +--- + + Code + create_model_task(task_ids = task_ids, output_type = list(a = 10), + target_metadata = create_target_metadata(create_target_metadata_item(target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", target_units = "rate per 100,000 population", + target_keys = list(targets = "inc hosp"), target_type = "discrete", + is_step_ahead = TRUE, time_unit = "week"))) + Condition + Error in `map()`: + i In index: 2. + Caused by error in `create_model_task()`: + x `output_type` must inherit from class but does not + +--- + + Code + create_model_task(task_ids = task_ids, output_type = output_type, + target_metadata = create_target_metadata(create_target_metadata_item( + target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = NULL, + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week"))) + Condition + Error in `create_model_task()`: + ! All arguments supplied must be created against the same Hub schema. + x `schema_id` attributes are not consistent across all arguments. + Argument `schema_id` attributes: + * task_ids : invalid_schema_id + * output_type : https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json + * target_metadata : https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json + diff --git a/tests/testthat/_snaps/create_model_tasks.md b/tests/testthat/_snaps/create_model_tasks.md new file mode 100644 index 0000000..d3452cf --- /dev/null +++ b/tests/testthat/_snaps/create_model_tasks.md @@ -0,0 +1,397 @@ +# create_model_tasks functions work correctly + + Code + create_model_tasks(create_model_task(task_ids = create_task_ids(create_task_id( + "origin_date", required = NULL, optional = c("2023-01-02", "2023-01-09", + "2023-01-16")), create_task_id("location", required = "US", optional = c( + "01", "02", "04", "05", "06")), create_task_id("horizon", required = 1L, + optional = 2:4)), output_type = create_output_type(create_output_type_mean( + is_required = TRUE, value_type = "double", value_minimum = 0L)), + target_metadata = create_target_metadata(create_target_metadata_item(target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", target_units = "rate per 100,000 population", + target_keys = NULL, target_type = "discrete", is_step_ahead = TRUE, + time_unit = "week")))) + Output + $model_tasks + $model_tasks[[1]] + $model_tasks[[1]]$task_ids + $model_tasks[[1]]$task_ids$origin_date + $model_tasks[[1]]$task_ids$origin_date$required + NULL + + $model_tasks[[1]]$task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $model_tasks[[1]]$task_ids$location + $model_tasks[[1]]$task_ids$location$required + [1] "US" + + $model_tasks[[1]]$task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $model_tasks[[1]]$task_ids$horizon + $model_tasks[[1]]$task_ids$horizon$required + [1] 1 + + $model_tasks[[1]]$task_ids$horizon$optional + [1] 2 3 4 + + + + $model_tasks[[1]]$output_type + $model_tasks[[1]]$output_type$mean + $model_tasks[[1]]$output_type$mean$output_type_id + $model_tasks[[1]]$output_type$mean$output_type_id$required + [1] NA + + $model_tasks[[1]]$output_type$mean$output_type_id$optional + NULL + + + $model_tasks[[1]]$output_type$mean$value + $model_tasks[[1]]$output_type$mean$value$type + [1] "double" + + $model_tasks[[1]]$output_type$mean$value$minimum + [1] 0 + + + + + $model_tasks[[1]]$target_metadata + $model_tasks[[1]]$target_metadata[[1]] + $model_tasks[[1]]$target_metadata[[1]]$target_id + [1] "inc hosp" + + $model_tasks[[1]]$target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $model_tasks[[1]]$target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $model_tasks[[1]]$target_metadata[[1]]$target_keys + NULL + + $model_tasks[[1]]$target_metadata[[1]]$target_type + [1] "discrete" + + $model_tasks[[1]]$target_metadata[[1]]$is_step_ahead + [1] TRUE + + $model_tasks[[1]]$target_metadata[[1]]$time_unit + [1] "week" + + + + + + attr(,"class") + [1] "model_tasks" "list" + attr(,"n") + [1] 1 + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +--- + + Code + create_model_tasks(create_model_task(task_ids = create_task_ids(create_task_id( + "origin_date", required = NULL, optional = c("2023-01-02", "2023-01-09", + "2023-01-16")), create_task_id("location", required = "US", optional = c( + "01", "02", "04", "05", "06")), create_task_id("target", required = NULL, + optional = c("inc death", "inc hosp")), create_task_id("horizon", required = 1L, + optional = 2:4)), output_type = create_output_type(create_output_type_mean( + is_required = TRUE, value_type = "double", value_minimum = 0L), + create_output_type_median(is_required = FALSE, value_type = "double"), + create_output_type_quantile(required = c(0.25, 0.5, 0.75), optional = c(0.1, + 0.2, 0.3, 0.4, 0.6, 0.7, 0.8, 0.9), value_type = "double", value_minimum = 0)), + target_metadata = create_target_metadata(create_target_metadata_item(target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), target_type = "discrete", + is_step_ahead = TRUE, time_unit = "week"), create_target_metadata_item( + target_id = "inc death", target_name = "Weekly incident influenza deaths", + target_units = "rate per 100,000 population", target_keys = list(target = "inc death"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week"))), + create_model_task(task_ids = create_task_ids(create_task_id("origin_date", + required = NULL, optional = c("2023-01-02", "2023-01-09", "2023-01-16")), + create_task_id("location", required = "US", optional = c("01", "02", "04", "05", + "06")), create_task_id("target", required = "flu hosp rt chng", optional = NULL), + create_task_id("horizon", required = 1L, optional = 2:4)), output_type = create_output_type( + create_output_type_pmf(required = c("large_decrease", "decrease", "stable", + "increase", "large_increase"), optional = NULL, value_type = "double")), + target_metadata = create_target_metadata(create_target_metadata_item(target_id = "flu hosp rt chng", + target_name = "Weekly influenza hospitalization rate change", target_units = "rate per 100,000 population", + target_keys = list(target = "flu hosp rt chng"), target_type = "nominal", + is_step_ahead = TRUE, time_unit = "week")))) + Output + $model_tasks + $model_tasks[[1]] + $model_tasks[[1]]$task_ids + $model_tasks[[1]]$task_ids$origin_date + $model_tasks[[1]]$task_ids$origin_date$required + NULL + + $model_tasks[[1]]$task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $model_tasks[[1]]$task_ids$location + $model_tasks[[1]]$task_ids$location$required + [1] "US" + + $model_tasks[[1]]$task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $model_tasks[[1]]$task_ids$target + $model_tasks[[1]]$task_ids$target$required + NULL + + $model_tasks[[1]]$task_ids$target$optional + [1] "inc death" "inc hosp" + + + $model_tasks[[1]]$task_ids$horizon + $model_tasks[[1]]$task_ids$horizon$required + [1] 1 + + $model_tasks[[1]]$task_ids$horizon$optional + [1] 2 3 4 + + + + $model_tasks[[1]]$output_type + $model_tasks[[1]]$output_type$mean + $model_tasks[[1]]$output_type$mean$output_type_id + $model_tasks[[1]]$output_type$mean$output_type_id$required + [1] NA + + $model_tasks[[1]]$output_type$mean$output_type_id$optional + NULL + + + $model_tasks[[1]]$output_type$mean$value + $model_tasks[[1]]$output_type$mean$value$type + [1] "double" + + $model_tasks[[1]]$output_type$mean$value$minimum + [1] 0 + + + + $model_tasks[[1]]$output_type$median + $model_tasks[[1]]$output_type$median$output_type_id + $model_tasks[[1]]$output_type$median$output_type_id$required + NULL + + $model_tasks[[1]]$output_type$median$output_type_id$optional + [1] NA + + + $model_tasks[[1]]$output_type$median$value + $model_tasks[[1]]$output_type$median$value$type + [1] "double" + + + + $model_tasks[[1]]$output_type$quantile + $model_tasks[[1]]$output_type$quantile$output_type_id + $model_tasks[[1]]$output_type$quantile$output_type_id$required + [1] 0.25 0.50 0.75 + + $model_tasks[[1]]$output_type$quantile$output_type_id$optional + [1] 0.1 0.2 0.3 0.4 0.6 0.7 0.8 0.9 + + + $model_tasks[[1]]$output_type$quantile$value + $model_tasks[[1]]$output_type$quantile$value$type + [1] "double" + + $model_tasks[[1]]$output_type$quantile$value$minimum + [1] 0 + + + + + $model_tasks[[1]]$target_metadata + $model_tasks[[1]]$target_metadata[[1]] + $model_tasks[[1]]$target_metadata[[1]]$target_id + [1] "inc hosp" + + $model_tasks[[1]]$target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $model_tasks[[1]]$target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $model_tasks[[1]]$target_metadata[[1]]$target_keys + $model_tasks[[1]]$target_metadata[[1]]$target_keys$target + [1] "inc hosp" + + + $model_tasks[[1]]$target_metadata[[1]]$target_type + [1] "discrete" + + $model_tasks[[1]]$target_metadata[[1]]$is_step_ahead + [1] TRUE + + $model_tasks[[1]]$target_metadata[[1]]$time_unit + [1] "week" + + + $model_tasks[[1]]$target_metadata[[2]] + $model_tasks[[1]]$target_metadata[[2]]$target_id + [1] "inc death" + + $model_tasks[[1]]$target_metadata[[2]]$target_name + [1] "Weekly incident influenza deaths" + + $model_tasks[[1]]$target_metadata[[2]]$target_units + [1] "rate per 100,000 population" + + $model_tasks[[1]]$target_metadata[[2]]$target_keys + $model_tasks[[1]]$target_metadata[[2]]$target_keys$target + [1] "inc death" + + + $model_tasks[[1]]$target_metadata[[2]]$target_type + [1] "discrete" + + $model_tasks[[1]]$target_metadata[[2]]$is_step_ahead + [1] TRUE + + $model_tasks[[1]]$target_metadata[[2]]$time_unit + [1] "week" + + + + + $model_tasks[[2]] + $model_tasks[[2]]$task_ids + $model_tasks[[2]]$task_ids$origin_date + $model_tasks[[2]]$task_ids$origin_date$required + NULL + + $model_tasks[[2]]$task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $model_tasks[[2]]$task_ids$location + $model_tasks[[2]]$task_ids$location$required + [1] "US" + + $model_tasks[[2]]$task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $model_tasks[[2]]$task_ids$target + $model_tasks[[2]]$task_ids$target$required + [1] "flu hosp rt chng" + + $model_tasks[[2]]$task_ids$target$optional + NULL + + + $model_tasks[[2]]$task_ids$horizon + $model_tasks[[2]]$task_ids$horizon$required + [1] 1 + + $model_tasks[[2]]$task_ids$horizon$optional + [1] 2 3 4 + + + + $model_tasks[[2]]$output_type + $model_tasks[[2]]$output_type$pmf + $model_tasks[[2]]$output_type$pmf$output_type_id + $model_tasks[[2]]$output_type$pmf$output_type_id$required + [1] "large_decrease" "decrease" "stable" "increase" + [5] "large_increase" + + $model_tasks[[2]]$output_type$pmf$output_type_id$optional + NULL + + + $model_tasks[[2]]$output_type$pmf$value + $model_tasks[[2]]$output_type$pmf$value$type + [1] "double" + + $model_tasks[[2]]$output_type$pmf$value$minimum + [1] 0 + + $model_tasks[[2]]$output_type$pmf$value$maximum + [1] 1 + + + + + $model_tasks[[2]]$target_metadata + $model_tasks[[2]]$target_metadata[[1]] + $model_tasks[[2]]$target_metadata[[1]]$target_id + [1] "flu hosp rt chng" + + $model_tasks[[2]]$target_metadata[[1]]$target_name + [1] "Weekly influenza hospitalization rate change" + + $model_tasks[[2]]$target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $model_tasks[[2]]$target_metadata[[1]]$target_keys + $model_tasks[[2]]$target_metadata[[1]]$target_keys$target + [1] "flu hosp rt chng" + + + $model_tasks[[2]]$target_metadata[[1]]$target_type + [1] "nominal" + + $model_tasks[[2]]$target_metadata[[1]]$is_step_ahead + [1] TRUE + + $model_tasks[[2]]$target_metadata[[1]]$time_unit + [1] "week" + + + + + + attr(,"class") + [1] "model_tasks" "list" + attr(,"n") + [1] 2 + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +# create_model_tasks functions error correctly + + Code + create_model_tasks(model_task_1, list(a = 10)) + Condition + Error in `create_model_tasks()`: + ! All items supplied must inherit from class + x Item 2 does not. + +--- + + Code + create_model_tasks(model_task_1, create_model_task(task_ids = create_task_ids( + create_task_id("origin_date", required = NULL, optional = c("2023-01-02", + "2023-01-09", "2023-01-16")), create_task_id("location", required = "US", + optional = c("01", "02", "04", "05", "06")), create_task_id("target", + required = "flu hosp rt chng", optional = NULL), create_task_id("horizon", + required = 1L, optional = 2:4)), output_type = create_output_type( + create_output_type_pmf(required = c("large_decrease", "decrease", "stable", + "increase", "large_increase"), optional = NULL, value_type = "double")), + target_metadata = create_target_metadata(create_target_metadata_item(target_id = "flu hosp rt chng", + target_name = "Weekly influenza hospitalization rate change", target_units = "rate per 100,000 population", + target_keys = list(target = "flu hosp rt chng"), target_type = "nominal", + is_step_ahead = TRUE, time_unit = "week")))) + Condition + Error in `create_model_tasks()`: + ! All items supplied must be created against the same Hub schema. + x `schema_id` attributes are not consistent across all items. + Item `schema_id` attributes: + * Item 1 : invalid_schema_id + * Item 2 : https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json + diff --git a/tests/testthat/_snaps/create_output_type.md b/tests/testthat/_snaps/create_output_type.md new file mode 100644 index 0000000..2c4e25f --- /dev/null +++ b/tests/testthat/_snaps/create_output_type.md @@ -0,0 +1,90 @@ +# create_output_type functions work correctly + + Code + create_output_type(create_output_type_mean(is_required = TRUE, value_type = "double", + value_minimum = 0L), create_output_type_median(is_required = FALSE, + value_type = "double"), create_output_type_quantile(required = c(0.25, 0.5, + 0.75), optional = c(0.1, 0.2, 0.3, 0.4, 0.6, 0.7, 0.8, 0.9), value_type = "double", + value_minimum = 0)) + Output + $output_type + $output_type$mean + $output_type$mean$output_type_id + $output_type$mean$output_type_id$required + [1] NA + + $output_type$mean$output_type_id$optional + NULL + + + $output_type$mean$value + $output_type$mean$value$type + [1] "double" + + $output_type$mean$value$minimum + [1] 0 + + + + $output_type$median + $output_type$median$output_type_id + $output_type$median$output_type_id$required + NULL + + $output_type$median$output_type_id$optional + [1] NA + + + $output_type$median$value + $output_type$median$value$type + [1] "double" + + + + $output_type$quantile + $output_type$quantile$output_type_id + $output_type$quantile$output_type_id$required + [1] 0.25 0.50 0.75 + + $output_type$quantile$output_type_id$optional + [1] 0.1 0.2 0.3 0.4 0.6 0.7 0.8 0.9 + + + $output_type$quantile$value + $output_type$quantile$value$type + [1] "double" + + $output_type$quantile$value$minimum + [1] 0 + + + + + attr(,"class") + [1] "output_type" "list" + attr(,"n") + [1] 3 + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +# create_output_type functions error correctly + + Code + create_output_type(create_output_type_mean(is_required = TRUE, value_type = "double", + value_minimum = 0L), create_output_type_mean(is_required = TRUE, value_type = "double", + value_minimum = 0L)) + Condition + Error in `create_output_type()`: + ! `names` must be unique across all items. + x Item 2 with `name` "mean" is duplicate. + +--- + + Code + create_output_type(create_output_type_mean(is_required = TRUE, value_type = "double", + value_minimum = 0L), list(a = "b")) + Condition + Error in `create_output_type()`: + ! All items supplied must inherit from class + x Item 2 does not. + diff --git a/tests/testthat/_snaps/create_output_type_item.md b/tests/testthat/_snaps/create_output_type_item.md new file mode 100644 index 0000000..0c3a7d7 --- /dev/null +++ b/tests/testthat/_snaps/create_output_type_item.md @@ -0,0 +1,419 @@ +# create_output_type_point functions work correctly + + Code + create_output_type_mean(is_required = TRUE, value_type = "double", + value_minimum = 0L) + Output + $mean + $mean$output_type_id + $mean$output_type_id$required + [1] NA + + $mean$output_type_id$optional + NULL + + + $mean$value + $mean$value$type + [1] "double" + + $mean$value$minimum + [1] 0 + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +--- + + Code + create_output_type_mean(is_required = FALSE, value_type = "integer", + value_maximum = 0L) + Output + $mean + $mean$output_type_id + $mean$output_type_id$required + NULL + + $mean$output_type_id$optional + [1] NA + + + $mean$value + $mean$value$type + [1] "integer" + + $mean$value$maximum + [1] 0 + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +--- + + Code + create_output_type_median(is_required = FALSE, value_type = "double") + Output + $median + $median$output_type_id + $median$output_type_id$required + NULL + + $median$output_type_id$optional + [1] NA + + + $median$value + $median$value$type + [1] "double" + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +--- + + Code + create_output_type_median(is_required = FALSE, value_type = "double", + schema_version = "v1.0.0") + Condition + Warning: + Hub configured using schema version v1.0.0. Support for schema earlier than v2.0.0 was deprecated in hubUtils 0.0.0.9010. + i Please upgrade Hub config files to conform to, at minimum, version v2.0.0 as soon as possible. + Output + $median + $median$type_id + $median$type_id$required + NULL + + $median$type_id$optional + [1] NA + + + $median$value + $median$value$type + [1] "double" + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v1.0.0/tasks-schema.json" + +# create_output_type_point functions error correctly + + Code + create_output_type_mean(is_required = "TRUE", value_type = "double") + Condition + Error in `create_output_type_point()`: + x Argument `is_required` must be and have length 1. + +--- + + Code + create_output_type_mean(is_required = TRUE, value_type = c("double", "integer")) + Condition + Error in `map()`: + i In index: 1. + Caused by error in `create_output_type_mean()`: + x `value_type` must be length 1, not 2. + +--- + + Code + create_output_type_mean(is_required = FALSE, value_type = "character", + value_maximum = 0L) + Condition + Error in `map()`: + i In index: 1. + Caused by error in `create_output_type_mean()`: + x `value_type` value is invalid. + ! Must be one of "double" and "integer". + i Actual value is "character" + +--- + + Code + create_output_type_median(is_required = FALSE) + Condition + Error in `create_output_type_point()`: + ! `value_type` is absent but must be supplied. + +# create_output_type_dist functions work correctly + + Code + create_output_type_quantile(required = c(0.25, 0.5, 0.75), optional = c(0.1, + 0.2, 0.3, 0.4, 0.6, 0.7, 0.8, 0.9), value_type = "double", value_minimum = 0) + Output + $quantile + $quantile$output_type_id + $quantile$output_type_id$required + [1] 0.25 0.50 0.75 + + $quantile$output_type_id$optional + [1] 0.1 0.2 0.3 0.4 0.6 0.7 0.8 0.9 + + + $quantile$value + $quantile$value$type + [1] "double" + + $quantile$value$minimum + [1] 0 + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +--- + + Code + create_output_type_cdf(required = c(10, 20), optional = NULL, value_type = "double") + Output + $cdf + $cdf$output_type_id + $cdf$output_type_id$required + [1] 10 20 + + $cdf$output_type_id$optional + NULL + + + $cdf$value + $cdf$value$type + [1] "double" + + $cdf$value$minimum + [1] 0 + + $cdf$value$maximum + [1] 1 + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +--- + + Code + create_output_type_cdf(required = NULL, optional = c("EW202240", "EW202241", + "EW202242"), value_type = "double") + Output + $cdf + $cdf$output_type_id + $cdf$output_type_id$required + NULL + + $cdf$output_type_id$optional + [1] "EW202240" "EW202241" "EW202242" + + + $cdf$value + $cdf$value$type + [1] "double" + + $cdf$value$minimum + [1] 0 + + $cdf$value$maximum + [1] 1 + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +--- + + Code + create_output_type_pmf(required = NULL, optional = c("low", "moderate", "high", + "extreme"), value_type = "double") + Output + $pmf + $pmf$output_type_id + $pmf$output_type_id$required + NULL + + $pmf$output_type_id$optional + [1] "low" "moderate" "high" "extreme" + + + $pmf$value + $pmf$value$type + [1] "double" + + $pmf$value$minimum + [1] 0 + + $pmf$value$maximum + [1] 1 + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +--- + + Code + create_output_type_sample(required = 1:10, optional = 11:15, value_type = "double") + Output + $sample + $sample$output_type_id + $sample$output_type_id$required + [1] 1 2 3 4 5 6 7 8 9 10 + + $sample$output_type_id$optional + [1] 11 12 13 14 15 + + + $sample$value + $sample$value$type + [1] "double" + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +--- + + Code + create_output_type_quantile(required = c(0.25, 0.5, 0.75), optional = c(0.1, + 0.2, 0.3, 0.4, 0.6, 0.7, 0.8, 0.9), value_type = "double", value_minimum = 0, + schema_version = "v1.0.0") + Condition + Warning: + Hub configured using schema version v1.0.0. Support for schema earlier than v2.0.0 was deprecated in hubUtils 0.0.0.9010. + i Please upgrade Hub config files to conform to, at minimum, version v2.0.0 as soon as possible. + Output + $quantile + $quantile$type_id + $quantile$type_id$required + [1] 0.25 0.50 0.75 + + $quantile$type_id$optional + [1] 0.1 0.2 0.3 0.4 0.6 0.7 0.8 0.9 + + + $quantile$value + $quantile$value$type + [1] "double" + + $quantile$value$minimum + [1] 0 + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v1.0.0/tasks-schema.json" + +# create_output_type_dist functions error correctly + + Code + create_output_type_cdf(required = NULL, optional = c("EW202240", "EW202241", + "EW2022423"), value_type = "double") + Condition + Error in `map()`: + i In index: 2. + Caused by error in `create_output_type_cdf()`: + ! The maximum number of characters allowed for values in `optional` is 8. + x Value "EW2022423" has more characters than allowed + +--- + + Code + create_output_type_quantile(required = c(0.25, 0.5, 0.6, 0.75), optional = c( + 0.1, 0.2, 0.3, 0.4, 0.6, 0.7, 0.8, 0.9), value_type = "double", + value_minimum = 0) + Condition + Error in `check_prop_dups()`: + x Values across arguments `required` and `optional` must be unique. + ! Provided value 0.6 is duplicated. + +--- + + Code + create_output_type_sample(required = 0:10, optional = 11:15, value_type = "double") + Condition + Error in `map()`: + i In index: 1. + Caused by error in `create_output_type_sample()`: + ! All values in `required` must be equal to or greater than 1. + x Value 0 is less. + +--- + + Code + create_output_type_sample(required = 1:10, optional = 11:15, value_type = "character") + Condition + Error in `map()`: + i In index: 1. + Caused by error in `create_output_type_sample()`: + x `value_type` value is invalid. + ! Must be one of "double" and "integer". + i Actual value is "character" + +# create_output_type_dist functions creates expected warnings + + Code + create_output_type_sample(required = 1:50, optional = NULL, value_type = "double", + value_minimum = 0L, value_maximum = 1L) + Condition + Warning in `create_output_type_sample()`: + ! Cannot determine appropriate type for argument `value_minimum`, type validation skipped. Schema may be invalid. Consult relevant schema and consider opening an issue at + Warning in `create_output_type_sample()`: + ! Cannot determine appropriate type for argument `value_maximum`, type validation skipped. Schema may be invalid. Consult relevant schema and consider opening an issue at + Output + $sample + $sample$output_type_id + $sample$output_type_id$required + [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 + [26] 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 + + $sample$output_type_id$optional + NULL + + + $sample$value + $sample$value$type + [1] "double" + + $sample$value$minimum + [1] 0 + + $sample$value$maximum + [1] 1 + + + + attr(,"class") + [1] "output_type_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + diff --git a/tests/testthat/_snaps/create_round.md b/tests/testthat/_snaps/create_round.md new file mode 100644 index 0000000..de3a021 --- /dev/null +++ b/tests/testthat/_snaps/create_round.md @@ -0,0 +1,270 @@ +# create_round functions work correctly + + Code + create_round(round_id_from_variable = FALSE, round_id = "round_1", model_tasks = model_tasks, + submissions_due = list(start = "2023-01-12", end = "2023-01-18"), + last_data_date = "2023-01-02") + Output + $round_id_from_variable + [1] FALSE + + $round_id + [1] "round_1" + + $model_tasks + $model_tasks[[1]] + $model_tasks[[1]]$task_ids + $model_tasks[[1]]$task_ids$origin_date + $model_tasks[[1]]$task_ids$origin_date$required + NULL + + $model_tasks[[1]]$task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $model_tasks[[1]]$task_ids$location + $model_tasks[[1]]$task_ids$location$required + [1] "US" + + $model_tasks[[1]]$task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $model_tasks[[1]]$task_ids$horizon + $model_tasks[[1]]$task_ids$horizon$required + [1] 1 + + $model_tasks[[1]]$task_ids$horizon$optional + [1] 2 3 4 + + + + $model_tasks[[1]]$output_type + $model_tasks[[1]]$output_type$mean + $model_tasks[[1]]$output_type$mean$output_type_id + $model_tasks[[1]]$output_type$mean$output_type_id$required + [1] NA + + $model_tasks[[1]]$output_type$mean$output_type_id$optional + NULL + + + $model_tasks[[1]]$output_type$mean$value + $model_tasks[[1]]$output_type$mean$value$type + [1] "double" + + $model_tasks[[1]]$output_type$mean$value$minimum + [1] 0 + + + + + $model_tasks[[1]]$target_metadata + $model_tasks[[1]]$target_metadata[[1]] + $model_tasks[[1]]$target_metadata[[1]]$target_id + [1] "inc hosp" + + $model_tasks[[1]]$target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $model_tasks[[1]]$target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $model_tasks[[1]]$target_metadata[[1]]$target_keys + NULL + + $model_tasks[[1]]$target_metadata[[1]]$target_type + [1] "discrete" + + $model_tasks[[1]]$target_metadata[[1]]$is_step_ahead + [1] TRUE + + $model_tasks[[1]]$target_metadata[[1]]$time_unit + [1] "week" + + + + + + $submissions_due + $submissions_due$start + [1] "2023-01-12" + + $submissions_due$end + [1] "2023-01-18" + + + $last_data_date + [1] "2023-01-02" + + attr(,"class") + [1] "round" "list" + attr(,"round_id") + [1] "round_1" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +--- + + Code + create_round(round_id_from_variable = TRUE, round_id = "origin_date", + model_tasks = model_tasks, submissions_due = list(relative_to = "origin_date", + start = -4L, end = 2L), last_data_date = "2023-01-02") + Output + $round_id_from_variable + [1] TRUE + + $round_id + [1] "origin_date" + + $model_tasks + $model_tasks[[1]] + $model_tasks[[1]]$task_ids + $model_tasks[[1]]$task_ids$origin_date + $model_tasks[[1]]$task_ids$origin_date$required + NULL + + $model_tasks[[1]]$task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $model_tasks[[1]]$task_ids$location + $model_tasks[[1]]$task_ids$location$required + [1] "US" + + $model_tasks[[1]]$task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $model_tasks[[1]]$task_ids$horizon + $model_tasks[[1]]$task_ids$horizon$required + [1] 1 + + $model_tasks[[1]]$task_ids$horizon$optional + [1] 2 3 4 + + + + $model_tasks[[1]]$output_type + $model_tasks[[1]]$output_type$mean + $model_tasks[[1]]$output_type$mean$output_type_id + $model_tasks[[1]]$output_type$mean$output_type_id$required + [1] NA + + $model_tasks[[1]]$output_type$mean$output_type_id$optional + NULL + + + $model_tasks[[1]]$output_type$mean$value + $model_tasks[[1]]$output_type$mean$value$type + [1] "double" + + $model_tasks[[1]]$output_type$mean$value$minimum + [1] 0 + + + + + $model_tasks[[1]]$target_metadata + $model_tasks[[1]]$target_metadata[[1]] + $model_tasks[[1]]$target_metadata[[1]]$target_id + [1] "inc hosp" + + $model_tasks[[1]]$target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $model_tasks[[1]]$target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $model_tasks[[1]]$target_metadata[[1]]$target_keys + NULL + + $model_tasks[[1]]$target_metadata[[1]]$target_type + [1] "discrete" + + $model_tasks[[1]]$target_metadata[[1]]$is_step_ahead + [1] TRUE + + $model_tasks[[1]]$target_metadata[[1]]$time_unit + [1] "week" + + + + + + $submissions_due + $submissions_due$relative_to + [1] "origin_date" + + $submissions_due$start + [1] -4 + + $submissions_due$end + [1] 2 + + + $last_data_date + [1] "2023-01-02" + + attr(,"class") + [1] "round" "list" + attr(,"round_id") + [1] "origin_date" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +# create_round name matching works correctly + + Code + create_round(round_id_from_variable = FALSE, round_id = "round_1", model_tasks = model_tasks, + submissions_due = list(start = "01/12/2023", end = "2023-01-18"), + last_data_date = "2023-01-02") + Condition + Error in `map()`: + i In index: 1. + Caused by error in `create_round()`: + x `start` value must be character string of date in valid ISO 8601 format (YYYY-MM-DD). + +--- + + Code + create_round(round_id_from_variable = FALSE, round_id = "round_1", model_tasks = model_tasks, + submissions_due = list(start = -4L, end = 2), last_data_date = "2023-01-02") + Condition + Error in `map()`: + i In index: 1. + Caused by error in `create_round()`: + x `start` value must be character string of date in valid ISO 8601 format (YYYY-MM-DD). Date object format not accepted. Consider using `as.character()` to convert to character. + +--- + + Code + create_round(round_id_from_variable = TRUE, round_id = "origin_dates", + model_tasks = model_tasks, submissions_due = list(relative_to = "origin_date", + start = -4L, end = 2L), last_data_date = "2023-01-02") + Condition + Error in `create_round()`: + ! `round_id` value must correspond to a valid `task_id` variable in every `model_task` object. + x `round_id` value "origin_dates" does not correspond to a valid variable in provided `model_tasks` `model_task` object 1. + +--- + + Code + create_round(round_id_from_variable = TRUE, round_id = "origin_date", + model_tasks = model_tasks, submissions_due = list(relative = "origin_date", + start = -4L, end = 2L), last_data_date = "2023-01-02") + Condition + Error in `create_round()`: + x Property "relative" in `submissions_due` is invalid. + ! Valid `submissions_due` properties: "relative_to", "start", and "end" + +--- + + Code + create_round(round_id_from_variable = TRUE, round_id = "origin_date", + model_tasks = "model_tasks", submissions_due = list(relative_to = "origin_date", + start = -4L, end = 2L), last_data_date = "2023-01-02") + Condition + Error in `create_round()`: + x `model_tasks` must inherit from class but does not + diff --git a/tests/testthat/_snaps/create_rounds.md b/tests/testthat/_snaps/create_rounds.md new file mode 100644 index 0000000..59e864d --- /dev/null +++ b/tests/testthat/_snaps/create_rounds.md @@ -0,0 +1,343 @@ +# create_rounds functions work correctly + + Code + create_rounds(create_round(round_id_from_variable = TRUE, round_id = "origin_date", + model_tasks = model_tasks, submissions_due = list(relative_to = "origin_date", + start = -4L, end = 2L))) + Output + $rounds + $rounds[[1]] + $rounds[[1]]$round_id_from_variable + [1] TRUE + + $rounds[[1]]$round_id + [1] "origin_date" + + $rounds[[1]]$model_tasks + $rounds[[1]]$model_tasks[[1]] + $rounds[[1]]$model_tasks[[1]]$task_ids + $rounds[[1]]$model_tasks[[1]]$task_ids$origin_date + $rounds[[1]]$model_tasks[[1]]$task_ids$origin_date$required + NULL + + $rounds[[1]]$model_tasks[[1]]$task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $rounds[[1]]$model_tasks[[1]]$task_ids$location + $rounds[[1]]$model_tasks[[1]]$task_ids$location$required + [1] "US" + + $rounds[[1]]$model_tasks[[1]]$task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $rounds[[1]]$model_tasks[[1]]$task_ids$horizon + $rounds[[1]]$model_tasks[[1]]$task_ids$horizon$required + [1] 1 + + $rounds[[1]]$model_tasks[[1]]$task_ids$horizon$optional + [1] 2 3 4 + + + + $rounds[[1]]$model_tasks[[1]]$output_type + $rounds[[1]]$model_tasks[[1]]$output_type$mean + $rounds[[1]]$model_tasks[[1]]$output_type$mean$output_type_id + $rounds[[1]]$model_tasks[[1]]$output_type$mean$output_type_id$required + [1] NA + + $rounds[[1]]$model_tasks[[1]]$output_type$mean$output_type_id$optional + NULL + + + $rounds[[1]]$model_tasks[[1]]$output_type$mean$value + $rounds[[1]]$model_tasks[[1]]$output_type$mean$value$type + [1] "double" + + $rounds[[1]]$model_tasks[[1]]$output_type$mean$value$minimum + [1] 0 + + + + + $rounds[[1]]$model_tasks[[1]]$target_metadata + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]] + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_id + [1] "inc hosp" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_keys + NULL + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_type + [1] "discrete" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$is_step_ahead + [1] TRUE + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$time_unit + [1] "week" + + + + + + $rounds[[1]]$submissions_due + $rounds[[1]]$submissions_due$relative_to + [1] "origin_date" + + $rounds[[1]]$submissions_due$start + [1] -4 + + $rounds[[1]]$submissions_due$end + [1] 2 + + + + + attr(,"class") + [1] "rounds" "list" + attr(,"n") + [1] 1 + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +--- + + Code + create_rounds(create_round(round_id_from_variable = FALSE, round_id = "round_1", + model_tasks = create_model_tasks(create_model_task(task_ids = create_task_ids( + create_task_id("origin_date", required = NULL, optional = c("2023-01-09")), + create_task_id("location", required = "US", optional = c("01", "02", "04", + "05", "06")), create_task_id("horizon", required = 1L, optional = 2:4)), + output_type = create_output_type(create_output_type_mean(is_required = TRUE, + value_type = "double", value_minimum = 0L)), target_metadata = create_target_metadata( + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = NULL, + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week")))), + submissions_due = list(start = "2023-01-05", end = "2023-01-11"), + last_data_date = "2023-01-06"), create_round(round_id_from_variable = FALSE, + round_id = "round_2", model_tasks = create_model_tasks(create_model_task( + task_ids = create_task_ids(create_task_id("origin_date", required = NULL, + optional = c("2023-01-16")), create_task_id("location", required = "US", + optional = c("01", "02", "04", "05", "06")), create_task_id("horizon", + required = 1L, optional = 2:4)), output_type = create_output_type( + create_output_type_mean(is_required = TRUE, value_type = "double", + value_minimum = 0L)), target_metadata = create_target_metadata( + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = NULL, + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week")))), + submissions_due = list(start = "2023-01-12", end = "2023-01-18"), + last_data_date = "2023-01-13")) + Output + $rounds + $rounds[[1]] + $rounds[[1]]$round_id_from_variable + [1] FALSE + + $rounds[[1]]$round_id + [1] "round_1" + + $rounds[[1]]$model_tasks + $rounds[[1]]$model_tasks[[1]] + $rounds[[1]]$model_tasks[[1]]$task_ids + $rounds[[1]]$model_tasks[[1]]$task_ids$origin_date + $rounds[[1]]$model_tasks[[1]]$task_ids$origin_date$required + NULL + + $rounds[[1]]$model_tasks[[1]]$task_ids$origin_date$optional + [1] "2023-01-09" + + + $rounds[[1]]$model_tasks[[1]]$task_ids$location + $rounds[[1]]$model_tasks[[1]]$task_ids$location$required + [1] "US" + + $rounds[[1]]$model_tasks[[1]]$task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $rounds[[1]]$model_tasks[[1]]$task_ids$horizon + $rounds[[1]]$model_tasks[[1]]$task_ids$horizon$required + [1] 1 + + $rounds[[1]]$model_tasks[[1]]$task_ids$horizon$optional + [1] 2 3 4 + + + + $rounds[[1]]$model_tasks[[1]]$output_type + $rounds[[1]]$model_tasks[[1]]$output_type$mean + $rounds[[1]]$model_tasks[[1]]$output_type$mean$output_type_id + $rounds[[1]]$model_tasks[[1]]$output_type$mean$output_type_id$required + [1] NA + + $rounds[[1]]$model_tasks[[1]]$output_type$mean$output_type_id$optional + NULL + + + $rounds[[1]]$model_tasks[[1]]$output_type$mean$value + $rounds[[1]]$model_tasks[[1]]$output_type$mean$value$type + [1] "double" + + $rounds[[1]]$model_tasks[[1]]$output_type$mean$value$minimum + [1] 0 + + + + + $rounds[[1]]$model_tasks[[1]]$target_metadata + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]] + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_id + [1] "inc hosp" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_keys + NULL + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$target_type + [1] "discrete" + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$is_step_ahead + [1] TRUE + + $rounds[[1]]$model_tasks[[1]]$target_metadata[[1]]$time_unit + [1] "week" + + + + + + $rounds[[1]]$submissions_due + $rounds[[1]]$submissions_due$start + [1] "2023-01-05" + + $rounds[[1]]$submissions_due$end + [1] "2023-01-11" + + + $rounds[[1]]$last_data_date + [1] "2023-01-06" + + + $rounds[[2]] + $rounds[[2]]$round_id_from_variable + [1] FALSE + + $rounds[[2]]$round_id + [1] "round_2" + + $rounds[[2]]$model_tasks + $rounds[[2]]$model_tasks[[1]] + $rounds[[2]]$model_tasks[[1]]$task_ids + $rounds[[2]]$model_tasks[[1]]$task_ids$origin_date + $rounds[[2]]$model_tasks[[1]]$task_ids$origin_date$required + NULL + + $rounds[[2]]$model_tasks[[1]]$task_ids$origin_date$optional + [1] "2023-01-16" + + + $rounds[[2]]$model_tasks[[1]]$task_ids$location + $rounds[[2]]$model_tasks[[1]]$task_ids$location$required + [1] "US" + + $rounds[[2]]$model_tasks[[1]]$task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $rounds[[2]]$model_tasks[[1]]$task_ids$horizon + $rounds[[2]]$model_tasks[[1]]$task_ids$horizon$required + [1] 1 + + $rounds[[2]]$model_tasks[[1]]$task_ids$horizon$optional + [1] 2 3 4 + + + + $rounds[[2]]$model_tasks[[1]]$output_type + $rounds[[2]]$model_tasks[[1]]$output_type$mean + $rounds[[2]]$model_tasks[[1]]$output_type$mean$output_type_id + $rounds[[2]]$model_tasks[[1]]$output_type$mean$output_type_id$required + [1] NA + + $rounds[[2]]$model_tasks[[1]]$output_type$mean$output_type_id$optional + NULL + + + $rounds[[2]]$model_tasks[[1]]$output_type$mean$value + $rounds[[2]]$model_tasks[[1]]$output_type$mean$value$type + [1] "double" + + $rounds[[2]]$model_tasks[[1]]$output_type$mean$value$minimum + [1] 0 + + + + + $rounds[[2]]$model_tasks[[1]]$target_metadata + $rounds[[2]]$model_tasks[[1]]$target_metadata[[1]] + $rounds[[2]]$model_tasks[[1]]$target_metadata[[1]]$target_id + [1] "inc hosp" + + $rounds[[2]]$model_tasks[[1]]$target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $rounds[[2]]$model_tasks[[1]]$target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $rounds[[2]]$model_tasks[[1]]$target_metadata[[1]]$target_keys + NULL + + $rounds[[2]]$model_tasks[[1]]$target_metadata[[1]]$target_type + [1] "discrete" + + $rounds[[2]]$model_tasks[[1]]$target_metadata[[1]]$is_step_ahead + [1] TRUE + + $rounds[[2]]$model_tasks[[1]]$target_metadata[[1]]$time_unit + [1] "week" + + + + + + $rounds[[2]]$submissions_due + $rounds[[2]]$submissions_due$start + [1] "2023-01-12" + + $rounds[[2]]$submissions_due$end + [1] "2023-01-18" + + + $rounds[[2]]$last_data_date + [1] "2023-01-13" + + + + attr(,"class") + [1] "rounds" "list" + attr(,"n") + [1] 2 + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +# create_round errors correctly + + Code + create_rounds(round_1, round_1) + Condition + Error in `collect_items()`: + x object 2 is a duplicate of object 1 + diff --git a/tests/testthat/_snaps/create_target_metadata.md b/tests/testthat/_snaps/create_target_metadata.md new file mode 100644 index 0000000..7ad7685 --- /dev/null +++ b/tests/testthat/_snaps/create_target_metadata.md @@ -0,0 +1,127 @@ +# create_target_metadata functions work correctly + + Code + create_target_metadata(create_target_metadata_item(target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), target_type = "discrete", + is_step_ahead = TRUE, time_unit = "week"), create_target_metadata_item( + target_id = "inc death", target_name = "Weekly incident influenza deaths", + target_units = "rate per 100,000 population", target_keys = list(target = "inc death"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week")) + Output + $target_metadata + $target_metadata[[1]] + $target_metadata[[1]]$target_id + [1] "inc hosp" + + $target_metadata[[1]]$target_name + [1] "Weekly incident influenza hospitalizations" + + $target_metadata[[1]]$target_units + [1] "rate per 100,000 population" + + $target_metadata[[1]]$target_keys + $target_metadata[[1]]$target_keys$target + [1] "inc hosp" + + + $target_metadata[[1]]$target_type + [1] "discrete" + + $target_metadata[[1]]$is_step_ahead + [1] TRUE + + $target_metadata[[1]]$time_unit + [1] "week" + + + $target_metadata[[2]] + $target_metadata[[2]]$target_id + [1] "inc death" + + $target_metadata[[2]]$target_name + [1] "Weekly incident influenza deaths" + + $target_metadata[[2]]$target_units + [1] "rate per 100,000 population" + + $target_metadata[[2]]$target_keys + $target_metadata[[2]]$target_keys$target + [1] "inc death" + + + $target_metadata[[2]]$target_type + [1] "discrete" + + $target_metadata[[2]]$is_step_ahead + [1] TRUE + + $target_metadata[[2]]$time_unit + [1] "week" + + + + attr(,"class") + [1] "target_metadata" "list" + attr(,"n") + [1] 2 + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +# create_target_metadata functions error correctly + + Code + create_target_metadata(create_target_metadata_item(target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), target_type = "discrete", + is_step_ahead = TRUE, time_unit = "week"), create_target_metadata_item( + target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = list(target = "inc hosp"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week")) + Condition + Error in `create_target_metadata()`: + ! `target_id`s must be unique across all `target_metadata_item`s. + x `target_metadata_item` 2 with `target_id` value inc hosp is duplicate. + +--- + + Code + create_target_metadata(create_target_metadata_item(target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), target_type = "discrete", + is_step_ahead = TRUE, time_unit = "week"), create_target_metadata_item( + target_id = "inc death", target_name = "Weekly incident influenza deaths", + target_units = "rate per 100,000 population", target_keys = list(target = "inc hosp"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week")) + Condition + Error in `create_target_metadata()`: + ! `target_keys`s must be unique across all `target_metadata_item`s. + x `target_metadata_item` 2 with `target_keys` value list(target = "inc hosp") is duplicate. + +--- + + Code + create_target_metadata(create_target_metadata_item(target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), target_type = "discrete", + is_step_ahead = TRUE, time_unit = "week"), list(a = 10), list(b = 10)) + Condition + Error in `create_target_metadata()`: + ! All items supplied must inherit from class + x Items 2 and 3 do not. + +--- + + Code + create_target_metadata(item_1, create_target_metadata_item(target_id = "inc death", + target_name = "Weekly incident influenza deaths", target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), target_type = "discrete", + is_step_ahead = TRUE, time_unit = "week")) + Condition + Error in `create_target_metadata()`: + ! All items supplied must be created against the same Hub schema. + x `schema_id` attributes are not consistent across all items. + Item `schema_id` attributes: + * Item 1 : invalid_schema + * Item 2 : https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json + diff --git a/tests/testthat/_snaps/create_target_metadata_item.md b/tests/testthat/_snaps/create_target_metadata_item.md new file mode 100644 index 0000000..c223716 --- /dev/null +++ b/tests/testthat/_snaps/create_target_metadata_item.md @@ -0,0 +1,151 @@ +# create_target_metadata_item functions work correctly + + Code + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = list(target = "inc hosp"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week") + Output + $target_id + [1] "inc hosp" + + $target_name + [1] "Weekly incident influenza hospitalizations" + + $target_units + [1] "rate per 100,000 population" + + $target_keys + $target_keys$target + [1] "inc hosp" + + + $target_type + [1] "discrete" + + $is_step_ahead + [1] TRUE + + $time_unit + [1] "week" + + attr(,"class") + [1] "target_metadata_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +--- + + Code + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_type = "discrete", + is_step_ahead = FALSE) + Output + $target_id + [1] "inc hosp" + + $target_name + [1] "Weekly incident influenza hospitalizations" + + $target_units + [1] "rate per 100,000 population" + + $target_keys + NULL + + $target_type + [1] "discrete" + + $is_step_ahead + [1] FALSE + + attr(,"class") + [1] "target_metadata_item" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +# create_target_metadata_item functions error correctly + + Code + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = list(target = "inc hosp"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "weekly") + Condition + Error in `map()`: + i In index: 6. + Caused by error in `create_target_metadata_item()`: + x `time_unit` value is invalid. + ! Must be one of "day", "week", and "month". + i Actual value is "weekly" + +--- + + Code + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = list(target = c( + "inc hosp", "inc death")), target_type = "discrete", is_step_ahead = TRUE, + time_unit = "week") + Condition + Error in `map2()`: + i In index: 1. + i With name: target. + Caused by error in `create_target_metadata_item()`: + ! `target_keys` element target must be a vector of length 1 not a vector of length 2 + +--- + + Code + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_keys = c(target = "inc hosp"), + target_type = "discrete", is_step_ahead = TRUE, time_unit = "week") + Condition + Error in `create_target_metadata_item()`: + ! `target_keys` must be a not a + +--- + + Code + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_type = "discrete", + is_step_ahead = TRUE) + Condition + Error in `create_target_metadata_item()`: + ! A value must be provided for `time_unit` when `is_step_ahead` is TRUE + +--- + + Code + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = 1e+05, target_type = "discrete", is_step_ahead = FALSE) + Condition + Error in `map()`: + i In index: 3. + Caused by error in `create_target_metadata_item()`: + x `target_units` is of type . + ! Must be . + +--- + + Code + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", target_type = "invalid_target_type", + is_step_ahead = FALSE) + Condition + Error in `map()`: + i In index: 4. + Caused by error in `create_target_metadata_item()`: + x `target_type` value is invalid. + ! Must be one of "continuous", "discrete", "date", "binary", "nominal", "ordinal", and "compositional". + i Actual value is "invalid_target_type" + +--- + + Code + create_target_metadata_item(target_id = "inc hosp", target_name = "Weekly incident influenza hospitalizations", + target_units = c("rate per 100,000 population", "count"), target_type = "discrete", + is_step_ahead = FALSE) + Condition + Error in `map()`: + i In index: 3. + Caused by error in `create_target_metadata_item()`: + x `target_units` must be length 1, not 2. + diff --git a/tests/testthat/_snaps/create_task_id.md b/tests/testthat/_snaps/create_task_id.md new file mode 100644 index 0000000..a719956 --- /dev/null +++ b/tests/testthat/_snaps/create_task_id.md @@ -0,0 +1,114 @@ +# create_task_id works correctly + + Code + create_task_id("horizon", required = 1L, optional = 2:4) + Output + $horizon + $horizon$required + [1] 1 + + $horizon$optional + [1] 2 3 4 + + + attr(,"class") + [1] "task_id" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +--- + + Code + create_task_id("origin_date", required = NULL, optional = c("2023-01-02", + "2023-01-09", "2023-01-16")) + Output + $origin_date + $origin_date$required + NULL + + $origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + attr(,"class") + [1] "task_id" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +--- + + Code + create_task_id("scenario_id", required = NULL, optional = c("A-2021-03-28", + "B-2021-03-28")) + Output + $scenario_id + $scenario_id$required + NULL + + $scenario_id$optional + [1] "A-2021-03-28" "B-2021-03-28" + + + attr(,"class") + [1] "task_id" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +--- + + Code + create_task_id("scenario_id", required = NULL, optional = c(1L, 2L)) + Output + $scenario_id + $scenario_id$required + NULL + + $scenario_id$optional + [1] 1 2 + + + attr(,"class") + [1] "task_id" "list" + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +# create_task_id errors correctly + + Code + create_task_id("horizon", required = NULL, optional = NULL) + Condition + Error in `check_prop_not_all_null()`: + x Both arguments `required` and `optional` cannot be NULL. + +--- + + Code + create_task_id("origin_date", required = NULL, optional = c("01/20/2023")) + Condition + Error in `map()`: + i In index: 2. + Caused by error in `create_task_id()`: + x `optional` value must be character string of date in valid ISO 8601 format (YYYY-MM-DD). + +--- + + Code + create_task_id("scenario_id", required = NULL, optional = c(1L, 1L)) + Condition + Error in `map()`: + i In index: 2. + Caused by error in `create_task_id()`: + ! All values in `optional` must be unique. + x Value 1 is duplicated. + +--- + + Code + create_task_id("horizon", required = c(TRUE, FALSE), optional = NULL) + Condition + Error in `map()`: + i In index: 1. + Caused by error in `create_task_id()`: + x `required` is of type . + ! Must be one of . + diff --git a/tests/testthat/_snaps/create_task_ids.md b/tests/testthat/_snaps/create_task_ids.md new file mode 100644 index 0000000..8456b16 --- /dev/null +++ b/tests/testthat/_snaps/create_task_ids.md @@ -0,0 +1,93 @@ +# create_task_ids functions work correctly + + Code + create_task_ids(create_task_id("origin_date", required = NULL, optional = c( + "2023-01-02", "2023-01-09", "2023-01-16")), create_task_id("scenario_id", + required = NULL, optional = c("A-2021-03-28", "B-2021-03-28")), + create_task_id("location", required = "US", optional = c("01", "02", "04", "05", + "06")), create_task_id("target", required = "inc hosp", optional = NULL), + create_task_id("horizon", required = 1L, optional = 2:4)) + Output + $task_ids + $task_ids$origin_date + $task_ids$origin_date$required + NULL + + $task_ids$origin_date$optional + [1] "2023-01-02" "2023-01-09" "2023-01-16" + + + $task_ids$scenario_id + $task_ids$scenario_id$required + NULL + + $task_ids$scenario_id$optional + [1] "A-2021-03-28" "B-2021-03-28" + + + $task_ids$location + $task_ids$location$required + [1] "US" + + $task_ids$location$optional + [1] "01" "02" "04" "05" "06" + + + $task_ids$target + $task_ids$target$required + [1] "inc hosp" + + $task_ids$target$optional + NULL + + + $task_ids$horizon + $task_ids$horizon$required + [1] 1 + + $task_ids$horizon$optional + [1] 2 3 4 + + + + attr(,"class") + [1] "task_ids" "list" + attr(,"n") + [1] 5 + attr(,"schema_id") + [1] "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json" + +# create_task_ids functions error correctly + + Code + create_task_ids(create_task_id("origin_date", required = NULL, optional = c( + "2023-01-02", "2023-01-09", "2023-01-16")), create_task_id("origin_date", + required = NULL, optional = c("2023-01-02", "2023-01-09", "2023-01-16"))) + Condition + Error in `create_task_ids()`: + ! `names` must be unique across all items. + x Item 2 with `name` "origin_date" is duplicate. + +--- + + Code + create_task_ids(create_task_id("origin_date", required = NULL, optional = c( + "2023-01-02", "2023-01-09", "2023-01-16")), list(a = 10), list(b = 10)) + Condition + Error in `create_task_ids()`: + ! All items supplied must inherit from class + x Items 2 and 3 do not. + +--- + + Code + create_task_ids(item_1, create_task_id("scenario_id", required = NULL, + optional = c("A-2021-03-28", "B-2021-03-28"))) + Condition + Error in `create_task_ids()`: + ! All items supplied must be created against the same Hub schema. + x `schema_id` attributes are not consistent across all items. + Item `schema_id` attributes: + * Item 1 : invalid_schema + * Item 2 : https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.1/tasks-schema.json + diff --git a/tests/testthat/_snaps/validate_config.md b/tests/testthat/_snaps/validate_config.md new file mode 100644 index 0000000..82bbe7b --- /dev/null +++ b/tests/testthat/_snaps/validate_config.md @@ -0,0 +1,773 @@ +# Config errors detected successfully + + Code + out + Output + [1] FALSE + attr(,"errors") + instancePath + 1 /rounds/0/model_tasks/0/task_ids/target/required + 2 /rounds/0/model_tasks/0/output_type/mean/type_id/optional + 3 /rounds/0/model_tasks/0/output_type/mean/type_id/required + 4 /rounds/0/model_tasks/0/output_type/mean/type_id + 5 /rounds/0/submissions_due + 6 /rounds/0/submissions_due/start + 7 /rounds/0/submissions_due/end + 8 /rounds/0/submissions_due + schemaPath + 1 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/target/properties/required/type + 2 #/properties/rounds/items/properties/model_tasks/items/properties/output_type/properties/mean/properties/type_id/oneOf/0/properties/optional/type + 3 #/properties/rounds/items/properties/model_tasks/items/properties/output_type/properties/mean/properties/type_id/oneOf/1/properties/required/type + 4 #/properties/rounds/items/properties/model_tasks/items/properties/output_type/properties/mean/properties/type_id/oneOf + 5 #/properties/rounds/items/properties/submissions_due/oneOf/0/required + 6 #/properties/rounds/items/properties/submissions_due/oneOf/1/properties/start/type + 7 #/properties/rounds/items/properties/submissions_due/oneOf/1/properties/end/type + 8 #/properties/rounds/items/properties/submissions_due/oneOf + keyword params.type params.passingSchemas params.missingProperty + 1 type array, null NA + 2 type null NA + 3 type null NA + 4 oneOf NULL NA + 5 required NULL NA relative_to + 6 type string NA + 7 type string NA + 8 oneOf NULL NA + message + 1 must be array,null + 2 must be null + 3 must be null + 4 must match exactly one schema in oneOf + 5 must have required property 'relative_to' + 6 must be string + 7 must be string + 8 must match exactly one schema in oneOf + schema + 1 array, null + 2 null + 3 null + 4 When mean is required, property set to single element 'NA' array, When mean is optional, property set to null, array, null, NA, NA, 1, NA, When mean is required, property set to null, When mean is optional, property set to single element 'NA' array, null, array, NA, NA, NA, 1 + 5 relative_to, start, end + 6 string + 7 string + 8 Name of task id variable in relation to which submission start and end dates are calculated., NA, string, NA, Difference in days between start and origin date., Submission start date., integer, string, NA, date, Difference in days between end and origin date., Submission end date., integer, string, NA, date, relative_to, start, end, start, end + parentSchema.description + 1 Array of target unique identifiers that must be present for submission to be valid. Can be null if no targets are required and all valid targets are specified in the optional property. + 2 When mean is required, property set to null + 3 When mean is optional, property set to null + 4 type_id is not meaningful for a mean output_type. The property is primarily used to determine whether mean is a required or optional output type through properties required and optional. If mean is a required output type, the required property must be an array containing the single string element 'NA' and the optional property must be set to null. If mean is an optional output type, the optional property must be an array containing the single string element 'NA' and the required property must be set to null + 5 + 6 Submission start date. + 7 Submission end date. + 8 Object defining the dates by which model forecasts must be submitted to the hub. + parentSchema.type parentSchema.type + 1 array, null string + 2 null + 3 null + 4 object + 5 NULL + 6 string + 7 string + 8 object + parentSchema.examples + 1 NULL + 2 NULL + 3 NULL + 4 NA, NA + 5 NULL + 6 NULL + 7 NULL + 8 2022-06-07, -4, 2022-07-20, 2, NA, origin_date + parentSchema.oneOf + 1 NULL + 2 NULL + 3 NULL + 4 When mean is required, property set to single element 'NA' array, When mean is optional, property set to null, array, null, NA, NA, 1, NA, When mean is required, property set to null, When mean is optional, property set to single element 'NA' array, null, array, NA, NA, NA, 1 + 5 NULL + 6 NULL + 7 NULL + 8 Name of task id variable in relation to which submission start and end dates are calculated., NA, string, NA, Difference in days between start and origin date., Submission start date., integer, string, NA, date, Difference in days between end and origin date., Submission end date., integer, string, NA, date, relative_to, start, end, start, end + parentSchema.required + 1 NULL + 2 NULL + 3 NULL + 4 required, optional + 5 relative_to, start, end + 6 NULL + 7 NULL + 8 start, end + parentSchema.properties.relative_to.description + 1 + 2 + 3 + 4 + 5 Name of task id variable in relation to which submission start and end dates are calculated. + 6 + 7 + 8 + parentSchema.properties.relative_to.type + 1 + 2 + 3 + 4 + 5 string + 6 + 7 + 8 + parentSchema.properties.start.description + 1 + 2 + 3 + 4 + 5 Difference in days between start and origin date. + 6 + 7 + 8 + parentSchema.properties.start.type + 1 + 2 + 3 + 4 + 5 integer + 6 + 7 + 8 + parentSchema.properties.end.description + 1 + 2 + 3 + 4 + 5 Difference in days between end and origin date. + 6 + 7 + 8 + parentSchema.properties.end.type parentSchema.format data + 1 wk inc flu hosp + 2 NA + 3 NA + 4 NA, NA + 5 integer -6, 1 + 6 date -6 + 7 date 1 + 8 -6, 1 + dataPath + 1 /rounds/0/model_tasks/0/task_ids/target/required + 2 /rounds/0/model_tasks/0/output_type/mean/type_id/optional + 3 /rounds/0/model_tasks/0/output_type/mean/type_id/required + 4 /rounds/0/model_tasks/0/output_type/mean/type_id + 5 /rounds/0/submissions_due + 6 /rounds/0/submissions_due/start + 7 /rounds/0/submissions_due/end + 8 /rounds/0/submissions_due + attr(,"config_path") + [1] "testdata/tasks-errors.json" + attr(,"schema_version") + [1] "v0.0.0.9" + attr(,"schema_url") + https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v0.0.0.9/tasks-schema.json + +# Dynamic config errors detected successfully by custom R validation + + Code + out + Output + [1] FALSE + attr(,"config_path") + [1] "testdata/tasks-errors-rval.json" + attr(,"schema_version") + [1] "v2.0.0" + attr(,"schema_url") + https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json + attr(,"errors") + instancePath + 1 /rounds/0/model_tasks/1/target_metadata/0/target_keys/target_measure + 2 /rounds/0/model_tasks/1/target_metadata/1/target_keys/target_measure + 3 /rounds/0/model_tasks/1/target_metadata/0/target_keys/target_outcome + 4 /rounds/0/model_tasks/1/task_ids/target_outcome + schemaPath + 1 #/properties/rounds/items/properties/model_tasks/items/properties/target_metadata/items/properties/target_keys + 2 #/properties/rounds/items/properties/model_tasks/items/properties/target_metadata/items/properties/target_keys + 3 #/properties/rounds/items/properties/model_tasks/items/properties/target_metadata/items/properties/target_keys + 4 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids + keyword + 1 target_keys names + 2 target_keys names + 3 target_keys values + 4 task_id values + message + 1 target_key(s) 'target_measure' not properties of modeling task group task IDs + 2 target_key(s) 'target_measure' not properties of modeling task group task IDs + 3 target_key value 'flu hospitalisation' does not match any values in corresponding modeling task group task_id + 4 task_id value(s) 'flu hosp, wk inc flu hosp' not defined in any corresponding target_key. + schema + 1 + 2 + 3 + 4 + data + 1 task_id names: origin_date, target_outcome, target_mesures, horizon, location;\ntarget_key names: target_measure, target_outcome + 2 task_id names: origin_date, target_outcome, target_mesures, horizon, location;\ntarget_key names: target_measure, target_outcome + 3 task_id.target_outcome values: flu hosp, wk inc flu hosp, flu case;\ntarget_key.target_outcome value: flu hospitalisation + 4 task_id.target_outcome unique values: flu hosp, wk inc flu hosp, flu case;\ntarget_key.target_outcome unique values: flu hospitalisation, flu case + +# Reserved hub variable task id name detected correctly + + Code + out + Output + [1] FALSE + attr(,"config_path") + [1] "testdata/tasks-errors-rval-reserved.json" + attr(,"schema_version") + [1] "v2.0.0" + attr(,"schema_url") + https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json + attr(,"errors") + instancePath + 1 /rounds/0/model_tasks/1/target_metadata/0/target_keys/target_measure + 2 /rounds/0/model_tasks/1/target_metadata/1/target_keys/target_measure + 3 /rounds/0/model_tasks/1/target_metadata/0/target_keys/target_outcome + 4 /rounds/0/model_tasks/1/task_ids/target_outcome + 5 /rounds/0/model_tasks/0/task_ids/ + 6 /rounds/0/model_tasks/1/task_ids/ + schemaPath + 1 #/properties/rounds/items/properties/model_tasks/items/properties/target_metadata/items/properties/target_keys + 2 #/properties/rounds/items/properties/model_tasks/items/properties/target_metadata/items/properties/target_keys + 3 #/properties/rounds/items/properties/model_tasks/items/properties/target_metadata/items/properties/target_keys + 4 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids + 5 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids + 6 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids + keyword + 1 target_keys names + 2 target_keys names + 3 target_keys values + 4 task_id values + 5 task_id names + 6 task_id names + message + 1 target_key(s) 'target_measure' not properties of modeling task group task IDs + 2 target_key(s) 'target_measure' not properties of modeling task group task IDs + 3 target_key value 'flu hospitalisation' does not match any values in corresponding modeling task group task_id + 4 task_id value(s) 'flu hosp, wk inc flu hosp' not defined in any corresponding target_key. + 5 task_id name(s) 'model_id' must not match reserved hub variable names. + 6 task_id name(s) 'value' must not match reserved hub variable names. + schema + 1 + 2 + 3 + 4 + 5 + 6 + data + 1 task_id names: origin_date, target_outcome, target_mesures, horizon, value;\ntarget_key names: target_measure, target_outcome + 2 task_id names: origin_date, target_outcome, target_mesures, horizon, value;\ntarget_key names: target_measure, target_outcome + 3 task_id.target_outcome values: flu hosp, wk inc flu hosp, flu case;\ntarget_key.target_outcome value: flu hospitalisation + 4 task_id.target_outcome unique values: flu hosp, wk inc flu hosp, flu case;\ntarget_key.target_outcome unique values: flu hospitalisation, flu case + 5 task_id names: origin_date, target, horizon & model_id;\nreserved hub variable names: model_id, output_type, output_type_id & value + 6 task_id names: origin_date, target_outcome, target_mesures, horizon & value;\nreserved hub variable names: model_id, output_type, output_type_id & value + +# Additional properties error successfully + + Code + out + Output + [1] FALSE + attr(,"errors") + instancePath + 1 /rounds/0/model_tasks/0/output_type + schemaPath + 1 #/properties/rounds/items/properties/model_tasks/items/properties/output_type/additionalProperties + keyword additionalProperty message + 1 additionalProperties target_metadata must NOT have additional properties + schema parentSchema.type + 1 FALSE object + parentSchema.description + 1 Object defining valid model output types for a given modeling task. The name of each property corresponds to valid values in column 'output_type' while the 'output_type_id' property of each output type defines the valid values of the 'output_type_id' column and the 'value' property defines the valid values of the 'value' column for a given output type. + parentSchema.properties.mean.type + 1 object + parentSchema.properties.mean.description + 1 Object defining the mean of the predictive distribution output type. + parentSchema.properties.mean.properties.output_type_id.description + 1 output_type_id is not meaningful for a mean output_type. The property is primarily used to determine whether mean is a required or optional output type through properties required and optional. If mean is a required output type, the required property must be an array containing the single string element 'NA' and the optional property must be set to null. If mean is an optional output type, the optional property must be an array containing the single string element 'NA' and the required property must be set to null + parentSchema.properties.mean.properties.output_type_id.examples + 1 NA, NA + parentSchema.properties.mean.properties.output_type_id.type + 1 object + parentSchema.properties.mean.properties.output_type_id.oneOf + 1 When mean is required, property set to single element 'NA' array, When mean is optional, property set to null, array, null, NA, NA, 1, NA, When mean is required, property set to null, When mean is optional, property set to single element 'NA' array, null, array, NA, NA, NA, 1 + parentSchema.properties.mean.properties.output_type_id.required + 1 required, optional + parentSchema.properties.mean.properties.value.type + 1 object + parentSchema.properties.mean.properties.value.description + 1 Object defining the characteristics of valid mean values. + parentSchema.properties.mean.properties.value.examples + 1 double, 0 + parentSchema.properties.mean.properties.value.properties.type.description + 1 Data type of mean values. + parentSchema.properties.mean.properties.value.properties.type.type + 1 string + parentSchema.properties.mean.properties.value.properties.type.enum + 1 double, integer + parentSchema.properties.mean.properties.value.properties.minimum.description + 1 The minimum inclusive valid mean value + parentSchema.properties.mean.properties.value.properties.minimum.type + 1 number, integer + parentSchema.properties.mean.properties.value.properties.maximum.description + 1 the maximum inclusive valid mean value + parentSchema.properties.mean.properties.value.properties.maximum.type + 1 number, integer + parentSchema.properties.mean.properties.value.required + 1 type + parentSchema.properties.mean.required parentSchema.properties.median.type + 1 output_type_id, value object + parentSchema.properties.median.description + 1 Object defining the median of the predictive distribution output type + parentSchema.properties.median.properties.output_type_id.description + 1 output_type_id is not meaningful for a median output_type. The property is primarily used to determine whether median is a required or optional output type through properties required and optional. If median is a required output type, the required property must be an array containing the single string element 'NA' and the optional property must be set to null. If median is an optional output type, the optional property must be an array containing the single string element 'NA' and the required property must be set to null + parentSchema.properties.median.properties.output_type_id.examples + 1 NA, NA + parentSchema.properties.median.properties.output_type_id.type + 1 object + parentSchema.properties.median.properties.output_type_id.oneOf + 1 When median is required, property set to single element 'NA' array, When median is optional, property set to null, array, null, NA, NA, 1, NA, When median is required, property set to null, When median is optional, property set to single element 'NA' array, null, array, NA, NA, NA, 1 + parentSchema.properties.median.properties.output_type_id.required + 1 required, optional + parentSchema.properties.median.properties.value.type + 1 object + parentSchema.properties.median.properties.value.description + 1 Object defining the characteristics of valid median values + parentSchema.properties.median.properties.value.examples + 1 double, 0 + parentSchema.properties.median.properties.value.properties.type.description + 1 Data type of median values + parentSchema.properties.median.properties.value.properties.type.type + 1 string + parentSchema.properties.median.properties.value.properties.type.enum + 1 double, integer + parentSchema.properties.median.properties.value.properties.minimum.description + 1 The minimum inclusive valid median value + parentSchema.properties.median.properties.value.properties.minimum.type + 1 number, integer + parentSchema.properties.median.properties.value.properties.maximum.description + 1 the maximum inclusive valid median value + parentSchema.properties.median.properties.value.properties.maximum.type + 1 number, integer + parentSchema.properties.median.properties.value.required + 1 type + parentSchema.properties.median.required + 1 output_type_id, value + parentSchema.properties.quantile.description + 1 Object defining the quantiles of the predictive distribution output type. + parentSchema.properties.quantile.type + 1 object + parentSchema.properties.quantile.properties.output_type_id.description + 1 Object containing required and optional arrays defining the probability levels at which quantiles of the predictive distribution will be recorded. + parentSchema.properties.quantile.properties.output_type_id.examples + 1 0.25, 0.50, 0.75, 0.10, 0.20, 0.30, 0.40, 0.60, 0.70, 0.80, 0.90 + parentSchema.properties.quantile.properties.output_type_id.type + 1 object + parentSchema.properties.quantile.properties.output_type_id.properties.required.description + 1 Array of unique probability levels between 0 and 1 that must be present for submission to be valid. Can be null if no probability levels are required and all valid probability levels are specified in the optional property. + parentSchema.properties.quantile.properties.output_type_id.properties.required.type + 1 array, null + parentSchema.properties.quantile.properties.output_type_id.properties.required.uniqueItems + 1 TRUE + parentSchema.properties.quantile.properties.output_type_id.properties.required.items.type + 1 number + parentSchema.properties.quantile.properties.output_type_id.properties.required.items.minimum + 1 0 + parentSchema.properties.quantile.properties.output_type_id.properties.required.items.maximum + 1 1 + parentSchema.properties.quantile.properties.output_type_id.properties.optional.description + 1 Array of valid but not required unique probability levels. Can be null if all probability levels are required and are specified in the required property. + parentSchema.properties.quantile.properties.output_type_id.properties.optional.type + 1 array, null + parentSchema.properties.quantile.properties.output_type_id.properties.optional.uniqueItems + 1 TRUE + parentSchema.properties.quantile.properties.output_type_id.properties.optional.items.type + 1 number + parentSchema.properties.quantile.properties.output_type_id.properties.optional.items.minimum + 1 0 + parentSchema.properties.quantile.properties.output_type_id.properties.optional.items.maximum + 1 1 + parentSchema.properties.quantile.properties.output_type_id.required + 1 required, optional + parentSchema.properties.quantile.properties.value.type + 1 object + parentSchema.properties.quantile.properties.value.description + 1 Object defining the characteristics of valid quantiles of the predictive distribution at a given probability level. + parentSchema.properties.quantile.properties.value.properties.type.description + 1 Data type of quantile values. + parentSchema.properties.quantile.properties.value.properties.type.examples + 1 double + parentSchema.properties.quantile.properties.value.properties.type.type + 1 string + parentSchema.properties.quantile.properties.value.properties.type.enum + 1 double, integer + parentSchema.properties.quantile.properties.value.properties.minimum.description + 1 The minimum inclusive valid quantile value (optional). + parentSchema.properties.quantile.properties.value.properties.minimum.examples + 1 0 + parentSchema.properties.quantile.properties.value.properties.minimum.type + 1 number, integer + parentSchema.properties.quantile.properties.value.properties.maximum.description + 1 The maximum inclusive valid quantile value (optional). + parentSchema.properties.quantile.properties.value.properties.maximum.type + 1 number, integer + parentSchema.properties.quantile.properties.value.required + 1 type + parentSchema.properties.quantile.required + 1 output_type_id, value + parentSchema.properties.cdf.description + 1 Object defining the cumulative distribution function of the predictive distribution output type. + parentSchema.properties.cdf.type + 1 object + parentSchema.properties.cdf.properties.output_type_id.description + 1 Object containing required and optional arrays defining possible values of the target variable at which values of the cumulative distribution function of the predictive distribution will be recorded. + parentSchema.properties.cdf.properties.output_type_id.examples + 1 10, 20, EW202240, EW202241, EW202242, EW202243, EW202244, EW202245, EW202246, EW202247, NA, NA + parentSchema.properties.cdf.properties.output_type_id.type + 1 object + parentSchema.properties.cdf.properties.output_type_id.properties.required.description + 1 Array of unique target values that must be present for submission to be valid. Can be null if no target values are required and all valid target values are specified in the optional property. + parentSchema.properties.cdf.properties.output_type_id.properties.required.type + 1 array, null + parentSchema.properties.cdf.properties.output_type_id.properties.required.uniqueItems + 1 TRUE + parentSchema.properties.cdf.properties.output_type_id.properties.required.oneOf + 1 number, integer, string, 0, NA, NA, ^EW[0-9]{6}, NA, 8, NA, 8 + parentSchema.properties.cdf.properties.output_type_id.properties.optional.description + 1 Array of valid but not required unique target values. Can be null if all target values are required and are specified in the required property. + parentSchema.properties.cdf.properties.output_type_id.properties.optional.type + 1 array, null + parentSchema.properties.cdf.properties.output_type_id.properties.optional.uniqueItems + 1 TRUE + parentSchema.properties.cdf.properties.output_type_id.properties.optional.oneOf + 1 number, integer, string, 0, NA, NA, ^EW[0-9]{6}, NA, 8, NA, 8 + parentSchema.properties.cdf.properties.output_type_id.required + 1 required, optional + parentSchema.properties.cdf.properties.value.type + 1 object + parentSchema.properties.cdf.properties.value.description + 1 Object defining the characteristics of valid values of the cumulative distribution function at a given target value. + parentSchema.properties.cdf.properties.value.properties.type.description + 1 Data type of cumulative distribution function values. + parentSchema.properties.cdf.properties.value.properties.type.examples + 1 double + parentSchema.properties.cdf.properties.value.properties.type.const + 1 double + parentSchema.properties.cdf.properties.value.properties.minimum.description + 1 The minimum inclusive valid cumulative distribution function value. Must be 0. + parentSchema.properties.cdf.properties.value.properties.minimum.const + 1 0 + parentSchema.properties.cdf.properties.value.properties.maximum.description + 1 The maximum inclusive valid cumulative distribution function value. Must be 1. + parentSchema.properties.cdf.properties.value.properties.maximum.const + 1 1 + parentSchema.properties.cdf.properties.value.required + 1 type, minimum, maximum + parentSchema.properties.cdf.required + 1 output_type_id, value + parentSchema.properties.pmf.description + 1 Object defining a probability mass function for a discrete variable output type. Includes nominal, binary and ordinal variable types. + parentSchema.properties.pmf.type + 1 object + parentSchema.properties.pmf.properties.output_type_id.description + 1 Object containing required and optional arrays specifying valid categories of a discrete variable. + parentSchema.properties.pmf.properties.output_type_id.examples + 1 NA, low, moderate, high, extreme + parentSchema.properties.pmf.properties.output_type_id.type + 1 object + parentSchema.properties.pmf.properties.output_type_id.properties.required.description + 1 Array of unique categories of a discrete variable that must be present for submission to be valid. Can be null if no categories are required and all valid categories are specified in the optional property. + parentSchema.properties.pmf.properties.output_type_id.properties.required.type + 1 array, null + parentSchema.properties.pmf.properties.output_type_id.properties.required.uniqueItems + 1 TRUE + parentSchema.properties.pmf.properties.output_type_id.properties.required.type + 1 string + parentSchema.properties.pmf.properties.output_type_id.properties.optional.description + 1 Array of valid but not required unique categories of a discrete variable. Can be null if all categories are required and are specified in the required property. + parentSchema.properties.pmf.properties.output_type_id.properties.optional.type + 1 array, null + parentSchema.properties.pmf.properties.output_type_id.properties.optional.uniqueItems + 1 TRUE + parentSchema.properties.pmf.properties.output_type_id.properties.optional.type + 1 string + parentSchema.properties.pmf.properties.output_type_id.required + 1 required, optional + parentSchema.properties.pmf.properties.value.type + 1 object + parentSchema.properties.pmf.properties.value.description + 1 Object defining valid values of the probability mass function of the predictive distribution for a given category of a discrete outcome variable. + parentSchema.properties.pmf.properties.value.examples + 1 double, 0, 1 + parentSchema.properties.pmf.properties.value.properties.type.description + 1 Data type of the probability mass function values. + parentSchema.properties.pmf.properties.value.properties.type.const + 1 double + parentSchema.properties.pmf.properties.value.properties.minimum.description + 1 The minimum inclusive valid probability mass function value. Must be 0. + parentSchema.properties.pmf.properties.value.properties.minimum.const + 1 0 + parentSchema.properties.pmf.properties.value.properties.maximum.description + 1 The maximum inclusive valid probability mass function value. Must be 1. + parentSchema.properties.pmf.properties.value.properties.maximum.const + 1 1 + parentSchema.properties.pmf.properties.value.required + 1 type, minimum, maximum + parentSchema.properties.pmf.required + 1 output_type_id, value + parentSchema.properties.sample.description + 1 Object defining a sample output type. + parentSchema.properties.sample.type + 1 object + parentSchema.properties.sample.properties.output_type_id.description + 1 Object containing required and optional arrays specifying valid sample values. + parentSchema.properties.sample.properties.output_type_id.examples + 1 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + parentSchema.properties.sample.properties.output_type_id.type + 1 object + parentSchema.properties.sample.properties.output_type_id.properties.required.description + 1 Array of unique sample indexes that must be present for submission to be valid. Can be null if no sample indexes are required and all valid sample indexes are specified in the optional property. + parentSchema.properties.sample.properties.output_type_id.properties.required.type + 1 array, null + parentSchema.properties.sample.properties.output_type_id.properties.required.uniqueItems + 1 TRUE + parentSchema.properties.sample.properties.output_type_id.properties.required.items.type + 1 integer + parentSchema.properties.sample.properties.output_type_id.properties.required.items.minimum + 1 1 + parentSchema.properties.sample.properties.output_type_id.properties.optional.description + 1 Array of valid but not required unique sample indexes. Can be null if all sample indexes are required and are specified in the required property. + parentSchema.properties.sample.properties.output_type_id.properties.optional.type + 1 array, null + parentSchema.properties.sample.properties.output_type_id.properties.optional.uniqueItems + 1 TRUE + parentSchema.properties.sample.properties.output_type_id.properties.optional.items.type + 1 integer + parentSchema.properties.sample.properties.output_type_id.properties.optional.items.minimum + 1 1 + parentSchema.properties.sample.properties.output_type_id.required + 1 required, optional + parentSchema.properties.sample.properties.value.type + 1 object + parentSchema.properties.sample.properties.value.description + 1 Object defining valid values of samples from the predictive distribution for a given sample index. Depending on the Hub specification, samples with the same sample index (specified by the output_type_id) may be assumed to correspond to a joint distribution across multiple levels of the task id variables. See Hub documentation for details. + parentSchema.properties.sample.properties.value.properties.type.description + 1 Data type of sample value from the predictive distribution. + parentSchema.properties.sample.properties.value.properties.type.examples + 1 double + parentSchema.properties.sample.properties.value.properties.type.type + 1 string + parentSchema.properties.sample.properties.value.properties.type.enum + 1 double, integer + parentSchema.properties.sample.properties.value.properties.description + 1 The minimum inclusive valid sample value from the predictive distribution + parentSchema.properties.sample.properties.value.properties.description + 1 The maximum inclusive valid sample value from the predictive distribution + parentSchema.properties.sample.properties.value.required + 1 type + parentSchema.properties.sample.required parentSchema.additionalProperties + 1 output_type_id, value FALSE + data.mean.output_type_id.required data.mean.output_type_id.optional + 1 NA NA + data.mean.value.type data.mean.value.minimum + 1 integer 0 + data.quantile.output_type_id.required + 1 0.010, 0.025, 0.050, 0.100, 0.150, 0.200, 0.250, 0.300, 0.350, 0.400, 0.450, 0.500, 0.550, 0.600, 0.650, 0.700, 0.750, 0.800, 0.850, 0.900, 0.950, 0.975, 0.990 + data.quantile.output_type_id.optional data.quantile.value.type + 1 NA integer + data.quantile.value.minimum + 1 0 + data.target_metadata + 1 inc flu hosp, daily incident influenza hospitalizations, count, inc flu hosp, This target represents a count of the number of new hospitalizations per day in a given location., TRUE, day + dataPath + 1 /rounds/0/model_tasks/0/output_type + attr(,"config_path") + [1] "testdata/tasks-addprop.json" + attr(,"schema_version") + [1] "v2.0.0" + attr(,"schema_url") + https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json + +# Duplicate values in individual array error successfully + + Code + out + Output + [1] FALSE + attr(,"errors") + instancePath + 1 /rounds/1/model_tasks/0/task_ids/origin_date/optional + 2 /rounds/1/model_tasks/0/output_type/quantile/output_type_id/required + schemaPath + 1 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/origin_date/properties/optional/uniqueItems + 2 #/properties/rounds/items/properties/model_tasks/items/properties/output_type/properties/quantile/properties/output_type_id/properties/required/uniqueItems + keyword params.i params.j + 1 uniqueItems 2 3 + 2 uniqueItems 15 16 + message schema + 1 must NOT have duplicate items (items ## 3 and 2 are identical) TRUE + 2 must NOT have duplicate items (items ## 16 and 15 are identical) TRUE + parentSchema.description + 1 Array of valid but not required unique origin date identifiers. Can be null if all origin dates are required and are specified in the required property. + 2 Array of unique probability levels between 0 and 1 that must be present for submission to be valid. Can be null if no probability levels are required and all valid probability levels are specified in the optional property. + parentSchema.type parentSchema.uniqueItems parentSchema.items.type + 1 array, null TRUE string + 2 array, null TRUE number + parentSchema.items.format parentSchema.items.minimum + 1 date NA + 2 0 + parentSchema.items.maximum + 1 NA + 2 1 + data + 1 2022-10-15, 2022-10-22, 2022-10-29, 2022-10-29 + 2 0.010, 0.025, 0.050, 0.050, 0.100, 0.150, 0.200, 0.250, 0.300, 0.350, 0.400, 0.450, 0.500, 0.550, 0.600, 0.650, 0.650, 0.700, 0.750, 0.800, 0.850, 0.900, 0.950, 0.975, 0.990 + dataPath + 1 /rounds/1/model_tasks/0/task_ids/origin_date/optional + 2 /rounds/1/model_tasks/0/output_type/quantile/output_type_id/required + attr(,"config_path") + [1] "testdata/dup-in-array.json" + attr(,"schema_version") + [1] "v2.0.0" + attr(,"schema_url") + https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json + +# Duplicate values across property error successfully + + Code + out + Output + [1] FALSE + attr(,"config_path") + [1] "testdata/dup-in-property.json" + attr(,"schema_version") + [1] "v2.0.0" + attr(,"schema_url") + https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json + attr(,"errors") + instancePath + 1 /rounds/0/model_tasks/0/task_ids/horizon + 2 /rounds/1/model_tasks/0/task_ids/age_group + 3 /rounds/1/model_tasks/0/output_type/quantile + schemaPath + 1 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/horizon + 2 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/age_group + 3 #/properties/rounds/items/properties/model_tasks/items/properties/output_type/properties/quantile + keyword + 1 task_ids uniqueItems + 2 task_ids uniqueItems + 3 output_type uniqueItems + message + 1 must NOT have duplicate items across 'required' and 'optional' properties. Task ID 'horizon' contains duplicates. + 2 must NOT have duplicate items across 'required' and 'optional' properties. Task ID 'age_group' contains duplicates. + 3 must NOT have duplicate items across 'required' and 'optional' properties. Output type IDs of output type 'quantile' contains duplicates. + schema data + 1 duplicate values: 2 + 2 duplicate values: 65+ + 3 duplicate values: 0.99 + +# Inconsistent round ID variables across model tasks error successfully + + Code + out + Output + [1] FALSE + attr(,"config_path") + [1] "testdata/round-id-inconsistent.json" + attr(,"schema_version") + [1] "v1.0.0" + attr(,"schema_url") + https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v1.0.0/tasks-schema.json + attr(,"errors") + instancePath schemaPath keyword + 1 /rounds/0/model_tasks/1/task_ids/forecast_date NA round_id var + 2 /rounds/0/model_tasks/1/task_ids/forecast_date NA round_id var + message + 1 round_id var 'forecast_date' property MUST be consistent across modeling task items + 2 round_id var 'forecast_date' property MUST be consistent across modeling task items + schema + 1 + 2 + data + 1 Component "optional": Lengths (23, 22) differ (string compare on first 22) compared to first model task item + 2 Component "optional": 1 string mismatch compared to first model task item + +--- + + Code + out + Output + [1] FALSE + attr(,"config_path") + [1] "testdata/round-id-inconsistent2.json" + attr(,"schema_version") + [1] "v2.0.0" + attr(,"schema_url") + https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json + attr(,"errors") + instancePath + 1 /rounds/0/model_tasks/1/task_ids/forecast_date + 2 /rounds/0/model_tasks/1/task_ids/forecast_date + schemaPath + 1 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/forecast_date + 2 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/forecast_date + keyword + 1 round_id var + 2 round_id var + message + 1 round_id var 'forecast_date' property MUST be consistent across modeling task items + 2 round_id var 'forecast_date' property MUST be consistent across modeling task items + schema + 1 + 2 + data + 1 Component "optional": Lengths (23, 22) differ (string compare on first 22) compared to first model task item + 2 Component "optional": 1 string mismatch compared to first model task item + +# Duplicate round ID values across rounds error successfully + + Code + out + Output + [1] FALSE + attr(,"config_path") + [1] "testdata/dup-in-round-id.json" + attr(,"schema_version") + [1] "v2.0.0" + attr(,"schema_url") + https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json + attr(,"errors") + instancePath + 1 /rounds/1/model_tasks/0/task_ids/origin_date + 2 /rounds/1/model_tasks/0/task_ids/origin_date + 3 /rounds/2/model_tasks/0/task_ids/origin_date + 4 /rounds/2/model_tasks/0/task_ids/origin_date + 5 /rounds/2/model_tasks/0/task_ids/origin_date + schemaPath + 1 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/origin_date + 2 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/origin_date + 3 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/origin_date + 4 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/origin_date + 5 #/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/origin_date + keyword + 1 round_id uniqueItems + 2 round_id uniqueItems + 3 round_id uniqueItems + 4 round_id uniqueItems + 5 round_id uniqueItems + message schema + 1 must NOT contains duplicate round ID values across rounds + 2 must NOT contains duplicate round ID values across rounds + 3 must NOT contains duplicate round ID values across rounds + 4 must NOT contains duplicate round ID values across rounds + 5 must NOT contains duplicate round ID values across rounds + data + 1 duplicate value: 2022-10-08 + 2 duplicate value: 2022-10-15 + 3 duplicate value: 2022-10-15 + 4 duplicate value: 2022-10-22 + 5 duplicate value: 2022-10-29 + diff --git a/tests/testthat/_snaps/validate_model_metadata_schema.md b/tests/testthat/_snaps/validate_model_metadata_schema.md new file mode 100644 index 0000000..90eef5e --- /dev/null +++ b/tests/testthat/_snaps/validate_model_metadata_schema.md @@ -0,0 +1,13 @@ +# validate_model_metadata_schema works + + Code + str(attr(out_error, "errors")) + Output + 'data.frame': 1 obs. of 6 variables: + $ instancePath: chr "" + $ schemaPath : chr "properties" + $ keyword : chr "required" + $ message : chr "must have required properties: either 'model_id' or both 'team_abbr' and 'model_abbr'." + $ schema : chr "" + $ data : chr "team_name, model_name, model_abbr, model_version, model_contributors, website_url, repo_url, license, designate"| __truncated__ + diff --git a/tests/testthat/_snaps/view_config_val_errors.md b/tests/testthat/_snaps/view_config_val_errors.md new file mode 100644 index 0000000..96ec664 --- /dev/null +++ b/tests/testthat/_snaps/view_config_val_errors.md @@ -0,0 +1,162 @@ +# Errors report launch successful + + Code + tbl$`_source_notes` + Output + [[1]] + [1] "For more information, please consult the\n [**`hubDocs` documentation**.](https://hubdocs.readthedocs.io/en/latest/)" + attr(,"class") + [1] "from_markdown" + + +--- + + Code + tbl$`_heading` + Output + $title + [1] "**`hubUtils` config validation error report**" + attr(,"class") + [1] "from_markdown" + + $subtitle + [1] "Report for file **`testdata/tasks-errors.json`** using\nschema version [**v0.0.0.9**](https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v0.0.0.9/tasks-schema.json)" + attr(,"class") + [1] "from_markdown" + + $preheader + NULL + + +--- + + Code + str(tbl$`_data`) + Output + tibble [3 x 6] (S3: tbl_df/tbl/data.frame) + $ instancePath: chr [1:3] "**rounds** \n └**1** \n └─**model_tasks** \n └──**1** \n └───**task_ids** \n └────**target** \n └─────**required**" "**rounds** \n └**1** \n └─**model_tasks** \n └──**1** \n └───**output_type** \n └────**mean** \n └─────**type_id**" "**rounds** \n └**1** \n └─**submissions_due**" + $ schemaPath : chr [1:3] "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────properties "| __truncated__ "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────properties "| __truncated__ "properties \n └**rounds** \n └─items \n └──properties \n └───**submissions_due** \n └────**oneOf**" + $ keyword : chr [1:3] "type" "oneOf" "oneOf" + $ message : chr [1:3] "❌ must be array,null" "❌ must match exactly one schema in oneOf" "❌ must match exactly one schema in oneOf" + $ schema : chr [1:3] "array, null" "**1** \n **required-description:** When mean is required, property set to single element 'NA' array \n **requir"| __truncated__ "**1** \n **relative_to-description:** Name of task id variable in relation to which submission start and end da"| __truncated__ + $ data : chr [1:3] "wk inc flu hosp" "required: NA, optional: NA" "start: -6, end: 1" + +--- + + Code + tbl$`_styles` + Output + # A tibble: 18 x 7 + locname grpname colname locnum rownum colnum styles + + 1 data instancePath 5 1 NA + 2 data schemaPath 5 1 NA + 3 data schema 5 1 NA + 4 data instancePath 5 2 NA + 5 data schemaPath 5 2 NA + 6 data schema 5 2 NA + 7 data instancePath 5 3 NA + 8 data schemaPath 5 3 NA + 9 data schema 5 3 NA + 10 data schema 5 1 NA + 11 data schema 5 2 NA + 12 data schema 5 3 NA + 13 data message 5 1 NA + 14 data data 5 1 NA + 15 data message 5 2 NA + 16 data data 5 2 NA + 17 data message 5 3 NA + 18 data data 5 3 NA + +# length 1 paths and related type & enum errors handled correctly + + Code + str(tbl$`_data`) + Output + tibble [2 x 6] (S3: tbl_df/tbl/data.frame) + $ instancePath: chr [1:2] "**file_format** \n └**1**" "**timezone**" + $ schemaPath : chr [1:2] "properties \n └**file_format** \n └─items \n └──**enum**" "properties \n └**timezone** \n └─**type**" + $ keyword : chr [1:2] "enum" "type" + $ message : chr [1:2] "❌ must be equal to one of the allowed values" "❌ must be string" + $ schema : chr [1:2] "csv, parquet, arrow" "string" + $ data : chr [1:2] "csvs" "US/Eastern" + +# Data column handled correctly when required property missing + + Code + str(tbl$`_data`) + Output + tibble [2 x 6] (S3: tbl_df/tbl/data.frame) + $ instancePath: chr [1:2] "**rounds** \n └**1** \n └─**model_tasks** \n └──**1**" "**rounds** \n └**1** \n └─**model_tasks** \n └──**1** \n └───**output_type** \n └────**mean** \n └─────**value*"| __truncated__ + $ schemaPath : chr [1:2] "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────**required**" "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────properties "| __truncated__ + $ keyword : chr [1:2] "required" "type" + $ message : chr [1:2] "❌ must have required property 'target_metadata'" "❌ must be number,integer" + $ schema : chr [1:2] "task_ids, output_type, target_metadata" "number, integer" + $ data : chr [1:2] "" "0" + +--- + + Code + str(tbl$`_data`) + Output + tibble [1 x 6] (S3: tbl_df/tbl/data.frame) + $ instancePath: chr "**rounds** \n └**1** \n └─**model_tasks** \n └──**1**" + $ schemaPath : chr "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────**required**" + $ keyword : chr "required" + $ message : chr "❌ must have required property 'target_metadata'" + $ schema : chr "task_ids, output_type, target_metadata" + $ data : chr "" + +--- + + Code + str(tbl$`_data`) + Output + tibble [2 x 6] (S3: tbl_df/tbl/data.frame) + $ instancePath: chr [1:2] "**rounds** \n └**1**" "**rounds** \n └**1** \n └─**model_tasks** \n └──**1**" + $ schemaPath : chr [1:2] "properties \n └**rounds** \n └─items \n └──**required**" "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────**required**" + $ keyword : chr [1:2] "required" "required" + $ message : chr [1:2] "❌ must have required property 'round_id_from_variable'" "❌ must have required property 'target_metadata'" + $ schema : chr [1:2] "round_id_from_variable, round_id, model_tasks, submissions_due" "task_ids, output_type, target_metadata" + $ data : chr [1:2] "" "" + +--- + + Code + str(tbl$`_data`) + Output + tibble [2 x 6] (S3: tbl_df/tbl/data.frame) + $ instancePath: chr [1:2] "**rounds** \n └**1** \n └─**model_tasks** \n └──**1**" "**rounds** \n └**1** \n └─**model_tasks** \n └──**1**" + $ schemaPath : chr [1:2] "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────**required**" "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────**required**" + $ keyword : chr [1:2] "required" "required" + $ message : chr [1:2] "❌ must have required property 'output_type'" "❌ must have required property 'target_metadata'" + $ schema : chr [1:2] "task_ids, output_type, target_metadata" "task_ids, output_type, target_metadata" + $ data : chr [1:2] "" "" + +# Report handles additional property errors successfully + + Code + str(tbl$`_data`) + Output + tibble [1 x 6] (S3: tbl_df/tbl/data.frame) + $ instancePath: chr "**rounds** \n └**1** \n └─**model_tasks** \n └──**1** \n └───**output_type**" + $ schemaPath : chr "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────properties "| __truncated__ + $ keyword : chr "additionalProperties" + $ message : chr "❌ must NOT have additional properties" + $ schema : chr "FALSE" + $ data : chr "" + +# Report works corectly on validate_hub_config output + + Code + str(tbl$`_data`) + Output + tibble [5 x 7] (S3: tbl_df/tbl/data.frame) + $ fileName : chr [1:5] "tasks.json" "tasks.json" "tasks.json" "tasks.json" ... + $ instancePath: chr [1:5] "**rounds** \n └**1** \n └─**model_tasks** \n └──**1**" "**rounds** \n └**1** \n └─**model_tasks** \n └──**1** \n └───**task_ids** \n └────**target** \n └─────**required**" "**rounds** \n └**1** \n └─**model_tasks** \n └──**1** \n └───**output_type** \n └────**mean** \n └─────**output_type_id**" "**rounds** \n └**1** \n └─**submissions_due**" ... + $ schemaPath : chr [1:5] "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────**required**" "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────properties "| __truncated__ "properties \n └**rounds** \n └─items \n └──properties \n └───**model_tasks** \n └────items \n └─────properties "| __truncated__ "properties \n └**rounds** \n └─items \n └──properties \n └───**submissions_due** \n └────**oneOf**" ... + $ keyword : chr [1:5] "required" "type" "oneOf" "oneOf" ... + $ message : chr [1:5] "❌ must have required property 'target_metadata'" "❌ must be array,null" "❌ must match exactly one schema in oneOf" "❌ must match exactly one schema in oneOf" ... + $ schema : chr [1:5] "task_ids, output_type, target_metadata" "array, null" "**1** \n **required-description:** When mean is required, property set to single element 'NA' array \n **requir"| __truncated__ "**1** \n **relative_to-description:** Name of task id variable in relation to which submission start and end da"| __truncated__ ... + $ data : chr [1:5] "" "wk inc flu hosp" "required: NA, optional: NA" "start: -6, end: 1" ... + diff --git a/tests/testthat/helper-view-val-tbl.R b/tests/testthat/helper-view-val-tbl.R new file mode 100644 index 0000000..4d50e09 --- /dev/null +++ b/tests/testthat/helper-view-val-tbl.R @@ -0,0 +1,113 @@ +expect_tab <- function(tab) { + # Expect that the object has the correct classes + expect_s3_class(tab, "gt_tbl") + expect_type(tab, "list") + + # Expect certain named attributes + expect_gt_attr_names(object = tab) + + # Expect that the attribute obejcts are of certain classes + expect_s3_class(gt:::dt_boxhead_get(data = tab), "data.frame") + expect_type(gt:::dt_stub_df_get(data = tab), "list") + expect_type(gt:::dt_row_groups_get(data = tab), "character") + expect_s3_class(gt:::dt_stub_df_get(data = tab), "data.frame") + expect_type(gt:::dt_heading_get(data = tab), "list") + expect_s3_class(gt:::dt_spanners_get(data = tab), "data.frame") + expect_type(gt:::dt_stubhead_get(data = tab), "list") + expect_s3_class(gt:::dt_footnotes_get(data = tab), "data.frame") + expect_type(gt:::dt_source_notes_get(data = tab), "list") + expect_type(gt:::dt_formats_get(data = tab), "list") + expect_type(gt:::dt_substitutions_get(data = tab), "list") + expect_s3_class(gt:::dt_styles_get(data = tab), "data.frame") + expect_s3_class(gt:::dt_options_get(data = tab), "data.frame") + expect_type(gt:::dt_transforms_get(data = tab), "list") + + (gt:::dt_boxhead_get(data = tab) %>% + dim())[2] %>% # nolint: indentation_linter + expect_equal(8) + + gt:::dt_stub_df_get(data = tab) %>% + dim() %>% + expect_equal(c(3, 6)) + + gt:::dt_heading_get(data = tab) %>% + length() %>% + expect_equal(3) + + gt:::dt_spanners_get(data = tab) %>% + dim() %>% + expect_equal(c(3, 8)) + + gt:::dt_stubhead_get(data = tab) %>% + length() %>% + expect_equal(1) + + gt:::dt_footnotes_get(data = tab) %>% + dim() %>% + expect_equal(c(0, 8)) + + gt:::dt_source_notes_get(data = tab) %>% + length() %>% + expect_equal(1) + + gt:::dt_formats_get(data = tab) %>% + length() %>% + expect_equal(1) + + gt:::dt_substitutions_get(data = tab) %>% + length() %>% + expect_equal(0) + + gt:::dt_styles_get(data = tab) %>% + dim() %>% + expect_equal(c(18, 7)) + + gt:::dt_transforms_get(data = tab) %>% + length() %>% + expect_equal(0) + + + # Expect that extracted df has the same number of + # rows as the original dataset + expect_equal( + tab %>% gt:::dt_data_get() %>% nrow(), + 3 + ) + + # Expect specific column names within the `stub_df` object + expect_equal( + colnames(gt:::dt_stub_df_get(data = tab)), + c( + "rownum_i", "row_id", "group_id", "group_label", + "indent", "built_group_label" + ) + ) +} + + +gt_attr_names <- function() { + c( + "_data", "_boxhead", + "_stub_df", "_row_groups", + "_heading", "_spanners", "_stubhead", + "_footnotes", "_source_notes", "_formats", "_substitutions", "_styles", + "_summary", "_options", "_transforms", "_locale", "_has_built" + ) +} + +expect_gt_attr_names <- function(object) { + # The `groups` attribute appears when we call dplyr::group_by() + # on the input table + expect_equal( + sort(names(object)), + sort(gt_attr_names()) + ) +} + + +recode_font <- function(tbl) { + tbl_font_nm_idx <- which(tbl$`_options`$parameter == "table_font_names") + tbl$`_options`$value[[tbl_font_nm_idx]] <- "test-font" + + tbl +} diff --git a/tests/testthat/test-config-schema-utils.R b/tests/testthat/test-config-schema-utils.R new file mode 100644 index 0000000..11603ab --- /dev/null +++ b/tests/testthat/test-config-schema-utils.R @@ -0,0 +1,19 @@ +test_that("version from file handled successfully", { + expect_equal( + get_config_file_schema_version( + system.file( + "testhubs/simple/hub-config/tasks.json", + package = "hubUtils" + ), + config = "tasks" + ), + "v2.0.0" + ) + + expect_error( + get_config_file_schema_version( + testthat::test_path("testdata", "empty.json"), + config = "tasks" + ) + ) +}) diff --git a/tests/testthat/test-create_config.R b/tests/testthat/test-create_config.R new file mode 100644 index 0000000..8681aa8 --- /dev/null +++ b/tests/testthat/test-create_config.R @@ -0,0 +1,65 @@ +rounds <- create_rounds( + create_round( + round_id_from_variable = TRUE, + round_id = "origin_date", + model_tasks = create_model_tasks( + create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = NULL, + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ) + ), + submissions_due = list( + relative_to = "origin_date", + start = -4L, + end = 2L + ) + ) +) + +test_that("create_config functions work correctly", { + expect_snapshot( + create_config(rounds) + ) +}) + + +test_that("create_config functions error correctly", { + expect_snapshot( + create_config(list(a = 10)), + error = TRUE + ) +}) diff --git a/tests/testthat/test-create_model_task.R b/tests/testthat/test-create_model_task.R new file mode 100644 index 0000000..8971ffe --- /dev/null +++ b/tests/testthat/test-create_model_task.R @@ -0,0 +1,228 @@ +test_that("create_model_task functions work correctly", { + expect_snapshot( + create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = NULL, + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ) + ) + + expect_snapshot( + create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("target", + required = NULL, + optional = c("inc death", "inc hosp") + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ), + create_output_type_median( + is_required = FALSE, + value_type = "double" + ), + create_output_type_quantile( + required = c(0.25, 0.5, 0.75), + optional = c( + 0.1, 0.2, 0.3, 0.4, 0.6, + 0.7, 0.8, 0.9 + ), + value_type = "double", + value_minimum = 0 + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ), + create_target_metadata_item( + target_id = "inc death", + target_name = "Weekly incident influenza deaths", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc death"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ) + ) +}) + +task_ids <- create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("target", + required = NULL, + optional = c("inc death", "inc hosp") + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) +) +output_type <- create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ) +) + +test_that("create_output_type_point functions error correctly", { + expect_snapshot( + create_model_task( + task_ids = task_ids, + output_type = output_type, + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ), + error = TRUE + ) + expect_snapshot( + create_model_task( + task_ids = task_ids, + output_type = output_type, + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = list(targets = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ), + error = TRUE + ) + + expect_snapshot( + create_model_task( + task_ids = task_ids, + output_type = list(a = 10), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = list(targets = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ), + error = TRUE + ) + + task_ids <- create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ) + + attr(task_ids, "schema_id") <- "invalid_schema_id" + expect_snapshot( + create_model_task( + task_ids = task_ids, + output_type = output_type, + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = NULL, + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ), + error = TRUE + ) +}) diff --git a/tests/testthat/test-create_model_tasks.R b/tests/testthat/test-create_model_tasks.R new file mode 100644 index 0000000..c6fc5a7 --- /dev/null +++ b/tests/testthat/test-create_model_tasks.R @@ -0,0 +1,293 @@ +test_that("create_model_tasks functions work correctly", { + expect_snapshot( + create_model_tasks( + create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = NULL, + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ) + ) + ) + + expect_snapshot( + create_model_tasks( + create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("target", + required = NULL, + optional = c("inc death", "inc hosp") + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ), + create_output_type_median( + is_required = FALSE, + value_type = "double" + ), + create_output_type_quantile( + required = c(0.25, 0.5, 0.75), + optional = c( + 0.1, 0.2, 0.3, 0.4, 0.6, + 0.7, 0.8, 0.9 + ), + value_type = "double", + value_minimum = 0 + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ), + create_target_metadata_item( + target_id = "inc death", + target_name = "Weekly incident influenza deaths", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc death"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ), + create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("target", + required = "flu hosp rt chng", + optional = NULL + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_pmf( + required = c( + "large_decrease", + "decrease", + "stable", + "increase", + "large_increase" + ), + optional = NULL, + value_type = "double" + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "flu hosp rt chng", + target_name = "Weekly influenza hospitalization rate change", + target_units = "rate per 100,000 population", + target_keys = list(target = "flu hosp rt chng"), + target_type = "nominal", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ) + ) + ) +}) + + + +test_that("create_model_tasks functions error correctly", { + model_task_1 <- create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("target", + required = NULL, + optional = c("inc death", "inc hosp") + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ), + create_output_type_median( + is_required = FALSE, + value_type = "double" + ), + create_output_type_quantile( + required = c(0.25, 0.5, 0.75), + optional = c( + 0.1, 0.2, 0.3, 0.4, 0.6, + 0.7, 0.8, 0.9 + ), + value_type = "double", + value_minimum = 0 + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ), + create_target_metadata_item( + target_id = "inc death", + target_name = "Weekly incident influenza deaths", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc death"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ) + expect_snapshot( + create_model_tasks( + model_task_1, + list(a = 10) + ), + error = TRUE + ) + + attr(model_task_1, "schema_id") <- "invalid_schema_id" + expect_snapshot( + create_model_tasks( + model_task_1, + create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("target", + required = "flu hosp rt chng", + optional = NULL + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_pmf( + required = c( + "large_decrease", + "decrease", + "stable", + "increase", + "large_increase" + ), + optional = NULL, + value_type = "double" + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "flu hosp rt chng", + target_name = "Weekly influenza hospitalization rate change", + target_units = "rate per 100,000 population", + target_keys = list(target = "flu hosp rt chng"), + target_type = "nominal", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ) + ), + error = TRUE + ) +}) diff --git a/tests/testthat/test-create_output_type.R b/tests/testthat/test-create_output_type.R new file mode 100644 index 0000000..aa44600 --- /dev/null +++ b/tests/testthat/test-create_output_type.R @@ -0,0 +1,54 @@ +test_that("create_output_type functions work correctly", { + expect_snapshot( + create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ), + create_output_type_median( + is_required = FALSE, + value_type = "double" + ), + create_output_type_quantile( + required = c(0.25, 0.5, 0.75), + optional = c( + 0.1, 0.2, 0.3, 0.4, 0.6, + 0.7, 0.8, 0.9 + ), + value_type = "double", + value_minimum = 0 + ) + ) + ) +}) + + +test_that("create_output_type functions error correctly", { + expect_snapshot( + create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ), + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ) + ), + error = TRUE + ) + expect_snapshot( + create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ), + list(a = "b") + ), + error = TRUE + ) +}) diff --git a/tests/testthat/test-create_output_type_item.R b/tests/testthat/test-create_output_type_item.R new file mode 100644 index 0000000..cb240ea --- /dev/null +++ b/tests/testthat/test-create_output_type_item.R @@ -0,0 +1,178 @@ +test_that("create_output_type_point functions work correctly", { + expect_snapshot( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ) + ) + expect_snapshot( + create_output_type_mean( + is_required = FALSE, + value_type = "integer", + value_maximum = 0L + ) + ) + expect_snapshot( + create_output_type_median( + is_required = FALSE, + value_type = "double" + ) + ) + # Test back-compatibility + expect_snapshot( + create_output_type_median( + is_required = FALSE, + value_type = "double", + schema_version = "v1.0.0" + ) + ) +}) + + +test_that("create_output_type_point functions error correctly", { + expect_snapshot( + create_output_type_mean( + is_required = "TRUE", + value_type = "double" + ), + error = TRUE + ) + expect_snapshot( + create_output_type_mean( + is_required = TRUE, + value_type = c("double", "integer") + ), + error = TRUE + ) + expect_snapshot( + create_output_type_mean( + is_required = FALSE, + value_type = "character", + value_maximum = 0L + ), + error = TRUE + ) + expect_snapshot( + create_output_type_median(is_required = FALSE), + error = TRUE + ) +}) + + +test_that("create_output_type_dist functions work correctly", { + expect_snapshot( + create_output_type_quantile( + required = c(0.25, 0.5, 0.75), + optional = c( + 0.1, 0.2, 0.3, 0.4, 0.6, + 0.7, 0.8, 0.9 + ), + value_type = "double", + value_minimum = 0 + ) + ) + expect_snapshot( + create_output_type_cdf( + required = c(10, 20), + optional = NULL, + value_type = "double" + ) + ) + expect_snapshot( + create_output_type_cdf( + required = NULL, + optional = c( + "EW202240", + "EW202241", + "EW202242" + ), + value_type = "double" + ) + ) + expect_snapshot( + create_output_type_pmf( + required = NULL, + optional = c( + "low", "moderate", + "high", "extreme" + ), + value_type = "double" + ) + ) + expect_snapshot( + create_output_type_sample( + required = 1:10, optional = 11:15, + value_type = "double" + ) + ) + + # Test back-compatibility + expect_snapshot( + create_output_type_quantile( + required = c(0.25, 0.5, 0.75), + optional = c( + 0.1, 0.2, 0.3, 0.4, 0.6, + 0.7, 0.8, 0.9 + ), + value_type = "double", + value_minimum = 0, + schema_version = "v1.0.0" + ) + ) +}) + + +test_that("create_output_type_dist functions error correctly", { + expect_snapshot( + create_output_type_cdf( + required = NULL, + optional = c( + "EW202240", + "EW202241", + "EW2022423" + ), + value_type = "double" + ), + error = TRUE + ) + expect_snapshot( + create_output_type_quantile( + required = c(0.25, 0.5, 0.6, 0.75), + optional = c( + 0.1, 0.2, 0.3, 0.4, 0.6, + 0.7, 0.8, 0.9 + ), + value_type = "double", + value_minimum = 0 + ), + error = TRUE + ) + expect_snapshot( + create_output_type_sample( + required = 0:10, optional = 11:15, + value_type = "double" + ), + error = TRUE + ) + expect_snapshot( + create_output_type_sample( + required = 1:10, optional = 11:15, + value_type = "character" + ), + error = TRUE + ) +}) + + +test_that("create_output_type_dist functions creates expected warnings", { + expect_snapshot( + create_output_type_sample( + required = 1:50, + optional = NULL, + value_type = "double", + value_minimum = 0L, + value_maximum = 1L + ) + ) +}) diff --git a/tests/testthat/test-create_round.R b/tests/testthat/test-create_round.R new file mode 100644 index 0000000..f884def --- /dev/null +++ b/tests/testthat/test-create_round.R @@ -0,0 +1,140 @@ +model_tasks <- create_model_tasks( + create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = NULL, + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ) +) + +test_that("create_round functions work correctly", { + expect_snapshot( + create_round( + round_id_from_variable = FALSE, + round_id = "round_1", + model_tasks = model_tasks, + submissions_due = list( + start = "2023-01-12", + end = "2023-01-18" + ), + last_data_date = "2023-01-02" + ) + ) + expect_snapshot( + create_round( + round_id_from_variable = TRUE, + round_id = "origin_date", + model_tasks = model_tasks, + submissions_due = list( + relative_to = "origin_date", + start = -4L, + end = 2L + ), + last_data_date = "2023-01-02" + ) + ) +}) + + +test_that("create_round name matching works correctly", { + expect_snapshot( + create_round( + round_id_from_variable = FALSE, + round_id = "round_1", + model_tasks = model_tasks, + submissions_due = list( + start = "01/12/2023", + end = "2023-01-18" + ), + last_data_date = "2023-01-02" + ), + error = TRUE + ) + expect_snapshot( + create_round( + round_id_from_variable = FALSE, + round_id = "round_1", + model_tasks = model_tasks, + submissions_due = list( + start = -4L, + end = 2 + ), + last_data_date = "2023-01-02" + ), + error = TRUE + ) + expect_snapshot( + create_round( + round_id_from_variable = TRUE, + round_id = "origin_dates", + model_tasks = model_tasks, + submissions_due = list( + relative_to = "origin_date", + start = -4L, + end = 2L + ), + last_data_date = "2023-01-02" + ), + error = TRUE + ) + expect_snapshot( + create_round( + round_id_from_variable = TRUE, + round_id = "origin_date", + model_tasks = model_tasks, + submissions_due = list( + relative = "origin_date", + start = -4L, + end = 2L + ), + last_data_date = "2023-01-02" + ), + error = TRUE + ) + expect_snapshot( + create_round( + round_id_from_variable = TRUE, + round_id = "origin_date", + model_tasks = "model_tasks", + submissions_due = list( + relative_to = "origin_date", + start = -4L, + end = 2L + ), + last_data_date = "2023-01-02" + ), + error = TRUE + ) +}) diff --git a/tests/testthat/test-create_rounds.R b/tests/testthat/test-create_rounds.R new file mode 100644 index 0000000..9d83abb --- /dev/null +++ b/tests/testthat/test-create_rounds.R @@ -0,0 +1,178 @@ +model_tasks <- create_model_tasks( + create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = NULL, + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ) +) + +test_that("create_rounds functions work correctly", { + expect_snapshot( + create_rounds( + create_round( + round_id_from_variable = TRUE, + round_id = "origin_date", + model_tasks = model_tasks, + submissions_due = list( + relative_to = "origin_date", + start = -4L, + end = 2L + ) + ) + ) + ) + expect_snapshot( + create_rounds( + create_round( + round_id_from_variable = FALSE, + round_id = "round_1", + model_tasks = + create_model_tasks( + create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-09" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = NULL, + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ) + ), + submissions_due = list( + start = "2023-01-05", + end = "2023-01-11" + ), + last_data_date = "2023-01-06" + ), + create_round( + round_id_from_variable = FALSE, + round_id = "round_2", + model_tasks = + create_model_tasks( + create_model_task( + task_ids = create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-16" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ), + output_type = create_output_type( + create_output_type_mean( + is_required = TRUE, + value_type = "double", + value_minimum = 0L + ) + ), + target_metadata = create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = NULL, + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ) + ), + submissions_due = list( + start = "2023-01-12", + end = "2023-01-18" + ), + last_data_date = "2023-01-13" + ) + ) + ) +}) + +round_1 <- create_round( + round_id_from_variable = FALSE, + round_id = "round_1", + model_tasks = model_tasks, + submissions_due = list( + start = "2023-01-12", + end = "2023-01-18" + ), + last_data_date = "2023-01-10" +) + +test_that("create_round errors correctly", { + expect_snapshot( + create_rounds( + round_1, + round_1 + ), + error = TRUE + ) +}) diff --git a/tests/testthat/test-create_target_metadata.R b/tests/testthat/test-create_target_metadata.R new file mode 100644 index 0000000..fe7a3f4 --- /dev/null +++ b/tests/testthat/test-create_target_metadata.R @@ -0,0 +1,116 @@ +test_that("create_target_metadata functions work correctly", { + expect_snapshot( + create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ), + create_target_metadata_item( + target_id = "inc death", + target_name = "Weekly incident influenza deaths", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc death"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + ) +}) + + +test_that("create_target_metadata functions error correctly", { + expect_snapshot( + create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ), + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ), + error = TRUE + ) + expect_snapshot( + create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ), + create_target_metadata_item( + target_id = "inc death", + target_name = "Weekly incident influenza deaths", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ), + error = TRUE + ) + expect_snapshot( + create_target_metadata( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ), + list(a = 10), + list(b = 10) + ), + error = TRUE + ) + + item_1 <- create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + attr(item_1, "schema_id") <- "invalid_schema" + expect_snapshot( + create_target_metadata( + item_1, + create_target_metadata_item( + target_id = "inc death", + target_name = "Weekly incident influenza deaths", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ), + error = TRUE + ) +}) diff --git a/tests/testthat/test-create_target_metadata_item.R b/tests/testthat/test-create_target_metadata_item.R new file mode 100644 index 0000000..81c41c6 --- /dev/null +++ b/tests/testthat/test-create_target_metadata_item.R @@ -0,0 +1,103 @@ +test_that("create_target_metadata_item functions work correctly", { + expect_snapshot( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ) + ) + expect_snapshot( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_type = "discrete", + is_step_ahead = FALSE + ) + ) +}) + + + +test_that("create_target_metadata_item functions error correctly", { + expect_snapshot( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = list(target = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "weekly" + ), + error = TRUE + ) + expect_snapshot( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = list(target = c("inc hosp", "inc death")), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ), + error = TRUE + ) + expect_snapshot( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_keys = c(target = "inc hosp"), + target_type = "discrete", + is_step_ahead = TRUE, + time_unit = "week" + ), + error = TRUE + ) + expect_snapshot( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_type = "discrete", + is_step_ahead = TRUE + ), + error = TRUE + ) + expect_snapshot( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = 100000, + target_type = "discrete", + is_step_ahead = FALSE + ), + error = TRUE + ) + expect_snapshot( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = "rate per 100,000 population", + target_type = "invalid_target_type", + is_step_ahead = FALSE + ), + error = TRUE + ) + expect_snapshot( + create_target_metadata_item( + target_id = "inc hosp", + target_name = "Weekly incident influenza hospitalizations", + target_units = c("rate per 100,000 population", "count"), + target_type = "discrete", + is_step_ahead = FALSE + ), + error = TRUE + ) +}) diff --git a/tests/testthat/test-create_task_id.R b/tests/testthat/test-create_task_id.R new file mode 100644 index 0000000..fd95c24 --- /dev/null +++ b/tests/testthat/test-create_task_id.R @@ -0,0 +1,91 @@ +test_that("create_task_id works correctly", { + expect_snapshot(create_task_id("horizon", + required = 1L, + optional = 2:4 + )) + + expect_snapshot(create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + )) + expect_snapshot(create_task_id("scenario_id", + required = NULL, + optional = c( + "A-2021-03-28", + "B-2021-03-28" + ) + )) + expect_snapshot(create_task_id("scenario_id", + required = NULL, + optional = c( + 1L, + 2L + ) + )) +}) + + +test_that("create_task_id errors correctly", { + expect_snapshot( + create_task_id("horizon", + required = NULL, + optional = NULL + ), + error = TRUE + ) + + expect_snapshot( + create_task_id("origin_date", + required = NULL, + optional = c("01/20/2023") + ), + error = TRUE + ) + expect_snapshot( + create_task_id("scenario_id", + required = NULL, + optional = c( + 1L, + 1L + ) + ), + error = TRUE + ) + expect_snapshot( + create_task_id("horizon", + required = c(TRUE, FALSE), + optional = NULL + ), + error = TRUE + ) +}) + +test_that("create_task_id name matching works correctly", { + expect_equal( + names( + suppressMessages( + create_task_id("scenario_ids", + required = NULL, + optional = c( + "A-2021-03-28", + "B-2021-03-28" + ) + ) + ) + ), + "scenario_id" + ) + + mockery::stub(match_element_name, "utils::askYesNo", FALSE) + expect_equal( + match_element_name("scenario_ids", "scenario_id", "task_id"), + "scenario_ids" + ) + + mockery::stub(match_element_name, "utils::askYesNo", NA) + expect_error(match_element_name("scenario_ids", "scenario_id", "task_id")) +}) diff --git a/tests/testthat/test-create_task_ids.R b/tests/testthat/test-create_task_ids.R new file mode 100644 index 0000000..0641e90 --- /dev/null +++ b/tests/testthat/test-create_task_ids.R @@ -0,0 +1,97 @@ +test_that("create_task_ids functions work correctly", { + expect_snapshot( + create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("scenario_id", + required = NULL, + optional = c( + "A-2021-03-28", + "B-2021-03-28" + ) + ), + create_task_id("location", + required = "US", + optional = c("01", "02", "04", "05", "06") + ), + create_task_id("target", + required = "inc hosp", + optional = NULL + ), + create_task_id("horizon", + required = 1L, + optional = 2:4 + ) + ) + ) +}) + + +test_that("create_task_ids functions error correctly", { + expect_snapshot( + create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ) + ), + error = TRUE + ) + + expect_snapshot( + create_task_ids( + create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ), + list(a = 10), + list(b = 10) + ), + error = TRUE + ) + + item_1 <- create_task_id("origin_date", + required = NULL, + optional = c( + "2023-01-02", + "2023-01-09", + "2023-01-16" + ) + ) + attr(item_1, "schema_id") <- "invalid_schema" + expect_snapshot( + create_task_ids( + item_1, + create_task_id("scenario_id", + required = NULL, + optional = c( + "A-2021-03-28", + "B-2021-03-28" + ) + ) + ), + error = TRUE + ) +}) diff --git a/tests/testthat/test-get_error_path.R b/tests/testthat/test-get_error_path.R new file mode 100644 index 0000000..c183b7e --- /dev/null +++ b/tests/testthat/test-get_error_path.R @@ -0,0 +1,107 @@ +test_that("Creating schema paths works correctly", { + schema <- hubUtils::get_schema( + "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v1.0.0/tasks-schema.json" + ) + expect_equal( + get_error_path(schema, "mean", "schema"), + "#/properties/rounds/items/properties/model_tasks/items/properties/output_type/properties/mean" + ) + expect_equal( + get_error_path(schema, "origin_date", "schema"), + "#/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/origin_date" + ) + expect_equal( + get_error_path(schema, "horizon", "schema", append_item_n = TRUE), + "#/properties/rounds/items/properties/model_tasks/items/properties/task_ids/properties/horizon" + ) + # Test that custom_task_id does not return schema path with version < v2.0.0 + expect_equal( + get_error_path(schema, "custom_task_id", "schema"), + NA + ) + # Test that custom_task_id returns task_ids/additionalProperties schema path with version >= v2.0.0 + schema <- hubUtils::get_schema( + "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/br-v2.0.0/v2.0.0/tasks-schema.json" + ) + expect_equal( + get_error_path(schema, "custom_task_id", "schema"), + "#/properties/rounds/items/properties/model_tasks/items/properties/task_ids/additionalProperties" + ) + + # Test that output_type_id schema path returned correctly with version >= v2.0.0 + expect_equal( + get_error_path(schema, "mean/properties/output_type_id", "schema"), + "#/properties/rounds/items/properties/model_tasks/items/properties/output_type/properties/mean/properties/output_type_id" # nolint: line_length_linter + ) +}) + +test_that("Creating instance paths works correctly", { + schema <- hubUtils::get_schema( + "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v1.0.0/tasks-schema.json" + ) + round_i <- 1L + model_task_i <- 2L + target_key_i <- 1L + + # Test that output type paths created correctly + expect_equal( + glue::glue(get_error_path(schema, "mean", "instance")), + "/rounds/0/model_tasks/1/output_type/mean" + ) + + # Test that std tasks ID paths created correctly, including append_item_n value + # not affecting the final output + expect_equal( + glue::glue(get_error_path(schema, "origin_date", "instance")), + "/rounds/0/model_tasks/1/task_ids/origin_date" + ) + expect_equal( + glue::glue(get_error_path(schema, "origin_date", "instance", + append_item_n = TRUE + )), + "/rounds/0/model_tasks/1/task_ids/origin_date" + ) + + # Test that appending item n works + expect_equal( + glue::glue(get_error_path(schema, "rounds", "instance")), + "/rounds" + ) + expect_equal( + glue::glue(get_error_path(schema, "rounds", "instance", + append_item_n = TRUE + )), + "/rounds/0" + ) + expect_equal( + glue::glue(get_error_path(schema, "model_tasks", "instance", + append_item_n = TRUE + )), + "/rounds/0/model_tasks/1" + ) + expect_equal( + glue::glue(get_error_path(schema, "target_metadata", "instance", + append_item_n = TRUE + )), + "/rounds/0/model_tasks/1/target_metadata/0" + ) + + # Test that custom tasks ID paths created correctly, including append_item_n value + # not affecting the final output + expect_equal( + glue::glue(get_error_path(schema, "custom_task_id", "instance")), + "/rounds/0/model_tasks/1/task_ids/custom_task_id" + ) + expect_equal( + glue::glue(get_error_path(schema, "custom_task_id", "instance", + append_item_n = TRUE + )), + "/rounds/0/model_tasks/1/task_ids/custom_task_id" + ) + + # Test that custom tasks ID paths created correctly + expect_equal( + glue::glue(get_error_path(schema, "target_keys", "instance")), + "/rounds/0/model_tasks/1/target_metadata/0/target_keys" + ) +}) diff --git a/tests/testthat/test-validate-inst-hubs.R b/tests/testthat/test-validate-inst-hubs.R new file mode 100644 index 0000000..34b5599 --- /dev/null +++ b/tests/testthat/test-validate-inst-hubs.R @@ -0,0 +1,31 @@ +test_that("simple example hub configured correctly", { + expect_true( + all( + unlist( + suppressMessages( + validate_hub_config( + hub_path = system.file("testhubs/simple", + package = "hubUtils" + ) + ) + ) + ) + ) + ) +}) + +test_that("flusight example hub configured correctly", { + expect_true( + all( + unlist( + suppressMessages( + validate_hub_config( + hub_path = system.file("testhubs/flusight", + package = "hubUtils" + ) + ) + ) + ) + ) + ) +}) diff --git a/tests/testthat/test-validate_config.R b/tests/testthat/test-validate_config.R new file mode 100644 index 0000000..4f309ff --- /dev/null +++ b/tests/testthat/test-validate_config.R @@ -0,0 +1,103 @@ +test_that("Config validated successfully", { + expect_true(suppressMessages(validate_config( + hub_path = system.file( + "testhubs/simple/", + package = "hubUtils" + ), + config = "tasks" + ))) + # Test deprecated version + expect_warning(suppressMessages(validate_config( + hub_path = system.file( + "testhubs/simple-dprc/", + package = "hubUtils" + ), + config = "tasks" + ))) + expect_true(suppressWarnings(suppressMessages(validate_config( + hub_path = system.file( + "testhubs/simple-dprc/", + package = "hubUtils" + ), + config = "tasks" + )))) +}) + + +test_that("Config errors detected successfully", { + config_path <- testthat::test_path("testdata", "tasks-errors.json") + out <- suppressWarnings(validate_config(config_path = config_path)) + expect_snapshot(out) + expect_false(out) +}) + + + +test_that("Dynamic config errors detected successfully by custom R validation", { + config_path <- testthat::test_path("testdata", "tasks-errors-rval.json") + out <- suppressWarnings(validate_config(config_path = config_path)) + expect_snapshot(out) + expect_false(out) +}) + +test_that("Reserved hub variable task id name detected correctly", { + config_path <- testthat::test_path("testdata", "tasks-errors-rval-reserved.json") + out <- suppressWarnings(validate_config(config_path = config_path)) + expect_snapshot(out) + expect_false(out) +}) + +test_that("NULL target keys validated successfully", { + config_path <- testthat::test_path("testdata", "tasks_null_rval.json") + out <- suppressMessages(validate_config(config_path = config_path)) + expect_true(out) +}) + + +test_that("Bad schema_version URL errors successfully", { + config_path <- testthat::test_path("testdata", "schema_version-errors.json") + expect_error(validate_config(config_path = config_path)) +}) + + +test_that("Additional properties error successfully", { + config_path <- testthat::test_path("testdata", "tasks-addprop.json") + out <- suppressWarnings(validate_config(config_path = config_path)) + expect_snapshot(out) + expect_false(out) +}) + + +test_that("Duplicate values in individual array error successfully", { + config_path <- testthat::test_path("testdata", "dup-in-array.json") + out <- suppressWarnings(validate_config(config_path = config_path)) + expect_snapshot(out) + expect_false(out) +}) + +test_that("Duplicate values across property error successfully", { + config_path <- testthat::test_path("testdata", "dup-in-property.json") + out <- suppressWarnings(validate_config(config_path = config_path)) + expect_snapshot(out) + expect_false(out) +}) + +test_that("Inconsistent round ID variables across model tasks error successfully", { + config_path <- testthat::test_path("testdata", "round-id-inconsistent.json") + out <- suppressWarnings(validate_config(config_path = config_path)) + expect_snapshot(out) + expect_false(out) + + config_path <- testthat::test_path("testdata", "round-id-inconsistent2.json") + out <- suppressWarnings(validate_config(config_path = config_path)) + expect_snapshot(out) + expect_false(out) +}) + + +test_that("Duplicate round ID values across rounds error successfully", { + config_path <- testthat::test_path("testdata", "dup-in-round-id.json") + out <- suppressWarnings(validate_config(config_path = config_path)) + expect_snapshot(out) + expect_false(out) +}) diff --git a/tests/testthat/test-validate_hub_config.R b/tests/testthat/test-validate_hub_config.R new file mode 100644 index 0000000..54905c2 --- /dev/null +++ b/tests/testthat/test-validate_hub_config.R @@ -0,0 +1,37 @@ +test_that("correct hub validates successfully", { + expect_true( + validate_hub_config(hub_path = system.file( + "testhubs/simple/", + package = "hubUtils" + )) %>% + unlist() %>% + all() %>% + suppressMessages() + ) +}) + +test_that("Hub with config errors fails validation", { + val <- validate_hub_config( + hub_path = testthat::test_path( + "testdata", "error_hub" + ) + ) %>% + suppressWarnings() + + expect_false(all(unlist(val))) + + expect_equal( + attr(val, "config_dir"), + fs::path("testdata/error_hub/hub-config") + ) + + expect_equal( + attr(val, "schema_url"), + "https://github.com/Infectious-Disease-Modeling-Hubs/schemas/tree/main/v2.0.0" + ) + + expect_equal( + attr(val, "schema_version"), + "v2.0.0" + ) +}) diff --git a/tests/testthat/test-validate_model_metadata_schema.R b/tests/testthat/test-validate_model_metadata_schema.R new file mode 100644 index 0000000..634e502 --- /dev/null +++ b/tests/testthat/test-validate_model_metadata_schema.R @@ -0,0 +1,22 @@ +test_that("validate_model_metadata_schema works", { + expect_true( + suppressMessages( + validate_model_metadata_schema( + hub_path = system.file( + "testhubs/simple/", + package = "hubUtils" + ) + ) + ) + ) + + out_error <- suppressWarnings( + validate_model_metadata_schema( + hub_path = testthat::test_path( + "testdata", "error_hub" + ) + ) + ) + expect_false(out_error) + expect_snapshot(str(attr(out_error, "errors"))) +}) diff --git a/tests/testthat/test-view_config_val_errors.R b/tests/testthat/test-view_config_val_errors.R new file mode 100644 index 0000000..dfc4b52 --- /dev/null +++ b/tests/testthat/test-view_config_val_errors.R @@ -0,0 +1,102 @@ +test_that("Errors report launch successful", { + config_path <- testthat::test_path("testdata", "tasks-errors.json") + validation <- suppressWarnings( + validate_config(config_path = config_path) + ) + set.seed(1) + tbl <- view_config_val_errors(validation) + + # Expect that the `gt_tbl` validation object has all of the + # usual components and that they have all of the + # expected dimensions and features + # Tests adapted from gt package tests: + # https://github.com/rstudio/gt/blob/master/tests/testthat/test-gt_object.R + expect_tab(tbl) + expect_snapshot(tbl$`_source_notes`) + expect_snapshot(tbl$`_heading`) + expect_snapshot(str(tbl$`_data`)) + expect_snapshot(tbl$`_styles`) +}) + +test_that("length 1 paths and related type & enum errors handled correctly", { + config_path <- testthat::test_path("testdata", "admin-errors2.json") + # TODO - change branch back to main branch when + validation <- suppressWarnings( + validate_config( + config_path = config_path, config = "admin", + branch = "main", schema_version = "v1.0.0" + ) + ) + set.seed(1) + tbl <- view_config_val_errors(validation) + expect_snapshot(str(tbl$`_data`)) +}) + +test_that("Data column handled correctly when required property missing", { + set.seed(1) + # One nested property missing, one type error + config_path <- testthat::test_path("testdata", "tasks_required_missing.json") + tbl <- view_config_val_errors(suppressWarnings( + validate_config(config_path = config_path) + )) + expect_snapshot(str(tbl$`_data`)) + + # Only a single property missing + config_path <- testthat::test_path("testdata", "tasks_required_missing_only.json") + tbl <- view_config_val_errors(suppressWarnings( + validate_config(config_path = config_path) + )) + + expect_snapshot(str(tbl$`_data`)) + + # Two properties missing, only one nested + config_path <- testthat::test_path("testdata", "tasks_required_missing_only2.json") + tbl <- view_config_val_errors(suppressWarnings( + validate_config(config_path = config_path) + )) + expect_snapshot(str(tbl$`_data`)) + + # Two properties missing, both nested + config_path <- testthat::test_path("testdata", "tasks_required_missing_only2b.json") + tbl <- view_config_val_errors(suppressWarnings( + validate_config(config_path = config_path) + )) + + expect_snapshot(str(tbl$`_data`)) +}) + +test_that("Report handles additional property errors successfully", { + config_path <- testthat::test_path("testdata", "tasks-addprop.json") + out <- suppressWarnings(validate_config(config_path = config_path)) + tbl <- view_config_val_errors(out) + + expect_snapshot(str(tbl$`_data`)) +}) + +# validate_hub_config output ---- + +test_that("Report works corectly on validate_hub_config output", { + config_dir <- system.file( + "testhubs/simple/", + package = "hubUtils" + ) + + tbl <- suppressMessages( + view_config_val_errors( + validate_hub_config(config_dir) + ) + ) + expect_null(tbl) + + + config_dir <- testthat::test_path( + "testdata", "error_hub" + ) + tbl <- suppressWarnings( + view_config_val_errors( + validate_hub_config(config_dir) + ) + ) + + expect_snapshot(str(tbl$`_data`)) +}) diff --git a/tests/testthat/testdata/admin-errors.json b/tests/testthat/testdata/admin-errors.json new file mode 100644 index 0000000..1a2625f --- /dev/null +++ b/tests/testthat/testdata/admin-errors.json @@ -0,0 +1,15 @@ +{ + "name": "Simple Forecast Hub", + "maintainer": "Consortium of Infectious Disease Modeling Hubs", + "contact": { + "name": "Joe Bloggs", + "email": "j.bloggs@cidmh.com" + }, + "repository_url": "https://github.com/Infectious-Disease-Modeling-Hubs/example-simple-forecast-hub", + "hub_models": [{ + "team_abbr": "simple_hub", + "model_abbr": "baseline", + "model_type": "baseline" + }], + "timezone": ["UTC-5"] +} \ No newline at end of file diff --git a/tests/testthat/testdata/admin-errors2.json b/tests/testthat/testdata/admin-errors2.json new file mode 100644 index 0000000..8031af8 --- /dev/null +++ b/tests/testthat/testdata/admin-errors2.json @@ -0,0 +1,17 @@ +{ + "schema_version": "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/admin-schema.json", + "name": "Simple Forecast Hub", + "maintainer": "Consortium of Infectious Disease Modeling Hubs", + "contact": { + "name": "Joe Bloggs", + "email": "j.bloggs@cidmh.com" + }, + "repository_url": "https://github.com/Infectious-Disease-Modeling-Hubs/example-simple-forecast-hub", + "hub_models": [{ + "team_abbr": "simple_hub", + "model_abbr": "baseline", + "model_type": "baseline" + }], + "file_format": ["csvs"], + "timezone": ["US/Eastern"] +} diff --git a/tests/testthat/testdata/dup-in-array.json b/tests/testthat/testdata/dup-in-array.json new file mode 100644 index 0000000..b36a63f --- /dev/null +++ b/tests/testthat/testdata/dup-in-array.json @@ -0,0 +1,298 @@ +{ + "schema_version": "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json", + "rounds": [{ + "round_id_from_variable": true, + "round_id": "origin_date", + "model_tasks": [{ + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-01", "2022-10-08", "2022-10-15"] + }, + "target": { + "required": ["wk inc flu hosp"], + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + }, + "target_metadata": [{ + "target_id": "wk inc flu hosp", + "target_name": "Weekly incident influenza hospitalizations", + "target_units": "count", + "target_keys": { + "target": "wk inc flu hosp" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }] + }], + "submissions_due": { + "relative_to": "origin_date", + "start": -6, + "end": 1 + } + },{ + "round_id_from_variable": true, + "round_id": "origin_date", + "model_tasks": [{ + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-15", "2022-10-22", "2022-10-29", "2022-10-29"] + }, + "target": { + "required": ["wk inc flu hosp"], + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + }, + "age_group": { + "required": [ + "65+" + ], + "optional": [ + "0-5", + "6-18", + "19-24", + "25-64" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + }, + "target_metadata": [{ + "target_id": "wk inc flu hosp", + "target_name": "Weekly incident influenza hospitalizations", + "target_units": "count", + "target_keys": { + "target": "wk inc flu hosp" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }] + }], + "submissions_due": { + "relative_to": "origin_date", + "start": -6, + "end": 1 + } + } + ] +} diff --git a/tests/testthat/testdata/dup-in-property.json b/tests/testthat/testdata/dup-in-property.json new file mode 100644 index 0000000..9f3650b --- /dev/null +++ b/tests/testthat/testdata/dup-in-property.json @@ -0,0 +1,297 @@ +{ + "schema_version": "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json", + "rounds": [{ + "round_id_from_variable": true, + "round_id": "origin_date", + "model_tasks": [{ + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-01", "2022-10-08", "2022-10-15"] + }, + "target": { + "required": ["wk inc flu hosp"], + "optional": null + }, + "horizon": { + "required": [1, 2], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + }, + "target_metadata": [{ + "target_id": "wk inc flu hosp", + "target_name": "Weekly incident influenza hospitalizations", + "target_units": "count", + "target_keys": { + "target": "wk inc flu hosp" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }] + }], + "submissions_due": { + "relative_to": "origin_date", + "start": -6, + "end": 1 + } + },{ + "round_id_from_variable": true, + "round_id": "origin_date", + "model_tasks": [{ + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-22", "2022-10-29"] + }, + "target": { + "required": ["wk inc flu hosp"], + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + }, + "age_group": { + "required": [ + "65+" + ], + "optional": [ + "0-5", + "6-18", + "19-24", + "25-64", + "65+" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": [0.990] + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + }, + "target_metadata": [{ + "target_id": "wk inc flu hosp", + "target_name": "Weekly incident influenza hospitalizations", + "target_units": "count", + "target_keys": { + "target": "wk inc flu hosp" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }] + }], + "submissions_due": { + "relative_to": "origin_date", + "start": -6, + "end": 1 + } + } + ] +} diff --git a/tests/testthat/testdata/dup-in-round-id.json b/tests/testthat/testdata/dup-in-round-id.json new file mode 100644 index 0000000..c055fb1 --- /dev/null +++ b/tests/testthat/testdata/dup-in-round-id.json @@ -0,0 +1,448 @@ +{ + "schema_version": "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json", + "rounds": [{ + "round_id_from_variable": true, + "round_id": "origin_date", + "model_tasks": [{ + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-01", "2022-10-08", "2022-10-15"] + }, + "target": { + "required": ["wk inc flu hosp"], + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + }, + "target_metadata": [{ + "target_id": "wk inc flu hosp", + "target_name": "Weekly incident influenza hospitalizations", + "target_units": "count", + "target_keys": { + "target": "wk inc flu hosp" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }] + }], + "submissions_due": { + "relative_to": "origin_date", + "start": -6, + "end": 1 + } + },{ + "round_id_from_variable": true, + "round_id": "origin_date", + "model_tasks": [{ + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-08", "2022-10-15", "2022-10-22", "2022-10-29"] + }, + "target": { + "required": ["wk inc flu hosp"], + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + }, + "age_group": { + "required": [ + "65+" + ], + "optional": [ + "0-5", + "6-18", + "19-24", + "25-64" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + }, + "target_metadata": [{ + "target_id": "wk inc flu hosp", + "target_name": "Weekly incident influenza hospitalizations", + "target_units": "count", + "target_keys": { + "target": "wk inc flu hosp" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }] + }], + "submissions_due": { + "relative_to": "origin_date", + "start": -6, + "end": 1 + } + },{ + "round_id_from_variable": true, + "round_id": "origin_date", + "model_tasks": [{ + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-15", "2022-10-22", "2022-10-29", + "2022-11-05", "2022-11-12", "2022-11-19"] + }, + "target": { + "required": ["wk inc flu hosp"], + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + }, + "age_group": { + "required": [ + "65+" + ], + "optional": [ + "0-5", + "6-18", + "19-24", + "25-64" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + }, + "target_metadata": [{ + "target_id": "wk inc flu hosp", + "target_name": "Weekly incident influenza hospitalizations", + "target_units": "count", + "target_keys": { + "target": "wk inc flu hosp" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }] + }], + "submissions_due": { + "relative_to": "origin_date", + "start": -6, + "end": 1 + } + } + ] +} diff --git a/tests/testthat/testdata/empty.json b/tests/testthat/testdata/empty.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/testthat/testdata/empty.json @@ -0,0 +1 @@ +{} diff --git a/tests/testthat/testdata/error_hub/hub-config/admin.json b/tests/testthat/testdata/error_hub/hub-config/admin.json new file mode 100644 index 0000000..ad8a33e --- /dev/null +++ b/tests/testthat/testdata/error_hub/hub-config/admin.json @@ -0,0 +1,17 @@ +{ + "schema_version": "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/admin-schema.json", + "name": "Simple Forecast Hub", + "maintainer": "Consortium of Infectious Disease Modeling Hubs", + "contact": { + "name": "Joe Bloggs", + "email": "j.bloggs@cidmh.com" + }, + "repository_url": "https://github.com/Infectious-Disease-Modeling-Hubs/example-simple-forecast-hub", + "hub_models": [{ + "team_abbr": "simple_hub", + "model_abbr": "baseline", + "model_type": "baseline" + }], + "file_format": ["csv", "parquet", "arrow"], + "timezone": "US/Eastern" +} diff --git a/tests/testthat/testdata/error_hub/hub-config/model-metadata-schema.json b/tests/testthat/testdata/error_hub/hub-config/model-metadata-schema.json new file mode 100644 index 0000000..dae928d --- /dev/null +++ b/tests/testthat/testdata/error_hub/hub-config/model-metadata-schema.json @@ -0,0 +1,122 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Schema for Modeling Hub model metadata", + "description": "This is the schema for model metadata files, please refer to https://github.com/covid19-forecast-hub-europe/covid19-forecast-hub-europe/wiki/Metadata for more information.", + "type": "object", + "properties": { + "team_name": { + "description": "The name of the team submitting the model", + "type": "string" + }, + "model_name": { + "description": "The name of the model", + "type": "string" + }, + "model_abbr": { + "description": "Abbreviated name of the model", + "type": "string", + "pattern": "^[a-zA-Z0-9_+]+$", + "maxLength": 16 + }, + "model_version": { + "description": "Identifier of the version of the model", + "type": "string" + }, + "model_contributors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "affiliation": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "orcid": { + "type": "string", + "pattern": "^\\d{4}\\-\\d{4}\\-\\d{4}\\-[\\dX]{4}$" + } + }, + "additionalProperties": false, + "required": ["name", "affiliation", "email"] + } + }, + "website_url": { + "description": "Public facing website for the model", + "type": "string", + "format": "uri" + }, + "repo_url": { + "description": "Repository containing code for the model", + "type": "string", + "format": "uri" + }, + "license": { + "description": "License for use of model output data", + "type": "string", + "enum": [ + "CC0-1.0", + "CC-BY-4.0", + "CC-BY_SA-4.0", + "PPDL", + "ODC-by", + "ODbL", + "OGL-3.0" + ] + }, + "designated_model": { + "description": "Team-specified indicator for whether the model should be eligible for inclusion in a Hub ensemble and public visualization. A team may designate up to two models.", + "type": "boolean" + }, + "citation": { + "description": "One or more citations for this model", + "type": "string", + "examples": ["Gibson GC , Reich NG , Sheldon D. Real-time mechanistic bayesian forecasts of Covid-19 mortality. medRxiv. 2020. https://doi.org/10.1101/2020.12.22.20248736"] + }, + "team_funding": { + "description": "Any information about funding source for the team or members of the team.", + "type": "string", + "examples": ["National Institutes of General Medical Sciences (R01GM123456). The content is solely the responsibility of the authors and does not necessarily represent the official views of NIGMS."] + }, + "data_inputs": { + "description": "List or description of data inputs used by the model", + "type": "string" + }, + "methods": { + "description": "A brief (200 char.) description of the methods used by this model", + "type": "string", + "maxLength": 200 + }, + "methods_long": { + "description": "A full description of the methods used by this model. Among other details, this should include whether spatial correlation is considered and how the model accounts for uncertainty.", + "type": "string" + }, + "ensemble_of_models": { + "description": "Indicator for whether this model is an ensemble of any separate component models", + "type": "boolean" + }, + "ensemble_of_hub_models": { + "description": "Indicator for whether this model is an ensemble specifically of other models submitted to this Hub", + "type": "boolean" + } + }, + "additionalProperties": true, + "required": [ + "team_name", + "team_abbr", + "model_name", + "model_abbr", + "model_contributors", + "license", + "data_inputs", + "methods", + "methods_long", + "ensemble_of_models", + "ensemble_of_hub_models" + ] +} diff --git a/tests/testthat/testdata/error_hub/hub-config/tasks.json b/tests/testthat/testdata/error_hub/hub-config/tasks.json new file mode 100644 index 0000000..dda3978 --- /dev/null +++ b/tests/testthat/testdata/error_hub/hub-config/tasks.json @@ -0,0 +1,134 @@ +{ + "schema_version": "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json", + "rounds": [{ + "round_id_from_variable": true, + "round_id": "origin_date", + "model_tasks": [{ + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-01", "2022-10-08", "2022-10-15", "2022-10-22", "2022-10-29"] + }, + "target": { + "required": "wk inc flu hosp", + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": ["NA"], + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + } + }], + "submissions_due": { + "start": -6, + "end": 1 + } + } + + ] +} diff --git a/tests/testthat/testdata/error_hub/model-metadata/hub-baseline-both-abbrs-no-model_id.yml b/tests/testthat/testdata/error_hub/model-metadata/hub-baseline-both-abbrs-no-model_id.yml new file mode 100644 index 0000000..3423d69 --- /dev/null +++ b/tests/testthat/testdata/error_hub/model-metadata/hub-baseline-both-abbrs-no-model_id.yml @@ -0,0 +1,24 @@ +team_name: "Hub Coordination Team" +team_abbr: "hub" +model_abbr: "baseline" +model_version: "1.0" +model_contributors: [ + { + "name": "Joe Bloggs", + "email": "j.bloggs@email.com" + }, + { + "name": "J Smith", + "email": "j.smith@email.com" + } +] +website_url: "https://team1goodmodel.website" +license: "cc-by-4.0" +include_viz: true +include_ensemble: false +include_eval: true +model_details: { + methods: "this the method description of the model.", + data_inputs: "description of the data inputs" +} +ensemble_of_hub_models: false diff --git a/tests/testthat/testdata/error_hub/model-metadata/hub-baseline-no-abbrs-or-model_id.yml b/tests/testthat/testdata/error_hub/model-metadata/hub-baseline-no-abbrs-or-model_id.yml new file mode 100644 index 0000000..bac15a0 --- /dev/null +++ b/tests/testthat/testdata/error_hub/model-metadata/hub-baseline-no-abbrs-or-model_id.yml @@ -0,0 +1,24 @@ +team_name: "Hub Coordination Team" +team_abbr: "hub" +model_name: "Baseline" +model_version: "1.0" +model_contributors: [ + { + "name": "Joe Bloggs", + "email": "j.bloggs@email.com" + }, + { + "name": "J Smith", + "email": "j.smith@email.com" + } +] +website_url: "https://team1goodmodel.website" +license: "cc-by-4.0" +include_viz: true +include_ensemble: false +include_eval: true +model_details: { + methods: "this the method description of the model.", + data_inputs: "description of the data inputs" +} +ensemble_of_hub_models: false diff --git a/tests/testthat/testdata/error_hub/model-metadata/hub-baseline-with-model_id.yml b/tests/testthat/testdata/error_hub/model-metadata/hub-baseline-with-model_id.yml new file mode 100644 index 0000000..89a61f6 --- /dev/null +++ b/tests/testthat/testdata/error_hub/model-metadata/hub-baseline-with-model_id.yml @@ -0,0 +1,24 @@ +team_name: "Hub Coordination Team" +model_name: "Baseline" +model_id: "hub-baseline-with-model_id" +model_version: "1.0" +model_contributors: [ + { + "name": "Joe Bloggs", + "email": "j.bloggs@email.com" + }, + { + "name": "J Smith", + "email": "j.smith@email.com" + } +] +website_url: "https://team1goodmodel.website" +license: "cc-by-4.0" +include_viz: true +include_ensemble: false +include_eval: true +model_details: { + methods: "this the method description of the model.", + data_inputs: "description of the data inputs" +} +ensemble_of_hub_models: false diff --git a/tests/testthat/testdata/error_hub/model-metadata/hub-baseline-with-wrong-model_id.yml b/tests/testthat/testdata/error_hub/model-metadata/hub-baseline-with-wrong-model_id.yml new file mode 100644 index 0000000..b4f53c8 --- /dev/null +++ b/tests/testthat/testdata/error_hub/model-metadata/hub-baseline-with-wrong-model_id.yml @@ -0,0 +1,24 @@ +team_name: "Hub Coordination Team" +model_name: "Baseline" +model_id: "wrong-model_id" +model_version: "1.0" +model_contributors: [ + { + "name": "Joe Bloggs", + "email": "j.bloggs@email.com" + }, + { + "name": "J Smith", + "email": "j.smith@email.com" + } +] +website_url: "https://team1goodmodel.website" +license: "cc-by-4.0" +include_viz: true +include_ensemble: false +include_eval: true +model_details: { + methods: "this the method description of the model.", + data_inputs: "description of the data inputs" +} +ensemble_of_hub_models: false diff --git a/tests/testthat/testdata/round-id-inconsistent.json b/tests/testthat/testdata/round-id-inconsistent.json new file mode 100644 index 0000000..de57410 --- /dev/null +++ b/tests/testthat/testdata/round-id-inconsistent.json @@ -0,0 +1,235 @@ +{ + "schema_version": "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v1.0.0/tasks-schema.json", + "rounds": [{ + "round_id_from_variable": true, + "round_id": "forecast_date", + "model_tasks": [{ + "task_ids": { + "forecast_date": { + "required": null, + "optional": [ + "2022-12-12", "2022-12-19", "2022-12-26", "2023-01-02", "2023-01-09", + "2023-01-16", "2023-01-23", "2023-01-30", "2023-02-06", "2023-02-13", + "2023-02-20", "2023-02-27", "2023-03-06", "2023-03-13", "2023-03-20", + "2023-03-27", "2023-04-03", "2023-04-10", "2023-04-17", "2023-04-24", + "2023-05-01", "2023-05-08", "2023-05-15" + ] + }, + "target": { + "required": null, + "optional": ["wk flu hosp rate change"] + }, + "horizon": { + "required": [2], + "optional": [1] + }, + "location": { + "required": ["US"], + "optional": [ + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "pmf": { + "type_id": { + "required": ["large_decrease", "decrease", "stable", "increase", "large_increase"], + "optional": null + }, + "value": { + "type": "double", + "minimum": 0, + "maximum": 1 + } + } + }, + "target_metadata": [{ + "target_id": "wk flu hosp rate change", + "target_name": "weekly influenza hospitalization rate change", + "target_units": "rate per 100,000 population", + "target_keys": { + "target": ["wk flu hosp rate change"] + }, + "target_type": "nominal", + "description": "This target represents the change in the rate of new hospitalizations per week comparing the week ending two days prior to the forecast_date to the week ending h weeks after the forecast_date.", + "is_step_ahead": true, + "time_unit": "week" + }] + }, { + "task_ids": { + "forecast_date": { + "required": null, + "optional": [ + "2022-12-12", "2022-12-19", "2022-12-26", "2023-01-02", "2023-01-09", + "2023-01-16", "2023-01-23", "2023-01-30", "2023-02-06", "2023-02-13", + "2023-02-20", "2023-02-27", "2023-03-06", "2023-03-13", "2023-03-20", + "2023-03-27", "2023-04-03", "2023-04-10", "2023-04-17", "2023-04-24", + "2023-05-02", "2023-05-08" + ] + }, + "target": { + "required": null, + "optional": ["wk ahead inc flu hosp"] + }, + "horizon": { + "required": [2], + "optional": [1] + }, + "location": { + "required": ["US"], + "optional": [ + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "quantile": { + "type_id": { + "required": [0.01, 0.025, 0.05, 0.1, 0.15, 0.2, 0.25, + 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, + 0.75, 0.8, 0.85, 0.9, 0.95, 0.975, 0.99 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "mean": { + "type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "double", + "minimum": 0 + } + } + }, + "target_metadata": [{ + "target_id": "wk ahead inc flu hosp", + "target_name": "weekly influenza hospitalization incidence", + "target_units": "rate per 100,000 population", + "target_keys": { + "target": ["wk ahead inc flu hosp"] + }, + "target_type": "discrete", + "description": "This target represents the counts of new hospitalizations per horizon week.", + "is_step_ahead": true, + "time_unit": "week" + }] + }], + "submissions_due": { + "relative_to": "forecast_date", + "start": -6, + "end": 2 + } + } + + ] +} diff --git a/tests/testthat/testdata/round-id-inconsistent2.json b/tests/testthat/testdata/round-id-inconsistent2.json new file mode 100644 index 0000000..0317563 --- /dev/null +++ b/tests/testthat/testdata/round-id-inconsistent2.json @@ -0,0 +1,235 @@ +{ + "schema_version": "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json", + "rounds": [{ + "round_id_from_variable": true, + "round_id": "forecast_date", + "model_tasks": [{ + "task_ids": { + "forecast_date": { + "required": null, + "optional": [ + "2022-12-12", "2022-12-19", "2022-12-26", "2023-01-02", "2023-01-09", + "2023-01-16", "2023-01-23", "2023-01-30", "2023-02-06", "2023-02-13", + "2023-02-20", "2023-02-27", "2023-03-06", "2023-03-13", "2023-03-20", + "2023-03-27", "2023-04-03", "2023-04-10", "2023-04-17", "2023-04-24", + "2023-05-01", "2023-05-08", "2023-05-15" + ] + }, + "target": { + "required": null, + "optional": ["wk flu hosp rate change"] + }, + "horizon": { + "required": [2], + "optional": [1] + }, + "location": { + "required": ["US"], + "optional": [ + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "pmf": { + "output_type_id": { + "required": ["large_decrease", "decrease", "stable", "increase", "large_increase"], + "optional": null + }, + "value": { + "type": "double", + "minimum": 0, + "maximum": 1 + } + } + }, + "target_metadata": [{ + "target_id": "wk flu hosp rate change", + "target_name": "weekly influenza hospitalization rate change", + "target_units": "rate per 100,000 population", + "target_keys": { + "target": ["wk flu hosp rate change"] + }, + "target_type": "nominal", + "description": "This target represents the change in the rate of new hospitalizations per week comparing the week ending two days prior to the forecast_date to the week ending h weeks after the forecast_date.", + "is_step_ahead": true, + "time_unit": "week" + }] + }, { + "task_ids": { + "forecast_date": { + "required": null, + "optional": [ + "2022-12-12", "2022-12-19", "2022-12-26", "2023-01-02", "2023-01-09", + "2023-01-16", "2023-01-23", "2023-01-30", "2023-02-06", "2023-02-13", + "2023-02-20", "2023-02-27", "2023-03-06", "2023-03-13", "2023-03-20", + "2023-03-27", "2023-04-03", "2023-04-10", "2023-04-17", "2023-04-24", + "2023-05-02", "2023-05-08" + ] + }, + "target": { + "required": null, + "optional": ["wk ahead inc flu hosp"] + }, + "horizon": { + "required": [2], + "optional": [1] + }, + "location": { + "required": ["US"], + "optional": [ + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "quantile": { + "output_type_id": { + "required": [0.01, 0.025, 0.05, 0.1, 0.15, 0.2, 0.25, + 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, + 0.75, 0.8, 0.85, 0.9, 0.95, 0.975, 0.99 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "double", + "minimum": 0 + } + } + }, + "target_metadata": [{ + "target_id": "wk ahead inc flu hosp", + "target_name": "weekly influenza hospitalization incidence", + "target_units": "rate per 100,000 population", + "target_keys": { + "target": ["wk ahead inc flu hosp"] + }, + "target_type": "discrete", + "description": "This target represents the counts of new hospitalizations per horizon week.", + "is_step_ahead": true, + "time_unit": "week" + }] + }], + "submissions_due": { + "relative_to": "forecast_date", + "start": -6, + "end": 2 + } + } + + ] +} diff --git a/tests/testthat/testdata/schema_version-errors.json b/tests/testthat/testdata/schema_version-errors.json new file mode 100644 index 0000000..b6b6001 --- /dev/null +++ b/tests/testthat/testdata/schema_version-errors.json @@ -0,0 +1,3 @@ +{ + "schema_version": "https://github.com/Infectious-Disease-Modeling-Hubs/schemas/blob/main/v2.0.0/tasks-schema.json" +} diff --git a/tests/testthat/testdata/tasks-addprop.json b/tests/testthat/testdata/tasks-addprop.json new file mode 100644 index 0000000..9412ae6 --- /dev/null +++ b/tests/testthat/testdata/tasks-addprop.json @@ -0,0 +1,162 @@ +{ + "schema_version": "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json", + "rounds": [{ + "round_id_from_variable": true, + "round_id": "origin_date", + "model_tasks": [{ + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2023-06-05", "2023-06-06", "2023-06-07", "2023-06-08", "2023-06-09", "2023-06-10"] + }, + "target": { + "required": ["inc flu hosp"], + "optional": null + }, + "horizon": { + "required": null, + "optional": [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "target_metadata": [ + { + "target_id": "inc flu hosp", + "target_name": "daily incident influenza hospitalizations", + "target_units": "count", + "target_keys": { + "target": ["inc flu hosp"] + }, + "description": "This target represents a count of the number of new hospitalizations per day in a given location.", + "is_step_ahead": true, + "time_unit": "day" + } + ] + }, + "target_metadata": [ + { + "target_id": "inc flu hosp", + "target_name": "Incident influenza hospitalizations", + "target_units": "count", + "target_keys": { + "target": "inc flu hosp" + }, + "description": "Weekly newly reported hospitalizations where the patient has influenza, as reported by hospital facilities and aggregated in the HHS Protect data collection system.", + "target_type": "discrete", + "is_step_ahead": true, + "time_unit": "week" + } + ] + }], + "submissions_due": { + "relative_to": "origin_date", + "start": -6, + "end": 1 + } + } + + ] +} diff --git a/tests/testthat/testdata/tasks-errors-rval-reserved.json b/tests/testthat/testdata/tasks-errors-rval-reserved.json new file mode 100644 index 0000000..d9fa308 --- /dev/null +++ b/tests/testthat/testdata/tasks-errors-rval-reserved.json @@ -0,0 +1,296 @@ +{ + "schema_version": "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json", + "rounds": [{ + "round_id_from_variable": true, + "round_id": "origin_date", + "model_tasks": [{ + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-01", "2022-10-08", "2022-10-15", "2022-10-22", "2022-10-29"] + }, + "target": { + "required": ["wk inc flu hosp"], + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "model_id": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + }, + "target_metadata": [{ + "target_id": "wk inc flu hosp", + "target_name": "Weekly incident influenza hospitalizations", + "target_units": "count", + "target_keys": { + "target": "wk inc flu hosp" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }] + }, + { + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-01", "2022-10-08", "2022-10-15", "2022-10-22", "2022-10-29"] + }, + "target_outcome": { + "required": ["flu hosp"], + "optional": ["wk inc flu hosp", "flu case"] + }, + "target_mesures": { + "required": ["wk inc"], + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "value": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + }, + "target_metadata": [ + { + "target_id": "wk inc flu hosp", + "target_name": "Weekly incident influenza hospitalizations", + "target_units": "count", + "target_keys": { + "target_measure": "wk inc", + "target_outcome": "flu hospitalisation" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }, + { + "target_id": "wk inc flu case", + "target_name": "Weekly incident influenza cases", + "target_units": "count", + "target_keys": { + "target_measure": "wk inc", + "target_outcome": "flu case" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }] + }], + "submissions_due": { + "relative_to": "origin_date", + "start": -6, + "end": 1 + } + } + + ] +} diff --git a/tests/testthat/testdata/tasks-errors-rval.json b/tests/testthat/testdata/tasks-errors-rval.json new file mode 100644 index 0000000..d7c5364 --- /dev/null +++ b/tests/testthat/testdata/tasks-errors-rval.json @@ -0,0 +1,296 @@ +{ + "schema_version": "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json", + "rounds": [{ + "round_id_from_variable": true, + "round_id": "origin_date", + "model_tasks": [{ + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-01", "2022-10-08", "2022-10-15", "2022-10-22", "2022-10-29"] + }, + "target": { + "required": ["wk inc flu hosp"], + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + }, + "target_metadata": [{ + "target_id": "wk inc flu hosp", + "target_name": "Weekly incident influenza hospitalizations", + "target_units": "count", + "target_keys": { + "target": "wk inc flu hosp" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }] + }, + { + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-01", "2022-10-08", "2022-10-15", "2022-10-22", "2022-10-29"] + }, + "target_outcome": { + "required": ["flu hosp"], + "optional": ["wk inc flu hosp", "flu case"] + }, + "target_mesures": { + "required": ["wk inc"], + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + }, + "target_metadata": [ + { + "target_id": "wk inc flu hosp", + "target_name": "Weekly incident influenza hospitalizations", + "target_units": "count", + "target_keys": { + "target_measure": "wk inc", + "target_outcome": "flu hospitalisation" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }, + { + "target_id": "wk inc flu case", + "target_name": "Weekly incident influenza cases", + "target_units": "count", + "target_keys": { + "target_measure": "wk inc", + "target_outcome": "flu case" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }] + }], + "submissions_due": { + "relative_to": "origin_date", + "start": -6, + "end": 1 + } + } + + ] +} diff --git a/tests/testthat/testdata/tasks-errors.json b/tests/testthat/testdata/tasks-errors.json new file mode 100644 index 0000000..c0c10ca --- /dev/null +++ b/tests/testthat/testdata/tasks-errors.json @@ -0,0 +1,134 @@ +{ + "schema_version": "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v0.0.0.9/tasks-schema.json", + "rounds": [{ + "round_id_from_variable": true, + "round_id": "origin_date", + "model_tasks": [{ + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-01", "2022-10-08", "2022-10-15", "2022-10-22", "2022-10-29"] + }, + "target": { + "required": "wk inc flu hosp", + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "mean": { + "type_id": { + "required": ["NA"], + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + } + }], + "submissions_due": { + "start": -6, + "end": 1 + } + } + + ] +} diff --git a/tests/testthat/testdata/tasks_null_rval.json b/tests/testthat/testdata/tasks_null_rval.json new file mode 100644 index 0000000..3178222 --- /dev/null +++ b/tests/testthat/testdata/tasks_null_rval.json @@ -0,0 +1,140 @@ +{ + "schema_version": "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json", + "rounds": [{ + "round_id_from_variable": true, + "round_id": "origin_date", + "model_tasks": [{ + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-01", "2022-10-08", "2022-10-15", "2022-10-22", "2022-10-29"] + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + }, + "target_metadata": [{ + "target_id": "wk inc flu hosp", + "target_name": "Weekly incident influenza hospitalizations", + "target_units": "count", + "target_keys": null, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }] + }], + "submissions_due": { + "relative_to": "origin_date", + "start": -6, + "end": 1 + } + } + + ] +} diff --git a/tests/testthat/testdata/tasks_required_missing.json b/tests/testthat/testdata/tasks_required_missing.json new file mode 100644 index 0000000..31c7cc7 --- /dev/null +++ b/tests/testthat/testdata/tasks_required_missing.json @@ -0,0 +1,285 @@ +{ + "schema_version": "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json", + "rounds": [{ + "round_id_from_variable": true, + "round_id": "origin_date", + "model_tasks": [{ + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-01", "2022-10-08", "2022-10-15", "2022-10-22", "2022-10-29"] + }, + "target": { + "required": ["wk inc flu hosp"], + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": "0" + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + } + }, + { + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-01", "2022-10-08", "2022-10-15", "2022-10-22", "2022-10-29"] + }, + "target_outcome": { + "required": ["flu hosp"], + "optional": ["wk inc flu hosp", "flu case"] + }, + "target_mesures": { + "required": ["wk inc"], + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + }, + "target_metadata": [ + { + "target_id": "wk inc flu hosp", + "target_name": "Weekly incident influenza hospitalizations", + "target_units": "count", + "target_keys": { + "target_measure": "wk inc", + "target_outcome": "flu hospitalisation" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }, + { + "target_id": "wk inc flu case", + "target_name": "Weekly incident influenza cases", + "target_units": "count", + "target_keys": { + "target_measure": "wk inc", + "target_outcome": "flu case" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }] + }], + "submissions_due": { + "relative_to": "origin_date", + "start": -6, + "end": 1 + } + } + + ] +} diff --git a/tests/testthat/testdata/tasks_required_missing_only.json b/tests/testthat/testdata/tasks_required_missing_only.json new file mode 100644 index 0000000..1a9d43e --- /dev/null +++ b/tests/testthat/testdata/tasks_required_missing_only.json @@ -0,0 +1,285 @@ +{ + "schema_version": "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json", + "rounds": [{ + "round_id_from_variable": true, + "round_id": "origin_date", + "model_tasks": [{ + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-01", "2022-10-08", "2022-10-15", "2022-10-22", "2022-10-29"] + }, + "target": { + "required": ["wk inc flu hosp"], + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + } + }, + { + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-01", "2022-10-08", "2022-10-15", "2022-10-22", "2022-10-29"] + }, + "target_outcome": { + "required": ["flu hosp"], + "optional": ["wk inc flu hosp", "flu case"] + }, + "target_mesures": { + "required": ["wk inc"], + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + }, + "target_metadata": [ + { + "target_id": "wk inc flu hosp", + "target_name": "Weekly incident influenza hospitalizations", + "target_units": "count", + "target_keys": { + "target_measure": "wk inc", + "target_outcome": "flu hospitalisation" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }, + { + "target_id": "wk inc flu case", + "target_name": "Weekly incident influenza cases", + "target_units": "count", + "target_keys": { + "target_measure": "wk inc", + "target_outcome": "flu case" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }] + }], + "submissions_due": { + "relative_to": "origin_date", + "start": -6, + "end": 1 + } + } + + ] +} diff --git a/tests/testthat/testdata/tasks_required_missing_only2.json b/tests/testthat/testdata/tasks_required_missing_only2.json new file mode 100644 index 0000000..711de05 --- /dev/null +++ b/tests/testthat/testdata/tasks_required_missing_only2.json @@ -0,0 +1,284 @@ +{ + "schema_version": "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json", + "rounds": [{ + "round_id": "origin_date", + "model_tasks": [{ + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-01", "2022-10-08", "2022-10-15", "2022-10-22", "2022-10-29"] + }, + "target": { + "required": ["wk inc flu hosp"], + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + } + }, + { + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-01", "2022-10-08", "2022-10-15", "2022-10-22", "2022-10-29"] + }, + "target_outcome": { + "required": ["flu hosp"], + "optional": ["wk inc flu hosp", "flu case"] + }, + "target_mesures": { + "required": ["wk inc"], + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + }, + "target_metadata": [ + { + "target_id": "wk inc flu hosp", + "target_name": "Weekly incident influenza hospitalizations", + "target_units": "count", + "target_keys": { + "target_measure": "wk inc", + "target_outcome": "flu hospitalisation" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }, + { + "target_id": "wk inc flu case", + "target_name": "Weekly incident influenza cases", + "target_units": "count", + "target_keys": { + "target_measure": "wk inc", + "target_outcome": "flu case" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }] + }], + "submissions_due": { + "relative_to": "origin_date", + "start": -6, + "end": 1 + } + } + + ] +} diff --git a/tests/testthat/testdata/tasks_required_missing_only2b.json b/tests/testthat/testdata/tasks_required_missing_only2b.json new file mode 100644 index 0000000..8f509aa --- /dev/null +++ b/tests/testthat/testdata/tasks_required_missing_only2b.json @@ -0,0 +1,239 @@ +{ + "schema_version": "https://raw.githubusercontent.com/Infectious-Disease-Modeling-Hubs/schemas/main/v2.0.0/tasks-schema.json", + "rounds": [{ + "round_id_from_variable": true, + "round_id": "origin_date", + "model_tasks": [{ + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-01", "2022-10-08", "2022-10-15", "2022-10-22", "2022-10-29"] + }, + "target": { + "required": ["wk inc flu hosp"], + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + } + }, + { + "task_ids": { + "origin_date": { + "required": null, + "optional": ["2022-10-01", "2022-10-08", "2022-10-15", "2022-10-22", "2022-10-29"] + }, + "target_outcome": { + "required": ["flu hosp"], + "optional": ["wk inc flu hosp", "flu case"] + }, + "target_mesures": { + "required": ["wk inc"], + "optional": null + }, + "horizon": { + "required": [1], + "optional": [2, 3, 4] + }, + "location": { + "required": null, + "optional": [ + "US", + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "72", + "78" + ] + } + }, + "output_type": { + "mean": { + "output_type_id": { + "required": null, + "optional": ["NA"] + }, + "value": { + "type": "integer", + "minimum": 0 + } + }, + "quantile": { + "output_type_id": { + "required": [ + 0.010, + 0.025, + 0.050, + 0.100, + 0.150, + 0.200, + 0.250, + 0.300, + 0.350, + 0.400, + 0.450, + 0.500, + 0.550, + 0.600, + 0.650, + 0.700, + 0.750, + 0.800, + 0.850, + 0.900, + 0.950, + 0.975, + 0.990 + ], + "optional": null + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + }, + "target_metadata": [ + { + "target_id": "wk inc flu hosp", + "target_name": "Weekly incident influenza hospitalizations", + "target_units": "count", + "target_keys": { + "target_measure": "wk inc", + "target_outcome": "flu hospitalisation" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }, + { + "target_id": "wk inc flu case", + "target_name": "Weekly incident influenza cases", + "target_units": "count", + "target_keys": { + "target_measure": "wk inc", + "target_outcome": "flu case" + }, + "target_type": "continuous", + "is_step_ahead": true, + "time_unit": "week" + }] + }], + "submissions_due": { + "relative_to": "origin_date", + "start": -6, + "end": 1 + } + } + + ] +} diff --git a/vignettes/.gitignore b/vignettes/.gitignore new file mode 100644 index 0000000..097b241 --- /dev/null +++ b/vignettes/.gitignore @@ -0,0 +1,2 @@ +*.html +*.R diff --git a/vignettes/articles/hub-setup.Rmd b/vignettes/articles/hub-setup.Rmd new file mode 100644 index 0000000..7bdc781 --- /dev/null +++ b/vignettes/articles/hub-setup.Rmd @@ -0,0 +1,130 @@ +--- +title: "Setting up a Hub" +--- + +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>" +) +``` + +```{r setup} +library(hubAdmin) +``` + + +## Create Hub Repository from Template + +The first step to setting up a Modeling Hub is to make a copy of the [`hubTemplate`](https://github.com/Infectious-Disease-Modeling-Hubs/hubTemplate) GitHub template repository and clone a local copy of it. + + +## Configure Modeling Hub + +Hubs are configured through two JSON config files which are used to validate new submissions as well as inform data users of available data. + +- `admin.json`: Contains Hub wide administrative configuration settings +- `tasks.json`: Contains round-specific metadata of modeling tasks including task, target and output type metadata as well as details of submission windows. +These files should live in a directory called `hub-config/` in the root of the Hub repository. + + +For more details on these files, guidance on how to set them up and access to templates, please see our central [**hubDocs**](https://github.com/Infectious-Disease-Modeling-Hubs/hubDocs) as well the [JSON schema](https://github.com/Infectious-Disease-Modeling-Hubs/schemas) the config files should adhere to. + + +## Validate Config files + + +You can use function `validate_config()` to check whether individual Hub config files are valid. +To specify the file you want to validate you can either provide the path to the root of the Hub to argument `hub_path` (which assumes the config files are correctly located in directory `hub-config/`) or you can provide a direct path to a config file to argument `config_path`. +You also need to specify the type of config file through argument `config` (one of `"tasks"` or `"admin"`, defaults to `"tasks"`). + +The function will validate a given config file against a specific version of it's schema, specified through argument `schema_version`. The default value of `schema_version` is `"from_config"` which uses the version specified in the `schema_version` property of the config file being validated. + + +```{r} +validate_config( + hub_path = system.file("testhubs/simple/", package = "hubUtils"), + config = "tasks" +) +``` + +If validation succeeds, the function returns `TRUE`. The path to the config file validated and the version and URL of the schema used for validation are also attached as attributes `"config_path"`, `"schema_version"` and `"schema_url"` respectively. + + +You can validate a config file against the latest version of the schema by using `"latest"` or you can choose a specific version, e.g `"v0.0.1"`. + +The function defaults to using stable schema versions released to the `main` branch, but you can choose to validate against another branch (e.g. an upcoming development version) through argument `branch`. + +```{r} +validate_config( + hub_path = system.file("testhubs/simple/", package = "hubUtils"), + config = "tasks", + schema_version = "v0.0.0.9" +) +``` + + +```{r} +validate_config( + hub_path = system.file("testhubs/simple/", package = "hubUtils"), + config = "tasks", + schema_version = "latest" +) +``` + + +### Validation returning errors + +If validation of the config file fails, the function returns `FALSE`. An additional list dataframe of errors returned by the `ajv` validation engine used is also attached as attribute `"errors"`. + + +```{r} +config_path <- system.file("error-schema/tasks-errors.json", + package = "hubUtils" +) +``` + +```{r} +validate_config(config_path = config_path, config = "tasks") +``` + +Because the default output of the validator can be unwieldy and difficult to review, you can use function `view_config_val_errors()` to launch a more user friendly and concise version of the errors table in the Viewer panel in Rstudio. + +```{r} +validation <- validate_config(config_path = config_path, config = "tasks") +view_config_val_errors(validation) +``` + + +In the example above: + +- `instancePath` indicates the location of the validation error in the config file. +- `schemaPath` indicates the location of the element in the schema which is failing validation. +- `keyword` indicates the keyword causing the validation error. +- `message` is the validation error message returned by the validator. +- `schema` describes the valid schema values the failing keyword should conform to. +- `data` is the value of the property in the config file which is failing validation. + + +## Validating all Hub config files + +To validate both `admin.json` and `tasks.json` in a single call, you can use function `validate_hub_config()`. +This functions tests both files for validity and returns a list of the results of the validation checks for each file. +By default it uses "from_config" as the `schema_version` argument and errors if both files are not using the same schema version. + +```{r} +validate_hub_config( + hub_path = system.file("testhubs/simple/", package = "hubUtils") +) +``` + +You can also use the function to validate a Hub's config against the latest version of the schema. + +```{r} +validate_config( + hub_path = system.file("testhubs/simple/", package = "hubUtils"), + schema_version = "latest" +) +``` + +You can also use `view_config_val_errors()` on the output of `validate_hub_config()` to review any detected validation errors.