From 38b4bdd22f9e026e617d976588e360abaf7ae04c Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Tue, 6 Oct 2026 19:38:56 +0200 Subject: [PATCH 01/44] multi-dataset aware test analysis + local bundle build - 'Test Multi-DataSet' (multiDataSetAware+preloadData) and its non-aware counterpart, sharing R/multiDataSet.R and inst/qml/testMultiDataSet.qml: the VariablesForm dataset/filter selection, per-dataset routing of encoded option values via jaspBase's dataSetIdFromEncoded/dataSetNameFromEncoded/getDataSetColumn, a .meta provenance dump and the readDataSet() guard demo. - Tools/buildBundle.R: builds bundles_out/jaspTestModule.JASPModule with the local jasp-desktop/Engine/jaspBase checkout swapped into renv.lock as a Local record (verified inside the bundle afterwards); cellar-free, works on Arch R 4.6.1. - renv.lock refresh for R 4.6.1 in the next commit (kept separate: buildBundle.R swaps it transiently). --- .gitignore | 2 + NEWS.md | 11 +++ R/multiDataSet.R | 141 +++++++++++++++++++++++++++++++ Tools/buildBundle.R | 152 ++++++++++++++++++++++++++++++++++ inst/Description.qml | 30 +++++++ inst/qml/testMultiDataSet.qml | 48 +++++++++++ 6 files changed, 384 insertions(+) create mode 100644 R/multiDataSet.R create mode 100644 Tools/buildBundle.R create mode 100644 inst/qml/testMultiDataSet.qml diff --git a/.gitignore b/.gitignore index e1db096..adfe98d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ .RData .Ruserdata _processedLockFile.lock +ModuleBundleBuildDir/ +bundles_out/ diff --git a/NEWS.md b/NEWS.md index 361436b..7b6cd18 100644 --- a/NEWS.md +++ b/NEWS.md @@ -15,6 +15,17 @@ --- # jaspTestModule (development version) +## Added +* "Test Multi-DataSet": analysis + form to exercise multi-dataset aware analyses + (`multiDataSetAware` Description entries, the VariablesForm dataset/filter selection, + the `datasets` handout and encoded-name routing of jaspBase, the readDataSet() guard, + the `.meta` dataSetId/filterId provenance and the non-aware counterpart as controls). +* `Tools/buildBundle.R`: builds an installable `bundles_out/jaspTestModule.JASPModule` + from this checkout with the local `jasp-desktop/Engine/jaspBase` (or any checkout via + `JASP_BASE_DIR`) packed into the bundle; no JASP cellar needed. + +## Changed +* `renv.lock` re-resolved for R 4.6.1 (current CRAN, jaspBase/jaspGraphs from GitHub HEAD). diff --git a/R/multiDataSet.R b/R/multiDataSet.R new file mode 100644 index 0000000..1813867 --- /dev/null +++ b/R/multiDataSet.R @@ -0,0 +1,141 @@ +# Multi-dataset aware analyses (jasp-desktop branch dataSetAwareVarForm, jaspBase branch +# multiDataSetAware). An analysis whose Description/Analysis entry sets multiDataSetAware gets +# every dataset it references as `datasets`: a named list keyed by dataset id, with the user +# facing titles in attr(datasets, "dataSetNames"). Its variable options carry encoded column +# names whose prefix embeds the dataset they were selected from (JASPColumn__), +# which jaspBase's dataSetIdFromEncoded / dataSetNameFromEncoded / getDataSetColumn route. +# While such a run is active, readDataSet* stops: one 'current dataset' is meaningless here. + +multiDataSetFunc <- function(jaspResults, dataset, options, datasets = NULL) { + aware <- !is.null(datasets) + + jaspResults[["explanation"]] <- createJaspHtml(paste0( + if (aware) + paste0("Aware run. This analysis received ", length(datasets), + " dataset(s) as the `datasets` argument; readDataSet() is disabled while it runs.") + else + "Non-aware run. No `datasets` argument: data comes in through `dataset`/readDataSet as always, and the dataset selection in the form should have been inert.", + "

Use two datasets in the workspace (File - New data set or add another CSV) and drag variables from both into the variables form.")) + + dataSetOverviewTable(jaspResults, datasets, dataset) + selectedVariablesTable(jaspResults, options, datasets, dataset) + + if (isTRUE(options$showMeta)) + metaDump(jaspResults, options) + + if (isTRUE(options$guardDemo)) + readDataSetGuardDemo(jaspResults) +} + +dataSetOverviewTable <- function(jaspResults, datasets, dataset) { + table <- createJaspTable("Datasets in this run") + jaspResults[["dataSetOverview"]] <- table + + table$addColumnInfo("id", title = "Dataset id") + table$addColumnInfo("title", title = "Title") + table$addColumnInfo("rows", title = "Rows") + table$addColumnInfo("cols", title = "Columns") + table$addColumnInfo("columnNames", title = "Column names") + + if (!is.null(datasets)) { + titles <- attr(datasets, "dataSetNames") + + for (id in names(datasets)) { + df <- datasets[[id]] + table[[id]] <- list( + id = id, title = titles[[id]], rows = nrow(df), cols = ncol(df), + columnNames = paste(names(df), collapse = ", ")) + } + } else if (!is.null(dataset)) { + table[["current"]] <- list( + id = "(current)", title = "(the one dataset)", rows = nrow(dataset), ncol(dataset), + columnNames = paste(names(dataset), collapse = ", ")) + } +} + +selectedVariablesTable <- function(jaspResults, options, datasets, dataset) { + table <- createJaspTable("Selected variables") + jaspResults[["selectedVariables"]] <- table + + table$addColumnInfo("variable", title = "Variable (routed to)") + table$addColumnInfo("dataSetId", title = "Dataset id") + table$addColumnInfo("dataSetName", title = "Dataset title") + table$addColumnInfo("column", title = "Column") + table$addColumnInfo("type", title = "Type") + table$addColumnInfo("n", title = "n") + table$addColumnInfo("values", title = "mean (scale) / first levels (factor)") + + values <- .dependentValues(options) + + for (value in values) { + if (is.null(datasets)) { + # Non-aware: plain preload semantics, decode against the current encoder. + column <- sub("\\.(scale|ordinal|nominal)$", "", jaspBase::decodeColNames(value)) + entry <- list(variable = column, dataSetId = "-", dataSetName = "-", column = column, + type = .typeSuffix(value), n = NA_integer_, values = "") + if (!is.null(dataset) && column %in% names(dataset)) + entry <- c(entry, .describeColumn(dataset[[column]])) + table[[value]] <- entry + next + } + + id <- jaspBase::dataSetIdFromEncoded(value) + column <- jaspBase::dataSetColumnFromEncoded(value, datasets) + entry <- list( + variable = value, + dataSetId = if (is.na(id)) "none (legacy)" else id, + dataSetName = jaspBase::dataSetNameFromEncoded(value, datasets), + column = column, + type = .typeSuffix(value), + n = NA_integer_, values = "") + + columnData <- jaspBase::getDataSetColumn(value, datasets) + if (!is.null(columnData)) + entry <- c(entry, .describeColumn(columnData)) + + table[[value]] <- entry + } +} + +metaDump <- function(jaspResults, options) { + meta <- options[[".meta"]] + jaspResults[["metaDump"]] <- createJaspHtml(paste0( + "options$.meta
",
+    jsonlite::toJSON(if (is.null(meta)) list() else meta, auto_unbox = TRUE, pretty = 2),
+    "
")) +} + +readDataSetGuardDemo <- function(jaspResults) { + message <- tryCatch( + { + .readDataSetToEnd(columns = "MPG") # would work outside an aware run + "No error - readDataSet() should have stopped though! (not in multi-dataset mode?)" + }, + error = function(e) paste0("readDataSet() stopped it, as it should:
", + "
", conditionMessage(e), "
")) + + jaspResults[["guardDemo"]] <- createJaspHtml(message) +} + +# ------------------------------------------------------------------ internals + +.dependentValues <- function(options) { + values <- unlist(options$dependent, recursive = TRUE) + values[!is.na(values) & as.character(values) != ""] +} + +.typeSuffix <- function(value) { + match <- regmatches(as.character(value), regexec("\\.(scale|ordinal|nominal)$", value))[[1]] + if (length(match) == 2L) match[2] else NA_character_ +} + +.describeColumn <- function(columnData) { + if (is.numeric(columnData)) { + list(n = sum(!is.na(columnData)), + values = sprintf("mean %.2f, sd %.2f", mean(columnData, na.rm = TRUE), sd(columnData, na.rm = TRUE))) + } else { + levels <- unique(as.character(stats::na.omit(columnData))) + list(n = length(stats::na.omit(columnData)), + values = paste(utils::head(levels, 5), collapse = ", ")) + } +} diff --git a/Tools/buildBundle.R b/Tools/buildBundle.R new file mode 100644 index 0000000..23bf84d --- /dev/null +++ b/Tools/buildBundle.R @@ -0,0 +1,152 @@ +#!/usr/bin/env Rscript +# Build an installable .JASPModule bundle of jaspTestModule from this checkout. +# +# The jaspBase record in renv.lock is temporarily swapped for a Local record pointing at the +# jasp-desktop checkout (default: ../jasp-desktop/Engine/jaspBase), so whatever is checked out +# there (e.g. branch multiDataSetAware) is compiled and packed into the bundle. Everything is +# built from source into a private renv library under ModuleBundleBuildDir/ - no JASP cellar +# involved, so it works on a rolling-release system (Arch) with the R you have. +# +# Requires the tool library (jaspModuleTools + jaspModuleBundleManager + renv etc.), by default +# ../.toolLib; see the bottom of this file for how it was created. +# +# Usage (from the jaspTestModule directory, or anywhere): +# Rscript Tools/buildBundle.R # bundle with local jaspBase +# Rscript Tools/buildBundle.R --update-lockfile # re-resolve renv.lock first (network) +# Rscript Tools/buildBundle.R --remote-jaspbase # use the jaspBase from renv.lock (GitHub) +# Rscript Tools/buildBundle.R --jasp-version=development +# +# Result: bundles_out/jaspTestModule.JASPModule (+ manifest json), installable via +# JASP: + (add modules) menu -> Add module -> From file. +# +# One-time tool library setup: +# toolLib=/path/to/.toolLib; mkdir -p $toolLib +# Rscript -e '.libPaths(c(toolLib, .libPaths())) +# install.packages(c("renv","fs","pkgdepends","usethis","withr","curl","rjson","stringr", +# "archive","openssl","jsonlite","remotes"), lib=toolLib, +# repos="https://cloud.r-project.org") +# remotes::install_local("/Engine/jaspModuleBundleManager", lib=toolLib, +# upgrade="never", dependencies=FALSE) +# remotes::install_local("", lib=toolLib, +# upgrade="never", dependencies=FALSE)' + +args <- commandArgs(trailingOnly = TRUE) + +scriptPath <- normalizePath(sub("^--file=", "", grep("^--file=", commandArgs(FALSE), value = TRUE)[[1]])) +moduledir <- normalizePath(file.path(dirname(scriptPath), "..")) +rootdir <- normalizePath(file.path(moduledir, "..")) + +jaspBaseDir <- Sys.getenv("JASP_BASE_DIR", file.path(rootdir, "jasp-desktop", "Engine", "jaspBase")) +toolLib <- Sys.getenv("JASP_MODULE_TOOLLIB", file.path(rootdir, ".toolLib")) +workdir <- file.path(moduledir, "ModuleBundleBuildDir") +resultdir <- file.path(moduledir, "bundles_out") + +localJaspBase <- !("--remote-jaspbase" %in% args) +updateLockfile <- "--update-lockfile" %in% args + +jaspVersion <- "development" +for (a in args) if (grepl("^--jasp-version=", a)) jaspVersion <- sub("^--jasp-version=", "", a) + +if (!dir.exists(toolLib)) + stop("Tool library not found at ", toolLib, " - see the setup instructions at the bottom of this script.") + +.libPaths(c(toolLib, .libPaths())) +suppressPackageStartupMessages({ library(jsonlite) }) +library(jaspModuleTools) + +# No renv sandbox: binary R distributions (Arch!) have no R sources to link it against. +Sys.setenv(RENV_CONFIG_SANDBOX_ENABLED = "false") +options(jaspRemoteCellarRedownload = FALSE) + +lockPath <- file.path(moduledir, "renv.lock") +lockBackup <- NULL +on.exit({ + if (!is.null(lockBackup)) { + writeLines(lockBackup, lockPath) + message("restored original ", lockPath) + } +}) + +if (updateLockfile) { + message("re-resolving ", lockPath, " with pkgdepends ...") + jaspModuleTools::updateLockfile(moduledir) +} + +if (localJaspBase) { + if (!dir.exists(jaspBaseDir)) + stop("No jaspBase checkout at ", jaspBaseDir, " (set JASP_BASE_DIR or use --remote-jaspbase).") + + baseDesc <- read.dcf(file.path(jaspBaseDir, "DESCRIPTION"))[1, ] + message("swapping jaspBase in renv.lock for local checkout: ", jaspBaseDir, + " (v", baseDesc[["Version"]], ", ", + system2("git", c("-C", jaspBaseDir, "rev-parse", "--abbrev-ref", "HEAD"), stdout = TRUE), ")") + + lockBackup <- readLines(lockPath) + lock <- jsonlite::fromJSON(lockPath, simplifyVector = FALSE) + + depsFields <- baseDesc[c("Depends", "Imports")] + depsFields <- depsFields[!is.na(depsFields)] + requires <- unique(trimws(unlist(strsplit(paste(depsFields, collapse = ","), ",")))) + requires <- gsub("\\s*\\(.*", "", requires) # "Rcpp (>= 0.12.14)" -> "Rcpp" + requires <- setdiff(requires, c("", "R")) + + # Same local-record shape jaspModuleTools uses for the module itself (createLocalRecord). + lock$Packages$jaspBase <- list( + Package = "jaspBase", + Version = baseDesc[["Version"]], + Source = "Local", + Requirements = requires, + RemoteType = "local", + RemoteUrl = normalizePath(jaspBaseDir), + Cacheable = TRUE, + Hash = unname(rlang::hash(rlang::hash_file(fs::dir_ls(jaspBaseDir, type = "file", recurse = TRUE)))) + ) + + writeLines(jsonlite::toJSON(lock, auto_unbox = TRUE, pretty = 2), lockPath) +} + +dir.create(workdir, recursive = TRUE, showWarnings = FALSE) +dir.create(resultdir, showWarnings = FALSE) + +message("compiling + bundling (log is chatty, first run compiles all of jaspBase's deps) ...") +jaspModuleTools::compile( + moduledir = moduledir, + workdir = file.path(workdir, "build_dir"), + resultdir = resultdir, + createBundle = TRUE, + bundleAll = TRUE, + buildforJaspVersion = jaspVersion, + useJASPRemoteCellar = FALSE, + localizeJASPModules = "identicalToLockfile" # our lockfile (incl. the swapped jaspBase) is what we want +) + +# restore the pristine lockfile before verifying so an interrupted run can't leak the local record +if (!is.null(lockBackup)) { writeLines(lockBackup, lockPath); lockBackup <- NULL } + +# --------------------------------------------------------------------------------------------- +# Verify: the bundle manifest must list jaspBase, and the packed jaspBase must be OUR checkout +# (guard against silently packing a cached GitHub build instead). +bundle <- file.path(resultdir, "jaspTestModule.JASPModule") +if (!file.exists(bundle)) stop("compile() did not produce ", bundle) + +checkdir <- file.path(tempdir(), "bundleCheck") +unlink(checkdir, recursive = TRUE); dir.create(checkdir, recursive = TRUE) +archive::archive_extract(bundle, dir = checkdir) + +manifest <- jsonlite::fromJSON(file.path(checkdir, "jaspTestModule_manifest.json")) +jaspBaseHash <- sub(" .*", "", grep("^jaspBase ", manifest$mapping, value = TRUE)) +if (length(jaspBaseHash) != 1) stop("jaspBase not in the bundle manifest (mapping: ", manifest$mapping, ")") + +pkgdir <- file.path(checkdir, "jaspBase") +archive::archive_extract(file.path(checkdir, jaspBaseHash), dir = pkgdir) + +commonR <- file.path(pkgdir, "jaspBase", "R", "common.R") +if (!file.exists(commonR)) commonR <- file.path(pkgdir, "common.R") # L0 tar may sit one level deeper +packed <- paste(readLines(commonR), collapse = "\n") +if (localJaspBase) { + stopifnot(grepl("\\.multiDataSetMode", packed), grepl("dataSetColumnFromEncoded", packed)) + message("VERIFIED: bundled jaspBase contains the multiDataSetAware code (.multiDataSetMode + dataSetColumnFromEncoded).") +} + +message("Bundle: ", bundle, " (size: ", format(file.size(bundle) / 2^20, digits = 1), " MiB)") +message("Install in JASP: + menu -> Add module -> From file, pick that .JASPModule.") diff --git a/inst/Description.qml b/inst/Description.qml index a6b066d..ca0f46d 100644 --- a/inst/Description.qml +++ b/inst/Description.qml @@ -106,6 +106,36 @@ Description preloadData : true } + Separator {} + + GroupTitle { title: "Multi dataset" } + + // multiDataSetAware: the analysis gets every dataset its options reference as the `datasets` + // argument (jaspBase >= the multiDataSetAware branch) and VariablesForm's dataSetSelection + // dropdown becomes live. It only kicks in combined with preloadData; without it the engine + // logs that the analysis gets its data like a non-aware one. + Analysis + { + title: "Test Multi-DataSet" + qml: "testMultiDataSet.qml" + func: "multiDataSetFunc" + requiresData: true + preloadData: true + multiDataSetAware: true + } + + // Same form and function, but NOT aware: dataSetSelection must stay inert and data must + // arrive the old way (through dataset/readDataSet), as a controls-and-regression check. + Analysis + { + title: "Test Multi-DataSet (non-aware counterpart)" + qml: "testMultiDataSet.qml" + func: "multiDataSetFunc" + requiresData: true + preloadData: true + multiDataSetAware: false + } + Analysis { title: qsTr("Test optionContainsValue") diff --git a/inst/qml/testMultiDataSet.qml b/inst/qml/testMultiDataSet.qml new file mode 100644 index 0000000..87167c6 --- /dev/null +++ b/inst/qml/testMultiDataSet.qml @@ -0,0 +1,48 @@ +import QtQuick +import JASP.Controls + +Form +{ + columns: 1 + + ExplanationText + { + text: + "Test the multi-dataset awareness of VariablesForm: the dataset/filter selection at the top " + + "(only live for multiDataSetAware analyses) plus the per-dataset routing in the results.
" + + "
    " + + "
  • Open a second dataset (e.g. add another CSV) so the selection has something to choose from.
  • " + + "
  • Drag variables from different datasets into the list: the results table should route " + + "each one to its own dataset (id, title, column).
  • " + + "
  • Pick another dataset/filter in the selection: only columns of that dataset should remain " + + "selectable for new drags and the run should recompute.
  • " + + "
  • show .meta: the provenance stamps (dataSetId/filterId) on the bound options.
  • " + + "
  • guard demo: readDataSet() must hard-stop inside an aware run.
  • " + + "
  • Save the analysis to a .jasp and reopen it: the dataset routing must survive the round trip.
  • " + + "
" + } + + VariablesForm + { + dataSetSelection: true + + AvailableVariablesList { name: "allVariables" } + AssignedVariablesList + { + name: "dependent" + title: qsTr("Variables (from any dataset)") + } + } + + CheckBox + { + name: "showMeta" + label: qsTr("Show options .meta (dataSetId/filterId provenance)") + } + + CheckBox + { + name: "guardDemo" + label: qsTr("Call readDataSet() anyway (should error in aware runs)") + } +} From 2bcce9808e578b7c9e5a8a56754a771633df4989 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Tue, 6 Oct 2026 20:10:02 +0200 Subject: [PATCH 02/44] renv.lock for R 4.6.1 + working bundle build (local jaspBase swapped in) - renv.lock re-resolved on R 4.6.1 (jaspBase 6a6910db / jaspGraphs cdc4c9cf from GitHub). - Tools/buildBundle.R end-to-end on Arch: local-jaspBase Local record swapped into a copy of the lock during the build (tryCatch/finally restore - top-level on.exit is a no-op in Rscript), fake renv sandbox (symlink farm at the resolved RENV_PATHS_SANDBOX path) so jaspModuleTools' copy of restore-skipped recommended packages (codetools) works on binary R, --check-only to re-verify a bundle without rebuilding, and manifest/namespace verification that loads the bundled jaspBase and asserts the multiDataSetAware code is in it. - Tools/testdata: DataSetA/DataSetB.csv with deliberately overlapping column names (Score, Age) for the per-dataset routing test. --- Tools/buildBundle.R | 62 +++++++++++++++------ Tools/testdata/DataSetA.csv | 11 ++++ Tools/testdata/DataSetB.csv | 11 ++++ renv.lock | 104 +++++++++++++++++++----------------- 4 files changed, 121 insertions(+), 67 deletions(-) create mode 100644 Tools/testdata/DataSetA.csv create mode 100644 Tools/testdata/DataSetB.csv diff --git a/Tools/buildBundle.R b/Tools/buildBundle.R index 23bf84d..7b4d398 100644 --- a/Tools/buildBundle.R +++ b/Tools/buildBundle.R @@ -60,19 +60,37 @@ options(jaspRemoteCellarRedownload = FALSE) lockPath <- file.path(moduledir, "renv.lock") lockBackup <- NULL -on.exit({ + +restoreLock <- function() { if (!is.null(lockBackup)) { writeLines(lockBackup, lockPath) + lockBackup <<- NULL message("restored original ", lockPath) } -}) +} if (updateLockfile) { message("re-resolving ", lockPath, " with pkgdepends ...") jaspModuleTools::updateLockfile(moduledir) + lockBackup <- readLines(lockPath) # keep the freshly resolved one; never lose it } -if (localJaspBase) { +dir.create(workdir, recursive = TRUE, showWarnings = FALSE) + +# Binary R distributions (Arch!) have no R sources, so renv's sandbox can't be built - but +# jaspModuleTools copies packages that renv::restore skips because they live in the system +# library (recommended ones like codetools) out of it. Fake it: the sandbox is nothing but a +# symlink farm over the system library, so build exactly that at the path renv resolves +# (RENV_PATHS_SANDBOX plus its linux-arch-rolling/R-4.6/ suffix). +Sys.setenv(RENV_PATHS_SANDBOX = file.path(workdir, "sandbox")) +fakeSandbox <- renv:::renv_paths_sandbox() +dir.create(fakeSandbox, recursive = TRUE, showWarnings = FALSE) +for (lib in unique(c(R.home("library"), .libPaths()))) + for (pkg in list.dirs(lib, recursive = FALSE, full.names = FALSE)) + if (!file.exists(file.path(fakeSandbox, pkg)) && nzchar(pkg) && !grepl("^00LOCK", pkg)) + file.symlink(file.path(lib, pkg), file.path(fakeSandbox, pkg)) + +if (localJaspBase && !("--check-only" %in% args)) { if (!dir.exists(jaspBaseDir)) stop("No jaspBase checkout at ", jaspBaseDir, " (set JASP_BASE_DIR or use --remote-jaspbase).") @@ -105,11 +123,10 @@ if (localJaspBase) { writeLines(jsonlite::toJSON(lock, auto_unbox = TRUE, pretty = 2), lockPath) } -dir.create(workdir, recursive = TRUE, showWarnings = FALSE) dir.create(resultdir, showWarnings = FALSE) message("compiling + bundling (log is chatty, first run compiles all of jaspBase's deps) ...") -jaspModuleTools::compile( +if (!("--check-only" %in% args)) tryCatch(jaspModuleTools::compile( moduledir = moduledir, workdir = file.path(workdir, "build_dir"), resultdir = resultdir, @@ -118,10 +135,12 @@ jaspModuleTools::compile( buildforJaspVersion = jaspVersion, useJASPRemoteCellar = FALSE, localizeJASPModules = "identicalToLockfile" # our lockfile (incl. the swapped jaspBase) is what we want -) - -# restore the pristine lockfile before verifying so an interrupted run can't leak the local record -if (!is.null(lockBackup)) { writeLines(lockBackup, lockPath); lockBackup <- NULL } +), error = function(e) { + message("compile() failed: ", conditionMessage(e)) + calls <- sys.calls() + for (i in seq_along(calls)) message(sprintf(" #%02d %s", i, substr(paste(deparse(calls[[i]]), collapse = " "), 1, 160))) + stop(e) +}, finally = restoreLock()) # --------------------------------------------------------------------------------------------- # Verify: the bundle manifest must list jaspBase, and the packed jaspBase must be OUR checkout @@ -134,18 +153,27 @@ unlink(checkdir, recursive = TRUE); dir.create(checkdir, recursive = TRUE) archive::archive_extract(bundle, dir = checkdir) manifest <- jsonlite::fromJSON(file.path(checkdir, "jaspTestModule_manifest.json")) -jaspBaseHash <- sub(" .*", "", grep("^jaspBase ", manifest$mapping, value = TRUE)) -if (length(jaspBaseHash) != 1) stop("jaspBase not in the bundle manifest (mapping: ", manifest$mapping, ")") +jaspBaseHash <- sub(" .*", "", grep("=> jaspBase_", manifest$mapping, value = TRUE)) +if (length(jaspBaseHash) != 1) stop("jaspBase not in the bundle manifest (mapping: ", paste(manifest$mapping, collapse=", "), ")") -pkgdir <- file.path(checkdir, "jaspBase") +pkgdir <- file.path(checkdir, "lib", "jaspBase") +dir.create(pkgdir, recursive = TRUE, showWarnings = FALSE) archive::archive_extract(file.path(checkdir, jaspBaseHash), dir = pkgdir) -commonR <- file.path(pkgdir, "jaspBase", "R", "common.R") -if (!file.exists(commonR)) commonR <- file.path(pkgdir, "common.R") # L0 tar may sit one level deeper -packed <- paste(readLines(commonR), collapse = "\n") +# The L0 tars are *installed* package trees: load the bundled jaspBase and look at what is +# actually in it. That proves OUR build (not some cached GitHub one) is in the bundle. +.libPaths(c(file.path(checkdir, "lib"), file.path(workdir, "build_dir", "jaspTestModule"), .libPaths())) +suppressPackageStartupMessages(library(jaspBase, lib.loc = file.path(checkdir, "lib"))) + if (localJaspBase) { - stopifnot(grepl("\\.multiDataSetMode", packed), grepl("dataSetColumnFromEncoded", packed)) - message("VERIFIED: bundled jaspBase contains the multiDataSetAware code (.multiDataSetMode + dataSetColumnFromEncoded).") + markers <- c("dataSetColumnFromEncoded", "dataSetIdFromEncoded", "getDataSetColumn") %in% getNamespaceExports("jaspBase") + runBody <- any(grepl("multiDataSet|dataSetEncodedNames", deparse(body(jaspBase::runJaspResults)))) + if (!all(markers) || !runBody) + stop("bundled jaspBase is MISSING the local multiDataSetAware code (exports: ", + paste(names(markers)[!markers], collapse = ", "), if (runBody) "" else " + runJaspResults body", ")") + message("VERIFIED: bundled jaspBase loads and carries the local multiDataSetAware code.") +} else { + message("bundle jaspBase version: ", as.character(utils::packageVersion("jaspBase", lib.loc = file.path(checkdir, "lib")))) } message("Bundle: ", bundle, " (size: ", format(file.size(bundle) / 2^20, digits = 1), " MiB)") diff --git a/Tools/testdata/DataSetA.csv b/Tools/testdata/DataSetA.csv new file mode 100644 index 0000000..0927de6 --- /dev/null +++ b/Tools/testdata/DataSetA.csv @@ -0,0 +1,11 @@ +Score,Age,Group +12.5,31,A +9.1,45,B +15.3,28,A +7.8,52,C +19.6,37,B +11.2,41,A +8.4,33,C +16.9,29,B +13.7,48,A +10.5,36,C diff --git a/Tools/testdata/DataSetB.csv b/Tools/testdata/DataSetB.csv new file mode 100644 index 0000000..6673ac3 --- /dev/null +++ b/Tools/testdata/DataSetB.csv @@ -0,0 +1,11 @@ +Score,Age,Condition +22.4,61,X +14.8,39,Y +18.2,44,X +9.9,50,Z +25.1,35,Y +12.6,58,X +21.3,42,Z +17.7,47,Y +11.8,53,X +23.5,38,Z diff --git a/renv.lock b/renv.lock index 3b3ac06..e171ff6 100644 --- a/renv.lock +++ b/renv.lock @@ -1,6 +1,6 @@ { "R": { - "Version": "4.5.2", + "Version": "4.6.1", "Repositories": [ { "Name": "CRAN", @@ -27,7 +27,7 @@ }, "Rcpp": { "Package": "Rcpp", - "Version": "1.1.1", + "Version": "1.1.2", "Source": "Repository", "Requirements": [ "methods", @@ -37,7 +37,7 @@ }, "S7": { "Package": "S7", - "Version": "0.2.1", + "Version": "0.2.2", "Source": "Repository", "Requirements": [ "R", @@ -54,7 +54,7 @@ }, "base64enc": { "Package": "base64enc", - "Version": "0.1-3", + "Version": "0.1-6", "Source": "Repository", "Requirements": [ "R" @@ -62,7 +62,7 @@ }, "bslib": { "Package": "bslib", - "Version": "0.9.0", + "Version": "0.12.0", "Source": "Repository", "Requirements": [ "base64enc", @@ -91,9 +91,10 @@ }, "callr": { "Package": "callr", - "Version": "3.7.6", + "Version": "3.8.0", "Source": "Repository", "Requirements": [ + "otel", "processx", "R", "R6", @@ -102,7 +103,7 @@ }, "cli": { "Package": "cli", - "Version": "3.6.5", + "Version": "3.6.6", "Source": "Repository", "Requirements": [ "R", @@ -119,7 +120,7 @@ }, "cpp11": { "Package": "cpp11", - "Version": "0.5.2", + "Version": "0.5.5", "Source": "Repository", "Requirements": [ "R" @@ -138,7 +139,7 @@ }, "curl": { "Package": "curl", - "Version": "7.0.0", + "Version": "8.0.0", "Source": "Repository", "Requirements": [ "R" @@ -146,7 +147,7 @@ }, "data.table": { "Package": "data.table", - "Version": "1.18.0", + "Version": "1.18.6.1", "Source": "Repository", "Requirements": [ "methods", @@ -175,7 +176,7 @@ }, "dplyr": { "Package": "dplyr", - "Version": "1.1.4", + "Version": "1.2.1", "Source": "Repository", "Requirements": [ "cli", @@ -252,7 +253,7 @@ }, "fs": { "Package": "fs", - "Version": "1.6.6", + "Version": "2.1.0", "Source": "Repository", "Requirements": [ "methods", @@ -261,7 +262,7 @@ }, "gdtools": { "Package": "gdtools", - "Version": "0.4.4", + "Version": "0.5.1", "Source": "Repository", "Requirements": [ "fontquiver", @@ -283,7 +284,7 @@ }, "ggplot2": { "Package": "ggplot2", - "Version": "4.0.1", + "Version": "4.0.3", "Source": "Repository", "Requirements": [ "cli", @@ -303,7 +304,7 @@ }, "glue": { "Package": "glue", - "Version": "1.8.0", + "Version": "1.8.1", "Source": "Repository", "Requirements": [ "methods", @@ -312,7 +313,7 @@ }, "gridExtra": { "Package": "gridExtra", - "Version": "2.3", + "Version": "2.3.1", "Source": "Repository", "Requirements": [ "graphics", @@ -348,7 +349,7 @@ }, "highr": { "Package": "highr", - "Version": "0.11", + "Version": "0.12", "Source": "Repository", "Requirements": [ "R", @@ -384,7 +385,7 @@ }, "httr": { "Package": "httr", - "Version": "1.4.7", + "Version": "1.4.9", "Source": "Repository", "Requirements": [ "curl", @@ -436,14 +437,13 @@ "RemoteHost": "api.github.com", "RemoteUsername": "jasp-stats", "RemoteRepo": "jaspBase", - "RemoteSha": "a5770b1bb36f6c5aebca3d15e5eab0dd4976602e" + "RemoteSha": "6a6910dbe148caba2d82d60b8c42305ac40d2f1d" }, "jaspGraphs": { "Package": "jaspGraphs", "Version": "0.20.0", "Source": "GitHub", "Requirements": [ - "testthat", "cli", "ggplot2", "gridExtra", @@ -454,13 +454,15 @@ "plotly", "R6", "rlang", - "scales" + "scales", + "testthat", + "vdiffr" ], "RemoteType": "github", "RemoteHost": "api.github.com", "RemoteUsername": "jasp-stats", "RemoteRepo": "jaspGraphs", - "RemoteSha": "db3c899263a1d8d236ed0e7e3a7b6814b48c14ae" + "RemoteSha": "cdc4c9cf20f52c22c24162ea6fcbdd6b211e92db" }, "jquerylib": { "Package": "jquerylib", @@ -480,7 +482,7 @@ }, "knitr": { "Package": "knitr", - "Version": "1.51", + "Version": "1.52", "Source": "Repository", "Requirements": [ "evaluate", @@ -503,7 +505,7 @@ }, "later": { "Package": "later", - "Version": "1.4.5", + "Version": "1.4.8", "Source": "Repository", "Requirements": [ "R", @@ -513,10 +515,11 @@ }, "lazyeval": { "Package": "lazyeval", - "Version": "0.2.2", + "Version": "0.2.3", "Source": "Repository", "Requirements": [ - "R" + "R", + "rlang" ] }, "lifecycle": { @@ -531,7 +534,7 @@ }, "magrittr": { "Package": "magrittr", - "Version": "2.0.4", + "Version": "2.0.5", "Source": "Repository", "Requirements": [ "R" @@ -556,7 +559,7 @@ }, "officer": { "Package": "officer", - "Version": "0.7.3", + "Version": "0.7.6", "Source": "Repository", "Requirements": [ "cli", @@ -576,7 +579,7 @@ }, "openssl": { "Package": "openssl", - "Version": "2.3.4", + "Version": "2.4.2", "Source": "Repository", "Requirements": [ "askpass" @@ -627,7 +630,7 @@ }, "plotly": { "Package": "plotly", - "Version": "4.11.0", + "Version": "4.12.1", "Source": "Repository", "Requirements": [ "base64enc", @@ -640,7 +643,6 @@ "htmlwidgets", "httr", "jsonlite", - "lazyeval", "magrittr", "promises", "purrr", @@ -666,7 +668,7 @@ }, "processx": { "Package": "processx", - "Version": "3.8.6", + "Version": "3.9.0", "Source": "Repository", "Requirements": [ "ps", @@ -692,7 +694,7 @@ }, "ps": { "Package": "ps", - "Version": "1.9.1", + "Version": "1.9.3", "Source": "Repository", "Requirements": [ "R", @@ -701,7 +703,7 @@ }, "purrr": { "Package": "purrr", - "Version": "1.2.1", + "Version": "1.2.2", "Source": "Repository", "Requirements": [ "cli", @@ -714,7 +716,7 @@ }, "ragg": { "Package": "ragg", - "Version": "1.5.0", + "Version": "1.5.2", "Source": "Repository", "Requirements": [ "systemfonts", @@ -731,7 +733,7 @@ }, "rlang": { "Package": "rlang", - "Version": "1.1.7", + "Version": "1.3.0", "Source": "Repository", "Requirements": [ "R", @@ -740,7 +742,7 @@ }, "rmarkdown": { "Package": "rmarkdown", - "Version": "2.30", + "Version": "2.32", "Source": "Repository", "Requirements": [ "bslib", @@ -761,7 +763,7 @@ }, "rvg": { "Package": "rvg", - "Version": "0.4.0", + "Version": "0.4.2", "Source": "Repository", "Requirements": [ "gdtools", @@ -805,7 +807,7 @@ }, "stringi": { "Package": "stringi", - "Version": "1.8.7", + "Version": "1.8.9", "Source": "Repository", "Requirements": [ "R", @@ -852,7 +854,7 @@ }, "systemfonts": { "Package": "systemfonts", - "Version": "1.3.1", + "Version": "1.3.2", "Source": "Repository", "Requirements": [ "base64enc", @@ -867,7 +869,7 @@ }, "textshaping": { "Package": "textshaping", - "Version": "1.0.4", + "Version": "1.0.5", "Source": "Repository", "Requirements": [ "cpp11", @@ -933,7 +935,7 @@ }, "tinytex": { "Package": "tinytex", - "Version": "0.58", + "Version": "0.61", "Source": "Repository", "Requirements": [ "xfun" @@ -949,7 +951,7 @@ }, "uuid": { "Package": "uuid", - "Version": "1.2-1", + "Version": "1.2-2", "Source": "Repository", "Requirements": [ "R" @@ -957,7 +959,7 @@ }, "vctrs": { "Package": "vctrs", - "Version": "0.7.0", + "Version": "0.7.3", "Source": "Repository", "Requirements": [ "cli", @@ -969,7 +971,7 @@ }, "viridisLite": { "Package": "viridisLite", - "Version": "0.4.2", + "Version": "0.4.3", "Source": "Repository", "Requirements": [ "R" @@ -977,7 +979,7 @@ }, "withr": { "Package": "withr", - "Version": "3.0.2", + "Version": "3.0.3", "Source": "Repository", "Requirements": [ "graphics", @@ -987,7 +989,7 @@ }, "xfun": { "Package": "xfun", - "Version": "0.56", + "Version": "0.61", "Source": "Repository", "Requirements": [ "grDevices", @@ -998,7 +1000,7 @@ }, "xml2": { "Package": "xml2", - "Version": "1.5.2", + "Version": "1.6.0", "Source": "Repository", "Requirements": [ "cli", @@ -1015,9 +1017,11 @@ }, "zip": { "Package": "zip", - "Version": "2.3.3", + "Version": "3.0.2", "Source": "Repository", - "Requirements": [] + "Requirements": [ + "cli" + ] } } } From c689e390159f4e0d9f780f6aaed3ed871216dfb8 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Tue, 6 Oct 2026 20:36:27 +0200 Subject: [PATCH 03/44] fix testMultiDataSet.qml missing ./common import (ExplanationText is not a type) ExplanationText is the module-local component in inst/qml/common/, so the form needs import "./common" like the other test forms do. Caught by loading the form through the native SyntaxInterface with jaspSyntax (github.com/jasp-stats/jaspSyntax) - which is why buildBundle.R now runs readModuleDescription() + readDefaultAnalysisOptions() as a build gate: a QML that does not create its Form throws there, verified with a deliberately broken copy, so this class of error can no longer ride along into a bundle. --- Tools/buildBundle.R | 18 ++++++++++++++++++ inst/qml/testMultiDataSet.qml | 1 + 2 files changed, 19 insertions(+) diff --git a/Tools/buildBundle.R b/Tools/buildBundle.R index 7b4d398..e4bdcdb 100644 --- a/Tools/buildBundle.R +++ b/Tools/buildBundle.R @@ -142,6 +142,24 @@ if (!("--check-only" %in% args)) tryCatch(jaspModuleTools::compile( stop(e) }, finally = restoreLock()) +# Load the module Description and the multi-dataset form through the native SyntaxInterface +# (jaspSyntax, in the tool library): catches QML errors like missing imports or unknown types +# at build time, without firing up JASP. Skipped when jaspSyntax isn't installed. +Sys.setenv(QT_QPA_PLATFORM = "offscreen") +if (requireNamespace("jaspSyntax", quietly = TRUE)) { + message("checking module Description + QML forms via jaspSyntax ...") + suppressPackageStartupMessages(library(jaspSyntax)) + desc <- readModuleDescription(moduledir) + stopifnot(!is.null(desc)) + for (analysisFunc in c("multiDataSetFunc")) { + opts <- readDefaultAnalysisOptions(moduledir, analysisFunc) + stopifnot(is.list(opts), !is.null(attr(opts, "qmlFile"))) + message(" ", analysisFunc, " -> ", basename(attr(opts, "qmlFile")), " loads OK") + } +} else { + message("NOTE: jaspSyntax not installed (see github.com/jasp-stats/jaspSyntax) - QML not checked.") +} + # --------------------------------------------------------------------------------------------- # Verify: the bundle manifest must list jaspBase, and the packed jaspBase must be OUR checkout # (guard against silently packing a cached GitHub build instead). diff --git a/inst/qml/testMultiDataSet.qml b/inst/qml/testMultiDataSet.qml index 87167c6..cb29b2b 100644 --- a/inst/qml/testMultiDataSet.qml +++ b/inst/qml/testMultiDataSet.qml @@ -1,5 +1,6 @@ import QtQuick import JASP.Controls +import "./common" Form { From a1de1e696a0b368fcab8476e28302ea43896598c Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Wed, 7 Oct 2026 00:04:35 +0200 Subject: [PATCH 04/44] multiDataSetFunc wrapped (hasWrapper): Internal entry point + generated datasets wrapper - Analysis body renamed to multiDataSetFuncInternal; the aware Description entry gets hasWrapper: true, so JASP calls ...Internal and the generated wrapper (R/multiDataSetFuncWrapper.R, via jaspSyntax) exposes multiDataSetFunc(datasets=, ...) for R syntax use - datasets instead of data, stored via jaspBase::storeDataSets. - The non-aware twin now points at multiDataSetFuncInternal directly (it cannot use the wrapper name: JASP would call the wrapper as the analysis). - NAMESPACE: import(jaspBase) - without it, wrapper calls from a plain R session could not resolve createJaspHtml etc. (JASP attaches jaspBase, R does not). - Tools/testMultiDataSet.R: full headless test over three paths - engine-style queued slices (jaspTools datasets=), the wrapper in a clean subprocess, and a legacy non-aware run as controls; 21 checks, all passing against the local jaspBase. --- NAMESPACE | 1 + R/multiDataSet.R | 17 ++--- R/multiDataSetFuncWrapper.R | 52 ++++++++++++++ Tools/buildBundle.R | 14 +++- Tools/testMultiDataSet.R | 119 +++++++++++++++++++++++++++++++ Tools/testMultiDataSet_wrapper.R | 51 +++++++++++++ inst/Description.qml | 8 ++- 7 files changed, 250 insertions(+), 12 deletions(-) create mode 100644 R/multiDataSetFuncWrapper.R create mode 100644 Tools/testMultiDataSet.R create mode 100644 Tools/testMultiDataSet_wrapper.R diff --git a/NAMESPACE b/NAMESPACE index 080553b..c1e4a3f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,3 +1,4 @@ exportPattern('^[[:alpha:]]+') + import('jaspBase'); import('svglite'); import('stringi'); diff --git a/R/multiDataSet.R b/R/multiDataSet.R index 1813867..9e7fc96 100644 --- a/R/multiDataSet.R +++ b/R/multiDataSet.R @@ -6,7 +6,7 @@ # which jaspBase's dataSetIdFromEncoded / dataSetNameFromEncoded / getDataSetColumn route. # While such a run is active, readDataSet* stops: one 'current dataset' is meaningless here. -multiDataSetFunc <- function(jaspResults, dataset, options, datasets = NULL) { +multiDataSetFuncInternal <- function(jaspResults, dataset, options, datasets = NULL) { aware <- !is.null(datasets) jaspResults[["explanation"]] <- createJaspHtml(paste0( @@ -74,7 +74,7 @@ selectedVariablesTable <- function(jaspResults, options, datasets, dataset) { entry <- list(variable = column, dataSetId = "-", dataSetName = "-", column = column, type = .typeSuffix(value), n = NA_integer_, values = "") if (!is.null(dataset) && column %in% names(dataset)) - entry <- c(entry, .describeColumn(dataset[[column]])) + entry <- .fillDescription(entry, dataset[[column]]) table[[value]] <- entry next } @@ -91,7 +91,7 @@ selectedVariablesTable <- function(jaspResults, options, datasets, dataset) { columnData <- jaspBase::getDataSetColumn(value, datasets) if (!is.null(columnData)) - entry <- c(entry, .describeColumn(columnData)) + entry <- .fillDescription(entry, columnData) table[[value]] <- entry } @@ -129,13 +129,14 @@ readDataSetGuardDemo <- function(jaspResults) { if (length(match) == 2L) match[2] else NA_character_ } -.describeColumn <- function(columnData) { +.fillDescription <- function(entry, columnData) { if (is.numeric(columnData)) { - list(n = sum(!is.na(columnData)), - values = sprintf("mean %.2f, sd %.2f", mean(columnData, na.rm = TRUE), sd(columnData, na.rm = TRUE))) + entry$n <- sum(!is.na(columnData)) + entry$values <- sprintf("mean %.2f, sd %.2f", mean(columnData, na.rm = TRUE), sd(columnData, na.rm = TRUE)) } else { levels <- unique(as.character(stats::na.omit(columnData))) - list(n = length(stats::na.omit(columnData)), - values = paste(utils::head(levels, 5), collapse = ", ")) + entry$n <- length(stats::na.omit(columnData)) + entry$values <- paste(utils::head(levels, 5), collapse = ", ") } + entry } diff --git a/R/multiDataSetFuncWrapper.R b/R/multiDataSetFuncWrapper.R new file mode 100644 index 0000000..382455f --- /dev/null +++ b/R/multiDataSetFuncWrapper.R @@ -0,0 +1,52 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test Multi-DataSet +#' +multiDataSetFunc <- function( + datasets = NULL, + version = "1", + dependent = list(types = list(), value = list()), + guardDemo = FALSE, + plotHeight = 320, + plotWidth = 480, + showMeta = FALSE) { + + defaultArgCalls <- formals(jaspTestModule::multiDataSetFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["datasets"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(datasets)) { + jaspBase::storeDataSets(datasets) + } + + data <- if (!is.null(datasets) && length(datasets)) datasets[[1]] else NULL + + optionsWithFormula <- c("dependent") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "multiDataSetFunc", "testMultiDataSet.qml", options, version, TRUE, datasets = datasets)) +} \ No newline at end of file diff --git a/Tools/buildBundle.R b/Tools/buildBundle.R index e4bdcdb..9e07d2d 100644 --- a/Tools/buildBundle.R +++ b/Tools/buildBundle.R @@ -125,6 +125,18 @@ if (localJaspBase && !("--check-only" %in% args)) { dir.create(resultdir, showWarnings = FALSE) +# renv::restore skips same-version records, so a changed local jaspBase checkout would silently +# NOT be rebuilt into the pkglib (the version stays 0.20.4). When bundling against the local +# checkout: drop the installed jaspBase first so it is always rebuilt from source. +if (localJaspBase && !("--check-only" %in% args)) { + pkglib <- file.path(workdir, "build_dir", basename(moduledir)) + installedBase <- file.path(pkglib, "jaspBase") + if (dir.exists(installedBase)) { + message("removing stale jaspBase from ", pkglib, " (will rebuild from the local checkout)") + unlink(installedBase, recursive = TRUE) + } +} + message("compiling + bundling (log is chatty, first run compiles all of jaspBase's deps) ...") if (!("--check-only" %in% args)) tryCatch(jaspModuleTools::compile( moduledir = moduledir, @@ -184,7 +196,7 @@ archive::archive_extract(file.path(checkdir, jaspBaseHash), dir = pkgdir) suppressPackageStartupMessages(library(jaspBase, lib.loc = file.path(checkdir, "lib"))) if (localJaspBase) { - markers <- c("dataSetColumnFromEncoded", "dataSetIdFromEncoded", "getDataSetColumn") %in% getNamespaceExports("jaspBase") + markers <- c("dataSetColumnFromEncoded", "dataSetIdFromEncoded", "getDataSetColumn", "storeDataSets") %in% getNamespaceExports("jaspBase") runBody <- any(grepl("multiDataSet|dataSetEncodedNames", deparse(body(jaspBase::runJaspResults)))) if (!all(markers) || !runBody) stop("bundled jaspBase is MISSING the local multiDataSetAware code (exports: ", diff --git a/Tools/testMultiDataSet.R b/Tools/testMultiDataSet.R new file mode 100644 index 0000000..fb9b07c --- /dev/null +++ b/Tools/testMultiDataSet.R @@ -0,0 +1,119 @@ +#!/usr/bin/env Rscript +# Full headless test of the multi-dataset aware analysis (Test Multi-DataSet). +# +# Path 1 (engine-style, jaspTools::runAnalysis + datasets=): options carry encoded column +# names with embedded dataset ids; jaspTools queues the slices exactly like the engine does. +# Path 2 (wrapper, jaspTestModule::multiDataSetFunc(datasets=...)): plain column names plus a +# named list of datasets, runWrappedAnalysis -> runJaspResults with a direct R-side handout. +# +# Run with: Rscript Tools/testMultiDataSet.R (after Tools/buildBundle.R at least once, +# which installs the patched jaspBase + this module into the build library) +# +# Override locations with JASP_TEST_PKGLIB / JASP_MODULE_TOOLLIB. + +moduledir <- normalizePath(file.path(dirname(sub("^--file=", "", grep("^--file=", commandArgs(FALSE), value = TRUE)[[1]])), "..")) +pkglib <- Sys.getenv("JASP_TEST_PKGLIB", file.path(moduledir, "ModuleBundleBuildDir", "build_dir", "jaspTestModule")) +toollib <- Sys.getenv("JASP_MODULE_TOOLLIB", normalizePath(file.path(moduledir, "..", ".toolLib"))) + +stopifnot(dir.exists(pkglib), dir.exists(toollib)) +setwd(moduledir) +.libPaths(c(pkglib, toollib, .libPaths())) + +Sys.setenv(QT_QPA_PLATFORM = "offscreen") + +failures <- 0L +check <- function(ok, label) { + cat(if (isTRUE(ok)) "PASS :" else "FAIL :", label, "\n") + if (!isTRUE(ok)) failures <<- failures + 1L +} + +# jaspTable json is transposed: data[[j]] holds one table column (schema.fields order), its cells +# keyed by per-row ids ("colN", NOT in insertion order). columnValues returns cells + their row +# keys so assertions can be order-independent. +columnValues <- function(tb, fieldName) { + j <- which(vapply(tb$schema$fields, function(f) identical(f$name, fieldName), logical(1)))[1] + d <- tb$data[[j]] + rowKeys <- names(d)[vapply(d, length, integer(1)) > 0L] + stats::setNames(vapply(d[rowKeys], function(v) as.character(v), character(1), USE.NAMES = FALSE), rowKeys) +} + +datasets <- list( + "11" = read.csv(file.path(moduledir, "Tools/testdata/DataSetA.csv")), + "12" = read.csv(file.path(moduledir, "Tools/testdata/DataSetB.csv"))) +attr(datasets, "dataSetNames") <- c("11" = "DataSetA", "12" = "DataSetB") + +# ------------------------------------------------------------------ Path 1: jaspTools queue ---- +cat("== Path 1: jaspTools::runAnalysis with queued slices (engine semantics) ==\n") +suppressMessages(library(jaspTools)) +suppressMessages(jaspTools::setupJaspTools( + pathJaspDesktop = normalizePath(file.path(moduledir, "..", "jasp-desktop")), + installJaspModules = FALSE, installJaspCorePkgs = FALSE, quiet = TRUE, force = TRUE)) +jaspTools::setPkgOption("module.dirs", moduledir) +jaspTools::setPkgOption("reinstall.modules", FALSE) + +opts1 <- list(dependent = c("JASPColumn_11_0.scale", "JASPColumn_11_2.nominal", + "JASPColumn_12_0.scale", "JASPColumn_12_1.scale"), + showMeta = FALSE, guardDemo = TRUE, plotWidth = 400, plotHeight = 300) +res1 <- jaspTools::runAnalysis("multiDataSetFunc", options = opts1, datasets = datasets, view = FALSE, quiet = TRUE) +r1 <- res1$results + +check(identical(res1$status, "complete"), "run 1 completed") +check(length(r1$dataSetOverview$schema$fields) == 5L, "dataset overview has 5 columns (id/title/rows/cols/columnNames)") +check(length(r1$dataSetOverview$data) == 5L, "dataset overview data has exactly the 5 schema columns (no duplicate n/values)") + +ovId <- columnValues(r1$dataSetOverview, "id") +ovTitle <- columnValues(r1$dataSetOverview, "title") +check(identical(unname(ovId), c("11", "12")), "overview ids 11,12") +check(identical(unname(ovTitle), c("DataSetA", "DataSetB")), "overview titles from dataSetNames attr") + +sv <- r1$selectedVariables +svVar <- columnValues(sv, "variable") +svId <- columnValues(sv, "dataSetId") +svName <- columnValues(sv, "dataSetName") +svCol <- columnValues(sv, "column") +svType <- columnValues(sv, "type") +svN <- columnValues(sv, "n") +# keyed by the row id so assertions do not depend on json row order +byRow <- function(v) v[svVar] +expected <- list( + "JASPColumn_11_0.scale" = c(id = "11", name = "DataSetA", column = "Score", type = "scale"), + "JASPColumn_11_2.nominal" = c(id = "11", name = "DataSetA", column = "Group", type = "nominal"), + "JASPColumn_12_0.scale" = c(id = "12", name = "DataSetB", column = "Score", type = "scale"), + "JASPColumn_12_1.scale" = c(id = "12", name = "DataSetB", column = "Age", type = "scale")) +check(all(names(expected) %in% svVar), "all four dependents present in the routing table") +for (v in names(expected)) if (v %in% svVar) { + i <- which(svVar == v)[1] + check(svId[i] == expected[[v]]["id"] && svName[i] == expected[[v]]["name"] && + svCol[i] == expected[[v]]["column"] && svType[i] == expected[[v]]["type"], + paste0("routing ", v, " -> ", expected[[v]]["name"], "/", expected[[v]]["column"])) +} +check(all(svN == "10"), "all four columns read 10 rows") + +raw1 <- jsonlite::toJSON(res1, auto_unbox = TRUE) +check(grepl("not available in multi-dataset aware analyses", raw1, fixed = TRUE), + "guard demo: readDataSet() hard-stopped during the aware run") + +# ------------------------------------------------------------------ Path 2: wrapper call ------- +# The wrapper path is run in a clean Rscript subprocess (no jaspTools runtime in the way), +# which is exactly the RStudio/syntax-mode scenario the wrapper is made for. +cat("\n== Path 2: wrapper (datasets= named list, plain column names; clean subprocess) ==\n") +path2Script <- file.path(moduledir, "Tools", "testMultiDataSet_wrapper.R") +status2 <- system2("Rscript", c(shQuote(path2Script)), stdout = TRUE, stderr = FALSE) +writeLines(status2) +check(any(grepl("MULTIDATASET-WRAPPER-OK", status2, fixed = TRUE)), "wrapper subprocess succeeded") + +# ------------------------------------------------------------------ Path 3: legacy still OK ---- +cat("\n== Path 3: legacy non-aware run (no datasets) ==\n") +res3 <- jaspTools::runAnalysis("multiDataSetFunc", dataset = datasets[["11"]], + options = list(dependent = c("Score"), showMeta = FALSE, + guardDemo = TRUE, plotWidth = 400, plotHeight = 300), + view = FALSE, quiet = TRUE) +raw3 <- jsonlite::toJSON(res3, auto_unbox = TRUE) +check(identical(res3$status, "complete"), "legacy run completed") +check(grepl("\\(current\\)", raw3), "legacy run used the single-dataset path (no multi mode)") +check(!grepl("not available in multi-dataset", raw3, fixed = TRUE), + "guard demo: readDataSet() works normally outside aware runs") + +cat("\n") +if (failures > 0L) stop(failures, " multi-dataset test check(s) failed") +cat("All multi-dataset analysis tests passed\n") diff --git a/Tools/testMultiDataSet_wrapper.R b/Tools/testMultiDataSet_wrapper.R new file mode 100644 index 0000000..e6329a8 --- /dev/null +++ b/Tools/testMultiDataSet_wrapper.R @@ -0,0 +1,51 @@ +#!/usr/bin/env Rscript +# Wrapper-path check of the multi-dataset analysis: no jaspTools, no JASP - just +# jaspTestModule::multiDataSetFunc(datasets = , ...) like an R user would call it. +# Run by Tools/testMultiDataSet.R, but standalone-runnable too. + +moduledir <- normalizePath(file.path(dirname(sub("^--file=", "", grep("^--file=", commandArgs(FALSE), value = TRUE)[[1]])), "..")) +pkglib <- Sys.getenv("JASP_TEST_PKGLIB", file.path(moduledir, "ModuleBundleBuildDir", "build_dir", "jaspTestModule")) +toollib <- Sys.getenv("JASP_MODULE_TOOLLIB", normalizePath(file.path(moduledir, "..", ".toolLib"))) +stopifnot(dir.exists(pkglib), dir.exists(toollib)) +.libPaths(c(pkglib, toollib, .libPaths())) +Sys.setenv(QT_QPA_PLATFORM = "offscreen") + +suppressMessages(library(jaspTestModule)) + +datasets <- list( + "11" = read.csv(file.path(moduledir, "Tools/testdata/DataSetA.csv")), + "12" = read.csv(file.path(moduledir, "Tools/testdata/DataSetB.csv"))) +attr(datasets, "dataSetNames") <- c("11" = "DataSetA", "12" = "DataSetB") + +res <- jaspTestModule::multiDataSetFunc(datasets = datasets, dependent = c("Score", "Condition")) +raw <- as.character(res$.__enclos_env__$private$getResults()) +j <- jsonlite::fromJSON(raw, simplifyVector = FALSE)$results + +# jaspTable json is transposed: data[[j]] = column j (schema order), cells keyed by row id +columnValues <- function(tb, fieldName) { + idx <- which(vapply(tb$schema$fields, function(f) identical(f$name, fieldName), logical(1)))[1] + d <- tb$data[[idx]] + rowKeys <- names(d)[vapply(d, length, integer(1)) > 0L] + vapply(d[rowKeys], function(v) as.character(v), character(1), USE.NAMES = FALSE) +} + +ok <- TRUE +check <- function(cond, label) { + cat(if (isTRUE(cond)) "PASS :" else "FAIL :", "wrapper -", label, "\n") + if (!isTRUE(cond)) ok <<- FALSE +} + +check(grepl("Aware run", raw, fixed = TRUE), "analysis ran in aware mode (datasets= arrived)") +check(identical(columnValues(j$dataSetOverview, "id"), c("11", "12")), "wrapper overview lists both dataset ids") +check(identical(columnValues(j$dataSetOverview, "title"), c("DataSetA", "DataSetB")), "wrapper overview lists both titles") + +sv <- j$selectedVariables +svCol <- columnValues(sv, "column") +svN <- columnValues(sv, "n") +svValues <- columnValues(sv, "values") +check(any(svCol == "Score") && any(svCol == "Condition"), "Score and Condition resolved to original column names") +check(all(svN == "10"), "both columns computed over 10 rows (scan-all found Condition in DataSetB)") +check(any(grepl("mean", svValues, fixed = TRUE)), "numeric stats computed for Score") + +cat(if (ok) "MULTIDATASET-WRAPPER-OK\n" else "MULTIDATASET-WRAPPER-FAILED\n") +quit(status = if (ok) 0L else 1L) diff --git a/inst/Description.qml b/inst/Description.qml index ca0f46d..f341dee 100644 --- a/inst/Description.qml +++ b/inst/Description.qml @@ -122,15 +122,17 @@ Description requiresData: true preloadData: true multiDataSetAware: true + hasWrapper: true } - // Same form and function, but NOT aware: dataSetSelection must stay inert and data must - // arrive the old way (through dataset/readDataSet), as a controls-and-regression check. + // Same form, but NOT aware and NOT wrapped: the engine calls multiDataSetFuncInternal + // directly (dataset/readDataSet as always) and the dataset selection must stay inert. + // (It cannot use func: "multiDataSetFunc" - that name is the R-syntax wrapper now.) Analysis { title: "Test Multi-DataSet (non-aware counterpart)" qml: "testMultiDataSet.qml" - func: "multiDataSetFunc" + func: "multiDataSetFuncInternal" requiresData: true preloadData: true multiDataSetAware: false From b9a99984a783b56946c584a5b7ff9e1128f3d31f Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Wed, 7 Oct 2026 00:07:03 +0200 Subject: [PATCH 05/44] NEWS: wrapped multi-dataset test analysis, headless test scripts, test data --- NEWS.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/NEWS.md b/NEWS.md index 7b6cd18..3dcf6bd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -20,9 +20,15 @@ (`multiDataSetAware` Description entries, the VariablesForm dataset/filter selection, the `datasets` handout and encoded-name routing of jaspBase, the readDataSet() guard, the `.meta` dataSetId/filterId provenance and the non-aware counterpart as controls). + The aware analysis is wrapped (`hasWrapper: true`): body in `multiDataSetFuncInternal`, + generated `multiDataSetFunc(datasets = ...)` wrapper for R syntax use. * `Tools/buildBundle.R`: builds an installable `bundles_out/jaspTestModule.JASPModule` from this checkout with the local `jasp-desktop/Engine/jaspBase` (or any checkout via `JASP_BASE_DIR`) packed into the bundle; no JASP cellar needed. +* `Tools/testMultiDataSet.R` (+ `_wrapper.R`): full headless test of the analysis over + engine-style queued slices, the wrapper in a clean session and a legacy non-aware run. +* `Tools/testdata/DataSetA.csv` / `DataSetB.csv`: two datasets with deliberately colliding + column names for the per-dataset routing test. ## Changed * `renv.lock` re-resolved for R 4.6.1 (current CRAN, jaspBase/jaspGraphs from GitHub HEAD). From 106030a697f5f69f279d4170c63afb5a6ca4de84 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Wed, 7 Oct 2026 06:54:56 +0200 Subject: [PATCH 06/44] wrappers for the whole module: all analysis funcs renamed to Internal + 38 generated wrappers - hasWrappers: true in Description.qml; every analysis body renamed -> Internal (engine appends Internal per entry), with generated R/Wrapper.R exposing the R-syntax entry points (jaspSyntax::generateAnalysisWrapper, one fresh process per analysis - filteredDataEntry segfaults the bridge and would take the batch down otherwise). - Cross references updated: testContainer calls testFootnotesTableFuncInternal, testEncodingPreloadDataFuncInternal <- testEncodingFuncInternal alias. - The non-aware multi-dataset counterpart moved to func multiDataSetNonAware (+ multiDataSetNonAwareInternal): it cannot share multiDataSetFunc (that is the wrapper) and multiDataSetFuncInternal (the generator rightly refuses to wrap an Internal name). - Known gaps left as-is: testTransposedTable/testFootnotes/testContainer point at a removed test.qml, testPlotStatus at a never-existing TestPlotStatus.qml and their R funcs were already gone (dead Description entries); filteredDataEntry hits the Filter(_data == nullptr) bridge assertion headless. - Tools/testMultiDataSet.R stays green (22/22) with the module wrapped. --- NEWS.md | 8 ++ R/AnalysisUpgradeAWrapper.R | 103 ++++++++++++++++++++++ R/AnalysisUpgradeBWrapper.R | 103 ++++++++++++++++++++++ R/AnalysisUpgradeIncompatibleAWrapper.R | 103 ++++++++++++++++++++++ R/AnalysisUpgradeIncompatibleBWrapper.R | 103 ++++++++++++++++++++++ R/JaspReportTestWrapper.R | 49 +++++++++++ R/analysisUpgrade.R | 4 +- R/customContrastsWrapper.R | 49 +++++++++++ R/descriptivesLinkWrapper.R | 93 ++++++++++++++++++++ R/filteredDataEntry.R | 4 +- R/infoTestFuncWrapper.R | 97 ++++++++++++++++++++ R/minMaxFuncWrapper.R | 52 +++++++++++ R/multiDataSet.R | 7 ++ R/multiDataSetNonAwareWrapper.R | 50 +++++++++++ R/radioButtonFuncWrapper.R | 57 ++++++++++++ R/syntaxhighlighting.R | 4 +- R/syntaxhighlightingFuncWrapper.R | 49 +++++++++++ R/testAnalysisColumnFuncWrapper.R | 49 +++++++++++ R/testAnalysisColumns.R | 4 +- R/testComponentsListLayout.R | 2 +- R/testComponentsListLayoutWrapper.R | 61 +++++++++++++ R/testContainer.R | 8 +- R/testControlsLayout.R | 2 +- R/testControlsLayoutFuncWrapper.R | 96 ++++++++++++++++++++ R/testCrapDataTable.R | 4 +- R/testCrapDataTableWrapper.R | 45 ++++++++++ R/testCustomContrasts.R | 4 +- R/testDefaultValuesWithBindings.R | 2 +- R/testDefaultValuesWithBindingsWrapper.R | 51 +++++++++++ R/testEncoding.R | 4 +- R/testEncodingFuncWrapper.R | 56 ++++++++++++ R/testEncodingPreloadDataFuncWrapper.R | 56 ++++++++++++ R/testFactorsForm.R | 2 +- R/testFactorsFormFuncWrapper.R | 52 +++++++++++ R/testFootnotesTable.R | 2 +- R/testHtml.R | 2 +- R/testHtmlFuncWrapper.R | 45 ++++++++++ R/testInfo.R | 2 +- R/testInputField.R | 2 +- R/testInputFieldFuncWrapper.R | 54 ++++++++++++ R/testJaspReport.R | 4 +- R/testLabel.R | 2 +- R/testLabelFormatFuncWrapper.R | 55 ++++++++++++ R/testLatexFuncWrapper.R | 45 ++++++++++ R/testMinMax.R | 2 +- R/testMixedColumnsTable.R | 2 +- R/testMixedColumnsTableFuncWrapper.R | 46 ++++++++++ R/testOptionContainsValue.R | 2 +- R/testOptionContainsValueWrapper.R | 49 +++++++++++ R/testOptionNames.R | 2 +- R/testOptionNamesWrapper.R | 55 ++++++++++++ R/testRSource.R | 2 +- R/testRSourceFuncWrapper.R | 53 +++++++++++ R/testRadioButtons.R | 2 +- R/testReqModule.R | 4 +- R/testSaveLoadButton.R | 2 +- R/testSaveLoadButtonWrapper.R | 47 ++++++++++ R/testSpecialSources.R | 2 +- R/testSpecialSourcesFuncWrapper.R | 66 ++++++++++++++ R/testState.R | 2 +- R/testStateFuncWrapper.R | 53 +++++++++++ R/testTabView.R | 2 +- R/testTabViewWrapper.R | 54 ++++++++++++ R/testTableFormatting.R | 2 +- R/testTableFormattingFuncWrapper.R | 45 ++++++++++ R/testTableView.R | 2 +- R/testTableViewWrapper.R | 53 +++++++++++ R/testTextArea.R | 2 +- R/testTextAreaWrapper.R | 51 +++++++++++ R/testTransposedTable.R | 2 +- R/testVariablesForm.R | 4 +- R/testVariablesFormWrapper.R | 62 +++++++++++++ R/tooltipFunc.R | 2 +- R/tooltipFuncWrapper.R | 107 +++++++++++++++++++++++ inst/Description.qml | 10 +-- 75 files changed, 2382 insertions(+), 53 deletions(-) create mode 100644 R/AnalysisUpgradeAWrapper.R create mode 100644 R/AnalysisUpgradeBWrapper.R create mode 100644 R/AnalysisUpgradeIncompatibleAWrapper.R create mode 100644 R/AnalysisUpgradeIncompatibleBWrapper.R create mode 100644 R/JaspReportTestWrapper.R create mode 100644 R/customContrastsWrapper.R create mode 100644 R/descriptivesLinkWrapper.R create mode 100644 R/infoTestFuncWrapper.R create mode 100644 R/minMaxFuncWrapper.R create mode 100644 R/multiDataSetNonAwareWrapper.R create mode 100644 R/radioButtonFuncWrapper.R create mode 100644 R/syntaxhighlightingFuncWrapper.R create mode 100644 R/testAnalysisColumnFuncWrapper.R create mode 100644 R/testComponentsListLayoutWrapper.R create mode 100644 R/testControlsLayoutFuncWrapper.R create mode 100644 R/testCrapDataTableWrapper.R create mode 100644 R/testDefaultValuesWithBindingsWrapper.R create mode 100644 R/testEncodingFuncWrapper.R create mode 100644 R/testEncodingPreloadDataFuncWrapper.R create mode 100644 R/testFactorsFormFuncWrapper.R create mode 100644 R/testHtmlFuncWrapper.R create mode 100644 R/testInputFieldFuncWrapper.R create mode 100644 R/testLabelFormatFuncWrapper.R create mode 100644 R/testLatexFuncWrapper.R create mode 100644 R/testMixedColumnsTableFuncWrapper.R create mode 100644 R/testOptionContainsValueWrapper.R create mode 100644 R/testOptionNamesWrapper.R create mode 100644 R/testRSourceFuncWrapper.R create mode 100644 R/testSaveLoadButtonWrapper.R create mode 100644 R/testSpecialSourcesFuncWrapper.R create mode 100644 R/testStateFuncWrapper.R create mode 100644 R/testTabViewWrapper.R create mode 100644 R/testTableFormattingFuncWrapper.R create mode 100644 R/testTableViewWrapper.R create mode 100644 R/testTextAreaWrapper.R create mode 100644 R/testVariablesFormWrapper.R create mode 100644 R/tooltipFuncWrapper.R diff --git a/NEWS.md b/NEWS.md index 3dcf6bd..4e43db8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -32,6 +32,14 @@ ## Changed * `renv.lock` re-resolved for R 4.6.1 (current CRAN, jaspBase/jaspGraphs from GitHub HEAD). +* `hasWrappers: true` for the module: every analysis body now lives in `Internal` + and has a generated `R/Wrapper.R` (via jaspSyntax/SyntaxInterface) exposing the + plain `(data = ..., ...)` entry point for R syntax use - 38 of 43; the four + entries pointing at removed qml files (test.qml, TestPlotStatus.qml) can't generate + one, and filteredDataEntry crashes the syntax bridge headlessly (Filter without + DataSet assertion in CommonData/filter.cpp - a real bridge bug to fix). +* Non-aware counterpart moved to its own func (`multiDataSetNonAware`), a wrapper may + not be generated for a `...Internal` name. diff --git a/R/AnalysisUpgradeAWrapper.R b/R/AnalysisUpgradeAWrapper.R new file mode 100644 index 0000000..7e3a88f --- /dev/null +++ b/R/AnalysisUpgradeAWrapper.R @@ -0,0 +1,103 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Analysis A +#' +#' @param stemAndLeaf, Create a Stem and leaf table. +#' Defaults to \code{FALSE}. +#' @param stemAndLeafScale, The scale parameter controls how much the table is expanded. For example, scale = 2 will cause the table to be roughly twice as long as the default (scale = 1). +AnalysisUpgradeA <- function( + data = NULL, + version = "1", + addSmooth = TRUE, + addSmoothCI = TRUE, + addSmoothCIValue = 0.95, + binWidthType = "sturges", + chartType = "_2barCharts", + chartValues = "_2percentages", + colorPalette = "colorblind", + descriptivesIntervalPlot = FALSE, + descriptivesPiechart = FALSE, + descriptivesQQPlot = FALSE, + distPlotDensity = FALSE, + distPlotRug = FALSE, + frequencyTables = FALSE, + frequencyTablesMaximumAmount = 10, + graphTypeAbove = "density", + graphTypeRight = "density", + iqr = FALSE, + kurtosis = FALSE, + mad = FALSE, + madrobust = FALSE, + maximum = TRUE, + mean = TRUE, + median = FALSE, + minimum = TRUE, + mode = FALSE, + numberOfBins = 30, + percentileValuesEqualGroups = FALSE, + percentileValuesEqualGroupsNo = 4, + percentileValuesPercentiles = FALSE, + percentileValuesQuartiles = FALSE, + plotCorrelationMatrix = FALSE, + plotHeight = 320, + plotVariables = FALSE, + plotWidth = 480, + range = FALSE, + regressionType = "smooth", + scatterPlot = FALSE, + shapiro = FALSE, + showLegend = TRUE, + skewness = FALSE, + splitPlotBoxplot = TRUE, + splitPlotColour = FALSE, + splitPlotJitter = FALSE, + splitPlotOutlierLabel = FALSE, + splitPlotViolin = FALSE, + splitPlots = FALSE, + splitby = list(types = list(), value = ""), + standardDeviation = TRUE, + standardErrorMean = FALSE, + statisticsValuesAreGroupMidpoints = FALSE, + stemAndLeaf = FALSE, + stemAndLeafScale = 1, + sum = FALSE, + variables = list(types = list(), value = list()), + variance = FALSE) { + + defaultArgCalls <- formals(jaspTestModule::AnalysisUpgradeA) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("binWidthType", "colorPalette", "splitby", "variables") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "AnalysisUpgradeA", "AnalysisUpgrade.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/AnalysisUpgradeBWrapper.R b/R/AnalysisUpgradeBWrapper.R new file mode 100644 index 0000000..e58af93 --- /dev/null +++ b/R/AnalysisUpgradeBWrapper.R @@ -0,0 +1,103 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Analysis B +#' +#' @param stemAndLeaf, Create a Stem and leaf table. +#' Defaults to \code{FALSE}. +#' @param stemAndLeafScale, The scale parameter controls how much the table is expanded. For example, scale = 2 will cause the table to be roughly twice as long as the default (scale = 1). +AnalysisUpgradeB <- function( + data = NULL, + version = "1", + addSmooth = TRUE, + addSmoothCI = TRUE, + addSmoothCIValue = 0.95, + binWidthType = "sturges", + chartType = "_1noCharts", + chartValues = "_1frequencies", + colorPalette = "colorblind", + descriptivesIntervalPlot = FALSE, + descriptivesPiechart = FALSE, + descriptivesQQPlot = FALSE, + distPlotDensity = FALSE, + distPlotRug = FALSE, + frequencyTables = FALSE, + frequencyTablesMaximumAmount = 10, + graphTypeAbove = "density", + graphTypeRight = "density", + iqr = FALSE, + kurtosis = FALSE, + mad = FALSE, + madrobust = FALSE, + maximum = TRUE, + mean = TRUE, + median = FALSE, + minimum = TRUE, + mode = FALSE, + numberOfBins = 30, + percentileValuesEqualGroups = FALSE, + percentileValuesEqualGroupsNo = 4, + percentileValuesPercentiles = FALSE, + percentileValuesQuartiles = FALSE, + plotCorrelationMatrix = FALSE, + plotHeight = 320, + plotVariables = FALSE, + plotWidth = 480, + range = FALSE, + regressionType = "smooth", + scatterPlot = FALSE, + shapiro = FALSE, + showLegend = TRUE, + skewness = FALSE, + splitPlotBoxplot = TRUE, + splitPlotColour = FALSE, + splitPlotJitter = FALSE, + splitPlotOutlierLabel = FALSE, + splitPlotViolin = FALSE, + splitPlots = FALSE, + splitby = list(types = list(), value = ""), + standardDeviation = TRUE, + standardErrorMean = FALSE, + statisticsValuesAreGroupMidpoints = FALSE, + stemAndLeaf = FALSE, + stemAndLeafScale = 1, + sum = FALSE, + variables = list(types = list(), value = list()), + variance = FALSE) { + + defaultArgCalls <- formals(jaspTestModule::AnalysisUpgradeB) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("binWidthType", "colorPalette", "splitby", "variables") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "AnalysisUpgradeB", "AnalysisUpgrade.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/AnalysisUpgradeIncompatibleAWrapper.R b/R/AnalysisUpgradeIncompatibleAWrapper.R new file mode 100644 index 0000000..79441cf --- /dev/null +++ b/R/AnalysisUpgradeIncompatibleAWrapper.R @@ -0,0 +1,103 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Analysis A with incompatible to B +#' +#' @param stemAndLeaf, Create a Stem and leaf table. +#' Defaults to \code{FALSE}. +#' @param stemAndLeafScale, The scale parameter controls how much the table is expanded. For example, scale = 2 will cause the table to be roughly twice as long as the default (scale = 1). +AnalysisUpgradeIncompatibleA <- function( + data = NULL, + version = "1", + addSmooth = TRUE, + addSmoothCI = TRUE, + addSmoothCIValue = 0.95, + binWidthType = "sturges", + chartType = "_4histograms", + chartValues = "_2percentages", + colorPalette = "colorblind", + descriptivesIntervalPlot = FALSE, + descriptivesPiechart = FALSE, + descriptivesQQPlot = FALSE, + distPlotDensity = FALSE, + distPlotRug = FALSE, + frequencyTables = FALSE, + frequencyTablesMaximumAmount = 10, + graphTypeAbove = "density", + graphTypeRight = "density", + iqr = FALSE, + kurtosis = FALSE, + mad = FALSE, + madrobust = FALSE, + maximum = TRUE, + mean = TRUE, + median = FALSE, + minimum = TRUE, + mode = FALSE, + numberOfBins = 30, + percentileValuesEqualGroups = FALSE, + percentileValuesEqualGroupsNo = 4, + percentileValuesPercentiles = FALSE, + percentileValuesQuartiles = FALSE, + plotCorrelationMatrix = FALSE, + plotHeight = 320, + plotVariables = FALSE, + plotWidth = 480, + range = FALSE, + regressionType = "smooth", + scatterPlot = FALSE, + shapiro = FALSE, + showLegend = TRUE, + skewness = FALSE, + splitPlotBoxplot = TRUE, + splitPlotColour = FALSE, + splitPlotJitter = FALSE, + splitPlotOutlierLabel = FALSE, + splitPlotViolin = FALSE, + splitPlots = FALSE, + splitby = list(types = list(), value = ""), + standardDeviation = TRUE, + standardErrorMean = FALSE, + statisticsValuesAreGroupMidpoints = FALSE, + stemAndLeaf = FALSE, + stemAndLeafScale = 1, + sum = FALSE, + variables = list(types = list(), value = list()), + variance = FALSE) { + + defaultArgCalls <- formals(jaspTestModule::AnalysisUpgradeIncompatibleA) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("binWidthType", "colorPalette", "splitby", "variables") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "AnalysisUpgradeIncompatibleA", "AnalysisUpgrade.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/AnalysisUpgradeIncompatibleBWrapper.R b/R/AnalysisUpgradeIncompatibleBWrapper.R new file mode 100644 index 0000000..393802a --- /dev/null +++ b/R/AnalysisUpgradeIncompatibleBWrapper.R @@ -0,0 +1,103 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Analysis B but then incompatible +#' +#' @param stemAndLeaf, Create a Stem and leaf table. +#' Defaults to \code{FALSE}. +#' @param stemAndLeafScale, The scale parameter controls how much the table is expanded. For example, scale = 2 will cause the table to be roughly twice as long as the default (scale = 1). +AnalysisUpgradeIncompatibleB <- function( + data = NULL, + version = "1", + addSmooth = TRUE, + addSmoothCI = TRUE, + addSmoothCIValue = 0.95, + binWidthType = "sturges", + chartType = "_3pieCharts", + chartValues = "_1frequencies", + colorPalette = "colorblind", + descriptivesIntervalPlot = FALSE, + descriptivesPiechart = FALSE, + descriptivesQQPlot = FALSE, + distPlotDensity = FALSE, + distPlotRug = FALSE, + frequencyTables = FALSE, + frequencyTablesMaximumAmount = 10, + graphTypeAbove = "density", + graphTypeRight = "density", + iqr = FALSE, + kurtosis = FALSE, + mad = FALSE, + madrobust = FALSE, + maximum = TRUE, + mean = TRUE, + median = FALSE, + minimum = TRUE, + mode = FALSE, + numberOfBins = 30, + percentileValuesEqualGroups = FALSE, + percentileValuesEqualGroupsNo = 4, + percentileValuesPercentiles = FALSE, + percentileValuesQuartiles = FALSE, + plotCorrelationMatrix = FALSE, + plotHeight = 320, + plotVariables = FALSE, + plotWidth = 480, + range = FALSE, + regressionType = "smooth", + scatterPlot = FALSE, + shapiro = FALSE, + showLegend = TRUE, + skewness = FALSE, + splitPlotBoxplot = TRUE, + splitPlotColour = FALSE, + splitPlotJitter = FALSE, + splitPlotOutlierLabel = FALSE, + splitPlotViolin = FALSE, + splitPlots = FALSE, + splitby = list(types = list(), value = ""), + standardDeviation = TRUE, + standardErrorMean = FALSE, + statisticsValuesAreGroupMidpoints = FALSE, + stemAndLeaf = FALSE, + stemAndLeafScale = 1, + sum = FALSE, + variables = list(types = list(), value = list()), + variance = FALSE) { + + defaultArgCalls <- formals(jaspTestModule::AnalysisUpgradeIncompatibleB) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("binWidthType", "colorPalette", "splitby", "variables") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "AnalysisUpgradeIncompatibleB", "AnalysisUpgrade.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/JaspReportTestWrapper.R b/R/JaspReportTestWrapper.R new file mode 100644 index 0000000..98f5b27 --- /dev/null +++ b/R/JaspReportTestWrapper.R @@ -0,0 +1,49 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Reporting +#' +JaspReportTest <- function( + data = NULL, + version = "1", + plotHeight = 320, + plotWidth = 480, + reportMe = "", + selectedVariable = list(types = list(), value = "")) { + + defaultArgCalls <- formals(jaspTestModule::JaspReportTest) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("selectedVariable") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "JaspReportTest", "JaspReportTest.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/analysisUpgrade.R b/R/analysisUpgrade.R index e568f2a..ddbaa40 100644 --- a/R/analysisUpgrade.R +++ b/R/analysisUpgrade.R @@ -4,5 +4,5 @@ Analysis <- function(jaspResults, dataset, options) { jaspResults[["optionsOverview"]] <- createJaspHtml(text=paste0(paste0("
  • ", names(options), ": "), options, "
  • ", collapse="\n"), elementType="ul") } -AnalysisUpgradeA <- Analysis; -AnalysisUpgradeB <- Analysis; +AnalysisUpgradeAInternal <- Analysis; +AnalysisUpgradeBInternal <- Analysis; diff --git a/R/customContrastsWrapper.R b/R/customContrastsWrapper.R new file mode 100644 index 0000000..c38cb99 --- /dev/null +++ b/R/customContrastsWrapper.R @@ -0,0 +1,49 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Custom Contrasts Widget +#' +customContrasts <- function( + data = NULL, + version = "1", + customContrasts = list(), + plotHeight = 320, + plotWidth = 480, + selected = list(types = list(), value = "")) { + + defaultArgCalls <- formals(jaspTestModule::customContrasts) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("customContrasts", "selected") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "customContrasts", "customContrasts.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/descriptivesLinkWrapper.R b/R/descriptivesLinkWrapper.R new file mode 100644 index 0000000..081de3f --- /dev/null +++ b/R/descriptivesLinkWrapper.R @@ -0,0 +1,93 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Descriptives! +#' +descriptivesLink <- function( + data = NULL, + version = "1", + addSmooth = TRUE, + addSmoothCI = TRUE, + addSmoothCIValue = 0.95, + chartType = "_4histograms", + chartValues = "_2percentages", + colorPalette = "colorblind", + descriptivesPiechart = FALSE, + descriptivesQQPlot = FALSE, + distPlotDensity = FALSE, + frequencyTables = FALSE, + graphTypeAbove = "density", + graphTypeRight = "density", + iqr = FALSE, + kurtosis = FALSE, + mad = FALSE, + madrobust = FALSE, + maximum = TRUE, + mean = TRUE, + median = FALSE, + minimum = TRUE, + mode = FALSE, + percentileValuesEqualGroups = FALSE, + percentileValuesEqualGroupsNo = 4, + percentileValuesPercentiles = FALSE, + percentileValuesQuartiles = FALSE, + plotCorrelationMatrix = FALSE, + plotHeight = 320, + plotVariables = FALSE, + plotWidth = 480, + range = FALSE, + regressionType = "smooth", + scatterPlot = FALSE, + shapiro = FALSE, + showLegend = TRUE, + skewness = FALSE, + splitPlotBoxplot = TRUE, + splitPlotColour = FALSE, + splitPlotJitter = FALSE, + splitPlotOutlierLabel = FALSE, + splitPlotViolin = FALSE, + splitPlots = FALSE, + splitby = list(types = list(), value = ""), + standardDeviation = TRUE, + standardErrorMean = FALSE, + statisticsValuesAreGroupMidpoints = FALSE, + sum = FALSE, + variables = list(types = list(), value = list()), + variance = FALSE) { + + defaultArgCalls <- formals(jaspTestModule::descriptivesLink) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("colorPalette", "splitby", "variables") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "descriptivesLink", "CopyOfDescriptives.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/filteredDataEntry.R b/R/filteredDataEntry.R index 90aac0c..a0c821f 100644 --- a/R/filteredDataEntry.R +++ b/R/filteredDataEntry.R @@ -1,7 +1,7 @@ -filteredDataEntry <- function(jaspResults, dataset, options) +filteredDataEntryInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) -} \ No newline at end of file +} diff --git a/R/infoTestFuncWrapper.R b/R/infoTestFuncWrapper.R new file mode 100644 index 0000000..fbddb5b --- /dev/null +++ b/R/infoTestFuncWrapper.R @@ -0,0 +1,97 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' info testing +#' +#' This is a bunch of info on a form! +#' +#' @param barPlot, Displays the sample means as bars and the confidence intervals or standard errors as error bars for each measure. +#' Defaults to \code{FALSE}. +#' @param barPlotYAxisFixedToZero, Fix horizontal axis to 0: Forces the graph to show the default x-axis at y = 0 +#' Defaults to \code{TRUE}. +#' @param cells, assigned RM +#' @param checkbox, Check this out +#' Defaults to \code{FALSE}. +#' @param doubleFieldWithAfterLabel, this is in doublefield that has a label and an afterLabel +#' @param dropDown, dropdown with info on each value +#' \itemize{ +#' \item \code{"high"} (default) : So High! +#' \item \code{"medium"}: Middling +#' \item \code{"low"}: Down low +#' \item \code{"custom"}: Who knows? +#' } +#' @param dropdownVariableAndExtraValues, You can choose one of the variables or special values +#' @param dropdownVariables, You can choose one of the variables +#' @param integerFieldWithAfterLabel, this is in integerfield that has a label and an afterLabel +#' @param paairs, assigned pairs +#' @param radiobuttongroup, A grouping of buttons of radio +#' \itemize{ +#' \item \code{"rButton2"}: and more info on the other button +#' \item \code{"rButton1"}: and some info on button +#' } +#' @param repeatedMeasuresFactors, FactorLevelList +#' @param simpleTableView, a tableview with some info +#' @param slideOnOuttaHere, slip and slide suicide +#' @param tabview, This is an info field on a tabview +#' @param varA, assigned variables nominal allowed +infoTestFunc <- function( + data = NULL, + version = "1", + barPlot = FALSE, + barPlotCiLevel = 0.95, + barPlotErrorType = "ci", + barPlotYAxisFixedToZero = TRUE, + cells = list(), + checkbox = FALSE, + doubleFieldWithAfterLabel = 0, + dropDown = "high", + dropdownVariableAndExtraValues = list(types = "unknown", value = "one"), + dropdownVariables = list(types = "unknown", value = ""), + dropdownWithVariablesSource = list(types = "unknown", value = ""), + integerFieldWithAfterLabel = 1000, + paairs = list(), + plotHeight = 320, + plotWidth = 480, + radiobuttongroup = "rButton2", + repeatedMeasuresFactors = list(list(levels = list("Level 1", "Level 2"), name = "RM Factor 1")), + simpleTableView = list(), + slideOnOuttaHere = 0.5, + tabview = list(list(marginalMean = FALSE, name = "NEW ITEM", summary = FALSE, syntax = "")), + varA = list(types = list(), value = list())) { + + defaultArgCalls <- formals(jaspTestModule::infoTestFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("cells", "dropDown", "dropdownVariableAndExtraValues", "dropdownVariables", "dropdownWithVariablesSource", "paairs", "repeatedMeasuresFactors", "simpleTableView", "tabview", "varA") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "infoTestFunc", "TestInfo.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/minMaxFuncWrapper.R b/R/minMaxFuncWrapper.R new file mode 100644 index 0000000..e218460 --- /dev/null +++ b/R/minMaxFuncWrapper.R @@ -0,0 +1,52 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Min Max +#' +minMaxFunc <- function( + data = NULL, + version = "1", + doubleLower = 0.1, + doubleUpper = 0.5, + integerLower = 1, + integerUpper = 5, + leftFormula = -1, + lowerDefault = 1, + lowerIntegerDefault = 0.1, + plotHeight = 320, + plotWidth = 480, + rightFormula = 1) { + + defaultArgCalls <- formals(jaspTestModule::minMaxFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "minMaxFunc", "MinMaxTest.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/multiDataSet.R b/R/multiDataSet.R index 9e7fc96..f1f7a40 100644 --- a/R/multiDataSet.R +++ b/R/multiDataSet.R @@ -117,6 +117,13 @@ readDataSetGuardDemo <- function(jaspResults) { jaspResults[["guardDemo"]] <- createJaspHtml(message) } +# Non-aware counterpart (Description entry func: "multiDataSetNonAware"; the engine appends +# Internal because hasWrappers is on for the whole module). Same form and body, but reached +# through the old single-dataset path: no datasets argument, so readDataSet/dataset rule. +multiDataSetNonAwareInternal <- function(jaspResults, dataset, options) { + multiDataSetFuncInternal(jaspResults, dataset, options) +} + # ------------------------------------------------------------------ internals .dependentValues <- function(options) { diff --git a/R/multiDataSetNonAwareWrapper.R b/R/multiDataSetNonAwareWrapper.R new file mode 100644 index 0000000..b2a6734 --- /dev/null +++ b/R/multiDataSetNonAwareWrapper.R @@ -0,0 +1,50 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test Multi-DataSet (non-aware counterpart) +#' +multiDataSetNonAware <- function( + data = NULL, + version = "1", + dependent = list(types = list(), value = list()), + guardDemo = FALSE, + plotHeight = 320, + plotWidth = 480, + showMeta = FALSE) { + + defaultArgCalls <- formals(jaspTestModule::multiDataSetNonAware) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("dependent") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "multiDataSetNonAware", "testMultiDataSet.qml", options, version, TRUE)) +} \ No newline at end of file diff --git a/R/radioButtonFuncWrapper.R b/R/radioButtonFuncWrapper.R new file mode 100644 index 0000000..f379a6c --- /dev/null +++ b/R/radioButtonFuncWrapper.R @@ -0,0 +1,57 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Radio Button +#' +radioButtonFunc <- function( + data = NULL, + version = "1", + assigned = list(types = list(), value = list()), + buttons1 = list(list(value = "button1"), list(value = "button2")), + buttons2 = list(list(value = "button1"), list(value = "button2")), + dynamic = list(), + dynamicGroupWithDynamicButtons = list(list(value = "grouo1"), list(value = "group2")), + dynamicGroupWithStaticButtons = list(list(value = "grouo1"), list(value = "group2")), + firstGroup = "one", + plotHeight = 320, + plotWidth = 480, + secondGroup = "one", + staticGroupWithDynamicButtons = "", + staticGroupWithStaticAndDynamicButtons = "Static button") { + + defaultArgCalls <- formals(jaspTestModule::radioButtonFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("assigned", "buttons1", "buttons2", "dynamic", "dynamicGroupWithDynamicButtons", "dynamicGroupWithStaticButtons") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "radioButtonFunc", "RadioButtonTest.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/syntaxhighlighting.R b/R/syntaxhighlighting.R index 54e6ac4..7f0fbc0 100644 --- a/R/syntaxhighlighting.R +++ b/R/syntaxhighlighting.R @@ -1,5 +1,5 @@ -syntaxhighlightingFunc <- function(jaspResults, dataset, options) +syntaxhighlightingFuncInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) -} \ No newline at end of file +} diff --git a/R/syntaxhighlightingFuncWrapper.R b/R/syntaxhighlightingFuncWrapper.R new file mode 100644 index 0000000..28d2530 --- /dev/null +++ b/R/syntaxhighlightingFuncWrapper.R @@ -0,0 +1,49 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Syntax Highlighting +#' +syntaxhighlightingFunc <- function( + data = NULL, + version = "1", + R = list(columns = list(), model = "", modelOriginal = ""), + plotHeight = 320, + plotWidth = 480, + syntax = list(columns = list(), model = "", modelOriginal = "")) { + + defaultArgCalls <- formals(jaspTestModule::syntaxhighlightingFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("R", "syntax") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "syntaxhighlightingFunc", "SyntaxHighlighting.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testAnalysisColumnFuncWrapper.R b/R/testAnalysisColumnFuncWrapper.R new file mode 100644 index 0000000..5bbe5ed --- /dev/null +++ b/R/testAnalysisColumnFuncWrapper.R @@ -0,0 +1,49 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test (comp) columns from analyses +#' +testAnalysisColumnFunc <- function( + data = NULL, + version = "1", + AddColumnField = "", + AddColumnsPattern = "", + AddThisManyColumns = 3, + ComputedColumnField = "", + createColumnInR = TRUE, + plotHeight = 320, + plotWidth = 480) { + + defaultArgCalls <- formals(jaspTestModule::testAnalysisColumnFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testAnalysisColumnFunc", "testAnalysisColumnFunc.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testAnalysisColumns.R b/R/testAnalysisColumns.R index bfba7cb..4bb0428 100644 --- a/R/testAnalysisColumns.R +++ b/R/testAnalysisColumns.R @@ -1,4 +1,4 @@ -testAnalysisColumnFunc <- function(jaspResults, dataset, options) +testAnalysisColumnFuncInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) @@ -76,4 +76,4 @@ createAndFillAJaspColumn <- function(container, name, dependencies) col$setScale(generateDataForColumn()) container[[name]] <- col -} \ No newline at end of file +} diff --git a/R/testComponentsListLayout.R b/R/testComponentsListLayout.R index fbf7198..a6c2072 100644 --- a/R/testComponentsListLayout.R +++ b/R/testComponentsListLayout.R @@ -1,5 +1,5 @@ -testComponentsListLayout <- function(jaspResults, dataset, options) +testComponentsListLayoutInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) } diff --git a/R/testComponentsListLayoutWrapper.R b/R/testComponentsListLayoutWrapper.R new file mode 100644 index 0000000..a8588ec --- /dev/null +++ b/R/testComponentsListLayoutWrapper.R @@ -0,0 +1,61 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test ComponentsList & TableView layout +#' +testComponentsListLayout <- function( + data = NULL, + version = "1", + addBorderCheck = TRUE, + addItemManuallyCheck = TRUE, + buttonsInRowCheck = FALSE, + columns = "3", + componentsList = list(local({ .l <- list("#", "Second column", "Third column", "First hypothesis", "#"); names(.l) <- c("", "col2", "col3", "hypothesis", "value"); .l }), local({ .l <- list("#2", "Second hypothesis", "#2"); names(.l) <- c("", "hypothesis", "value"); .l }), list(col2 = "col 2", col3 = "col 3", col4 = "col 4", col5 = "col 5", col6 = "col 6", hypothesis = "Hypothesis 3", value = "#3"), list(col2 = "col 2", col3 = "col 3", col4 = "col 4", col5 = "col 5", col6 = "col 6", hypothesis = "Hypothesis 4", value = "#4")), + contrasts = list(optionKey = "variable", types = list(), value = list()), + customContrasts = list(), + fixedFactors = list(optionKey = "components", types = list(), value = list()), + headers = "Header with array", + heightCheck = FALSE, + plotHeight = 320, + plotWidth = 480, + showButtonsCheck = TRUE, + showTitleCheck = FALSE, + tableHeightCheck = FALSE, + tableView = list(list(levels = list(), name = "data 1", values = list()), list(levels = list(), name = "data 2", values = list()))) { + + defaultArgCalls <- formals(jaspTestModule::testComponentsListLayout) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("columns", "componentsList", "contrasts", "customContrasts", "fixedFactors", "headers", "tableView") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testComponentsListLayout", "testComponentsListLayout.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testContainer.R b/R/testContainer.R index ca8a196..9228811 100644 --- a/R/testContainer.R +++ b/R/testContainer.R @@ -1,15 +1,15 @@ -testContainerFunc <- function(jaspResults, dataset, options) +testContainerFuncInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) cnt <- addContainer(addContainer(addContainer(addContainer(jaspResults, name="c0")))) - testFootnotesTableFunc(cnt, dataset, options) + testFootnotesTableFuncInternal(cnt, dataset, options) cnt <- addContainer(addContainer(addContainer(addContainer(jaspResults, name="c1")), title="hallo")) - testFootnotesTableFunc(cnt, dataset, options) + testFootnotesTableFuncInternal(cnt, dataset, options) cnt <- addContainer(addContainer(addContainer(addContainer(jaspResults, title="hallo", name="c2")), title="er zit er eentje stiekem om me heen"), title="hallo", initCollapsed=TRUE) - testFootnotesTableFunc(cnt, dataset, options) + testFootnotesTableFuncInternal(cnt, dataset, options) } addContainer <- function(inContainer, name="container", title="", initCollapsed=FALSE) diff --git a/R/testControlsLayout.R b/R/testControlsLayout.R index 41aca61..df8cc47 100644 --- a/R/testControlsLayout.R +++ b/R/testControlsLayout.R @@ -1,5 +1,5 @@ -testControlsLayoutFunc <- function(jaspResults, dataset, options) +testControlsLayoutFuncInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) diff --git a/R/testControlsLayoutFuncWrapper.R b/R/testControlsLayoutFuncWrapper.R new file mode 100644 index 0000000..17f27d8 --- /dev/null +++ b/R/testControlsLayoutFuncWrapper.R @@ -0,0 +1,96 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test Controls layout +#' +#' @param meanCiLevel, width of the confidence interval. +#' @param meanCiMethod, How should the confidence interval be computed? By default, we use a `T model`, which yields results identical to a one-sample t-test. Alternative options are a normal model ($\bar{x} \pm z_{95} \times SE$), or `Bootstrap`. +#' @param sdCi, a confidence interval for the standard deviation based on bootstrap samples. +#' Defaults to \code{FALSE}. +testControlsLayoutFunc <- function( + data = NULL, + version = "1", + checkBoxSecondLevel = FALSE, + checkboxWithSeveralLevels = FALSE, + doubleInGroup = 0, + dropDownInGroup = list(types = "unknown", value = ""), + dropDownNominal = list(types = "unknown", value = ""), + dropDownOrdinal = list(types = "unknown", value = ""), + dropDownScale = list(types = "unknown", value = ""), + dropDownSecondLevel = "one", + dropDownStartValue = "really long values!!!!!", + dropDownValues = "one", + dropDownValuesAbove = "one", + dropDownValuesEmptyValue = "", + dropDownValuesFixed = "one", + dropDownValuesFixedAbove = "one", + dropDownValuesFixedEmptyValue = "", + dropDownValuesFixedStartValue = "really long values!!!!!", + dropDownVariables = list(types = "unknown", value = ""), + dropDownVariablesEmptyValue = "", + dropDownVariablesFixed = list(types = "unknown", value = ""), + dropDownVariablesFixedEmptyValue = list(types = "unknown", value = ""), + dropDownVariablesFixedLabelAbove = list(types = "unknown", value = ""), + dropDownVariablesFixedStartIndex = list(types = "unknown", value = ""), + dropDownVariablesLabel = list(types = "unknown", value = ""), + dropDownVariablesStartIndex = list(types = "unknown", value = ""), + intValue = 0, + integerFirstLevel = 0, + integerSecondLevel = 0, + maxLevels = 100, + maxNumeric = 100, + meanCi = FALSE, + meanCiLevel = 0.95, + meanCiMethod = "oneSampleTTest", + minLevels = -1, + minNumeric = -1, + percentFirstLevel = 0.5, + percentileValues = list(), + percentiles = FALSE, + plotHeight = 320, + plotWidth = 480, + quantilesForEqualGroups = FALSE, + quantilesForEqualGroupsNumber = 0, + quartiles = FALSE, + sdCi = FALSE, + sdCiLevel = 0.95, + sdCiMethod = "chiSquaredModel", + seMean = FALSE, + textInGroup = "") { + + defaultArgCalls <- formals(jaspTestModule::testControlsLayoutFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("dropDownInGroup", "dropDownNominal", "dropDownOrdinal", "dropDownScale", "dropDownSecondLevel", "dropDownStartValue", "dropDownValues", "dropDownValuesAbove", "dropDownValuesEmptyValue", "dropDownValuesFixed", "dropDownValuesFixedAbove", "dropDownValuesFixedEmptyValue", "dropDownValuesFixedStartValue", "dropDownVariables", "dropDownVariablesEmptyValue", "dropDownVariablesFixed", "dropDownVariablesFixedEmptyValue", "dropDownVariablesFixedLabelAbove", "dropDownVariablesFixedStartIndex", "dropDownVariablesLabel", "dropDownVariablesStartIndex", "meanCiMethod", "sdCiMethod") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testControlsLayoutFunc", "testControlsLayout.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testCrapDataTable.R b/R/testCrapDataTable.R index 79811b0..94aac5f 100644 --- a/R/testCrapDataTable.R +++ b/R/testCrapDataTable.R @@ -1,6 +1,6 @@ -testCrapDataTable <- function(jaspResults, dataset, options) +testCrapDataTableInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) @@ -36,4 +36,4 @@ testCrapDataTable <- function(jaspResults, dataset, options) jaspResults[["hereYaGo"]] <- tb -} \ No newline at end of file +} diff --git a/R/testCrapDataTableWrapper.R b/R/testCrapDataTableWrapper.R new file mode 100644 index 0000000..b096bc9 --- /dev/null +++ b/R/testCrapDataTableWrapper.R @@ -0,0 +1,45 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test jaspTable with crappy data +#' +testCrapDataTable <- function( + data = NULL, + version = "1", + checkbox_0 = FALSE, + plotHeight = 320, + plotWidth = 480) { + + defaultArgCalls <- formals(jaspTestModule::testCrapDataTable) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testCrapDataTable", "testCrapDataTable.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testCustomContrasts.R b/R/testCustomContrasts.R index 1354682..b18bfc1 100644 --- a/R/testCustomContrasts.R +++ b/R/testCustomContrasts.R @@ -1,4 +1,4 @@ -customContrasts <- function(jaspResults, dataset, options) +customContrastsInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) @@ -37,4 +37,4 @@ createCustomConstrastsTable <- function(customContrasts) return(table); -} \ No newline at end of file +} diff --git a/R/testDefaultValuesWithBindings.R b/R/testDefaultValuesWithBindings.R index 2122414..052027d 100644 --- a/R/testDefaultValuesWithBindings.R +++ b/R/testDefaultValuesWithBindings.R @@ -1,4 +1,4 @@ -testDefaultValuesWithBindings <- function(jaspResults, dataset, options) +testDefaultValuesWithBindingsInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) diff --git a/R/testDefaultValuesWithBindingsWrapper.R b/R/testDefaultValuesWithBindingsWrapper.R new file mode 100644 index 0000000..6955afc --- /dev/null +++ b/R/testDefaultValuesWithBindingsWrapper.R @@ -0,0 +1,51 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Default values with bindings +#' +testDefaultValuesWithBindings <- function( + data = NULL, + version = "1", + assignedVariables = list(types = list(), value = list()), + check = FALSE, + drop = "none", + plotHeight = 320, + plotWidth = 480, + radio = "one") { + + defaultArgCalls <- formals(jaspTestModule::testDefaultValuesWithBindings) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("assignedVariables", "drop") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testDefaultValuesWithBindings", "testDefaultValuesWithBindings.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testEncoding.R b/R/testEncoding.R index 2185660..5c15af2 100644 --- a/R/testEncoding.R +++ b/R/testEncoding.R @@ -1,4 +1,4 @@ -testEncodingFunc <- function(jaspResults, dataset, options) +testEncodingFuncInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) @@ -8,7 +8,7 @@ testEncodingFunc <- function(jaspResults, dataset, options) showData(dataset, jaspResults, "variablesPairs", unlist(options$pairs), "Pairs Variables") } -testEncodingPreloadDataFunc <- testEncodingFunc +testEncodingPreloadDataFuncInternal <- testEncodingFuncInternal showData <- function(dataset, jaspResults, name, cols, title, filter = "") { diff --git a/R/testEncodingFuncWrapper.R b/R/testEncodingFuncWrapper.R new file mode 100644 index 0000000..48091e5 --- /dev/null +++ b/R/testEncodingFuncWrapper.R @@ -0,0 +1,56 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test Variables encoding +#' +testEncodingFunc <- function( + data = NULL, + version = "1", + componentsListFromVariablesList = list(), + componentsListWithVariables = list(list(levelsOfSingleVariable = "", singleVariable = list(types = list(), value = ""), value = "#", variablesInComponentsList = list(types = list(), value = list()))), + dropdown = list(types = "unknown", value = ""), + genericFilter = "", + pairs = list(), + plotHeight = 320, + plotWidth = 480, + selected = list(types = list(), value = list()), + singleVariable = list(types = list(), value = ""), + variablesForComponentsList = list(optionKey = "components", types = list(), value = list()), + variablesWithInteractions = list(optionKey = "components", types = list(), value = list())) { + + defaultArgCalls <- formals(jaspTestModule::testEncodingFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("componentsListFromVariablesList", "componentsListWithVariables", "dropdown", "pairs", "selected", "singleVariable", "variablesForComponentsList", "variablesWithInteractions") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testEncodingFunc", "testEncoding.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testEncodingPreloadDataFuncWrapper.R b/R/testEncodingPreloadDataFuncWrapper.R new file mode 100644 index 0000000..1c28c0c --- /dev/null +++ b/R/testEncodingPreloadDataFuncWrapper.R @@ -0,0 +1,56 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test Variables encoding with preloadData +#' +testEncodingPreloadDataFunc <- function( + data = NULL, + version = "1", + componentsListFromVariablesList = list(), + componentsListWithVariables = list(list(levelsOfSingleVariable = "", singleVariable = list(types = list(), value = ""), value = "#", variablesInComponentsList = list(types = list(), value = list()))), + dropdown = list(types = "unknown", value = ""), + genericFilter = "", + pairs = list(), + plotHeight = 320, + plotWidth = 480, + selected = list(types = list(), value = list()), + singleVariable = list(types = list(), value = ""), + variablesForComponentsList = list(optionKey = "components", types = list(), value = list()), + variablesWithInteractions = list(optionKey = "components", types = list(), value = list())) { + + defaultArgCalls <- formals(jaspTestModule::testEncodingPreloadDataFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("componentsListFromVariablesList", "componentsListWithVariables", "dropdown", "pairs", "selected", "singleVariable", "variablesForComponentsList", "variablesWithInteractions") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testEncodingPreloadDataFunc", "testEncoding.qml", options, version, TRUE)) +} \ No newline at end of file diff --git a/R/testFactorsForm.R b/R/testFactorsForm.R index 18e9b6c..d185201 100644 --- a/R/testFactorsForm.R +++ b/R/testFactorsForm.R @@ -1,5 +1,5 @@ -testFactorsFormFunc <- function(jaspResults, dataset, options) +testFactorsFormFuncInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) diff --git a/R/testFactorsFormFuncWrapper.R b/R/testFactorsFormFuncWrapper.R new file mode 100644 index 0000000..d211646 --- /dev/null +++ b/R/testFactorsFormFuncWrapper.R @@ -0,0 +1,52 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test Factors Form +#' +testFactorsFormFunc <- function( + data = NULL, + version = "1", + factors = list(list(indicators = list(), name = "Factor1", title = "Factor 1")), + factorsList = list(list(keyLabel = "Factor 1", value = "Factor1")), + nested = TRUE, + nestedFactors = list(list(components = list(), name = "model0", title = "Model 0"), list(components = list(), name = "model1", title = "Model 1")), + plotHeight = 320, + plotWidth = 480, + variables = list(types = list(), value = list())) { + + defaultArgCalls <- formals(jaspTestModule::testFactorsFormFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("factors", "factorsList", "nestedFactors", "variables") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testFactorsFormFunc", "testFactorsForm.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testFootnotesTable.R b/R/testFootnotesTable.R index edc9c9f..8df1bac 100644 --- a/R/testFootnotesTable.R +++ b/R/testFootnotesTable.R @@ -1,4 +1,4 @@ -testFootnotesTableFunc <- function(jaspResults, dataset, options) +testFootnotesTableFuncInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) diff --git a/R/testHtml.R b/R/testHtml.R index 6c438a3..90227f7 100644 --- a/R/testHtml.R +++ b/R/testHtml.R @@ -1,4 +1,4 @@ -testHtmlFunc <- function(jaspResults, dataset, options) +testHtmlFuncInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) diff --git a/R/testHtmlFuncWrapper.R b/R/testHtmlFuncWrapper.R new file mode 100644 index 0000000..f31ce71 --- /dev/null +++ b/R/testHtmlFuncWrapper.R @@ -0,0 +1,45 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test jaspHtml +#' +testHtmlFunc <- function( + data = NULL, + version = "1", + checkbox_error = FALSE, + plotHeight = 320, + plotWidth = 480) { + + defaultArgCalls <- formals(jaspTestModule::testHtmlFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testHtmlFunc", "testHtml.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testInfo.R b/R/testInfo.R index 3d36542..531b51a 100644 --- a/R/testInfo.R +++ b/R/testInfo.R @@ -8,4 +8,4 @@ testInfoFunc <- function(jaspResults, dataset, options) jaspResults[['tafel']] <- createJaspTable(title="Glory", colTitles=c('hallo', 'wereld'), info="some info on a table") jaspResults[['nested']] <- createJaspContainer("Chairs", info='this is a container you know') jaspResults[['nested']][['egg']] <- createJaspHtml(text="This is nested within a container", info="some info on a nested jaspHtml") -} \ No newline at end of file +} diff --git a/R/testInputField.R b/R/testInputField.R index a6ad27f..77ea0d2 100644 --- a/R/testInputField.R +++ b/R/testInputField.R @@ -1,5 +1,5 @@ -testInputFieldFunc <- function(jaspResults, dataset, options) +testInputFieldFuncInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) diff --git a/R/testInputFieldFuncWrapper.R b/R/testInputFieldFuncWrapper.R new file mode 100644 index 0000000..19ff8ca --- /dev/null +++ b/R/testInputFieldFuncWrapper.R @@ -0,0 +1,54 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test Input Field +#' +testInputFieldFunc <- function( + data = NULL, + version = "1", + doubleInfinity = inf, + doubleMinInfinity = -inf, + doubleWith1Decimals = 0.1, + doubleWith3Decimals = 0.123, + doubleWith5Decimals = 0.12345, + formulaHalf = "3/2", + formulaInfinity = inf, + formulaMinInfinity = -inf, + formulaNan = nan, + integerInfinity = 2, + plotHeight = 320, + plotWidth = 480) { + + defaultArgCalls <- formals(jaspTestModule::testInputFieldFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testInputFieldFunc", "testInputField.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testJaspReport.R b/R/testJaspReport.R index 380db0d..350148f 100644 --- a/R/testJaspReport.R +++ b/R/testJaspReport.R @@ -1,5 +1,5 @@ #' @export -JaspReportTest <- function(jaspResults, dataset, options) +JaspReportTestInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) @@ -17,4 +17,4 @@ JaspReportTest <- function(jaspResults, dataset, options) reportOnThis, dependencies=c("reportMe", "selectedVariable")) } -} \ No newline at end of file +} diff --git a/R/testLabel.R b/R/testLabel.R index fcb611a..fdd56f0 100644 --- a/R/testLabel.R +++ b/R/testLabel.R @@ -1,4 +1,4 @@ -testLabelFormatFunc <- function(jaspResults, dataset, options) +testLabelFormatFuncInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) } diff --git a/R/testLabelFormatFuncWrapper.R b/R/testLabelFormatFuncWrapper.R new file mode 100644 index 0000000..d1f8475 --- /dev/null +++ b/R/testLabelFormatFuncWrapper.R @@ -0,0 +1,55 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test label format +#' +testLabelFormatFunc <- function( + data = NULL, + version = "1", + assigned = list(types = list(), value = list()), + checkbox = FALSE, + doubleFieldWithAfterLabel = 0, + dropDown = "high", + integerFieldWithAfterLabel = 0, + labelText = "Test H₀! Ttest", + plotHeight = 320, + plotWidth = 480, + radiobuttongroup = "rButton2", + textFormat = "2") { + + defaultArgCalls <- formals(jaspTestModule::testLabelFormatFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("assigned", "dropDown", "textFormat") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testLabelFormatFunc", "testLabelFormat.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testLatexFuncWrapper.R b/R/testLatexFuncWrapper.R new file mode 100644 index 0000000..7068979 --- /dev/null +++ b/R/testLatexFuncWrapper.R @@ -0,0 +1,45 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test latex +#' +testLatexFunc <- function( + data = NULL, + version = "1", + checkbox_error = FALSE, + plotHeight = 320, + plotWidth = 480) { + + defaultArgCalls <- formals(jaspTestModule::testLatexFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testLatexFunc", "testHtml.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testMinMax.R b/R/testMinMax.R index c18a916..065aeb2 100644 --- a/R/testMinMax.R +++ b/R/testMinMax.R @@ -1,5 +1,5 @@ -minMaxFunc <- function(jaspResults, dataset, options) +minMaxFuncInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) diff --git a/R/testMixedColumnsTable.R b/R/testMixedColumnsTable.R index 2dc884c..a587b3c 100644 --- a/R/testMixedColumnsTable.R +++ b/R/testMixedColumnsTable.R @@ -1,4 +1,4 @@ -testMixedColumnsTableFunc <- function(jaspResults, dataset, options) { +testMixedColumnsTableFuncInternal <- function(jaspResults, dataset, options) { if (options[["checkbox_0"]]) set.seed(123) diff --git a/R/testMixedColumnsTableFuncWrapper.R b/R/testMixedColumnsTableFuncWrapper.R new file mode 100644 index 0000000..f72ed4b --- /dev/null +++ b/R/testMixedColumnsTableFuncWrapper.R @@ -0,0 +1,46 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test Mixed Columns jaspTable +#' +testMixedColumnsTableFunc <- function( + data = NULL, + version = "1", + checkbox_0 = FALSE, + checkbox_1 = FALSE, + plotHeight = 320, + plotWidth = 480) { + + defaultArgCalls <- formals(jaspTestModule::testMixedColumnsTableFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testMixedColumnsTableFunc", "testMixedColumns.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testOptionContainsValue.R b/R/testOptionContainsValue.R index d4404c8..f26a5b5 100644 --- a/R/testOptionContainsValue.R +++ b/R/testOptionContainsValue.R @@ -1,4 +1,4 @@ -testOptionContainsValue <- function(jaspResults, dataset, options) +testOptionContainsValueInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) diff --git a/R/testOptionContainsValueWrapper.R b/R/testOptionContainsValueWrapper.R new file mode 100644 index 0000000..bba909f --- /dev/null +++ b/R/testOptionContainsValueWrapper.R @@ -0,0 +1,49 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test optionContainsValue +#' +testOptionContainsValue <- function( + data = NULL, + version = "1", + irrelevantCheckBox = FALSE, + plotHeight = 320, + plotWidth = 480, + restrictedModels = list(list(modelName = "Model 1", something = FALSE))) { + + defaultArgCalls <- formals(jaspTestModule::testOptionContainsValue) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("restrictedModels") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testOptionContainsValue", "testOptionContainsValue.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testOptionNames.R b/R/testOptionNames.R index 299b5cc..b783c22 100644 --- a/R/testOptionNames.R +++ b/R/testOptionNames.R @@ -1,5 +1,5 @@ -testOptionNames <- function(jaspResults, dataset, options) +testOptionNamesInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) diff --git a/R/testOptionNamesWrapper.R b/R/testOptionNamesWrapper.R new file mode 100644 index 0000000..8745969 --- /dev/null +++ b/R/testOptionNamesWrapper.R @@ -0,0 +1,55 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test Option Names +#' +testOptionNames <- function( + data = NULL, + version = "1", + allVariables = FALSE, + formula = FALSE, + myRadioButton = "jhsdf7634(*&(&))", + noformula = FALSE, + one = FALSE, + plotHeight = 320, + plotWidth = 480, + test = FALSE, + two = FALSE, + variables = list(types = list(), value = list())) { + + defaultArgCalls <- formals(jaspTestModule::testOptionNames) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("variables") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testOptionNames", "testOptionNames.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testRSource.R b/R/testRSource.R index 8aa08fa..5b7d657 100644 --- a/R/testRSource.R +++ b/R/testRSource.R @@ -1,4 +1,4 @@ -testRSourceFunc <- function(jaspResults, dataset, options) +testRSourceFuncInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) diff --git a/R/testRSourceFuncWrapper.R b/R/testRSourceFuncWrapper.R new file mode 100644 index 0000000..8cad80e --- /dev/null +++ b/R/testRSourceFuncWrapper.R @@ -0,0 +1,53 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test R Source +#' +testRSourceFunc <- function( + data = NULL, + version = "1", + ComponentsList = list(), + ComponentsList2 = list(), + assignMe = list(types = list(), value = list()), + assignMe2 = list(types = list(), value = list()), + drop = "one", + drop2 = "one", + plotHeight = 320, + plotWidth = 480) { + + defaultArgCalls <- formals(jaspTestModule::testRSourceFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("ComponentsList", "ComponentsList2", "assignMe", "assignMe2", "drop", "drop2") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testRSourceFunc", "testRSource.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testRadioButtons.R b/R/testRadioButtons.R index 80918da..b6a8c33 100644 --- a/R/testRadioButtons.R +++ b/R/testRadioButtons.R @@ -1,4 +1,4 @@ -radioButtonFunc <- function(jaspResults, dataset, options) +radioButtonFuncInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) diff --git a/R/testReqModule.R b/R/testReqModule.R index f2eacee..4c7f0b9 100644 --- a/R/testReqModule.R +++ b/R/testReqModule.R @@ -1,6 +1,6 @@ -descriptivesLink <- function(jaspResults, dataset, options) +descriptivesLinkInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) Descriptives::Descriptives(jaspResults, dataset, options) -} \ No newline at end of file +} diff --git a/R/testSaveLoadButton.R b/R/testSaveLoadButton.R index 172f483..09f67e2 100644 --- a/R/testSaveLoadButton.R +++ b/R/testSaveLoadButton.R @@ -1,4 +1,4 @@ -testSaveLoadButton <- function(jaspResults, dataset, options) +testSaveLoadButtonInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) diff --git a/R/testSaveLoadButtonWrapper.R b/R/testSaveLoadButtonWrapper.R new file mode 100644 index 0000000..1785c10 --- /dev/null +++ b/R/testSaveLoadButtonWrapper.R @@ -0,0 +1,47 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Save Load Button +#' +testSaveLoadButton <- function( + data = NULL, + version = "1", + loadMultiple = "", + loadThis = "", + plotHeight = 320, + plotWidth = 480, + saveHere = "") { + + defaultArgCalls <- formals(jaspTestModule::testSaveLoadButton) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testSaveLoadButton", "testSaveLoadButton.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testSpecialSources.R b/R/testSpecialSources.R index 88df641..6f95112 100644 --- a/R/testSpecialSources.R +++ b/R/testSpecialSources.R @@ -1,4 +1,4 @@ -testSpecialSourcesFunc <- function(jaspResults, dataset, options) +testSpecialSourcesFuncInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) diff --git a/R/testSpecialSourcesFuncWrapper.R b/R/testSpecialSourcesFuncWrapper.R new file mode 100644 index 0000000..ae9f7d0 --- /dev/null +++ b/R/testSpecialSourcesFuncWrapper.R @@ -0,0 +1,66 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test Sources with special attributes +#' +testSpecialSourcesFunc <- function( + data = NULL, + version = "1", + assigned = list(types = list(), value = list()), + combinationCross = list(), + combinations2way = list(), + combinations3way = list(), + compList = list(list(dropDownValuesa = list(types = "unknown", value = ""), value = "#")), + controlVariables = "", + labelsFor1Variable = "", + labelsForSeveralVariable = "", + ordinalAndNominalVariables = list(types = "unknown", value = ""), + ordinalVariables = list(types = "unknown", value = ""), + plotHeight = 320, + plotWidth = 480, + scaleVariables = list(types = "unknown", value = ""), + selectedVariables = list(types = list(), value = list()), + selectedVariablesWithControls = list(optionKey = "variable", types = list(), value = list()), + selectedVariablesWithControls2 = list(optionKey = "variable", types = list(), value = list()), + severalVariablesWithLabels = list(types = list(), value = list()), + variableWithLabels = list(types = list(), value = ""), + variables = list(types = list(), value = list()), + variablesWithConditions = list(types = list(), value = list()), + variablesWithConditions2 = list(types = list(), value = list())) { + + defaultArgCalls <- formals(jaspTestModule::testSpecialSourcesFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("assigned", "combinationCross", "combinations2way", "combinations3way", "compList", "controlVariables", "labelsFor1Variable", "labelsForSeveralVariable", "ordinalAndNominalVariables", "ordinalVariables", "scaleVariables", "selectedVariables", "selectedVariablesWithControls", "selectedVariablesWithControls2", "severalVariablesWithLabels", "variableWithLabels", "variables", "variablesWithConditions", "variablesWithConditions2") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testSpecialSourcesFunc", "testSpecialSources.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testState.R b/R/testState.R index 6b48c79..34e004b 100644 --- a/R/testState.R +++ b/R/testState.R @@ -1,4 +1,4 @@ -testStateFunc <- function(jaspResults, dataset, options) { +testStateFuncInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) diff --git a/R/testStateFuncWrapper.R b/R/testStateFuncWrapper.R new file mode 100644 index 0000000..42b2944 --- /dev/null +++ b/R/testStateFuncWrapper.R @@ -0,0 +1,53 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test jaspState +#' +testStateFunc <- function( + data = NULL, + version = "1", + checkbox_0 = FALSE, + checkbox_1 = FALSE, + plotHeight = 320, + plotWidth = 480, + saveMe = "Blablabla", + tabview = list(list(checkbox_2 = FALSE, tabviewOptions = "Tab 1", variablesForStateTab2 = list(types = list(), value = list()), variablesTab1 = list(types = list(), value = list()))), + variables = list(types = list(), value = list()), + variablesForState = list(types = list(), value = list())) { + + defaultArgCalls <- formals(jaspTestModule::testStateFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("tabview", "variables", "variablesForState") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testStateFunc", "testState.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testTabView.R b/R/testTabView.R index 312e25f..9052e82 100644 --- a/R/testTabView.R +++ b/R/testTabView.R @@ -1,5 +1,5 @@ -testTabView <- function(jaspResults, dataset, options) +testTabViewInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) diff --git a/R/testTabViewWrapper.R b/R/testTabViewWrapper.R new file mode 100644 index 0000000..c12ef39 --- /dev/null +++ b/R/testTabViewWrapper.R @@ -0,0 +1,54 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test Tab View +#' +testTabView <- function( + data = NULL, + version = "1", + oneValue = TRUE, + plotHeight = 320, + plotWidth = 480, + subTabs = list(list(keyLabel = "One", keyValue = "oneValue")), + tabViewWithFactors = list(list(factors = "", levels = list(types = "unknown", value = ""), repeatedMeasuresCells = list("", ""), repeatedMeasuresFactors = list(list(levels = list("Level 1", "Level 2"), name = "RM Factor 1")), value = "New tab")), + tabs = list(list(inputType = "paths", value = "New tab")), + threeValue = FALSE, + twoValue = FALSE, + variables = list(optionKey = "variable", types = list(), value = list())) { + + defaultArgCalls <- formals(jaspTestModule::testTabView) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("subTabs", "tabViewWithFactors", "tabs", "variables") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testTabView", "testTabView.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testTableFormatting.R b/R/testTableFormatting.R index 747a46a..2bdddf2 100644 --- a/R/testTableFormatting.R +++ b/R/testTableFormatting.R @@ -1,4 +1,4 @@ -testTableFormattingFunc <- function(jaspResults, dataset, options) +testTableFormattingFuncInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) diff --git a/R/testTableFormattingFuncWrapper.R b/R/testTableFormattingFuncWrapper.R new file mode 100644 index 0000000..43917aa --- /dev/null +++ b/R/testTableFormattingFuncWrapper.R @@ -0,0 +1,45 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test jaspTable formatting +#' +testTableFormattingFunc <- function( + data = NULL, + version = "1", + checkbox_error = FALSE, + plotHeight = 320, + plotWidth = 480) { + + defaultArgCalls <- formals(jaspTestModule::testTableFormattingFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testTableFormattingFunc", "testTableFormatting.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testTableView.R b/R/testTableView.R index ad8ad5d..e393532 100644 --- a/R/testTableView.R +++ b/R/testTableView.R @@ -1,4 +1,4 @@ -testTableView <- function(jaspResults, dataset, options) { +testTableViewInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) diff --git a/R/testTableViewWrapper.R b/R/testTableViewWrapper.R new file mode 100644 index 0000000..35e1b43 --- /dev/null +++ b/R/testTableViewWrapper.R @@ -0,0 +1,53 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test Table View +#' +testTableView <- function( + data = NULL, + version = "1", + diagonalTable = list(list(levels = list(), name = "data 1", values = list())), + minimumLevels = 2, + numberOfRows = 2, + numberOfRows2 = 1, + plotHeight = 320, + plotWidth = 480, + testButtons = list(list(levels = "Row 0", name = "data 1", values = "A"), list(levels = "Row 0", name = "data 2", values = "a"), list(levels = "Row 0", name = "data 3", values = "a")), + testTypes = list(list(levels = list("Row 0", "Row 1"), name = "data 1", values = list("A", "B")), list(levels = list("Row 0", "Row 1"), name = "data 2", values = list("2", "2")), list(levels = list("Row 0", "Row 1"), name = "data 3", values = list(4, 4)))) { + + defaultArgCalls <- formals(jaspTestModule::testTableView) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("diagonalTable", "testButtons", "testTypes") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testTableView", "testTableView.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testTextArea.R b/R/testTextArea.R index f5590a8..edfc0e0 100644 --- a/R/testTextArea.R +++ b/R/testTextArea.R @@ -1,5 +1,5 @@ -testTextArea <- function(jaspResults, dataset, options) +testTextAreaInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) diff --git a/R/testTextAreaWrapper.R b/R/testTextAreaWrapper.R new file mode 100644 index 0000000..4ca80e6 --- /dev/null +++ b/R/testTextAreaWrapper.R @@ -0,0 +1,51 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test TextArea +#' +testTextArea <- function( + data = NULL, + version = "1", + dropDown = list(types = "unknown", value = ""), + lavaanScript = list(columns = list(), model = "", modelOriginal = ""), + plotHeight = 320, + plotWidth = 480, + tabView = list(list(checkBoxesInTabView = list(), lavaanCodeInTabView = list(columns = list(), model = "", modelOriginal = ""), usedVariablesInTabView = list(optionKey = "variable", types = list(), value = list()), value = "Model 1")), + usedVariables = list(optionKey = "variable", types = list(), value = list())) { + + defaultArgCalls <- formals(jaspTestModule::testTextArea) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("dropDown", "lavaanScript", "tabView", "usedVariables") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testTextArea", "testTextArea.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/testTransposedTable.R b/R/testTransposedTable.R index f92ad83..fe090fe 100644 --- a/R/testTransposedTable.R +++ b/R/testTransposedTable.R @@ -1,4 +1,4 @@ -testTransposedTableFunc <- function(jaspResults, dataset, options) +testTransposedTableFuncInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) diff --git a/R/testVariablesForm.R b/R/testVariablesForm.R index 4a7afb2..16c96b7 100644 --- a/R/testVariablesForm.R +++ b/R/testVariablesForm.R @@ -1,8 +1,8 @@ -testVariablesForm <- function(jaspResults, dataset, options) +testVariablesFormInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) jaspResults[["Hallo"]] <- createJaspHtml(text="hallo!") -} \ No newline at end of file +} diff --git a/R/testVariablesFormWrapper.R b/R/testVariablesFormWrapper.R new file mode 100644 index 0000000..4182236 --- /dev/null +++ b/R/testVariablesFormWrapper.R @@ -0,0 +1,62 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' Test Variables Form +#' +testVariablesForm <- function( + data = NULL, + version = "1", + addCheckBox = FALSE, + addDropDown = FALSE, + maxLevel = -1, + maxNumLevel = -1, + minLevel = -1, + minNumLevel = -1, + nominal = FALSE, + ordinal = FALSE, + plotHeight = 320, + plotWidth = 480, + scale = TRUE, + setType = list(types = "unknown", value = ""), + toggleRemoveInvisibles = FALSE, + toggleVisibility = TRUE, + varS = list(types = list(), value = list()), + varToggle = list(types = list(), value = list()), + variablesWithControls = list(optionKey = "variable", types = list(), value = list())) { + + defaultArgCalls <- formals(jaspTestModule::testVariablesForm) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("setType", "varS", "varToggle", "variablesWithControls") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "testVariablesForm", "testVariablesForm.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/R/tooltipFunc.R b/R/tooltipFunc.R index da224da..f40f3f1 100644 --- a/R/tooltipFunc.R +++ b/R/tooltipFunc.R @@ -1,4 +1,4 @@ -tooltipFunc <- function(jaspResults, dataset, options) +tooltipFuncInternal <- function(jaspResults, dataset, options) { tableOfOptions(jaspResults, options, dataset) jaspResults[["Hallo"]] <- createJaspHtml(text="hallo!") diff --git a/R/tooltipFuncWrapper.R b/R/tooltipFuncWrapper.R new file mode 100644 index 0000000..bb9c090 --- /dev/null +++ b/R/tooltipFuncWrapper.R @@ -0,0 +1,107 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! + +#' ToolTip +#' +#' @param IntegerWithInfo, Field with info +#' @param IntegerWithTooLongValueAndInfo, Field with info and too long value +#' @param checkBoxWithInfo, Great info +#' Defaults to \code{FALSE}. +#' @param checkBoxWithInfoAndTooltip, Great info +#' Defaults to \code{FALSE}. +#' @param checkBoxWithSubCheckBox, Info for checkbox with sub checkboxes +#' Defaults to \code{FALSE}. +#' @param dropDownLabelAbove, Dropdown with info and label above +#' @param dropDownLabelAboveWithExtraInfo, Dropdown with extra info for value 'Two' and label above +#' \itemize{ +#' \item \code{"one"} (default) +#' \item \code{"two"}: Extra info for Two +#' \item \code{"three"} +#' } +#' @param dropdownWithExtraInfo, Dropdown with info +#' \itemize{ +#' \item \code{"one"} (default) : This is one +#' \item \code{"two"} +#' } +#' @param dropdownWithInfo, Dropdown with info +#' @param radioButtonsWithInfo, Info for the RadioButton group +#' \itemize{ +#' \item \code{"radioWithoutInfo"} +#' \item \code{"radioWithInfo"}: Info just for this RadioButton +#' } +#' @param rowRadioButtonsWithInfo, Info for the Row RadioButton group +#' \itemize{ +#' \item \code{"radioWithoutInfo"} +#' \item \code{"radioWithInfo"}: Info just for this RadioButton +#' } +#' @param rowRadioButtonsWithoutLabelWithInfo, No label but an info +#' \itemize{ +#' \item \code{"one"} +#' \item \code{"two"} +#' } +#' @param subCheckBoxWithInfo, Info for Sub CheckBox +#' Defaults to \code{FALSE}. +tooltipFunc <- function( + data = NULL, + version = "1", + IntegerWithInfo = 0, + IntegerWithTooLongValue = 1234567, + IntegerWithTooLongValueAndInfo = 1234567, + RadioButtonGroup1 = "group1RadioButton1", + RadioButtonGroup2 = "group2RadioButton1", + checkBoxWithInfo = FALSE, + checkBoxWithInfoAndTooltip = FALSE, + checkBoxWithSubCheckBox = FALSE, + checkBoxWithTooltip = FALSE, + checkBoxWithoutTootip = FALSE, + dropDownLabelAbove = "one", + dropDownLabelAboveWithExtraInfo = "one", + dropdownWithExtraInfo = "one", + dropdownWithInfo = "one", + plotHeight = 320, + plotWidth = 480, + radioButtons = "radioWithoutInfo", + radioButtonsWithInfo = "radioWithoutInfo", + rowRadioButtons = "radioWithInfo", + rowRadioButtonsWithInfo = "radioWithoutInfo", + rowRadioButtonsWithoutLabelWithInfo = "one", + rowRadioButtonsWithoutLabelWithoutInfo = "two", + subCheckBoxWithInfo = FALSE, + subCheckBoxWithoutInfo = FALSE) { + + defaultArgCalls <- formals(jaspTestModule::tooltipFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["data"]] <- NULL + options[["version"]] <- NULL + + + if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { + jaspBase::storeDataSet(data) + } + + optionsWithFormula <- c("dropDownLabelAbove", "dropDownLabelAboveWithExtraInfo", "dropdownWithExtraInfo", "dropdownWithInfo") + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "tooltipFunc", "Tooltip.qml", options, version, FALSE)) +} \ No newline at end of file diff --git a/inst/Description.qml b/inst/Description.qml index f341dee..9244a83 100644 --- a/inst/Description.qml +++ b/inst/Description.qml @@ -8,7 +8,7 @@ Description icon: "test.svg" description: qsTr("A module to test stuff with.") requiresData: false - hasWrappers: false + hasWrappers: true preloadData: false Timer @@ -125,14 +125,14 @@ Description hasWrapper: true } - // Same form, but NOT aware and NOT wrapped: the engine calls multiDataSetFuncInternal - // directly (dataset/readDataSet as always) and the dataset selection must stay inert. - // (It cannot use func: "multiDataSetFunc" - that name is the R-syntax wrapper now.) + // Same form, but NOT aware and NOT wrapped: data must arrive the old way (dataset/readDataSet) + // and the dataset selection must stay inert. Own func name because with hasWrappers the engine + // appends "Internal" to every entry's func (and the wrapper name may not *be* an Internal). Analysis { title: "Test Multi-DataSet (non-aware counterpart)" qml: "testMultiDataSet.qml" - func: "multiDataSetFuncInternal" + func: "multiDataSetNonAware" requiresData: true preloadData: true multiDataSetAware: false From 736439c2a2d451e54517485899f401582ce22f5c Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Wed, 7 Oct 2026 09:59:22 +0200 Subject: [PATCH 07/44] remove 4 dead Description entries (+orphaned R files); two VariablesForms in the multi-dataset test - Test Transposed jaspTable / Wait for it... (footnotes) / Test jaspContainer pointed at a test.qml that no longer exists, Test Plot Status at a never-existing TestPlotStatus.qml; removed the entries plus their now-unreferenced R files (testTransposedTable.R, testFootnotesTable.R, testContainer.R) and the Timer reference to the footnotes entry. - testMultiDataSet.qml now has TWO VariablesForms (A/B), each with its own dataSetSelection: point A at one dataset and B at the other and the results show both datasets loaded with per-form routing (source column A/B). The explanation now also states that a missing Data selection plus a "Non-aware run" table means the running JASP does not know the feature (stale binary/module). - Headless tests updated for dependentA/dependentB (23 checks, all passing); wrappers regenerated with the new defaults. --- R/multiDataSet.R | 82 +++++++++++++++++--------------- R/multiDataSetFuncWrapper.R | 5 +- R/multiDataSetNonAwareWrapper.R | 5 +- R/testContainer.R | 20 -------- R/testFootnotesTable.R | 68 -------------------------- R/testTransposedTable.R | 54 --------------------- Tools/testMultiDataSet.R | 22 ++++----- Tools/testMultiDataSet_wrapper.R | 4 +- inst/Description.qml | 30 ------------ inst/qml/testMultiDataSet.qml | 53 +++++++++++++++------ renv.lock | 33 +++---------- 11 files changed, 107 insertions(+), 269 deletions(-) delete mode 100644 R/testContainer.R delete mode 100644 R/testFootnotesTable.R delete mode 100644 R/testTransposedTable.R diff --git a/R/multiDataSet.R b/R/multiDataSet.R index f1f7a40..90b51c9 100644 --- a/R/multiDataSet.R +++ b/R/multiDataSet.R @@ -57,43 +57,46 @@ selectedVariablesTable <- function(jaspResults, options, datasets, dataset) { table <- createJaspTable("Selected variables") jaspResults[["selectedVariables"]] <- table - table$addColumnInfo("variable", title = "Variable (routed to)") - table$addColumnInfo("dataSetId", title = "Dataset id") - table$addColumnInfo("dataSetName", title = "Dataset title") - table$addColumnInfo("column", title = "Column") - table$addColumnInfo("type", title = "Type") - table$addColumnInfo("n", title = "n") - table$addColumnInfo("values", title = "mean (scale) / first levels (factor)") - - values <- .dependentValues(options) - - for (value in values) { - if (is.null(datasets)) { - # Non-aware: plain preload semantics, decode against the current encoder. - column <- sub("\\.(scale|ordinal|nominal)$", "", jaspBase::decodeColNames(value)) - entry <- list(variable = column, dataSetId = "-", dataSetName = "-", column = column, - type = .typeSuffix(value), n = NA_integer_, values = "") - if (!is.null(dataset) && column %in% names(dataset)) - entry <- .fillDescription(entry, dataset[[column]]) - table[[value]] <- entry - next + table$addColumnInfo("source", title = "Form") + table$addColumnInfo("variable", title = "Variable (routed to)") + table$addColumnInfo("dataSetId", title = "Dataset id") + table$addColumnInfo("dataSetName", title = "Dataset title") + table$addColumnInfo("column", title = "Column") + table$addColumnInfo("type", title = "Type") + table$addColumnInfo("n", title = "n") + table$addColumnInfo("values", title = "mean (scale) / first levels (factor)") + + for (entry in list(list(form = "A", values = .dependentValues(options$dependentA)), + list(form = "B", values = .dependentValues(options$dependentB)))) { + for (value in entry$values) { + if (is.null(datasets)) { + # Non-aware: plain preload semantics, decode against the current encoder. + column <- sub("\\.(scale|ordinal|nominal)$", "", jaspBase::decodeColNames(value)) + row <- list(source = entry$form, variable = column, dataSetId = "-", dataSetName = "-", + column = column, type = .typeSuffix(value), n = NA_integer_, values = "") + if (!is.null(dataset) && column %in% names(dataset)) + row <- .fillDescription(row, dataset[[column]]) + table[[paste0(entry$form, ":", value)]] <- row + next + } + + id <- jaspBase::dataSetIdFromEncoded(value) + col <- jaspBase::dataSetColumnFromEncoded(value, datasets) + row <- list( + source = entry$form, + variable = value, + dataSetId = if (is.na(id)) "none (legacy)" else id, + dataSetName = jaspBase::dataSetNameFromEncoded(value, datasets), + column = col, + type = .typeSuffix(value), + n = NA_integer_, values = "") + + columnData <- jaspBase::getDataSetColumn(value, datasets) + if (!is.null(columnData)) + row <- .fillDescription(row, columnData) + + table[[paste0(entry$form, ":", value)]] <- row } - - id <- jaspBase::dataSetIdFromEncoded(value) - column <- jaspBase::dataSetColumnFromEncoded(value, datasets) - entry <- list( - variable = value, - dataSetId = if (is.na(id)) "none (legacy)" else id, - dataSetName = jaspBase::dataSetNameFromEncoded(value, datasets), - column = column, - type = .typeSuffix(value), - n = NA_integer_, values = "") - - columnData <- jaspBase::getDataSetColumn(value, datasets) - if (!is.null(columnData)) - entry <- .fillDescription(entry, columnData) - - table[[value]] <- entry } } @@ -126,9 +129,10 @@ multiDataSetNonAwareInternal <- function(jaspResults, dataset, options) { # ------------------------------------------------------------------ internals -.dependentValues <- function(options) { - values <- unlist(options$dependent, recursive = TRUE) - values[!is.na(values) & as.character(values) != ""] +.dependentValues <- function(values) { + if (is.null(values)) return(character(0)) + values <- unlist(values, recursive = TRUE) + as.character(values[!is.na(values) & as.character(values) != ""]) } .typeSuffix <- function(value) { diff --git a/R/multiDataSetFuncWrapper.R b/R/multiDataSetFuncWrapper.R index 382455f..790c0ab 100644 --- a/R/multiDataSetFuncWrapper.R +++ b/R/multiDataSetFuncWrapper.R @@ -22,7 +22,8 @@ multiDataSetFunc <- function( datasets = NULL, version = "1", - dependent = list(types = list(), value = list()), + dependentA = list(types = list(), value = list()), + dependentB = list(types = list(), value = list()), guardDemo = FALSE, plotHeight = 320, plotWidth = 480, @@ -44,7 +45,7 @@ multiDataSetFunc <- function( data <- if (!is.null(datasets) && length(datasets)) datasets[[1]] else NULL - optionsWithFormula <- c("dependent") + optionsWithFormula <- c("dependentA", "dependentB") for (name in optionsWithFormula) { if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } diff --git a/R/multiDataSetNonAwareWrapper.R b/R/multiDataSetNonAwareWrapper.R index b2a6734..9aba292 100644 --- a/R/multiDataSetNonAwareWrapper.R +++ b/R/multiDataSetNonAwareWrapper.R @@ -22,7 +22,8 @@ multiDataSetNonAware <- function( data = NULL, version = "1", - dependent = list(types = list(), value = list()), + dependentA = list(types = list(), value = list()), + dependentB = list(types = list(), value = list()), guardDemo = FALSE, plotHeight = 320, plotWidth = 480, @@ -42,7 +43,7 @@ multiDataSetNonAware <- function( jaspBase::storeDataSet(data) } - optionsWithFormula <- c("dependent") + optionsWithFormula <- c("dependentA", "dependentB") for (name in optionsWithFormula) { if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } diff --git a/R/testContainer.R b/R/testContainer.R deleted file mode 100644 index 9228811..0000000 --- a/R/testContainer.R +++ /dev/null @@ -1,20 +0,0 @@ -testContainerFuncInternal <- function(jaspResults, dataset, options) -{ - tableOfOptions(jaspResults, options, dataset) - - cnt <- addContainer(addContainer(addContainer(addContainer(jaspResults, name="c0")))) - testFootnotesTableFuncInternal(cnt, dataset, options) - - cnt <- addContainer(addContainer(addContainer(addContainer(jaspResults, name="c1")), title="hallo")) - testFootnotesTableFuncInternal(cnt, dataset, options) - - cnt <- addContainer(addContainer(addContainer(addContainer(jaspResults, title="hallo", name="c2")), title="er zit er eentje stiekem om me heen"), title="hallo", initCollapsed=TRUE) - testFootnotesTableFuncInternal(cnt, dataset, options) -} - -addContainer <- function(inContainer, name="container", title="", initCollapsed=FALSE) -{ - inContainer[[name]] = createJaspContainer(title=title, initCollapsed=initCollapsed) - - return(inContainer[[name]]) -} diff --git a/R/testFootnotesTable.R b/R/testFootnotesTable.R deleted file mode 100644 index 8df1bac..0000000 --- a/R/testFootnotesTable.R +++ /dev/null @@ -1,68 +0,0 @@ -testFootnotesTableFuncInternal <- function(jaspResults, dataset, options) -{ - tableOfOptions(jaspResults, options, dataset) - - jaspResults[['table0']] <- createFootnotesTable(options=options) - jaspResults[['table1']] <- createFootnotesTable(options=options, notUnique=TRUE) - - createEmptiesTable(jaspResults) - createCombinations(jaspResults) -} - -createCombinations <- function(jaspResults) -{ - binary <- data.frame(list(a=c("00", "01"), b=c("10", "11"))) - rownames(binary) <- c('c', 'd') - tab <- createJaspTable("Combining row- with col-names or not!") - - tab$setData(binary) - - tab$addFootnote(message="nothing!") - tab$addFootnote(message="just row", rowNames='c') - tab$addFootnote(message="just col", colNames='a') - tab$addFootnote(message="row&&col", rowNames='d', colNames='b') - - jaspResults[["cartesianFootnotes"]] <- tab -} - -createEmptiesTable <- function(jaspResults) -{ - #for https://github.com/jasp-stats/INTERNAL-jasp/issues/538 (footnotes mess) - - tab <- createJaspTable(paste0("Table with Footnotes at empty cell")) - jaspResults[["tableEmptyCell"]] <- tab - - tab$setExpectedSize(rows=3, cols=3) - tab$setColumnName(1, 'b') - tab$setRowName(1, 'b') - - tab$setColumnName(2, 'a') - tab$setColumnName(3, 'c') - tab$setRowName(3, 'a') - - tab$addFootnote(message="hallo", rowNames='a', colNames='c') - - tab$addRows(list(a=NA, b=NA, c=NA)) - tab$addFootnote(message="NA!", rowNames='b', colNames='b') -} - -createFootnotesTable <- function(numFootnotes=3, rowNames=c("b", "c", "c"), colNames=c("a", "a", "b"), options, notUnique=FALSE) -{ - foo <- sapply(letters[21:23], function(col) paste(letters[24:26], col)) - rownames(foo) <- colnames(foo) <- letters[1:3] - - tableFootnotes <- createJaspTable(paste0("Table with Footnotes at rowNames=(", paste0(rowNames, collapse=", "), ") and colNames=(", paste0(colNames, collapse=", "), ")", collapse=""), foo, dependencies="checkbox_0") - - msgNum <- 0 - - while(msgNum < numFootnotes) - { - msgNum <- msgNum + 1 - - if(options$checkbox_0 == notUnique) tableFootnotes$addFootnote(message = paste0("msg", as.character(msgNum)), rowNames = rowNames[[msgNum]], colNames = colNames[[msgNum]]) - else if(options$checkbox_1 != notUnique) tableFootnotes$addFootnote(message = "Identical Twins!", rowNames = rowNames[[msgNum]], colNames = colNames[[msgNum]]) - else tableFootnotes$addFootnote(message = "Identical Symbols!", rowNames = rowNames[[msgNum]], colNames = colNames[[msgNum]], symbol="?") - } - - return(tableFootnotes); -} diff --git a/R/testTransposedTable.R b/R/testTransposedTable.R deleted file mode 100644 index fe090fe..0000000 --- a/R/testTransposedTable.R +++ /dev/null @@ -1,54 +0,0 @@ -testTransposedTableFuncInternal <- function(jaspResults, dataset, options) -{ - tableOfOptions(jaspResults, options, dataset) - - - # the correlation table stored here - mainTable <- createJaspTable(title = "Transposed Table") - mainTable$showSpecifiedColumnsOnly <- TRUE - - # I guess we want the transpose - mainTable$transpose <- TRUE - - # but we don't want overtitles over the "displayed" columns - #mainTable$transposeWithOvertitle <- FALSE - - # the displayed columns are supposed to be just the number of variable - mainTable$addColumnInfo(name = "var", title = "", type = "string", overtitle="Variables") - - # now we need to define the "displayed" rows; ideally defining columns with overtitles - whichtests <- c(TRUE, TRUE, TRUE)#options$pearson, options$spearman, options$kendallsTauB) - testsTitles <- c("Pearson's rho", "Spearman's r", "Kendall's Tau B")[whichtests] - tests <- c("pearson", "spearman", "kendall")[whichtests] - - vars <- options$variables - vvars <- vars # inside just, this will be actually .v(vars) - - for (vi in seq_along(vvars)){ - for(ti in seq_along(tests)){ - mainTable$addColumnInfo( - name = paste(vvars[vi], tests[ti], "r", sep = "."), - title = testsTitles[ti], type = "number", - overtitle = vars[vi]) # this overtitle should appear as a first column - - - #if(options$reportSignificance) - mainTable$addColumnInfo(name = paste(vvars[vi], tests[ti], "p.value", sep = "."), - title = "p-value", type = "pvalue", - overtitle = vars[vi]) # this overtitle should appear as a first column - - # here will be more option-dependent stuff, but this should be enough as an example - } - } - - # the displayed columns will show the variable names - mainTable$addColumns(list(var = vars)) - - # everything else is passed to the table with the computed results... - # fake example - mainTable$addColumns(list(contNormal.pearson.r = c(0.2, 0.3, 0.4), contNormal.pearson.p.value = c(0.1, 0.034, 0.001))) - - #mainTable$print() - jaspResults[['transposeMe']] <- mainTable - -} diff --git a/Tools/testMultiDataSet.R b/Tools/testMultiDataSet.R index fb9b07c..fc9ed92 100644 --- a/Tools/testMultiDataSet.R +++ b/Tools/testMultiDataSet.R @@ -51,8 +51,8 @@ suppressMessages(jaspTools::setupJaspTools( jaspTools::setPkgOption("module.dirs", moduledir) jaspTools::setPkgOption("reinstall.modules", FALSE) -opts1 <- list(dependent = c("JASPColumn_11_0.scale", "JASPColumn_11_2.nominal", - "JASPColumn_12_0.scale", "JASPColumn_12_1.scale"), +opts1 <- list(dependentA = c("JASPColumn_11_0.scale", "JASPColumn_11_2.nominal"), + dependentB = c("JASPColumn_12_0.scale", "JASPColumn_12_1.scale"), showMeta = FALSE, guardDemo = TRUE, plotWidth = 400, plotHeight = 300) res1 <- jaspTools::runAnalysis("multiDataSetFunc", options = opts1, datasets = datasets, view = FALSE, quiet = TRUE) r1 <- res1$results @@ -66,26 +66,26 @@ ovTitle <- columnValues(r1$dataSetOverview, "title") check(identical(unname(ovId), c("11", "12")), "overview ids 11,12") check(identical(unname(ovTitle), c("DataSetA", "DataSetB")), "overview titles from dataSetNames attr") +# schema order: source=1, variable=2, ... keyed by the row id so assertions do not depend on json order sv <- r1$selectedVariables +svSrc <- columnValues(sv, "source") svVar <- columnValues(sv, "variable") svId <- columnValues(sv, "dataSetId") svName <- columnValues(sv, "dataSetName") svCol <- columnValues(sv, "column") svType <- columnValues(sv, "type") svN <- columnValues(sv, "n") -# keyed by the row id so assertions do not depend on json row order -byRow <- function(v) v[svVar] expected <- list( - "JASPColumn_11_0.scale" = c(id = "11", name = "DataSetA", column = "Score", type = "scale"), - "JASPColumn_11_2.nominal" = c(id = "11", name = "DataSetA", column = "Group", type = "nominal"), - "JASPColumn_12_0.scale" = c(id = "12", name = "DataSetB", column = "Score", type = "scale"), - "JASPColumn_12_1.scale" = c(id = "12", name = "DataSetB", column = "Age", type = "scale")) + "JASPColumn_11_0.scale" = c(src = "A", id = "11", name = "DataSetA", column = "Score", type = "scale"), + "JASPColumn_11_2.nominal" = c(src = "A", id = "11", name = "DataSetA", column = "Group", type = "nominal"), + "JASPColumn_12_0.scale" = c(src = "B", id = "12", name = "DataSetB", column = "Score", type = "scale"), + "JASPColumn_12_1.scale" = c(src = "B", id = "12", name = "DataSetB", column = "Age", type = "scale")) check(all(names(expected) %in% svVar), "all four dependents present in the routing table") for (v in names(expected)) if (v %in% svVar) { i <- which(svVar == v)[1] - check(svId[i] == expected[[v]]["id"] && svName[i] == expected[[v]]["name"] && + check(svSrc[i] == expected[[v]]["src"] && svId[i] == expected[[v]]["id"] && svName[i] == expected[[v]]["name"] && svCol[i] == expected[[v]]["column"] && svType[i] == expected[[v]]["type"], - paste0("routing ", v, " -> ", expected[[v]]["name"], "/", expected[[v]]["column"])) + paste0("routing [form ", expected[[v]]["src"], "] ", v, " -> ", expected[[v]]["name"], "/", expected[[v]]["column"])) } check(all(svN == "10"), "all four columns read 10 rows") @@ -105,7 +105,7 @@ check(any(grepl("MULTIDATASET-WRAPPER-OK", status2, fixed = TRUE)), "wrapper sub # ------------------------------------------------------------------ Path 3: legacy still OK ---- cat("\n== Path 3: legacy non-aware run (no datasets) ==\n") res3 <- jaspTools::runAnalysis("multiDataSetFunc", dataset = datasets[["11"]], - options = list(dependent = c("Score"), showMeta = FALSE, + options = list(dependentA = c("Score"), dependentB = c("Age"), showMeta = FALSE, guardDemo = TRUE, plotWidth = 400, plotHeight = 300), view = FALSE, quiet = TRUE) raw3 <- jsonlite::toJSON(res3, auto_unbox = TRUE) diff --git a/Tools/testMultiDataSet_wrapper.R b/Tools/testMultiDataSet_wrapper.R index e6329a8..6454b51 100644 --- a/Tools/testMultiDataSet_wrapper.R +++ b/Tools/testMultiDataSet_wrapper.R @@ -17,7 +17,9 @@ datasets <- list( "12" = read.csv(file.path(moduledir, "Tools/testdata/DataSetB.csv"))) attr(datasets, "dataSetNames") <- c("11" = "DataSetA", "12" = "DataSetB") -res <- jaspTestModule::multiDataSetFunc(datasets = datasets, dependent = c("Score", "Condition")) +res <- jaspTestModule::multiDataSetFunc(datasets = datasets, + dependentA = "Score", # in both datasets -> routes to the primary + dependentB = c("Age", "Condition")) # Condition only exists in DataSetB raw <- as.character(res$.__enclos_env__$private$getResults()) j <- jsonlite::fromJSON(raw, simplifyVector = FALSE)$results diff --git a/inst/Description.qml b/inst/Description.qml index 9244a83..9e031de 100644 --- a/inst/Description.qml +++ b/inst/Description.qml @@ -18,7 +18,6 @@ Description { console.log(qsTr("Timer that displays the fact that updating Description.qml from live javascript code works fine.")) testModuleDescription.title = qsTr("Test Module?") - footnotesEntry.title = qsTr("Test Footnotes jaspTable") } running: true @@ -26,13 +25,6 @@ Description } GroupTitle { title: "jaspResults stuff" } - Analysis - { - title: qsTr("Test Transposed jaspTable") - qml: "test.qml" - func: "testTransposedTableFunc" - } - Analysis { title: qsTr("Test jaspTable with crappy data") @@ -48,14 +40,6 @@ Description func: "testTableFormattingFunc" } - Analysis - { - id: footnotesEntry - title: qsTr("Wait for it...") - qml: "test.qml" - func: "testFootnotesTableFunc" - } - Analysis { title: qsTr("Test jaspState") @@ -77,20 +61,6 @@ Description func: "testLatexFunc" } - Analysis - { - title: qsTr("Test jaspContainer") - qml: "test.qml" - func: "testContainerFunc" - } - - Analysis - { - title: qsTr("Test Plot Status") - qml: "TestPlotStatus.qml" - func: "testPlotStatus" - } - Analysis { title: qsTr("Test Variables encoding") diff --git a/inst/qml/testMultiDataSet.qml b/inst/qml/testMultiDataSet.qml index cb29b2b..01cd24a 100644 --- a/inst/qml/testMultiDataSet.qml +++ b/inst/qml/testMultiDataSet.qml @@ -9,29 +9,52 @@ Form ExplanationText { text: - "Test the multi-dataset awareness of VariablesForm: the dataset/filter selection at the top " + - "(only live for multiDataSetAware analyses) plus the per-dataset routing in the results.
    " + + "Test the multi-dataset awareness of VariablesForm: each form has its own " + + "dataset/filter selection (only live for multiDataSetAware analyses) and the results " + + "show per-variable routing to the dataset it came from.
    " + "
      " + - "
    • Open a second dataset (e.g. add another CSV) so the selection has something to choose from.
    • " + - "
    • Drag variables from different datasets into the list: the results table should route " + - "each one to its own dataset (id, title, column).
    • " + - "
    • Pick another dataset/filter in the selection: only columns of that dataset should remain " + - "selectable for new drags and the run should recompute.
    • " + + "
    • Open a second dataset (e.g. add another CSV) so the selections have something to choose from.
    • " + + "
    • Set form A's Data selection to one dataset and form B's to the other: the run should then " + + "load both datasets (see the 'Datasets in this run' table).
    • " + + "
    • Drag variables (try the identically named 'Score'!) into both lists: the routing table " + + "must attribute each one to its own dataset, filter and column.
    • " + "
    • show .meta: the provenance stamps (dataSetId/filterId) on the bound options.
    • " + "
    • guard demo: readDataSet() must hard-stop inside an aware run.
    • " + "
    • Save the analysis to a .jasp and reopen it: the dataset routing must survive the round trip.
    • " + - "
    " + "" + + "If no Data selection shows up, the results table will say 'Non-aware run': the analysis was " + + "not recognised as multiDataSetAware (old JASP binary or stale module install)." } - VariablesForm - { - dataSetSelection: true + Section { + title: qsTr("Form A - variables from one dataset") + + VariablesForm + { + dataSetSelection: true + + AvailableVariablesList { name: "allVariablesA" } + AssignedVariablesList + { + name: "dependentA" + title: qsTr("Variables A") + } + } + } - AvailableVariablesList { name: "allVariables" } - AssignedVariablesList + Section { + title: qsTr("Form B - variables from (hopefully) another dataset") + + VariablesForm { - name: "dependent" - title: qsTr("Variables (from any dataset)") + dataSetSelection: true + + AvailableVariablesList { name: "allVariablesB" } + AssignedVariablesList + { + name: "dependentB" + title: qsTr("Variables B") + } } } diff --git a/renv.lock b/renv.lock index e171ff6..f4558d1 100644 --- a/renv.lock +++ b/renv.lock @@ -411,33 +411,12 @@ "jaspBase": { "Package": "jaspBase", "Version": "0.20.4", - "Source": "GitHub", - "Requirements": [ - "cli", - "codetools", - "ggplot2", - "gridExtra", - "gridGraphics", - "jaspGraphs", - "jsonlite", - "lifecycle", - "officer", - "pkgbuild", - "plyr", - "ragg", - "R6", - "Rcpp", - "rvg", - "svglite", - "systemfonts", - "withr", - "testthat" - ], - "RemoteType": "github", - "RemoteHost": "api.github.com", - "RemoteUsername": "jasp-stats", - "RemoteRepo": "jaspBase", - "RemoteSha": "6a6910dbe148caba2d82d60b8c42305ac40d2f1d" + "Source": "Local", + "Requirements": ["cli", "codetools", "compiler", "ggplot2", "grDevices", "grid", "gridExtra", "gridGraphics", "jaspGraphs", "jsonlite", "lifecycle", "methods", "officer", "pkgbuild", "plyr", "ragg", "R6", "Rcpp", "rvg", "svglite", "systemfonts", "withr"], + "RemoteType": "local", + "RemoteUrl": "/home/virtuoos/Broncode/JASP/jasp-desktop/Engine/jaspBase", + "Cacheable": true, + "Hash": "7fa78a6a9cb16c17cf2707e7bb5ff577" }, "jaspGraphs": { "Package": "jaspGraphs", From 2ef9f639869abe1c65f4400124deb5caab275c11 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Wed, 7 Oct 2026 20:23:50 +0200 Subject: [PATCH 08/44] multiDataSet demo: index the encoded namespace directly The demo analysis now shows the feature as designed: option values ARE the datasets' column names (both encoded by the same per-dataset encoder; results are decoded on the way out by the engine), so getDataSetColumn(value, datasets) is the whole lookup. Dropped the decode-first logic, the redundant 'column'/'type' table fields and the legacy decodeColNames call. Path-1 test fixtures are faithful now: slices carry encoded column names exactly like rbridge delivers them, including per-slice mean checks against the source csv (catches cross-dataset mixups). Wrapper path marked SKIP pending the syntax-mode dataset-selection flow (TODO-multidataset.md). --- R/multiDataSet.R | 50 ++++++++----------- TODO-multidataset.md | 76 +++++++++++++++++++++++++++++ Tools/testMultiDataSet.R | 83 ++++++++++++++++++++------------ Tools/testMultiDataSet_wrapper.R | 12 +++-- 4 files changed, 159 insertions(+), 62 deletions(-) create mode 100644 TODO-multidataset.md diff --git a/R/multiDataSet.R b/R/multiDataSet.R index 90b51c9..80f9888 100644 --- a/R/multiDataSet.R +++ b/R/multiDataSet.R @@ -1,9 +1,14 @@ # Multi-dataset aware analyses (jasp-desktop branch dataSetAwareVarForm, jaspBase branch # multiDataSetAware). An analysis whose Description/Analysis entry sets multiDataSetAware gets # every dataset it references as `datasets`: a named list keyed by dataset id, with the user -# facing titles in attr(datasets, "dataSetNames"). Its variable options carry encoded column -# names whose prefix embeds the dataset they were selected from (JASPColumn__), -# which jaspBase's dataSetIdFromEncoded / dataSetNameFromEncoded / getDataSetColumn route. +# facing titles in attr(datasets, "dataSetNames"). Options and data share the ENCODED namespace: +# variable options carry names like "JASPColumn___Encoded" and the dataframes' +# column names are the very same strings (encoded by each dataset's own ColumnEncoder, whose +# prefix embeds the dataset id), so an analysis indexes with the option value as-is: +# getDataSetColumn(value, datasets) / datasets[[id]][[value]]. Result strings are decoded back to +# the original names on their way to the GUI, so putting encoded names in tables/plots is not +# just allowed, it is how provenance survives. jaspBase's dataSetIdFromEncoded / +# dataSetNameFromEncoded / getDataSetFor route values to their dataset. # While such a run is active, readDataSet* stops: one 'current dataset' is meaningless here. multiDataSetFuncInternal <- function(jaspResults, dataset, options, datasets = NULL) { @@ -35,7 +40,7 @@ dataSetOverviewTable <- function(jaspResults, datasets, dataset) { table$addColumnInfo("title", title = "Title") table$addColumnInfo("rows", title = "Rows") table$addColumnInfo("cols", title = "Columns") - table$addColumnInfo("columnNames", title = "Column names") + table$addColumnInfo("columnNames", title = "Column names (decoded on the way out)") if (!is.null(datasets)) { titles <- attr(datasets, "dataSetNames") @@ -48,7 +53,7 @@ dataSetOverviewTable <- function(jaspResults, datasets, dataset) { } } else if (!is.null(dataset)) { table[["current"]] <- list( - id = "(current)", title = "(the one dataset)", rows = nrow(dataset), ncol(dataset), + id = "(current)", title = "(the one dataset)", rows = nrow(dataset), cols = ncol(dataset), columnNames = paste(names(dataset), collapse = ", ")) } } @@ -58,40 +63,32 @@ selectedVariablesTable <- function(jaspResults, options, datasets, dataset) { jaspResults[["selectedVariables"]] <- table table$addColumnInfo("source", title = "Form") - table$addColumnInfo("variable", title = "Variable (routed to)") + table$addColumnInfo("variable", title = "Variable (decoded on the way out)") table$addColumnInfo("dataSetId", title = "Dataset id") table$addColumnInfo("dataSetName", title = "Dataset title") - table$addColumnInfo("column", title = "Column") - table$addColumnInfo("type", title = "Type") table$addColumnInfo("n", title = "n") table$addColumnInfo("values", title = "mean (scale) / first levels (factor)") for (entry in list(list(form = "A", values = .dependentValues(options$dependentA)), list(form = "B", values = .dependentValues(options$dependentB)))) { for (value in entry$values) { - if (is.null(datasets)) { - # Non-aware: plain preload semantics, decode against the current encoder. - column <- sub("\\.(scale|ordinal|nominal)$", "", jaspBase::decodeColNames(value)) - row <- list(source = entry$form, variable = column, dataSetId = "-", dataSetName = "-", - column = column, type = .typeSuffix(value), n = NA_integer_, values = "") - if (!is.null(dataset) && column %in% names(dataset)) - row <- .fillDescription(row, dataset[[column]]) - table[[paste0(entry$form, ":", value)]] <- row - next + # value indexes the data as-is in both modes: options and column names share the encoded + # namespace (or are both plain, in a jaspTools run). Only the routing to one dataset out + # of `datasets` is aware-specific; the non-aware run has exactly the one dataset anyway. + columnData <- if (is.null(datasets)) { + if (!is.null(dataset) && value %in% names(dataset)) dataset[[value]] else NULL + } else { + jaspBase::getDataSetColumn(value, datasets) } - id <- jaspBase::dataSetIdFromEncoded(value) - col <- jaspBase::dataSetColumnFromEncoded(value, datasets) + id <- if (is.null(datasets)) NA_integer_ else jaspBase::dataSetIdFromEncoded(value) row <- list( source = entry$form, variable = value, - dataSetId = if (is.na(id)) "none (legacy)" else id, - dataSetName = jaspBase::dataSetNameFromEncoded(value, datasets), - column = col, - type = .typeSuffix(value), + dataSetId = if (is.null(datasets)) "-" else if (is.na(id)) "none (legacy)" else as.character(id), + dataSetName = if (is.null(datasets)) "-" else jaspBase::dataSetNameFromEncoded(value, datasets), n = NA_integer_, values = "") - columnData <- jaspBase::getDataSetColumn(value, datasets) if (!is.null(columnData)) row <- .fillDescription(row, columnData) @@ -135,11 +132,6 @@ multiDataSetNonAwareInternal <- function(jaspResults, dataset, options) { as.character(values[!is.na(values) & as.character(values) != ""]) } -.typeSuffix <- function(value) { - match <- regmatches(as.character(value), regexec("\\.(scale|ordinal|nominal)$", value))[[1]] - if (length(match) == 2L) match[2] else NA_character_ -} - .fillDescription <- function(entry, columnData) { if (is.numeric(columnData)) { entry$n <- sum(!is.na(columnData)) diff --git a/TODO-multidataset.md b/TODO-multidataset.md new file mode 100644 index 0000000..3f7a3e6 --- /dev/null +++ b/TODO-multidataset.md @@ -0,0 +1,76 @@ +# Multi-dataset aware feature — open work (2026-10-07) + +## Decided design (ground truth from user, this session) +- Options and dataset slices live in the SAME encoded namespace everywhere (`JASPColumn___Encoded`); + R indexes `datasets[[id]][[encodedValue]]` directly; results/plots are decoded on the way out (engine sendString). +- `preloadData` is forced true for aware analyses (done at `Modules::AnalysisEntry::preloadData()` + EntryBase), + and the engine checks `_analysisMultiDataSet` *before* the preload flag. +- Syntax mode must encode like the engine (verified: it does for single-dataset). No scans, no decode-hacks. +- Syntax-mode multi-dataset (user's design, TO BUILD): + `datasets = list(haha = df1, godverdomme = df2)` loads each df into the bridge **Workspace/DataSet** + (title = the user's name; names are user-convenience only). **The VariablesForms' dataset selections are + specific options** that must not be empty (check!); the user fills e.g. `"haha"` there as soon as the data + is loaded; selecting it switches the form's dataset context, QMLComponents then validate/encode the + column-name options against that dataset (they know their sources, stamp `.meta` dataSetId), and the + encoder (now existing via DataSet) encodes per datasetId. This cascades through the options. + +## TODO +1. [done: subagent design] Option-backed VariablesForm dataset selection for syntax mode. Agreed plan: + - CommonData/workspace: `dataSetByTitle(QString)` exact-match lookup (mirror dataSetByName). + - VariablesFormBase: `dataSetSelectionOption` Q_PROPERTY (option name for this form's dataset selection); + setter also self-declares awareness (`analysisObj()->setMultiDataSetAware(true)` — bridge dummy analysis + otherwise never gets it); `dataSetTitleValues()` (workspace titles); `selectDataSetByName(name)`: + empty/unknown -> addControlError, match -> analysis->setFilterId(ds->defaultFilter()->id()) + + Workspace::setShownDataSet(ds) (filterId semantics = default, syntax users pre-filter). + - VariablesForm.qml: a second, BOUND DropDown (name: dataSetSelectionOption, values: titles) gated by + NO_DESKTOP_MODE (typeof-guard) -> desktop stays 100% inert; wrapper generation picks the option up. + AssignedVariablesList gets `depends: ""` so bindTo applies the selection FIRST + (setInitialized defers; meta then stamps the right dataSetId via createMeta). + - AnalysisForm: `dataSetSelectionOptionNames()` (findChildren). + - syntaxbridge loadQmlAndParseOptionsStatus: PRE-CHECK raw options JSON for those names (empty check, + because ComboBoxBase coerces "" to first entry); after parse + when analysisObj aware: + mirror Engine::runAnalysis aware block -> encodeColumnNamesinOptionsPerDataSet + setMultiDataSetQueue + + status["multiDataSetJson"] = {ids, names-by-title} (additive JSON key, no ABI change). + - DataSetProvider::loadDataSet overload with title; SyntaxBridgeDataSet.name finally used + (dataframeimporter sets it; loadDataSets R export validates: named list, unique non-empty names); + provider tracks first-loaded id as primary. createDataSet's empty-reuse quirk handled. + - jaspBase: storeDataSets -> jaspSyntax::loadDataSets; runWrappedAnalysis aware path uses + loadQmlAndParseOptionsStatus + runJaspResults(multiDataSetJson=...) -> engine-parity queued slices + (encoded names + real ids; the user's plain list is NOT handed over anymore on this path). + jaspTools keeps using the direct datasets= handout branch. +2. [impl] SyntaxInterface + jaspSyntax: multi-dataset named loading into bridge workspace (plan items above). +3. [impl] Extract the Engine::runAnalysis aware encode/queue block to shared code (DataBridge method or + similar) so engine + syntaxbridge use ONE implementation (no duplication!). +4. [impl] jaspBase runWrappedAnalysis aware flow per plan (incl. empty-selection pre-check surfacing via + Rcpp::stop channel). +5. [test] Module wrapper test rewrite for the new flow: `dataSetA = "haha"` style options + `depends` in + testMultiDataSet.qml; overview keyed by bridge ids with user names as titles; direct indexing. + (Current path-2 FAIL is the known gap this closes; mark accordingly until done.) +6. [fix] Desktop plot gap: `Analysis::loadPlotlyJsonInResults` decodes plotly JSON against the analysis' + OWN dataset encoder only — aware analyses plotting other datasets stay encoded. Same multi-encoder + treatment as `Engine::sendString` `_analysisDataSetEncoders`. +7. [harden] `_analysisDataSetEncoders` stores raw `ColumnEncoder*` — dataset deleted mid-run dangles + (only the CURRENT pointer is guarded in ~DataSet). Re-resolve by id (or QPointer-style) at send time. +8. [verify] GUI end-to-end after rebuild: dropdown fills tables (aware + twin), guard demo, per-form dataset + switching, saved-file reload of an aware analysis (provenance remap path `restoreProvenanceFromBoundValues` + / `remapSavedProvenance` has no end-to-end test yet). +9. [rebuild] Bundle via `Tools/buildBundle.R` (new jaspBase + module), user reinstalls in dev JASP. +10. [commit] Everything across: jasp-desktop (incl. submodule bump), jaspBase branch multiDataSetAware, + jaspTools branch multiDataSetAware, jaspTestModule. +11. [note] Side effects to document/verify once syntax flow lands: readRequestedDataset() after an aware + parse consumes queue slices; syntax-mode result decoding (columnEncoderContext) per dataset. + +## Done this session (committed unless noted) +- jasp-desktop `515c1f241`: dropdown visibility fixes (syncMultiDataSetAwareness push instead of computed + getter; EntryBase multiDataSetAwareChanged→somethingChanged; VariablesFormBase::setUp bootstrap — static + controls never get formIsKnown!) + render probe (93 QuickTest incl. probe). +- UNCOMMITTED (working tree, all tests green): real-format `_Encoded` regex (C++ mirror later removed per + user), removed `.decodeColNamesForDataSet` callback chain (jasprcpp/interface/rbridge/jaspTools stub), + preloadData forced for aware (EntryBase + AnalysisEntry), engine checks multiAware before preload + + primaryDataSetId fallback (fresh unfiltered analyses sent dataSetId -1 → empty slice queue = the + "empty cells" root cause), encode-per-dataset always collects cols for aware, testall encode→decode→ + encode→decode cycle test on real encoders, rlogic bridge round-trip via jaspSyntax (real encoder output, + index-directly invariant proven through the bridge), jaspBase helpers rewritten to encoded-namespace + direct indexing (dataSetColumnFromEncoded + dataSetEncodedNames snapshot deleted), module rewritten for + direct indexing, module test path-1 faithful (encoded slice names) green. +- Known NOT-a-bug: jaspTools doesn't encode (longstanding, user-confirmed, out of scope). diff --git a/Tools/testMultiDataSet.R b/Tools/testMultiDataSet.R index fc9ed92..deaed33 100644 --- a/Tools/testMultiDataSet.R +++ b/Tools/testMultiDataSet.R @@ -2,7 +2,10 @@ # Full headless test of the multi-dataset aware analysis (Test Multi-DataSet). # # Path 1 (engine-style, jaspTools::runAnalysis + datasets=): options carry encoded column -# names with embedded dataset ids; jaspTools queues the slices exactly like the engine does. +# names and the slices are dataframes with THOSE SAME encoded column names - exactly what the +# engine delivers (both sides encoded by each dataset's ColumnEncoder; see the bridge round-trip +# in jasp-desktop Tests/R/test_multidataset_rlogic.R). jaspTools itself does not encode +# (longstanding), hence the hand-encoded fixtures below. # Path 2 (wrapper, jaspTestModule::multiDataSetFunc(datasets=...)): plain column names plus a # named list of datasets, runWrappedAnalysis -> runJaspResults with a direct R-side handout. # @@ -37,13 +40,24 @@ columnValues <- function(tb, fieldName) { stats::setNames(vapply(d[rowKeys], function(v) as.character(v), character(1), USE.NAMES = FALSE), rowKeys) } -datasets <- list( - "11" = read.csv(file.path(moduledir, "Tools/testdata/DataSetA.csv")), - "12" = read.csv(file.path(moduledir, "Tools/testdata/DataSetB.csv"))) +# Slices as the engine hands them over: the dataframe columns carry the encoded names +# (JASPColumn___Encoded - DataSet::setupEncoderPrefix), NOT the csv headers. +encodeSlice <- function(df, dataSetId) { + names(df) <- sprintf("JASPColumn_%d_%d_Encoded", dataSetId, seq_along(df) - 1L) + df +} + +csvA <- read.csv(file.path(moduledir, "Tools/testdata/DataSetA.csv")) # Score, Age, Group +csvB <- read.csv(file.path(moduledir, "Tools/testdata/DataSetB.csv")) # Score, Age, Condition + +datasets <- list("11" = encodeSlice(csvA, 11), "12" = encodeSlice(csvB, 12)) attr(datasets, "dataSetNames") <- c("11" = "DataSetA", "12" = "DataSetB") +encA <- stats::setNames(sprintf("JASPColumn_11_%d_Encoded", seq_along(csvA) - 1L), names(csvA)) # Score, Age, Group +encB <- stats::setNames(sprintf("JASPColumn_12_%d_Encoded", seq_along(csvB) - 1L), names(csvB)) # Score, Age, Condition + # ------------------------------------------------------------------ Path 1: jaspTools queue ---- -cat("== Path 1: jaspTools::runAnalysis with queued slices (engine semantics) ==\n") +cat("== Path 1: jaspTools::runAnalysis with queued slices (engine semantics, encoded names) ==\n") suppressMessages(library(jaspTools)) suppressMessages(jaspTools::setupJaspTools( pathJaspDesktop = normalizePath(file.path(moduledir, "..", "jasp-desktop")), @@ -51,8 +65,8 @@ suppressMessages(jaspTools::setupJaspTools( jaspTools::setPkgOption("module.dirs", moduledir) jaspTools::setPkgOption("reinstall.modules", FALSE) -opts1 <- list(dependentA = c("JASPColumn_11_0.scale", "JASPColumn_11_2.nominal"), - dependentB = c("JASPColumn_12_0.scale", "JASPColumn_12_1.scale"), +opts1 <- list(dependentA = c(encA[["Score"]], encA[["Group"]]), + dependentB = c(encB[["Score"]], encB[["Age"]]), showMeta = FALSE, guardDemo = TRUE, plotWidth = 400, plotHeight = 300) res1 <- jaspTools::runAnalysis("multiDataSetFunc", options = opts1, datasets = datasets, view = FALSE, quiet = TRUE) r1 <- res1$results @@ -63,50 +77,59 @@ check(length(r1$dataSetOverview$data) == 5L, "dataset overview data has exactly ovId <- columnValues(r1$dataSetOverview, "id") ovTitle <- columnValues(r1$dataSetOverview, "title") +ovCols <- columnValues(r1$dataSetOverview, "cols") check(identical(unname(ovId), c("11", "12")), "overview ids 11,12") check(identical(unname(ovTitle), c("DataSetA", "DataSetB")), "overview titles from dataSetNames attr") +check(all(ovCols == "3"), "both slices reported 3 columns") + +# the overview shows the ENCODED names (headless run: no engine sendString decode in between) +ovNames <- columnValues(r1$dataSetOverview, "columnNames") +check(all(c(paste(encA, collapse = ", "), paste(encB, collapse = ", ")) %in% unname(ovNames)), + "overview column names are the encoded names the analysis received") -# schema order: source=1, variable=2, ... keyed by the row id so assertions do not depend on json order sv <- r1$selectedVariables svSrc <- columnValues(sv, "source") svVar <- columnValues(sv, "variable") svId <- columnValues(sv, "dataSetId") svName <- columnValues(sv, "dataSetName") -svCol <- columnValues(sv, "column") -svType <- columnValues(sv, "type") svN <- columnValues(sv, "n") -expected <- list( - "JASPColumn_11_0.scale" = c(src = "A", id = "11", name = "DataSetA", column = "Score", type = "scale"), - "JASPColumn_11_2.nominal" = c(src = "A", id = "11", name = "DataSetA", column = "Group", type = "nominal"), - "JASPColumn_12_0.scale" = c(src = "B", id = "12", name = "DataSetB", column = "Score", type = "scale"), - "JASPColumn_12_1.scale" = c(src = "B", id = "12", name = "DataSetB", column = "Age", type = "scale")) -check(all(names(expected) %in% svVar), "all four dependents present in the routing table") -for (v in names(expected)) if (v %in% svVar) { +svValues <- columnValues(sv, "values") +check(all(c(encA[["Score"]], encA[["Group"]], encB[["Score"]], encB[["Age"]]) %in% svVar), + "all four encoded dependents present in the routing table") +for (v in c(encA[["Score"]], encA[["Group"]], encB[["Score"]], encB[["Age"]])) if (v %in% svVar) { i <- which(svVar == v)[1] - check(svSrc[i] == expected[[v]]["src"] && svId[i] == expected[[v]]["id"] && svName[i] == expected[[v]]["name"] && - svCol[i] == expected[[v]]["column"] && svType[i] == expected[[v]]["type"], - paste0("routing [form ", expected[[v]]["src"], "] ", v, " -> ", expected[[v]]["name"], "/", expected[[v]]["column"])) + expId <- if (v %in% encA) "11" else "12" + expNm <- if (v %in% encA) "DataSetA" else "DataSetB" + check(svId[i] == expId && svName[i] == expNm && svN[i] == "10" && nzchar(svValues[i]), + paste0("routing ", v, " -> ", expNm, ", 10 rows, values computed (", svValues[i], ")")) } -check(all(svN == "10"), "all four columns read 10 rows") + +# numeric stats must correspond to the right slice: A/Score mean vs csv +iScoreA <- which(svVar == encA[["Score"]])[1] +check(grepl(sprintf("mean %.2f", mean(csvA$Score)), svValues[[iScoreA]], fixed = TRUE), + "form A mean matches DataSetA's Score column") +iScoreB <- which(svVar == encB[["Score"]])[1] +check(grepl(sprintf("mean %.2f", mean(csvB$Score)), svValues[[iScoreB]], fixed = TRUE), + "form B mean matches DataSetB's Score column (no cross-slice mixup)") raw1 <- jsonlite::toJSON(res1, auto_unbox = TRUE) check(grepl("not available in multi-dataset aware analyses", raw1, fixed = TRUE), "guard demo: readDataSet() hard-stopped during the aware run") # ------------------------------------------------------------------ Path 2: wrapper call ------- -# The wrapper path is run in a clean Rscript subprocess (no jaspTools runtime in the way), -# which is exactly the RStudio/syntax-mode scenario the wrapper is made for. -cat("\n== Path 2: wrapper (datasets= named list, plain column names; clean subprocess) ==\n") -path2Script <- file.path(moduledir, "Tools", "testMultiDataSet_wrapper.R") -status2 <- system2("Rscript", c(shQuote(path2Script)), stdout = TRUE, stderr = FALSE) -writeLines(status2) -check(any(grepl("MULTIDATASET-WRAPPER-OK", status2, fixed = TRUE)), "wrapper subprocess succeeded") +# The syntax-mode wrapper flow (loadQmlAndParseOptions) still encodes option values against the +# bridge's single dataset view while the handed-over list keeps plain names: the dataset-selection +# design (TODO-multidataset.md items 1-5: selection options + multi-dataset workspace + shared +# per-dataset encode/queue) replaces this path. Tools/testMultiDataSet_wrapper.R stays ready to +# assert the new semantics once that lands. +cat("\n== Path 2: wrapper (datasets= named list) ==\n") +cat("SKIP : wrapper path awaits the syntax-mode dataset-selection flow (TODO-multidataset.md #1-#5)\n") # ------------------------------------------------------------------ Path 3: legacy still OK ---- cat("\n== Path 3: legacy non-aware run (no datasets) ==\n") res3 <- jaspTools::runAnalysis("multiDataSetFunc", dataset = datasets[["11"]], - options = list(dependentA = c("Score"), dependentB = c("Age"), showMeta = FALSE, - guardDemo = TRUE, plotWidth = 400, plotHeight = 300), + options = list(dependentA = encA[c("Score")], dependentB = encA[c("Age")], + showMeta = FALSE, guardDemo = TRUE, plotWidth = 400, plotHeight = 300), view = FALSE, quiet = TRUE) raw3 <- jsonlite::toJSON(res3, auto_unbox = TRUE) check(identical(res3$status, "complete"), "legacy run completed") diff --git a/Tools/testMultiDataSet_wrapper.R b/Tools/testMultiDataSet_wrapper.R index 6454b51..fb7f4ff 100644 --- a/Tools/testMultiDataSet_wrapper.R +++ b/Tools/testMultiDataSet_wrapper.R @@ -2,6 +2,10 @@ # Wrapper-path check of the multi-dataset analysis: no jaspTools, no JASP - just # jaspTestModule::multiDataSetFunc(datasets = , ...) like an R user would call it. # Run by Tools/testMultiDataSet.R, but standalone-runnable too. +# +# Also reports (via the raw `variable` cells) whether the syntax-mode option parsing encoded +# the values or left them plain - the module indexes whatever arrives directly, so both +# namespaces must line up with the handed-over dataframes for the n/values cells to fill. moduledir <- normalizePath(file.path(dirname(sub("^--file=", "", grep("^--file=", commandArgs(FALSE), value = TRUE)[[1]])), "..")) pkglib <- Sys.getenv("JASP_TEST_PKGLIB", file.path(moduledir, "ModuleBundleBuildDir", "build_dir", "jaspTestModule")) @@ -42,11 +46,13 @@ check(identical(columnValues(j$dataSetOverview, "id"), c("11", "12")), "wrapper check(identical(columnValues(j$dataSetOverview, "title"), c("DataSetA", "DataSetB")), "wrapper overview lists both titles") sv <- j$selectedVariables -svCol <- columnValues(sv, "column") +svVar <- columnValues(sv, "variable") svN <- columnValues(sv, "n") svValues <- columnValues(sv, "values") -check(any(svCol == "Score") && any(svCol == "Condition"), "Score and Condition resolved to original column names") -check(all(svN == "10"), "both columns computed over 10 rows (scan-all found Condition in DataSetB)") +cat("info : raw option values seen by the analysis:", paste(svVar, collapse = ", "), "\n") + +check(all(c("Score", "Age", "Condition") %in% basename(svVar)), "all three dependents reached the table") +check(all(svN == "10"), "all columns computed over 10 rows (direct indexing found each one)") check(any(grepl("mean", svValues, fixed = TRUE)), "numeric stats computed for Score") cat(if (ok) "MULTIDATASET-WRAPPER-OK\n" else "MULTIDATASET-WRAPPER-FAILED\n") From 5b983c9c22e1452ce4193c837d061a2f8b197c2c Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Wed, 7 Oct 2026 23:52:14 +0200 Subject: [PATCH 09/44] demo the syntax-mode dataset selection; twin gets its own form - testMultiDataSet.qml: dataSetSelectionOption 'dataSetA'/'dataSetB' + depends, so a syntax call selects each form's dataset by name (multiDataSetFunc(datasets = list(haha = dfA, godverdomme = dfB), dataSetA = 'haha', dataSetB = 'godverdomme', dependentA = 'Score', ...) routes every column to its own dataset). - non-aware twin now has testMultiDataSetNonAware.qml (no selection options - a non-aware entry neither has nor needs them, and the bridge would demand them). - wrapper test asserts the new semantics end-to-end (bridge ids + user-name titles, encoded slices + encoded options, per-form routing of identically named columns); path 2 re-enabled in the suite (29 checks green). - buildBundle jaspBase check: markers match the current exports (dataSetColumnFromEncoded is gone) and actually print the missing names (bare %in% has no names). - TODO-multidataset.md tracks the remaining work. --- R/multiDataSetFuncWrapper.R | 4 +- R/multiDataSetNonAwareWrapper.R | 4 +- TODO-multidataset.md | 126 +++++++++++--------------- Tools/buildBundle.R | 8 +- Tools/testMultiDataSet.R | 16 ++-- Tools/testMultiDataSet_wrapper.R | 75 ++++++++++----- inst/Description.qml | 10 +- inst/qml/testMultiDataSet.qml | 6 ++ inst/qml/testMultiDataSetNonAware.qml | 63 +++++++++++++ 9 files changed, 201 insertions(+), 111 deletions(-) create mode 100644 inst/qml/testMultiDataSetNonAware.qml diff --git a/R/multiDataSetFuncWrapper.R b/R/multiDataSetFuncWrapper.R index 790c0ab..69bf3ac 100644 --- a/R/multiDataSetFuncWrapper.R +++ b/R/multiDataSetFuncWrapper.R @@ -22,6 +22,8 @@ multiDataSetFunc <- function( datasets = NULL, version = "1", + dataSetA = list(types = "unknown", value = ""), + dataSetB = list(types = "unknown", value = ""), dependentA = list(types = list(), value = list()), dependentB = list(types = list(), value = list()), guardDemo = FALSE, @@ -45,7 +47,7 @@ multiDataSetFunc <- function( data <- if (!is.null(datasets) && length(datasets)) datasets[[1]] else NULL - optionsWithFormula <- c("dependentA", "dependentB") + optionsWithFormula <- c("dataSetA", "dataSetB", "dependentA", "dependentB") for (name in optionsWithFormula) { if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } diff --git a/R/multiDataSetNonAwareWrapper.R b/R/multiDataSetNonAwareWrapper.R index 9aba292..fe9000d 100644 --- a/R/multiDataSetNonAwareWrapper.R +++ b/R/multiDataSetNonAwareWrapper.R @@ -22,6 +22,8 @@ multiDataSetNonAware <- function( data = NULL, version = "1", + dataSetA = list(types = "unknown", value = ""), + dataSetB = list(types = "unknown", value = ""), dependentA = list(types = list(), value = list()), dependentB = list(types = list(), value = list()), guardDemo = FALSE, @@ -43,7 +45,7 @@ multiDataSetNonAware <- function( jaspBase::storeDataSet(data) } - optionsWithFormula <- c("dependentA", "dependentB") + optionsWithFormula <- c("dataSetA", "dataSetB", "dependentA", "dependentB") for (name in optionsWithFormula) { if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } diff --git a/TODO-multidataset.md b/TODO-multidataset.md index 3f7a3e6..248430c 100644 --- a/TODO-multidataset.md +++ b/TODO-multidataset.md @@ -1,76 +1,60 @@ -# Multi-dataset aware feature — open work (2026-10-07) +# Multi-dataset aware feature — open work (2026-10-07, post syntax-mode-selection) -## Decided design (ground truth from user, this session) -- Options and dataset slices live in the SAME encoded namespace everywhere (`JASPColumn___Encoded`); - R indexes `datasets[[id]][[encodedValue]]` directly; results/plots are decoded on the way out (engine sendString). -- `preloadData` is forced true for aware analyses (done at `Modules::AnalysisEntry::preloadData()` + EntryBase), - and the engine checks `_analysisMultiDataSet` *before* the preload flag. -- Syntax mode must encode like the engine (verified: it does for single-dataset). No scans, no decode-hacks. -- Syntax-mode multi-dataset (user's design, TO BUILD): - `datasets = list(haha = df1, godverdomme = df2)` loads each df into the bridge **Workspace/DataSet** - (title = the user's name; names are user-convenience only). **The VariablesForms' dataset selections are - specific options** that must not be empty (check!); the user fills e.g. `"haha"` there as soon as the data - is loaded; selecting it switches the form's dataset context, QMLComponents then validate/encode the - column-name options against that dataset (they know their sources, stamp `.meta` dataSetId), and the - encoder (now existing via DataSet) encodes per datasetId. This cascades through the options. +## Decided design (ground truth from user) +- Options and dataset slices share the ENCODED namespace (`JASPColumn___Encoded`); + R indexes `datasets[[id]][[encodedValue]]` directly; results/plots are decoded on the way out + (engine sendString loops every encoder the analysis references). +- `preloadData` forced true for aware analyses (Modules::AnalysisEntry + EntryBase getters); + the engine checks multiAware BEFORE the preload flag. +- Syntax mode: `datasets = list(name = df, ...)` loads each df into the bridge Workspace/DataSet + (title = list name, real ids + encoders). Each VariablesForm has a dataset-selection OPTION + (dataSetSelectionOption names it; NO_DESKTOP_MODE-gated bound DropDown); the user fills the + dataset NAME there; JASPControl::_setInitialized re-applies the owning form's selection before + every child binds (order-proof), QML validates + stamps .meta, the bridge then encodes + per-dataset and queues slices via the SHARED DataBridge::prepareMultiDataSetRun (same code as + Engine::runAnalysis). Empty/missing selection options fail with a clear R error (bridge + pre-check; ComboBoxBase coerces "" to first entry). No filters in syntax mode (pre-filtered + data); default filter queued per slice. Names are user-convenience only (DataSet title). ## TODO -1. [done: subagent design] Option-backed VariablesForm dataset selection for syntax mode. Agreed plan: - - CommonData/workspace: `dataSetByTitle(QString)` exact-match lookup (mirror dataSetByName). - - VariablesFormBase: `dataSetSelectionOption` Q_PROPERTY (option name for this form's dataset selection); - setter also self-declares awareness (`analysisObj()->setMultiDataSetAware(true)` — bridge dummy analysis - otherwise never gets it); `dataSetTitleValues()` (workspace titles); `selectDataSetByName(name)`: - empty/unknown -> addControlError, match -> analysis->setFilterId(ds->defaultFilter()->id()) + - Workspace::setShownDataSet(ds) (filterId semantics = default, syntax users pre-filter). - - VariablesForm.qml: a second, BOUND DropDown (name: dataSetSelectionOption, values: titles) gated by - NO_DESKTOP_MODE (typeof-guard) -> desktop stays 100% inert; wrapper generation picks the option up. - AssignedVariablesList gets `depends: ""` so bindTo applies the selection FIRST - (setInitialized defers; meta then stamps the right dataSetId via createMeta). - - AnalysisForm: `dataSetSelectionOptionNames()` (findChildren). - - syntaxbridge loadQmlAndParseOptionsStatus: PRE-CHECK raw options JSON for those names (empty check, - because ComboBoxBase coerces "" to first entry); after parse + when analysisObj aware: - mirror Engine::runAnalysis aware block -> encodeColumnNamesinOptionsPerDataSet + setMultiDataSetQueue - + status["multiDataSetJson"] = {ids, names-by-title} (additive JSON key, no ABI change). - - DataSetProvider::loadDataSet overload with title; SyntaxBridgeDataSet.name finally used - (dataframeimporter sets it; loadDataSets R export validates: named list, unique non-empty names); - provider tracks first-loaded id as primary. createDataSet's empty-reuse quirk handled. - - jaspBase: storeDataSets -> jaspSyntax::loadDataSets; runWrappedAnalysis aware path uses - loadQmlAndParseOptionsStatus + runJaspResults(multiDataSetJson=...) -> engine-parity queued slices - (encoded names + real ids; the user's plain list is NOT handed over anymore on this path). - jaspTools keeps using the direct datasets= handout branch. -2. [impl] SyntaxInterface + jaspSyntax: multi-dataset named loading into bridge workspace (plan items above). -3. [impl] Extract the Engine::runAnalysis aware encode/queue block to shared code (DataBridge method or - similar) so engine + syntaxbridge use ONE implementation (no duplication!). -4. [impl] jaspBase runWrappedAnalysis aware flow per plan (incl. empty-selection pre-check surfacing via - Rcpp::stop channel). -5. [test] Module wrapper test rewrite for the new flow: `dataSetA = "haha"` style options + `depends` in - testMultiDataSet.qml; overview keyed by bridge ids with user names as titles; direct indexing. - (Current path-2 FAIL is the known gap this closes; mark accordingly until done.) -6. [fix] Desktop plot gap: `Analysis::loadPlotlyJsonInResults` decodes plotly JSON against the analysis' - OWN dataset encoder only — aware analyses plotting other datasets stay encoded. Same multi-encoder - treatment as `Engine::sendString` `_analysisDataSetEncoders`. -7. [harden] `_analysisDataSetEncoders` stores raw `ColumnEncoder*` — dataset deleted mid-run dangles - (only the CURRENT pointer is guarded in ~DataSet). Re-resolve by id (or QPointer-style) at send time. -8. [verify] GUI end-to-end after rebuild: dropdown fills tables (aware + twin), guard demo, per-form dataset - switching, saved-file reload of an aware analysis (provenance remap path `restoreProvenanceFromBoundValues` - / `remapSavedProvenance` has no end-to-end test yet). -9. [rebuild] Bundle via `Tools/buildBundle.R` (new jaspBase + module), user reinstalls in dev JASP. -10. [commit] Everything across: jasp-desktop (incl. submodule bump), jaspBase branch multiDataSetAware, - jaspTools branch multiDataSetAware, jaspTestModule. -11. [note] Side effects to document/verify once syntax flow lands: readRequestedDataset() after an aware - parse consumes queue slices; syntax-mode result decoding (columnEncoderContext) per dataset. +1. [fix] Desktop plot gap: `Analysis::loadPlotlyJsonInResults` decodes plotly JSON against the + analysis' OWN dataset encoder only — aware analyses plotting other datasets stay encoded. + Same multi-encoder treatment as `Engine::sendString` `_analysisDataSetEncoders`. +2. [harden] `_analysisDataSetEncoders` stores raw `ColumnEncoder*` — dataset deleted mid-run + dangles (only the CURRENT pointer is guarded in ~DataSet). Re-resolve by id at send time. +3. [verify] GUI end-to-end after bundle rebuild: dropdown fills tables (aware + twin), guard + demo, per-form dataset switching in the GUI, and saved-file reload of an aware analysis + (`restoreProvenanceFromBoundValues` / `remapSavedProvenance` still lack an end-to-end test). +4. [rebuild] Bundle via `Tools/buildBundle.R` (new jaspBase + module + qmls); user reinstalls. +5. [note] Syntax-mode side effects to watch: readRequestedDataset() after an aware parse consumes + queue slices; jaspSyntax result decoding (columnEncoderContext) is per current dataset — + multi-dataset result decoding in pure syntax output may need the same multi-encoder care. +6. [ops] jaspSyntax installs MUST pass `JASPSYNTAX_LIB_PATH=` + (configure otherwise downloads release binaries needing mysql/pq/icu74, absent here); + after each SyntaxInterface rebuild copy the .so to BOTH `jaspSyntax/src/` and + `.toolLib/jaspSyntax/libs/`. -## Done this session (committed unless noted) -- jasp-desktop `515c1f241`: dropdown visibility fixes (syncMultiDataSetAwareness push instead of computed - getter; EntryBase multiDataSetAwareChanged→somethingChanged; VariablesFormBase::setUp bootstrap — static - controls never get formIsKnown!) + render probe (93 QuickTest incl. probe). -- UNCOMMITTED (working tree, all tests green): real-format `_Encoded` regex (C++ mirror later removed per - user), removed `.decodeColNamesForDataSet` callback chain (jasprcpp/interface/rbridge/jaspTools stub), - preloadData forced for aware (EntryBase + AnalysisEntry), engine checks multiAware before preload + - primaryDataSetId fallback (fresh unfiltered analyses sent dataSetId -1 → empty slice queue = the - "empty cells" root cause), encode-per-dataset always collects cols for aware, testall encode→decode→ - encode→decode cycle test on real encoders, rlogic bridge round-trip via jaspSyntax (real encoder output, - index-directly invariant proven through the bridge), jaspBase helpers rewritten to encoded-namespace - direct indexing (dataSetColumnFromEncoded + dataSetEncodedNames snapshot deleted), module rewritten for - direct indexing, module test path-1 faithful (encoded slice names) green. +## Done (this session) +- jasp-desktop `515c1f241` (dropdown visibility: syncMultiDataSetAwareness push, EntryBase notify, + VariablesFormBase::setUp bootstrap — static controls never get formIsKnown — + render probe). +- jasp-desktop `a8cd23e5d` dead dataset-aware decode plumbing removed (C++ dataSetIdFromEncoded + mirror, .decodeColNamesForDataSet callback chain), encode->decode->encode cycle test on real + encoders (pins the `_Encoded` format). +- jasp-desktop `b87a8bbbe` empty-cells fix (primaryDataSetId for unfiltered analyses; aware + checked before preload; cols always collected; rlogic bridge round-trip through jaspSyntax + proving the index-directly invariant against real encoder output) + submodule bumps. +- jaspBase `edc73fe` helpers operate in the encoded namespace (dataSetColumnFromEncoded + + dataSetEncodedNames snapshot deleted; real `_Encoded` parsing; direct indexing; faithful fixtures). +- jaspTools `0d621f3` dead stub + maps removed. +- jaspTestModule `6f6cec1` demo indexes directly; path-1 fixtures carry real encoded names. +- UNCOMMITTED (all suites green: JASPTest 109, QuickTest 93, rlogic incl bridge, jaspBase 46, + module 29 incl wrapper): syntax-mode dataset selection across jasp-desktop (workspace + dataSetByTitle, DataSetProvider titled loads + firstLoadedDataSetId + named loads skip reset, + VariablesFormBase dataSetSelectionOption/dataSetTitleValues/selectDataSetByName/ + applyDataSetSelection, JASPControl _setInitialized apply-hook, AnalysisForm + dataSetSelectionOptionNames, syntaxbridge pre-check + aware prepare branch + shared + DataBridge::prepareMultiDataSetRun extraction), jaspSyntax (loadDataSets, + loadQmlAndParseOptionsStatus, importer name), jaspBase (storeDataSets -> loadDataSets, + runWrappedAnalysis aware flow with string parts), jaspTestModule (selection options + depends + in testMultiDataSet.qml, twin's own qml, wrapper test rewritten, buildBundle check fixed). - Known NOT-a-bug: jaspTools doesn't encode (longstanding, user-confirmed, out of scope). diff --git a/Tools/buildBundle.R b/Tools/buildBundle.R index 9e07d2d..e55f9b2 100644 --- a/Tools/buildBundle.R +++ b/Tools/buildBundle.R @@ -196,10 +196,12 @@ archive::archive_extract(file.path(checkdir, jaspBaseHash), dir = pkgdir) suppressPackageStartupMessages(library(jaspBase, lib.loc = file.path(checkdir, "lib"))) if (localJaspBase) { - markers <- c("dataSetColumnFromEncoded", "dataSetIdFromEncoded", "getDataSetColumn", "storeDataSets") %in% getNamespaceExports("jaspBase") - runBody <- any(grepl("multiDataSet|dataSetEncodedNames", deparse(body(jaspBase::runJaspResults)))) + expected <- c("dataSetIdFromEncoded", "dataSetNameFromEncoded", "getDataSetFor", + "getDataSetColumn", "storeDataSets") + markers <- stats::setNames(expected %in% getNamespaceExports("jaspBase"), expected) + runBody <- any(grepl("multiDataSet", deparse(body(jaspBase::runJaspResults)))) if (!all(markers) || !runBody) - stop("bundled jaspBase is MISSING the local multiDataSetAware code (exports: ", + stop("bundled jaspBase is MISSING the local multiDataSetAware code (missing exports: ", paste(names(markers)[!markers], collapse = ", "), if (runBody) "" else " + runJaspResults body", ")") message("VERIFIED: bundled jaspBase loads and carries the local multiDataSetAware code.") } else { diff --git a/Tools/testMultiDataSet.R b/Tools/testMultiDataSet.R index deaed33..b71b020 100644 --- a/Tools/testMultiDataSet.R +++ b/Tools/testMultiDataSet.R @@ -117,13 +117,15 @@ check(grepl("not available in multi-dataset aware analyses", raw1, fixed = TRUE) "guard demo: readDataSet() hard-stopped during the aware run") # ------------------------------------------------------------------ Path 2: wrapper call ------- -# The syntax-mode wrapper flow (loadQmlAndParseOptions) still encodes option values against the -# bridge's single dataset view while the handed-over list keeps plain names: the dataset-selection -# design (TODO-multidataset.md items 1-5: selection options + multi-dataset workspace + shared -# per-dataset encode/queue) replaces this path. Tools/testMultiDataSet_wrapper.R stays ready to -# assert the new semantics once that lands. -cat("\n== Path 2: wrapper (datasets= named list) ==\n") -cat("SKIP : wrapper path awaits the syntax-mode dataset-selection flow (TODO-multidataset.md #1-#5)\n") +# The wrapper path is run in a clean Rscript subprocess (no jaspTools runtime in the way): +# datasets= loads into the bridge workspace, the dataSetA/dataSetB options select each form's +# dataset, the bridge encodes per dataset and queues slices - engine parity via the shared +# DataBridge preparation. Exactly the RStudio/syntax-mode scenario the wrapper is made for. +cat("\n== Path 2: wrapper (datasets= named list + dataset selection options) ==\n") +path2Script <- file.path(moduledir, "Tools", "testMultiDataSet_wrapper.R") +status2 <- system2("Rscript", c(shQuote(path2Script)), stdout = TRUE, stderr = FALSE) +writeLines(status2) +check(any(grepl("MULTIDATASET-WRAPPER-OK", status2, fixed = TRUE)), "wrapper subprocess succeeded") # ------------------------------------------------------------------ Path 3: legacy still OK ---- cat("\n== Path 3: legacy non-aware run (no datasets) ==\n") diff --git a/Tools/testMultiDataSet_wrapper.R b/Tools/testMultiDataSet_wrapper.R index fb7f4ff..e730abe 100644 --- a/Tools/testMultiDataSet_wrapper.R +++ b/Tools/testMultiDataSet_wrapper.R @@ -1,11 +1,13 @@ #!/usr/bin/env Rscript # Wrapper-path check of the multi-dataset analysis: no jaspTools, no JASP - just -# jaspTestModule::multiDataSetFunc(datasets = , ...) like an R user would call it. +# jaspTestModule::multiDataSetFunc(datasets = , ...) like an R user would call it, +# with the dataset-selection options naming which dataset each variables-form uses. # Run by Tools/testMultiDataSet.R, but standalone-runnable too. # -# Also reports (via the raw `variable` cells) whether the syntax-mode option parsing encoded -# the values or left them plain - the module indexes whatever arrives directly, so both -# namespaces must line up with the handed-over dataframes for the n/values cells to fill. +# Flow under test: loadDataSets -> workspace (titles, ids, encoders) -> QML binds the selection +# options first (depends), switching each form; column options then encode per dataset and the +# bridge queues one slice per dataset; runJaspResults hands the analysis encoded options + +# encoded slices keyed by (bridge) dataset id, with the user names as titles. moduledir <- normalizePath(file.path(dirname(sub("^--file=", "", grep("^--file=", commandArgs(FALSE), value = TRUE)[[1]])), "..")) pkglib <- Sys.getenv("JASP_TEST_PKGLIB", file.path(moduledir, "ModuleBundleBuildDir", "build_dir", "jaspTestModule")) @@ -16,23 +18,23 @@ Sys.setenv(QT_QPA_PLATFORM = "offscreen") suppressMessages(library(jaspTestModule)) -datasets <- list( - "11" = read.csv(file.path(moduledir, "Tools/testdata/DataSetA.csv")), - "12" = read.csv(file.path(moduledir, "Tools/testdata/DataSetB.csv"))) -attr(datasets, "dataSetNames") <- c("11" = "DataSetA", "12" = "DataSetB") +csvA <- read.csv(file.path(moduledir, "Tools/testdata/DataSetA.csv")) # Score, Age, Group +csvB <- read.csv(file.path(moduledir, "Tools/testdata/DataSetB.csv")) # Score, Age, Condition -res <- jaspTestModule::multiDataSetFunc(datasets = datasets, - dependentA = "Score", # in both datasets -> routes to the primary - dependentB = c("Age", "Condition")) # Condition only exists in DataSetB +res <- jaspTestModule::multiDataSetFunc( + datasets = list(haha = csvA, godverdomme = csvB), + dataSetA = "haha", + dataSetB = "godverdomme", + dependentA = "Score", # Score exists in both: form A picked haha + dependentB = c("Age", "Condition")) # Condition only in godverdomme raw <- as.character(res$.__enclos_env__$private$getResults()) j <- jsonlite::fromJSON(raw, simplifyVector = FALSE)$results -# jaspTable json is transposed: data[[j]] = column j (schema order), cells keyed by row id columnValues <- function(tb, fieldName) { idx <- which(vapply(tb$schema$fields, function(f) identical(f$name, fieldName), logical(1)))[1] d <- tb$data[[idx]] rowKeys <- names(d)[vapply(d, length, integer(1)) > 0L] - vapply(d[rowKeys], function(v) as.character(v), character(1), USE.NAMES = FALSE) + stats::setNames(vapply(d[rowKeys], function(v) paste(as.character(v), collapse = ", "), character(1), USE.NAMES = FALSE), rowKeys) } ok <- TRUE @@ -41,19 +43,44 @@ check <- function(cond, label) { if (!isTRUE(cond)) ok <<- FALSE } -check(grepl("Aware run", raw, fixed = TRUE), "analysis ran in aware mode (datasets= arrived)") -check(identical(columnValues(j$dataSetOverview, "id"), c("11", "12")), "wrapper overview lists both dataset ids") -check(identical(columnValues(j$dataSetOverview, "title"), c("DataSetA", "DataSetB")), "wrapper overview lists both titles") +check(grepl("Aware run", raw, fixed = TRUE), "analysis ran in aware mode") -sv <- j$selectedVariables -svVar <- columnValues(sv, "variable") -svN <- columnValues(sv, "n") -svValues <- columnValues(sv, "values") -cat("info : raw option values seen by the analysis:", paste(svVar, collapse = ", "), "\n") +ovTitle <- unname(columnValues(j$dataSetOverview, "title")) +ovId <- unname(columnValues(j$dataSetOverview, "id")) +ovNames <- unname(columnValues(j$dataSetOverview, "columnNames")) +check(setequal(ovTitle, c("haha", "godverdomme")), "both datasets handed over, user names as titles") +check(length(ovId) == 2L && all(grepl("^[0-9]+$", ovId)), "datasets keyed by (bridge) numeric dataset ids") +check(all(grepl("JASPColumn_", ovNames)), "slices arrive with encoded column names") -check(all(c("Score", "Age", "Condition") %in% basename(svVar)), "all three dependents reached the table") -check(all(svN == "10"), "all columns computed over 10 rows (direct indexing found each one)") -check(any(grepl("mean", svValues, fixed = TRUE)), "numeric stats computed for Score") +# the bridge dataset ids behind each title, so routing can be asserted exactly +idByTitle <- stats::setNames(ovId, ovTitle) + +sv <- j$selectedVariables +svSrc <- columnValues(sv, "source") +svVar <- columnValues(sv, "variable") +svId <- columnValues(sv, "dataSetId") +svN <- columnValues(sv, "n") +svValues <- columnValues(sv, "values") + +check(all(grepl("^JASPColumn_[0-9]+_[0-9]+_Encoded$", svVar)), + "option values arrive in the encoded namespace (not the plain user names)") + +rowFor <- function(src, dsTitle) { + which(svSrc == src & svId == idByTitle[[dsTitle]]) +} + +iA <- rowFor("A", "haha"); iB1 <- rowFor("B", "godverdomme") +check(length(iA) == 1L && length(iB1) == 2L, "three rows routed: A->haha once, B->godverdomme twice") +check(all(svN == "10"), "every column computed over 10 rows (encoded names indexed directly)") +check(grepl(sprintf("mean %.2f", mean(csvA$Score)), svValues[[iA]], fixed = TRUE), + "form A Score = haha's Score (per-form dataset selection)") +# integer columns arrive from the syntax bridge as ordinal -> the demo shows first levels, not mean; +# the level list itself proves it is GODVERDOMME'S Age (31/45/28... would be haha's) +ageLevelsB <- paste(utils::head(unique(as.character(csvB$Age)), 5), collapse = ", ") +check(any(grepl(ageLevelsB, svValues[iB1], fixed = TRUE)), + "form B Age = godverdomme's Age (identically named column, different dataset)") +check(any(grepl("X, Y, Z", svValues[iB1], fixed = TRUE)), + "form B Condition found in godverdomme (only exists there)") cat(if (ok) "MULTIDATASET-WRAPPER-OK\n" else "MULTIDATASET-WRAPPER-FAILED\n") quit(status = if (ok) 0L else 1L) diff --git a/inst/Description.qml b/inst/Description.qml index 9e031de..eebcdec 100644 --- a/inst/Description.qml +++ b/inst/Description.qml @@ -95,13 +95,15 @@ Description hasWrapper: true } - // Same form, but NOT aware and NOT wrapped: data must arrive the old way (dataset/readDataSet) - // and the dataset selection must stay inert. Own func name because with hasWrappers the engine - // appends "Internal" to every entry's func (and the wrapper name may not *be* an Internal). + // Same forms, but NOT aware: data must arrive the old way (dataset/readDataSet) and the dataset + // selection must stay inert. Own func name because with hasWrappers the engine appends + // "Internal" to every entry's func (and the wrapper name may not *be* an Internal). Own qml too: + // the aware form's datasetSelectionOptions would (rightly) demand selections in syntax mode, + // which a non-aware analysis neither has nor needs. Analysis { title: "Test Multi-DataSet (non-aware counterpart)" - qml: "testMultiDataSet.qml" + qml: "testMultiDataSetNonAware.qml" func: "multiDataSetNonAware" requiresData: true preloadData: true diff --git a/inst/qml/testMultiDataSet.qml b/inst/qml/testMultiDataSet.qml index 01cd24a..6a52ec0 100644 --- a/inst/qml/testMultiDataSet.qml +++ b/inst/qml/testMultiDataSet.qml @@ -32,12 +32,16 @@ Form VariablesForm { dataSetSelection: true + //syntax mode: an option carrying the dataset name to select for this form + //(jaspSyntax gates the bound dropdown on it; desktop never sees it) + dataSetSelectionOption: "dataSetA" AvailableVariablesList { name: "allVariablesA" } AssignedVariablesList { name: "dependentA" title: qsTr("Variables A") + depends: "dataSetA" //bind the dataset selection first, then the columns } } } @@ -48,12 +52,14 @@ Form VariablesForm { dataSetSelection: true + dataSetSelectionOption: "dataSetB" AvailableVariablesList { name: "allVariablesB" } AssignedVariablesList { name: "dependentB" title: qsTr("Variables B") + depends: "dataSetB" } } } diff --git a/inst/qml/testMultiDataSetNonAware.qml b/inst/qml/testMultiDataSetNonAware.qml new file mode 100644 index 0000000..2f9a9b6 --- /dev/null +++ b/inst/qml/testMultiDataSetNonAware.qml @@ -0,0 +1,63 @@ +import QtQuick +import JASP.Controls +import "./common" + +// Twin of testMultiDataSet.qml for the NON-aware entry (multiDataSetNonAware): same forms and +// checkboxes, but no dataSetSelectionOption - a non-aware analysis has exactly one (shown) +// dataset, in the GUI as well as in syntax mode. The dataSetSelection: true stays to demo that +// the dropdown remains inert when the analysis is not multiDataSetAware. +Form +{ + columns: 1 + + ExplanationText + { + text: + "Non-aware twin: identical form, but the analysis entry lacks multiDataSetAware, so " + + "the Data selections stay inert and data comes in through the classic dataset/readDataSet path." + } + + Section { + title: qsTr("Form A - variables from one dataset") + + VariablesForm + { + dataSetSelection: true + + AvailableVariablesList { name: "allVariablesA" } + AssignedVariablesList + { + name: "dependentA" + title: qsTr("Variables A") + } + } + } + + Section { + title: qsTr("Form B - variables from another dataset") + + VariablesForm + { + dataSetSelection: true + + AvailableVariablesList { name: "allVariablesB" } + AssignedVariablesList + { + name: "dependentB" + title: qsTr("Variables B") + } + } + } + + CheckBox + { + name: "showMeta" + label: qsTr("Show options .meta (dataSetId/filterId provenance)") + } + + CheckBox + { + name: "guardDemo" + label: qsTr("Call readDataSet() anyway (should not error: non-aware run)") + } +} From 2bc37b87eef9f9ee04993518bfde8ce426bcdc0b Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Thu, 8 Oct 2026 00:00:50 +0200 Subject: [PATCH 10/44] buildBundle QML gate: parse forms in-process with a dummy dataset selection The aware form's dataSetSelectionOptions now (rightly) demand a loaded dataset to name, which broke the smoke check. The gate loads a one-column dummy via loadDataSets and parses with isolated = FALSE (the subprocess would not see it), pointing every detected dataSetSelectionOption at it; the non-aware twin is checked without selections. --- Tools/buildBundle.R | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/Tools/buildBundle.R b/Tools/buildBundle.R index e55f9b2..0deda97 100644 --- a/Tools/buildBundle.R +++ b/Tools/buildBundle.R @@ -163,10 +163,28 @@ if (requireNamespace("jaspSyntax", quietly = TRUE)) { suppressPackageStartupMessages(library(jaspSyntax)) desc <- readModuleDescription(moduledir) stopifnot(!is.null(desc)) - for (analysisFunc in c("multiDataSetFunc")) { - opts <- readDefaultAnalysisOptions(moduledir, analysisFunc) - stopifnot(is.list(opts), !is.null(attr(opts, "qmlFile"))) - message(" ", analysisFunc, " -> ", basename(attr(opts, "qmlFile")), " loads OK") + for (check in list( + list(func = "multiDataSetFunc", qml = "testMultiDataSet.qml", aware = TRUE), + list(func = "multiDataSetNonAware", qml = "testMultiDataSetNonAware.qml", aware = FALSE))) { + qmlFile <- file.path(moduledir, "inst", "qml", check$qml) + stopifnot(file.exists(qmlFile)) + parseOptions <- NULL + if (check$aware) { + # The aware forms declare dataSetSelectionOptions; the bridge (rightly) demands every one + # of them names a loaded dataset - give the smoke check one dummy to point them all at. + jaspSyntax::loadDataSets(list(bundleCheck = data.frame(ok = 1))) + hits <- Filter(function(h) length(h) == 2L, + regmatches(readLines(qmlFile, warn = FALSE), + regexec('dataSetSelectionOption[[:space:]]*:[[:space:]]*"([^"]+)"', + readLines(qmlFile, warn = FALSE)))) + sels <- vapply(hits, function(h) h[[2]], character(1)) + stopifnot(length(sels) > 0L) + parseOptions <- stats::setNames(as.list(rep("bundleCheck", length(sels))), sels) + } + parsed <- jaspSyntax::parseQmlOptions(qmlFile, options = parseOptions, moduleName = "jaspTestModule", + analysisName = check$func, isolated = FALSE) + stopifnot(!is.null(parsed)) + message(" ", check$func, " -> ", check$qml, " loads OK") } } else { message("NOTE: jaspSyntax not installed (see github.com/jasp-stats/jaspSyntax) - QML not checked.") From 08cc56ad6eae7c13d3a4e35b4d641731028cb38c Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Thu, 8 Oct 2026 00:09:40 +0200 Subject: [PATCH 11/44] TODO: mark plot decode + encoder-id hardening done (jasp-desktop d8ed73558, 0bf83f920) --- TODO-multidataset.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/TODO-multidataset.md b/TODO-multidataset.md index 248430c..6da8f14 100644 --- a/TODO-multidataset.md +++ b/TODO-multidataset.md @@ -17,10 +17,10 @@ data); default filter queued per slice. Names are user-convenience only (DataSet title). ## TODO -1. [fix] Desktop plot gap: `Analysis::loadPlotlyJsonInResults` decodes plotly JSON against the +1. [DONE jasp-desktop d8ed73558] Desktop plot gap: `Analysis::loadPlotlyJsonInResults` decodes plotly JSON against the analysis' OWN dataset encoder only — aware analyses plotting other datasets stay encoded. Same multi-encoder treatment as `Engine::sendString` `_analysisDataSetEncoders`. -2. [harden] `_analysisDataSetEncoders` stores raw `ColumnEncoder*` — dataset deleted mid-run +2. [DONE jasp-desktop 0bf83f920] `_analysisDataSetEncoders` stored raw `ColumnEncoder*` — dataset deleted mid-run dangles (only the CURRENT pointer is guarded in ~DataSet). Re-resolve by id at send time. 3. [verify] GUI end-to-end after bundle rebuild: dropdown fills tables (aware + twin), guard demo, per-form dataset switching in the GUI, and saved-file reload of an aware analysis @@ -47,8 +47,8 @@ dataSetEncodedNames snapshot deleted; real `_Encoded` parsing; direct indexing; faithful fixtures). - jaspTools `0d621f3` dead stub + maps removed. - jaspTestModule `6f6cec1` demo indexes directly; path-1 fixtures carry real encoded names. -- UNCOMMITTED (all suites green: JASPTest 109, QuickTest 93, rlogic incl bridge, jaspBase 46, - module 29 incl wrapper): syntax-mode dataset selection across jasp-desktop (workspace +- COMMITTED `2bc37b8`+jasp-desktop `3e09b7b31` etc. (all suites green: JASPTest 109, QuickTest 93, rlogic incl bridge, jaspBase 46, +X dataset selection across jasp-desktop (workspace dataSetByTitle, DataSetProvider titled loads + firstLoadedDataSetId + named loads skip reset, VariablesFormBase dataSetSelectionOption/dataSetTitleValues/selectDataSetByName/ applyDataSetSelection, JASPControl _setInitialized apply-hook, AnalysisForm From 631a92be5f32e4c97939a7b26f521ffab28485bb Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Thu, 8 Oct 2026 01:58:56 +0200 Subject: [PATCH 12/44] buildBundle installs the result into the local dev JASP automatically No more manual 'Add module -> From file' per iteration: the build ends with Tools/installModuleLocal.sh (JASP's own jaspModuleBundleManager install + manifest version normalisation + developerMode/modulesRemembered prefs). --no-install-local / SKIP_MODULE_LOCAL_INSTALL=1 to opt out; failure is a warning (bundle itself is fine, usually JASP still running). TODO records the parseModuleInfo-vs-array-version bug worth reporting upstream. --- TODO-multidataset.md | 13 +++- Tools/buildBundle.R | 18 ++++- Tools/installModuleLocal.sh | 144 ++++++++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+), 3 deletions(-) create mode 100755 Tools/installModuleLocal.sh diff --git a/TODO-multidataset.md b/TODO-multidataset.md index 6da8f14..8940977 100644 --- a/TODO-multidataset.md +++ b/TODO-multidataset.md @@ -25,8 +25,17 @@ 3. [verify] GUI end-to-end after bundle rebuild: dropdown fills tables (aware + twin), guard demo, per-form dataset switching in the GUI, and saved-file reload of an aware analysis (`restoreProvenanceFromBoundValues` / `remapSavedProvenance` still lack an end-to-end test). -4. [rebuild] Bundle via `Tools/buildBundle.R` (new jaspBase + module + qmls); user reinstalls. -5. [note] Syntax-mode side effects to watch: readRequestedDataset() after an aware parse consumes +4. [DONE] Bundle + local install: `Tools/buildBundle.R` now ends by installing the bundle into + the local dev JASP automatically (Tools/installModuleLocal.sh: jaspModuleBundleManager + install + manifest version normalization + developerMode/modulesRemembered prefs). + Skip with `--no-install-local` / SKIP_MODULE_LOCAL_INSTALL=1; JASP must not be running. +5. [upstream] JASP bug candidate: bundle tooling writes manifest `"version":[[0,96,2]]` but + `InstalledModules::parseModuleInfo` (installedmodules.cpp:28) throws on arrays via + `version.isMember("Version")` → "Could not parse module manifest" → module silently skipped + at every restart after a perfectly fine GUI install. Fix belongs in jasp-desktop + (accept arrays) and/or jaspModuleBundleManager (write strings). installModuleLocal.sh + works around it by rewriting the manifest field. +6. [note] Syntax-mode side effects to watch: readRequestedDataset() after an aware parse consumes queue slices; jaspSyntax result decoding (columnEncoderContext) is per current dataset — multi-dataset result decoding in pure syntax output may need the same multi-encoder care. 6. [ops] jaspSyntax installs MUST pass `JASPSYNTAX_LIB_PATH=` diff --git a/Tools/buildBundle.R b/Tools/buildBundle.R index 0deda97..dae512a 100644 --- a/Tools/buildBundle.R +++ b/Tools/buildBundle.R @@ -227,4 +227,20 @@ if (localJaspBase) { } message("Bundle: ", bundle, " (size: ", format(file.size(bundle) / 2^20, digits = 1), " MiB)") -message("Install in JASP: + menu -> Add module -> From file, pick that .JASPModule.") + +# --------------------------------------------------------------------------------------------- +# Install into the local developer JASP so the module is simply THERE after every build +# (same result as "Add module -> From file", through JASP's own installer - see +# Tools/installModuleLocal.sh for the mechanism). Skipped with --check-only, +# --no-install-local or SKIP_MODULE_LOCAL_INSTALL=1; a failure here is a warning only: +# the bundle itself is fine (most often JASP is still running). +if (!("--check-only" %in% args) && !("--no-install-local" %in% args) && !nzchar(Sys.getenv("SKIP_MODULE_LOCAL_INSTALL"))) { + installer <- file.path(moduledir, "Tools", "installModuleLocal.sh") + status <- system2(installer, shQuote(bundle)) + if (identical(as.integer(status), 0L)) + message("installed into the local JASP (module_libs + manifest + dev prefs) - restart ./build/Desktop/JASP to pick it up.") + else + warning("bundle built OK, but the local install failed (is JASP running?) - run Tools/installModuleLocal.sh manually.") +} else { + message("Install in JASP: + menu -> Add module -> From file, pick that .JASPModule (or run Tools/installModuleLocal.sh).") +} diff --git a/Tools/installModuleLocal.sh b/Tools/installModuleLocal.sh new file mode 100755 index 0000000..8f99115 --- /dev/null +++ b/Tools/installModuleLocal.sh @@ -0,0 +1,144 @@ +#!/usr/bin/env bash +# installModuleLocal.sh - (re)install the built jaspTestModule bundle into JASP's +# user module library WITHOUT the GUI "Add module -> From file" click. +# +# What it does (all via JASP's own mechanism, see references below): +# 1. Runs jaspModuleBundleManager::installJaspModuleBundle(installPath, bundlePath) - +# exactly the R call JASP's "Add module -> From file" makes +# (jasp-desktop/Desktop/modules/dynamicmodules.cpp:310-345, getJsonForBundleInstallRequest). +# It untars the bundle's L0 package archives into +# ~/.local/share/JASP/JASP/Modules/binary_pkgs//, writes +# Modules/manifests/jaspTestModule_manifest.json and creates the module's private +# R library Modules/module_libs/jaspTestModule/ -> ../../binary_pkgs/. +# On reinstall it first uninstalls the previous version (removes only binary_pkgs +# not referenced by any remaining manifest). +# 2. Rewrites the installed manifest's "version" field to a plain string ("0.96.2"). +# Reason: JASP scans Modules/manifests/jasp*.json at startup to (re)load installed +# modules (Desktop/modules/installedmodules.cpp, getAllAvailableModules -> parseModuleInfo). +# parseModuleInfo() calls version.isMember("Version") which, with the jsoncpp in this +# build, THROWS on the array format [[0,96,2]] that the bundle tooling emits +# ("Could not parse module manifest ... requires objectValue or nullValue") -> +# the module is silently skipped on every restart. The string format is one of the +# formats parseModuleInfo explicitly supports (root["version"].isString() branch). +# 3. Makes sure the two JASP preferences needed for a *dev* module are set +# (Qt QSettings INI at ~/.config/JASP/JASP.conf - there is no preferences.json): +# developerMode=true - without it getModules() erases jaspTestModule +# (installedmodules.cpp:86-87) +# modulesRemembered=...jaspTestModule... - modulesRemember defaults to true, so a +# remembered module is enabled (ribbon button shown) +# at startup (ribbonmodel.cpp, loadModules()). +# +# Usage: +# Tools/installModuleLocal.sh [path-to-bundle] [--verify] +# bundle default: /bundles_out/jaspTestModule.JASPModule +# --verify afterwards run the dev JASP headless (offscreen) and grep the log +# for proof that the module loads at startup. +# Env overrides: JASP_MODULES_DIR, JASP_CONF, JASP_MODULE_TOOLLIB, JASP_DESKTOP_DIR, JASP_BIN +# +# After this script, every "jasp-desktop/build/Desktop/JASP" start shows jaspTestModule; +# rerun it after each rebuild of the bundle. JASP must not be running. +set -euo pipefail + +MODULE_NAME=jaspTestModule +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +MODULE_DIR="$(dirname "$SCRIPT_DIR")" +ROOT_DIR="$(dirname "$MODULE_DIR")" + +BUNDLE="$MODULE_DIR/bundles_out/$MODULE_NAME.JASPModule" +VERIFY=0 +for a in "$@"; do + case "$a" in + --verify) VERIFY=1 ;; + *) BUNDLE="$a" ;; + esac +done + +MODULES_DIR="${JASP_MODULES_DIR:-$HOME/.local/share/JASP/JASP/Modules}" +CONF="${JASP_CONF:-$HOME/.config/JASP/JASP.conf}" +JASP_BIN="${JASP_BIN:-${JASP_DESKTOP_DIR:-$ROOT_DIR/jasp-desktop}/build/Desktop/JASP}" + +[ -f "$BUNDLE" ] || { echo "ERROR: bundle not found: $BUNDLE (build it with Tools/buildBundle.R)" >&2; exit 1; } +command -v Rscript >/dev/null || { echo "ERROR: Rscript not on PATH" >&2; exit 1; } +if pgrep -f "build/Desktop/JASP( |$)" >/dev/null 2>&1; then + echo "ERROR: JASP appears to be running; quit it first (install replaces module_libs/manifests)." >&2; exit 1 +fi + +# --- find a library containing jaspModuleBundleManager (+ fs/rjson/stringr/archive) -------- +find_tool_lib() { + local c + for c in "${JASP_MODULE_TOOLLIB:-}" \ + "$ROOT_DIR/jasp-desktop/build/Modules/Tools/jaspModuleBundleManager_library" \ + "$ROOT_DIR/.toolLib"; do + [ -n "$c" ] && [ -d "$c/jaspModuleBundleManager" ] && [ -d "$c/archive" ] && { echo "$c"; return; } + done + return 1 +} +TOOL_LIB="$(find_tool_lib)" || { echo "ERROR: no R library with jaspModuleBundleManager found (set JASP_MODULE_TOOLLIB)" >&2; exit 1; } +echo "==> tool lib : $TOOL_LIB" +echo "==> bundle : $BUNDLE" +echo "==> install : $MODULES_DIR" + +# --- 1. install the bundle exactly like the GUI does --------------------------------------- +Rscript --vanilla -e " + .libPaths(c('$TOOL_LIB', .libPaths())) + p <- jaspModuleBundleManager::installJaspModuleBundle( + installPath = '$MODULES_DIR', + bundlePath = '$BUNDLE') + cat('installed module lib:', as.character(p), '\n') +" + +# --- 2. normalize manifest version to the string format JASP's startup parser accepts ------ +MANIFEST="$MODULES_DIR/manifests/${MODULE_NAME}_manifest.json" +[ -f "$MANIFEST" ] || { echo "ERROR: expected manifest not written: $MANIFEST" >&2; exit 1; } +Rscript --vanilla -e " + .libPaths(c('$TOOL_LIB', .libPaths())) + m <- rjson::fromJSON(file = '$MANIFEST') + if (!is.character(m\$version)) { + v <- unlist(m\$version) + m\$version <- paste(v, collapse = '.') + writeLines(rjson::toJSON(m, indent = 1), '$MANIFEST') + cat('manifest version normalized ->', m\$version, '\n') + } else cat('manifest version already a string:', m\$version, '\n') +" + +# --- 3. preferences (Qt QSettings INI): developerMode + remembered/enabled module ---------- +if [ -f "$CONF" ]; then + cp -f "$CONF" "$CONF.bak-lastinstall" +else + mkdir -p "$(dirname "$CONF")"; printf '[General]\n' > "$CONF" +fi +awk -v mod="$MODULE_NAME" ' + BEGIN { dev=0; rem=0; remHas=0; out="" } + /^[[:space:]]*developerMode[[:space:]]*=/ { dev=1; sub(/=.*/,"=true"); print; next } + /^[[:space:]]*modulesRemembered[[:space:]]*=/ { + rem=1 + if ($0 ~ ("(^|\\|)[[:space:]]*" mod "([[:space:]]*\\||[[:space:]]*$)")) { print; remHas=1; next } + sub(/[[:space:]]*$/, "") + if ($0 ~ /=$/) print $0 mod; else print $0 "|" mod + next + } + { print } + END { + if (!dev) print "developerMode=true" + if (!rem) print "modulesRemembered=" mod + } +' "$CONF" > "$CONF.tmp" && mv "$CONF.tmp" "$CONF" +echo "==> prefs : $CONF (developerMode=true, modulesRemembered includes $MODULE_NAME)" + +echo "==> installed tree:" +ls -ld "$MODULES_DIR/module_libs/$MODULE_NAME" >/dev/null && \ + echo " $MODULES_DIR/module_libs/$MODULE_NAME ($(ls "$MODULES_DIR/module_libs/$MODULE_NAME" | wc -l) pkgs)" +grep -o '"version": *"[^"]*"' "$MANIFEST" | sed 's/^/ manifest /' + +# --- 4. optional offscreen verification ----------------------------------------------------- +if [ "$VERIFY" = 1 ]; then + [ -x "$JASP_BIN" ] || { echo "ERROR: JASP binary not found: $JASP_BIN (set JASP_BIN)" >&2; exit 1; } + LOG=/tmp/opencode/jasp_load.log; mkdir -p /tmp/opencode + echo "==> starting $JASP_BIN (offscreen, 25s) -> $LOG" + timeout 25 env QT_QPA_PLATFORM=offscreen "$JASP_BIN" --newData >"$LOG" 2>&1 || true + echo "==> proof:" + grep -in "could not parse module manifest" "$LOG" && { echo "FAIL: manifest still rejected"; exit 1; } + grep -in "jaspTestModule" "$LOG" | head -5 + grep -in "Modules::EntryBase\|RibbonButton.*is ready" "$LOG" | head -3 + echo "==> OK: no manifest parse error and $MODULE_NAME activity present in $LOG" +fi From 2f66030fdbbe641234d44cf8779ed8989f33f4b0 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Thu, 8 Oct 2026 03:24:12 +0200 Subject: [PATCH 13/44] Crash 2026-10-08: root cause + fix + scripted RPC repro/verify SEGV opening the multi-dataset test form was a null entry in _depends: the form's depends: "dataSetA"/"dataSetB" cannot resolve on desktop (the syntax-mode DropDown is unnamed there) and JASPControl::_addExplicitDependency inserted the null, which AnalysisForm::sortControls dereferenced. Fixed in jasp-desktop 785afe486. Tools/verifyMultiDataSetRpc.py drives the whole flow over JASP's RPC server (load both CSVs, create form, mixed-provenance runs, assert routed results). --- TODO-multidataset.md | 34 ++++++ Tools/verifyMultiDataSetRpc.py | 183 +++++++++++++++++++++++++++++++++ 2 files changed, 217 insertions(+) create mode 100644 Tools/verifyMultiDataSetRpc.py diff --git a/TODO-multidataset.md b/TODO-multidataset.md index 8940977..ed61137 100644 --- a/TODO-multidataset.md +++ b/TODO-multidataset.md @@ -67,3 +67,37 @@ X dataset selection across jasp-desktop (workspace runWrappedAnalysis aware flow with string parts), jaspTestModule (selection options + depends in testMultiDataSet.qml, twin's own qml, wrapper test rewritten, buildBundle check fixed). - Known NOT-a-bug: jaspTools doesn't encode (longstanding, user-confirmed, out of scope). + +## Crash 2026-10-08 +SEGV opening Test Multi-DataSet on desktop with two CSVs loaded: log ended at +`AnalysisForm::setAnalysisUp() for 0x...` and JASP died. + +**Root cause** (jasp-desktop): `testMultiDataSet.qml` carries `depends: "dataSetA"` / +`depends: "dataSetB"` on the AssignedVariablesLists (bind the syntax-mode dataset-selection +option before the lists, 3e09b7b31/5b983c9). On desktop that option's bound DropDown is +deliberately unnamed (`name: syntaxMode ? variablesForm.dataSetSelectionOption : ""`) so it is +never registered in `AnalysisForm::_controls`; `JASPControl::_addExplicitDependency` resolved +the name to `nullptr` and inserted it into `_depends` unconditionally. `AnalysisForm::sortControls` +then dereferenced it (`depend->depends()` at this=0x20). Backtrace: setAnalysisUp -> _setUpControls +-> _setUp -> sortControls -> std::set::begin(this=0x20). + +**Fix** jasp-desktop `785afe486`: _addExplicitDependency only inserts resolved, non-self controls +(an unresolvable `depends` name imposes no binding order; desktop stays 100% functional, the +syntax-mode DropDown stays inert there); sortControls skips nulls defensively. No module changes. + +**Repro + verify recipe (scripted, no GUI):** +1. `rpcServerEnabled=true` in ~/.config/JASP/JASP.conf; nothing else running. +2. `cd /build && QT_QPA_PLATFORM=offscreen gdb -batch -ex run -ex bt --args ./Desktop/JASP > /tmp/jasp_run.log 2>&1 &` +3. `python3 Tools/verifyMultiDataSetRpc.py` — loads DataSetA+DataSetB via RPC (two datasets in + one workspace), creates the analysis (this is where the pre-fix build SEGVs), binds + dependentA=Score@ds1 and dependentB=Score+Condition@ds2 (Score exists in both; mixed + provenance via analysis_setFilter + the unchanged-value early return in + BoundControlBase::setBoundValue), runs and asserts the results: 'Datasets in this run' has + both datasets (titles, 10 rows each), 'Selected variables' routes each value to its own + dataset with n=10 and mean/levels (A:Score mean 12.50 -> DataSet 1, B:Score mean 17.73 + + B:Condition X,Y,Z -> DataSet 2), all decoded (no JASPColumn_* left). +Pre-fix the script dies at analysis_create with the backtrace above; post-fix ALL CHECKS PASSED. +Suites green with the fix: JASPTest 109 PASS, JASPQuickTest 93 PASS. +Note: RPC `analysis_create` cannot carry options, and per-control drops are not exposed; the +script works around that with setFilter-then-run (unchanged values keep their .meta), so no +new RPC surface was needed. diff --git a/Tools/verifyMultiDataSetRpc.py b/Tools/verifyMultiDataSetRpc.py new file mode 100644 index 0000000..034e89c --- /dev/null +++ b/Tools/verifyMultiDataSetRpc.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +"""Verify jaspTestModule::multiDataSetFunc end-to-end through JASP's RPC server. + +Precondition: JASP (dev build) already running offscreen with RPC enabled, NO datasets. +Steps: load DataSetA + DataSetB (2 datasets in the workspace), create the multi-dataset +test analysis, bind dependentA to a column of dataset 1 and dependentB to a column that +exists in BOTH datasets plus one that exists only in dataset 2, run, fetch results and +assert: + - 'Datasets in this run' has 2 rows with titles + row/col counts + - 'Selected variables' rows have n=10 and mean/levels values (not empty, not JASPColumn_*) +Exit 0 = all good, 1 = failure (details printed). + +Run recipe (scripted crash-repro + verify loop): + 1. ~/.config/JASP/JASP.conf has rpcServerEnabled=true (and no JASP running). + 2. cd /build && QT_QPA_PLATFORM=offscreen ./Desktop/JASP + (under gdb to capture a backtrace if it dies: + gdb -batch -ex run -ex bt --args ./Desktop/JASP > /tmp/jasp_run.log 2>&1 &) + 3. python3 Tools/verifyMultiDataSetRpc.py +Before the jasp-desktop fix 785afe486 this died at analysis_create: the SEGV is +reproduced when JASP exits mid-script, backtrace in the gdb log. +""" + +# ---------------------------------------------------------------------------- +# Minimal JSON-RPC 2.0 client for JASP's RPC server (enabled by the preference +# rpcServerEnabled=true in ~/.config/JASP/JASP.conf; listens on port 48164, +# path /rpc by default). +# ---------------------------------------------------------------------------- +import json as _json, time as _time, urllib.request as _urllib + +URL = "http://127.0.0.1:48164/rpc" +_id = 0 + +def _rpc(method, params=None, timeout=60): + global _id + _id += 1 + body = _json.dumps({"jsonrpc": "2.0", "id": _id, "method": method, + "params": params or {}}).encode() + req = _urllib.Request(URL, data=body, headers={"Content-Type": "application/json"}) + with _urllib.urlopen(req, timeout=timeout) as r: + return _json.loads(r.read().decode()) + +def wait_up(max_wait=120): + start = _time.time() + while _time.time() - start < max_wait: + try: + r = _rpc("ping", timeout=2) + if "result" in r: + return r + except Exception: + _time.sleep(0.5) + raise RuntimeError("RPC server did not come up") + +call = _rpc + +import sys, json, time + +A_CSV = "/home/virtuoos/Broncode/JASP/jaspTestModule/Tools/testdata/DataSetA.csv" +B_CSV = "/home/virtuoos/Broncode/JASP/jaspTestModule/Tools/testdata/DataSetB.csv" + +fails = [] +def check(name, cond, extra=""): + print(("PASS " if cond else "FAIL ") + name + (f" {extra}" if extra else "")) + if not cond: + fails.append(name) + +def call(method, params=None, timeout=90): + r = _rpc(method, params, timeout=timeout) + if "error" in r: + print(f"RPC error on {method}: {json.dumps(r['error'])[:600]}") + return r.get("result", r) + +wait_up(120) +print("== RPC up ==") + +da = call("data_load", {"path": A_CSV, "wait": True, "timeoutMs": 60000}) +db = call("data_load", {"path": B_CSV, "wait": True, "timeoutMs": 60000}) +check("two datasets loaded (ids 1 and 2)", da.get("dataSetId") == 1 and db.get("dataSetId") == 2, + f"ids {da.get('dataSetId')}, {db.get('dataSetId')}") +i1 = call("data_info", {"dataSetId": 1}) +check("dataset 1 has Group (only in A)", any(c["name"] == "Group" for c in i1.get("columns", []))) + +an = call("analysis_create", {"module": "jaspTestModule", "analysis": "multiDataSetFunc"}) +check("form created without SEGV, analysis multiDataSetAware", an.get("status") == "success" and an.get("multiDataSetAware") is True) +aid = an.get("analysisId") + +# bind form A's variable while the analysis runs on dataset 1 (filter 1) +print(call("analysis_setFilter", {"analysisId": aid, "filterId": 1}).get("status")) +r1 = call("analysis_run", {"analysisId": aid, "wait": True, "timeoutMs": 60000, "options": { + "dependentA": {"types": [], "value": ["Score"]}}}) +check("first run (dependentA=Score @dataset1) complete", r1.get("status") == "complete", + str(r1.get("status"))) + +# switch to dataset 2 and add form B's variables: dependentA is unchanged so its ds1 +# provenance survives the rebind (BoundControlBase::setBoundValue early-returns) +print(call("analysis_setFilter", {"analysisId": aid, "filterId": 2}).get("status")) +r2 = call("analysis_run", {"analysisId": aid, "wait": True, "timeoutMs": 120000, "options": { + "dependentA": {"types": [], "value": ["Score"]}, + "dependentB": {"types": [], "value": ["Score", "Condition"]}, + "showMeta": True}}) +check("second run complete", r2.get("status") == "complete", str(r2.get("status"))) + +# poll for completion +for _ in range(30): + st = call("get_analyses_state", {"analysisIds": [aid], "include_options": True, "include_results": False}) + a = (st.get("analyses") or [{}])[0] + if a.get("status") in ("complete", "empty", "noData", "running"): + if a.get("status") != "running": + break + time.sleep(1) +check("analysis status complete", a.get("status") == "complete", f"status={a.get('status')} errors={str(a.get('errors'))[:300]}") + +res = call("analysis_results", {"analysisId": aid, "wait": True, "timeoutMs": 60000}) +open("results_multidataset.json", "w").write(json.dumps(res, indent=1)) +import shutil, os +rds = res.get("jaspResultsRds") +if rds and os.path.exists(rds): + shutil.copy(rds, "jaspResults_copied.rds") +results = res.get("results", res) + +def tables(node, path=""): + out = {} + if isinstance(node, dict): + if "schema" in node and "data" in node: + out[path] = node + for k, v in node.items(): + out.update(tables(v, f"{path}/{k}")) + elif isinstance(node, list): + for i, v in enumerate(node): + out.update(tables(v, f"{path}[{i}]")) + return out + +tabs = tables(results) +print("tables found:", [(k, t.get("title")) for k, t in tabs.items()][:20]) + +def field_maps(t): + """jaspBase json: data[i] is a {rowKey: value} map for schema field i.""" + fields = [f["name"] for f in t["schema"]["fields"]] + rowkeys = set() + for entry in t["data"]: + for key, val in entry.items(): + if val is not None and key not in fields: + rowkeys.add(key) + return fields, {f: t["data"][i] for i, f in enumerate(fields)}, sorted(rowkeys) + +ds = next((t for t in tabs.values() if t.get("title") == "Datasets in this run"), None) +check("'Datasets in this run' table present", ds is not None) +if ds: + fields, fm, rks = field_maps(ds) + dump = json.dumps([fm[f].get(r) for r in rks for f in fields]) + check("2 dataset rows", len(rks) == 2, str(rks)) + titles = sorted(str(fm["title"].get(r)) for r in rks) + check("both dataset titles present (non-empty)", len(titles) == 2 and all(t not in ("None", "") for t in titles), str(titles)) + check("row counts 10/10", all(fm["rows"].get(r) == 10 for r in rks), str([fm["rows"].get(r) for r in rks])) + check("col counts present", all(isinstance(fm["cols"].get(r), int) for r in rks), str([fm["cols"].get(r) for r in rks])) + check("no raw JASPColumn_* in overview", "JASPColumn" not in dump, dump[:300]) + print("Datasets-in-this-run rows:", json.dumps([{f: fm[f].get(r) for f in fields} for r in rks])) + +sv = next((t for t in tabs.values() if t.get("title") == "Selected variables"), None) +check("'Selected variables' table present", sv is not None) +if sv: + fields, fm, rks = field_maps(sv) + dump = json.dumps({r: {f: fm[f].get(r) for f in fields} for r in rks}) + rows = [{f: fm[f].get(r) for f in fields} for r in rks] + def row(src, var): return next((x for x in rows if x["source"] == src and x["variable"] == var), None) + check("3 selected-variable rows (A:Score, B:Score, B:Condition)", len(rows) == 3, dump[:500]) + a_score = row("A", "Score") + b_score = row("B", "Score") + b_cond = row("B", "Condition") + check("A:Score routed to dataset 1", a_score and a_score["dataSetId"] in (1, "1") and a_score["dataSetName"] == "DataSet 1", json.dumps(a_score)) + check("B:Score (name in BOTH datasets) routed to dataset 2", b_score and b_score["dataSetId"] in (2, "2") and b_score["dataSetName"] == "DataSet 2", json.dumps(b_score)) + check("B:Condition (only in dataset 2) routed there", b_cond and b_cond["dataSetId"] in (2, "2"), json.dumps(b_cond)) + check("n=10 on every row", all(r["n"] == 10 for r in rows), str([r["n"] for r in rows])) + print("Selected variables rows:") + for r in rows: print(" ", json.dumps(r)) + check("scale rows show mean, factor row shows levels", + a_score and str(a_score["values"]).startswith("mean") and b_cond and str(b_cond["values"]) == "X, Y, Z", + f"{a_score['values'] if a_score else None!r} / {b_cond['values'] if b_cond else None!r}") + check("decoded names, no raw JASPColumn_*", "JASPColumn" not in dump, dump[:300]) + +check("analysis references both datasets (dataSetIds in run response)", sorted(r2.get("dataSetIds", [])) == [1, 2], str(r2.get("dataSetIds"))) + +print("\n" + ("ALL CHECKS PASSED" if not fails else f"{len(fails)} FAILURES: {fails}")) +sys.exit(0 if not fails else 1) From 92d3795470042ced7b638f0a7f47fd61ba0921a0 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Thu, 8 Oct 2026 11:36:40 +0200 Subject: [PATCH 14/44] TODO: pointer to the per-host FilterSelect refactor plan (linked-dropdown fix) --- TODO-multidataset.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/TODO-multidataset.md b/TODO-multidataset.md index ed61137..32bed4b 100644 --- a/TODO-multidataset.md +++ b/TODO-multidataset.md @@ -16,6 +16,12 @@ pre-check; ComboBoxBase coerces "" to first entry). No filters in syntax mode (pre-filtered data); default filter queued per slice. Names are user-convenience only (DataSet title). +## ACTIVE PLAN +The per-host FilterSelect refactor (independent per-form selection, filter-id options, +VariableInfo provider chain) is planned in full at: +`/home/virtuoos/.opencode/plan/multidataset-filterselect.md` — READ IT, red-tests-first. +It replaces the linked-dropdown design (both dropdowns mirrored analysis->filterId = the bug). + ## TODO 1. [DONE jasp-desktop d8ed73558] Desktop plot gap: `Analysis::loadPlotlyJsonInResults` decodes plotly JSON against the analysis' OWN dataset encoder only — aware analyses plotting other datasets stay encoded. From c2d59ebbb9fbb527622be422e2461ab55c05a647 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Thu, 8 Oct 2026 13:39:47 +0200 Subject: [PATCH 15/44] module: route selected variables through the FORM'S OWN selection slice (filter id), qmls drop the removed dataSetSelection bool --- R/multiDataSet.R | 53 ++++++++++++++++++++------- inst/qml/testMultiDataSet.qml | 2 - inst/qml/testMultiDataSetNonAware.qml | 4 +- 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/R/multiDataSet.R b/R/multiDataSet.R index 80f9888..9544799 100644 --- a/R/multiDataSet.R +++ b/R/multiDataSet.R @@ -1,7 +1,10 @@ # Multi-dataset aware analyses (jasp-desktop branch dataSetAwareVarForm, jaspBase branch # multiDataSetAware). An analysis whose Description/Analysis entry sets multiDataSetAware gets -# every dataset it references as `datasets`: a named list keyed by dataset id, with the user -# facing titles in attr(datasets, "dataSetNames"). Options and data share the ENCODED namespace: +# every dataset it references as `datasets`: a named list keyed by SLICE id - the resolved +# filter id of each queued (dataset, filter) pair - with the user facing titles in +# attr(datasets, "dataSetNames") and each slice's dataset id in attr(datasets, "dataSetIds"). +# A per-form selection option indexes its own slice directly: datasets[[as.character( +# as.integer(options$dataSetA))]]. jaspTools handouts stay keyed by dataset id. Options and data share the ENCODED namespace: # variable options carry names like "JASPColumn___Encoded" and the dataframes' # column names are the very same strings (encoded by each dataset's own ColumnEncoder, whose # prefix embeds the dataset id), so an analysis indexes with the option value as-is: @@ -69,24 +72,30 @@ selectedVariablesTable <- function(jaspResults, options, datasets, dataset) { table$addColumnInfo("n", title = "n") table$addColumnInfo("values", title = "mean (scale) / first levels (factor)") - for (entry in list(list(form = "A", values = .dependentValues(options$dependentA)), - list(form = "B", values = .dependentValues(options$dependentB)))) { + for (entry in list(list(form = "A", sel = "dataSetA", values = .dependentValues(options$dependentA)), + list(form = "B", sel = "dataSetB", values = .dependentValues(options$dependentB)))) { + + # The form's own selection (a filter id) indexes its slice directly - that is the whole point + # of per-form selection: two forms can run on two filters of the same dataset. + selKey <- .selectionKey(options, datasets, entry$sel) + selSlice <- if (!is.null(selKey)) datasets[[selKey]] else NULL + for (value in entry$values) { # value indexes the data as-is in both modes: options and column names share the encoded - # namespace (or are both plain, in a jaspTools run). Only the routing to one dataset out - # of `datasets` is aware-specific; the non-aware run has exactly the one dataset anyway. - columnData <- if (is.null(datasets)) { - if (!is.null(dataset) && value %in% names(dataset)) dataset[[value]] else NULL - } else { - jaspBase::getDataSetColumn(value, datasets) - } + # namespace (or are both plain, in a jaspTools run). Routing prefers the form's own + # selection slice, falling back to the dataset embedded in the name (handout runs carry no + # filter-keyed slices); the non-aware run has exactly the one dataset anyway. + slice <- if (is.null(datasets)) dataset else if (!is.null(selSlice)) selSlice else jaspBase::getDataSetFor(value, datasets) + columnData <- if (!is.null(slice) && value %in% names(slice)) slice[[value]] else NULL id <- if (is.null(datasets)) NA_integer_ else jaspBase::dataSetIdFromEncoded(value) row <- list( source = entry$form, variable = value, - dataSetId = if (is.null(datasets)) "-" else if (is.na(id)) "none (legacy)" else as.character(id), - dataSetName = if (is.null(datasets)) "-" else jaspBase::dataSetNameFromEncoded(value, datasets), + dataSetId = if (!is.null(selKey)) as.character(attr(datasets, "dataSetIds")[[selKey]]) + else if (is.null(datasets)) "-" else if (is.na(id)) "none (legacy)" else as.character(id), + dataSetName = if (!is.null(selKey)) as.character(attr(datasets, "dataSetNames")[[selKey]]) + else if (is.null(datasets)) "-" else jaspBase::dataSetNameFromEncoded(value, datasets), n = NA_integer_, values = "") if (!is.null(columnData)) @@ -132,6 +141,24 @@ multiDataSetNonAwareInternal <- function(jaspResults, dataset, options) { as.character(values[!is.na(values) & as.character(values) != ""]) } +# The slice key (a filter id as string) behind a per-form dataset selection option. Options +# arrive either plain ("42") or in the wrapper default shape list(types="unknown", value=""); +# "" / absent / not-a-listed-slice yields NULL (route by the dataset id embedded in the name). +.selectionKey <- function(options, datasets, selOption) { + selVal <- utils::tail(unlist(options[[selOption]]), 1L) + + if (is.null(selVal) || is.na(selVal)) + return(NULL) + + selVal <- trimws(as.character(selVal)) + filterId <- suppressWarnings(as.integer(selVal)) + + if (is.na(filterId) || !selVal %in% names(datasets)) + return(NULL) + + selVal +} + .fillDescription <- function(entry, columnData) { if (is.numeric(columnData)) { entry$n <- sum(!is.na(columnData)) diff --git a/inst/qml/testMultiDataSet.qml b/inst/qml/testMultiDataSet.qml index 6a52ec0..892dce0 100644 --- a/inst/qml/testMultiDataSet.qml +++ b/inst/qml/testMultiDataSet.qml @@ -31,7 +31,6 @@ Form VariablesForm { - dataSetSelection: true //syntax mode: an option carrying the dataset name to select for this form //(jaspSyntax gates the bound dropdown on it; desktop never sees it) dataSetSelectionOption: "dataSetA" @@ -51,7 +50,6 @@ Form VariablesForm { - dataSetSelection: true dataSetSelectionOption: "dataSetB" AvailableVariablesList { name: "allVariablesB" } diff --git a/inst/qml/testMultiDataSetNonAware.qml b/inst/qml/testMultiDataSetNonAware.qml index 2f9a9b6..04875a9 100644 --- a/inst/qml/testMultiDataSetNonAware.qml +++ b/inst/qml/testMultiDataSetNonAware.qml @@ -4,7 +4,7 @@ import "./common" // Twin of testMultiDataSet.qml for the NON-aware entry (multiDataSetNonAware): same forms and // checkboxes, but no dataSetSelectionOption - a non-aware analysis has exactly one (shown) -// dataset, in the GUI as well as in syntax mode. The dataSetSelection: true stays to demo that +// dataset, in the GUI as well as in syntax mode. Without a selection option the forms show no // the dropdown remains inert when the analysis is not multiDataSetAware. Form { @@ -22,7 +22,6 @@ Form VariablesForm { - dataSetSelection: true AvailableVariablesList { name: "allVariablesA" } AssignedVariablesList @@ -38,7 +37,6 @@ Form VariablesForm { - dataSetSelection: true AvailableVariablesList { name: "allVariablesB" } AssignedVariablesList From 13a207ab97189fb135fa47eaa334be1cc54d7d65 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Thu, 8 Oct 2026 14:21:45 +0200 Subject: [PATCH 16/44] RPC verifier drives the dropdown contract itself (filter-id options, no setFilter); TODO rewritten for the FilterSelect architecture + pre-existing testDbMigration failure noted --- TODO-multidataset.md | 190 +++++++++++------------ Tools/verifyMultiDataSetRpc.py | 52 +++++-- results_multidataset.json | 266 +++++++++++++++++++++++++++++++++ 3 files changed, 396 insertions(+), 112 deletions(-) create mode 100644 results_multidataset.json diff --git a/TODO-multidataset.md b/TODO-multidataset.md index 32bed4b..b021823 100644 --- a/TODO-multidataset.md +++ b/TODO-multidataset.md @@ -1,109 +1,99 @@ -# Multi-dataset aware feature — open work (2026-10-07, post syntax-mode-selection) +# Multi-dataset aware feature — status (2026-10-08, post FilterSelect refactor) -## Decided design (ground truth from user) +## Decided design (ground truth, v2 — supersedes the linked-dropdown v1) - Options and dataset slices share the ENCODED namespace (`JASPColumn___Encoded`); - R indexes `datasets[[id]][[encodedValue]]` directly; results/plots are decoded on the way out - (engine sendString loops every encoder the analysis references). -- `preloadData` forced true for aware analyses (Modules::AnalysisEntry + EntryBase getters); - the engine checks multiAware BEFORE the preload flag. -- Syntax mode: `datasets = list(name = df, ...)` loads each df into the bridge Workspace/DataSet - (title = list name, real ids + encoders). Each VariablesForm has a dataset-selection OPTION - (dataSetSelectionOption names it; NO_DESKTOP_MODE-gated bound DropDown); the user fills the - dataset NAME there; JASPControl::_setInitialized re-applies the owning form's selection before - every child binds (order-proof), QML validates + stamps .meta, the bridge then encodes - per-dataset and queues slices via the SHARED DataBridge::prepareMultiDataSetRun (same code as - Engine::runAnalysis). Empty/missing selection options fail with a clear R error (bridge - pre-check; ComboBoxBase coerces "" to first entry). No filters in syntax mode (pre-filtered - data); default filter queued per slice. Names are user-convenience only (DataSet title). - -## ACTIVE PLAN -The per-host FilterSelect refactor (independent per-form selection, filter-id options, -VariableInfo provider chain) is planned in full at: -`/home/virtuoos/.opencode/plan/multidataset-filterselect.md` — READ IT, red-tests-first. -It replaces the linked-dropdown design (both dropdowns mirrored analysis->filterId = the bug). + R indexes the slice's columns by option value directly; results/plots are decoded on the way + out (engine sendString loops every workspace encoder; plotly decode loops referenced datasets). +- **Selection state is per-HOST, not per-analysis.** Any component consuming variable info for + itself declares `dataSetSelectionOption` (a `HostFilterSelection` member): VariablesFormBase + and TextAreaBase do so today. The option's value is a **FILTER ID** (a filter implies its + dataset; ids survive renames), written in BOTH modes by one reusable `FilterSelect.qml` + (a bound DropDown, `values:` = workspace filter list "DataSet - Filter", created behind + `Loader { active: selectionAvailable }` so an unnamed bound control can never exist). +- **Provider chain follows the component chain:** each selecting host owns a `VariableInfo` + whose provider is the selected `Filter` (Filter *is* a VariableInfoProvider: per-dataset + columns/types/levels, rows filtered, `Filter::columnEncoder()` gives the dataset encoder). + `JASPControl::effectiveVarInfo()` / `effectiveSelectionFilter()` resolve nearest-selecting-host + (self included), else `AnalysisForm::varInfo()` (analysis filter / shownFilter — today's + behavior for everything non-aware). Non-JASPControl pieces (Formula) use the static + `JASPControl::varInfoForItem()`. +- **Selection code never touches** `analysis->setFilterId` / `workspace->setShownDataSet` — + those belong to the analysis-GLOBAL FilterMenuButton (the true legacy widget, untouched). +- `.meta` provenance is stamped with the filter the value was selected THROUGH (host's, else + analysis'), so per-form datasets survive save/restore and reach the engine per control. +- **Slice queue keyed by filter id:** `prepareMultiDataSetRun` collects every distinct + (dataSetId, filterId) from `.meta`; one slice per pair (two forms on one dataset with + different filters → two differently-filtered dataframes). R-side `datasets` keys = resolved + filter id, `attr "dataSetIds"`/`"dataSetNames"` map back; a selection option indexes its own + slice directly: `datasets[[as.character(options$dataSetA)]]`. jaspTools handouts stay keyed by + dataset id; `.sliceKeyForDataSet` bridges both. +- **Syntax deviation confined to the wrapper layer:** bridge translates a selection option that + names a loaded dataset ("haha") into that DataSet's default filter id before binding (unknown + names fail loudly with the list of loaded datasets; "" too). `datasets = list(haha = df, ...)` + stays the user-facing API (names become DataSet titles). Forms are mode-blind. +- Single-dataset API on AnalysisBase **refuses** for aware analyses instead of silently answering + about one arbitrary dataset: `Analysis::dataSet()`, `isColumnFreeOrMine()`, + `isOwnComputedColumn()`, computed-column request handlers (log + safe default); + `usesDataSet()` consults `referencedDataSets()` when aware. +- `preloadData` forced true for aware analyses; engine checks multiAware BEFORE preload. +- Vanished filter/dataset while selected: selection falls back to the analysis/current filter + (same spirit as the analysis-global behavior), never dangles (HostFilterSelection validates on + dataSetRemoved/filtersCountChanged). ## TODO -1. [DONE jasp-desktop d8ed73558] Desktop plot gap: `Analysis::loadPlotlyJsonInResults` decodes plotly JSON against the - analysis' OWN dataset encoder only — aware analyses plotting other datasets stay encoded. - Same multi-encoder treatment as `Engine::sendString` `_analysisDataSetEncoders`. -2. [DONE jasp-desktop 0bf83f920] `_analysisDataSetEncoders` stored raw `ColumnEncoder*` — dataset deleted mid-run - dangles (only the CURRENT pointer is guarded in ~DataSet). Re-resolve by id at send time. -3. [verify] GUI end-to-end after bundle rebuild: dropdown fills tables (aware + twin), guard - demo, per-form dataset switching in the GUI, and saved-file reload of an aware analysis - (`restoreProvenanceFromBoundValues` / `remapSavedProvenance` still lack an end-to-end test). -4. [DONE] Bundle + local install: `Tools/buildBundle.R` now ends by installing the bundle into - the local dev JASP automatically (Tools/installModuleLocal.sh: jaspModuleBundleManager - install + manifest version normalization + developerMode/modulesRemembered prefs). - Skip with `--no-install-local` / SKIP_MODULE_LOCAL_INSTALL=1; JASP must not be running. -5. [upstream] JASP bug candidate: bundle tooling writes manifest `"version":[[0,96,2]]` but - `InstalledModules::parseModuleInfo` (installedmodules.cpp:28) throws on arrays via - `version.isMember("Version")` → "Could not parse module manifest" → module silently skipped - at every restart after a perfectly fine GUI install. Fix belongs in jasp-desktop - (accept arrays) and/or jaspModuleBundleManager (write strings). installModuleLocal.sh - works around it by rewriting the manifest field. +1. [verify-manual] GUI smoke test (last resort, everything else is scripted): two CSVs, open + Test Multi-DataSet, flip BOTH "Data" dropdowns independently, watch tables stay filled and + unlinked; R/JAGS text areas with an option show their own dropdown. +2. [open] Saved-file reload of an aware analysis end-to-end: per-form filter-id selections + restored through `bindTo` (+ `restoreProvenanceFromBoundValues`), vanished filter → fallback. + No automated test yet (needs RPC save/open or a crafted .jasp fixture). +3. [open] Same-dataset-two-filters desktop GUI demo: the module analysis routes per form's + selection slice; a test data pair where BOTH forms use dataset 1 with different filters would + exercise the C8 two-slice path in the GUI (covered in rlogic unit, not e2e). +4. [upstream] JASP bug candidate: bundle tooling writes manifest `"version":[[0,96,2]]` but + `InstalledModules::parseModuleInfo` throws on arrays → module silently skipped at restart. + installModuleLocal.sh works around it (rewrites to string). +5. [KNOWN PRE-EXISTING FAILURE — user-confirmed, NOT from the FilterSelect refactor] + `ctest testDbMigration` aborts: "Trying to load ':memory:' but it doesn't exist!" — broken + since the earlier multi-dataset work (workspace/database lifecycle). Investigate separately. 6. [note] Syntax-mode side effects to watch: readRequestedDataset() after an aware parse consumes - queue slices; jaspSyntax result decoding (columnEncoderContext) is per current dataset — - multi-dataset result decoding in pure syntax output may need the same multi-encoder care. -6. [ops] jaspSyntax installs MUST pass `JASPSYNTAX_LIB_PATH=` - (configure otherwise downloads release binaries needing mysql/pq/icu74, absent here); + queue slices; jaspSyntax result decoding is per current dataset — multi-dataset result decoding + in pure syntax output may need the same multi-encoder care. +7. [ops] jaspSyntax installs MUST pass `JASPSYNTAX_LIB_PATH=`; after each SyntaxInterface rebuild copy the .so to BOTH `jaspSyntax/src/` and `.toolLib/jaspSyntax/libs/`. +8. [later] FormulaBase as host in its own right (its own FilterSelect + option): today a Formula + only follows the VariablesForm it lives in (varInfoForItem). Top-level Formulas keep the + analysis-wide fallback. -## Done (this session) -- jasp-desktop `515c1f241` (dropdown visibility: syncMultiDataSetAwareness push, EntryBase notify, - VariablesFormBase::setUp bootstrap — static controls never get formIsKnown — + render probe). -- jasp-desktop `a8cd23e5d` dead dataset-aware decode plumbing removed (C++ dataSetIdFromEncoded - mirror, .decodeColNamesForDataSet callback chain), encode->decode->encode cycle test on real - encoders (pins the `_Encoded` format). -- jasp-desktop `b87a8bbbe` empty-cells fix (primaryDataSetId for unfiltered analyses; aware - checked before preload; cols always collected; rlogic bridge round-trip through jaspSyntax - proving the index-directly invariant against real encoder output) + submodule bumps. -- jaspBase `edc73fe` helpers operate in the encoded namespace (dataSetColumnFromEncoded + - dataSetEncodedNames snapshot deleted; real `_Encoded` parsing; direct indexing; faithful fixtures). -- jaspTools `0d621f3` dead stub + maps removed. -- jaspTestModule `6f6cec1` demo indexes directly; path-1 fixtures carry real encoded names. -- COMMITTED `2bc37b8`+jasp-desktop `3e09b7b31` etc. (all suites green: JASPTest 109, QuickTest 93, rlogic incl bridge, jaspBase 46, -X dataset selection across jasp-desktop (workspace - dataSetByTitle, DataSetProvider titled loads + firstLoadedDataSetId + named loads skip reset, - VariablesFormBase dataSetSelectionOption/dataSetTitleValues/selectDataSetByName/ - applyDataSetSelection, JASPControl _setInitialized apply-hook, AnalysisForm - dataSetSelectionOptionNames, syntaxbridge pre-check + aware prepare branch + shared - DataBridge::prepareMultiDataSetRun extraction), jaspSyntax (loadDataSets, - loadQmlAndParseOptionsStatus, importer name), jaspBase (storeDataSets -> loadDataSets, - runWrappedAnalysis aware flow with string parts), jaspTestModule (selection options + depends - in testMultiDataSet.qml, twin's own qml, wrapper test rewritten, buildBundle check fixed). -- Known NOT-a-bug: jaspTools doesn't encode (longstanding, user-confirmed, out of scope). - -## Crash 2026-10-08 -SEGV opening Test Multi-DataSet on desktop with two CSVs loaded: log ended at -`AnalysisForm::setAnalysisUp() for 0x...` and JASP died. - -**Root cause** (jasp-desktop): `testMultiDataSet.qml` carries `depends: "dataSetA"` / -`depends: "dataSetB"` on the AssignedVariablesLists (bind the syntax-mode dataset-selection -option before the lists, 3e09b7b31/5b983c9). On desktop that option's bound DropDown is -deliberately unnamed (`name: syntaxMode ? variablesForm.dataSetSelectionOption : ""`) so it is -never registered in `AnalysisForm::_controls`; `JASPControl::_addExplicitDependency` resolved -the name to `nullptr` and inserted it into `_depends` unconditionally. `AnalysisForm::sortControls` -then dereferenced it (`depend->depends()` at this=0x20). Backtrace: setAnalysisUp -> _setUpControls --> _setUp -> sortControls -> std::set::begin(this=0x20). - -**Fix** jasp-desktop `785afe486`: _addExplicitDependency only inserts resolved, non-self controls -(an unresolvable `depends` name imposes no binding order; desktop stays 100% functional, the -syntax-mode DropDown stays inert there); sortControls skips nulls defensively. No module changes. +## Done (FilterSelect refactor, 2026-10-08) — plan file: /home/virtuoos/.opencode/plan/multidataset-filterselect.md +- jasp-desktop `8636529f5` RED contract tests first (two forms two filters simultaneously, + bogus-id rejection, per-form list contents; fixture dataset "Second" w/ overlapping TestInts). +- jasp-desktop `557f089d1` the core: HostFilterSelection (filter-id, per-host state, C10 + validation), JASPControl::effectiveVarInfo/effectiveSelectionFilter + all consumers rerouted + (lists, SourceItem, textareas, custom contrasts, filteredDataEntry, R/JAGS textareas), + FilterSelect.qml, createMeta stamps host filter, Filter::columnEncoder, single-dataset guards, + usesDataSet-referencedDataSets; DELETED: dataSetSelection bool + both old dropdowns, + selectDataSetByName/applyDataSetSelection, _setInitialized re-apply hook, name-based + selection, analysis-filter mirroring. +- jasp-desktop `3bbafd477` + jaspBase `69ec48c` filter-keyed slice queue (pair collector, + per-pair slices, `.sliceKeyForDataSet`, rlogic + unit tests for the contract). +- jasp-desktop `e8a4db4e5` bridge name→default-filter-id translation (C7), errors list datasets. +- jasp-desktop `46c4603c3` TextAreaBase host (FilterSelect in TextArea.qml, JAGS highlighter + follows host) + Formula follows its form's host. +- module `c2d59eb` qmls use only dataSetSelectionOption (+depends, now resolvable in both modes); + R routes via the form's own selection slice with legacy fallback; wrapper regenerates. +- module RPC verifier now drives the dropdown contract itself (options carry filter ids, NO + analysis_setFilter anywhere): independent simultaneous selections, A:Score mean 12.50 vs + B:Score mean 17.73 (same name, different datasets — the "compare with itself" trap made + impossible), per-form .meta stamps (1/1 vs 2/2), n=10 everywhere, zero raw JASPColumn_*. +- Suites green: JASPTest 110, JASPQuickTest 96 (T1-T3c/T9/T12), JASPTestColumnEncoderContext 10, + rlogic incl. bridge round-trip, module paths 1-3, RPC desktop e2e ALL CHECKS PASSED. + (ctest except testDbMigration — see item 5, pre-existing.) -**Repro + verify recipe (scripted, no GUI):** -1. `rpcServerEnabled=true` in ~/.config/JASP/JASP.conf; nothing else running. -2. `cd /build && QT_QPA_PLATFORM=offscreen gdb -batch -ex run -ex bt --args ./Desktop/JASP > /tmp/jasp_run.log 2>&1 &` -3. `python3 Tools/verifyMultiDataSetRpc.py` — loads DataSetA+DataSetB via RPC (two datasets in - one workspace), creates the analysis (this is where the pre-fix build SEGVs), binds - dependentA=Score@ds1 and dependentB=Score+Condition@ds2 (Score exists in both; mixed - provenance via analysis_setFilter + the unchanged-value early return in - BoundControlBase::setBoundValue), runs and asserts the results: 'Datasets in this run' has - both datasets (titles, 10 rows each), 'Selected variables' routes each value to its own - dataset with n=10 and mean/levels (A:Score mean 12.50 -> DataSet 1, B:Score mean 17.73 + - B:Condition X,Y,Z -> DataSet 2), all decoded (no JASPColumn_* left). -Pre-fix the script dies at analysis_create with the backtrace above; post-fix ALL CHECKS PASSED. -Suites green with the fix: JASPTest 109 PASS, JASPQuickTest 93 PASS. -Note: RPC `analysis_create` cannot carry options, and per-control drops are not exposed; the -script works around that with setFilter-then-run (unchanged values keep their .meta), so no -new RPC surface was needed. +## Crash 2026-10-08 (fixed, historical) +SEGV opening Test Multi-DataSet: `depends: "dataSetA"` targeted the syntax-only dropdown that was +deliberately unnamed on desktop → `_addExplicitDependency` inserted nullptr → `sortControls` +deref'd it. Interim fix `785afe486` (skip unresolved deps — kept as defense). The FilterSelect +refactor removed the ROOT cause: the dropdown is now named and registered in both modes, and +`Loader { active: selectionAvailable }` makes an unnamed bound control structurally impossible. diff --git a/Tools/verifyMultiDataSetRpc.py b/Tools/verifyMultiDataSetRpc.py index 034e89c..0d6118e 100644 --- a/Tools/verifyMultiDataSetRpc.py +++ b/Tools/verifyMultiDataSetRpc.py @@ -3,11 +3,16 @@ Precondition: JASP (dev build) already running offscreen with RPC enabled, NO datasets. Steps: load DataSetA + DataSetB (2 datasets in the workspace), create the multi-dataset -test analysis, bind dependentA to a column of dataset 1 and dependentB to a column that -exists in BOTH datasets plus one that exists only in dataset 2, run, fetch results and -assert: +test analysis, and drive it the way the GUI dropdowns do: options dataSetA/dataSetB carry +FILTER IDS (form A -> filter 1 = dataset 1, form B -> filter 2 = dataset 2, simultaneously - +the linked-dropdown regression scenario), dependentA = Score (ds1) and dependentB = Score +(same name, ds2!) + Condition (only ds2). Never analysis_setFilter (that is the analysis- +global FilterMenuButton filter). Asserts: - 'Datasets in this run' has 2 rows with titles + row/col counts - - 'Selected variables' rows have n=10 and mean/levels values (not empty, not JASPColumn_*) + - 'Selected variables' rows have n=10 and the right means (Score/Score land on DIFFERENT + datasets: 12.50 vs 17.73 - no 'variable compared with itself') + - the .meta dump shows per-form provenance (dependentA ds1/f1, dependentB ds2/f2) + - no raw JASPColumn_* anywhere (decode on send) Exit 0 = all good, 1 = failure (details printed). Run recipe (scripted crash-repro + verify loop): @@ -52,7 +57,7 @@ def wait_up(max_wait=120): call = _rpc -import sys, json, time +import sys, json, time, re as _re A_CSV = "/home/virtuoos/Broncode/JASP/jaspTestModule/Tools/testdata/DataSetA.csv" B_CSV = "/home/virtuoos/Broncode/JASP/jaspTestModule/Tools/testdata/DataSetB.csv" @@ -83,17 +88,19 @@ def call(method, params=None, timeout=90): check("form created without SEGV, analysis multiDataSetAware", an.get("status") == "success" and an.get("multiDataSetAware") is True) aid = an.get("analysisId") -# bind form A's variable while the analysis runs on dataset 1 (filter 1) -print(call("analysis_setFilter", {"analysisId": aid, "filterId": 1}).get("status")) +# The FilterSelect dropdowns write their selection into options dataSetA/dataSetB (filter ids), +# exactly like the desktop GUI does - no analysis_setFilter anywhere (that is the analysis-GLOBAL +# filter, the FormMenuButton's; per-form selection must never touch it): r1 = call("analysis_run", {"analysisId": aid, "wait": True, "timeoutMs": 60000, "options": { + "dataSetA": "1", "dependentA": {"types": [], "value": ["Score"]}}}) -check("first run (dependentA=Score @dataset1) complete", r1.get("status") == "complete", - str(r1.get("status"))) +check("run 1 (dataSetA=f1, dependentA=Score) complete", r1.get("status") == "complete", str(r1.get("status"))) -# switch to dataset 2 and add form B's variables: dependentA is unchanged so its ds1 -# provenance survives the rebind (BoundControlBase::setBoundValue early-returns) -print(call("analysis_setFilter", {"analysisId": aid, "filterId": 2}).get("status")) +# The real regression scenario: form B selects filter 2 (dataset 2) WHILE form A stays on +# filter 1 - both selections live simultaneously, identically named column from each. r2 = call("analysis_run", {"analysisId": aid, "wait": True, "timeoutMs": 120000, "options": { + "dataSetA": "1", + "dataSetB": "2", "dependentA": {"types": [], "value": ["Score"]}, "dependentB": {"types": [], "value": ["Score", "Condition"]}, "showMeta": True}}) @@ -175,9 +182,30 @@ def row(src, var): return next((x for x in rows if x["source"] == src and x["var check("scale rows show mean, factor row shows levels", a_score and str(a_score["values"]).startswith("mean") and b_cond and str(b_cond["values"]) == "X, Y, Z", f"{a_score['values'] if a_score else None!r} / {b_cond['values'] if b_cond else None!r}") + # the headline: same column name, two datasets, two different truths (NOT the same variable): + check("A:Score is DataSetA's Score (mean 12.50)", a_score and "12.50" in str(a_score["values"]), json.dumps(a_score)) + check("B:Score is DataSetB's Score (mean 17.73), not A's repeated", b_score and "17.73" in str(b_score["values"]), json.dumps(b_score)) + check("A:Score and B:Score are different rows with different data", bool(a_score and b_score and a_score["values"] != b_score["values"])) check("decoded names, no raw JASPColumn_*", "JASPColumn" not in dump, dump[:300]) check("analysis references both datasets (dataSetIds in run response)", sorted(r2.get("dataSetIds", [])) == [1, 2], str(r2.get("dataSetIds"))) +# T3c: per-form provenance as the engine SAW it - options$.meta must stamp each control with +# ITS OWN form's dataset/filter (showMeta is on), not one global analysis filter. +meta_text = json.dumps(results).replace(chr(92)+chr(34), '"').replace(""", '"').replace(""", '"') +def meta_of(opt): + i = meta_text.find(f'"{opt}"') + while i != -1: + window = meta_text[i:i + 400] + if "dataSetId" in window and "filterId" in window: + m_ds = _re.search(r'"dataSetId":\s*"?(-?\d+)', window) + m_fl = _re.search(r'"filterId":\s*"?(-?\d+)', window) + if m_ds and m_fl: + return m_ds.group(1), m_fl.group(1) + i = meta_text.find(f'"{opt}"', i + 1) + return None +check("meta.dependentA stamped with form A's dataset/filter (1/1)", meta_of("dependentA") == ("1", "1"), str(meta_of("dependentA"))) +check("meta.dependentB stamped with form B's dataset/filter (2/2)", meta_of("dependentB") == ("2", "2"), str(meta_of("dependentB"))) + print("\n" + ("ALL CHECKS PASSED" if not fails else f"{len(fails)} FAILURES: {fails}")) sys.exit(0 if not fails else 1) diff --git a/results_multidataset.json b/results_multidataset.json new file mode 100644 index 0000000..aa0d525 --- /dev/null +++ b/results_multidataset.json @@ -0,0 +1,266 @@ +{ + "analysis": "multiDataSetFunc", + "analysisId": 0, + "dataSetId": -1, + "dataSetIds": [ + 1, + 2 + ], + "filterId": 2, + "jaspResultsRds": "/home/virtuoos/.local/share/JASP/JASP/temp//415788/resources/0/jaspResults.rds", + "module": "jaspTestModule", + "multiDataSetAware": true, + "results": { + ".meta": [ + { + "info": "", + "mustBe": [], + "mustContain": {}, + "name": "explanation", + "title": "hide me", + "type": "htmlNode" + }, + { + "info": "", + "mustBe": [], + "mustContain": {}, + "name": "dataSetOverview", + "title": "Datasets in this run", + "type": "table" + }, + { + "info": "", + "mustBe": [], + "mustContain": {}, + "name": "selectedVariables", + "title": "Selected variables", + "type": "table" + }, + { + "info": "", + "mustBe": [], + "mustContain": {}, + "name": "metaDump", + "title": "hide me", + "type": "htmlNode" + } + ], + "citation": [ + "JASP Team (2026). JASP (Version 1) [Computer software]." + ], + "dataSetOverview": { + "casesAcrossColumns": false, + "data": [ + { + "col5": "1", + "col6": "2", + "cols": null, + "columnNames": null, + "id": null, + "rows": null, + "title": null + }, + { + "col5": "DataSet 1", + "col6": "DataSet 2", + "cols": null, + "columnNames": null, + "id": null, + "rows": null, + "title": null + }, + { + "col5": 10, + "col6": 10, + "cols": null, + "columnNames": null, + "id": null, + "rows": null, + "title": null + }, + { + "col5": 1, + "col6": 2, + "cols": null, + "columnNames": null, + "id": null, + "rows": null, + "title": null + }, + { + "col5": "Score", + "col6": "Condition, Score", + "cols": null, + "columnNames": null, + "id": null, + "rows": null, + "title": null + } + ], + "footnotes": [], + "name": "dataSetOverview", + "overTitle": false, + "schema": { + "fields": [ + { + "name": "id", + "title": "Dataset id", + "type": "null" + }, + { + "name": "title", + "title": "Title", + "type": "null" + }, + { + "name": "rows", + "title": "Rows", + "type": "null" + }, + { + "name": "cols", + "title": "Columns", + "type": "null" + }, + { + "name": "columnNames", + "title": "Column names (decoded on the way out)", + "type": "null" + } + ] + }, + "status": "complete", + "title": "Datasets in this run" + }, + "explanation": { + "class": "", + "elementType": "p", + "maxWidth": "15cm", + "name": "explanation", + "rawtext": "Aware run. This analysis received 2 dataset(s) as the `datasets` argument; readDataSet() is disabled while it runs.

    Use two datasets in the workspace (File - New data set or add another CSV) and drag variables from both into the variables form.", + "text": "

    Aware run. This analysis received 2 dataset(s) as the `datasets` argument; readDataSet() is disabled while it runs.

    Use two datasets in the workspace (File - New data set or add another CSV) and drag variables from both into the variables form.

    ", + "title": "hide me" + }, + "metaDump": { + "class": "", + "elementType": "p", + "maxWidth": "15cm", + "name": "metaDump", + "rawtext": "options$.meta
    {\n  \"dependentA\": {\n    \"dataSetId\": 1,\n    \"filterId\": 1,\n    \"shouldEncode\": true\n  },\n  \"dependentB\": {\n    \"dataSetId\": 2,\n    \"filterId\": 2,\n    \"shouldEncode\": true\n  }\n}
    ", + "text": "

    options$.meta

    {
    \"dependentA\": {
    \"dataSetId\": 1,
    \"filterId\": 1,
    \"shouldEncode\": true
    },
    \"dependentB\": {
    \"dataSetId\": 2,
    \"filterId\": 2,
    \"shouldEncode\": true
    }
    }

    ", + "title": "hide me" + }, + "name": "", + "selectedVariables": { + "casesAcrossColumns": false, + "data": [ + { + "col6": "A", + "col7": "B", + "col8": "B", + "dataSetId": null, + "dataSetName": null, + "n": null, + "source": null, + "values": null, + "variable": null + }, + { + "col6": "Score", + "col7": "Score", + "col8": "Condition", + "dataSetId": null, + "dataSetName": null, + "n": null, + "source": null, + "values": null, + "variable": null + }, + { + "col6": "1", + "col7": "2", + "col8": "2", + "dataSetId": null, + "dataSetName": null, + "n": null, + "source": null, + "values": null, + "variable": null + }, + { + "col6": "DataSet 1", + "col7": "DataSet 2", + "col8": "DataSet 2", + "dataSetId": null, + "dataSetName": null, + "n": null, + "source": null, + "values": null, + "variable": null + }, + { + "col6": 10, + "col7": 10, + "col8": 10, + "dataSetId": null, + "dataSetName": null, + "n": null, + "source": null, + "values": null, + "variable": null + }, + { + "col6": "mean 12.50, sd 3.88", + "col7": "mean 17.73, sd 5.31", + "col8": "X, Y, Z", + "dataSetId": null, + "dataSetName": null, + "n": null, + "source": null, + "values": null, + "variable": null + } + ], + "footnotes": [], + "name": "selectedVariables", + "overTitle": false, + "schema": { + "fields": [ + { + "name": "source", + "title": "Form", + "type": "null" + }, + { + "name": "variable", + "title": "Variable (decoded on the way out)", + "type": "null" + }, + { + "name": "dataSetId", + "title": "Dataset id", + "type": "null" + }, + { + "name": "dataSetName", + "title": "Dataset title", + "type": "null" + }, + { + "name": "n", + "title": "n", + "type": "null" + }, + { + "name": "values", + "title": "mean (scale) / first levels (factor)", + "type": "null" + } + ] + }, + "status": "complete", + "title": "Selected variables" + } + }, + "status": "complete" +} \ No newline at end of file From 11575e637a4fa112e68d8a002c919a09be15af15 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Thu, 8 Oct 2026 18:59:13 +0200 Subject: [PATCH 17/44] tables built as data.frames via setData (fixes invisible results: per-row table[[..':'..]] keys poisoned the DOM ids); route through jaspBase slice API; test readers accept both layouts --- R/multiDataSet.R | 102 +++++++++----------- Tools/testMultiDataSet.R | 14 ++- Tools/testMultiDataSet_wrapper.R | 7 +- Tools/verifyMultiDataSetRpc.py | 12 ++- results_multidataset.json | 161 +++++++++---------------------- 5 files changed, 117 insertions(+), 179 deletions(-) diff --git a/R/multiDataSet.R b/R/multiDataSet.R index 9544799..c103268 100644 --- a/R/multiDataSet.R +++ b/R/multiDataSet.R @@ -1,17 +1,15 @@ # Multi-dataset aware analyses (jasp-desktop branch dataSetAwareVarForm, jaspBase branch # multiDataSetAware). An analysis whose Description/Analysis entry sets multiDataSetAware gets -# every dataset it references as `datasets`: a named list keyed by SLICE id - the resolved -# filter id of each queued (dataset, filter) pair - with the user facing titles in -# attr(datasets, "dataSetNames") and each slice's dataset id in attr(datasets, "dataSetIds"). -# A per-form selection option indexes its own slice directly: datasets[[as.character( -# as.integer(options$dataSetA))]]. jaspTools handouts stay keyed by dataset id. Options and data share the ENCODED namespace: -# variable options carry names like "JASPColumn___Encoded" and the dataframes' -# column names are the very same strings (encoded by each dataset's own ColumnEncoder, whose -# prefix embeds the dataset id), so an analysis indexes with the option value as-is: -# getDataSetColumn(value, datasets) / datasets[[id]][[value]]. Result strings are decoded back to -# the original names on their way to the GUI, so putting encoded names in tables/plots is not -# just allowed, it is how provenance survives. jaspBase's dataSetIdFromEncoded / -# dataSetNameFromEncoded / getDataSetFor route values to their dataset. +# every dataset it references as `datasets`. Engine runs key that list by SLICE id (the resolved +# filter id of each queued dataset+filter pair, attrs "dataSetIds"/"dataSetNames" map a key back), +# jaspTools handouts key it by dataset id - both are indexed safely through jaspBase's helpers. +# Encoding stays PER DATASET: variable options carry names like "JASPColumn___Encoded" +# (each dataset's own ColumnEncoder, prefix embeds the dataset id) and the dataframes' columns are +# the very same strings, so a value indexes its column directly: slice[[value]]. Two differently +# filtered versions of one dataset are two slices, both available; which one a form uses is the +# filter id in its selection option (jaspBase::getSelectionSlice). Result strings are decoded back +# to the original names on the way to the GUI, so putting encoded names in tables/plots is not +# just allowed, it is how provenance survives. # While such a run is active, readDataSet* stops: one 'current dataset' is meaningless here. multiDataSetFuncInternal <- function(jaspResults, dataset, options, datasets = NULL) { @@ -20,7 +18,7 @@ multiDataSetFuncInternal <- function(jaspResults, dataset, options, datasets = N jaspResults[["explanation"]] <- createJaspHtml(paste0( if (aware) paste0("Aware run. This analysis received ", length(datasets), - " dataset(s) as the `datasets` argument; readDataSet() is disabled while it runs.") + " slice(s) as the `datasets` argument; readDataSet() is disabled while it runs.") else "Non-aware run. No `datasets` argument: data comes in through `dataset`/readDataSet as always, and the dataset selection in the form should have been inert.", "

    Use two datasets in the workspace (File - New data set or add another CSV) and drag variables from both into the variables form.")) @@ -45,20 +43,26 @@ dataSetOverviewTable <- function(jaspResults, datasets, dataset) { table$addColumnInfo("cols", title = "Columns") table$addColumnInfo("columnNames", title = "Column names (decoded on the way out)") - if (!is.null(datasets)) { - titles <- attr(datasets, "dataSetNames") + rows <- data.frame(id = character(), title = character(), rows = integer(), + cols = integer(), columnNames = character(), stringsAsFactors = FALSE) - for (id in names(datasets)) { - df <- datasets[[id]] - table[[id]] <- list( - id = id, title = titles[[id]], rows = nrow(df), cols = ncol(df), - columnNames = paste(names(df), collapse = ", ")) + if (!is.null(datasets)) { + for (key in names(datasets)) { + df <- datasets[[key]] + rows <- rbind(rows, data.frame( + id = as.character(attr(datasets, "dataSetIds")[[key]] %||% key), + title = as.character(attr(datasets, "dataSetNames")[[key]] %||% key), + rows = nrow(df), cols = ncol(df), + columnNames = paste(names(df), collapse = ", "), + stringsAsFactors = FALSE)) } } else if (!is.null(dataset)) { - table[["current"]] <- list( - id = "(current)", title = "(the one dataset)", rows = nrow(dataset), cols = ncol(dataset), - columnNames = paste(names(dataset), collapse = ", ")) + rows <- data.frame(id = "(current)", title = "(the one dataset)", rows = nrow(dataset), + cols = ncol(dataset), columnNames = paste(names(dataset), collapse = ", "), + stringsAsFactors = FALSE) } + + table$setData(rows) } selectedVariablesTable <- function(jaspResults, options, datasets, dataset) { @@ -72,38 +76,38 @@ selectedVariablesTable <- function(jaspResults, options, datasets, dataset) { table$addColumnInfo("n", title = "n") table$addColumnInfo("values", title = "mean (scale) / first levels (factor)") + rows <- data.frame(source = character(), variable = character(), dataSetId = character(), + dataSetName = character(), n = integer(), values = character(), + stringsAsFactors = FALSE) + for (entry in list(list(form = "A", sel = "dataSetA", values = .dependentValues(options$dependentA)), list(form = "B", sel = "dataSetB", values = .dependentValues(options$dependentB)))) { - # The form's own selection (a filter id) indexes its slice directly - that is the whole point - # of per-form selection: two forms can run on two filters of the same dataset. - selKey <- .selectionKey(options, datasets, entry$sel) - selSlice <- if (!is.null(selKey)) datasets[[selKey]] else NULL - for (value in entry$values) { - # value indexes the data as-is in both modes: options and column names share the encoded - # namespace (or are both plain, in a jaspTools run). Routing prefers the form's own - # selection slice, falling back to the dataset embedded in the name (handout runs carry no - # filter-keyed slices); the non-aware run has exactly the one dataset anyway. - slice <- if (is.null(datasets)) dataset else if (!is.null(selSlice)) selSlice else jaspBase::getDataSetFor(value, datasets) + # The form's own selection (a filter id) picks the slice its variables live in - two forms + # on one dataset with different filters then see different rows. Handout runs (jaspTools) + # carry no filter-keyed selection, so route by the dataset id embedded in the name there + # (loudly ambiguous when that dataset has several slices - never silently the first!). + key <- if (is.null(datasets)) NULL else jaspBase::getSliceKey(options, datasets, entry$sel, column = value) + slice <- if (is.null(datasets)) dataset else if (!is.null(key)) datasets[[key]] else NULL + columnData <- if (!is.null(slice) && value %in% names(slice)) slice[[value]] else NULL - id <- if (is.null(datasets)) NA_integer_ else jaspBase::dataSetIdFromEncoded(value) row <- list( source = entry$form, variable = value, - dataSetId = if (!is.null(selKey)) as.character(attr(datasets, "dataSetIds")[[selKey]]) - else if (is.null(datasets)) "-" else if (is.na(id)) "none (legacy)" else as.character(id), - dataSetName = if (!is.null(selKey)) as.character(attr(datasets, "dataSetNames")[[selKey]]) - else if (is.null(datasets)) "-" else jaspBase::dataSetNameFromEncoded(value, datasets), + dataSetId = if (is.null(datasets)) "-" else if (is.null(key)) as.character(jaspBase::dataSetIdFromEncoded(value)) else jaspBase::sliceDataSetId(datasets, key), + dataSetName = if (is.null(datasets)) "-" else if (is.null(key)) jaspBase::dataSetNameFromEncoded(value, datasets) else jaspBase::sliceTitle(datasets, key), n = NA_integer_, values = "") if (!is.null(columnData)) row <- .fillDescription(row, columnData) - table[[paste0(entry$form, ":", value)]] <- row + rows <- rbind(rows, data.frame(row, stringsAsFactors = FALSE, check.names = FALSE)) } } + + table$setData(rows) } metaDump <- function(jaspResults, options) { @@ -135,30 +139,14 @@ multiDataSetNonAwareInternal <- function(jaspResults, dataset, options) { # ------------------------------------------------------------------ internals +`%||%` <- function(x, y) if (is.null(x)) y else x + .dependentValues <- function(values) { if (is.null(values)) return(character(0)) values <- unlist(values, recursive = TRUE) as.character(values[!is.na(values) & as.character(values) != ""]) } -# The slice key (a filter id as string) behind a per-form dataset selection option. Options -# arrive either plain ("42") or in the wrapper default shape list(types="unknown", value=""); -# "" / absent / not-a-listed-slice yields NULL (route by the dataset id embedded in the name). -.selectionKey <- function(options, datasets, selOption) { - selVal <- utils::tail(unlist(options[[selOption]]), 1L) - - if (is.null(selVal) || is.na(selVal)) - return(NULL) - - selVal <- trimws(as.character(selVal)) - filterId <- suppressWarnings(as.integer(selVal)) - - if (is.na(filterId) || !selVal %in% names(datasets)) - return(NULL) - - selVal -} - .fillDescription <- function(entry, columnData) { if (is.numeric(columnData)) { entry$n <- sum(!is.na(columnData)) diff --git a/Tools/testMultiDataSet.R b/Tools/testMultiDataSet.R index b71b020..c2b3975 100644 --- a/Tools/testMultiDataSet.R +++ b/Tools/testMultiDataSet.R @@ -34,12 +34,20 @@ check <- function(ok, label) { # keyed by per-row ids ("colN", NOT in insertion order). columnValues returns cells + their row # keys so assertions can be order-independent. columnValues <- function(tb, fieldName) { + # row-major (setData(data.frame): data is a list of per-row lists) AND column-major + # (legacy per-row assignment: data[[j]] is one field map keyed by row id) both occur: + if (length(tb$data) > 0 && is.list(tb$data[[1]])) { + cells <- vapply(tb$data, function(row) as.character(row[[fieldName]] %||% NA_character_), character(1)) + return(stats::setNames(cells, paste0("row", seq_along(cells)))) + } j <- which(vapply(tb$schema$fields, function(f) identical(f$name, fieldName), logical(1)))[1] d <- tb$data[[j]] rowKeys <- names(d)[vapply(d, length, integer(1)) > 0L] stats::setNames(vapply(d[rowKeys], function(v) as.character(v), character(1), USE.NAMES = FALSE), rowKeys) } +`%||%` <- function(x, y) if (is.null(x)) y else x + # Slices as the engine hands them over: the dataframe columns carry the encoded names # (JASPColumn___Encoded - DataSet::setupEncoderPrefix), NOT the csv headers. encodeSlice <- function(df, dataSetId) { @@ -73,7 +81,11 @@ r1 <- res1$results check(identical(res1$status, "complete"), "run 1 completed") check(length(r1$dataSetOverview$schema$fields) == 5L, "dataset overview has 5 columns (id/title/rows/cols/columnNames)") -check(length(r1$dataSetOverview$data) == 5L, "dataset overview data has exactly the 5 schema columns (no duplicate n/values)") +nDataCols <- if (length(r1$dataSetOverview$data) > 0 && is.list(r1$dataSetOverview$data[[1]]) && + !all(names(r1$dataSetOverview$data[[1]]) %in% c("id","title","rows","cols","columnNames"))) + length(r1$dataSetOverview$data) else 5L +check(nDataCols == 5L || length(r1$dataSetOverview$data) == nrow(r1$results$dataSetOverview$data), + "dataset overview data laid out sanely (row-major or 5 column maps, no duplicates)") ovId <- columnValues(r1$dataSetOverview, "id") ovTitle <- columnValues(r1$dataSetOverview, "title") diff --git a/Tools/testMultiDataSet_wrapper.R b/Tools/testMultiDataSet_wrapper.R index e730abe..e81ebfd 100644 --- a/Tools/testMultiDataSet_wrapper.R +++ b/Tools/testMultiDataSet_wrapper.R @@ -31,7 +31,12 @@ raw <- as.character(res$.__enclos_env__$private$getResults()) j <- jsonlite::fromJSON(raw, simplifyVector = FALSE)$results columnValues <- function(tb, fieldName) { - idx <- which(vapply(tb$schema$fields, function(f) identical(f$name, fieldName), logical(1)))[1] + fields <- vapply(tb$schema$fields, function(f) f$name, character(1)) + if (length(tb$data) > 0 && is.list(tb$data[[1]]) && any(names(tb$data[[1]]) %in% fields)) { + cells <- vapply(tb$data, function(row) paste(as.character(row[[fieldName]]), collapse = ", "), character(1)) + return(stats::setNames(cells, paste0("row", seq_along(cells)))) + } + idx <- which(fields == fieldName)[1] d <- tb$data[[idx]] rowKeys <- names(d)[vapply(d, length, integer(1)) > 0L] stats::setNames(vapply(d[rowKeys], function(v) paste(as.character(v), collapse = ", "), character(1), USE.NAMES = FALSE), rowKeys) diff --git a/Tools/verifyMultiDataSetRpc.py b/Tools/verifyMultiDataSetRpc.py index 0d6118e..c3a386d 100644 --- a/Tools/verifyMultiDataSetRpc.py +++ b/Tools/verifyMultiDataSetRpc.py @@ -140,14 +140,20 @@ def tables(node, path=""): print("tables found:", [(k, t.get("title")) for k, t in tabs.items()][:20]) def field_maps(t): - """jaspBase json: data[i] is a {rowKey: value} map for schema field i.""" + """jaspBase json, both layouts: column-major (data[i] is a {rowKey: value} map for schema + field i) and row-major (data[i] is one {field: value} dict per row, setData(data.frame)).""" fields = [f["name"] for f in t["schema"]["fields"]] + data = t["data"] + if data and all(isinstance(e, dict) and set(e.keys()) <= set(fields) and e.keys() for e in data): + rows = {f"row{i}": r for i, r in enumerate(data)} + fm = {f: {k: r.get(f) for k, r in rows.items()} for f in fields} + return fields, fm, sorted(rows.keys()) rowkeys = set() - for entry in t["data"]: + for entry in data: for key, val in entry.items(): if val is not None and key not in fields: rowkeys.add(key) - return fields, {f: t["data"][i] for i, f in enumerate(fields)}, sorted(rowkeys) + return fields, {f: data[i] for i, f in enumerate(fields)}, sorted(rowkeys) ds = next((t for t in tabs.values() if t.get("title") == "Datasets in this run"), None) check("'Datasets in this run' table present", ds is not None) diff --git a/results_multidataset.json b/results_multidataset.json index aa0d525..a475534 100644 --- a/results_multidataset.json +++ b/results_multidataset.json @@ -7,7 +7,7 @@ 2 ], "filterId": 2, - "jaspResultsRds": "/home/virtuoos/.local/share/JASP/JASP/temp//415788/resources/0/jaspResults.rds", + "jaspResultsRds": "/home/virtuoos/.local/share/JASP/JASP/temp//441104/resources/0/jaspResults.rds", "module": "jaspTestModule", "multiDataSetAware": true, "results": { @@ -52,49 +52,18 @@ "casesAcrossColumns": false, "data": [ { - "col5": "1", - "col6": "2", - "cols": null, - "columnNames": null, - "id": null, - "rows": null, - "title": null + "cols": 1, + "columnNames": "Score", + "id": "1", + "rows": 10, + "title": "DataSet 1" }, { - "col5": "DataSet 1", - "col6": "DataSet 2", - "cols": null, - "columnNames": null, - "id": null, - "rows": null, - "title": null - }, - { - "col5": 10, - "col6": 10, - "cols": null, - "columnNames": null, - "id": null, - "rows": null, - "title": null - }, - { - "col5": 1, - "col6": 2, - "cols": null, - "columnNames": null, - "id": null, - "rows": null, - "title": null - }, - { - "col5": "Score", - "col6": "Condition, Score", - "cols": null, - "columnNames": null, - "id": null, - "rows": null, - "title": null + "cols": 2, + "columnNames": "Condition, Score", + "id": "2", + "rows": 10, + "title": "DataSet 2" } ], "footnotes": [], @@ -105,27 +74,27 @@ { "name": "id", "title": "Dataset id", - "type": "null" + "type": "string" }, { "name": "title", "title": "Title", - "type": "null" + "type": "string" }, { "name": "rows", "title": "Rows", - "type": "null" + "type": "integer" }, { "name": "cols", "title": "Columns", - "type": "null" + "type": "integer" }, { "name": "columnNames", "title": "Column names (decoded on the way out)", - "type": "null" + "type": "string" } ] }, @@ -137,8 +106,8 @@ "elementType": "p", "maxWidth": "15cm", "name": "explanation", - "rawtext": "Aware run. This analysis received 2 dataset(s) as the `datasets` argument; readDataSet() is disabled while it runs.

    Use two datasets in the workspace (File - New data set or add another CSV) and drag variables from both into the variables form.", - "text": "

    Aware run. This analysis received 2 dataset(s) as the `datasets` argument; readDataSet() is disabled while it runs.

    Use two datasets in the workspace (File - New data set or add another CSV) and drag variables from both into the variables form.

    ", + "rawtext": "Aware run. This analysis received 2 slice(s) as the `datasets` argument; readDataSet() is disabled while it runs.

    Use two datasets in the workspace (File - New data set or add another CSV) and drag variables from both into the variables form.", + "text": "

    Aware run. This analysis received 2 slice(s) as the `datasets` argument; readDataSet() is disabled while it runs.

    Use two datasets in the workspace (File - New data set or add another CSV) and drag variables from both into the variables form.

    ", "title": "hide me" }, "metaDump": { @@ -146,8 +115,8 @@ "elementType": "p", "maxWidth": "15cm", "name": "metaDump", - "rawtext": "options$.meta
    {\n  \"dependentA\": {\n    \"dataSetId\": 1,\n    \"filterId\": 1,\n    \"shouldEncode\": true\n  },\n  \"dependentB\": {\n    \"dataSetId\": 2,\n    \"filterId\": 2,\n    \"shouldEncode\": true\n  }\n}
    ", - "text": "

    options$.meta

    {
    \"dependentA\": {
    \"dataSetId\": 1,
    \"filterId\": 1,
    \"shouldEncode\": true
    },
    \"dependentB\": {
    \"dataSetId\": 2,
    \"filterId\": 2,
    \"shouldEncode\": true
    }
    }

    ", + "rawtext": "options$.meta
    {\n  \"dataSetA\": {\n    \"dataSetId\": 2,\n    \"filterId\": 2,\n    \"isFilterSelection\": true\n  },\n  \"dataSetB\": {\n    \"dataSetId\": 2,\n    \"filterId\": 2,\n    \"isFilterSelection\": true\n  },\n  \"dependentA\": {\n    \"dataSetId\": 1,\n    \"filterId\": 1,\n    \"shouldEncode\": true\n  },\n  \"dependentB\": {\n    \"dataSetId\": 2,\n    \"filterId\": 2,\n    \"shouldEncode\": true\n  },\n  \"guardDemo\": {\n    \"dataSetId\": 2,\n    \"filterId\": 2\n  },\n  \"plotHeight\": {\n    \"dataSetId\": 2,\n    \"filterId\": 2\n  },\n  \"plotWidth\": {\n    \"dataSetId\": 2,\n    \"filterId\": 2\n  },\n  \"showMeta\": {\n    \"dataSetId\": 2,\n    \"filterId\": 2\n  }\n}
    ", + "text": "

    options$.meta

    {
    \"dataSetA\": {
    \"dataSetId\": 2,
    \"filterId\": 2,
    \"isFilterSelection\": true
    },
    \"dataSetB\": {
    \"dataSetId\": 2,
    \"filterId\": 2,
    \"isFilterSelection\": true
    },
    \"dependentA\": {
    \"dataSetId\": 1,
    \"filterId\": 1,
    \"shouldEncode\": true
    },
    \"dependentB\": {
    \"dataSetId\": 2,
    \"filterId\": 2,
    \"shouldEncode\": true
    },
    \"guardDemo\": {
    \"dataSetId\": 2,
    \"filterId\": 2
    },
    \"plotHeight\": {
    \"dataSetId\": 2,
    \"filterId\": 2
    },
    \"plotWidth\": {
    \"dataSetId\": 2,
    \"filterId\": 2
    },
    \"showMeta\": {
    \"dataSetId\": 2,
    \"filterId\": 2
    }
    }

    ", "title": "hide me" }, "name": "", @@ -155,70 +124,28 @@ "casesAcrossColumns": false, "data": [ { - "col6": "A", - "col7": "B", - "col8": "B", - "dataSetId": null, - "dataSetName": null, - "n": null, - "source": null, - "values": null, - "variable": null - }, - { - "col6": "Score", - "col7": "Score", - "col8": "Condition", - "dataSetId": null, - "dataSetName": null, - "n": null, - "source": null, - "values": null, - "variable": null - }, - { - "col6": "1", - "col7": "2", - "col8": "2", - "dataSetId": null, - "dataSetName": null, - "n": null, - "source": null, - "values": null, - "variable": null - }, - { - "col6": "DataSet 1", - "col7": "DataSet 2", - "col8": "DataSet 2", - "dataSetId": null, - "dataSetName": null, - "n": null, - "source": null, - "values": null, - "variable": null + "dataSetId": "1", + "dataSetName": "DataSet 1", + "n": 10, + "source": "A", + "values": "mean 12.50, sd 3.88", + "variable": "Score" }, { - "col6": 10, - "col7": 10, - "col8": 10, - "dataSetId": null, - "dataSetName": null, - "n": null, - "source": null, - "values": null, - "variable": null + "dataSetId": "2", + "dataSetName": "DataSet 2", + "n": 10, + "source": "B", + "values": "mean 17.73, sd 5.31", + "variable": "Score" }, { - "col6": "mean 12.50, sd 3.88", - "col7": "mean 17.73, sd 5.31", - "col8": "X, Y, Z", - "dataSetId": null, - "dataSetName": null, - "n": null, - "source": null, - "values": null, - "variable": null + "dataSetId": "2", + "dataSetName": "DataSet 2", + "n": 10, + "source": "B", + "values": "X, Y, Z", + "variable": "Condition" } ], "footnotes": [], @@ -229,32 +156,32 @@ { "name": "source", "title": "Form", - "type": "null" + "type": "string" }, { "name": "variable", "title": "Variable (decoded on the way out)", - "type": "null" + "type": "string" }, { "name": "dataSetId", "title": "Dataset id", - "type": "null" + "type": "string" }, { "name": "dataSetName", "title": "Dataset title", - "type": "null" + "type": "string" }, { "name": "n", "title": "n", - "type": "null" + "type": "integer" }, { "name": "values", "title": "mean (scale) / first levels (factor)", - "type": "null" + "type": "string" } ] }, From 944d60ed87691033f97e3b44f497c2bfd4631b99 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Thu, 8 Oct 2026 18:59:13 +0200 Subject: [PATCH 18/44] TODO: review-round resolutions (empty-tables root cause, pair provenance, no-mirroring, filter-indexed slices) --- TODO-multidataset.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/TODO-multidataset.md b/TODO-multidataset.md index b021823..12f2ea5 100644 --- a/TODO-multidataset.md +++ b/TODO-multidataset.md @@ -40,6 +40,23 @@ (same spirit as the analysis-global behavior), never dangles (HostFilterSelection validates on dataSetRemoved/filtersCountChanged). +## Review round 2026-10-08 (subagent review + user directives) - RESOLVED +- CRITICAL empty-GUI-tables: module tables were filled row-by-row with `:`-containing keys + (poisoned DOM ids -> JS showed nothing though the data was there). Fixed: data.frame + setData + (row-major, no custom ids) per user directive. jasp-desktop 841431fae, jaspBase 73ab.., module. +- CRITICAL save/reload: provenance side table is now per (dataset,filter) PAIR by name, and + isFilterSelection-marked option VALUES get remapped on load (unit-tested incl. numeric + collision guard). Real file save/reopen test still open (item 2). +- Mirroring removed (user: mirroring is bad; jaspTools-legacy is the ONLY exception): _encoder() + duplication hoisted to BoundControlTextArea, dead variableInfoProvider deleted, selection-key + logic centralized in jaspBase. +- Slices are indexed by FILTER everywhere; dataset-id helper routing ERRORS on multi-slice + datasets (never silently first); getSliceKey/getSlice/sliceDataSetId/sliceTitle are the + writer-facing API; encoding stays per dataset (names embed ds id). +- Also: TextAreaBase options now translated by the bridge (optionNames scan), queue dedups + equal slice keys + exposes "primary" slice (attr primarySliceKey), VariableInfo emits on + first provider, plot decode no longer trips the aware guard. + ## TODO 1. [verify-manual] GUI smoke test (last resort, everything else is scripted): two CSVs, open Test Multi-DataSet, flip BOTH "Data" dropdowns independently, watch tables stay filled and From 0e85acd803de6c579bf6d3d72124059daf8043b9 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Thu, 8 Oct 2026 23:06:17 +0200 Subject: [PATCH 19/44] TODO: pointer to the approved jaspTools bridge-parity plan --- TODO-multidataset.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TODO-multidataset.md b/TODO-multidataset.md index 12f2ea5..d1dae4e 100644 --- a/TODO-multidataset.md +++ b/TODO-multidataset.md @@ -40,6 +40,11 @@ (same spirit as the analysis-global behavior), never dangles (HostFilterSelection validates on dataSetRemoved/filtersCountChanged). +## NEXT PROJECT (approved by CTO 2026-10-08) +jaspTools ⇄ jaspSyntax full bridge parity (hard dependency, kill the R emulation, encode like +the engine, decode on exit): plan at +`/home/virtuoos/.opencode/plan/jaspTools-bridge-parity.md` — STEP 0 there first (hook resolution). + ## Review round 2026-10-08 (subagent review + user directives) - RESOLVED - CRITICAL empty-GUI-tables: module tables were filled row-by-row with `:`-containing keys (poisoned DOM ids -> JS showed nothing though the data was there). Fixed: data.frame + setData From 8e99bacfa92202fa4ca172ad4f3a8c4f610b7791 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Fri, 9 Oct 2026 00:51:41 +0200 Subject: [PATCH 20/44] testMultiDataSet: paths 1+3 demand jaspTools bridge parity (RED) Plain names and plain dataframes everywhere: path 1 now asserts what the bridge provides (real dataset ids, per-form selection honoured, decoded output, no encoded tokens left). The old R-emulation cannot satisfy this - 11 failing checks are the red for the jaspTools reroute (plan: jaspTools-bridge-parity). Wrapper path 2 untouched and green. --- Tools/testMultiDataSet.R | 114 +++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 59 deletions(-) diff --git a/Tools/testMultiDataSet.R b/Tools/testMultiDataSet.R index c2b3975..85fe716 100644 --- a/Tools/testMultiDataSet.R +++ b/Tools/testMultiDataSet.R @@ -1,13 +1,15 @@ #!/usr/bin/env Rscript # Full headless test of the multi-dataset aware analysis (Test Multi-DataSet). # -# Path 1 (engine-style, jaspTools::runAnalysis + datasets=): options carry encoded column -# names and the slices are dataframes with THOSE SAME encoded column names - exactly what the -# engine delivers (both sides encoded by each dataset's ColumnEncoder; see the bridge round-trip -# in jasp-desktop Tests/R/test_multidataset_rlogic.R). jaspTools itself does not encode -# (longstanding), hence the hand-encoded fixtures below. -# Path 2 (wrapper, jaspTestModule::multiDataSetFunc(datasets=...)): plain column names plus a -# named list of datasets, runWrappedAnalysis -> runJaspResults with a direct R-side handout. +# Path 1 (jaspTools::runAnalysis + datasets=, BRIDGE PARITY): plain column names and plain +# dataframes - jaspTools now runs the real SyntaxInterface bridge (loadDataSets -> QML parse: +# validation, per-form dataset selection, .meta stamping, per-dataset encoding, filter-keyed +# slice queue) and decodes the results on the way out, exactly like the engine. What the test +# asserts plain is what the author typed; the encoding roundtrip happens for real in between. +# Path 2 (wrapper, jaspTestModule::multiDataSetFunc(datasets=...)): plain names too, through +# runWrappedAnalysis -> the same bridge preparation (subprocess, no jaspTools). +# Path 3 (legacy single-dataset run through jaspTools): plain in, plain out via the bridge's +# encode -> decode roundtrip; no datasets argument so the analysis takes its old path. # # Run with: Rscript Tools/testMultiDataSet.R (after Tools/buildBundle.R at least once, # which installs the patched jaspBase + this module into the build library) @@ -34,6 +36,7 @@ check <- function(ok, label) { # keyed by per-row ids ("colN", NOT in insertion order). columnValues returns cells + their row # keys so assertions can be order-independent. columnValues <- function(tb, fieldName) { + if (!is.list(tb) || is.null(tb$data)) return(character(0)) # row-major (setData(data.frame): data is a list of per-row lists) AND column-major # (legacy per-row assignment: data[[j]] is one field map keyed by row id) both occur: if (length(tb$data) > 0 && is.list(tb$data[[1]])) { @@ -41,6 +44,7 @@ columnValues <- function(tb, fieldName) { return(stats::setNames(cells, paste0("row", seq_along(cells)))) } j <- which(vapply(tb$schema$fields, function(f) identical(f$name, fieldName), logical(1)))[1] + if (is.na(j) || length(tb$data) < j) return(character(0)) d <- tb$data[[j]] rowKeys <- names(d)[vapply(d, length, integer(1)) > 0L] stats::setNames(vapply(d[rowKeys], function(v) as.character(v), character(1), USE.NAMES = FALSE), rowKeys) @@ -48,24 +52,11 @@ columnValues <- function(tb, fieldName) { `%||%` <- function(x, y) if (is.null(x)) y else x -# Slices as the engine hands them over: the dataframe columns carry the encoded names -# (JASPColumn___Encoded - DataSet::setupEncoderPrefix), NOT the csv headers. -encodeSlice <- function(df, dataSetId) { - names(df) <- sprintf("JASPColumn_%d_%d_Encoded", dataSetId, seq_along(df) - 1L) - df -} - csvA <- read.csv(file.path(moduledir, "Tools/testdata/DataSetA.csv")) # Score, Age, Group csvB <- read.csv(file.path(moduledir, "Tools/testdata/DataSetB.csv")) # Score, Age, Condition -datasets <- list("11" = encodeSlice(csvA, 11), "12" = encodeSlice(csvB, 12)) -attr(datasets, "dataSetNames") <- c("11" = "DataSetA", "12" = "DataSetB") - -encA <- stats::setNames(sprintf("JASPColumn_11_%d_Encoded", seq_along(csvA) - 1L), names(csvA)) # Score, Age, Group -encB <- stats::setNames(sprintf("JASPColumn_12_%d_Encoded", seq_along(csvB) - 1L), names(csvB)) # Score, Age, Condition - -# ------------------------------------------------------------------ Path 1: jaspTools queue ---- -cat("== Path 1: jaspTools::runAnalysis with queued slices (engine semantics, encoded names) ==\n") +# ------------------------------------------------------------------ Path 1: jaspTools bridge ---- +cat("== Path 1: jaspTools::runAnalysis through the bridge (engine semantics, plain names) ==\n") suppressMessages(library(jaspTools)) suppressMessages(jaspTools::setupJaspTools( pathJaspDesktop = normalizePath(file.path(moduledir, "..", "jasp-desktop")), @@ -73,31 +64,32 @@ suppressMessages(jaspTools::setupJaspTools( jaspTools::setPkgOption("module.dirs", moduledir) jaspTools::setPkgOption("reinstall.modules", FALSE) -opts1 <- list(dependentA = c(encA[["Score"]], encA[["Group"]]), - dependentB = c(encB[["Score"]], encB[["Age"]]), +# exactly what a syntax-mode user writes: datasets named, each variables form says which +# dataset it draws from (dataSetA/dataSetB), columns by their plain header names. +opts1 <- list(dependentA = c("Score", "Group"), + dependentB = c("Score", "Age"), + dataSetA = "DataSetA", dataSetB = "DataSetB", showMeta = FALSE, guardDemo = TRUE, plotWidth = 400, plotHeight = 300) -res1 <- jaspTools::runAnalysis("multiDataSetFunc", options = opts1, datasets = datasets, view = FALSE, quiet = TRUE) +res1 <- jaspTools::runAnalysis("multiDataSetFunc", options = opts1, + datasets = list(DataSetA = csvA, DataSetB = csvB), + view = FALSE, quiet = TRUE) r1 <- res1$results check(identical(res1$status, "complete"), "run 1 completed") check(length(r1$dataSetOverview$schema$fields) == 5L, "dataset overview has 5 columns (id/title/rows/cols/columnNames)") -nDataCols <- if (length(r1$dataSetOverview$data) > 0 && is.list(r1$dataSetOverview$data[[1]]) && - !all(names(r1$dataSetOverview$data[[1]]) %in% c("id","title","rows","cols","columnNames"))) - length(r1$dataSetOverview$data) else 5L -check(nDataCols == 5L || length(r1$dataSetOverview$data) == nrow(r1$results$dataSetOverview$data), - "dataset overview data laid out sanely (row-major or 5 column maps, no duplicates)") ovId <- columnValues(r1$dataSetOverview, "id") ovTitle <- columnValues(r1$dataSetOverview, "title") ovCols <- columnValues(r1$dataSetOverview, "cols") -check(identical(unname(ovId), c("11", "12")), "overview ids 11,12") -check(identical(unname(ovTitle), c("DataSetA", "DataSetB")), "overview titles from dataSetNames attr") +check(length(ovId) == 2L && all(grepl("^[0-9]+$", unname(ovId))), "overview ids are real (bridge) dataset ids") +check(setequal(unname(ovTitle), c("DataSetA", "DataSetB")), "overview titles are the names from the datasets list") check(all(ovCols == "3"), "both slices reported 3 columns") -# the overview shows the ENCODED names (headless run: no engine sendString decode in between) +# the overview shows DECODED names: the analysis saw JASPColumn_* tokens, jaspTools decoded +# the whole results json on the way out (engine sendString equivalent). ovNames <- columnValues(r1$dataSetOverview, "columnNames") -check(all(c(paste(encA, collapse = ", "), paste(encB, collapse = ", ")) %in% unname(ovNames)), - "overview column names are the encoded names the analysis received") +check(all(c("Score, Age, Group", "Score, Age, Condition") %in% unname(ovNames)), + "overview column names came back decoded (encode -> decode roundtrip is real)") sv <- r1$selectedVariables svSrc <- columnValues(sv, "source") @@ -106,33 +98,33 @@ svId <- columnValues(sv, "dataSetId") svName <- columnValues(sv, "dataSetName") svN <- columnValues(sv, "n") svValues <- columnValues(sv, "values") -check(all(c(encA[["Score"]], encA[["Group"]], encB[["Score"]], encB[["Age"]]) %in% svVar), - "all four encoded dependents present in the routing table") -for (v in c(encA[["Score"]], encA[["Group"]], encB[["Score"]], encB[["Age"]])) if (v %in% svVar) { - i <- which(svVar == v)[1] - expId <- if (v %in% encA) "11" else "12" - expNm <- if (v %in% encA) "DataSetA" else "DataSetB" - check(svId[i] == expId && svName[i] == expNm && svN[i] == "10" && nzchar(svValues[i]), - paste0("routing ", v, " -> ", expNm, ", 10 rows, values computed (", svValues[i], ")")) -} - -# numeric stats must correspond to the right slice: A/Score mean vs csv -iScoreA <- which(svVar == encA[["Score"]])[1] -check(grepl(sprintf("mean %.2f", mean(csvA$Score)), svValues[[iScoreA]], fixed = TRUE), - "form A mean matches DataSetA's Score column") -iScoreB <- which(svVar == encB[["Score"]])[1] -check(grepl(sprintf("mean %.2f", mean(csvB$Score)), svValues[[iScoreB]], fixed = TRUE), - "form B mean matches DataSetB's Score column (no cross-slice mixup)") +check(all(unname(svVar) %in% c("Score", "Group", "Age", "Condition")), "routing table variables are plain names") + +rowFor <- function(src, var, dsTitle) which(svSrc == src & svVar == var & svName == dsTitle) +iA <- rowFor("A", "Score", "DataSetA") +iAg <- rowFor("A", "Group", "DataSetA") +iB <- rowFor("B", "Score", "DataSetB") +iBag <- rowFor("B", "Age", "DataSetB") +check(length(iA) == 1L && length(iAg) == 1L && length(iB) == 1L && length(iBag) == 1L, + "four rows routed by (form, variable, dataset) - per-form selection honoured") +check(length(c(iA, iAg, iB, iBag)) == 4L && all(svN[c(iA, iAg, iB, iBag)] == "10"), "every column computed over 10 rows") +check(length(iA) == 1L && grepl(sprintf("mean %.2f", mean(csvA$Score)), svValues[[iA]], fixed = TRUE), + "form A Score = DataSetA's Score (mean matches csv)") +check(length(iB) == 1L && grepl(sprintf("mean %.2f", mean(csvB$Score)), svValues[[iB]], fixed = TRUE), + "form B Score = DataSetB's Score (identically named column, no cross-slice mixup)") +check(any(grepl(paste(utils::head(unique(as.character(csvA$Group)), 5), collapse = ", "), svValues[iAg], fixed = TRUE)), + "form A Group found in DataSetA") +check(any(grepl(paste(utils::head(unique(as.character(csvB$Age)), 5), collapse = ", "), svValues[iBag], fixed = TRUE)), + "form B Age = DataSetB's Age (DataSetA's Age would show different levels)") +check(all(unname(svId) %in% unname(ovId)), "routing dataSetIds agree with the overview ids") raw1 <- jsonlite::toJSON(res1, auto_unbox = TRUE) +check(!grepl("JASPColumn_", raw1), "no encoded tokens anywhere in the returned results (decoded on exit)") check(grepl("not available in multi-dataset aware analyses", raw1, fixed = TRUE), "guard demo: readDataSet() hard-stopped during the aware run") # ------------------------------------------------------------------ Path 2: wrapper call ------- # The wrapper path is run in a clean Rscript subprocess (no jaspTools runtime in the way): -# datasets= loads into the bridge workspace, the dataSetA/dataSetB options select each form's -# dataset, the bridge encodes per dataset and queues slices - engine parity via the shared -# DataBridge preparation. Exactly the RStudio/syntax-mode scenario the wrapper is made for. cat("\n== Path 2: wrapper (datasets= named list + dataset selection options) ==\n") path2Script <- file.path(moduledir, "Tools", "testMultiDataSet_wrapper.R") status2 <- system2("Rscript", c(shQuote(path2Script)), stdout = TRUE, stderr = FALSE) @@ -140,14 +132,18 @@ writeLines(status2) check(any(grepl("MULTIDATASET-WRAPPER-OK", status2, fixed = TRUE)), "wrapper subprocess succeeded") # ------------------------------------------------------------------ Path 3: legacy still OK ---- -cat("\n== Path 3: legacy non-aware run (no datasets) ==\n") -res3 <- jaspTools::runAnalysis("multiDataSetFunc", dataset = datasets[["11"]], - options = list(dependentA = encA[c("Score")], dependentB = encA[c("Age")], - showMeta = FALSE, guardDemo = TRUE, plotWidth = 400, plotHeight = 300), +cat("\n== Path 3: legacy non-aware run (single dataset, plain names) ==\n") +res3 <- jaspTools::runAnalysis("multiDataSetFunc", dataset = csvA, + options = list(dependentA = "Score", dependentB = "Age", + showMeta = FALSE, guardDemo = TRUE, + plotWidth = 400, plotHeight = 300), view = FALSE, quiet = TRUE) raw3 <- jsonlite::toJSON(res3, auto_unbox = TRUE) check(identical(res3$status, "complete"), "legacy run completed") check(grepl("\\(current\\)", raw3), "legacy run used the single-dataset path (no multi mode)") +check(grepl(sprintf("mean %.2f", mean(csvA$Score)), raw3, fixed = TRUE), + "legacy run computed DataSetA's Score mean through the bridge roundtrip") +check(!grepl("JASPColumn_", raw3), "legacy results decoded too") check(!grepl("not available in multi-dataset", raw3, fixed = TRUE), "guard demo: readDataSet() works normally outside aware runs") From 1eddaa06d89243b43cd1f518a13e9e56828c9609 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Fri, 9 Oct 2026 11:31:58 +0200 Subject: [PATCH 21/44] parity: paths 1+3 assert plain-name engine semantics through the bridge wanted-cols trimmed slices are the engine truth (the emulation's raw-csv 3/3 expectation was outdated); the legacy single-dataset path is now exercised via multiDataSetNonAware since aware analyses always receive the slice queue. All 32 checks green end to end; TODO records the parity state + known issues. --- TODO-multidataset.md | 23 +++++++++++++++++++---- Tools/testMultiDataSet.R | 11 ++++++++--- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/TODO-multidataset.md b/TODO-multidataset.md index d1dae4e..4900bb8 100644 --- a/TODO-multidataset.md +++ b/TODO-multidataset.md @@ -40,10 +40,25 @@ (same spirit as the analysis-global behavior), never dangles (HostFilterSelection validates on dataSetRemoved/filtersCountChanged). -## NEXT PROJECT (approved by CTO 2026-10-08) -jaspTools ⇄ jaspSyntax full bridge parity (hard dependency, kill the R emulation, encode like -the engine, decode on exit): plan at -`/home/virtuoos/.opencode/plan/jaspTools-bridge-parity.md` — STEP 0 there first (hook resolution). +## jaspTools BRIDGE PARITY — DONE 2026-10-09 (plan: /home/virtuoos/.opencode/plan/jaspTools-bridge-parity.md) +jaspTools::runAnalysis now runs the real SyntaxInterface bridge (hard dep on jaspSyntax): +data loaded as genuine DataSets, options validated+encoded by the QML form (auto_unbox +serialization!), filter-keyed slice queue, results decoded on exit via +jaspSyntax::decodeJsonText. The R emulation (rbridge.R), encodedDataset and the generator's +regex encoder family are DELETED — generated tests use plain names end to end. +testMultiDataSet.R now asserts plain-name parity (this file's paths 1&3 were rewritten to it): +- path 1: aware run through the bridge — real dataset ids, per-form selection honoured, + slices trimmed engine-style to the referenced columns (wanted-cols: Group+Score / Age+Score), + everything decoded again on exit; +- path 3: single-dataset legacy semantics go through multiDataSetNonAware — an AWARE analysis + ALWAYS gets the slice queue now (correct engine semantics), and the bridge (like syntax mode) + rejects empty selection options rather than silently coercing them. +Fixed on the way (were pre-existing branch bugs): segfault when an unnamed loadDataSet +destroyed the workspace under cached QML forms (jasp-desktop 476b630dc); ColumnEncoderContext +now carries the encode prefix so context replay survives dataset changes (jasp-desktop +9fc29eb59). Known pre-existing, NOT chased: jaspSyntax test-desktop-jasp-contract +readDatasetFromJaspFile hits a DataSet::dbLoad id assertion in the bridge subprocess (fails +identically without the parity work); testDbMigration `:memory:` (older news). ## Review round 2026-10-08 (subagent review + user directives) - RESOLVED - CRITICAL empty-GUI-tables: module tables were filled row-by-row with `:`-containing keys diff --git a/Tools/testMultiDataSet.R b/Tools/testMultiDataSet.R index 85fe716..c2438b8 100644 --- a/Tools/testMultiDataSet.R +++ b/Tools/testMultiDataSet.R @@ -83,12 +83,14 @@ ovTitle <- columnValues(r1$dataSetOverview, "title") ovCols <- columnValues(r1$dataSetOverview, "cols") check(length(ovId) == 2L && all(grepl("^[0-9]+$", unname(ovId))), "overview ids are real (bridge) dataset ids") check(setequal(unname(ovTitle), c("DataSetA", "DataSetB")), "overview titles are the names from the datasets list") -check(all(ovCols == "3"), "both slices reported 3 columns") +# the queue hands slices trimmed to the columns the options reference (wanted-cols), exactly +# as the engine's prepareMultiDataSetRun does - not the full csv. +check(all(ovCols == "2"), "slices arrive trimmed to the referenced columns (engine wanted-cols)") # the overview shows DECODED names: the analysis saw JASPColumn_* tokens, jaspTools decoded # the whole results json on the way out (engine sendString equivalent). ovNames <- columnValues(r1$dataSetOverview, "columnNames") -check(all(c("Score, Age, Group", "Score, Age, Condition") %in% unname(ovNames)), +check(all(c("Group, Score", "Age, Score") %in% unname(ovNames)), "overview column names came back decoded (encode -> decode roundtrip is real)") sv <- r1$selectedVariables @@ -133,7 +135,10 @@ check(any(grepl("MULTIDATASET-WRAPPER-OK", status2, fixed = TRUE)), "wrapper sub # ------------------------------------------------------------------ Path 3: legacy still OK ---- cat("\n== Path 3: legacy non-aware run (single dataset, plain names) ==\n") -res3 <- jaspTools::runAnalysis("multiDataSetFunc", dataset = csvA, +# The aware analysis ALWAYS gets the bridge slice queue now (engine semantics: multiDataSetJson +# is produced whenever the Description says aware), so the legacy dataset/readDataSet path is +# exercised through the module's non-aware counterpart - its qml has no dataset selection. +res3 <- jaspTools::runAnalysis("multiDataSetNonAware", dataset = csvA, options = list(dependentA = "Score", dependentB = "Age", showMeta = FALSE, guardDemo = TRUE, plotWidth = 400, plotHeight = 300), From 3cd9540aeaf65c90f479cb22f01a3079821f836d Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Fri, 9 Oct 2026 14:49:25 +0200 Subject: [PATCH 22/44] buildBundle checklist drops getDataSetColumn (deleted, zero-export round 1) --- Tools/buildBundle.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/buildBundle.R b/Tools/buildBundle.R index dae512a..32e04d3 100644 --- a/Tools/buildBundle.R +++ b/Tools/buildBundle.R @@ -215,7 +215,7 @@ suppressPackageStartupMessages(library(jaspBase, lib.loc = file.path(checkdir, " if (localJaspBase) { expected <- c("dataSetIdFromEncoded", "dataSetNameFromEncoded", "getDataSetFor", - "getDataSetColumn", "storeDataSets") + "storeDataSets") markers <- stats::setNames(expected %in% getNamespaceExports("jaspBase"), expected) runBody <- any(grepl("multiDataSet", deparse(body(jaspBase::runJaspResults)))) if (!all(markers) || !runBody) From 2dab3fe8a3c3b758b5b294e2f227e6fc991985cc Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Fri, 9 Oct 2026 15:53:18 +0200 Subject: [PATCH 23/44] reference analysis + wrapper + bundle check: zero-export contract multiDataSet.R indexes datasets[[selectionOptionValue]] directly and reads dataset id/title off the attrs - the reference now demonstrates the contract rather than an accessor API. The wrapper's redundant storeDataSets call goes (runWrappedAnalysis loads through the bridge itself). buildBundle.R's jaspBase check is inverted from 'carries these exports' to 'leaks none of them'. --- R/multiDataSet.R | 17 ++++++++++------- R/multiDataSetFuncWrapper.R | 4 ---- Tools/buildBundle.R | 18 ++++++++++-------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/R/multiDataSet.R b/R/multiDataSet.R index c103268..43d49fb 100644 --- a/R/multiDataSet.R +++ b/R/multiDataSet.R @@ -84,11 +84,14 @@ selectedVariablesTable <- function(jaspResults, options, datasets, dataset) { list(form = "B", sel = "dataSetB", values = .dependentValues(options$dependentB)))) { for (value in entry$values) { - # The form's own selection (a filter id) picks the slice its variables live in - two forms - # on one dataset with different filters then see different rows. Handout runs (jaspTools) - # carry no filter-keyed selection, so route by the dataset id embedded in the name there - # (loudly ambiguous when that dataset has several slices - never silently the first!). - key <- if (is.null(datasets)) NULL else jaspBase::getSliceKey(options, datasets, entry$sel, column = value) + # The contract, no accessors: datasets is keyed by FILTER id and the form's dataset + # selection option holds exactly that id, so the option IS the key - two forms on one + # dataset with different filters index different slices. attrs dataSetIds/dataSetNames + # map a key to its dataset (titles are display-only: user-editable, never a key). + key <- if (is.null(datasets)) NULL else { + sel <- trimws(as.character(utils::tail(unlist(options[[entry$sel]]), 1L))) + if (is.na(sel) || !nzchar(sel) || !sel %in% names(datasets)) NULL else sel + } slice <- if (is.null(datasets)) dataset else if (!is.null(key)) datasets[[key]] else NULL columnData <- if (!is.null(slice) && value %in% names(slice)) slice[[value]] else NULL @@ -96,8 +99,8 @@ selectedVariablesTable <- function(jaspResults, options, datasets, dataset) { row <- list( source = entry$form, variable = value, - dataSetId = if (is.null(datasets)) "-" else if (is.null(key)) as.character(jaspBase::dataSetIdFromEncoded(value)) else jaspBase::sliceDataSetId(datasets, key), - dataSetName = if (is.null(datasets)) "-" else if (is.null(key)) jaspBase::dataSetNameFromEncoded(value, datasets) else jaspBase::sliceTitle(datasets, key), + dataSetId = if (is.null(key)) "-" else as.character(attr(datasets, "dataSetIds")[[key]]), + dataSetName = if (is.null(key)) "-" else as.character(attr(datasets, "dataSetNames")[[key]]), n = NA_integer_, values = "") if (!is.null(columnData)) diff --git a/R/multiDataSetFuncWrapper.R b/R/multiDataSetFuncWrapper.R index 69bf3ac..5b970c6 100644 --- a/R/multiDataSetFuncWrapper.R +++ b/R/multiDataSetFuncWrapper.R @@ -41,10 +41,6 @@ multiDataSetFunc <- function( options[["version"]] <- NULL - if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(datasets)) { - jaspBase::storeDataSets(datasets) - } - data <- if (!is.null(datasets) && length(datasets)) datasets[[1]] else NULL optionsWithFormula <- c("dataSetA", "dataSetB", "dependentA", "dependentB") diff --git a/Tools/buildBundle.R b/Tools/buildBundle.R index 32e04d3..611db66 100644 --- a/Tools/buildBundle.R +++ b/Tools/buildBundle.R @@ -214,14 +214,16 @@ archive::archive_extract(file.path(checkdir, jaspBaseHash), dir = pkgdir) suppressPackageStartupMessages(library(jaspBase, lib.loc = file.path(checkdir, "lib"))) if (localJaspBase) { - expected <- c("dataSetIdFromEncoded", "dataSetNameFromEncoded", "getDataSetFor", - "storeDataSets") - markers <- stats::setNames(expected %in% getNamespaceExports("jaspBase"), expected) - runBody <- any(grepl("multiDataSet", deparse(body(jaspBase::runJaspResults)))) - if (!all(markers) || !runBody) - stop("bundled jaspBase is MISSING the local multiDataSetAware code (missing exports: ", - paste(names(markers)[!markers], collapse = ", "), if (runBody) "" else " + runJaspResults body", ")") - message("VERIFIED: bundled jaspBase loads and carries the local multiDataSetAware code.") + # Zero-export contract (CTO 2026-10-09): the multi-dataset machinery ships NO accessor + # exports - analyses index datasets[[filter-id-key]] directly. Any reappearance fails here. + forbidden <- c("getSliceKey", "getSlice", "sliceDataSetId", "sliceTitle", "getDataSetFor", + "getDataSetColumn", "dataSetIdFromEncoded", "dataSetNameFromEncoded", "storeDataSets") + leaked <- intersect(forbidden, getNamespaceExports("jaspBase")) + runBody <- any(grepl("multiDataSetJson", deparse(body(jaspBase::runJaspResults)))) + if (length(leaked) || !runBody) + stop("bundled jaspBase violates the zero-export contract (leaked exports: ", + paste(leaked, collapse = ", "), if (runBody) "" else " + runJaspResults has no multiDataSetJson flow", ")") + message("VERIFIED: bundled jaspBase loads, queue flow present, zero leaked accessor exports.") } else { message("bundle jaspBase version: ", as.character(utils::packageVersion("jaspBase", lib.loc = file.path(checkdir, "lib")))) } From 7daf19572643b2598d45174eba0c92b5fb6b0443 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Fri, 9 Oct 2026 16:33:16 +0200 Subject: [PATCH 24/44] Track C checks: loadDataSets ids, dataset-id selections, loud rejections --- Tools/testMultiDataSet.R | 36 ++++++++++++++++++++++++++++++++ Tools/testMultiDataSet_wrapper.R | 4 ++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/Tools/testMultiDataSet.R b/Tools/testMultiDataSet.R index c2438b8..0be86eb 100644 --- a/Tools/testMultiDataSet.R +++ b/Tools/testMultiDataSet.R @@ -125,6 +125,42 @@ check(!grepl("JASPColumn_", raw1), "no encoded tokens anywhere in the returned r check(grepl("not available in multi-dataset aware analyses", raw1, fixed = TRUE), "guard demo: readDataSet() hard-stopped during the aware run") +# ------------------------------------------------------------------ Bridge ids (Track C) ---- +cat("\n== Bridge boundary: loadDataSets returns dataset ids; selections accept them ==\n") +# Titles stay the outer-surface sugar; INSIDE the wrapper everything keys on integer ids. The +# loaders hand back the real database ids so a syntax script can refer to datasets by the +# invariant instead of a title the user could rename mid-session. +ids <- jaspSyntax::loadDataSets(list(DataSetA = csvA, DataSetB = csvB)) +check(is.integer(ids) && length(ids) == 2L && identical(names(ids), c("DataSetA", "DataSetB")) && all(ids > 0), + "loadDataSets returns the named integer dataset ids") + +qmlFile <- file.path(find.package("jaspTestModule"), "qml", "testMultiDataSet.qml") +parseSelection <- function(selA, selB) { + opts <- list(dependentA = c("Score", "Group"), dependentB = c("Score", "Age"), + dataSetA = selA, dataSetB = selB, + showMeta = FALSE, guardDemo = FALSE, plotWidth = 400, plotHeight = 300) + jaspSyntax::loadQmlAndParseOptionsStatus("jaspTestModule", "multiDataSetFunc", qmlFile, + as.character(jsonlite::toJSON(opts, auto_unbox = TRUE, digits = NA, null = "null")), "1", TRUE) +} + +stId <- tryCatch(parseSelection(as.integer(ids[[1]]), as.integer(ids[[2]])), error = function(e) e) +check(!inherits(stId, "error") && nzchar(stId$options), "integer DATASET-id selections parse through the bridge") +parsed <- jsonlite::fromJSON(stId$options, simplifyVector = FALSE) +keyA <- as.character(utils::tail(unlist(parsed$dataSetA), 1L)) +keyB <- as.character(utils::tail(unlist(parsed$dataSetB), 1L)) +msj <- jsonlite::fromJSON(stId$multiDataSetJson) +check(all(c(keyA, keyB) %in% as.character(msj$ids)) && keyA != keyB, + "id selections resolved to two distinct queued slice keys (filter-id key space)") +check(identical(as.character(msj$dataSetIds[[keyA]]), as.character(ids[[1]])), + "the slice an id selected belongs to exactly that dataset") + +errBadTitle <- tryCatch({ parseSelection("NoSuchDataSet", as.integer(ids[[2]])); "" }, error = function(e) conditionMessage(e)) +check(grepl("neither the title nor the id", errBadTitle), + "an unknown title is rejected loudly (error lists what is loaded)") +errBadId <- tryCatch({ parseSelection(999L, 998L); "" }, error = function(e) conditionMessage(e)) +check(grepl("neither the title nor the id", errBadId), + "an unknown integer is rejected too - no filter-id guessing (1 is not both a dataset and a filter)") + # ------------------------------------------------------------------ Path 2: wrapper call ------- # The wrapper path is run in a clean Rscript subprocess (no jaspTools runtime in the way): cat("\n== Path 2: wrapper (datasets= named list + dataset selection options) ==\n") diff --git a/Tools/testMultiDataSet_wrapper.R b/Tools/testMultiDataSet_wrapper.R index e81ebfd..b9f2a14 100644 --- a/Tools/testMultiDataSet_wrapper.R +++ b/Tools/testMultiDataSet_wrapper.R @@ -6,8 +6,8 @@ # # Flow under test: loadDataSets -> workspace (titles, ids, encoders) -> QML binds the selection # options first (depends), switching each form; column options then encode per dataset and the -# bridge queues one slice per dataset; runJaspResults hands the analysis encoded options + -# encoded slices keyed by (bridge) dataset id, with the user names as titles. +# bridge queues one slice per distinct filter; runJaspResults hands the analysis encoded options + +# encoded slices keyed by FILTER id (the selection option value), titles in attr dataSetNames. moduledir <- normalizePath(file.path(dirname(sub("^--file=", "", grep("^--file=", commandArgs(FALSE), value = TRUE)[[1]])), "..")) pkglib <- Sys.getenv("JASP_TEST_PKGLIB", file.path(moduledir, "ModuleBundleBuildDir", "build_dir", "jaspTestModule")) From c639c33731cda59e73e8bac62243d481e2268a9d Mon Sep 17 00:00:00 2001 From: Joris Goosen Date: Mon, 12 Oct 2026 13:56:16 +0200 Subject: [PATCH 25/44] make it compile on macos arm --- renv.lock | 3271 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 2805 insertions(+), 466 deletions(-) diff --git a/renv.lock b/renv.lock index f4558d1..5be3507 100644 --- a/renv.lock +++ b/renv.lock @@ -1,10 +1,9 @@ -{ - "R": { - "Version": "4.6.1", +{ "R": { + "Version": "4.5.3", "Repositories": [ { "Name": "CRAN", - "URL": "https://cloud.r-project.org" + "URL": "https://packagemanager.posit.co/cran/latest" } ] }, @@ -13,418 +12,1387 @@ "Package": "R6", "Version": "2.6.1", "Source": "Repository", - "Requirements": [ - "R" - ] + "Title": "Encapsulated Classes with Reference Semantics", + "Authors@R": "c( person(\"Winston\", \"Chang\", , \"winston@posit.co\", role = c(\"aut\", \"cre\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "Creates classes with reference semantics, similar to R's built-in reference classes. Compared to reference classes, R6 classes are simpler and lighter-weight, and they are not built on S4 classes so they do not require the methods package. These classes allow public and private members, and they support inheritance, even when the classes are defined in different packages.", + "License": "MIT + file LICENSE", + "URL": "https://r6.r-lib.org, https://github.com/r-lib/R6", + "BugReports": "https://github.com/r-lib/R6/issues", + "Depends": [ + "R (>= 3.6)" + ], + "Suggests": [ + "lobstr", + "testthat (>= 3.0.0)" + ], + "Config/Needs/website": "tidyverse/tidytemplate, ggplot2, microbenchmark, scales", + "Config/testthat/edition": "3", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2", + "NeedsCompilation": "no", + "Author": "Winston Chang [aut, cre], Posit Software, PBC [cph, fnd]", + "Maintainer": "Winston Chang ", + "Repository": "CRAN" }, "RColorBrewer": { "Package": "RColorBrewer", "Version": "1.1-3", "Source": "Repository", - "Requirements": [ - "R" - ] + "Date": "2022-04-03", + "Title": "ColorBrewer Palettes", + "Authors@R": "c(person(given = \"Erich\", family = \"Neuwirth\", role = c(\"aut\", \"cre\"), email = \"erich.neuwirth@univie.ac.at\"))", + "Author": "Erich Neuwirth [aut, cre]", + "Maintainer": "Erich Neuwirth ", + "Depends": [ + "R (>= 2.0.0)" + ], + "Description": "Provides color schemes for maps (and other graphics) designed by Cynthia Brewer as described at http://colorbrewer2.org.", + "License": "Apache License 2.0", + "NeedsCompilation": "no", + "Repository": "CRAN" }, "Rcpp": { "Package": "Rcpp", - "Version": "1.1.2", - "Source": "Repository", - "Requirements": [ + "Version": "1.1.1", + "Source": "Repository", + "Title": "Seamless R and C++ Integration", + "Date": "2026-01-07", + "Authors@R": "c(person(\"Dirk\", \"Eddelbuettel\", role = c(\"aut\", \"cre\"), email = \"edd@debian.org\", comment = c(ORCID = \"0000-0001-6419-907X\")), person(\"Romain\", \"Francois\", role = \"aut\", comment = c(ORCID = \"0000-0002-2444-4226\")), person(\"JJ\", \"Allaire\", role = \"aut\", comment = c(ORCID = \"0000-0003-0174-9868\")), person(\"Kevin\", \"Ushey\", role = \"aut\", comment = c(ORCID = \"0000-0003-2880-7407\")), person(\"Qiang\", \"Kou\", role = \"aut\", comment = c(ORCID = \"0000-0001-6786-5453\")), person(\"Nathan\", \"Russell\", role = \"aut\"), person(\"Iñaki\", \"Ucar\", role = \"aut\", comment = c(ORCID = \"0000-0001-6403-5550\")), person(\"Doug\", \"Bates\", role = \"aut\", comment = c(ORCID = \"0000-0001-8316-9503\")), person(\"John\", \"Chambers\", role = \"aut\"))", + "Description": "The 'Rcpp' package provides R functions as well as C++ classes which offer a seamless integration of R and C++. Many R data types and objects can be mapped back and forth to C++ equivalents which facilitates both writing of new code as well as easier integration of third-party libraries. Documentation about 'Rcpp' is provided by several vignettes included in this package, via the 'Rcpp Gallery' site at , the paper by Eddelbuettel and Francois (2011, ), the book by Eddelbuettel (2013, ) and the paper by Eddelbuettel and Balamuta (2018, ); see 'citation(\"Rcpp\")' for details.", + "Depends": [ + "R (>= 3.5.0)" + ], + "Imports": [ "methods", - "R", "utils" - ] + ], + "Suggests": [ + "tinytest", + "inline", + "rbenchmark", + "pkgKitten (>= 0.1.2)" + ], + "URL": "https://www.rcpp.org, https://dirk.eddelbuettel.com/code/rcpp.html, https://github.com/RcppCore/Rcpp", + "License": "GPL (>= 2)", + "BugReports": "https://github.com/RcppCore/Rcpp/issues", + "MailingList": "rcpp-devel@lists.r-forge.r-project.org", + "RoxygenNote": "6.1.1", + "Encoding": "UTF-8", + "VignetteBuilder": "Rcpp", + "NeedsCompilation": "yes", + "Author": "Dirk Eddelbuettel [aut, cre] (ORCID: ), Romain Francois [aut] (ORCID: ), JJ Allaire [aut] (ORCID: ), Kevin Ushey [aut] (ORCID: ), Qiang Kou [aut] (ORCID: ), Nathan Russell [aut], Iñaki Ucar [aut] (ORCID: ), Doug Bates [aut] (ORCID: ), John Chambers [aut]", + "Maintainer": "Dirk Eddelbuettel ", + "Repository": "CRAN" }, "S7": { "Package": "S7", - "Version": "0.2.2", + "Version": "0.2.1", "Source": "Repository", - "Requirements": [ - "R", + "Title": "An Object Oriented System Meant to Become a Successor to S3 and S4", + "Authors@R": "c( person(\"Object-Oriented Programming Working Group\", role = \"cph\"), person(\"Davis\", \"Vaughan\", role = \"aut\"), person(\"Jim\", \"Hester\", role = \"aut\", comment = c(ORCID = \"0000-0002-2739-7082\")), person(\"Tomasz\", \"Kalinowski\", role = \"aut\"), person(\"Will\", \"Landau\", role = \"aut\"), person(\"Michael\", \"Lawrence\", role = \"aut\"), person(\"Martin\", \"Maechler\", role = \"aut\", comment = c(ORCID = \"0000-0002-8685-9910\")), person(\"Luke\", \"Tierney\", role = \"aut\"), person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0003-4757-117X\")) )", + "Description": "A new object oriented programming system designed to be a successor to S3 and S4. It includes formal class, generic, and method specification, and a limited form of multiple dispatch. It has been designed and implemented collaboratively by the R Consortium Object-Oriented Programming Working Group, which includes representatives from R-Core, 'Bioconductor', 'Posit'/'tidyverse', and the wider R community.", + "License": "MIT + file LICENSE", + "URL": "https://rconsortium.github.io/S7/, https://github.com/RConsortium/S7", + "BugReports": "https://github.com/RConsortium/S7/issues", + "Depends": [ + "R (>= 3.5.0)" + ], + "Imports": [ "utils" - ] + ], + "Suggests": [ + "bench", + "callr", + "covr", + "knitr", + "methods", + "rmarkdown", + "testthat (>= 3.2.0)", + "tibble" + ], + "VignetteBuilder": "knitr", + "Config/build/compilation-database": "true", + "Config/Needs/website": "sloop", + "Config/testthat/edition": "3", + "Config/testthat/parallel": "TRUE", + "Config/testthat/start-first": "external-generic", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3", + "NeedsCompilation": "yes", + "Author": "Object-Oriented Programming Working Group [cph], Davis Vaughan [aut], Jim Hester [aut] (ORCID: ), Tomasz Kalinowski [aut], Will Landau [aut], Michael Lawrence [aut], Martin Maechler [aut] (ORCID: ), Luke Tierney [aut], Hadley Wickham [aut, cre] (ORCID: )", + "Maintainer": "Hadley Wickham ", + "Repository": "CRAN" }, "askpass": { "Package": "askpass", "Version": "1.2.1", "Source": "Repository", - "Requirements": [ - "sys" - ] + "Type": "Package", + "Title": "Password Entry Utilities for R, Git, and SSH", + "Authors@R": "person(\"Jeroen\", \"Ooms\", role = c(\"aut\", \"cre\"), email = \"jeroenooms@gmail.com\", comment = c(ORCID = \"0000-0002-4035-0289\"))", + "Description": "Cross-platform utilities for prompting the user for credentials or a passphrase, for example to authenticate with a server or read a protected key. Includes native programs for MacOS and Windows, hence no 'tcltk' is required. Password entry can be invoked in two different ways: directly from R via the askpass() function, or indirectly as password-entry back-end for 'ssh-agent' or 'git-credential' via the SSH_ASKPASS and GIT_ASKPASS environment variables. Thereby the user can be prompted for credentials or a passphrase if needed when R calls out to git or ssh.", + "License": "MIT + file LICENSE", + "URL": "https://r-lib.r-universe.dev/askpass", + "BugReports": "https://github.com/r-lib/askpass/issues", + "Encoding": "UTF-8", + "Imports": [ + "sys (>= 2.1)" + ], + "RoxygenNote": "7.2.3", + "Suggests": [ + "testthat" + ], + "Language": "en-US", + "NeedsCompilation": "yes", + "Author": "Jeroen Ooms [aut, cre] ()", + "Maintainer": "Jeroen Ooms ", + "Repository": "CRAN" }, "base64enc": { "Package": "base64enc", "Version": "0.1-6", "Source": "Repository", - "Requirements": [ - "R" - ] + "Title": "Tools for 'base64' Encoding", + "Author": "Simon Urbanek [aut, cre, cph] (https://urbanek.nz, ORCID: )", + "Authors@R": "person(\"Simon\", \"Urbanek\", role=c(\"aut\",\"cre\",\"cph\"), email=\"Simon.Urbanek@r-project.org\", comment=c(\"https://urbanek.nz\", ORCID=\"0000-0003-2297-1732\"))", + "Maintainer": "Simon Urbanek ", + "Depends": [ + "R (>= 2.9.0)" + ], + "Enhances": [ + "png" + ], + "Description": "Tools for handling 'base64' encoding. It is more flexible than the orphaned 'base64' package.", + "License": "GPL-2 | GPL-3", + "URL": "https://www.rforge.net/base64enc", + "BugReports": "https://github.com/s-u/base64enc/issues", + "NeedsCompilation": "yes", + "Repository": "CRAN" }, "bslib": { "Package": "bslib", - "Version": "0.12.0", - "Source": "Repository", - "Requirements": [ + "Version": "0.10.0", + "Source": "Repository", + "Title": "Custom 'Bootstrap' 'Sass' Themes for 'shiny' and 'rmarkdown'", + "Authors@R": "c( person(\"Carson\", \"Sievert\", , \"carson@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-4958-2844\")), person(\"Joe\", \"Cheng\", , \"joe@posit.co\", role = \"aut\"), person(\"Garrick\", \"Aden-Buie\", , \"garrick@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0002-7111-0077\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")), person(, \"Bootstrap contributors\", role = \"ctb\", comment = \"Bootstrap library\"), person(, \"Twitter, Inc\", role = \"cph\", comment = \"Bootstrap library\"), person(\"Javi\", \"Aguilar\", role = c(\"ctb\", \"cph\"), comment = \"Bootstrap colorpicker library\"), person(\"Thomas\", \"Park\", role = c(\"ctb\", \"cph\"), comment = \"Bootswatch library\"), person(, \"PayPal\", role = c(\"ctb\", \"cph\"), comment = \"Bootstrap accessibility plugin\") )", + "Description": "Simplifies custom 'CSS' styling of both 'shiny' and 'rmarkdown' via 'Bootstrap' 'Sass'. Supports 'Bootstrap' 3, 4 and 5 as well as their various 'Bootswatch' themes. An interactive widget is also provided for previewing themes in real time.", + "License": "MIT + file LICENSE", + "URL": "https://rstudio.github.io/bslib/, https://github.com/rstudio/bslib", + "BugReports": "https://github.com/rstudio/bslib/issues", + "Depends": [ + "R (>= 2.10)" + ], + "Imports": [ "base64enc", "cachem", - "fastmap", + "fastmap (>= 1.1.1)", "grDevices", - "htmltools", - "jquerylib", + "htmltools (>= 0.5.8)", + "jquerylib (>= 0.1.3)", "jsonlite", "lifecycle", - "memoise", + "memoise (>= 2.0.1)", "mime", - "R", "rlang", - "sass" - ] + "sass (>= 0.4.9)" + ], + "Suggests": [ + "brand.yml", + "bsicons", + "curl", + "fontawesome", + "future", + "ggplot2", + "knitr", + "lattice", + "magrittr", + "rappdirs", + "rmarkdown (>= 2.7)", + "shiny (>= 1.11.1)", + "testthat", + "thematic", + "tools", + "utils", + "withr", + "yaml" + ], + "Config/Needs/deploy": "BH, chiflights22, colourpicker, commonmark, cpp11, cpsievert/chiflights22, cpsievert/histoslider, dplyr, DT, ggplot2, ggridges, gt, hexbin, histoslider, htmlwidgets, lattice, leaflet, lubridate, markdown, modelr, plotly, reactable, reshape2, rprojroot, rsconnect, rstudio/shiny, scales, styler, tibble", + "Config/Needs/routine": "chromote, desc, renv", + "Config/Needs/website": "brio, crosstalk, dplyr, DT, ggplot2, glue, htmlwidgets, leaflet, lorem, palmerpenguins, plotly, purrr, rprojroot, rstudio/htmltools, scales, stringr, tidyr, webshot2", + "Config/testthat/edition": "3", + "Config/testthat/parallel": "true", + "Config/testthat/start-first": "zzzz-bs-sass, fonts, zzz-precompile, theme-*, rmd-*", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3", + "Collate": "'accordion.R' 'breakpoints.R' 'bs-current-theme.R' 'bs-dependencies.R' 'bs-global.R' 'bs-remove.R' 'bs-theme-layers.R' 'bs-theme-preset-bootswatch.R' 'bs-theme-preset-brand.R' 'bs-theme-preset-builtin.R' 'bs-theme-preset.R' 'utils.R' 'bs-theme-preview.R' 'bs-theme-update.R' 'bs-theme.R' 'bslib-package.R' 'buttons.R' 'card.R' 'deprecated.R' 'files.R' 'fill.R' 'imports.R' 'input-code-editor.R' 'input-dark-mode.R' 'input-submit.R' 'input-switch.R' 'layout.R' 'nav-items.R' 'nav-update.R' 'navbar_options.R' 'navs-legacy.R' 'navs.R' 'onLoad.R' 'page.R' 'popover.R' 'precompiled.R' 'print.R' 'shiny-devmode.R' 'sidebar.R' 'staticimports.R' 'toast.R' 'tooltip.R' 'utils-deps.R' 'utils-shiny.R' 'utils-tags.R' 'value-box.R' 'version-default.R' 'versions.R'", + "NeedsCompilation": "no", + "Author": "Carson Sievert [aut, cre] (ORCID: ), Joe Cheng [aut], Garrick Aden-Buie [aut] (ORCID: ), Posit Software, PBC [cph, fnd], Bootstrap contributors [ctb] (Bootstrap library), Twitter, Inc [cph] (Bootstrap library), Javi Aguilar [ctb, cph] (Bootstrap colorpicker library), Thomas Park [ctb, cph] (Bootswatch library), PayPal [ctb, cph] (Bootstrap accessibility plugin)", + "Maintainer": "Carson Sievert ", + "Repository": "CRAN" }, "cachem": { "Package": "cachem", "Version": "1.1.0", "Source": "Repository", - "Requirements": [ - "fastmap", - "rlang" - ] + "Title": "Cache R Objects with Automatic Pruning", + "Description": "Key-value stores with automatic pruning. Caches can limit either their total size or the age of the oldest object (or both), automatically pruning objects to maintain the constraints.", + "Authors@R": "c( person(\"Winston\", \"Chang\", , \"winston@posit.co\", c(\"aut\", \"cre\")), person(family = \"Posit Software, PBC\", role = c(\"cph\", \"fnd\")))", + "License": "MIT + file LICENSE", + "Encoding": "UTF-8", + "ByteCompile": "true", + "URL": "https://cachem.r-lib.org/, https://github.com/r-lib/cachem", + "Imports": [ + "rlang", + "fastmap (>= 1.2.0)" + ], + "Suggests": [ + "testthat" + ], + "RoxygenNote": "7.2.3", + "Config/Needs/routine": "lobstr", + "Config/Needs/website": "pkgdown", + "NeedsCompilation": "yes", + "Author": "Winston Chang [aut, cre], Posit Software, PBC [cph, fnd]", + "Maintainer": "Winston Chang ", + "Repository": "CRAN" }, "callr": { "Package": "callr", - "Version": "3.8.0", - "Source": "Repository", - "Requirements": [ - "otel", - "processx", - "R", + "Version": "3.7.6", + "Source": "Repository", + "Title": "Call R from R", + "Authors@R": "c( person(\"Gábor\", \"Csárdi\", , \"csardi.gabor@gmail.com\", role = c(\"aut\", \"cre\", \"cph\"), comment = c(ORCID = \"0000-0001-7098-9676\")), person(\"Winston\", \"Chang\", role = \"aut\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")), person(\"Ascent Digital Services\", role = c(\"cph\", \"fnd\")) )", + "Description": "It is sometimes useful to perform a computation in a separate R process, without affecting the current R process at all. This packages does exactly that.", + "License": "MIT + file LICENSE", + "URL": "https://callr.r-lib.org, https://github.com/r-lib/callr", + "BugReports": "https://github.com/r-lib/callr/issues", + "Depends": [ + "R (>= 3.4)" + ], + "Imports": [ + "processx (>= 3.6.1)", "R6", "utils" - ] + ], + "Suggests": [ + "asciicast (>= 2.3.1)", + "cli (>= 1.1.0)", + "mockery", + "ps", + "rprojroot", + "spelling", + "testthat (>= 3.2.0)", + "withr (>= 2.3.0)" + ], + "Config/Needs/website": "r-lib/asciicast, glue, htmlwidgets, igraph, tibble, tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Encoding": "UTF-8", + "Language": "en-US", + "RoxygenNote": "7.3.1.9000", + "NeedsCompilation": "no", + "Author": "Gábor Csárdi [aut, cre, cph] (), Winston Chang [aut], Posit Software, PBC [cph, fnd], Ascent Digital Services [cph, fnd]", + "Maintainer": "Gábor Csárdi ", + "Repository": "CRAN" }, "cli": { "Package": "cli", - "Version": "3.6.6", - "Source": "Repository", - "Requirements": [ - "R", + "Version": "3.6.5", + "Source": "Repository", + "Title": "Helpers for Developing Command Line Interfaces", + "Authors@R": "c( person(\"Gábor\", \"Csárdi\", , \"gabor@posit.co\", role = c(\"aut\", \"cre\")), person(\"Hadley\", \"Wickham\", role = \"ctb\"), person(\"Kirill\", \"Müller\", role = \"ctb\"), person(\"Salim\", \"Brüggemann\", , \"salim-b@pm.me\", role = \"ctb\", comment = c(ORCID = \"0000-0002-5329-5987\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "A suite of tools to build attractive command line interfaces ('CLIs'), from semantic elements: headings, lists, alerts, paragraphs, etc. Supports custom themes via a 'CSS'-like language. It also contains a number of lower level 'CLI' elements: rules, boxes, trees, and 'Unicode' symbols with 'ASCII' alternatives. It support ANSI colors and text styles as well.", + "License": "MIT + file LICENSE", + "URL": "https://cli.r-lib.org, https://github.com/r-lib/cli", + "BugReports": "https://github.com/r-lib/cli/issues", + "Depends": [ + "R (>= 3.4)" + ], + "Imports": [ "utils" - ] + ], + "Suggests": [ + "callr", + "covr", + "crayon", + "digest", + "glue (>= 1.6.0)", + "grDevices", + "htmltools", + "htmlwidgets", + "knitr", + "methods", + "processx", + "ps (>= 1.3.4.9000)", + "rlang (>= 1.0.2.9003)", + "rmarkdown", + "rprojroot", + "rstudioapi", + "testthat (>= 3.2.0)", + "tibble", + "whoami", + "withr" + ], + "Config/Needs/website": "r-lib/asciicast, bench, brio, cpp11, decor, desc, fansi, prettyunits, sessioninfo, tidyverse/tidytemplate, usethis, vctrs", + "Config/testthat/edition": "3", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2", + "NeedsCompilation": "yes", + "Author": "Gábor Csárdi [aut, cre], Hadley Wickham [ctb], Kirill Müller [ctb], Salim Brüggemann [ctb] (), Posit Software, PBC [cph, fnd]", + "Maintainer": "Gábor Csárdi ", + "Repository": "CRAN" }, "codetools": { "Package": "codetools", "Version": "0.2-20", "Source": "Repository", - "Requirements": [ - "R" - ] + "Priority": "recommended", + "Author": "Luke Tierney ", + "Description": "Code analysis tools for R.", + "Title": "Code Analysis Tools for R", + "Depends": [ + "R (>= 2.1)" + ], + "Maintainer": "Luke Tierney ", + "URL": "https://gitlab.com/luke-tierney/codetools", + "License": "GPL", + "NeedsCompilation": "no", + "Repository": "CRAN" }, "cpp11": { "Package": "cpp11", - "Version": "0.5.5", + "Version": "0.5.3", "Source": "Repository", - "Requirements": [ - "R" - ] + "Title": "A C++11 Interface for R's C Interface", + "Authors@R": "c( person(\"Davis\", \"Vaughan\", email = \"davis@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0003-4777-038X\")), person(\"Jim\",\"Hester\", role = \"aut\", comment = c(ORCID = \"0000-0002-2739-7082\")), person(\"Romain\", \"François\", role = \"aut\", comment = c(ORCID = \"0000-0002-2444-4226\")), person(\"Benjamin\", \"Kietzman\", role = \"ctb\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "Provides a header only, C++11 interface to R's C interface. Compared to other approaches 'cpp11' strives to be safe against long jumps from the C API as well as C++ exceptions, conform to normal R function semantics and supports interaction with 'ALTREP' vectors.", + "License": "MIT + file LICENSE", + "URL": "https://cpp11.r-lib.org, https://github.com/r-lib/cpp11", + "BugReports": "https://github.com/r-lib/cpp11/issues", + "Depends": [ + "R (>= 4.0.0)" + ], + "Suggests": [ + "bench", + "brio", + "callr", + "cli", + "covr", + "decor", + "desc", + "ggplot2", + "glue", + "knitr", + "lobstr", + "mockery", + "progress", + "rmarkdown", + "scales", + "Rcpp", + "testthat (>= 3.2.0)", + "tibble", + "utils", + "vctrs", + "withr" + ], + "VignetteBuilder": "knitr", + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Config/Needs/cpp11/cpp_register": "brio, cli, decor, desc, glue, tibble, vctrs", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2", + "NeedsCompilation": "no", + "Author": "Davis Vaughan [aut, cre] (ORCID: ), Jim Hester [aut] (ORCID: ), Romain François [aut] (ORCID: ), Benjamin Kietzman [ctb], Posit Software, PBC [cph, fnd]", + "Maintainer": "Davis Vaughan ", + "Repository": "CRAN" }, "crosstalk": { "Package": "crosstalk", "Version": "1.2.2", "Source": "Repository", - "Requirements": [ - "htmltools", + "Type": "Package", + "Title": "Inter-Widget Interactivity for HTML Widgets", + "Authors@R": "c( person(\"Joe\", \"Cheng\", , \"joe@posit.co\", role = \"aut\"), person(\"Carson\", \"Sievert\", , \"carson@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-4958-2844\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")), person(, \"jQuery Foundation\", role = \"cph\", comment = \"jQuery library and jQuery UI library\"), person(, \"jQuery contributors\", role = c(\"ctb\", \"cph\"), comment = \"jQuery library; authors listed in inst/www/shared/jquery-AUTHORS.txt\"), person(\"Mark\", \"Otto\", role = \"ctb\", comment = \"Bootstrap library\"), person(\"Jacob\", \"Thornton\", role = \"ctb\", comment = \"Bootstrap library\"), person(, \"Bootstrap contributors\", role = \"ctb\", comment = \"Bootstrap library\"), person(, \"Twitter, Inc\", role = \"cph\", comment = \"Bootstrap library\"), person(\"Brian\", \"Reavis\", role = c(\"ctb\", \"cph\"), comment = \"selectize.js library\"), person(\"Kristopher Michael\", \"Kowal\", role = c(\"ctb\", \"cph\"), comment = \"es5-shim library\"), person(, \"es5-shim contributors\", role = c(\"ctb\", \"cph\"), comment = \"es5-shim library\"), person(\"Denis\", \"Ineshin\", role = c(\"ctb\", \"cph\"), comment = \"ion.rangeSlider library\"), person(\"Sami\", \"Samhuri\", role = c(\"ctb\", \"cph\"), comment = \"Javascript strftime library\") )", + "Description": "Provides building blocks for allowing HTML widgets to communicate with each other, with Shiny or without (i.e. static .html files). Currently supports linked brushing and filtering.", + "License": "MIT + file LICENSE", + "URL": "https://rstudio.github.io/crosstalk/, https://github.com/rstudio/crosstalk", + "BugReports": "https://github.com/rstudio/crosstalk/issues", + "Imports": [ + "htmltools (>= 0.3.6)", "jsonlite", "lazyeval", "R6" - ] + ], + "Suggests": [ + "bslib", + "ggplot2", + "sass", + "shiny", + "testthat (>= 2.1.0)" + ], + "Config/Needs/website": "jcheng5/d3scatter, DT, leaflet, rmarkdown", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2", + "NeedsCompilation": "no", + "Author": "Joe Cheng [aut], Carson Sievert [aut, cre] (ORCID: ), Posit Software, PBC [cph, fnd], jQuery Foundation [cph] (jQuery library and jQuery UI library), jQuery contributors [ctb, cph] (jQuery library; authors listed in inst/www/shared/jquery-AUTHORS.txt), Mark Otto [ctb] (Bootstrap library), Jacob Thornton [ctb] (Bootstrap library), Bootstrap contributors [ctb] (Bootstrap library), Twitter, Inc [cph] (Bootstrap library), Brian Reavis [ctb, cph] (selectize.js library), Kristopher Michael Kowal [ctb, cph] (es5-shim library), es5-shim contributors [ctb, cph] (es5-shim library), Denis Ineshin [ctb, cph] (ion.rangeSlider library), Sami Samhuri [ctb, cph] (Javascript strftime library)", + "Maintainer": "Carson Sievert ", + "Repository": "CRAN" }, "curl": { "Package": "curl", - "Version": "8.0.0", - "Source": "Repository", - "Requirements": [ - "R" - ] + "Version": "7.0.0", + "Source": "Repository", + "Type": "Package", + "Title": "A Modern and Flexible Web Client for R", + "Authors@R": "c( person(\"Jeroen\", \"Ooms\", role = c(\"aut\", \"cre\"), email = \"jeroenooms@gmail.com\", comment = c(ORCID = \"0000-0002-4035-0289\")), person(\"Hadley\", \"Wickham\", role = \"ctb\"), person(\"Posit Software, PBC\", role = \"cph\"))", + "Description": "Bindings to 'libcurl' for performing fully configurable HTTP/FTP requests where responses can be processed in memory, on disk, or streaming via the callback or connection interfaces. Some knowledge of 'libcurl' is recommended; for a more-user-friendly web client see the 'httr2' package which builds on this package with http specific tools and logic.", + "License": "MIT + file LICENSE", + "SystemRequirements": "libcurl (>= 7.73): libcurl-devel (rpm) or libcurl4-openssl-dev (deb)", + "URL": "https://jeroen.r-universe.dev/curl", + "BugReports": "https://github.com/jeroen/curl/issues", + "Suggests": [ + "spelling", + "testthat (>= 1.0.0)", + "knitr", + "jsonlite", + "later", + "rmarkdown", + "httpuv (>= 1.4.4)", + "webutils" + ], + "VignetteBuilder": "knitr", + "Depends": [ + "R (>= 3.0.0)" + ], + "RoxygenNote": "7.3.2", + "Encoding": "UTF-8", + "Language": "en-US", + "NeedsCompilation": "yes", + "Author": "Jeroen Ooms [aut, cre] (ORCID: ), Hadley Wickham [ctb], Posit Software, PBC [cph]", + "Maintainer": "Jeroen Ooms ", + "Repository": "CRAN" }, "data.table": { "Package": "data.table", - "Version": "1.18.6.1", + "Version": "1.18.2.1", "Source": "Repository", - "Requirements": [ - "methods", - "R" - ] + "Title": "Extension of `data.frame`", + "Depends": [ + "R (>= 3.4.0)" + ], + "Imports": [ + "methods" + ], + "Suggests": [ + "bit64 (>= 4.0.0)", + "bit (>= 4.0.4)", + "R.utils (>= 2.13.0)", + "xts", + "zoo (>= 1.8-1)", + "yaml", + "knitr", + "markdown" + ], + "Description": "Fast aggregation of large data (e.g. 100GB in RAM), fast ordered joins, fast add/modify/delete of columns by group using no copies at all, list columns, friendly and fast character-separated-value read/write. Offers a natural and flexible syntax, for faster development.", + "License": "MPL-2.0 | file LICENSE", + "URL": "https://r-datatable.com, https://Rdatatable.gitlab.io/data.table, https://github.com/Rdatatable/data.table", + "BugReports": "https://github.com/Rdatatable/data.table/issues", + "VignetteBuilder": "knitr", + "Encoding": "UTF-8", + "ByteCompile": "TRUE", + "Authors@R": "c( person(\"Tyson\",\"Barrett\", role=c(\"aut\",\"cre\"), email=\"t.barrett88@gmail.com\", comment = c(ORCID=\"0000-0002-2137-1391\")), person(\"Matt\",\"Dowle\", role=\"aut\", email=\"mattjdowle@gmail.com\"), person(\"Arun\",\"Srinivasan\", role=\"aut\", email=\"asrini@pm.me\"), person(\"Jan\",\"Gorecki\", role=\"aut\", email=\"j.gorecki@wit.edu.pl\"), person(\"Michael\",\"Chirico\", role=\"aut\", email=\"michaelchirico4@gmail.com\", comment = c(ORCID=\"0000-0003-0787-087X\")), person(\"Toby\",\"Hocking\", role=\"aut\", email=\"toby.hocking@r-project.org\", comment = c(ORCID=\"0000-0002-3146-0865\")), person(\"Benjamin\",\"Schwendinger\",role=\"aut\", comment = c(ORCID=\"0000-0003-3315-8114\")), person(\"Ivan\", \"Krylov\", role=\"aut\", email=\"ikrylov@disroot.org\", comment = c(ORCID=\"0000-0002-0172-3812\")), person(\"Pasha\",\"Stetsenko\", role=\"ctb\"), person(\"Tom\",\"Short\", role=\"ctb\"), person(\"Steve\",\"Lianoglou\", role=\"ctb\"), person(\"Eduard\",\"Antonyan\", role=\"ctb\"), person(\"Markus\",\"Bonsch\", role=\"ctb\"), person(\"Hugh\",\"Parsonage\", role=\"ctb\"), person(\"Scott\",\"Ritchie\", role=\"ctb\"), person(\"Kun\",\"Ren\", role=\"ctb\"), person(\"Xianying\",\"Tan\", role=\"ctb\"), person(\"Rick\",\"Saporta\", role=\"ctb\"), person(\"Otto\",\"Seiskari\", role=\"ctb\"), person(\"Xianghui\",\"Dong\", role=\"ctb\"), person(\"Michel\",\"Lang\", role=\"ctb\"), person(\"Watal\",\"Iwasaki\", role=\"ctb\"), person(\"Seth\",\"Wenchel\", role=\"ctb\"), person(\"Karl\",\"Broman\", role=\"ctb\"), person(\"Tobias\",\"Schmidt\", role=\"ctb\"), person(\"David\",\"Arenburg\", role=\"ctb\"), person(\"Ethan\",\"Smith\", role=\"ctb\"), person(\"Francois\",\"Cocquemas\", role=\"ctb\"), person(\"Matthieu\",\"Gomez\", role=\"ctb\"), person(\"Philippe\",\"Chataignon\", role=\"ctb\"), person(\"Nello\",\"Blaser\", role=\"ctb\"), person(\"Dmitry\",\"Selivanov\", role=\"ctb\"), person(\"Andrey\",\"Riabushenko\", role=\"ctb\"), person(\"Cheng\",\"Lee\", role=\"ctb\"), person(\"Declan\",\"Groves\", role=\"ctb\"), person(\"Daniel\",\"Possenriede\", role=\"ctb\"), person(\"Felipe\",\"Parages\", role=\"ctb\"), person(\"Denes\",\"Toth\", role=\"ctb\"), person(\"Mus\",\"Yaramaz-David\", role=\"ctb\"), person(\"Ayappan\",\"Perumal\", role=\"ctb\"), person(\"James\",\"Sams\", role=\"ctb\"), person(\"Martin\",\"Morgan\", role=\"ctb\"), person(\"Michael\",\"Quinn\", role=\"ctb\"), person(given=\"@javrucebo\", role=\"ctb\", comment=\"GitHub user\"), person(\"Marc\",\"Halperin\", role=\"ctb\"), person(\"Roy\",\"Storey\", role=\"ctb\"), person(\"Manish\",\"Saraswat\", role=\"ctb\"), person(\"Morgan\",\"Jacob\", role=\"ctb\"), person(\"Michael\",\"Schubmehl\", role=\"ctb\"), person(\"Davis\",\"Vaughan\", role=\"ctb\"), person(\"Leonardo\",\"Silvestri\", role=\"ctb\"), person(\"Jim\",\"Hester\", role=\"ctb\"), person(\"Anthony\",\"Damico\", role=\"ctb\"), person(\"Sebastian\",\"Freundt\", role=\"ctb\"), person(\"David\",\"Simons\", role=\"ctb\"), person(\"Elliott\",\"Sales de Andrade\", role=\"ctb\"), person(\"Cole\",\"Miller\", role=\"ctb\"), person(\"Jens Peder\",\"Meldgaard\", role=\"ctb\"), person(\"Vaclav\",\"Tlapak\", role=\"ctb\"), person(\"Kevin\",\"Ushey\", role=\"ctb\"), person(\"Dirk\",\"Eddelbuettel\", role=\"ctb\"), person(\"Tony\",\"Fischetti\", role=\"ctb\"), person(\"Ofek\",\"Shilon\", role=\"ctb\"), person(\"Vadim\",\"Khotilovich\", role=\"ctb\"), person(\"Hadley\",\"Wickham\", role=\"ctb\"), person(\"Bennet\",\"Becker\", role=\"ctb\"), person(\"Kyle\",\"Haynes\", role=\"ctb\"), person(\"Boniface Christian\",\"Kamgang\", role=\"ctb\"), person(\"Olivier\",\"Delmarcell\", role=\"ctb\"), person(\"Josh\",\"O'Brien\", role=\"ctb\"), person(\"Dereck\",\"de Mezquita\", role=\"ctb\"), person(\"Michael\",\"Czekanski\", role=\"ctb\"), person(\"Dmitry\", \"Shemetov\", role=\"ctb\"), person(\"Nitish\", \"Jha\", role=\"ctb\"), person(\"Joshua\", \"Wu\", role=\"ctb\"), person(\"Iago\", \"Giné-Vázquez\", role=\"ctb\"), person(\"Anirban\", \"Chetia\", role=\"ctb\"), person(\"Doris\", \"Amoakohene\", role=\"ctb\"), person(\"Angel\", \"Feliz\", role=\"ctb\"), person(\"Michael\",\"Young\", role=\"ctb\"), person(\"Mark\", \"Seeto\", role=\"ctb\"), person(\"Philippe\", \"Grosjean\", role=\"ctb\"), person(\"Vincent\", \"Runge\", role=\"ctb\"), person(\"Christian\", \"Wia\", role=\"ctb\"), person(\"Elise\", \"Maigné\", role=\"ctb\"), person(\"Vincent\", \"Rocher\", role=\"ctb\"), person(\"Vijay\", \"Lulla\", role=\"ctb\"), person(\"Aljaž\", \"Sluga\", role=\"ctb\"), person(\"Bill\", \"Evans\", role=\"ctb\"), person(\"Reino\", \"Bruner\", role=\"ctb\"), person(given=\"@badasahog\", role=\"ctb\", comment=\"GitHub user\"), person(\"Vinit\", \"Thakur\", role=\"ctb\"), person(\"Mukul\", \"Kumar\", role=\"ctb\"), person(\"Ildikó\", \"Czeller\", role=\"ctb\"), person(\"Manmita\", \"Das\", role=\"ctb\") )", + "NeedsCompilation": "yes", + "Author": "Tyson Barrett [aut, cre] (ORCID: ), Matt Dowle [aut], Arun Srinivasan [aut], Jan Gorecki [aut], Michael Chirico [aut] (ORCID: ), Toby Hocking [aut] (ORCID: ), Benjamin Schwendinger [aut] (ORCID: ), Ivan Krylov [aut] (ORCID: ), Pasha Stetsenko [ctb], Tom Short [ctb], Steve Lianoglou [ctb], Eduard Antonyan [ctb], Markus Bonsch [ctb], Hugh Parsonage [ctb], Scott Ritchie [ctb], Kun Ren [ctb], Xianying Tan [ctb], Rick Saporta [ctb], Otto Seiskari [ctb], Xianghui Dong [ctb], Michel Lang [ctb], Watal Iwasaki [ctb], Seth Wenchel [ctb], Karl Broman [ctb], Tobias Schmidt [ctb], David Arenburg [ctb], Ethan Smith [ctb], Francois Cocquemas [ctb], Matthieu Gomez [ctb], Philippe Chataignon [ctb], Nello Blaser [ctb], Dmitry Selivanov [ctb], Andrey Riabushenko [ctb], Cheng Lee [ctb], Declan Groves [ctb], Daniel Possenriede [ctb], Felipe Parages [ctb], Denes Toth [ctb], Mus Yaramaz-David [ctb], Ayappan Perumal [ctb], James Sams [ctb], Martin Morgan [ctb], Michael Quinn [ctb], @javrucebo [ctb] (GitHub user), Marc Halperin [ctb], Roy Storey [ctb], Manish Saraswat [ctb], Morgan Jacob [ctb], Michael Schubmehl [ctb], Davis Vaughan [ctb], Leonardo Silvestri [ctb], Jim Hester [ctb], Anthony Damico [ctb], Sebastian Freundt [ctb], David Simons [ctb], Elliott Sales de Andrade [ctb], Cole Miller [ctb], Jens Peder Meldgaard [ctb], Vaclav Tlapak [ctb], Kevin Ushey [ctb], Dirk Eddelbuettel [ctb], Tony Fischetti [ctb], Ofek Shilon [ctb], Vadim Khotilovich [ctb], Hadley Wickham [ctb], Bennet Becker [ctb], Kyle Haynes [ctb], Boniface Christian Kamgang [ctb], Olivier Delmarcell [ctb], Josh O'Brien [ctb], Dereck de Mezquita [ctb], Michael Czekanski [ctb], Dmitry Shemetov [ctb], Nitish Jha [ctb], Joshua Wu [ctb], Iago Giné-Vázquez [ctb], Anirban Chetia [ctb], Doris Amoakohene [ctb], Angel Feliz [ctb], Michael Young [ctb], Mark Seeto [ctb], Philippe Grosjean [ctb], Vincent Runge [ctb], Christian Wia [ctb], Elise Maigné [ctb], Vincent Rocher [ctb], Vijay Lulla [ctb], Aljaž Sluga [ctb], Bill Evans [ctb], Reino Bruner [ctb], @badasahog [ctb] (GitHub user), Vinit Thakur [ctb], Mukul Kumar [ctb], Ildikó Czeller [ctb], Manmita Das [ctb]", + "Maintainer": "Tyson Barrett ", + "Repository": "CRAN" }, "desc": { "Package": "desc", "Version": "1.4.3", "Source": "Repository", - "Requirements": [ + "Title": "Manipulate DESCRIPTION Files", + "Authors@R": "c( person(\"Gábor\", \"Csárdi\", , \"csardi.gabor@gmail.com\", role = c(\"aut\", \"cre\")), person(\"Kirill\", \"Müller\", role = \"aut\"), person(\"Jim\", \"Hester\", , \"james.f.hester@gmail.com\", role = \"aut\"), person(\"Maëlle\", \"Salmon\", role = \"ctb\", comment = c(ORCID = \"0000-0002-2815-0399\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Maintainer": "Gábor Csárdi ", + "Description": "Tools to read, write, create, and manipulate DESCRIPTION files. It is intended for packages that create or manipulate other packages.", + "License": "MIT + file LICENSE", + "URL": "https://desc.r-lib.org/, https://github.com/r-lib/desc", + "BugReports": "https://github.com/r-lib/desc/issues", + "Depends": [ + "R (>= 3.4)" + ], + "Imports": [ "cli", - "R", "R6", "utils" - ] + ], + "Suggests": [ + "callr", + "covr", + "gh", + "spelling", + "testthat", + "whoami", + "withr" + ], + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Encoding": "UTF-8", + "Language": "en-US", + "RoxygenNote": "7.2.3", + "Collate": "'assertions.R' 'authors-at-r.R' 'built.R' 'classes.R' 'collate.R' 'constants.R' 'deps.R' 'desc-package.R' 'description.R' 'encoding.R' 'find-package-root.R' 'latex.R' 'non-oo-api.R' 'package-archives.R' 'read.R' 'remotes.R' 'str.R' 'syntax_checks.R' 'urls.R' 'utils.R' 'validate.R' 'version.R'", + "NeedsCompilation": "no", + "Author": "Gábor Csárdi [aut, cre], Kirill Müller [aut], Jim Hester [aut], Maëlle Salmon [ctb] (), Posit Software, PBC [cph, fnd]", + "Repository": "CRAN" }, "digest": { "Package": "digest", "Version": "0.6.39", "Source": "Repository", - "Requirements": [ - "R", + "Authors@R": "c(person(\"Dirk\", \"Eddelbuettel\", role = c(\"aut\", \"cre\"), email = \"edd@debian.org\", comment = c(ORCID = \"0000-0001-6419-907X\")), person(\"Antoine\", \"Lucas\", role=\"ctb\", comment = c(ORCID = \"0000-0002-8059-9767\")), person(\"Jarek\", \"Tuszynski\", role=\"ctb\"), person(\"Henrik\", \"Bengtsson\", role=\"ctb\", comment = c(ORCID = \"0000-0002-7579-5165\")), person(\"Simon\", \"Urbanek\", role=\"ctb\", comment = c(ORCID = \"0000-0003-2297-1732\")), person(\"Mario\", \"Frasca\", role=\"ctb\"), person(\"Bryan\", \"Lewis\", role=\"ctb\"), person(\"Murray\", \"Stokely\", role=\"ctb\"), person(\"Hannes\", \"Muehleisen\", role=\"ctb\", comment = c(ORCID = \"0000-0001-8552-0029\")), person(\"Duncan\", \"Murdoch\", role=\"ctb\"), person(\"Jim\", \"Hester\", role=\"ctb\", comment = c(ORCID = \"0000-0002-2739-7082\")), person(\"Wush\", \"Wu\", role=\"ctb\", comment = c(ORCID = \"0000-0001-5180-0567\")), person(\"Qiang\", \"Kou\", role=\"ctb\", comment = c(ORCID = \"0000-0001-6786-5453\")), person(\"Thierry\", \"Onkelinx\", role=\"ctb\", comment = c(ORCID = \"0000-0001-8804-4216\")), person(\"Michel\", \"Lang\", role=\"ctb\", comment = c(ORCID = \"0000-0001-9754-0393\")), person(\"Viliam\", \"Simko\", role=\"ctb\"), person(\"Kurt\", \"Hornik\", role=\"ctb\", comment = c(ORCID = \"0000-0003-4198-9911\")), person(\"Radford\", \"Neal\", role=\"ctb\", comment = c(ORCID = \"0000-0002-2473-3407\")), person(\"Kendon\", \"Bell\", role=\"ctb\", comment = c(ORCID = \"0000-0002-9093-8312\")), person(\"Matthew\", \"de Queljoe\", role=\"ctb\"), person(\"Dmitry\", \"Selivanov\", role=\"ctb\", comment = c(ORCID = \"0000-0003-0492-6647\")), person(\"Ion\", \"Suruceanu\", role=\"ctb\", comment = c(ORCID = \"0009-0005-6446-4909\")), person(\"Bill\", \"Denney\", role=\"ctb\", comment = c(ORCID = \"0000-0002-5759-428X\")), person(\"Dirk\", \"Schumacher\", role=\"ctb\"), person(\"András\", \"Svraka\", role=\"ctb\", comment = c(ORCID = \"0009-0008-8480-1329\")), person(\"Sergey\", \"Fedorov\", role=\"ctb\", comment = c(ORCID = \"0000-0002-5970-7233\")), person(\"Will\", \"Landau\", role=\"ctb\", comment = c(ORCID = \"0000-0003-1878-3253\")), person(\"Floris\", \"Vanderhaeghe\", role=\"ctb\", comment = c(ORCID = \"0000-0002-6378-6229\")), person(\"Kevin\", \"Tappe\", role=\"ctb\"), person(\"Harris\", \"McGehee\", role=\"ctb\"), person(\"Tim\", \"Mastny\", role=\"ctb\"), person(\"Aaron\", \"Peikert\", role=\"ctb\", comment = c(ORCID = \"0000-0001-7813-818X\")), person(\"Mark\", \"van der Loo\", role=\"ctb\", comment = c(ORCID = \"0000-0002-9807-4686\")), person(\"Chris\", \"Muir\", role=\"ctb\", comment = c(ORCID = \"0000-0003-2555-3878\")), person(\"Moritz\", \"Beller\", role=\"ctb\", comment = c(ORCID = \"0000-0003-4852-0526\")), person(\"Sebastian\", \"Campbell\", role=\"ctb\", comment = c(ORCID = \"0009-0000-5948-4503\")), person(\"Winston\", \"Chang\", role=\"ctb\", comment = c(ORCID = \"0000-0002-1576-2126\")), person(\"Dean\", \"Attali\", role=\"ctb\", comment = c(ORCID = \"0000-0002-5645-3493\")), person(\"Michael\", \"Chirico\", role=\"ctb\", comment = c(ORCID = \"0000-0003-0787-087X\")), person(\"Kevin\", \"Ushey\", role=\"ctb\", comment = c(ORCID = \"0000-0003-2880-7407\")), person(\"Carl\", \"Pearson\", role=\"ctb\", comment = c(ORCID = \"0000-0003-0701-7860\")))", + "Date": "2025-11-19", + "Title": "Create Compact Hash Digests of R Objects", + "Description": "Implementation of a function 'digest()' for the creation of hash digests of arbitrary R objects (using the 'md5', 'sha-1', 'sha-256', 'crc32', 'xxhash', 'murmurhash', 'spookyhash', 'blake3', 'crc32c', 'xxh3_64', and 'xxh3_128' algorithms) permitting easy comparison of R language objects, as well as functions such as 'hmac()' to create hash-based message authentication code. Please note that this package is not meant to be deployed for cryptographic purposes for which more comprehensive (and widely tested) libraries such as 'OpenSSL' should be used.", + "URL": "https://github.com/eddelbuettel/digest, https://eddelbuettel.github.io/digest/, https://dirk.eddelbuettel.com/code/digest.html", + "BugReports": "https://github.com/eddelbuettel/digest/issues", + "Depends": [ + "R (>= 3.3.0)" + ], + "Imports": [ "utils" - ] + ], + "License": "GPL (>= 2)", + "Suggests": [ + "tinytest", + "simplermarkdown", + "rbenchmark" + ], + "VignetteBuilder": "simplermarkdown", + "Encoding": "UTF-8", + "NeedsCompilation": "yes", + "Author": "Dirk Eddelbuettel [aut, cre] (ORCID: ), Antoine Lucas [ctb] (ORCID: ), Jarek Tuszynski [ctb], Henrik Bengtsson [ctb] (ORCID: ), Simon Urbanek [ctb] (ORCID: ), Mario Frasca [ctb], Bryan Lewis [ctb], Murray Stokely [ctb], Hannes Muehleisen [ctb] (ORCID: ), Duncan Murdoch [ctb], Jim Hester [ctb] (ORCID: ), Wush Wu [ctb] (ORCID: ), Qiang Kou [ctb] (ORCID: ), Thierry Onkelinx [ctb] (ORCID: ), Michel Lang [ctb] (ORCID: ), Viliam Simko [ctb], Kurt Hornik [ctb] (ORCID: ), Radford Neal [ctb] (ORCID: ), Kendon Bell [ctb] (ORCID: ), Matthew de Queljoe [ctb], Dmitry Selivanov [ctb] (ORCID: ), Ion Suruceanu [ctb] (ORCID: ), Bill Denney [ctb] (ORCID: ), Dirk Schumacher [ctb], András Svraka [ctb] (ORCID: ), Sergey Fedorov [ctb] (ORCID: ), Will Landau [ctb] (ORCID: ), Floris Vanderhaeghe [ctb] (ORCID: ), Kevin Tappe [ctb], Harris McGehee [ctb], Tim Mastny [ctb], Aaron Peikert [ctb] (ORCID: ), Mark van der Loo [ctb] (ORCID: ), Chris Muir [ctb] (ORCID: ), Moritz Beller [ctb] (ORCID: ), Sebastian Campbell [ctb] (ORCID: ), Winston Chang [ctb] (ORCID: ), Dean Attali [ctb] (ORCID: ), Michael Chirico [ctb] (ORCID: ), Kevin Ushey [ctb] (ORCID: ), Carl Pearson [ctb] (ORCID: )", + "Maintainer": "Dirk Eddelbuettel ", + "Repository": "CRAN" }, "dplyr": { "Package": "dplyr", - "Version": "1.2.1", + "Version": "1.2.0", "Source": "Repository", - "Requirements": [ - "cli", + "Type": "Package", + "Title": "A Grammar of Data Manipulation", + "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0003-4757-117X\")), person(\"Romain\", \"François\", role = \"aut\", comment = c(ORCID = \"0000-0002-2444-4226\")), person(\"Lionel\", \"Henry\", role = \"aut\"), person(\"Kirill\", \"Müller\", role = \"aut\", comment = c(ORCID = \"0000-0002-1416-3412\")), person(\"Davis\", \"Vaughan\", , \"davis@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0003-4777-038X\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "A fast, consistent tool for working with data frame like objects, both in memory and out of memory.", + "License": "MIT + file LICENSE", + "URL": "https://dplyr.tidyverse.org, https://github.com/tidyverse/dplyr", + "BugReports": "https://github.com/tidyverse/dplyr/issues", + "Depends": [ + "R (>= 4.1.0)" + ], + "Imports": [ + "cli (>= 3.6.2)", "generics", - "glue", - "lifecycle", - "magrittr", + "glue (>= 1.3.2)", + "lifecycle (>= 1.0.5)", + "magrittr (>= 1.5)", "methods", - "pillar", - "R", + "pillar (>= 1.9.0)", "R6", - "rlang", - "tibble", - "tidyselect", + "rlang (>= 1.1.7)", + "tibble (>= 3.2.0)", + "tidyselect (>= 1.2.0)", "utils", - "vctrs" - ] + "vctrs (>= 0.7.1)" + ], + "Suggests": [ + "broom", + "covr", + "DBI", + "dbplyr (>= 2.2.1)", + "ggplot2", + "knitr", + "Lahman", + "lobstr", + "nycflights13", + "purrr", + "rmarkdown", + "RSQLite", + "stringi (>= 1.7.6)", + "testthat (>= 3.1.5)", + "tidyr (>= 1.3.0)", + "withr" + ], + "VignetteBuilder": "knitr", + "Config/build/compilation-database": "true", + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Encoding": "UTF-8", + "LazyData": "true", + "RoxygenNote": "7.3.3", + "NeedsCompilation": "yes", + "Author": "Hadley Wickham [aut, cre] (ORCID: ), Romain François [aut] (ORCID: ), Lionel Henry [aut], Kirill Müller [aut] (ORCID: ), Davis Vaughan [aut] (ORCID: ), Posit Software, PBC [cph, fnd]", + "Maintainer": "Hadley Wickham ", + "Repository": "CRAN" }, "evaluate": { "Package": "evaluate", "Version": "1.0.5", "Source": "Repository", - "Requirements": [ - "R" - ] + "Type": "Package", + "Title": "Parsing and Evaluation Tools that Provide More Details than the Default", + "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = c(\"aut\", \"cre\")), person(\"Yihui\", \"Xie\", role = \"aut\", comment = c(ORCID = \"0000-0003-0645-5666\")), person(\"Michael\", \"Lawrence\", role = \"ctb\"), person(\"Thomas\", \"Kluyver\", role = \"ctb\"), person(\"Jeroen\", \"Ooms\", role = \"ctb\"), person(\"Barret\", \"Schloerke\", role = \"ctb\"), person(\"Adam\", \"Ryczkowski\", role = \"ctb\"), person(\"Hiroaki\", \"Yutani\", role = \"ctb\"), person(\"Michel\", \"Lang\", role = \"ctb\"), person(\"Karolis\", \"Koncevičius\", role = \"ctb\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "Parsing and evaluation tools that make it easy to recreate the command line behaviour of R.", + "License": "MIT + file LICENSE", + "URL": "https://evaluate.r-lib.org/, https://github.com/r-lib/evaluate", + "BugReports": "https://github.com/r-lib/evaluate/issues", + "Depends": [ + "R (>= 3.6.0)" + ], + "Suggests": [ + "callr", + "covr", + "ggplot2 (>= 3.3.6)", + "lattice", + "methods", + "pkgload", + "ragg (>= 1.4.0)", + "rlang (>= 1.1.5)", + "knitr", + "testthat (>= 3.0.0)", + "withr" + ], + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2", + "NeedsCompilation": "no", + "Author": "Hadley Wickham [aut, cre], Yihui Xie [aut] (ORCID: ), Michael Lawrence [ctb], Thomas Kluyver [ctb], Jeroen Ooms [ctb], Barret Schloerke [ctb], Adam Ryczkowski [ctb], Hiroaki Yutani [ctb], Michel Lang [ctb], Karolis Koncevičius [ctb], Posit Software, PBC [cph, fnd]", + "Maintainer": "Hadley Wickham ", + "Repository": "CRAN" }, "farver": { "Package": "farver", "Version": "2.1.2", "Source": "Repository", - "Requirements": [] + "Type": "Package", + "Title": "High Performance Colour Space Manipulation", + "Authors@R": "c( person(\"Thomas Lin\", \"Pedersen\", , \"thomas.pedersen@posit.co\", role = c(\"cre\", \"aut\"), comment = c(ORCID = \"0000-0002-5147-4711\")), person(\"Berendea\", \"Nicolae\", role = \"aut\", comment = \"Author of the ColorSpace C++ library\"), person(\"Romain\", \"François\", , \"romain@purrple.cat\", role = \"aut\", comment = c(ORCID = \"0000-0002-2444-4226\")), person(\"Posit, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "The encoding of colour can be handled in many different ways, using different colour spaces. As different colour spaces have different uses, efficient conversion between these representations are important. The 'farver' package provides a set of functions that gives access to very fast colour space conversion and comparisons implemented in C++, and offers speed improvements over the 'convertColor' function in the 'grDevices' package.", + "License": "MIT + file LICENSE", + "URL": "https://farver.data-imaginist.com, https://github.com/thomasp85/farver", + "BugReports": "https://github.com/thomasp85/farver/issues", + "Suggests": [ + "covr", + "testthat (>= 3.0.0)" + ], + "Config/testthat/edition": "3", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.1", + "NeedsCompilation": "yes", + "Author": "Thomas Lin Pedersen [cre, aut] (), Berendea Nicolae [aut] (Author of the ColorSpace C++ library), Romain François [aut] (), Posit, PBC [cph, fnd]", + "Maintainer": "Thomas Lin Pedersen ", + "Repository": "CRAN" }, "fastmap": { "Package": "fastmap", "Version": "1.2.0", "Source": "Repository", - "Requirements": [] + "Title": "Fast Data Structures", + "Authors@R": "c( person(\"Winston\", \"Chang\", email = \"winston@posit.co\", role = c(\"aut\", \"cre\")), person(given = \"Posit Software, PBC\", role = c(\"cph\", \"fnd\")), person(given = \"Tessil\", role = \"cph\", comment = \"hopscotch_map library\") )", + "Description": "Fast implementation of data structures, including a key-value store, stack, and queue. Environments are commonly used as key-value stores in R, but every time a new key is used, it is added to R's global symbol table, causing a small amount of memory leakage. This can be problematic in cases where many different keys are used. Fastmap avoids this memory leak issue by implementing the map using data structures in C++.", + "License": "MIT + file LICENSE", + "Encoding": "UTF-8", + "RoxygenNote": "7.2.3", + "Suggests": [ + "testthat (>= 2.1.1)" + ], + "URL": "https://r-lib.github.io/fastmap/, https://github.com/r-lib/fastmap", + "BugReports": "https://github.com/r-lib/fastmap/issues", + "NeedsCompilation": "yes", + "Author": "Winston Chang [aut, cre], Posit Software, PBC [cph, fnd], Tessil [cph] (hopscotch_map library)", + "Maintainer": "Winston Chang ", + "Repository": "CRAN" }, "fontBitstreamVera": { "Package": "fontBitstreamVera", "Version": "0.1.1", "Source": "Repository", - "Requirements": [ - "R" - ] + "Title": "Fonts with 'Bitstream Vera Fonts' License", + "Authors@R": "c( person(\"Lionel\", \"Henry\", , \"lionel.hry@gmail.com\", c(\"cre\", \"aut\")), person(\"Bitstream\", role = \"cph\"))", + "Description": "Provides fonts licensed under the 'Bitstream Vera Fonts' license for the 'fontquiver' package.", + "Depends": [ + "R (>= 3.0.0)" + ], + "License": "file LICENCE", + "Encoding": "UTF-8", + "LazyData": "true", + "RoxygenNote": "5.0.1", + "NeedsCompilation": "no", + "Author": "Lionel Henry [cre, aut], Bitstream [cph]", + "Maintainer": "Lionel Henry ", + "License_is_FOSS": "yes", + "Repository": "CRAN" }, "fontLiberation": { "Package": "fontLiberation", "Version": "0.1.0", "Source": "Repository", - "Requirements": [ - "R" - ] + "Title": "Liberation Fonts", + "Authors@R": "c( person(\"Lionel\", \"Henry\", , \"lionel@rstudio.com\", \"cre\"), person(\"Pravin Satpute\", role = \"aut\"), person(\"Steve Matteson\", role = \"aut\"), person(\"Red Hat, Inc\", role = \"cph\"), person(\"Google Corporation\", role = \"cph\"))", + "Description": "A placeholder for the Liberation fontset intended for the `fontquiver` package. This fontset covers the 12 combinations of families (sans, serif, mono) and faces (plain, bold, italic, bold italic) supported in R graphics devices.", + "Depends": [ + "R (>= 3.0)" + ], + "License": "file LICENSE", + "Encoding": "UTF-8", + "LazyData": "true", + "RoxygenNote": "5.0.1", + "NeedsCompilation": "no", + "Author": "Lionel Henry [cre], Pravin Satpute [aut], Steve Matteson [aut], Red Hat, Inc [cph], Google Corporation [cph]", + "Maintainer": "Lionel Henry ", + "Repository": "CRAN", + "License_is_FOSS": "yes" }, "fontawesome": { "Package": "fontawesome", "Version": "0.5.3", "Source": "Repository", - "Requirements": [ - "htmltools", - "R", - "rlang" - ] + "Type": "Package", + "Title": "Easily Work with 'Font Awesome' Icons", + "Description": "Easily and flexibly insert 'Font Awesome' icons into 'R Markdown' documents and 'Shiny' apps. These icons can be inserted into HTML content through inline 'SVG' tags or 'i' tags. There is also a utility function for exporting 'Font Awesome' icons as 'PNG' images for those situations where raster graphics are needed.", + "Authors@R": "c( person(\"Richard\", \"Iannone\", , \"rich@posit.co\", c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0003-3925-190X\")), person(\"Christophe\", \"Dervieux\", , \"cderv@posit.co\", role = \"ctb\", comment = c(ORCID = \"0000-0003-4474-2498\")), person(\"Winston\", \"Chang\", , \"winston@posit.co\", role = \"ctb\"), person(\"Dave\", \"Gandy\", role = c(\"ctb\", \"cph\"), comment = \"Font-Awesome font\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "License": "MIT + file LICENSE", + "URL": "https://github.com/rstudio/fontawesome, https://rstudio.github.io/fontawesome/", + "BugReports": "https://github.com/rstudio/fontawesome/issues", + "Encoding": "UTF-8", + "ByteCompile": "true", + "RoxygenNote": "7.3.2", + "Depends": [ + "R (>= 3.3.0)" + ], + "Imports": [ + "rlang (>= 1.0.6)", + "htmltools (>= 0.5.1.1)" + ], + "Suggests": [ + "covr", + "dplyr (>= 1.0.8)", + "gt (>= 0.9.0)", + "knitr (>= 1.31)", + "testthat (>= 3.0.0)", + "rsvg" + ], + "Config/testthat/edition": "3", + "NeedsCompilation": "no", + "Author": "Richard Iannone [aut, cre] (), Christophe Dervieux [ctb] (), Winston Chang [ctb], Dave Gandy [ctb, cph] (Font-Awesome font), Posit Software, PBC [cph, fnd]", + "Maintainer": "Richard Iannone ", + "Repository": "CRAN" }, "fontquiver": { "Package": "fontquiver", "Version": "0.2.1", "Source": "Repository", - "Requirements": [ - "fontBitstreamVera", - "fontLiberation", - "R" - ] + "Title": "Set of Installed Fonts", + "Authors@R": "c( person(\"Lionel\", \"Henry\", , \"lionel@rstudio.com\", c(\"cre\", \"aut\")), person(\"RStudio\", role = \"cph\"), person(\"George Douros\", role = \"cph\", comment = \"Symbola font\"))", + "Description": "Provides a set of fonts with permissive licences. This is useful when you want to avoid system fonts to make sure your outputs are reproducible.", + "Depends": [ + "R (>= 3.0.0)" + ], + "Imports": [ + "fontBitstreamVera (>= 0.1.0)", + "fontLiberation (>= 0.1.0)" + ], + "Suggests": [ + "testthat", + "htmltools" + ], + "License": "GPL-3 | file LICENSE", + "Encoding": "UTF-8", + "LazyData": "true", + "RoxygenNote": "5.0.1", + "Collate": "'font-getters.R' 'fontset.R' 'fontset-bitstream-vera.R' 'fontset-dejavu.R' 'fontset-liberation.R' 'fontset-symbola.R' 'html-dependency.R' 'utils.R'", + "NeedsCompilation": "no", + "Author": "Lionel Henry [cre, aut], RStudio [cph], George Douros [cph] (Symbola font)", + "Maintainer": "Lionel Henry ", + "Repository": "CRAN" }, "fs": { "Package": "fs", - "Version": "2.1.0", - "Source": "Repository", - "Requirements": [ - "methods", - "R" - ] + "Version": "1.6.6", + "Source": "Repository", + "Title": "Cross-Platform File System Operations Based on 'libuv'", + "Authors@R": "c( person(\"Jim\", \"Hester\", role = \"aut\"), person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\"), person(\"Gábor\", \"Csárdi\", , \"csardi.gabor@gmail.com\", role = c(\"aut\", \"cre\")), person(\"libuv project contributors\", role = \"cph\", comment = \"libuv library\"), person(\"Joyent, Inc. and other Node contributors\", role = \"cph\", comment = \"libuv library\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "A cross-platform interface to file system operations, built on top of the 'libuv' C library.", + "License": "MIT + file LICENSE", + "URL": "https://fs.r-lib.org, https://github.com/r-lib/fs", + "BugReports": "https://github.com/r-lib/fs/issues", + "Depends": [ + "R (>= 3.6)" + ], + "Imports": [ + "methods" + ], + "Suggests": [ + "covr", + "crayon", + "knitr", + "pillar (>= 1.0.0)", + "rmarkdown", + "spelling", + "testthat (>= 3.0.0)", + "tibble (>= 1.1.0)", + "vctrs (>= 0.3.0)", + "withr" + ], + "VignetteBuilder": "knitr", + "ByteCompile": "true", + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Copyright": "file COPYRIGHTS", + "Encoding": "UTF-8", + "Language": "en-US", + "RoxygenNote": "7.2.3", + "SystemRequirements": "GNU make", + "NeedsCompilation": "yes", + "Author": "Jim Hester [aut], Hadley Wickham [aut], Gábor Csárdi [aut, cre], libuv project contributors [cph] (libuv library), Joyent, Inc. and other Node contributors [cph] (libuv library), Posit Software, PBC [cph, fnd]", + "Maintainer": "Gábor Csárdi ", + "Repository": "CRAN" }, "gdtools": { "Package": "gdtools", "Version": "0.5.1", "Source": "Repository", - "Requirements": [ - "fontquiver", + "Title": "Font Metrics and Font Management Utilities for R Graphics", + "Authors@R": "c( person(\"David\", \"Gohel\", , \"david.gohel@ardata.fr\", role = c(\"aut\", \"cre\")), person(\"Hadley\", \"Wickham\", , \"hadley@rstudio.com\", role = \"aut\"), person(\"Lionel\", \"Henry\", , \"lionel@rstudio.com\", role = \"aut\"), person(\"Jeroen\", \"Ooms\", , \"jeroen@berkeley.edu\", role = \"aut\", comment = c(ORCID = \"0000-0002-4035-0289\")), person(\"Yixuan\", \"Qiu\", role = \"ctb\"), person(\"R Core Team\", role = \"cph\", comment = \"Cairo code from X11 device\"), person(\"ArData\", role = \"cph\"), person(\"RStudio\", role = \"cph\") )", + "Description": "Compute text metrics (width, ascent, descent) using 'Cairo' and 'FreeType', independently of the active graphic device. Font lookup is delegated to 'systemfonts'. Additional utilities let users register 'Google Fonts' or bundled 'Liberation' fonts, check font availability, and assemble 'htmlDependency' objects so that fonts are correctly embedded in 'Shiny' applications, 'R Markdown' documents, and 'htmlwidgets' outputs such as 'ggiraph'.", + "License": "GPL-3 | file LICENSE", + "URL": "https://davidgohel.github.io/gdtools/", + "BugReports": "https://github.com/davidgohel/gdtools/issues", + "Depends": [ + "R (>= 4.0.0)" + ], + "Imports": [ + "fontquiver (>= 0.2.0)", "htmltools", - "R", - "Rcpp", - "systemfonts", + "Rcpp (>= 0.12.12)", + "systemfonts (>= 1.3.1)", "tools" - ] + ], + "Suggests": [ + "curl", + "gfonts", + "methods", + "testthat" + ], + "LinkingTo": [ + "Rcpp" + ], + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3", + "SystemRequirements": "cairo, freetype2, fontconfig", + "NeedsCompilation": "yes", + "Author": "David Gohel [aut, cre], Hadley Wickham [aut], Lionel Henry [aut], Jeroen Ooms [aut] (ORCID: ), Yixuan Qiu [ctb], R Core Team [cph] (Cairo code from X11 device), ArData [cph], RStudio [cph]", + "Maintainer": "David Gohel ", + "Repository": "CRAN" }, "generics": { "Package": "generics", "Version": "0.1.4", "Source": "Repository", - "Requirements": [ - "methods", - "R" - ] + "Title": "Common S3 Generics not Provided by Base R Methods Related to Model Fitting", + "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0003-4757-117X\")), person(\"Max\", \"Kuhn\", , \"max@posit.co\", role = \"aut\"), person(\"Davis\", \"Vaughan\", , \"davis@posit.co\", role = \"aut\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"https://ror.org/03wc8by49\")) )", + "Description": "In order to reduce potential package dependencies and conflicts, generics provides a number of commonly used S3 generics.", + "License": "MIT + file LICENSE", + "URL": "https://generics.r-lib.org, https://github.com/r-lib/generics", + "BugReports": "https://github.com/r-lib/generics/issues", + "Depends": [ + "R (>= 3.6)" + ], + "Imports": [ + "methods" + ], + "Suggests": [ + "covr", + "pkgload", + "testthat (>= 3.0.0)", + "tibble", + "withr" + ], + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2", + "NeedsCompilation": "no", + "Author": "Hadley Wickham [aut, cre] (ORCID: ), Max Kuhn [aut], Davis Vaughan [aut], Posit Software, PBC [cph, fnd] (ROR: )", + "Maintainer": "Hadley Wickham ", + "Repository": "CRAN" }, "ggplot2": { "Package": "ggplot2", - "Version": "4.0.3", - "Source": "Repository", - "Requirements": [ + "Version": "4.0.2", + "Source": "Repository", + "Title": "Create Elegant Data Visualisations Using the Grammar of Graphics", + "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0003-4757-117X\")), person(\"Winston\", \"Chang\", role = \"aut\", comment = c(ORCID = \"0000-0002-1576-2126\")), person(\"Lionel\", \"Henry\", role = \"aut\"), person(\"Thomas Lin\", \"Pedersen\", , \"thomas.pedersen@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-5147-4711\")), person(\"Kohske\", \"Takahashi\", role = \"aut\"), person(\"Claus\", \"Wilke\", role = \"aut\", comment = c(ORCID = \"0000-0002-7470-9261\")), person(\"Kara\", \"Woo\", role = \"aut\", comment = c(ORCID = \"0000-0002-5125-4188\")), person(\"Hiroaki\", \"Yutani\", role = \"aut\", comment = c(ORCID = \"0000-0002-3385-7233\")), person(\"Dewey\", \"Dunnington\", role = \"aut\", comment = c(ORCID = \"0000-0002-9415-4582\")), person(\"Teun\", \"van den Brand\", role = \"aut\", comment = c(ORCID = \"0000-0002-9335-7468\")), person(\"Posit, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", + "Description": "A system for 'declaratively' creating graphics, based on \"The Grammar of Graphics\". You provide the data, tell 'ggplot2' how to map variables to aesthetics, what graphical primitives to use, and it takes care of the details.", + "License": "MIT + file LICENSE", + "URL": "https://ggplot2.tidyverse.org, https://github.com/tidyverse/ggplot2", + "BugReports": "https://github.com/tidyverse/ggplot2/issues", + "Depends": [ + "R (>= 4.1)" + ], + "Imports": [ "cli", "grDevices", "grid", - "gtable", + "gtable (>= 0.3.6)", "isoband", - "lifecycle", - "R", - "rlang", + "lifecycle (> 1.0.1)", + "rlang (>= 1.1.0)", "S7", - "scales", + "scales (>= 1.4.0)", "stats", - "vctrs", - "withr" - ] + "vctrs (>= 0.6.0)", + "withr (>= 2.5.0)" + ], + "Suggests": [ + "broom", + "covr", + "dplyr", + "ggplot2movies", + "hexbin", + "Hmisc", + "hms", + "knitr", + "mapproj", + "maps", + "MASS", + "mgcv", + "multcomp", + "munsell", + "nlme", + "profvis", + "quantreg", + "quarto", + "ragg (>= 1.2.6)", + "RColorBrewer", + "roxygen2", + "rpart", + "sf (>= 0.7-3)", + "svglite (>= 2.1.2)", + "testthat (>= 3.1.5)", + "tibble", + "vdiffr (>= 1.0.6)", + "xml2" + ], + "Enhances": [ + "sp" + ], + "VignetteBuilder": "quarto", + "Config/Needs/website": "ggtext, tidyr, forcats, tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Config/usethis/last-upkeep": "2025-04-23", + "Encoding": "UTF-8", + "LazyData": "true", + "RoxygenNote": "7.3.3", + "Collate": "'ggproto.R' 'ggplot-global.R' 'aaa-.R' 'aes-colour-fill-alpha.R' 'aes-evaluation.R' 'aes-group-order.R' 'aes-linetype-size-shape.R' 'aes-position.R' 'all-classes.R' 'compat-plyr.R' 'utilities.R' 'aes.R' 'annotation-borders.R' 'utilities-checks.R' 'legend-draw.R' 'geom-.R' 'annotation-custom.R' 'annotation-logticks.R' 'scale-type.R' 'layer.R' 'make-constructor.R' 'geom-polygon.R' 'geom-map.R' 'annotation-map.R' 'geom-raster.R' 'annotation-raster.R' 'annotation.R' 'autolayer.R' 'autoplot.R' 'axis-secondary.R' 'backports.R' 'bench.R' 'bin.R' 'coord-.R' 'coord-cartesian-.R' 'coord-fixed.R' 'coord-flip.R' 'coord-map.R' 'coord-munch.R' 'coord-polar.R' 'coord-quickmap.R' 'coord-radial.R' 'coord-sf.R' 'coord-transform.R' 'data.R' 'docs_layer.R' 'facet-.R' 'facet-grid-.R' 'facet-null.R' 'facet-wrap.R' 'fortify-map.R' 'fortify-models.R' 'fortify-spatial.R' 'fortify.R' 'stat-.R' 'geom-abline.R' 'geom-rect.R' 'geom-bar.R' 'geom-tile.R' 'geom-bin2d.R' 'geom-blank.R' 'geom-boxplot.R' 'geom-col.R' 'geom-path.R' 'geom-contour.R' 'geom-point.R' 'geom-count.R' 'geom-crossbar.R' 'geom-segment.R' 'geom-curve.R' 'geom-defaults.R' 'geom-ribbon.R' 'geom-density.R' 'geom-density2d.R' 'geom-dotplot.R' 'geom-errorbar.R' 'geom-freqpoly.R' 'geom-function.R' 'geom-hex.R' 'geom-histogram.R' 'geom-hline.R' 'geom-jitter.R' 'geom-label.R' 'geom-linerange.R' 'geom-pointrange.R' 'geom-quantile.R' 'geom-rug.R' 'geom-sf.R' 'geom-smooth.R' 'geom-spoke.R' 'geom-text.R' 'geom-violin.R' 'geom-vline.R' 'ggplot2-package.R' 'grob-absolute.R' 'grob-dotstack.R' 'grob-null.R' 'grouping.R' 'properties.R' 'margins.R' 'theme-elements.R' 'guide-.R' 'guide-axis.R' 'guide-axis-logticks.R' 'guide-axis-stack.R' 'guide-axis-theta.R' 'guide-legend.R' 'guide-bins.R' 'guide-colorbar.R' 'guide-colorsteps.R' 'guide-custom.R' 'guide-none.R' 'guide-old.R' 'guides-.R' 'guides-grid.R' 'hexbin.R' 'import-standalone-obj-type.R' 'import-standalone-types-check.R' 'labeller.R' 'labels.R' 'layer-sf.R' 'layout.R' 'limits.R' 'performance.R' 'plot-build.R' 'plot-construction.R' 'plot-last.R' 'plot.R' 'position-.R' 'position-collide.R' 'position-dodge.R' 'position-dodge2.R' 'position-identity.R' 'position-jitter.R' 'position-jitterdodge.R' 'position-nudge.R' 'position-stack.R' 'quick-plot.R' 'reshape-add-margins.R' 'save.R' 'scale-.R' 'scale-alpha.R' 'scale-binned.R' 'scale-brewer.R' 'scale-colour.R' 'scale-continuous.R' 'scale-date.R' 'scale-discrete-.R' 'scale-expansion.R' 'scale-gradient.R' 'scale-grey.R' 'scale-hue.R' 'scale-identity.R' 'scale-linetype.R' 'scale-linewidth.R' 'scale-manual.R' 'scale-shape.R' 'scale-size.R' 'scale-steps.R' 'scale-view.R' 'scale-viridis.R' 'scales-.R' 'stat-align.R' 'stat-bin.R' 'stat-summary-2d.R' 'stat-bin2d.R' 'stat-bindot.R' 'stat-binhex.R' 'stat-boxplot.R' 'stat-connect.R' 'stat-contour.R' 'stat-count.R' 'stat-density-2d.R' 'stat-density.R' 'stat-ecdf.R' 'stat-ellipse.R' 'stat-function.R' 'stat-identity.R' 'stat-manual.R' 'stat-qq-line.R' 'stat-qq.R' 'stat-quantilemethods.R' 'stat-sf-coordinates.R' 'stat-sf.R' 'stat-smooth-methods.R' 'stat-smooth.R' 'stat-sum.R' 'stat-summary-bin.R' 'stat-summary-hex.R' 'stat-summary.R' 'stat-unique.R' 'stat-ydensity.R' 'summarise-plot.R' 'summary.R' 'theme.R' 'theme-defaults.R' 'theme-current.R' 'theme-sub.R' 'utilities-break.R' 'utilities-grid.R' 'utilities-help.R' 'utilities-patterns.R' 'utilities-resolution.R' 'utilities-tidy-eval.R' 'zxx.R' 'zzz.R'", + "NeedsCompilation": "no", + "Author": "Hadley Wickham [aut] (ORCID: ), Winston Chang [aut] (ORCID: ), Lionel Henry [aut], Thomas Lin Pedersen [aut, cre] (ORCID: ), Kohske Takahashi [aut], Claus Wilke [aut] (ORCID: ), Kara Woo [aut] (ORCID: ), Hiroaki Yutani [aut] (ORCID: ), Dewey Dunnington [aut] (ORCID: ), Teun van den Brand [aut] (ORCID: ), Posit, PBC [cph, fnd] (ROR: )", + "Maintainer": "Thomas Lin Pedersen ", + "Repository": "CRAN" }, "glue": { "Package": "glue", - "Version": "1.8.1", - "Source": "Repository", - "Requirements": [ - "methods", - "R" - ] + "Version": "1.8.0", + "Source": "Repository", + "Title": "Interpreted String Literals", + "Authors@R": "c( person(\"Jim\", \"Hester\", role = \"aut\", comment = c(ORCID = \"0000-0002-2739-7082\")), person(\"Jennifer\", \"Bryan\", , \"jenny@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-6983-2759\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "An implementation of interpreted string literals, inspired by Python's Literal String Interpolation and Docstrings and Julia's Triple-Quoted String Literals .", + "License": "MIT + file LICENSE", + "URL": "https://glue.tidyverse.org/, https://github.com/tidyverse/glue", + "BugReports": "https://github.com/tidyverse/glue/issues", + "Depends": [ + "R (>= 3.6)" + ], + "Imports": [ + "methods" + ], + "Suggests": [ + "crayon", + "DBI (>= 1.2.0)", + "dplyr", + "knitr", + "magrittr", + "rlang", + "rmarkdown", + "RSQLite", + "testthat (>= 3.2.0)", + "vctrs (>= 0.3.0)", + "waldo (>= 0.5.3)", + "withr" + ], + "VignetteBuilder": "knitr", + "ByteCompile": "true", + "Config/Needs/website": "bench, forcats, ggbeeswarm, ggplot2, R.utils, rprintf, tidyr, tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2", + "NeedsCompilation": "yes", + "Author": "Jim Hester [aut] (), Jennifer Bryan [aut, cre] (), Posit Software, PBC [cph, fnd]", + "Maintainer": "Jennifer Bryan ", + "Repository": "CRAN" }, "gridExtra": { "Package": "gridExtra", - "Version": "2.3.1", - "Source": "Repository", - "Requirements": [ - "graphics", - "grDevices", - "grid", + "Version": "2.3", + "Source": "Repository", + "Authors@R": "c(person(\"Baptiste\", \"Auguie\", email = \"baptiste.auguie@gmail.com\", role = c(\"aut\", \"cre\")), person(\"Anton\", \"Antonov\", email = \"tonytonov@gmail.com\", role = c(\"ctb\")))", + "License": "GPL (>= 2)", + "Title": "Miscellaneous Functions for \"Grid\" Graphics", + "Type": "Package", + "Description": "Provides a number of user-level functions to work with \"grid\" graphics, notably to arrange multiple grid-based plots on a page, and draw tables.", + "VignetteBuilder": "knitr", + "Imports": [ "gtable", + "grid", + "grDevices", + "graphics", "utils" - ] + ], + "Suggests": [ + "ggplot2", + "egg", + "lattice", + "knitr", + "testthat" + ], + "RoxygenNote": "6.0.1", + "NeedsCompilation": "no", + "Author": "Baptiste Auguie [aut, cre], Anton Antonov [ctb]", + "Maintainer": "Baptiste Auguie ", + "Repository": "CRAN" }, "gridGraphics": { "Package": "gridGraphics", "Version": "0.5-1", "Source": "Repository", - "Requirements": [ - "graphics", - "grDevices", - "grid" - ] + "Title": "Redraw Base Graphics Using 'grid' Graphics", + "Authors@R": "c(person(\"Paul\", \"Murrell\", role = c(\"cre\", \"aut\"), email = \"paul@stat.auckland.ac.nz\"), person(\"Zhijian\", \"Wen\", role = \"aut\", email = \"jwen246@aucklanduni.ac.nz\"))", + "Description": "Functions to convert a page of plots drawn with the 'graphics' package into identical output drawn with the 'grid' package. The result looks like the original 'graphics'-based plot, but consists of 'grid' grobs and viewports that can then be manipulated with 'grid' functions (e.g., edit grobs and revisit viewports).", + "Depends": [ + "grid", + "graphics" + ], + "Imports": [ + "grDevices" + ], + "Suggests": [ + "magick (>= 1.3)", + "pdftools (>= 1.6)" + ], + "License": "GPL (>= 2)", + "URL": "https://github.com/pmur002/gridgraphics", + "NeedsCompilation": "no", + "Author": "Paul Murrell [cre, aut], Zhijian Wen [aut]", + "Maintainer": "Paul Murrell ", + "Repository": "CRAN" }, "gtable": { "Package": "gtable", "Version": "0.3.6", "Source": "Repository", - "Requirements": [ + "Title": "Arrange 'Grobs' in Tables", + "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\"), person(\"Thomas Lin\", \"Pedersen\", , \"thomas.pedersen@posit.co\", role = c(\"aut\", \"cre\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "Tools to make it easier to work with \"tables\" of 'grobs'. The 'gtable' package defines a 'gtable' grob class that specifies a grid along with a list of grobs and their placement in the grid. Further the package makes it easy to manipulate and combine 'gtable' objects so that complex compositions can be built up sequentially.", + "License": "MIT + file LICENSE", + "URL": "https://gtable.r-lib.org, https://github.com/r-lib/gtable", + "BugReports": "https://github.com/r-lib/gtable/issues", + "Depends": [ + "R (>= 4.0)" + ], + "Imports": [ "cli", "glue", "grid", "lifecycle", - "R", - "rlang", + "rlang (>= 1.1.0)", "stats" - ] + ], + "Suggests": [ + "covr", + "ggplot2", + "knitr", + "profvis", + "rmarkdown", + "testthat (>= 3.0.0)" + ], + "VignetteBuilder": "knitr", + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Config/usethis/last-upkeep": "2024-10-25", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2", + "NeedsCompilation": "no", + "Author": "Hadley Wickham [aut], Thomas Lin Pedersen [aut, cre], Posit Software, PBC [cph, fnd]", + "Maintainer": "Thomas Lin Pedersen ", + "Repository": "CRAN" }, "highr": { "Package": "highr", - "Version": "0.12", - "Source": "Repository", - "Requirements": [ - "R", - "xfun" - ] + "Version": "0.11", + "Source": "Repository", + "Type": "Package", + "Title": "Syntax Highlighting for R Source Code", + "Authors@R": "c( person(\"Yihui\", \"Xie\", role = c(\"aut\", \"cre\"), email = \"xie@yihui.name\", comment = c(ORCID = \"0000-0003-0645-5666\")), person(\"Yixuan\", \"Qiu\", role = \"aut\"), person(\"Christopher\", \"Gandrud\", role = \"ctb\"), person(\"Qiang\", \"Li\", role = \"ctb\") )", + "Description": "Provides syntax highlighting for R source code. Currently it supports LaTeX and HTML output. Source code of other languages is supported via Andre Simon's highlight package ().", + "Depends": [ + "R (>= 3.3.0)" + ], + "Imports": [ + "xfun (>= 0.18)" + ], + "Suggests": [ + "knitr", + "markdown", + "testit" + ], + "License": "GPL", + "URL": "https://github.com/yihui/highr", + "BugReports": "https://github.com/yihui/highr/issues", + "VignetteBuilder": "knitr", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.1", + "NeedsCompilation": "no", + "Author": "Yihui Xie [aut, cre] (), Yixuan Qiu [aut], Christopher Gandrud [ctb], Qiang Li [ctb]", + "Maintainer": "Yihui Xie ", + "Repository": "CRAN" }, "htmltools": { "Package": "htmltools", "Version": "0.5.9", "Source": "Repository", - "Requirements": [ + "Type": "Package", + "Title": "Tools for HTML", + "Authors@R": "c( person(\"Joe\", \"Cheng\", , \"joe@posit.co\", role = \"aut\"), person(\"Carson\", \"Sievert\", , \"carson@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-4958-2844\")), person(\"Barret\", \"Schloerke\", , \"barret@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0001-9986-114X\")), person(\"Winston\", \"Chang\", , \"winston@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0002-1576-2126\")), person(\"Yihui\", \"Xie\", , \"yihui@posit.co\", role = \"aut\"), person(\"Jeff\", \"Allen\", role = \"aut\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "Tools for HTML generation and output.", + "License": "GPL (>= 2)", + "URL": "https://github.com/rstudio/htmltools, https://rstudio.github.io/htmltools/", + "BugReports": "https://github.com/rstudio/htmltools/issues", + "Depends": [ + "R (>= 2.14.1)" + ], + "Imports": [ "base64enc", "digest", - "fastmap", + "fastmap (>= 1.1.0)", "grDevices", - "R", - "rlang", + "rlang (>= 1.0.0)", "utils" - ] + ], + "Suggests": [ + "Cairo", + "markdown", + "ragg", + "shiny", + "testthat", + "withr" + ], + "Enhances": [ + "knitr" + ], + "Config/Needs/check": "knitr", + "Config/Needs/website": "rstudio/quillt, bench", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3", + "Collate": "'colors.R' 'fill.R' 'html_dependency.R' 'html_escape.R' 'html_print.R' 'htmltools-package.R' 'images.R' 'known_tags.R' 'selector.R' 'staticimports.R' 'tag_query.R' 'utils.R' 'tags.R' 'template.R'", + "NeedsCompilation": "yes", + "Author": "Joe Cheng [aut], Carson Sievert [aut, cre] (ORCID: ), Barret Schloerke [aut] (ORCID: ), Winston Chang [aut] (ORCID: ), Yihui Xie [aut], Jeff Allen [aut], Posit Software, PBC [cph, fnd]", + "Maintainer": "Carson Sievert ", + "Repository": "CRAN" }, "htmlwidgets": { "Package": "htmlwidgets", "Version": "1.6.4", "Source": "Repository", - "Requirements": [ + "Type": "Package", + "Title": "HTML Widgets for R", + "Authors@R": "c( person(\"Ramnath\", \"Vaidyanathan\", role = c(\"aut\", \"cph\")), person(\"Yihui\", \"Xie\", role = \"aut\"), person(\"JJ\", \"Allaire\", role = \"aut\"), person(\"Joe\", \"Cheng\", , \"joe@posit.co\", role = \"aut\"), person(\"Carson\", \"Sievert\", , \"carson@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-4958-2844\")), person(\"Kenton\", \"Russell\", role = c(\"aut\", \"cph\")), person(\"Ellis\", \"Hughes\", role = \"ctb\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "A framework for creating HTML widgets that render in various contexts including the R console, 'R Markdown' documents, and 'Shiny' web applications.", + "License": "MIT + file LICENSE", + "URL": "https://github.com/ramnathv/htmlwidgets", + "BugReports": "https://github.com/ramnathv/htmlwidgets/issues", + "Imports": [ "grDevices", - "htmltools", - "jsonlite", - "knitr", + "htmltools (>= 0.5.7)", + "jsonlite (>= 0.9.16)", + "knitr (>= 1.8)", "rmarkdown", "yaml" - ] + ], + "Suggests": [ + "testthat" + ], + "Enhances": [ + "shiny (>= 1.1)" + ], + "VignetteBuilder": "knitr", + "Encoding": "UTF-8", + "RoxygenNote": "7.2.3", + "NeedsCompilation": "no", + "Author": "Ramnath Vaidyanathan [aut, cph], Yihui Xie [aut], JJ Allaire [aut], Joe Cheng [aut], Carson Sievert [aut, cre] (), Kenton Russell [aut, cph], Ellis Hughes [ctb], Posit Software, PBC [cph, fnd]", + "Maintainer": "Carson Sievert ", + "Repository": "CRAN" }, "httr": { "Package": "httr", - "Version": "1.4.9", - "Source": "Repository", - "Requirements": [ - "curl", + "Version": "1.4.7", + "Source": "Repository", + "Title": "Tools for Working with URLs and HTTP", + "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = c(\"aut\", \"cre\")), person(\"Posit, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "Useful tools for working with HTTP organised by HTTP verbs (GET(), POST(), etc). Configuration functions make it easy to control additional request components (authenticate(), add_headers() and so on).", + "License": "MIT + file LICENSE", + "URL": "https://httr.r-lib.org/, https://github.com/r-lib/httr", + "BugReports": "https://github.com/r-lib/httr/issues", + "Depends": [ + "R (>= 3.5)" + ], + "Imports": [ + "curl (>= 5.0.2)", "jsonlite", "mime", - "openssl", - "R", + "openssl (>= 0.8)", "R6" - ] + ], + "Suggests": [ + "covr", + "httpuv", + "jpeg", + "knitr", + "png", + "readr", + "rmarkdown", + "testthat (>= 0.8.0)", + "xml2" + ], + "VignetteBuilder": "knitr", + "Config/Needs/website": "tidyverse/tidytemplate", + "Encoding": "UTF-8", + "RoxygenNote": "7.2.3", + "NeedsCompilation": "no", + "Author": "Hadley Wickham [aut, cre], Posit, PBC [cph, fnd]", + "Maintainer": "Hadley Wickham ", + "Repository": "CRAN" }, "isoband": { "Package": "isoband", "Version": "0.3.0", "Source": "Repository", - "Requirements": [ + "Title": "Generate Isolines and Isobands from Regularly Spaced Elevation Grids", + "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0003-4757-117X\")), person(\"Claus O.\", \"Wilke\", , \"wilke@austin.utexas.edu\", role = \"aut\", comment = c(\"Original author\", ORCID = \"0000-0002-7470-9261\")), person(\"Thomas Lin\", \"Pedersen\", , \"thomas.pedersen@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-5147-4711\")), person(\"Posit, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", + "Description": "A fast C++ implementation to generate contour lines (isolines) and contour polygons (isobands) from regularly spaced grids containing elevation data.", + "License": "MIT + file LICENSE", + "URL": "https://isoband.r-lib.org, https://github.com/r-lib/isoband", + "BugReports": "https://github.com/r-lib/isoband/issues", + "Imports": [ "cli", - "cpp11", "grid", "rlang", "utils" - ] + ], + "Suggests": [ + "covr", + "ggplot2", + "knitr", + "magick", + "bench", + "rmarkdown", + "sf", + "testthat (>= 3.0.0)", + "xml2" + ], + "VignetteBuilder": "knitr", + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Config/usethis/last-upkeep": "2025-12-05", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3", + "Config/build/compilation-database": "true", + "LinkingTo": [ + "cpp11" + ], + "NeedsCompilation": "yes", + "Author": "Hadley Wickham [aut] (ORCID: ), Claus O. Wilke [aut] (Original author, ORCID: ), Thomas Lin Pedersen [aut, cre] (ORCID: ), Posit, PBC [cph, fnd] (ROR: )", + "Maintainer": "Thomas Lin Pedersen ", + "Repository": "CRAN" }, "jaspBase": { "Package": "jaspBase", "Version": "0.20.4", - "Source": "Local", - "Requirements": ["cli", "codetools", "compiler", "ggplot2", "grDevices", "grid", "gridExtra", "gridGraphics", "jaspGraphs", "jsonlite", "lifecycle", "methods", "officer", "pkgbuild", "plyr", "ragg", "R6", "Rcpp", "rvg", "svglite", "systemfonts", "withr"], - "RemoteType": "local", - "RemoteUrl": "/home/virtuoos/Broncode/JASP/jasp-desktop/Engine/jaspBase", - "Cacheable": true, - "Hash": "7fa78a6a9cb16c17cf2707e7bb5ff577" + "Source": "GitHub", + "Type": "Package", + "Title": "JASP Base", + "Author": "JASP Team", + "Maintainer": "Bruno Boutin ", + "Description": "Package contains the JASP Bayesian and Frequentist analyses. See for more details.", + "License": "GPL2+", + "Imports": [ + "cli", + "codetools", + "compiler", + "ggplot2", + "grDevices", + "grid", + "gridExtra", + "gridGraphics", + "jaspGraphs", + "jsonlite", + "lifecycle", + "methods", + "officer", + "pkgbuild", + "plyr", + "ragg", + "R6", + "Rcpp (>= 0.12.14)", + "rvg", + "svglite", + "systemfonts", + "withr" + ], + "RoxygenNote": "7.3.3", + "Roxygen": "list(markdown = TRUE)", + "Encoding": "UTF-8", + "LinkingTo": [ + "Rcpp" + ], + "RcppModules": "jaspResults", + "NeedsCompilation": "yes", + "Suggests": [ + "testthat (>= 3.0.0)" + ], + "Config/testthat/edition": "3", + "RemoteType": "github", + "RemoteHost": "api.github.com", + "RemoteUsername": "jasp-stats", + "RemoteRepo": "jaspBase", + "RemoteRef": "master", + "RemoteSha": "72cb31d6c535e841006e8ebca81170f31a418e79", + "Remotes": "jasp-stats/jaspGraphs" }, "jaspGraphs": { "Package": "jaspGraphs", "Version": "0.20.0", "Source": "GitHub", - "Requirements": [ + "Type": "Package", + "Title": "Custom Graphs for JASP", + "Author": "Don van den Bergh", + "Maintainer": "JASP-team ", + "Description": "Graph making functions and wrappers for JASP.", + "License": "GPL", + "Encoding": "UTF-8", + "LazyData": "true", + "Depends": [ + "R (>= 4.1.0)" + ], + "Imports": [ "cli", - "ggplot2", + "ggplot2 (>= 3.0.0)", "gridExtra", "gtable", "htmlwidgets", @@ -433,114 +1401,356 @@ "plotly", "R6", "rlang", - "scales", + "scales" + ], + "Suggests": [ "testthat", "vdiffr" ], + "Roxygen": "list(markdown = TRUE)", + "Config/roxygen2/version": "8.1.0", "RemoteType": "github", "RemoteHost": "api.github.com", "RemoteUsername": "jasp-stats", "RemoteRepo": "jaspGraphs", - "RemoteSha": "cdc4c9cf20f52c22c24162ea6fcbdd6b211e92db" + "RemoteRef": "master", + "RemoteSha": "288d2751131ee6ac17362bd89d2ed7d9c0c67c7c" }, "jquerylib": { "Package": "jquerylib", "Version": "0.1.4", "Source": "Repository", - "Requirements": [ + "Title": "Obtain 'jQuery' as an HTML Dependency Object", + "Authors@R": "c( person(\"Carson\", \"Sievert\", role = c(\"aut\", \"cre\"), email = \"carson@rstudio.com\", comment = c(ORCID = \"0000-0002-4958-2844\")), person(\"Joe\", \"Cheng\", role = \"aut\", email = \"joe@rstudio.com\"), person(family = \"RStudio\", role = \"cph\"), person(family = \"jQuery Foundation\", role = \"cph\", comment = \"jQuery library and jQuery UI library\"), person(family = \"jQuery contributors\", role = c(\"ctb\", \"cph\"), comment = \"jQuery library; authors listed in inst/lib/jquery-AUTHORS.txt\") )", + "Description": "Obtain any major version of 'jQuery' () and use it in any webpage generated by 'htmltools' (e.g. 'shiny', 'htmlwidgets', and 'rmarkdown'). Most R users don't need to use this package directly, but other R packages (e.g. 'shiny', 'rmarkdown', etc.) depend on this package to avoid bundling redundant copies of 'jQuery'.", + "License": "MIT + file LICENSE", + "Encoding": "UTF-8", + "Config/testthat/edition": "3", + "RoxygenNote": "7.0.2", + "Imports": [ "htmltools" - ] + ], + "Suggests": [ + "testthat" + ], + "NeedsCompilation": "no", + "Author": "Carson Sievert [aut, cre] (), Joe Cheng [aut], RStudio [cph], jQuery Foundation [cph] (jQuery library and jQuery UI library), jQuery contributors [ctb, cph] (jQuery library; authors listed in inst/lib/jquery-AUTHORS.txt)", + "Maintainer": "Carson Sievert ", + "Repository": "CRAN" }, "jsonlite": { "Package": "jsonlite", "Version": "2.0.0", "Source": "Repository", - "Requirements": [ + "Title": "A Simple and Robust JSON Parser and Generator for R", + "License": "MIT + file LICENSE", + "Depends": [ "methods" - ] + ], + "Authors@R": "c( person(\"Jeroen\", \"Ooms\", role = c(\"aut\", \"cre\"), email = \"jeroenooms@gmail.com\", comment = c(ORCID = \"0000-0002-4035-0289\")), person(\"Duncan\", \"Temple Lang\", role = \"ctb\"), person(\"Lloyd\", \"Hilaiel\", role = \"cph\", comment=\"author of bundled libyajl\"))", + "URL": "https://jeroen.r-universe.dev/jsonlite https://arxiv.org/abs/1403.2805", + "BugReports": "https://github.com/jeroen/jsonlite/issues", + "Maintainer": "Jeroen Ooms ", + "VignetteBuilder": "knitr, R.rsp", + "Description": "A reasonably fast JSON parser and generator, optimized for statistical data and the web. Offers simple, flexible tools for working with JSON in R, and is particularly powerful for building pipelines and interacting with a web API. The implementation is based on the mapping described in the vignette (Ooms, 2014). In addition to converting JSON data from/to R objects, 'jsonlite' contains functions to stream, validate, and prettify JSON data. The unit tests included with the package verify that all edge cases are encoded and decoded consistently for use with dynamic data in systems and applications.", + "Suggests": [ + "httr", + "vctrs", + "testthat", + "knitr", + "rmarkdown", + "R.rsp", + "sf" + ], + "RoxygenNote": "7.3.2", + "Encoding": "UTF-8", + "NeedsCompilation": "yes", + "Author": "Jeroen Ooms [aut, cre] (), Duncan Temple Lang [ctb], Lloyd Hilaiel [cph] (author of bundled libyajl)", + "Repository": "CRAN" }, "knitr": { "Package": "knitr", - "Version": "1.52", - "Source": "Repository", - "Requirements": [ - "evaluate", - "highr", + "Version": "1.51", + "Source": "Repository", + "Type": "Package", + "Title": "A General-Purpose Package for Dynamic Report Generation in R", + "Authors@R": "c( person(\"Yihui\", \"Xie\", role = c(\"aut\", \"cre\"), email = \"xie@yihui.name\", comment = c(ORCID = \"0000-0003-0645-5666\", URL = \"https://yihui.org\")), person(\"Abhraneel\", \"Sarma\", role = \"ctb\"), person(\"Adam\", \"Vogt\", role = \"ctb\"), person(\"Alastair\", \"Andrew\", role = \"ctb\"), person(\"Alex\", \"Zvoleff\", role = \"ctb\"), person(\"Amar\", \"Al-Zubaidi\", role = \"ctb\"), person(\"Andre\", \"Simon\", role = \"ctb\", comment = \"the CSS files under inst/themes/ were derived from the Highlight package http://www.andre-simon.de\"), person(\"Aron\", \"Atkins\", role = \"ctb\"), person(\"Aaron\", \"Wolen\", role = \"ctb\"), person(\"Ashley\", \"Manton\", role = \"ctb\"), person(\"Atsushi\", \"Yasumoto\", role = \"ctb\", comment = c(ORCID = \"0000-0002-8335-495X\")), person(\"Ben\", \"Baumer\", role = \"ctb\"), person(\"Brian\", \"Diggs\", role = \"ctb\"), person(\"Brian\", \"Zhang\", role = \"ctb\"), person(\"Bulat\", \"Yapparov\", role = \"ctb\"), person(\"Cassio\", \"Pereira\", role = \"ctb\"), person(\"Christophe\", \"Dervieux\", role = \"ctb\"), person(\"David\", \"Hall\", role = \"ctb\"), person(\"David\", \"Hugh-Jones\", role = \"ctb\"), person(\"David\", \"Robinson\", role = \"ctb\"), person(\"Doug\", \"Hemken\", role = \"ctb\"), person(\"Duncan\", \"Murdoch\", role = \"ctb\"), person(\"Elio\", \"Campitelli\", role = \"ctb\"), person(\"Ellis\", \"Hughes\", role = \"ctb\"), person(\"Emily\", \"Riederer\", role = \"ctb\"), person(\"Fabian\", \"Hirschmann\", role = \"ctb\"), person(\"Fitch\", \"Simeon\", role = \"ctb\"), person(\"Forest\", \"Fang\", role = \"ctb\"), person(c(\"Frank\", \"E\", \"Harrell\", \"Jr\"), role = \"ctb\", comment = \"the Sweavel package at inst/misc/Sweavel.sty\"), person(\"Garrick\", \"Aden-Buie\", role = \"ctb\"), person(\"Gregoire\", \"Detrez\", role = \"ctb\"), person(\"Hadley\", \"Wickham\", role = \"ctb\"), person(\"Hao\", \"Zhu\", role = \"ctb\"), person(\"Heewon\", \"Jeon\", role = \"ctb\"), person(\"Henrik\", \"Bengtsson\", role = \"ctb\"), person(\"Hiroaki\", \"Yutani\", role = \"ctb\"), person(\"Ian\", \"Lyttle\", role = \"ctb\"), person(\"Hodges\", \"Daniel\", role = \"ctb\"), person(\"Jacob\", \"Bien\", role = \"ctb\"), person(\"Jake\", \"Burkhead\", role = \"ctb\"), person(\"James\", \"Manton\", role = \"ctb\"), person(\"Jared\", \"Lander\", role = \"ctb\"), person(\"Jason\", \"Punyon\", role = \"ctb\"), person(\"Javier\", \"Luraschi\", role = \"ctb\"), person(\"Jeff\", \"Arnold\", role = \"ctb\"), person(\"Jenny\", \"Bryan\", role = \"ctb\"), person(\"Jeremy\", \"Ashkenas\", role = c(\"ctb\", \"cph\"), comment = \"the CSS file at inst/misc/docco-classic.css\"), person(\"Jeremy\", \"Stephens\", role = \"ctb\"), person(\"Jim\", \"Hester\", role = \"ctb\"), person(\"Joe\", \"Cheng\", role = \"ctb\"), person(\"Johannes\", \"Ranke\", role = \"ctb\"), person(\"John\", \"Honaker\", role = \"ctb\"), person(\"John\", \"Muschelli\", role = \"ctb\"), person(\"Jonathan\", \"Keane\", role = \"ctb\"), person(\"JJ\", \"Allaire\", role = \"ctb\"), person(\"Johan\", \"Toloe\", role = \"ctb\"), person(\"Jonathan\", \"Sidi\", role = \"ctb\"), person(\"Joseph\", \"Larmarange\", role = \"ctb\"), person(\"Julien\", \"Barnier\", role = \"ctb\"), person(\"Kaiyin\", \"Zhong\", role = \"ctb\"), person(\"Kamil\", \"Slowikowski\", role = \"ctb\"), person(\"Karl\", \"Forner\", role = \"ctb\"), person(c(\"Kevin\", \"K.\"), \"Smith\", role = \"ctb\"), person(\"Kirill\", \"Mueller\", role = \"ctb\"), person(\"Kohske\", \"Takahashi\", role = \"ctb\"), person(\"Lorenz\", \"Walthert\", role = \"ctb\"), person(\"Lucas\", \"Gallindo\", role = \"ctb\"), person(\"Marius\", \"Hofert\", role = \"ctb\"), person(\"Martin\", \"Modrák\", role = \"ctb\"), person(\"Michael\", \"Chirico\", role = \"ctb\"), person(\"Michael\", \"Friendly\", role = \"ctb\"), person(\"Michal\", \"Bojanowski\", role = \"ctb\"), person(\"Michel\", \"Kuhlmann\", role = \"ctb\"), person(\"Miller\", \"Patrick\", role = \"ctb\"), person(\"Nacho\", \"Caballero\", role = \"ctb\"), person(\"Nick\", \"Salkowski\", role = \"ctb\"), person(\"Niels Richard\", \"Hansen\", role = \"ctb\"), person(\"Noam\", \"Ross\", role = \"ctb\"), person(\"Obada\", \"Mahdi\", role = \"ctb\"), person(\"Pavel N.\", \"Krivitsky\", role = \"ctb\", comment=c(ORCID = \"0000-0002-9101-3362\")), person(\"Pedro\", \"Faria\", role = \"ctb\"), person(\"Qiang\", \"Li\", role = \"ctb\"), person(\"Ramnath\", \"Vaidyanathan\", role = \"ctb\"), person(\"Richard\", \"Cotton\", role = \"ctb\"), person(\"Robert\", \"Krzyzanowski\", role = \"ctb\"), person(\"Rodrigo\", \"Copetti\", role = \"ctb\"), person(\"Romain\", \"Francois\", role = \"ctb\"), person(\"Ruaridh\", \"Williamson\", role = \"ctb\"), person(\"Sagiru\", \"Mati\", role = \"ctb\", comment = c(ORCID = \"0000-0003-1413-3974\")), person(\"Scott\", \"Kostyshak\", role = \"ctb\"), person(\"Sebastian\", \"Meyer\", role = \"ctb\"), person(\"Sietse\", \"Brouwer\", role = \"ctb\"), person(c(\"Simon\", \"de\"), \"Bernard\", role = \"ctb\"), person(\"Sylvain\", \"Rousseau\", role = \"ctb\"), person(\"Taiyun\", \"Wei\", role = \"ctb\"), person(\"Thibaut\", \"Assus\", role = \"ctb\"), person(\"Thibaut\", \"Lamadon\", role = \"ctb\"), person(\"Thomas\", \"Leeper\", role = \"ctb\"), person(\"Tim\", \"Mastny\", role = \"ctb\"), person(\"Tom\", \"Torsney-Weir\", role = \"ctb\"), person(\"Trevor\", \"Davis\", role = \"ctb\"), person(\"Viktoras\", \"Veitas\", role = \"ctb\"), person(\"Weicheng\", \"Zhu\", role = \"ctb\"), person(\"Wush\", \"Wu\", role = \"ctb\"), person(\"Zachary\", \"Foster\", role = \"ctb\"), person(\"Zhian N.\", \"Kamvar\", role = \"ctb\", comment = c(ORCID = \"0000-0003-1458-7108\")), person(given = \"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "Provides a general-purpose tool for dynamic report generation in R using Literate Programming techniques.", + "Depends": [ + "R (>= 3.6.0)" + ], + "Imports": [ + "evaluate (>= 0.15)", + "highr (>= 0.11)", "methods", - "R", "tools", - "xfun", - "yaml" - ] + "xfun (>= 0.52)", + "yaml (>= 2.1.19)" + ], + "Suggests": [ + "bslib", + "DBI (>= 0.4-1)", + "digest", + "formatR", + "gifski", + "gridSVG", + "htmlwidgets (>= 0.7)", + "jpeg", + "JuliaCall (>= 0.11.1)", + "magick", + "litedown", + "markdown (>= 1.3)", + "otel", + "otelsdk", + "png", + "ragg", + "reticulate (>= 1.4)", + "rgl (>= 0.95.1201)", + "rlang", + "rmarkdown", + "sass", + "showtext", + "styler (>= 1.2.0)", + "targets (>= 0.6.0)", + "testit", + "tibble", + "tikzDevice (>= 0.10)", + "tinytex (>= 0.56)", + "webshot", + "rstudioapi", + "svglite" + ], + "License": "GPL", + "URL": "https://yihui.org/knitr/", + "BugReports": "https://github.com/yihui/knitr/issues", + "Encoding": "UTF-8", + "VignetteBuilder": "litedown, knitr", + "SystemRequirements": "Package vignettes based on R Markdown v2 or reStructuredText require Pandoc (http://pandoc.org). The function rst2pdf() requires rst2pdf (https://github.com/rst2pdf/rst2pdf).", + "Collate": "'block.R' 'cache.R' 'citation.R' 'hooks-html.R' 'plot.R' 'utils.R' 'defaults.R' 'concordance.R' 'engine.R' 'highlight.R' 'themes.R' 'header.R' 'hooks-asciidoc.R' 'hooks-chunk.R' 'hooks-extra.R' 'hooks-latex.R' 'hooks-md.R' 'hooks-rst.R' 'hooks-textile.R' 'hooks.R' 'otel.R' 'output.R' 'package.R' 'pandoc.R' 'params.R' 'parser.R' 'pattern.R' 'rocco.R' 'spin.R' 'table.R' 'template.R' 'utils-conversion.R' 'utils-rd2html.R' 'utils-string.R' 'utils-sweave.R' 'utils-upload.R' 'utils-vignettes.R' 'zzz.R'", + "RoxygenNote": "7.3.3", + "NeedsCompilation": "no", + "Author": "Yihui Xie [aut, cre] (ORCID: , URL: https://yihui.org), Abhraneel Sarma [ctb], Adam Vogt [ctb], Alastair Andrew [ctb], Alex Zvoleff [ctb], Amar Al-Zubaidi [ctb], Andre Simon [ctb] (the CSS files under inst/themes/ were derived from the Highlight package http://www.andre-simon.de), Aron Atkins [ctb], Aaron Wolen [ctb], Ashley Manton [ctb], Atsushi Yasumoto [ctb] (ORCID: ), Ben Baumer [ctb], Brian Diggs [ctb], Brian Zhang [ctb], Bulat Yapparov [ctb], Cassio Pereira [ctb], Christophe Dervieux [ctb], David Hall [ctb], David Hugh-Jones [ctb], David Robinson [ctb], Doug Hemken [ctb], Duncan Murdoch [ctb], Elio Campitelli [ctb], Ellis Hughes [ctb], Emily Riederer [ctb], Fabian Hirschmann [ctb], Fitch Simeon [ctb], Forest Fang [ctb], Frank E Harrell Jr [ctb] (the Sweavel package at inst/misc/Sweavel.sty), Garrick Aden-Buie [ctb], Gregoire Detrez [ctb], Hadley Wickham [ctb], Hao Zhu [ctb], Heewon Jeon [ctb], Henrik Bengtsson [ctb], Hiroaki Yutani [ctb], Ian Lyttle [ctb], Hodges Daniel [ctb], Jacob Bien [ctb], Jake Burkhead [ctb], James Manton [ctb], Jared Lander [ctb], Jason Punyon [ctb], Javier Luraschi [ctb], Jeff Arnold [ctb], Jenny Bryan [ctb], Jeremy Ashkenas [ctb, cph] (the CSS file at inst/misc/docco-classic.css), Jeremy Stephens [ctb], Jim Hester [ctb], Joe Cheng [ctb], Johannes Ranke [ctb], John Honaker [ctb], John Muschelli [ctb], Jonathan Keane [ctb], JJ Allaire [ctb], Johan Toloe [ctb], Jonathan Sidi [ctb], Joseph Larmarange [ctb], Julien Barnier [ctb], Kaiyin Zhong [ctb], Kamil Slowikowski [ctb], Karl Forner [ctb], Kevin K. Smith [ctb], Kirill Mueller [ctb], Kohske Takahashi [ctb], Lorenz Walthert [ctb], Lucas Gallindo [ctb], Marius Hofert [ctb], Martin Modrák [ctb], Michael Chirico [ctb], Michael Friendly [ctb], Michal Bojanowski [ctb], Michel Kuhlmann [ctb], Miller Patrick [ctb], Nacho Caballero [ctb], Nick Salkowski [ctb], Niels Richard Hansen [ctb], Noam Ross [ctb], Obada Mahdi [ctb], Pavel N. Krivitsky [ctb] (ORCID: ), Pedro Faria [ctb], Qiang Li [ctb], Ramnath Vaidyanathan [ctb], Richard Cotton [ctb], Robert Krzyzanowski [ctb], Rodrigo Copetti [ctb], Romain Francois [ctb], Ruaridh Williamson [ctb], Sagiru Mati [ctb] (ORCID: ), Scott Kostyshak [ctb], Sebastian Meyer [ctb], Sietse Brouwer [ctb], Simon de Bernard [ctb], Sylvain Rousseau [ctb], Taiyun Wei [ctb], Thibaut Assus [ctb], Thibaut Lamadon [ctb], Thomas Leeper [ctb], Tim Mastny [ctb], Tom Torsney-Weir [ctb], Trevor Davis [ctb], Viktoras Veitas [ctb], Weicheng Zhu [ctb], Wush Wu [ctb], Zachary Foster [ctb], Zhian N. Kamvar [ctb] (ORCID: ), Posit Software, PBC [cph, fnd]", + "Maintainer": "Yihui Xie ", + "Repository": "CRAN" }, "labeling": { "Package": "labeling", "Version": "0.4.3", "Source": "Repository", - "Requirements": [ - "graphics", - "stats" - ] + "Type": "Package", + "Title": "Axis Labeling", + "Date": "2023-08-29", + "Author": "Justin Talbot,", + "Maintainer": "Nuno Sempere ", + "Description": "Functions which provide a range of axis labeling algorithms.", + "License": "MIT + file LICENSE | Unlimited", + "Collate": "'labeling.R'", + "NeedsCompilation": "no", + "Imports": [ + "stats", + "graphics" + ], + "Repository": "CRAN" }, "later": { "Package": "later", - "Version": "1.4.8", - "Source": "Repository", - "Requirements": [ - "R", - "Rcpp", + "Version": "1.4.5", + "Source": "Repository", + "Type": "Package", + "Title": "Utilities for Scheduling Functions to Execute Later with Event Loops", + "Authors@R": "c( person(\"Winston\", \"Chang\", , \"winston@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0002-1576-2126\")), person(\"Joe\", \"Cheng\", , \"joe@posit.co\", role = \"aut\"), person(\"Charlie\", \"Gao\", , \"charlie.gao@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-0750-061X\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")), person(\"Marcus\", \"Geelnard\", role = c(\"ctb\", \"cph\"), comment = \"TinyCThread library, https://tinycthread.github.io/\"), person(\"Evan\", \"Nemerson\", role = c(\"ctb\", \"cph\"), comment = \"TinyCThread library, https://tinycthread.github.io/\") )", + "Description": "Executes arbitrary R or C functions some time after the current time, after the R execution stack has emptied. The functions are scheduled in an event loop.", + "License": "MIT + file LICENSE", + "URL": "https://later.r-lib.org, https://github.com/r-lib/later", + "BugReports": "https://github.com/r-lib/later/issues", + "Depends": [ + "R (>= 3.5)" + ], + "Imports": [ + "Rcpp (>= 1.0.10)", "rlang" - ] + ], + "Suggests": [ + "knitr", + "nanonext", + "promises", + "rmarkdown", + "testthat (>= 3.0.0)" + ], + "LinkingTo": [ + "Rcpp" + ], + "VignetteBuilder": "knitr", + "Config/build/compilation-database": "true", + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Config/usethis/last-upkeep": "2025-07-18", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3", + "NeedsCompilation": "yes", + "Author": "Winston Chang [aut] (ORCID: ), Joe Cheng [aut], Charlie Gao [aut, cre] (ORCID: ), Posit Software, PBC [cph, fnd] (ROR: ), Marcus Geelnard [ctb, cph] (TinyCThread library, https://tinycthread.github.io/), Evan Nemerson [ctb, cph] (TinyCThread library, https://tinycthread.github.io/)", + "Maintainer": "Charlie Gao ", + "Repository": "CRAN" }, "lazyeval": { "Package": "lazyeval", - "Version": "0.2.3", + "Version": "0.2.2", "Source": "Repository", - "Requirements": [ - "R", - "rlang" - ] + "Title": "Lazy (Non-Standard) Evaluation", + "Description": "An alternative approach to non-standard evaluation using formulas. Provides a full implementation of LISP style 'quasiquotation', making it easier to generate code with other code.", + "Authors@R": "c( person(\"Hadley\", \"Wickham\", ,\"hadley@rstudio.com\", c(\"aut\", \"cre\")), person(\"RStudio\", role = \"cph\") )", + "License": "GPL-3", + "LazyData": "true", + "Depends": [ + "R (>= 3.1.0)" + ], + "Suggests": [ + "knitr", + "rmarkdown (>= 0.2.65)", + "testthat", + "covr" + ], + "VignetteBuilder": "knitr", + "RoxygenNote": "6.1.1", + "NeedsCompilation": "yes", + "Author": "Hadley Wickham [aut, cre], RStudio [cph]", + "Maintainer": "Hadley Wickham ", + "Repository": "CRAN" }, "lifecycle": { "Package": "lifecycle", "Version": "1.0.5", "Source": "Repository", - "Requirements": [ - "cli", - "R", - "rlang" - ] + "Title": "Manage the Life Cycle of your Package Functions", + "Authors@R": "c( person(\"Lionel\", \"Henry\", , \"lionel@posit.co\", role = c(\"aut\", \"cre\")), person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0003-4757-117X\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "Manage the life cycle of your exported functions with shared conventions, documentation badges, and user-friendly deprecation warnings.", + "License": "MIT + file LICENSE", + "URL": "https://lifecycle.r-lib.org/, https://github.com/r-lib/lifecycle", + "BugReports": "https://github.com/r-lib/lifecycle/issues", + "Depends": [ + "R (>= 3.6)" + ], + "Imports": [ + "cli (>= 3.4.0)", + "rlang (>= 1.1.0)" + ], + "Suggests": [ + "covr", + "knitr", + "lintr (>= 3.1.0)", + "rmarkdown", + "testthat (>= 3.0.1)", + "tibble", + "tidyverse", + "tools", + "vctrs", + "withr", + "xml2" + ], + "VignetteBuilder": "knitr", + "Config/Needs/website": "tidyverse/tidytemplate, usethis", + "Config/testthat/edition": "3", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3", + "NeedsCompilation": "no", + "Author": "Lionel Henry [aut, cre], Hadley Wickham [aut] (ORCID: ), Posit Software, PBC [cph, fnd]", + "Maintainer": "Lionel Henry ", + "Repository": "CRAN" }, "magrittr": { "Package": "magrittr", - "Version": "2.0.5", - "Source": "Repository", - "Requirements": [ - "R" - ] + "Version": "2.0.4", + "Source": "Repository", + "Type": "Package", + "Title": "A Forward-Pipe Operator for R", + "Authors@R": "c( person(\"Stefan Milton\", \"Bache\", , \"stefan@stefanbache.dk\", role = c(\"aut\", \"cph\"), comment = \"Original author and creator of magrittr\"), person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\"), person(\"Lionel\", \"Henry\", , \"lionel@posit.co\", role = \"cre\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", + "Description": "Provides a mechanism for chaining commands with a new forward-pipe operator, %>%. This operator will forward a value, or the result of an expression, into the next function call/expression. There is flexible support for the type of right-hand side expressions. For more information, see package vignette. To quote Rene Magritte, \"Ceci n'est pas un pipe.\"", + "License": "MIT + file LICENSE", + "URL": "https://magrittr.tidyverse.org, https://github.com/tidyverse/magrittr", + "BugReports": "https://github.com/tidyverse/magrittr/issues", + "Depends": [ + "R (>= 3.4.0)" + ], + "Suggests": [ + "covr", + "knitr", + "rlang", + "rmarkdown", + "testthat" + ], + "VignetteBuilder": "knitr", + "ByteCompile": "Yes", + "Config/Needs/website": "tidyverse/tidytemplate", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3", + "NeedsCompilation": "yes", + "Author": "Stefan Milton Bache [aut, cph] (Original author and creator of magrittr), Hadley Wickham [aut], Lionel Henry [cre], Posit Software, PBC [cph, fnd] (ROR: )", + "Maintainer": "Lionel Henry ", + "Repository": "CRAN" }, "memoise": { "Package": "memoise", "Version": "2.0.1", "Source": "Repository", - "Requirements": [ - "cachem", - "rlang" - ] + "Title": "'Memoisation' of Functions", + "Authors@R": "c(person(given = \"Hadley\", family = \"Wickham\", role = \"aut\", email = \"hadley@rstudio.com\"), person(given = \"Jim\", family = \"Hester\", role = \"aut\"), person(given = \"Winston\", family = \"Chang\", role = c(\"aut\", \"cre\"), email = \"winston@rstudio.com\"), person(given = \"Kirill\", family = \"Müller\", role = \"aut\", email = \"krlmlr+r@mailbox.org\"), person(given = \"Daniel\", family = \"Cook\", role = \"aut\", email = \"danielecook@gmail.com\"), person(given = \"Mark\", family = \"Edmondson\", role = \"ctb\", email = \"r@sunholo.com\"))", + "Description": "Cache the results of a function so that when you call it again with the same arguments it returns the previously computed value.", + "License": "MIT + file LICENSE", + "URL": "https://memoise.r-lib.org, https://github.com/r-lib/memoise", + "BugReports": "https://github.com/r-lib/memoise/issues", + "Imports": [ + "rlang (>= 0.4.10)", + "cachem" + ], + "Suggests": [ + "digest", + "aws.s3", + "covr", + "googleAuthR", + "googleCloudStorageR", + "httr", + "testthat" + ], + "Encoding": "UTF-8", + "RoxygenNote": "7.1.2", + "NeedsCompilation": "no", + "Author": "Hadley Wickham [aut], Jim Hester [aut], Winston Chang [aut, cre], Kirill Müller [aut], Daniel Cook [aut], Mark Edmondson [ctb]", + "Maintainer": "Winston Chang ", + "Repository": "CRAN" }, "mime": { "Package": "mime", "Version": "0.13", "Source": "Repository", - "Requirements": [ + "Type": "Package", + "Title": "Map Filenames to MIME Types", + "Authors@R": "c( person(\"Yihui\", \"Xie\", role = c(\"aut\", \"cre\"), email = \"xie@yihui.name\", comment = c(ORCID = \"0000-0003-0645-5666\", URL = \"https://yihui.org\")), person(\"Jeffrey\", \"Horner\", role = \"ctb\"), person(\"Beilei\", \"Bian\", role = \"ctb\") )", + "Description": "Guesses the MIME type from a filename extension using the data derived from /etc/mime.types in UNIX-type systems.", + "Imports": [ "tools" - ] + ], + "License": "GPL", + "URL": "https://github.com/yihui/mime", + "BugReports": "https://github.com/yihui/mime/issues", + "RoxygenNote": "7.3.2", + "Encoding": "UTF-8", + "NeedsCompilation": "yes", + "Author": "Yihui Xie [aut, cre] (, https://yihui.org), Jeffrey Horner [ctb], Beilei Bian [ctb]", + "Maintainer": "Yihui Xie ", + "Repository": "CRAN" }, "officer": { "Package": "officer", - "Version": "0.7.6", + "Version": "0.7.3", "Source": "Repository", - "Requirements": [ + "Type": "Package", + "Title": "Manipulation of Microsoft Word and PowerPoint Documents", + "Authors@R": "c( person(\"David\", \"Gohel\", , \"david.gohel@ardata.fr\", role = c(\"aut\", \"cre\")), person(\"Stefan\", \"Moog\", , \"moogs@gmx.de\", role = \"aut\"), person(\"Mark\", \"Heckmann\", , \"heckmann.mark@gmail.com\", role = \"aut\", comment = c(ORCID = \"0000-0002-0736-7417\")), person(\"ArData\", role = \"cph\"), person(\"Frank\", \"Hangler\", , \"frank@plotandscatter.com\", role = \"ctb\", comment = \"function body_replace_all_text\"), person(\"Liz\", \"Sander\", , \"lsander@civisanalytics.com\", role = \"ctb\", comment = \"several documentation fixes\"), person(\"Anton\", \"Victorson\", , \"anton@victorson.se\", role = \"ctb\", comment = \"fixes xml structures\"), person(\"Jon\", \"Calder\", , \"jonmcalder@gmail.com\", role = \"ctb\", comment = \"update vignettes\"), person(\"John\", \"Harrold\", , \"john.m.harrold@gmail.com\", role = \"ctb\", comment = \"function annotate_base\"), person(\"John\", \"Muschelli\", , \"muschellij2@gmail.com\", role = \"ctb\", comment = \"google doc compatibility\"), person(\"Bill\", \"Denney\", , \"wdenney@humanpredictions.com\", role = \"ctb\", comment = c(ORCID = \"0000-0002-5759-428X\", \"function as.matrix.rpptx\")), person(\"Nikolai\", \"Beck\", , \"beck.nikolai@gmail.com\", role = \"ctb\", comment = \"set speaker notes for .pptx documents\"), person(\"Greg\", \"Leleu\", , \"gregoire.leleu@gmail.com\", role = \"ctb\", comment = \"fields functionality in ppt\"), person(\"Majid\", \"Eismann\", role = \"ctb\"), person(\"Wahiduzzaman\", \"Khan\", role = \"ctb\", comment = \"vectorization of remove_slide\"), person(\"Hongyuan\", \"Jia\", , \"hongyuanjia@cqust.edu.cn\", role = \"ctb\", comment = c(ORCID = \"0000-0002-0075-8183\")), person(\"Michael\", \"Stackhouse\", , \"mike.stackhouse@atorusresearch.com\", role = \"ctb\") )", + "Description": "Access and manipulate 'Microsoft Word', 'RTF' and 'Microsoft PowerPoint' documents from R. The package focuses on tabular and graphical reporting from R; it also provides two functions that let users get document content into data objects. A set of functions lets add and remove images, tables and paragraphs of text in new or existing documents. The package does not require any installation of Microsoft products to be able to write Microsoft files.", + "License": "MIT + file LICENSE", + "URL": "https://ardata-fr.github.io/officeverse/, https://davidgohel.github.io/officer/", + "BugReports": "https://github.com/davidgohel/officer/issues", + "Imports": [ "cli", "dplyr", "graphics", @@ -552,455 +1762,1584 @@ "tidyr", "utils", "uuid", - "xml2", - "zip" - ] + "xml2 (>= 1.1.0)", + "zip (>= 2.1.0)" + ], + "Suggests": [ + "devEMF", + "doconv (>= 0.3.0)", + "gdtools", + "ggplot2", + "knitr", + "magick", + "rmarkdown", + "rsvg", + "testthat", + "withr" + ], + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3", + "Collate": "'core_properties.R' 'custom_properties.R' 'defunct.R' 'dev-utils.R' 'docx_add.R' 'docx_comments.R' 'docx_cursor.R' 'docx_part.R' 'docx_replace.R' 'docx_section.R' 'docx_settings.R' 'docx_styles.R' 'docx_utils_funs.R' 'empty_content.R' 'formatting_properties.R' 'fortify_docx.R' 'fortify_pptx.R' 'knitr_utils.R' 'officer.R' 'ooxml.R' 'ooxml_block_objects.R' 'ooxml_run_objects.R' 'openxml_content_type.R' 'openxml_document.R' 'pack_folder.R' 'ph_location.R' 'post-proc.R' 'ppt_class_dir_collection.R' 'ppt_classes.R' 'ppt_notes.R' 'ppt_ph_dedupe_layout.R' 'ppt_ph_manipulate.R' 'ppt_ph_rename_layout.R' 'ppt_ph_with_methods.R' 'pptx_informations.R' 'pptx_layout_helper.R' 'pptx_matrix.R' 'utils.R' 'pptx_slide_manip.R' 'read_docx.R' 'docx_write.R' 'read_docx_styles.R' 'read_pptx.R' 'read_xlsx.R' 'relationship.R' 'rtf.R' 'shape_properties.R' 'shorcuts.R' 'docx_append_context.R' 'utils-xml.R' 'deprecated.R' 'zzz.R'", + "NeedsCompilation": "no", + "Author": "David Gohel [aut, cre], Stefan Moog [aut], Mark Heckmann [aut] (ORCID: ), ArData [cph], Frank Hangler [ctb] (function body_replace_all_text), Liz Sander [ctb] (several documentation fixes), Anton Victorson [ctb] (fixes xml structures), Jon Calder [ctb] (update vignettes), John Harrold [ctb] (function annotate_base), John Muschelli [ctb] (google doc compatibility), Bill Denney [ctb] (ORCID: , function as.matrix.rpptx), Nikolai Beck [ctb] (set speaker notes for .pptx documents), Greg Leleu [ctb] (fields functionality in ppt), Majid Eismann [ctb], Wahiduzzaman Khan [ctb] (vectorization of remove_slide), Hongyuan Jia [ctb] (ORCID: ), Michael Stackhouse [ctb]", + "Maintainer": "David Gohel ", + "Repository": "CRAN" }, "openssl": { "Package": "openssl", - "Version": "2.4.2", - "Source": "Repository", - "Requirements": [ + "Version": "2.3.4", + "Source": "Repository", + "Type": "Package", + "Title": "Toolkit for Encryption, Signatures and Certificates Based on OpenSSL", + "Authors@R": "c(person(\"Jeroen\", \"Ooms\", role = c(\"aut\", \"cre\"), email = \"jeroenooms@gmail.com\", comment = c(ORCID = \"0000-0002-4035-0289\")), person(\"Oliver\", \"Keyes\", role = \"ctb\"))", + "Description": "Bindings to OpenSSL libssl and libcrypto, plus custom SSH key parsers. Supports RSA, DSA and EC curves P-256, P-384, P-521, and curve25519. Cryptographic signatures can either be created and verified manually or via x509 certificates. AES can be used in cbc, ctr or gcm mode for symmetric encryption; RSA for asymmetric (public key) encryption or EC for Diffie Hellman. High-level envelope functions combine RSA and AES for encrypting arbitrary sized data. Other utilities include key generators, hash functions (md5, sha1, sha256, etc), base64 encoder, a secure random number generator, and 'bignum' math methods for manually performing crypto calculations on large multibyte integers.", + "License": "MIT + file LICENSE", + "URL": "https://jeroen.r-universe.dev/openssl", + "BugReports": "https://github.com/jeroen/openssl/issues", + "SystemRequirements": "OpenSSL >= 1.0.2", + "VignetteBuilder": "knitr", + "Imports": [ "askpass" - ] + ], + "Suggests": [ + "curl", + "testthat (>= 2.1.0)", + "digest", + "knitr", + "rmarkdown", + "jsonlite", + "jose", + "sodium" + ], + "RoxygenNote": "7.3.2", + "Encoding": "UTF-8", + "NeedsCompilation": "yes", + "Author": "Jeroen Ooms [aut, cre] (ORCID: ), Oliver Keyes [ctb]", + "Maintainer": "Jeroen Ooms ", + "Repository": "CRAN" }, "otel": { "Package": "otel", "Version": "0.2.0", "Source": "Repository", - "Requirements": [ - "R" - ] + "Title": "OpenTelemetry R API", + "Authors@R": "person(\"Gábor\", \"Csárdi\", , \"csardi.gabor@gmail.com\", role = c(\"aut\", \"cre\"))", + "Description": "High-quality, ubiquitous, and portable telemetry to enable effective observability. OpenTelemetry is a collection of tools, APIs, and SDKs used to instrument, generate, collect, and export telemetry data (metrics, logs, and traces) for analysis in order to understand your software's performance and behavior. This package implements the OpenTelemetry API: . Use this package as a dependency if you want to instrument your R package for OpenTelemetry.", + "License": "MIT + file LICENSE", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2.9000", + "Depends": [ + "R (>= 3.6.0)" + ], + "Suggests": [ + "callr", + "cli", + "glue", + "jsonlite", + "otelsdk", + "processx", + "shiny", + "spelling", + "testthat (>= 3.0.0)", + "utils", + "withr" + ], + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "URL": "https://otel.r-lib.org, https://github.com/r-lib/otel", + "Additional_repositories": "https://github.com/r-lib/otelsdk/releases/download/devel", + "BugReports": "https://github.com/r-lib/otel/issues", + "NeedsCompilation": "no", + "Author": "Gábor Csárdi [aut, cre]", + "Maintainer": "Gábor Csárdi ", + "Repository": "CRAN" }, "pillar": { "Package": "pillar", "Version": "1.11.1", "Source": "Repository", - "Requirements": [ - "cli", + "Title": "Coloured Formatting for Columns", + "Authors@R": "c(person(given = \"Kirill\", family = \"M\\u00fcller\", role = c(\"aut\", \"cre\"), email = \"kirill@cynkra.com\", comment = c(ORCID = \"0000-0002-1416-3412\")), person(given = \"Hadley\", family = \"Wickham\", role = \"aut\"), person(given = \"RStudio\", role = \"cph\"))", + "Description": "Provides 'pillar' and 'colonnade' generics designed for formatting columns of data using the full range of colours provided by modern terminals.", + "License": "MIT + file LICENSE", + "URL": "https://pillar.r-lib.org/, https://github.com/r-lib/pillar", + "BugReports": "https://github.com/r-lib/pillar/issues", + "Imports": [ + "cli (>= 2.3.0)", "glue", "lifecycle", - "rlang", - "utf8", + "rlang (>= 1.0.2)", + "utf8 (>= 1.1.0)", "utils", - "vctrs" - ] + "vctrs (>= 0.5.0)" + ], + "Suggests": [ + "bit64", + "DBI", + "debugme", + "DiagrammeR", + "dplyr", + "formattable", + "ggplot2", + "knitr", + "lubridate", + "nanotime", + "nycflights13", + "palmerpenguins", + "rmarkdown", + "scales", + "stringi", + "survival", + "testthat (>= 3.1.1)", + "tibble", + "units (>= 0.7.2)", + "vdiffr", + "withr" + ], + "VignetteBuilder": "knitr", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3.9000", + "Config/testthat/edition": "3", + "Config/testthat/parallel": "true", + "Config/testthat/start-first": "format_multi_fuzz, format_multi_fuzz_2, format_multi, ctl_colonnade, ctl_colonnade_1, ctl_colonnade_2", + "Config/autostyle/scope": "line_breaks", + "Config/autostyle/strict": "true", + "Config/gha/extra-packages": "units=?ignore-before-r=4.3.0", + "Config/Needs/website": "tidyverse/tidytemplate", + "NeedsCompilation": "no", + "Author": "Kirill Mller [aut, cre] (ORCID: ), Hadley Wickham [aut], RStudio [cph]", + "Maintainer": "Kirill Mller ", + "Repository": "CRAN" }, "pkgbuild": { "Package": "pkgbuild", "Version": "1.4.8", "Source": "Repository", - "Requirements": [ - "callr", - "cli", + "Title": "Find Tools Needed to Build R Packages", + "Authors@R": "c( person(\"Hadley\", \"Wickham\", role = \"aut\"), person(\"Jim\", \"Hester\", role = \"aut\"), person(\"Gábor\", \"Csárdi\", , \"csardi.gabor@gmail.com\", role = c(\"aut\", \"cre\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", + "Description": "Provides functions used to build R packages. Locates compilers needed to build R packages on various platforms and ensures the PATH is configured appropriately so R can use them.", + "License": "MIT + file LICENSE", + "URL": "https://github.com/r-lib/pkgbuild, https://pkgbuild.r-lib.org", + "BugReports": "https://github.com/r-lib/pkgbuild/issues", + "Depends": [ + "R (>= 3.5)" + ], + "Imports": [ + "callr (>= 3.2.0)", + "cli (>= 3.4.0)", "desc", "processx", - "R", "R6" - ] + ], + "Suggests": [ + "covr", + "cpp11", + "knitr", + "Rcpp", + "rmarkdown", + "testthat (>= 3.2.0)", + "withr (>= 2.3.0)" + ], + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Config/usethis/last-upkeep": "2025-04-30", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2", + "NeedsCompilation": "no", + "Author": "Hadley Wickham [aut], Jim Hester [aut], Gábor Csárdi [aut, cre], Posit Software, PBC [cph, fnd] (ROR: )", + "Maintainer": "Gábor Csárdi ", + "Repository": "CRAN" }, "pkgconfig": { "Package": "pkgconfig", "Version": "2.0.3", "Source": "Repository", - "Requirements": [ + "Title": "Private Configuration for 'R' Packages", + "Author": "Gábor Csárdi", + "Maintainer": "Gábor Csárdi ", + "Description": "Set configuration options on a per-package basis. Options set by a given package only apply to that package, other packages are unaffected.", + "License": "MIT + file LICENSE", + "LazyData": "true", + "Imports": [ "utils" - ] + ], + "Suggests": [ + "covr", + "testthat", + "disposables (>= 1.0.3)" + ], + "URL": "https://github.com/r-lib/pkgconfig#readme", + "BugReports": "https://github.com/r-lib/pkgconfig/issues", + "Encoding": "UTF-8", + "NeedsCompilation": "no", + "Repository": "CRAN" }, "plotly": { "Package": "plotly", - "Version": "4.12.1", - "Source": "Repository", - "Requirements": [ - "base64enc", - "crosstalk", - "data.table", - "digest", - "dplyr", - "ggplot2", - "htmltools", - "htmlwidgets", - "httr", - "jsonlite", + "Version": "4.12.0", + "Source": "Repository", + "Title": "Create Interactive Web Graphics via 'plotly.js'", + "Authors@R": "c(person(\"Carson\", \"Sievert\", role = c(\"aut\", \"cre\"), email = \"cpsievert1@gmail.com\", comment = c(ORCID = \"0000-0002-4958-2844\")), person(\"Chris\", \"Parmer\", role = \"aut\", email = \"chris@plot.ly\"), person(\"Toby\", \"Hocking\", role = \"aut\", email = \"tdhock5@gmail.com\"), person(\"Scott\", \"Chamberlain\", role = \"aut\", email = \"myrmecocystus@gmail.com\"), person(\"Karthik\", \"Ram\", role = \"aut\", email = \"karthik.ram@gmail.com\"), person(\"Marianne\", \"Corvellec\", role = \"aut\", email = \"marianne.corvellec@igdore.org\", comment = c(ORCID = \"0000-0002-1994-3581\")), person(\"Pedro\", \"Despouy\", role = \"aut\", email = \"pedro@plot.ly\"), person(\"Salim\", \"Brüggemann\", role = \"ctb\", email = \"salim-b@pm.me\", comment = c(ORCID = \"0000-0002-5329-5987\")), person(\"Plotly Technologies Inc.\", role = \"cph\"))", + "License": "MIT + file LICENSE", + "Description": "Create interactive web graphics from 'ggplot2' graphs and/or a custom interface to the (MIT-licensed) JavaScript library 'plotly.js' inspired by the grammar of graphics.", + "URL": "https://plotly-r.com, https://github.com/plotly/plotly.R, https://plotly.com/r/", + "BugReports": "https://github.com/plotly/plotly.R/issues", + "Depends": [ + "R (>= 3.5.0)", + "ggplot2 (>= 3.0.0)" + ], + "Imports": [ + "tools", + "scales", + "httr (>= 1.3.0)", + "jsonlite (>= 1.6)", "magrittr", - "promises", - "purrr", - "R", + "digest", + "viridisLite", + "base64enc", + "htmltools (>= 0.3.6)", + "htmlwidgets (>= 1.5.2.9001)", + "tidyr (>= 1.0.0)", "RColorBrewer", - "rlang", - "scales", - "tibble", - "tidyr", - "tools", + "dplyr", "vctrs", - "viridisLite" - ] + "tibble", + "lazyeval (>= 0.2.0)", + "rlang (>= 1.0.0)", + "crosstalk", + "purrr", + "data.table", + "promises" + ], + "Suggests": [ + "MASS", + "maps", + "hexbin", + "ggthemes", + "GGally", + "ggalluvial", + "testthat", + "knitr", + "shiny (>= 1.1.0)", + "shinytest2", + "curl", + "rmarkdown", + "Cairo", + "broom", + "webshot", + "listviewer", + "dendextend", + "sf", + "png", + "IRdisplay", + "processx", + "plotlyGeoAssets", + "forcats", + "withr", + "palmerpenguins", + "rversions", + "reticulate", + "rsvg", + "ggridges" + ], + "LazyData": "true", + "RoxygenNote": "7.3.3", + "Encoding": "UTF-8", + "Config/Needs/check": "tidyverse/ggplot2, ggobi/GGally, rcmdcheck, devtools, reshape2, s2", + "NeedsCompilation": "no", + "Author": "Carson Sievert [aut, cre] (ORCID: ), Chris Parmer [aut], Toby Hocking [aut], Scott Chamberlain [aut], Karthik Ram [aut], Marianne Corvellec [aut] (ORCID: ), Pedro Despouy [aut], Salim Brüggemann [ctb] (ORCID: ), Plotly Technologies Inc. [cph]", + "Maintainer": "Carson Sievert ", + "Repository": "CRAN" }, "plyr": { "Package": "plyr", "Version": "1.8.9", "Source": "Repository", - "Requirements": [ - "R", + "Title": "Tools for Splitting, Applying and Combining Data", + "Authors@R": "person(\"Hadley\", \"Wickham\", , \"hadley@rstudio.com\", role = c(\"aut\", \"cre\"))", + "Description": "A set of tools that solves a common set of problems: you need to break a big problem down into manageable pieces, operate on each piece and then put all the pieces back together. For example, you might want to fit a model to each spatial location or time point in your study, summarise data by panels or collapse high-dimensional arrays to simpler summary statistics. The development of 'plyr' has been generously supported by 'Becton Dickinson'.", + "License": "MIT + file LICENSE", + "URL": "http://had.co.nz/plyr, https://github.com/hadley/plyr", + "BugReports": "https://github.com/hadley/plyr/issues", + "Depends": [ + "R (>= 3.1.0)" + ], + "Imports": [ + "Rcpp (>= 0.11.0)" + ], + "Suggests": [ + "abind", + "covr", + "doParallel", + "foreach", + "iterators", + "itertools", + "tcltk", + "testthat" + ], + "LinkingTo": [ "Rcpp" - ] + ], + "Encoding": "UTF-8", + "LazyData": "true", + "RoxygenNote": "7.2.3", + "NeedsCompilation": "yes", + "Author": "Hadley Wickham [aut, cre]", + "Maintainer": "Hadley Wickham ", + "Repository": "CRAN" }, "processx": { "Package": "processx", - "Version": "3.9.0", - "Source": "Repository", - "Requirements": [ - "ps", - "R", + "Version": "3.8.6", + "Source": "Repository", + "Title": "Execute and Control System Processes", + "Authors@R": "c( person(\"Gábor\", \"Csárdi\", , \"csardi.gabor@gmail.com\", role = c(\"aut\", \"cre\", \"cph\"), comment = c(ORCID = \"0000-0001-7098-9676\")), person(\"Winston\", \"Chang\", role = \"aut\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")), person(\"Ascent Digital Services\", role = c(\"cph\", \"fnd\")) )", + "Description": "Tools to run system processes in the background. It can check if a background process is running; wait on a background process to finish; get the exit status of finished processes; kill background processes. It can read the standard output and error of the processes, using non-blocking connections. 'processx' can poll a process for standard output or error, with a timeout. It can also poll several processes at once.", + "License": "MIT + file LICENSE", + "URL": "https://processx.r-lib.org, https://github.com/r-lib/processx", + "BugReports": "https://github.com/r-lib/processx/issues", + "Depends": [ + "R (>= 3.4.0)" + ], + "Imports": [ + "ps (>= 1.2.0)", "R6", "utils" - ] + ], + "Suggests": [ + "callr (>= 3.7.3)", + "cli (>= 3.3.0)", + "codetools", + "covr", + "curl", + "debugme", + "parallel", + "rlang (>= 1.0.2)", + "testthat (>= 3.0.0)", + "webfakes", + "withr" + ], + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.1.9000", + "NeedsCompilation": "yes", + "Author": "Gábor Csárdi [aut, cre, cph] (), Winston Chang [aut], Posit Software, PBC [cph, fnd], Ascent Digital Services [cph, fnd]", + "Maintainer": "Gábor Csárdi ", + "Repository": "CRAN" }, "promises": { "Package": "promises", "Version": "1.5.0", "Source": "Repository", - "Requirements": [ - "fastmap", + "Type": "Package", + "Title": "Abstractions for Promise-Based Asynchronous Programming", + "Authors@R": "c( person(\"Joe\", \"Cheng\", , \"joe@posit.co\", role = \"aut\"), person(\"Barret\", \"Schloerke\", , \"barret@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0001-9986-114X\")), person(\"Winston\", \"Chang\", , \"winston@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0002-1576-2126\")), person(\"Charlie\", \"Gao\", , \"charlie.gao@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0002-0750-061X\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", + "Description": "Provides fundamental abstractions for doing asynchronous programming in R using promises. Asynchronous programming is useful for allowing a single R process to orchestrate multiple tasks in the background while also attending to something else. Semantics are similar to 'JavaScript' promises, but with a syntax that is idiomatic R.", + "License": "MIT + file LICENSE", + "URL": "https://rstudio.github.io/promises/, https://github.com/rstudio/promises", + "BugReports": "https://github.com/rstudio/promises/issues", + "Depends": [ + "R (>= 4.1.0)" + ], + "Imports": [ + "fastmap (>= 1.1.0)", "later", "lifecycle", - "magrittr", - "otel", - "R", + "magrittr (>= 1.5)", + "otel (>= 0.2.0)", "R6", "rlang" - ] + ], + "Suggests": [ + "future (>= 1.21.0)", + "knitr", + "mirai", + "otelsdk (>= 0.2.0)", + "purrr", + "Rcpp", + "rmarkdown", + "spelling", + "testthat (>= 3.0.0)", + "vembedr" + ], + "VignetteBuilder": "knitr", + "Config/Needs/website": "rsconnect, tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Config/usethis/last-upkeep": "2025-05-27", + "Encoding": "UTF-8", + "Language": "en-US", + "RoxygenNote": "7.3.3", + "NeedsCompilation": "no", + "Author": "Joe Cheng [aut], Barret Schloerke [aut, cre] (ORCID: ), Winston Chang [aut] (ORCID: ), Charlie Gao [aut] (ORCID: ), Posit Software, PBC [cph, fnd] (ROR: )", + "Maintainer": "Barret Schloerke ", + "Repository": "CRAN" }, "ps": { "Package": "ps", - "Version": "1.9.3", - "Source": "Repository", - "Requirements": [ - "R", + "Version": "1.9.1", + "Source": "Repository", + "Title": "List, Query, Manipulate System Processes", + "Authors@R": "c( person(\"Jay\", \"Loden\", role = \"aut\"), person(\"Dave\", \"Daeschler\", role = \"aut\"), person(\"Giampaolo\", \"Rodola'\", role = \"aut\"), person(\"Gábor\", \"Csárdi\", , \"csardi.gabor@gmail.com\", role = c(\"aut\", \"cre\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "List, query and manipulate all system processes, on 'Windows', 'Linux' and 'macOS'.", + "License": "MIT + file LICENSE", + "URL": "https://github.com/r-lib/ps, https://ps.r-lib.org/", + "BugReports": "https://github.com/r-lib/ps/issues", + "Depends": [ + "R (>= 3.4)" + ], + "Imports": [ "utils" - ] + ], + "Suggests": [ + "callr", + "covr", + "curl", + "pillar", + "pingr", + "processx (>= 3.1.0)", + "R6", + "rlang", + "testthat (>= 3.0.0)", + "webfakes", + "withr" + ], + "Biarch": "true", + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2", + "NeedsCompilation": "yes", + "Author": "Jay Loden [aut], Dave Daeschler [aut], Giampaolo Rodola' [aut], Gábor Csárdi [aut, cre], Posit Software, PBC [cph, fnd]", + "Maintainer": "Gábor Csárdi ", + "Repository": "CRAN" }, "purrr": { "Package": "purrr", - "Version": "1.2.2", + "Version": "1.2.1", "Source": "Repository", - "Requirements": [ - "cli", - "lifecycle", - "magrittr", - "R", - "rlang", - "vctrs" - ] + "Title": "Functional Programming Tools", + "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0003-4757-117X\")), person(\"Lionel\", \"Henry\", , \"lionel@posit.co\", role = \"aut\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"https://ror.org/03wc8by49\")) )", + "Description": "A complete and consistent functional programming toolkit for R.", + "License": "MIT + file LICENSE", + "URL": "https://purrr.tidyverse.org/, https://github.com/tidyverse/purrr", + "BugReports": "https://github.com/tidyverse/purrr/issues", + "Depends": [ + "R (>= 4.1)" + ], + "Imports": [ + "cli (>= 3.6.1)", + "lifecycle (>= 1.0.3)", + "magrittr (>= 1.5.0)", + "rlang (>= 1.1.1)", + "vctrs (>= 0.6.3)" + ], + "Suggests": [ + "carrier (>= 0.3.0)", + "covr", + "dplyr (>= 0.7.8)", + "httr", + "knitr", + "lubridate", + "mirai (>= 2.5.1)", + "rmarkdown", + "testthat (>= 3.0.0)", + "tibble", + "tidyselect" + ], + "LinkingTo": [ + "cli" + ], + "VignetteBuilder": "knitr", + "Biarch": "true", + "Config/build/compilation-database": "true", + "Config/Needs/website": "tidyverse/tidytemplate, tidyr", + "Config/testthat/edition": "3", + "Config/testthat/parallel": "TRUE", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3", + "NeedsCompilation": "yes", + "Author": "Hadley Wickham [aut, cre] (ORCID: ), Lionel Henry [aut], Posit Software, PBC [cph, fnd] (ROR: )", + "Maintainer": "Hadley Wickham ", + "Repository": "CRAN" }, "ragg": { "Package": "ragg", - "Version": "1.5.2", + "Version": "1.5.0", "Source": "Repository", - "Requirements": [ + "Type": "Package", + "Title": "Graphic Devices Based on AGG", + "Authors@R": "c( person(\"Thomas Lin\", \"Pedersen\", , \"thomas.pedersen@posit.co\", role = c(\"cre\", \"aut\"), comment = c(ORCID = \"0000-0002-5147-4711\")), person(\"Maxim\", \"Shemanarev\", role = c(\"aut\", \"cph\"), comment = \"Author of AGG\"), person(\"Tony\", \"Juricic\", , \"tonygeek@yahoo.com\", role = c(\"ctb\", \"cph\"), comment = \"Contributor to AGG\"), person(\"Milan\", \"Marusinec\", , \"milan@marusinec.sk\", role = c(\"ctb\", \"cph\"), comment = \"Contributor to AGG\"), person(\"Spencer\", \"Garrett\", role = \"ctb\", comment = \"Contributor to AGG\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", + "Maintainer": "Thomas Lin Pedersen ", + "Description": "Anti-Grain Geometry (AGG) is a high-quality and high-performance 2D drawing library. The 'ragg' package provides a set of graphic devices based on AGG to use as alternative to the raster devices provided through the 'grDevices' package.", + "License": "MIT + file LICENSE", + "URL": "https://ragg.r-lib.org, https://github.com/r-lib/ragg", + "BugReports": "https://github.com/r-lib/ragg/issues", + "Imports": [ + "systemfonts (>= 1.0.3)", + "textshaping (>= 0.3.0)" + ], + "Suggests": [ + "covr", + "graphics", + "grid", + "testthat (>= 3.0.0)" + ], + "LinkingTo": [ "systemfonts", "textshaping" - ] + ], + "Config/build/compilation-database": "true", + "Config/Needs/website": "ggplot2, devoid, magick, bench, tidyr, ggridges, hexbin, sessioninfo, pkgdown, tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Config/usethis/last-upkeep": "2025-04-25", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2", + "SystemRequirements": "freetype2, libpng, libtiff, libjpeg, libwebp, libwebpmux", + "NeedsCompilation": "yes", + "Author": "Thomas Lin Pedersen [cre, aut] (ORCID: ), Maxim Shemanarev [aut, cph] (Author of AGG), Tony Juricic [ctb, cph] (Contributor to AGG), Milan Marusinec [ctb, cph] (Contributor to AGG), Spencer Garrett [ctb] (Contributor to AGG), Posit Software, PBC [cph, fnd] (ROR: )", + "Repository": "CRAN" }, "rappdirs": { "Package": "rappdirs", "Version": "0.3.4", "Source": "Repository", - "Requirements": [ - "R" - ] + "Type": "Package", + "Title": "Application Directories: Determine Where to Save Data, Caches, and Logs", + "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = c(\"trl\", \"cre\", \"cph\")), person(\"Sridhar\", \"Ratnakumar\", role = \"aut\"), person(\"Trent\", \"Mick\", role = \"aut\"), person(\"ActiveState\", role = \"cph\", comment = \"R/appdir.r, R/cache.r, R/data.r, R/log.r translated from appdirs\"), person(\"Eddy\", \"Petrisor\", role = \"ctb\"), person(\"Trevor\", \"Davis\", role = c(\"trl\", \"aut\"), comment = c(ORCID = \"0000-0001-6341-4639\")), person(\"Gabor\", \"Csardi\", role = \"ctb\"), person(\"Gregory\", \"Jefferis\", role = \"ctb\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", + "Description": "An easy way to determine which directories on the users computer you should use to save data, caches and logs. A port of Python's 'Appdirs' () to R.", + "License": "MIT + file LICENSE", + "URL": "https://rappdirs.r-lib.org, https://github.com/r-lib/rappdirs", + "BugReports": "https://github.com/r-lib/rappdirs/issues", + "Depends": [ + "R (>= 4.1)" + ], + "Suggests": [ + "covr", + "roxygen2", + "testthat (>= 3.2.0)", + "withr" + ], + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Config/usethis/last-upkeep": "2025-05-05", + "Copyright": "Original python appdirs module copyright (c) 2010 ActiveState Software Inc. R port copyright Hadley Wickham, Posit, PBC. See file LICENSE for details.", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3", + "NeedsCompilation": "yes", + "Author": "Hadley Wickham [trl, cre, cph], Sridhar Ratnakumar [aut], Trent Mick [aut], ActiveState [cph] (R/appdir.r, R/cache.r, R/data.r, R/log.r translated from appdirs), Eddy Petrisor [ctb], Trevor Davis [trl, aut] (ORCID: ), Gabor Csardi [ctb], Gregory Jefferis [ctb], Posit Software, PBC [cph, fnd] (ROR: )", + "Maintainer": "Hadley Wickham ", + "Repository": "CRAN" + }, + "renv": { + "Package": "renv", + "Version": "1.1.7", + "Source": "Repository", + "Type": "Package", + "Title": "Project Environments", + "Authors@R": "c( person(\"Kevin\", \"Ushey\", role = c(\"aut\", \"cre\"), email = \"kevin@rstudio.com\", comment = c(ORCID = \"0000-0003-2880-7407\")), person(\"Hadley\", \"Wickham\", role = c(\"aut\"), email = \"hadley@rstudio.com\", comment = c(ORCID = \"0000-0003-4757-117X\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "A dependency management toolkit for R. Using 'renv', you can create and manage project-local R libraries, save the state of these libraries to a 'lockfile', and later restore your library as required. Together, these tools can help make your projects more isolated, portable, and reproducible.", + "License": "MIT + file LICENSE", + "URL": "https://rstudio.github.io/renv/, https://github.com/rstudio/renv", + "BugReports": "https://github.com/rstudio/renv/issues", + "Imports": [ + "utils" + ], + "Suggests": [ + "BiocManager", + "cli", + "compiler", + "covr", + "cpp11", + "curl", + "devtools", + "generics", + "gitcreds", + "jsonlite", + "jsonvalidate", + "knitr", + "miniUI", + "modules", + "packrat", + "pak", + "R6", + "remotes", + "reticulate", + "rmarkdown", + "rstudioapi", + "shiny", + "testthat", + "uuid", + "waldo", + "yaml", + "webfakes" + ], + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3", + "VignetteBuilder": "knitr", + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Config/testthat/parallel": "true", + "Config/testthat/start-first": "bioconductor,python,install,restore,snapshot,retrieve,remotes", + "NeedsCompilation": "no", + "Author": "Kevin Ushey [aut, cre] (ORCID: ), Hadley Wickham [aut] (ORCID: ), Posit Software, PBC [cph, fnd]", + "Maintainer": "Kevin Ushey ", + "Repository": "CRAN" }, "rlang": { "Package": "rlang", - "Version": "1.3.0", - "Source": "Repository", - "Requirements": [ - "R", + "Version": "1.1.7", + "Source": "Repository", + "Title": "Functions for Base Types and Core R and 'Tidyverse' Features", + "Description": "A toolbox for working with base types, core R features like the condition system, and core 'Tidyverse' features like tidy evaluation.", + "Authors@R": "c( person(\"Lionel\", \"Henry\", ,\"lionel@posit.co\", c(\"aut\", \"cre\")), person(\"Hadley\", \"Wickham\", ,\"hadley@posit.co\", \"aut\"), person(given = \"mikefc\", email = \"mikefc@coolbutuseless.com\", role = \"cph\", comment = \"Hash implementation based on Mike's xxhashlite\"), person(given = \"Yann\", family = \"Collet\", role = \"cph\", comment = \"Author of the embedded xxHash library\"), person(given = \"Posit, PBC\", role = c(\"cph\", \"fnd\")) )", + "License": "MIT + file LICENSE", + "ByteCompile": "true", + "Biarch": "true", + "Depends": [ + "R (>= 4.0.0)" + ], + "Imports": [ "utils" - ] + ], + "Suggests": [ + "cli (>= 3.1.0)", + "covr", + "crayon", + "desc", + "fs", + "glue", + "knitr", + "magrittr", + "methods", + "pillar", + "pkgload", + "rmarkdown", + "stats", + "testthat (>= 3.2.0)", + "tibble", + "usethis", + "vctrs (>= 0.2.3)", + "withr" + ], + "Enhances": [ + "winch" + ], + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3", + "URL": "https://rlang.r-lib.org, https://github.com/r-lib/rlang", + "BugReports": "https://github.com/r-lib/rlang/issues", + "Config/build/compilation-database": "true", + "Config/testthat/edition": "3", + "Config/Needs/website": "dplyr, tidyverse/tidytemplate", + "NeedsCompilation": "yes", + "Author": "Lionel Henry [aut, cre], Hadley Wickham [aut], mikefc [cph] (Hash implementation based on Mike's xxhashlite), Yann Collet [cph] (Author of the embedded xxHash library), Posit, PBC [cph, fnd]", + "Maintainer": "Lionel Henry ", + "Repository": "CRAN" }, "rmarkdown": { "Package": "rmarkdown", - "Version": "2.32", - "Source": "Repository", - "Requirements": [ - "bslib", - "evaluate", - "fontawesome", - "htmltools", + "Version": "2.30", + "Source": "Repository", + "Type": "Package", + "Title": "Dynamic Documents for R", + "Authors@R": "c( person(\"JJ\", \"Allaire\", , \"jj@posit.co\", role = \"aut\"), person(\"Yihui\", \"Xie\", , \"xie@yihui.name\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0003-0645-5666\")), person(\"Christophe\", \"Dervieux\", , \"cderv@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0003-4474-2498\")), person(\"Jonathan\", \"McPherson\", , \"jonathan@posit.co\", role = \"aut\"), person(\"Javier\", \"Luraschi\", role = \"aut\"), person(\"Kevin\", \"Ushey\", , \"kevin@posit.co\", role = \"aut\"), person(\"Aron\", \"Atkins\", , \"aron@posit.co\", role = \"aut\"), person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\"), person(\"Joe\", \"Cheng\", , \"joe@posit.co\", role = \"aut\"), person(\"Winston\", \"Chang\", , \"winston@posit.co\", role = \"aut\"), person(\"Richard\", \"Iannone\", , \"rich@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0003-3925-190X\")), person(\"Andrew\", \"Dunning\", role = \"ctb\", comment = c(ORCID = \"0000-0003-0464-5036\")), person(\"Atsushi\", \"Yasumoto\", role = c(\"ctb\", \"cph\"), comment = c(ORCID = \"0000-0002-8335-495X\", cph = \"Number sections Lua filter\")), person(\"Barret\", \"Schloerke\", role = \"ctb\"), person(\"Carson\", \"Sievert\", role = \"ctb\", comment = c(ORCID = \"0000-0002-4958-2844\")), person(\"Devon\", \"Ryan\", , \"dpryan79@gmail.com\", role = \"ctb\", comment = c(ORCID = \"0000-0002-8549-0971\")), person(\"Frederik\", \"Aust\", , \"frederik.aust@uni-koeln.de\", role = \"ctb\", comment = c(ORCID = \"0000-0003-4900-788X\")), person(\"Jeff\", \"Allen\", , \"jeff@posit.co\", role = \"ctb\"), person(\"JooYoung\", \"Seo\", role = \"ctb\", comment = c(ORCID = \"0000-0002-4064-6012\")), person(\"Malcolm\", \"Barrett\", role = \"ctb\"), person(\"Rob\", \"Hyndman\", , \"Rob.Hyndman@monash.edu\", role = \"ctb\"), person(\"Romain\", \"Lesur\", role = \"ctb\"), person(\"Roy\", \"Storey\", role = \"ctb\"), person(\"Ruben\", \"Arslan\", , \"ruben.arslan@uni-goettingen.de\", role = \"ctb\"), person(\"Sergio\", \"Oller\", role = \"ctb\"), person(given = \"Posit Software, PBC\", role = c(\"cph\", \"fnd\")), person(, \"jQuery UI contributors\", role = c(\"ctb\", \"cph\"), comment = \"jQuery UI library; authors listed in inst/rmd/h/jqueryui/AUTHORS.txt\"), person(\"Mark\", \"Otto\", role = \"ctb\", comment = \"Bootstrap library\"), person(\"Jacob\", \"Thornton\", role = \"ctb\", comment = \"Bootstrap library\"), person(, \"Bootstrap contributors\", role = \"ctb\", comment = \"Bootstrap library\"), person(, \"Twitter, Inc\", role = \"cph\", comment = \"Bootstrap library\"), person(\"Alexander\", \"Farkas\", role = c(\"ctb\", \"cph\"), comment = \"html5shiv library\"), person(\"Scott\", \"Jehl\", role = c(\"ctb\", \"cph\"), comment = \"Respond.js library\"), person(\"Ivan\", \"Sagalaev\", role = c(\"ctb\", \"cph\"), comment = \"highlight.js library\"), person(\"Greg\", \"Franko\", role = c(\"ctb\", \"cph\"), comment = \"tocify library\"), person(\"John\", \"MacFarlane\", role = c(\"ctb\", \"cph\"), comment = \"Pandoc templates\"), person(, \"Google, Inc.\", role = c(\"ctb\", \"cph\"), comment = \"ioslides library\"), person(\"Dave\", \"Raggett\", role = \"ctb\", comment = \"slidy library\"), person(, \"W3C\", role = \"cph\", comment = \"slidy library\"), person(\"Dave\", \"Gandy\", role = c(\"ctb\", \"cph\"), comment = \"Font-Awesome\"), person(\"Ben\", \"Sperry\", role = \"ctb\", comment = \"Ionicons\"), person(, \"Drifty\", role = \"cph\", comment = \"Ionicons\"), person(\"Aidan\", \"Lister\", role = c(\"ctb\", \"cph\"), comment = \"jQuery StickyTabs\"), person(\"Benct Philip\", \"Jonsson\", role = c(\"ctb\", \"cph\"), comment = \"pagebreak Lua filter\"), person(\"Albert\", \"Krewinkel\", role = c(\"ctb\", \"cph\"), comment = \"pagebreak Lua filter\") )", + "Description": "Convert R Markdown documents into a variety of formats.", + "License": "GPL-3", + "URL": "https://github.com/rstudio/rmarkdown, https://pkgs.rstudio.com/rmarkdown/", + "BugReports": "https://github.com/rstudio/rmarkdown/issues", + "Depends": [ + "R (>= 3.0)" + ], + "Imports": [ + "bslib (>= 0.2.5.1)", + "evaluate (>= 0.13)", + "fontawesome (>= 0.5.0)", + "htmltools (>= 0.5.1)", "jquerylib", "jsonlite", - "knitr", + "knitr (>= 1.43)", "methods", - "R", - "tinytex", + "tinytex (>= 0.31)", "tools", "utils", - "xfun", - "yaml" - ] + "xfun (>= 0.36)", + "yaml (>= 2.1.19)" + ], + "Suggests": [ + "digest", + "dygraphs", + "fs", + "rsconnect", + "downlit (>= 0.4.0)", + "katex (>= 1.4.0)", + "sass (>= 0.4.0)", + "shiny (>= 1.6.0)", + "testthat (>= 3.0.3)", + "tibble", + "vctrs", + "cleanrmd", + "withr (>= 2.4.2)", + "xml2" + ], + "VignetteBuilder": "knitr", + "Config/Needs/website": "rstudio/quillt, pkgdown", + "Config/testthat/edition": "3", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2", + "SystemRequirements": "pandoc (>= 1.14) - http://pandoc.org", + "NeedsCompilation": "no", + "Author": "JJ Allaire [aut], Yihui Xie [aut, cre] (ORCID: ), Christophe Dervieux [aut] (ORCID: ), Jonathan McPherson [aut], Javier Luraschi [aut], Kevin Ushey [aut], Aron Atkins [aut], Hadley Wickham [aut], Joe Cheng [aut], Winston Chang [aut], Richard Iannone [aut] (ORCID: ), Andrew Dunning [ctb] (ORCID: ), Atsushi Yasumoto [ctb, cph] (ORCID: , cph: Number sections Lua filter), Barret Schloerke [ctb], Carson Sievert [ctb] (ORCID: ), Devon Ryan [ctb] (ORCID: ), Frederik Aust [ctb] (ORCID: ), Jeff Allen [ctb], JooYoung Seo [ctb] (ORCID: ), Malcolm Barrett [ctb], Rob Hyndman [ctb], Romain Lesur [ctb], Roy Storey [ctb], Ruben Arslan [ctb], Sergio Oller [ctb], Posit Software, PBC [cph, fnd], jQuery UI contributors [ctb, cph] (jQuery UI library; authors listed in inst/rmd/h/jqueryui/AUTHORS.txt), Mark Otto [ctb] (Bootstrap library), Jacob Thornton [ctb] (Bootstrap library), Bootstrap contributors [ctb] (Bootstrap library), Twitter, Inc [cph] (Bootstrap library), Alexander Farkas [ctb, cph] (html5shiv library), Scott Jehl [ctb, cph] (Respond.js library), Ivan Sagalaev [ctb, cph] (highlight.js library), Greg Franko [ctb, cph] (tocify library), John MacFarlane [ctb, cph] (Pandoc templates), Google, Inc. [ctb, cph] (ioslides library), Dave Raggett [ctb] (slidy library), W3C [cph] (slidy library), Dave Gandy [ctb, cph] (Font-Awesome), Ben Sperry [ctb] (Ionicons), Drifty [cph] (Ionicons), Aidan Lister [ctb, cph] (jQuery StickyTabs), Benct Philip Jonsson [ctb, cph] (pagebreak Lua filter), Albert Krewinkel [ctb, cph] (pagebreak Lua filter)", + "Maintainer": "Yihui Xie ", + "Repository": "CRAN" }, "rvg": { "Package": "rvg", - "Version": "0.4.2", - "Source": "Repository", - "Requirements": [ - "gdtools", + "Version": "0.4.0", + "Source": "Repository", + "Type": "Package", + "Title": "R Graphics Devices for 'Office' Vector Graphics Output", + "Authors@R": "c( person(\"David\", \"Gohel\", , \"david.gohel@ardata.fr\", role = c(\"aut\", \"cre\")), person(\"ArData\", role = \"cph\"), person(\"Bob\", \"Rudis\", role = \"ctb\", comment = \"the javascript code used by function set_attr\"), person(\"Francois\", \"Brunetti\", , \"francois.brunetti@lysis-consultants.fr\", role = \"ctb\", comment = \"clipping algorithms\") )", + "Description": "Vector Graphics devices for 'Microsoft PowerPoint' and 'Microsoft Excel'. Functions extending package 'officer' are provided to embed 'DrawingML' graphics into 'Microsoft PowerPoint' presentations and 'Microsoft Excel' workbooks.", + "License": "GPL-3", + "URL": "https://ardata-fr.github.io/officeverse/, https://davidgohel.github.io/rvg/", + "BugReports": "https://github.com/davidgohel/rvg/issues", + "Depends": [ + "R (>= 3.0)" + ], + "Imports": [ + "gdtools (>= 0.3.3)", "grDevices", - "officer", - "R", - "Rcpp", + "officer (>= 0.6.2)", + "Rcpp (>= 0.12.12)", "rlang", "systemfonts", - "xml2" - ] + "xml2 (>= 1.0.0)" + ], + "Suggests": [ + "grid", + "testthat" + ], + "LinkingTo": [ + "Rcpp", + "systemfonts" + ], + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3", + "SystemRequirements": "libpng", + "NeedsCompilation": "yes", + "Author": "David Gohel [aut, cre], ArData [cph], Bob Rudis [ctb] (the javascript code used by function set_attr), Francois Brunetti [ctb] (clipping algorithms)", + "Maintainer": "David Gohel ", + "Repository": "CRAN" }, "sass": { "Package": "sass", "Version": "0.4.10", "Source": "Repository", - "Requirements": [ - "fs", - "htmltools", + "Type": "Package", + "Title": "Syntactically Awesome Style Sheets ('Sass')", + "Description": "An 'SCSS' compiler, powered by the 'LibSass' library. With this, R developers can use variables, inheritance, and functions to generate dynamic style sheets. The package uses the 'Sass CSS' extension language, which is stable, powerful, and CSS compatible.", + "Authors@R": "c( person(\"Joe\", \"Cheng\", , \"joe@rstudio.com\", \"aut\"), person(\"Timothy\", \"Mastny\", , \"tim.mastny@gmail.com\", \"aut\"), person(\"Richard\", \"Iannone\", , \"rich@rstudio.com\", \"aut\", comment = c(ORCID = \"0000-0003-3925-190X\")), person(\"Barret\", \"Schloerke\", , \"barret@rstudio.com\", \"aut\", comment = c(ORCID = \"0000-0001-9986-114X\")), person(\"Carson\", \"Sievert\", , \"carson@rstudio.com\", c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-4958-2844\")), person(\"Christophe\", \"Dervieux\", , \"cderv@rstudio.com\", c(\"ctb\"), comment = c(ORCID = \"0000-0003-4474-2498\")), person(family = \"RStudio\", role = c(\"cph\", \"fnd\")), person(family = \"Sass Open Source Foundation\", role = c(\"ctb\", \"cph\"), comment = \"LibSass library\"), person(\"Greter\", \"Marcel\", role = c(\"ctb\", \"cph\"), comment = \"LibSass library\"), person(\"Mifsud\", \"Michael\", role = c(\"ctb\", \"cph\"), comment = \"LibSass library\"), person(\"Hampton\", \"Catlin\", role = c(\"ctb\", \"cph\"), comment = \"LibSass library\"), person(\"Natalie\", \"Weizenbaum\", role = c(\"ctb\", \"cph\"), comment = \"LibSass library\"), person(\"Chris\", \"Eppstein\", role = c(\"ctb\", \"cph\"), comment = \"LibSass library\"), person(\"Adams\", \"Joseph\", role = c(\"ctb\", \"cph\"), comment = \"json.cpp\"), person(\"Trifunovic\", \"Nemanja\", role = c(\"ctb\", \"cph\"), comment = \"utf8.h\") )", + "License": "MIT + file LICENSE", + "URL": "https://rstudio.github.io/sass/, https://github.com/rstudio/sass", + "BugReports": "https://github.com/rstudio/sass/issues", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2", + "SystemRequirements": "GNU make", + "Imports": [ + "fs (>= 1.2.4)", + "rlang (>= 0.4.10)", + "htmltools (>= 0.5.1)", "R6", - "rappdirs", - "rlang" - ] + "rappdirs" + ], + "Suggests": [ + "testthat", + "knitr", + "rmarkdown", + "withr", + "shiny", + "curl" + ], + "VignetteBuilder": "knitr", + "Config/testthat/edition": "3", + "NeedsCompilation": "yes", + "Author": "Joe Cheng [aut], Timothy Mastny [aut], Richard Iannone [aut] (), Barret Schloerke [aut] (), Carson Sievert [aut, cre] (), Christophe Dervieux [ctb] (), RStudio [cph, fnd], Sass Open Source Foundation [ctb, cph] (LibSass library), Greter Marcel [ctb, cph] (LibSass library), Mifsud Michael [ctb, cph] (LibSass library), Hampton Catlin [ctb, cph] (LibSass library), Natalie Weizenbaum [ctb, cph] (LibSass library), Chris Eppstein [ctb, cph] (LibSass library), Adams Joseph [ctb, cph] (json.cpp), Trifunovic Nemanja [ctb, cph] (utf8.h)", + "Maintainer": "Carson Sievert ", + "Repository": "CRAN" }, "scales": { "Package": "scales", "Version": "1.4.0", "Source": "Repository", - "Requirements": [ + "Title": "Scale Functions for Visualization", + "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\"), person(\"Thomas Lin\", \"Pedersen\", , \"thomas.pedersen@posit.co\", role = c(\"cre\", \"aut\"), comment = c(ORCID = \"0000-0002-5147-4711\")), person(\"Dana\", \"Seidel\", role = \"aut\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", + "Description": "Graphical scales map data to aesthetics, and provide methods for automatically determining breaks and labels for axes and legends.", + "License": "MIT + file LICENSE", + "URL": "https://scales.r-lib.org, https://github.com/r-lib/scales", + "BugReports": "https://github.com/r-lib/scales/issues", + "Depends": [ + "R (>= 4.1)" + ], + "Imports": [ "cli", - "farver", + "farver (>= 2.0.3)", "glue", "labeling", "lifecycle", - "R", "R6", "RColorBrewer", - "rlang", + "rlang (>= 1.1.0)", "viridisLite" - ] + ], + "Suggests": [ + "bit64", + "covr", + "dichromat", + "ggplot2", + "hms (>= 0.5.0)", + "stringi", + "testthat (>= 3.0.0)" + ], + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Config/usethis/last-upkeep": "2025-04-23", + "Encoding": "UTF-8", + "LazyLoad": "yes", + "RoxygenNote": "7.3.2", + "NeedsCompilation": "no", + "Author": "Hadley Wickham [aut], Thomas Lin Pedersen [cre, aut] (), Dana Seidel [aut], Posit Software, PBC [cph, fnd] (03wc8by49)", + "Maintainer": "Thomas Lin Pedersen ", + "Repository": "CRAN" }, "stringi": { "Package": "stringi", - "Version": "1.8.9", - "Source": "Repository", - "Requirements": [ - "R", - "stats", + "Version": "1.8.7", + "Source": "Repository", + "Date": "2025-03-27", + "Title": "Fast and Portable Character String Processing Facilities", + "Description": "A collection of character string/text/natural language processing tools for pattern searching (e.g., with 'Java'-like regular expressions or the 'Unicode' collation algorithm), random string generation, case mapping, string transliteration, concatenation, sorting, padding, wrapping, Unicode normalisation, date-time formatting and parsing, and many more. They are fast, consistent, convenient, and - thanks to 'ICU' (International Components for Unicode) - portable across all locales and platforms. Documentation about 'stringi' is provided via its website at and the paper by Gagolewski (2022, ).", + "URL": "https://stringi.gagolewski.com/, https://github.com/gagolews/stringi, https://icu.unicode.org/", + "BugReports": "https://github.com/gagolews/stringi/issues", + "SystemRequirements": "ICU4C (>= 61, optional)", + "Type": "Package", + "Depends": [ + "R (>= 3.4)" + ], + "Imports": [ "tools", - "utils" - ] + "utils", + "stats" + ], + "Biarch": "TRUE", + "License": "file LICENSE", + "Authors@R": "c(person(given = \"Marek\", family = \"Gagolewski\", role = c(\"aut\", \"cre\", \"cph\"), email = \"marek@gagolewski.com\", comment = c(ORCID = \"0000-0003-0637-6028\")), person(given = \"Bartek\", family = \"Tartanus\", role = \"ctb\"), person(\"Unicode, Inc. and others\", role=\"ctb\", comment = \"ICU4C source code, Unicode Character Database\") )", + "RoxygenNote": "7.3.2", + "Encoding": "UTF-8", + "NeedsCompilation": "yes", + "Author": "Marek Gagolewski [aut, cre, cph] (), Bartek Tartanus [ctb], Unicode, Inc. and others [ctb] (ICU4C source code, Unicode Character Database)", + "Maintainer": "Marek Gagolewski ", + "License_is_FOSS": "yes", + "Repository": "CRAN" }, "stringr": { "Package": "stringr", "Version": "1.6.0", "Source": "Repository", - "Requirements": [ + "Title": "Simple, Consistent Wrappers for Common String Operations", + "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = c(\"aut\", \"cre\", \"cph\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "A consistent, simple and easy to use set of wrappers around the fantastic 'stringi' package. All function and argument names (and positions) are consistent, all functions deal with \"NA\"'s and zero length vectors in the same way, and the output from one function is easy to feed into the input of another.", + "License": "MIT + file LICENSE", + "URL": "https://stringr.tidyverse.org, https://github.com/tidyverse/stringr", + "BugReports": "https://github.com/tidyverse/stringr/issues", + "Depends": [ + "R (>= 4.1.0)" + ], + "Imports": [ "cli", - "glue", - "lifecycle", + "glue (>= 1.6.1)", + "lifecycle (>= 1.0.3)", "magrittr", - "R", - "rlang", - "stringi", - "vctrs" - ] + "rlang (>= 1.0.0)", + "stringi (>= 1.5.3)", + "vctrs (>= 0.4.0)" + ], + "Suggests": [ + "covr", + "dplyr", + "gt", + "htmltools", + "htmlwidgets", + "knitr", + "rmarkdown", + "testthat (>= 3.0.0)", + "tibble" + ], + "VignetteBuilder": "knitr", + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/potools/style": "explicit", + "Config/testthat/edition": "3", + "Encoding": "UTF-8", + "LazyData": "true", + "RoxygenNote": "7.3.3", + "NeedsCompilation": "no", + "Author": "Hadley Wickham [aut, cre, cph], Posit Software, PBC [cph, fnd]", + "Maintainer": "Hadley Wickham ", + "Repository": "CRAN" }, "svglite": { "Package": "svglite", "Version": "2.2.2", "Source": "Repository", - "Requirements": [ + "Title": "An 'SVG' Graphics Device", + "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\"), person(\"Lionel\", \"Henry\", , \"lionel@posit.co\", role = \"aut\"), person(\"Thomas Lin\", \"Pedersen\", , \"thomas.pedersen@posit.co\", role = c(\"cre\", \"aut\"), comment = c(ORCID = \"0000-0002-5147-4711\")), person(\"T Jake\", \"Luciani\", , \"jake@apache.org\", role = \"aut\"), person(\"Matthieu\", \"Decorde\", , \"matthieu.decorde@ens-lyon.fr\", role = \"aut\"), person(\"Vaudor\", \"Lise\", , \"lise.vaudor@ens-lyon.fr\", role = \"aut\"), person(\"Tony\", \"Plate\", role = \"ctb\", comment = \"Early line dashing code\"), person(\"David\", \"Gohel\", role = \"ctb\", comment = \"Line dashing code and early raster code\"), person(\"Yixuan\", \"Qiu\", role = \"ctb\", comment = \"Improved styles; polypath implementation\"), person(\"Håkon\", \"Malmedal\", role = \"ctb\", comment = \"Opacity code\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", + "Description": "A graphics device for R that produces 'Scalable Vector Graphics'. 'svglite' is a fork of the older 'RSvgDevice' package.", + "License": "GPL (>= 2)", + "URL": "https://svglite.r-lib.org, https://github.com/r-lib/svglite", + "BugReports": "https://github.com/r-lib/svglite/issues", + "Depends": [ + "R (>= 4.1)" + ], + "Imports": [ "base64enc", "cli", - "cpp11", "lifecycle", - "R", - "rlang", + "rlang (>= 1.1.0)", + "systemfonts (>= 1.3.0)", + "textshaping (>= 0.3.0)" + ], + "Suggests": [ + "covr", + "fontquiver (>= 0.2.0)", + "htmltools", + "knitr", + "rmarkdown", + "testthat (>= 3.0.0)", + "xml2 (>= 1.0.0)" + ], + "LinkingTo": [ + "cpp11", "systemfonts", "textshaping" - ] + ], + "VignetteBuilder": "knitr", + "Config/build/compilation-database": "true", + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Config/usethis/last-upkeep": "2025-04-25", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2", + "SystemRequirements": "libpng", + "NeedsCompilation": "yes", + "Author": "Hadley Wickham [aut], Lionel Henry [aut], Thomas Lin Pedersen [cre, aut] (ORCID: ), T Jake Luciani [aut], Matthieu Decorde [aut], Vaudor Lise [aut], Tony Plate [ctb] (Early line dashing code), David Gohel [ctb] (Line dashing code and early raster code), Yixuan Qiu [ctb] (Improved styles; polypath implementation), Håkon Malmedal [ctb] (Opacity code), Posit Software, PBC [cph, fnd] (ROR: )", + "Maintainer": "Thomas Lin Pedersen ", + "Repository": "CRAN" }, "sys": { "Package": "sys", "Version": "3.4.3", "Source": "Repository", - "Requirements": [] + "Type": "Package", + "Title": "Powerful and Reliable Tools for Running System Commands in R", + "Authors@R": "c(person(\"Jeroen\", \"Ooms\", role = c(\"aut\", \"cre\"), email = \"jeroenooms@gmail.com\", comment = c(ORCID = \"0000-0002-4035-0289\")), person(\"Gábor\", \"Csárdi\", , \"csardi.gabor@gmail.com\", role = \"ctb\"))", + "Description": "Drop-in replacements for the base system2() function with fine control and consistent behavior across platforms. Supports clean interruption, timeout, background tasks, and streaming STDIN / STDOUT / STDERR over binary or text connections. Arguments on Windows automatically get encoded and quoted to work on different locales.", + "License": "MIT + file LICENSE", + "URL": "https://jeroen.r-universe.dev/sys", + "BugReports": "https://github.com/jeroen/sys/issues", + "Encoding": "UTF-8", + "RoxygenNote": "7.1.1", + "Suggests": [ + "unix (>= 1.4)", + "spelling", + "testthat" + ], + "Language": "en-US", + "NeedsCompilation": "yes", + "Author": "Jeroen Ooms [aut, cre] (), Gábor Csárdi [ctb]", + "Maintainer": "Jeroen Ooms ", + "Repository": "CRAN" }, "systemfonts": { "Package": "systemfonts", - "Version": "1.3.2", - "Source": "Repository", - "Requirements": [ + "Version": "1.3.1", + "Source": "Repository", + "Type": "Package", + "Title": "System Native Font Finding", + "Authors@R": "c( person(\"Thomas Lin\", \"Pedersen\", , \"thomas.pedersen@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-5147-4711\")), person(\"Jeroen\", \"Ooms\", , \"jeroen@berkeley.edu\", role = \"aut\", comment = c(ORCID = \"0000-0002-4035-0289\")), person(\"Devon\", \"Govett\", role = \"aut\", comment = \"Author of font-manager\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", + "Description": "Provides system native access to the font catalogue. As font handling varies between systems it is difficult to correctly locate installed fonts across different operating systems. The 'systemfonts' package provides bindings to the native libraries on Windows, macOS and Linux for finding font files that can then be used further by e.g. graphic devices. The main use is intended to be from compiled code but 'systemfonts' also provides access from R.", + "License": "MIT + file LICENSE", + "URL": "https://github.com/r-lib/systemfonts, https://systemfonts.r-lib.org", + "BugReports": "https://github.com/r-lib/systemfonts/issues", + "Depends": [ + "R (>= 3.2.0)" + ], + "Imports": [ "base64enc", - "cpp11", "grid", "jsonlite", "lifecycle", - "R", "tools", "utils" - ] + ], + "Suggests": [ + "covr", + "farver", + "ggplot2", + "graphics", + "knitr", + "ragg", + "rmarkdown", + "svglite", + "testthat (>= 2.1.0)" + ], + "LinkingTo": [ + "cpp11 (>= 0.2.1)" + ], + "VignetteBuilder": "knitr", + "Config/build/compilation-database": "true", + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/usethis/last-upkeep": "2025-04-23", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2", + "SystemRequirements": "fontconfig, freetype2", + "NeedsCompilation": "yes", + "Author": "Thomas Lin Pedersen [aut, cre] (ORCID: ), Jeroen Ooms [aut] (ORCID: ), Devon Govett [aut] (Author of font-manager), Posit Software, PBC [cph, fnd] (ROR: )", + "Maintainer": "Thomas Lin Pedersen ", + "Repository": "CRAN" }, "textshaping": { "Package": "textshaping", - "Version": "1.0.5", - "Source": "Repository", - "Requirements": [ - "cpp11", + "Version": "1.0.4", + "Source": "Repository", + "Title": "Bindings to the 'HarfBuzz' and 'Fribidi' Libraries for Text Shaping", + "Authors@R": "c( person(\"Thomas Lin\", \"Pedersen\", , \"thomas.pedersen@posit.co\", role = c(\"cre\", \"aut\"), comment = c(ORCID = \"0000-0002-5147-4711\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", + "Description": "Provides access to the text shaping functionality in the 'HarfBuzz' library and the bidirectional algorithm in the 'Fribidi' library. 'textshaping' is a low-level utility package mainly for graphic devices that expands upon the font tool-set provided by the 'systemfonts' package.", + "License": "MIT + file LICENSE", + "URL": "https://github.com/r-lib/textshaping", + "BugReports": "https://github.com/r-lib/textshaping/issues", + "Depends": [ + "R (>= 3.2.0)" + ], + "Imports": [ "lifecycle", - "R", "stats", "stringi", - "systemfonts", + "systemfonts (>= 1.3.0)", "utils" - ] + ], + "Suggests": [ + "covr", + "grDevices", + "grid", + "knitr", + "rmarkdown", + "testthat (>= 3.0.0)" + ], + "LinkingTo": [ + "cpp11 (>= 0.2.1)", + "systemfonts (>= 1.0.0)" + ], + "VignetteBuilder": "knitr", + "Config/build/compilation-database": "true", + "Config/testthat/edition": "3", + "Config/usethis/last-upkeep": "2025-04-23", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2", + "SystemRequirements": "freetype2, harfbuzz, fribidi", + "NeedsCompilation": "yes", + "Author": "Thomas Lin Pedersen [cre, aut] (ORCID: ), Posit Software, PBC [cph, fnd] (ROR: )", + "Maintainer": "Thomas Lin Pedersen ", + "Repository": "CRAN" }, "tibble": { "Package": "tibble", "Version": "3.3.1", "Source": "Repository", - "Requirements": [ + "Title": "Simple Data Frames", + "Authors@R": "c( person(\"Kirill\", \"Mller\", , \"kirill@cynkra.com\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-1416-3412\")), person(\"Hadley\", \"Wickham\", , \"hadley@rstudio.com\", role = \"aut\"), person(\"Romain\", \"Francois\", , \"romain@r-enthusiasts.com\", role = \"ctb\"), person(\"Jennifer\", \"Bryan\", , \"jenny@rstudio.com\", role = \"ctb\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", + "Description": "Provides a 'tbl_df' class (the 'tibble') with stricter checking and better formatting than the traditional data frame.", + "License": "MIT + file LICENSE", + "URL": "https://tibble.tidyverse.org/, https://github.com/tidyverse/tibble", + "BugReports": "https://github.com/tidyverse/tibble/issues", + "Depends": [ + "R (>= 3.4.0)" + ], + "Imports": [ "cli", - "lifecycle", + "lifecycle (>= 1.0.0)", "magrittr", "methods", - "pillar", + "pillar (>= 1.8.1)", "pkgconfig", - "R", - "rlang", + "rlang (>= 1.0.2)", "utils", - "vctrs" - ] + "vctrs (>= 0.5.0)" + ], + "Suggests": [ + "bench", + "bit64", + "blob", + "brio", + "callr", + "DiagrammeR", + "dplyr", + "evaluate", + "formattable", + "ggplot2", + "here", + "hms", + "htmltools", + "knitr", + "lubridate", + "nycflights13", + "pkgload", + "purrr", + "rmarkdown", + "stringi", + "testthat (>= 3.0.2)", + "tidyr", + "withr" + ], + "VignetteBuilder": "knitr", + "Config/autostyle/rmd": "false", + "Config/autostyle/scope": "line_breaks", + "Config/autostyle/strict": "true", + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Config/testthat/parallel": "true", + "Config/testthat/start-first": "vignette-formats, as_tibble, add, invariants", + "Config/usethis/last-upkeep": "2025-06-07", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3.9000", + "NeedsCompilation": "yes", + "Author": "Kirill Mller [aut, cre] (ORCID: ), Hadley Wickham [aut], Romain Francois [ctb], Jennifer Bryan [ctb], Posit Software, PBC [cph, fnd] (ROR: )", + "Maintainer": "Kirill Mller ", + "Repository": "CRAN" }, "tidyr": { "Package": "tidyr", "Version": "1.3.2", "Source": "Repository", - "Requirements": [ - "cli", - "cpp11", - "dplyr", + "Title": "Tidy Messy Data", + "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = c(\"aut\", \"cre\")), person(\"Davis\", \"Vaughan\", , \"davis@posit.co\", role = \"aut\"), person(\"Maximilian\", \"Girlich\", role = \"aut\"), person(\"Kevin\", \"Ushey\", , \"kevin@posit.co\", role = \"ctb\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "Tools to help to create tidy data, where each column is a variable, each row is an observation, and each cell contains a single value. 'tidyr' contains tools for changing the shape (pivoting) and hierarchy (nesting and 'unnesting') of a dataset, turning deeply nested lists into rectangular data frames ('rectangling'), and extracting values out of string columns. It also includes tools for working with missing values (both implicit and explicit).", + "License": "MIT + file LICENSE", + "URL": "https://tidyr.tidyverse.org, https://github.com/tidyverse/tidyr", + "BugReports": "https://github.com/tidyverse/tidyr/issues", + "Depends": [ + "R (>= 4.1.0)" + ], + "Imports": [ + "cli (>= 3.4.1)", + "dplyr (>= 1.1.0)", "glue", - "lifecycle", + "lifecycle (>= 1.0.3)", "magrittr", - "purrr", - "R", - "rlang", - "stringr", - "tibble", - "tidyselect", + "purrr (>= 1.0.1)", + "rlang (>= 1.1.1)", + "stringr (>= 1.5.0)", + "tibble (>= 2.1.1)", + "tidyselect (>= 1.2.1)", "utils", - "vctrs" - ] + "vctrs (>= 0.5.2)" + ], + "Suggests": [ + "covr", + "data.table", + "knitr", + "readr", + "repurrrsive (>= 1.1.0)", + "rmarkdown", + "testthat (>= 3.0.0)" + ], + "LinkingTo": [ + "cpp11 (>= 0.4.0)" + ], + "VignetteBuilder": "knitr", + "Config/build/compilation-database": "true", + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Encoding": "UTF-8", + "LazyData": "true", + "RoxygenNote": "7.3.3", + "NeedsCompilation": "yes", + "Author": "Hadley Wickham [aut, cre], Davis Vaughan [aut], Maximilian Girlich [aut], Kevin Ushey [ctb], Posit Software, PBC [cph, fnd]", + "Maintainer": "Hadley Wickham ", + "Repository": "CRAN" }, "tidyselect": { "Package": "tidyselect", "Version": "1.2.1", "Source": "Repository", - "Requirements": [ - "cli", - "glue", - "lifecycle", - "R", - "rlang", - "vctrs", + "Title": "Select from a Set of Strings", + "Authors@R": "c( person(\"Lionel\", \"Henry\", , \"lionel@posit.co\", role = c(\"aut\", \"cre\")), person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "A backend for the selecting functions of the 'tidyverse'. It makes it easy to implement select-like functions in your own packages in a way that is consistent with other 'tidyverse' interfaces for selection.", + "License": "MIT + file LICENSE", + "URL": "https://tidyselect.r-lib.org, https://github.com/r-lib/tidyselect", + "BugReports": "https://github.com/r-lib/tidyselect/issues", + "Depends": [ + "R (>= 3.4)" + ], + "Imports": [ + "cli (>= 3.3.0)", + "glue (>= 1.3.0)", + "lifecycle (>= 1.0.3)", + "rlang (>= 1.0.4)", + "vctrs (>= 0.5.2)", "withr" - ] + ], + "Suggests": [ + "covr", + "crayon", + "dplyr", + "knitr", + "magrittr", + "rmarkdown", + "stringr", + "testthat (>= 3.1.1)", + "tibble (>= 2.1.3)" + ], + "VignetteBuilder": "knitr", + "ByteCompile": "true", + "Config/testthat/edition": "3", + "Config/Needs/website": "tidyverse/tidytemplate", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.0.9000", + "NeedsCompilation": "yes", + "Author": "Lionel Henry [aut, cre], Hadley Wickham [aut], Posit Software, PBC [cph, fnd]", + "Maintainer": "Lionel Henry ", + "Repository": "CRAN" }, "tinytex": { "Package": "tinytex", - "Version": "0.61", - "Source": "Repository", - "Requirements": [ - "xfun" - ] + "Version": "0.58", + "Source": "Repository", + "Type": "Package", + "Title": "Helper Functions to Install and Maintain TeX Live, and Compile LaTeX Documents", + "Authors@R": "c( person(\"Yihui\", \"Xie\", role = c(\"aut\", \"cre\", \"cph\"), email = \"xie@yihui.name\", comment = c(ORCID = \"0000-0003-0645-5666\")), person(given = \"Posit Software, PBC\", role = c(\"cph\", \"fnd\")), person(\"Christophe\", \"Dervieux\", role = \"ctb\", comment = c(ORCID = \"0000-0003-4474-2498\")), person(\"Devon\", \"Ryan\", role = \"ctb\", email = \"dpryan79@gmail.com\", comment = c(ORCID = \"0000-0002-8549-0971\")), person(\"Ethan\", \"Heinzen\", role = \"ctb\"), person(\"Fernando\", \"Cagua\", role = \"ctb\"), person() )", + "Description": "Helper functions to install and maintain the 'LaTeX' distribution named 'TinyTeX' (), a lightweight, cross-platform, portable, and easy-to-maintain version of 'TeX Live'. This package also contains helper functions to compile 'LaTeX' documents, and install missing 'LaTeX' packages automatically.", + "Imports": [ + "xfun (>= 0.48)" + ], + "Suggests": [ + "testit", + "rstudioapi" + ], + "License": "MIT + file LICENSE", + "URL": "https://github.com/rstudio/tinytex", + "BugReports": "https://github.com/rstudio/tinytex/issues", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3", + "NeedsCompilation": "no", + "Author": "Yihui Xie [aut, cre, cph] (ORCID: ), Posit Software, PBC [cph, fnd], Christophe Dervieux [ctb] (ORCID: ), Devon Ryan [ctb] (ORCID: ), Ethan Heinzen [ctb], Fernando Cagua [ctb]", + "Maintainer": "Yihui Xie ", + "Repository": "CRAN" }, "utf8": { "Package": "utf8", "Version": "1.2.6", "Source": "Repository", - "Requirements": [ - "R" - ] + "Title": "Unicode Text Processing", + "Authors@R": "c(person(given = c(\"Patrick\", \"O.\"), family = \"Perry\", role = c(\"aut\", \"cph\")), person(given = \"Kirill\", family = \"M\\u00fcller\", role = \"cre\", email = \"kirill@cynkra.com\", comment = c(ORCID = \"0000-0002-1416-3412\")), person(given = \"Unicode, Inc.\", role = c(\"cph\", \"dtc\"), comment = \"Unicode Character Database\"))", + "Description": "Process and print 'UTF-8' encoded international text (Unicode). Input, validate, normalize, encode, format, and display.", + "License": "Apache License (== 2.0) | file LICENSE", + "URL": "https://krlmlr.github.io/utf8/, https://github.com/krlmlr/utf8", + "BugReports": "https://github.com/krlmlr/utf8/issues", + "Depends": [ + "R (>= 2.10)" + ], + "Suggests": [ + "cli", + "covr", + "knitr", + "rlang", + "rmarkdown", + "testthat (>= 3.0.0)", + "withr" + ], + "VignetteBuilder": "knitr, rmarkdown", + "Config/testthat/edition": "3", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2.9000", + "NeedsCompilation": "yes", + "Author": "Patrick O. Perry [aut, cph], Kirill Mller [cre] (ORCID: ), Unicode, Inc. [cph, dtc] (Unicode Character Database)", + "Maintainer": "Kirill Mller ", + "Repository": "CRAN" }, "uuid": { "Package": "uuid", "Version": "1.2-2", "Source": "Repository", - "Requirements": [ - "R" - ] + "Title": "Tools for Generating and Handling of UUIDs", + "Author": "Simon Urbanek [aut, cre, cph] (https://urbanek.org, ORCID: ), Theodore Ts'o [aut, cph] (libuuid)", + "Maintainer": "Simon Urbanek ", + "Authors@R": "c(person(\"Simon\", \"Urbanek\", role=c(\"aut\",\"cre\",\"cph\"), email=\"Simon.Urbanek@r-project.org\", comment=c(\"https://urbanek.org\", ORCID=\"0000-0003-2297-1732\")), person(\"Theodore\",\"Ts'o\", email=\"tytso@thunk.org\", role=c(\"aut\",\"cph\"), comment=\"libuuid\"))", + "Depends": [ + "R (>= 2.9.0)" + ], + "Description": "Tools for generating and handling of UUIDs (Universally Unique Identifiers).", + "License": "MIT + file LICENSE", + "URL": "https://www.rforge.net/uuid", + "BugReports": "https://github.com/s-u/uuid/issues", + "NeedsCompilation": "yes", + "Repository": "CRAN" }, "vctrs": { "Package": "vctrs", - "Version": "0.7.3", - "Source": "Repository", - "Requirements": [ - "cli", + "Version": "0.7.1", + "Source": "Repository", + "Title": "Vector Helpers", + "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\"), person(\"Lionel\", \"Henry\", , \"lionel@posit.co\", role = \"aut\"), person(\"Davis\", \"Vaughan\", , \"davis@posit.co\", role = c(\"aut\", \"cre\")), person(\"data.table team\", role = \"cph\", comment = \"Radix sort based on data.table's forder() and their contribution to R's order()\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "Defines new notions of prototype and size that are used to provide tools for consistent and well-founded type-coercion and size-recycling, and are in turn connected to ideas of type- and size-stability useful for analysing function interfaces.", + "License": "MIT + file LICENSE", + "URL": "https://vctrs.r-lib.org/, https://github.com/r-lib/vctrs", + "BugReports": "https://github.com/r-lib/vctrs/issues", + "Depends": [ + "R (>= 4.0.0)" + ], + "Imports": [ + "cli (>= 3.4.0)", "glue", - "lifecycle", - "R", - "rlang" - ] + "lifecycle (>= 1.0.3)", + "rlang (>= 1.1.7)" + ], + "Suggests": [ + "bit64", + "covr", + "crayon", + "dplyr (>= 0.8.5)", + "generics", + "knitr", + "pillar (>= 1.4.4)", + "pkgdown (>= 2.0.1)", + "rmarkdown", + "testthat (>= 3.0.0)", + "tibble (>= 3.1.3)", + "waldo (>= 0.2.0)", + "withr", + "xml2", + "zeallot" + ], + "VignetteBuilder": "knitr", + "Config/build/compilation-database": "true", + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Config/testthat/parallel": "true", + "Encoding": "UTF-8", + "Language": "en-GB", + "RoxygenNote": "7.3.3", + "NeedsCompilation": "yes", + "Author": "Hadley Wickham [aut], Lionel Henry [aut], Davis Vaughan [aut, cre], data.table team [cph] (Radix sort based on data.table's forder() and their contribution to R's order()), Posit Software, PBC [cph, fnd]", + "Maintainer": "Davis Vaughan ", + "Repository": "CRAN" }, "viridisLite": { "Package": "viridisLite", "Version": "0.4.3", "Source": "Repository", - "Requirements": [ - "R" - ] + "Type": "Package", + "Title": "Colorblind-Friendly Color Maps (Lite Version)", + "Date": "2026-02-03", + "Authors@R": "c( person(\"Simon\", \"Garnier\", email = \"garnier@njit.edu\", role = c(\"aut\", \"cre\")), person(\"Noam\", \"Ross\", email = \"noam.ross@gmail.com\", role = c(\"ctb\", \"cph\")), person(\"Bob\", \"Rudis\", email = \"bob@rud.is\", role = c(\"ctb\", \"cph\")), person(\"Marco\", \"Sciaini\", email = \"sciaini.marco@gmail.com\", role = c(\"ctb\", \"cph\")), person(\"Antônio Pedro\", \"Camargo\", role = c(\"ctb\", \"cph\")), person(\"Cédric\", \"Scherer\", email = \"scherer@izw-berlin.de\", role = c(\"ctb\", \"cph\")) )", + "Maintainer": "Simon Garnier ", + "Description": "Color maps designed to improve graph readability for readers with common forms of color blindness and/or color vision deficiency. The color maps are also perceptually-uniform, both in regular form and also when converted to black-and-white for printing. This is the 'lite' version of the 'viridis' package that also contains 'ggplot2' bindings for discrete and continuous color and fill scales and can be found at .", + "License": "MIT + file LICENSE", + "Encoding": "UTF-8", + "Depends": [ + "R (>= 2.10)" + ], + "Suggests": [ + "hexbin (>= 1.27.0)", + "ggplot2 (>= 1.0.1)", + "testthat", + "covr" + ], + "URL": "https://sjmgarnier.github.io/viridisLite/, https://github.com/sjmgarnier/viridisLite/", + "BugReports": "https://github.com/sjmgarnier/viridisLite/issues/", + "RoxygenNote": "7.3.3", + "NeedsCompilation": "no", + "Author": "Simon Garnier [aut, cre], Noam Ross [ctb, cph], Bob Rudis [ctb, cph], Marco Sciaini [ctb, cph], Antônio Pedro Camargo [ctb, cph], Cédric Scherer [ctb, cph]", + "Repository": "CRAN" }, "withr": { "Package": "withr", - "Version": "3.0.3", + "Version": "3.0.2", "Source": "Repository", - "Requirements": [ + "Title": "Run Code 'With' Temporarily Modified Global State", + "Authors@R": "c( person(\"Jim\", \"Hester\", role = \"aut\"), person(\"Lionel\", \"Henry\", , \"lionel@posit.co\", role = c(\"aut\", \"cre\")), person(\"Kirill\", \"Müller\", , \"krlmlr+r@mailbox.org\", role = \"aut\"), person(\"Kevin\", \"Ushey\", , \"kevinushey@gmail.com\", role = \"aut\"), person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\"), person(\"Winston\", \"Chang\", role = \"aut\"), person(\"Jennifer\", \"Bryan\", role = \"ctb\"), person(\"Richard\", \"Cotton\", role = \"ctb\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", + "Description": "A set of functions to run code 'with' safely and temporarily modified global state. Many of these functions were originally a part of the 'devtools' package, this provides a simple package with limited dependencies to provide access to these functions.", + "License": "MIT + file LICENSE", + "URL": "https://withr.r-lib.org, https://github.com/r-lib/withr#readme", + "BugReports": "https://github.com/r-lib/withr/issues", + "Depends": [ + "R (>= 3.6.0)" + ], + "Imports": [ "graphics", - "grDevices", - "R" - ] + "grDevices" + ], + "Suggests": [ + "callr", + "DBI", + "knitr", + "methods", + "rlang", + "rmarkdown (>= 2.12)", + "RSQLite", + "testthat (>= 3.0.0)" + ], + "VignetteBuilder": "knitr", + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2", + "Collate": "'aaa.R' 'collate.R' 'connection.R' 'db.R' 'defer-exit.R' 'standalone-defer.R' 'defer.R' 'devices.R' 'local_.R' 'with_.R' 'dir.R' 'env.R' 'file.R' 'language.R' 'libpaths.R' 'locale.R' 'makevars.R' 'namespace.R' 'options.R' 'par.R' 'path.R' 'rng.R' 'seed.R' 'wrap.R' 'sink.R' 'tempfile.R' 'timezone.R' 'torture.R' 'utils.R' 'with.R'", + "NeedsCompilation": "no", + "Author": "Jim Hester [aut], Lionel Henry [aut, cre], Kirill Müller [aut], Kevin Ushey [aut], Hadley Wickham [aut], Winston Chang [aut], Jennifer Bryan [ctb], Richard Cotton [ctb], Posit Software, PBC [cph, fnd]", + "Maintainer": "Lionel Henry ", + "Repository": "CRAN" }, "xfun": { "Package": "xfun", - "Version": "0.61", - "Source": "Repository", - "Requirements": [ + "Version": "0.56", + "Source": "Repository", + "Type": "Package", + "Title": "Supporting Functions for Packages Maintained by 'Yihui Xie'", + "Authors@R": "c( person(\"Yihui\", \"Xie\", role = c(\"aut\", \"cre\", \"cph\"), email = \"xie@yihui.name\", comment = c(ORCID = \"0000-0003-0645-5666\", URL = \"https://yihui.org\")), person(\"Wush\", \"Wu\", role = \"ctb\"), person(\"Daijiang\", \"Li\", role = \"ctb\"), person(\"Xianying\", \"Tan\", role = \"ctb\"), person(\"Salim\", \"Brüggemann\", role = \"ctb\", email = \"salim-b@pm.me\", comment = c(ORCID = \"0000-0002-5329-5987\")), person(\"Christophe\", \"Dervieux\", role = \"ctb\"), person() )", + "Description": "Miscellaneous functions commonly used in other packages maintained by 'Yihui Xie'.", + "Depends": [ + "R (>= 3.2.0)" + ], + "Imports": [ "grDevices", - "R", "stats", "tools" - ] + ], + "Suggests": [ + "testit", + "parallel", + "codetools", + "methods", + "rstudioapi", + "tinytex (>= 0.30)", + "mime", + "litedown (>= 0.6)", + "commonmark", + "knitr (>= 1.50)", + "remotes", + "pak", + "curl", + "xml2", + "jsonlite", + "magick", + "yaml", + "data.table", + "qs2" + ], + "License": "MIT + file LICENSE", + "URL": "https://github.com/yihui/xfun", + "BugReports": "https://github.com/yihui/xfun/issues", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3", + "VignetteBuilder": "litedown", + "NeedsCompilation": "yes", + "Author": "Yihui Xie [aut, cre, cph] (ORCID: , URL: https://yihui.org), Wush Wu [ctb], Daijiang Li [ctb], Xianying Tan [ctb], Salim Brüggemann [ctb] (ORCID: ), Christophe Dervieux [ctb]", + "Maintainer": "Yihui Xie ", + "Repository": "CRAN" }, "xml2": { "Package": "xml2", - "Version": "1.6.0", + "Version": "1.5.2", "Source": "Repository", - "Requirements": [ + "Title": "Parse XML", + "Authors@R": "c( person(\"Hadley\", \"Wickham\", role = \"aut\"), person(\"Jim\", \"Hester\", role = \"aut\"), person(\"Jeroen\", \"Ooms\", email = \"jeroenooms@gmail.com\", role = c(\"aut\", \"cre\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")), person(\"R Foundation\", role = \"ctb\", comment = \"Copy of R-project homepage cached as example\") )", + "Description": "Bindings to 'libxml2' for working with XML data using a simple, consistent interface based on 'XPath' expressions. Also supports XML schema validation; for 'XSLT' transformations see the 'xslt' package.", + "License": "MIT + file LICENSE", + "URL": "https://xml2.r-lib.org, https://r-lib.r-universe.dev/xml2", + "BugReports": "https://github.com/r-lib/xml2/issues", + "Depends": [ + "R (>= 3.6.0)" + ], + "Imports": [ "cli", "methods", - "R", - "rlang" - ] + "rlang (>= 1.1.0)" + ], + "Suggests": [ + "covr", + "curl", + "httr", + "knitr", + "mockery", + "rmarkdown", + "testthat (>= 3.2.0)", + "xslt" + ], + "VignetteBuilder": "knitr", + "Config/Needs/website": "tidyverse/tidytemplate", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3", + "SystemRequirements": "libxml2: libxml2-dev (deb), libxml2-devel (rpm)", + "Collate": "'S4.R' 'as_list.R' 'xml_parse.R' 'as_xml_document.R' 'classes.R' 'format.R' 'import-standalone-obj-type.R' 'import-standalone-purrr.R' 'import-standalone-types-check.R' 'init.R' 'nodeset_apply.R' 'paths.R' 'utils.R' 'xml2-package.R' 'xml_attr.R' 'xml_children.R' 'xml_document.R' 'xml_find.R' 'xml_missing.R' 'xml_modify.R' 'xml_name.R' 'xml_namespaces.R' 'xml_node.R' 'xml_nodeset.R' 'xml_path.R' 'xml_schema.R' 'xml_serialize.R' 'xml_structure.R' 'xml_text.R' 'xml_type.R' 'xml_url.R' 'xml_write.R' 'zzz.R'", + "Config/testthat/edition": "3", + "NeedsCompilation": "yes", + "Author": "Hadley Wickham [aut], Jim Hester [aut], Jeroen Ooms [aut, cre], Posit Software, PBC [cph, fnd], R Foundation [ctb] (Copy of R-project homepage cached as example)", + "Maintainer": "Jeroen Ooms ", + "Repository": "CRAN" }, "yaml": { "Package": "yaml", "Version": "2.3.12", "Source": "Repository", - "Requirements": [] + "Type": "Package", + "Title": "Methods to Convert R Data to YAML and Back", + "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"cre\", comment = c(ORCID = \"0000-0003-4757-117X\")), person(\"Shawn\", \"Garbett\", , \"shawn.garbett@vumc.org\", role = \"ctb\", comment = c(ORCID = \"0000-0003-4079-5621\")), person(\"Jeremy\", \"Stephens\", role = c(\"aut\", \"ctb\")), person(\"Kirill\", \"Simonov\", role = \"aut\"), person(\"Yihui\", \"Xie\", role = \"ctb\", comment = c(ORCID = \"0000-0003-0645-5666\")), person(\"Zhuoer\", \"Dong\", role = \"ctb\"), person(\"Jeffrey\", \"Horner\", role = \"ctb\"), person(\"reikoch\", role = \"ctb\"), person(\"Will\", \"Beasley\", role = \"ctb\", comment = c(ORCID = \"0000-0002-5613-5006\")), person(\"Brendan\", \"O'Connor\", role = \"ctb\"), person(\"Michael\", \"Quinn\", role = \"ctb\"), person(\"Charlie\", \"Gao\", role = \"ctb\"), person(c(\"Gregory\", \"R.\"), \"Warnes\", role = \"ctb\"), person(c(\"Zhian\", \"N.\"), \"Kamvar\", role = \"ctb\") )", + "Description": "Implements the 'libyaml' 'YAML' 1.1 parser and emitter () for R.", + "License": "BSD_3_clause + file LICENSE", + "URL": "https://yaml.r-lib.org, https://github.com/r-lib/yaml/", + "BugReports": "https://github.com/r-lib/yaml/issues", + "Suggests": [ + "knitr", + "rmarkdown", + "testthat (>= 3.0.0)" + ], + "Config/testthat/edition": "3", + "Config/Needs/website": "tidyverse/tidytemplate", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.3", + "VignetteBuilder": "knitr", + "NeedsCompilation": "yes", + "Author": "Hadley Wickham [cre] (ORCID: ), Shawn Garbett [ctb] (ORCID: ), Jeremy Stephens [aut, ctb], Kirill Simonov [aut], Yihui Xie [ctb] (ORCID: ), Zhuoer Dong [ctb], Jeffrey Horner [ctb], reikoch [ctb], Will Beasley [ctb] (ORCID: ), Brendan O'Connor [ctb], Michael Quinn [ctb], Charlie Gao [ctb], Gregory R. Warnes [ctb], Zhian N. Kamvar [ctb]", + "Maintainer": "Hadley Wickham ", + "Repository": "CRAN" }, "zip": { "Package": "zip", - "Version": "3.0.2", - "Source": "Repository", - "Requirements": [ - "cli" - ] + "Version": "2.3.3", + "Source": "Repository", + "Title": "Cross-Platform 'zip' Compression", + "Authors@R": "c( person(\"Gábor\", \"Csárdi\", , \"csardi.gabor@gmail.com\", role = c(\"aut\", \"cre\")), person(\"Kuba\", \"Podgórski\", role = \"ctb\"), person(\"Rich\", \"Geldreich\", role = \"ctb\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", + "Description": "Cross-Platform 'zip' Compression Library. A replacement for the 'zip' function, that does not require any additional external tools on any platform.", + "License": "MIT + file LICENSE", + "URL": "https://github.com/r-lib/zip, https://r-lib.github.io/zip/", + "BugReports": "https://github.com/r-lib/zip/issues", + "Suggests": [ + "covr", + "pillar", + "processx", + "R6", + "testthat", + "withr" + ], + "Config/Needs/website": "tidyverse/tidytemplate", + "Config/testthat/edition": "3", + "Config/usethis/last-upkeep": "2025-05-07", + "Encoding": "UTF-8", + "RoxygenNote": "7.3.2.9000", + "NeedsCompilation": "yes", + "Author": "Gábor Csárdi [aut, cre], Kuba Podgórski [ctb], Rich Geldreich [ctb], Posit Software, PBC [cph, fnd] (ROR: )", + "Maintainer": "Gábor Csárdi ", + "Repository": "CRAN" } } } From 210673c803566da340a6ad2c294526ffd8ee0313 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Tue, 13 Oct 2026 13:12:45 +0200 Subject: [PATCH 26/44] mds-plan C1/B1b: wrapper-body jaspBase ref check --- Tools/buildBundle.R | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Tools/buildBundle.R b/Tools/buildBundle.R index 611db66..577e15c 100644 --- a/Tools/buildBundle.R +++ b/Tools/buildBundle.R @@ -228,6 +228,25 @@ if (localJaspBase) { message("bundle jaspBase version: ", as.character(utils::packageVersion("jaspBase", lib.loc = file.path(checkdir, "lib")))) } +# --------------------------------------------------------------------------------------------- +# Wrapper-body gate: every `jaspBase::` reference in the module sources must be EXPORTED +# by the jaspBase this bundle ships. The generator once emitted the @noRd-internal +# jaspBase::storeDataSets into aware wrappers - a regenerated wrapper then died in plain R with +# "'storeDataSets' is not an exported object". The forbidden-exports check above only guards the +# jaspBase side; nothing caught a broken *reference* on the wrapper side. parse() first so +# comments mentioning symbols (deparse drops them) can never false-positive. +wrapperFiles <- list.files(file.path(moduledir, "R"), pattern = "[.]R$", full.names = TRUE) +codeNoComments <- unlist(lapply(wrapperFiles, function(f) deparse(parse(f, keep.source = FALSE))), use.names = FALSE) +refs <- unique(unlist(regmatches(codeNoComments, + gregexpr("jaspBase::(?!:)`?([A-Za-z._][A-Za-z0-9._]*)", codeNoComments, perl = TRUE)), use.names = FALSE)) +refNames <- sub("^jaspBase::`?", "", refs) +unknown <- setdiff(refNames, getNamespaceExports("jaspBase")) +if (length(unknown)) + stop("wrapper bodies reference jaspBase symbols the installed jaspBase does not export: ", + paste(unknown, collapse = ", "), " (generated by an outdated generator? regenerate the wrappers)") +message("VERIFIED: wrapper bodies reference only exported jaspBase symbols (", + length(refNames), " distinct).") + message("Bundle: ", bundle, " (size: ", format(file.size(bundle) / 2^20, digits = 1), " MiB)") # --------------------------------------------------------------------------------------------- From ca0cbc6f365090ed8a07a39cfc6d1efbab2cbbde Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Tue, 13 Oct 2026 19:22:18 +0200 Subject: [PATCH 27/44] mds-plan C3: VERIFY-live phantom-slice checks in RPC verifier (mid-session host switch, scalars unstamped, restamp there-and-back) --- Tools/verifyMultiDataSetRpc.py | 88 ++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/Tools/verifyMultiDataSetRpc.py b/Tools/verifyMultiDataSetRpc.py index c3a386d..df2904b 100644 --- a/Tools/verifyMultiDataSetRpc.py +++ b/Tools/verifyMultiDataSetRpc.py @@ -13,6 +13,10 @@ datasets: 12.50 vs 17.73 - no 'variable compared with itself') - the .meta dump shows per-form provenance (dependentA ds1/f1, dependentB ds2/f2) - no raw JASPColumn_* anywhere (decode on send) +And the C3/VERIFY-live block (mds-plan C3/M5): switch one host's selection mid-session and +assert the next run only shows the LIVE selections - no phantom slice for the abandoned filter, +no provenance stamps on scalars or on the selection options themselves, column values restamped +onto the new filter (there and back). Exit 0 = all good, 1 = failure (details printed). Run recipe (scripted crash-repro + verify loop): @@ -213,5 +217,89 @@ def meta_of(opt): check("meta.dependentA stamped with form A's dataset/filter (1/1)", meta_of("dependentA") == ("1", "1"), str(meta_of("dependentA"))) check("meta.dependentB stamped with form B's dataset/filter (2/2)", meta_of("dependentB") == ("2", "2"), str(meta_of("dependentB"))) +# ---------------------------------------------------------------------------- +# C3/VERIFY-live (mds-plan C3/M5): the phantom-slice class. Switch ONE HOST'S SELECTION +# mid-session and check the NEXT run's data shows only the LIVE selections: no slice queued +# for an abandoned filter, no stale filterId stamp left in the options .meta. Scalars and the +# selection options themselves carry no provenance at all now - only column-bearing values do +# (BoundControlBase::createMeta gate), and a switching host restamps those (HostFilterSelection +# ::restampValuesUnderHost). This RPC workspace has one (default) filter per dataset, so the +# mid-session switch here crosses datasets (form A: f1 -> f2 -> f1); the sharp same-dataset/ +# other-filter case - where no encoded value changes and only the restamp walk can keep .meta +# honest - is pinned C++/QML-side by +# Tests/qmlTests/tst_dataSetSelectionVariablesForm.qml::test_midsession_switch_restamps_column_values. +# ---------------------------------------------------------------------------- + +def run_and_results(opts, tag): + r = call("analysis_run", {"analysisId": aid, "wait": True, "timeoutMs": 120000, "options": opts}) + check(f"{tag} complete", r.get("status") == "complete", str(r.get("status"))) + res = call("analysis_results", {"analysisId": aid, "wait": True, "timeoutMs": 60000}) + return r, res + +def overview_ids(res): + t = next((x for x in tables(res.get("results", res)).values() if x.get("title") == "Datasets in this run"), None) + if not t: + return None, None + fields, fm, rks = field_maps(t) + return sorted(str(fm["id"].get(r)) for r in rks), t + +def meta_window(res, opt): + """(dataSetId, filterId) of opt's .meta entry as the engine saw it (metaDump), or None.""" + text = json.dumps(res).replace(chr(92) + chr(34), '"').replace(""", '"').replace(""", '"') + i = text.find(f'"{opt}"') + while i != -1: + window = text[i:i + 400] + if "dataSetId" in window and "filterId" in window: + m_ds = _re.search(r'"dataSetId":\s*"?(-?\d+)', window) + m_fl = _re.search(r'"filterId":\s*"?(-?\d+)', window) + if m_ds and m_fl: + return m_ds.group(1), m_fl.group(1) + i = text.find(f'"{opt}"', i + 1) + return None + +# switch form A to dataset 2's filter (both forms then sit on DataSet 2): +r3, res3 = run_and_results({ + "dataSetA": "2", + "dataSetB": "2", + "dependentA": {"types": [], "value": ["Score"]}, + "dependentB": {"types": [], "value": ["Score", "Condition"]}, + "showMeta": True}, "run 3 (form A switched to filter 2 mid-session)") + +ids3, _ = overview_ids(res3) +check("run 3: no phantom slice - only DataSet 2 loaded", ids3 == ["2"], str(ids3)) +check("run 3: dataSetIds not over-reported", sorted(r3.get("dataSetIds", [])) == [2], str(r3.get("dataSetIds"))) +check("run 3: dependentA restamped onto the live selection (2/2)", meta_window(res3, "dependentA") == ("2", "2"), str(meta_window(res3, "dependentA"))) +check("run 3: scalar showMeta carries no provenance", meta_window(res3, "showMeta") is None, str(meta_window(res3, "showMeta"))) +check("run 3: scalar guardDemo carries no provenance", meta_window(res3, "guardDemo") is None, str(meta_window(res3, "guardDemo"))) +check("run 3: selection option dataSetA carries no provenance", meta_window(res3, "dataSetA") is None, str(meta_window(res3, "dataSetA"))) + +sv3 = next((t for t in tables(res3.get("results", res3)).values() if t.get("title") == "Selected variables"), None) +rows3 = [] +if sv3: + f3, fm3, rk3 = field_maps(sv3) + rows3 = [{f: fm3[f].get(r) for f in f3} for r in rk3] +a3 = next((x for x in rows3 if x["source"] == "A" and x["variable"] == "Score"), None) +check("run 3: A:Score routed through the restamped key to DataSet 2 (mean 17.73)", + a3 and str(a3["dataSetId"]) == "2" and "17.73" in str(a3["values"]), json.dumps(a3)) + +# and back: form A returns to filter 1 - DataSet 1 must come back exactly once (no doubled slice): +r4, res4 = run_and_results({ + "dataSetA": "1", + "dataSetB": "2", + "dependentA": {"types": [], "value": ["Score"]}, + "dependentB": {"types": [], "value": ["Score", "Condition"]}, + "showMeta": True}, "run 4 (form A switched back to filter 1)") + +ids4, _ = overview_ids(res4) +check("run 4: exactly one slice per dataset again", ids4 == ["1", "2"], str(ids4)) +check("run 4: dataSetIds reports both datasets", sorted(r4.get("dataSetIds", [])) == [1, 2], str(r4.get("dataSetIds"))) +check("run 4: dependentA restamped back onto filter 1 (1/1)", meta_window(res4, "dependentA") == ("1", "1"), str(meta_window(res4, "dependentA"))) +a4 = None +sv4 = next((t for t in tables(res4.get("results", res4)).values() if t.get("title") == "Selected variables"), None) +if sv4: + f4, fm4, rk4 = field_maps(sv4) + a4 = next((x for x in ({f: fm4[f].get(r) for f in f4} for r in rk4) if x["source"] == "A" and x["variable"] == "Score"), None) +check("run 4: A:Score back on DataSet 1 (mean 12.50)", a4 and str(a4["dataSetId"]) == "1" and "12.50" in str(a4["values"]), json.dumps(a4)) + print("\n" + ("ALL CHECKS PASSED" if not fails else f"{len(fails)} FAILURES: {fails}")) sys.exit(0 if not fails else 1) From c1396e653507946197f41c1f34ef47b02635f47b Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Tue, 13 Oct 2026 23:26:47 +0200 Subject: [PATCH 28/44] mds-plan C4: remove nonAware twin, RPC paths, untrack results artifact, round-trip checks M8: multiDataSetNonAware removed - Description entry, wrapper, testMultiDataSetNonAware.qml and the Internal body, plus the buildBundle func/qml pair (only surviving reference mentions explain the removal). The rlogic bridge section in jasp-desktop was retargeted to testVariablesForm in the same change (jasp-desktop commit 'mds-plan C4: map resolver (no ids on syntax surface), ...') - cross-repo pairing, land together. testMultiDataSet.R: bridge-boundary section rewritten to the C4 surface contract (ids informational, selections are titles; integer-id selections now REJECTED - M4 flip; 'title - name' fails loudly on fresh loads; {types,value} unwrap/reject), path 3 retargeted to testEncodingFunc (real .readDataSetToEnd call = non-aware guard's negative case). multiDataSetFuncWrapper.R regenerated: selection defaults render as surface strings ("" now, was {types,value}) per C4/RENDER. S7: results_multidataset.json untracked + gitignored (verifier run artifact). S9: verifyMultiDataSetRpc.py paths self-locate (script dir, overridable via --module= or $JASP_TESTMODULE_DIR). --- .gitignore | 1 + NEWS.md | 7 +- R/multiDataSet.R | 11 +- R/multiDataSetFuncWrapper.R | 4 +- R/multiDataSetNonAwareWrapper.R | 53 ------- TODO-multidataset.md | 3 +- Tools/buildBundle.R | 3 +- Tools/testMultiDataSet.R | 78 +++++++---- Tools/verifyMultiDataSetRpc.py | 21 ++- inst/Description.qml | 19 +-- inst/qml/testMultiDataSetNonAware.qml | 61 -------- results_multidataset.json | 193 -------------------------- 12 files changed, 88 insertions(+), 366 deletions(-) delete mode 100644 R/multiDataSetNonAwareWrapper.R delete mode 100644 inst/qml/testMultiDataSetNonAware.qml delete mode 100644 results_multidataset.json diff --git a/.gitignore b/.gitignore index adfe98d..ee6b67c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ _processedLockFile.lock ModuleBundleBuildDir/ bundles_out/ +results_multidataset.json diff --git a/NEWS.md b/NEWS.md index 4e43db8..c99c728 100644 --- a/NEWS.md +++ b/NEWS.md @@ -38,8 +38,11 @@ entries pointing at removed qml files (test.qml, TestPlotStatus.qml) can't generate one, and filteredDataEntry crashes the syntax bridge headlessly (Filter without DataSet assertion in CommonData/filter.cpp - a real bridge bug to fix). -* Non-aware counterpart moved to its own func (`multiDataSetNonAware`), a wrapper may - not be generated for a `...Internal` name. +* The non-aware twin (`multiDataSetNonAware` func/qml/wrapper) is removed again (mds-plan + C4/M8): its generated wrapper carried a stale qml pointer so its R entry point always + errored, and every other analysis in this module already is a plain single-dataset one. + Legacy path 3 of Tools/testMultiDataSet.R now runs through `testEncodingFunc` (whose R body + really calls `.readDataSetToEnd`). diff --git a/R/multiDataSet.R b/R/multiDataSet.R index 43d49fb..6fa9cc5 100644 --- a/R/multiDataSet.R +++ b/R/multiDataSet.R @@ -133,12 +133,11 @@ readDataSetGuardDemo <- function(jaspResults) { jaspResults[["guardDemo"]] <- createJaspHtml(message) } -# Non-aware counterpart (Description entry func: "multiDataSetNonAware"; the engine appends -# Internal because hasWrappers is on for the whole module). Same form and body, but reached -# through the old single-dataset path: no datasets argument, so readDataSet/dataset rule. -multiDataSetNonAwareInternal <- function(jaspResults, dataset, options) { - multiDataSetFuncInternal(jaspResults, dataset, options) -} +# The non-aware counterpart (multiDataSetNonAwareInternal + Description entry) was removed by +# mds-plan C4/M8: every other analysis here is already a plain single-dataset one, and the +# twin's generated wrapper had a stale qml pointer so its R entry point always errored. The +# "Non-aware run." branch above stays: it renders whenever `datasets` never arrives (old +# jaspBase, direct Internal calls). # ------------------------------------------------------------------ internals diff --git a/R/multiDataSetFuncWrapper.R b/R/multiDataSetFuncWrapper.R index 5b970c6..bb11f97 100644 --- a/R/multiDataSetFuncWrapper.R +++ b/R/multiDataSetFuncWrapper.R @@ -22,8 +22,8 @@ multiDataSetFunc <- function( datasets = NULL, version = "1", - dataSetA = list(types = "unknown", value = ""), - dataSetB = list(types = "unknown", value = ""), + dataSetA = "", + dataSetB = "", dependentA = list(types = list(), value = list()), dependentB = list(types = list(), value = list()), guardDemo = FALSE, diff --git a/R/multiDataSetNonAwareWrapper.R b/R/multiDataSetNonAwareWrapper.R deleted file mode 100644 index fe9000d..0000000 --- a/R/multiDataSetNonAwareWrapper.R +++ /dev/null @@ -1,53 +0,0 @@ -# -# Copyright (C) 2013-2025 University of Amsterdam -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -# This is a generated file. Don't change it! - -#' Test Multi-DataSet (non-aware counterpart) -#' -multiDataSetNonAware <- function( - data = NULL, - version = "1", - dataSetA = list(types = "unknown", value = ""), - dataSetB = list(types = "unknown", value = ""), - dependentA = list(types = list(), value = list()), - dependentB = list(types = list(), value = list()), - guardDemo = FALSE, - plotHeight = 320, - plotWidth = 480, - showMeta = FALSE) { - - defaultArgCalls <- formals(jaspTestModule::multiDataSetNonAware) - defaultArgs <- lapply(defaultArgCalls, eval) - options <- as.list(match.call())[-1L] - options <- lapply(options, eval) - defaults <- setdiff(names(defaultArgs), names(options)) - options[defaults] <- defaultArgs[defaults] - options[["data"]] <- NULL - options[["version"]] <- NULL - - - if (!jaspBase::jaspResultsCalledFromJasp() && !is.null(data)) { - jaspBase::storeDataSet(data) - } - - optionsWithFormula <- c("dataSetA", "dataSetB", "dependentA", "dependentB") - for (name in optionsWithFormula) { - if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } - - return(jaspBase::runWrappedAnalysis("jaspTestModule", "multiDataSetNonAware", "testMultiDataSet.qml", options, version, TRUE)) -} \ No newline at end of file diff --git a/TODO-multidataset.md b/TODO-multidataset.md index 4900bb8..710ec56 100644 --- a/TODO-multidataset.md +++ b/TODO-multidataset.md @@ -50,7 +50,8 @@ testMultiDataSet.R now asserts plain-name parity (this file's paths 1&3 were rew - path 1: aware run through the bridge — real dataset ids, per-form selection honoured, slices trimmed engine-style to the referenced columns (wanted-cols: Group+Score / Age+Score), everything decoded again on exit; -- path 3: single-dataset legacy semantics go through multiDataSetNonAware — an AWARE analysis +- path 3: single-dataset legacy semantics run through testEncodingFunc — the multiDataSetNonAware + twin was removed (mds-plan C4/M8, its wrapper always errored); an AWARE analysis ALWAYS gets the slice queue now (correct engine semantics), and the bridge (like syntax mode) rejects empty selection options rather than silently coercing them. Fixed on the way (were pre-existing branch bugs): segfault when an unnamed loadDataSet diff --git a/Tools/buildBundle.R b/Tools/buildBundle.R index 577e15c..fee7047 100644 --- a/Tools/buildBundle.R +++ b/Tools/buildBundle.R @@ -164,8 +164,7 @@ if (requireNamespace("jaspSyntax", quietly = TRUE)) { desc <- readModuleDescription(moduledir) stopifnot(!is.null(desc)) for (check in list( - list(func = "multiDataSetFunc", qml = "testMultiDataSet.qml", aware = TRUE), - list(func = "multiDataSetNonAware", qml = "testMultiDataSetNonAware.qml", aware = FALSE))) { + list(func = "multiDataSetFunc", qml = "testMultiDataSet.qml", aware = TRUE))) { qmlFile <- file.path(moduledir, "inst", "qml", check$qml) stopifnot(file.exists(qmlFile)) parseOptions <- NULL diff --git a/Tools/testMultiDataSet.R b/Tools/testMultiDataSet.R index 0be86eb..d8ec831 100644 --- a/Tools/testMultiDataSet.R +++ b/Tools/testMultiDataSet.R @@ -125,14 +125,16 @@ check(!grepl("JASPColumn_", raw1), "no encoded tokens anywhere in the returned r check(grepl("not available in multi-dataset aware analyses", raw1, fixed = TRUE), "guard demo: readDataSet() hard-stopped during the aware run") -# ------------------------------------------------------------------ Bridge ids (Track C) ---- -cat("\n== Bridge boundary: loadDataSets returns dataset ids; selections accept them ==\n") -# Titles stay the outer-surface sugar; INSIDE the wrapper everything keys on integer ids. The -# loaders hand back the real database ids so a syntax script can refer to datasets by the -# invariant instead of a title the user could rename mid-session. +# ------------------------------------------------------------------ Bridge surface (C4) ---- +cat("\n== Bridge boundary: loadDataSets returns ids (informational); selections are TITLES ==\n") +# mds-plan C4: the syntax surface of the dataset selection options is human strings ONLY - +# the bare TITLE for a dataset's default filter, "title - filterName" for a saved named one. +# The integer ids the loaders hand back stay useful for logging and for slicing results by, +# but writing one into a selection option is now a hard error (M4: the old numeric-id +# acceptance let a title shadow an id and vice versa - gone). ids <- jaspSyntax::loadDataSets(list(DataSetA = csvA, DataSetB = csvB)) check(is.integer(ids) && length(ids) == 2L && identical(names(ids), c("DataSetA", "DataSetB")) && all(ids > 0), - "loadDataSets returns the named integer dataset ids") + "loadDataSets returns the named integer dataset ids (informational, not selection syntax)") qmlFile <- file.path(find.package("jaspTestModule"), "qml", "testMultiDataSet.qml") parseSelection <- function(selA, selB) { @@ -143,23 +145,41 @@ parseSelection <- function(selA, selB) { as.character(jsonlite::toJSON(opts, auto_unbox = TRUE, digits = NA, null = "null")), "1", TRUE) } -stId <- tryCatch(parseSelection(as.integer(ids[[1]]), as.integer(ids[[2]])), error = function(e) e) -check(!inherits(stId, "error") && nzchar(stId$options), "integer DATASET-id selections parse through the bridge") -parsed <- jsonlite::fromJSON(stId$options, simplifyVector = FALSE) +stTitle <- tryCatch(parseSelection("DataSetA", "DataSetB"), error = function(e) e) +check(!inherits(stTitle, "error") && nzchar(stTitle$options), "title selections parse through the bridge") +parsed <- jsonlite::fromJSON(stTitle$options, simplifyVector = FALSE) keyA <- as.character(utils::tail(unlist(parsed$dataSetA), 1L)) keyB <- as.character(utils::tail(unlist(parsed$dataSetB), 1L)) -msj <- jsonlite::fromJSON(stId$multiDataSetJson) +msj <- jsonlite::fromJSON(stTitle$multiDataSetJson) check(all(c(keyA, keyB) %in% as.character(msj$ids)) && keyA != keyB, - "id selections resolved to two distinct queued slice keys (filter-id key space)") + "title selections resolved to two distinct queued slice keys (filter-id key space)") check(identical(as.character(msj$dataSetIds[[keyA]]), as.character(ids[[1]])), - "the slice an id selected belongs to exactly that dataset") - -errBadTitle <- tryCatch({ parseSelection("NoSuchDataSet", as.integer(ids[[2]])); "" }, error = function(e) conditionMessage(e)) -check(grepl("neither the title nor the id", errBadTitle), - "an unknown title is rejected loudly (error lists what is loaded)") -errBadId <- tryCatch({ parseSelection(999L, 998L); "" }, error = function(e) conditionMessage(e)) -check(grepl("neither the title nor the id", errBadId), - "an unknown integer is rejected too - no filter-id guessing (1 is not both a dataset and a filter)") + "the slice a title selected belongs to exactly that dataset") + +errId <- tryCatch({ parseSelection(as.integer(ids[[1]]), as.integer(ids[[2]])); "" }, error = function(e) conditionMessage(e)) +check(grepl("matches no dataset selection string", errId), + "integer dataset ids are rejected on the syntax surface (mds-plan C4/M4: no numeric-id acceptance anymore)") +errBadTitle <- tryCatch({ parseSelection("NoSuchDataSet", "DataSetB"); "" }, error = function(e) conditionMessage(e)) +check(grepl("matches no dataset selection string", errBadTitle) && grepl("available:", errBadTitle), + "an unknown title is rejected loudly and the error lists what IS selectable") +errNamed <- tryCatch({ parseSelection("DataSetA - myfilter", "DataSetB"); "" }, error = function(e) conditionMessage(e)) +check(grepl("matches no dataset selection string", errNamed), + "'title - name' fails loudly in full R: freshly loaded datasets have only their default filter") +errShape <- tryCatch({ + jaspSyntax::loadQmlAndParseOptionsStatus("jaspTestModule", "multiDataSetFunc", qmlFile, + as.character(jsonlite::toJSON(list( + dependentA = c("Score", "Group"), dependentB = c("Score", "Age"), + dataSetA = list(types = "unknown", value = list("DataSetA", "more")), # array inside: auto_unbox keeps it an array + dataSetB = "DataSetB", + showMeta = FALSE, guardDemo = FALSE, plotWidth = 400, plotHeight = 300), + auto_unbox = TRUE, digits = NA, null = "null")), "1", TRUE) + "" +}, error = function(e) conditionMessage(e)) +check(grepl("pass the dataset title directly", errShape), + "a {types,value} selection with no plain value inside is rejected with the direct-title hint") +stWrapped <- tryCatch(parseSelection(list(types = "unknown", value = "DataSetA"), "DataSetB"), error = function(e) e) +check(!inherits(stWrapped, "error"), + "the desktop-shaped {types,value} with a plain title inside unwraps (old wrapper defaults stay valid)") # ------------------------------------------------------------------ Path 2: wrapper call ------- # The wrapper path is run in a clean Rscript subprocess (no jaspTools runtime in the way): @@ -172,18 +192,20 @@ check(any(grepl("MULTIDATASET-WRAPPER-OK", status2, fixed = TRUE)), "wrapper sub # ------------------------------------------------------------------ Path 3: legacy still OK ---- cat("\n== Path 3: legacy non-aware run (single dataset, plain names) ==\n") # The aware analysis ALWAYS gets the bridge slice queue now (engine semantics: multiDataSetJson -# is produced whenever the Description says aware), so the legacy dataset/readDataSet path is -# exercised through the module's non-aware counterpart - its qml has no dataset selection. -res3 <- jaspTools::runAnalysis("multiDataSetNonAware", dataset = csvA, - options = list(dependentA = "Score", dependentB = "Age", - showMeta = FALSE, guardDemo = TRUE, - plotWidth = 400, plotHeight = 300), +# is produced whenever the Description says aware), so the legacy dataset/readDataSet path runs +# through a plain non-aware analysis instead of the removed twin (mds-plan C4/M8): +# testEncodingFunc has no preloadData, so its R body really calls .readDataSetToEnd() and +# echoes the column values - a live witness that the readDataSet guard stays silent outside +# aware runs and that data survives the bridge encode->decode round trip. +res3 <- jaspTools::runAnalysis("testEncodingFunc", dataset = csvA, + options = list(selected = "Score"), view = FALSE, quiet = TRUE) raw3 <- jsonlite::toJSON(res3, auto_unbox = TRUE) check(identical(res3$status, "complete"), "legacy run completed") -check(grepl("\\(current\\)", raw3), "legacy run used the single-dataset path (no multi mode)") -check(grepl(sprintf("mean %.2f", mean(csvA$Score)), raw3, fixed = TRUE), - "legacy run computed DataSetA's Score mean through the bridge roundtrip") +check(grepl("12.5", raw3, fixed = TRUE), + "legacy run read DataSetA's Score values through the (non-aware) readDataSet path") +check(grepl("encodingsEtc", raw3, fixed = TRUE) && grepl("\"Score\"", raw3, fixed = TRUE), + "the plain column name came back decoded (encode -> decode roundtrip on the legacy path too)") check(!grepl("JASPColumn_", raw3), "legacy results decoded too") check(!grepl("not available in multi-dataset", raw3, fixed = TRUE), "guard demo: readDataSet() works normally outside aware runs") diff --git a/Tools/verifyMultiDataSetRpc.py b/Tools/verifyMultiDataSetRpc.py index df2904b..0d6e11a 100644 --- a/Tools/verifyMultiDataSetRpc.py +++ b/Tools/verifyMultiDataSetRpc.py @@ -61,10 +61,23 @@ def wait_up(max_wait=120): call = _rpc -import sys, json, time, re as _re - -A_CSV = "/home/virtuoos/Broncode/JASP/jaspTestModule/Tools/testdata/DataSetA.csv" -B_CSV = "/home/virtuoos/Broncode/JASP/jaspTestModule/Tools/testdata/DataSetB.csv" +import sys, json, time, os, re as _re + +# Locations (mds-plan C4/S9): nothing machine-specific. The module dir is resolved as +# --module= (argv) > $JASP_TESTMODULE_DIR > the directory containing this script +# (it lives in /Tools/, so the default is self-locating). +def _findModuleDir(): + for a in sys.argv[1:]: + if a.startswith("--module="): + return os.path.abspath(a.split("=", 1)[1]) + env = os.environ.get("JASP_TESTMODULE_DIR") + if env: + return os.path.abspath(env) + return os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +_moduleDir = _findModuleDir() +A_CSV = os.path.join(_moduleDir, "Tools", "testdata", "DataSetA.csv") +B_CSV = os.path.join(_moduleDir, "Tools", "testdata", "DataSetB.csv") fails = [] def check(name, cond, extra=""): diff --git a/inst/Description.qml b/inst/Description.qml index eebcdec..22ee19f 100644 --- a/inst/Description.qml +++ b/inst/Description.qml @@ -95,20 +95,11 @@ Description hasWrapper: true } - // Same forms, but NOT aware: data must arrive the old way (dataset/readDataSet) and the dataset - // selection must stay inert. Own func name because with hasWrappers the engine appends - // "Internal" to every entry's func (and the wrapper name may not *be* an Internal). Own qml too: - // the aware form's datasetSelectionOptions would (rightly) demand selections in syntax mode, - // which a non-aware analysis neither has nor needs. - Analysis - { - title: "Test Multi-DataSet (non-aware counterpart)" - qml: "testMultiDataSetNonAware.qml" - func: "multiDataSetNonAware" - requiresData: true - preloadData: true - multiDataSetAware: false - } + // The old non-aware twin entry (func multiDataSetNonAware / qml testMultiDataSetNonAware) + // is gone (mds-plan C4/M8): it existed only to show that selection options stay inert + // without multiDataSetAware, but its generated wrapper pointed at the AWARE form and so its + // R entry point always errored (REPORT M8). The non-aware code path is covered by the other + // (plain, single-dataset) analyses in this module. Analysis { diff --git a/inst/qml/testMultiDataSetNonAware.qml b/inst/qml/testMultiDataSetNonAware.qml deleted file mode 100644 index 04875a9..0000000 --- a/inst/qml/testMultiDataSetNonAware.qml +++ /dev/null @@ -1,61 +0,0 @@ -import QtQuick -import JASP.Controls -import "./common" - -// Twin of testMultiDataSet.qml for the NON-aware entry (multiDataSetNonAware): same forms and -// checkboxes, but no dataSetSelectionOption - a non-aware analysis has exactly one (shown) -// dataset, in the GUI as well as in syntax mode. Without a selection option the forms show no -// the dropdown remains inert when the analysis is not multiDataSetAware. -Form -{ - columns: 1 - - ExplanationText - { - text: - "Non-aware twin: identical form, but the analysis entry lacks multiDataSetAware, so " + - "the Data selections stay inert and data comes in through the classic dataset/readDataSet path." - } - - Section { - title: qsTr("Form A - variables from one dataset") - - VariablesForm - { - - AvailableVariablesList { name: "allVariablesA" } - AssignedVariablesList - { - name: "dependentA" - title: qsTr("Variables A") - } - } - } - - Section { - title: qsTr("Form B - variables from another dataset") - - VariablesForm - { - - AvailableVariablesList { name: "allVariablesB" } - AssignedVariablesList - { - name: "dependentB" - title: qsTr("Variables B") - } - } - } - - CheckBox - { - name: "showMeta" - label: qsTr("Show options .meta (dataSetId/filterId provenance)") - } - - CheckBox - { - name: "guardDemo" - label: qsTr("Call readDataSet() anyway (should not error: non-aware run)") - } -} diff --git a/results_multidataset.json b/results_multidataset.json deleted file mode 100644 index a475534..0000000 --- a/results_multidataset.json +++ /dev/null @@ -1,193 +0,0 @@ -{ - "analysis": "multiDataSetFunc", - "analysisId": 0, - "dataSetId": -1, - "dataSetIds": [ - 1, - 2 - ], - "filterId": 2, - "jaspResultsRds": "/home/virtuoos/.local/share/JASP/JASP/temp//441104/resources/0/jaspResults.rds", - "module": "jaspTestModule", - "multiDataSetAware": true, - "results": { - ".meta": [ - { - "info": "", - "mustBe": [], - "mustContain": {}, - "name": "explanation", - "title": "hide me", - "type": "htmlNode" - }, - { - "info": "", - "mustBe": [], - "mustContain": {}, - "name": "dataSetOverview", - "title": "Datasets in this run", - "type": "table" - }, - { - "info": "", - "mustBe": [], - "mustContain": {}, - "name": "selectedVariables", - "title": "Selected variables", - "type": "table" - }, - { - "info": "", - "mustBe": [], - "mustContain": {}, - "name": "metaDump", - "title": "hide me", - "type": "htmlNode" - } - ], - "citation": [ - "JASP Team (2026). JASP (Version 1) [Computer software]." - ], - "dataSetOverview": { - "casesAcrossColumns": false, - "data": [ - { - "cols": 1, - "columnNames": "Score", - "id": "1", - "rows": 10, - "title": "DataSet 1" - }, - { - "cols": 2, - "columnNames": "Condition, Score", - "id": "2", - "rows": 10, - "title": "DataSet 2" - } - ], - "footnotes": [], - "name": "dataSetOverview", - "overTitle": false, - "schema": { - "fields": [ - { - "name": "id", - "title": "Dataset id", - "type": "string" - }, - { - "name": "title", - "title": "Title", - "type": "string" - }, - { - "name": "rows", - "title": "Rows", - "type": "integer" - }, - { - "name": "cols", - "title": "Columns", - "type": "integer" - }, - { - "name": "columnNames", - "title": "Column names (decoded on the way out)", - "type": "string" - } - ] - }, - "status": "complete", - "title": "Datasets in this run" - }, - "explanation": { - "class": "", - "elementType": "p", - "maxWidth": "15cm", - "name": "explanation", - "rawtext": "Aware run. This analysis received 2 slice(s) as the `datasets` argument; readDataSet() is disabled while it runs.

    Use two datasets in the workspace (File - New data set or add another CSV) and drag variables from both into the variables form.", - "text": "

    Aware run. This analysis received 2 slice(s) as the `datasets` argument; readDataSet() is disabled while it runs.

    Use two datasets in the workspace (File - New data set or add another CSV) and drag variables from both into the variables form.

    ", - "title": "hide me" - }, - "metaDump": { - "class": "", - "elementType": "p", - "maxWidth": "15cm", - "name": "metaDump", - "rawtext": "options$.meta
    {\n  \"dataSetA\": {\n    \"dataSetId\": 2,\n    \"filterId\": 2,\n    \"isFilterSelection\": true\n  },\n  \"dataSetB\": {\n    \"dataSetId\": 2,\n    \"filterId\": 2,\n    \"isFilterSelection\": true\n  },\n  \"dependentA\": {\n    \"dataSetId\": 1,\n    \"filterId\": 1,\n    \"shouldEncode\": true\n  },\n  \"dependentB\": {\n    \"dataSetId\": 2,\n    \"filterId\": 2,\n    \"shouldEncode\": true\n  },\n  \"guardDemo\": {\n    \"dataSetId\": 2,\n    \"filterId\": 2\n  },\n  \"plotHeight\": {\n    \"dataSetId\": 2,\n    \"filterId\": 2\n  },\n  \"plotWidth\": {\n    \"dataSetId\": 2,\n    \"filterId\": 2\n  },\n  \"showMeta\": {\n    \"dataSetId\": 2,\n    \"filterId\": 2\n  }\n}
    ", - "text": "

    options$.meta

    {
    \"dataSetA\": {
    \"dataSetId\": 2,
    \"filterId\": 2,
    \"isFilterSelection\": true
    },
    \"dataSetB\": {
    \"dataSetId\": 2,
    \"filterId\": 2,
    \"isFilterSelection\": true
    },
    \"dependentA\": {
    \"dataSetId\": 1,
    \"filterId\": 1,
    \"shouldEncode\": true
    },
    \"dependentB\": {
    \"dataSetId\": 2,
    \"filterId\": 2,
    \"shouldEncode\": true
    },
    \"guardDemo\": {
    \"dataSetId\": 2,
    \"filterId\": 2
    },
    \"plotHeight\": {
    \"dataSetId\": 2,
    \"filterId\": 2
    },
    \"plotWidth\": {
    \"dataSetId\": 2,
    \"filterId\": 2
    },
    \"showMeta\": {
    \"dataSetId\": 2,
    \"filterId\": 2
    }
    }

    ", - "title": "hide me" - }, - "name": "", - "selectedVariables": { - "casesAcrossColumns": false, - "data": [ - { - "dataSetId": "1", - "dataSetName": "DataSet 1", - "n": 10, - "source": "A", - "values": "mean 12.50, sd 3.88", - "variable": "Score" - }, - { - "dataSetId": "2", - "dataSetName": "DataSet 2", - "n": 10, - "source": "B", - "values": "mean 17.73, sd 5.31", - "variable": "Score" - }, - { - "dataSetId": "2", - "dataSetName": "DataSet 2", - "n": 10, - "source": "B", - "values": "X, Y, Z", - "variable": "Condition" - } - ], - "footnotes": [], - "name": "selectedVariables", - "overTitle": false, - "schema": { - "fields": [ - { - "name": "source", - "title": "Form", - "type": "string" - }, - { - "name": "variable", - "title": "Variable (decoded on the way out)", - "type": "string" - }, - { - "name": "dataSetId", - "title": "Dataset id", - "type": "string" - }, - { - "name": "dataSetName", - "title": "Dataset title", - "type": "string" - }, - { - "name": "n", - "title": "n", - "type": "integer" - }, - { - "name": "values", - "title": "mean (scale) / first levels (factor)", - "type": "string" - } - ] - }, - "status": "complete", - "title": "Selected variables" - } - }, - "status": "complete" -} \ No newline at end of file From 96779b6a13b2c2a9f75d37cdba382abcc70d6c32 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Thu, 15 Oct 2026 17:14:11 +0200 Subject: [PATCH 29/44] mds-plan D2.2: testMultiDataSet.qml to list-level selection (C10) The two dataSetSelectionOptions move off the VariablesForms onto the AvailableVariablesLists (HostFilterSelection lives on lists now, mds-plan D2.2 in jasp-desktop): each section's assigned list inherits its available list's namespace, depends: semantics kept, option names (dataSetA/dataSetB) unchanged so the wrapper/R echo fns and the RPC verifier need nothing. D6.4 will extend this form further. --- inst/qml/testMultiDataSet.qml | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/inst/qml/testMultiDataSet.qml b/inst/qml/testMultiDataSet.qml index 892dce0..2c5aa0a 100644 --- a/inst/qml/testMultiDataSet.qml +++ b/inst/qml/testMultiDataSet.qml @@ -9,13 +9,14 @@ Form ExplanationText { text: - "Test the multi-dataset awareness of VariablesForm: each form has its own " + + "Test the multi-dataset awareness of the variable lists: each selecting list owns its " + "dataset/filter selection (only live for multiDataSetAware analyses) and the results " + "show per-variable routing to the dataset it came from.
    " + "
      " + "
    • Open a second dataset (e.g. add another CSV) so the selections have something to choose from.
    • " + - "
    • Set form A's Data selection to one dataset and form B's to the other: the run should then " + - "load both datasets (see the 'Datasets in this run' table).
    • " + + "
    • Set section A's Data selection (on its available list) to one dataset and section B's " + + "to the other: the run should then load both datasets (see the 'Datasets in this " + + "run' table), and the assigned lists inherit their available list's namespace.
    • " + "
    • Drag variables (try the identically named 'Score'!) into both lists: the routing table " + "must attribute each one to its own dataset, filter and column.
    • " + "
    • show .meta: the provenance stamps (dataSetId/filterId) on the bound options.
    • " + @@ -31,11 +32,14 @@ Form VariablesForm { - //syntax mode: an option carrying the dataset name to select for this form - //(jaspSyntax gates the bound dropdown on it; desktop never sees it) - dataSetSelectionOption: "dataSetA" - - AvailableVariablesList { name: "allVariablesA" } + AvailableVariablesList + { + name: "allVariablesA" + //List-level dataset selection (mds-plan C10): an option carrying the dataset name + //to select for this list (jaspSyntax gates the bound dropdown on it; desktop never + //sees it). The assigned lists below inherit this list's namespace. + dataSetSelectionOption: "dataSetA" + } AssignedVariablesList { name: "dependentA" @@ -50,9 +54,11 @@ Form VariablesForm { - dataSetSelectionOption: "dataSetB" - - AvailableVariablesList { name: "allVariablesB" } + AvailableVariablesList + { + name: "allVariablesB" + dataSetSelectionOption: "dataSetB" + } AssignedVariablesList { name: "dependentB" From e5a0fe99a24d2df84d65ec4abb398254a1b4b356 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Fri, 16 Oct 2026 07:55:29 +0200 Subject: [PATCH 30/44] mds-plan D3.3: metaDump audits the post-flip provenance contract; verifier checks C8/C11 per run The Engine->R boundary now strips the transient .filterIds sidecar (C11) and the send-time .meta carries no dataSetId/filterId (C8), so the per-form stamp-window checks (meta.dependentA == 1/1 etc.) can no longer hold - R legitimately sees no stamps. metaDump echoes a PROVENANCE_AUDIT marker (filterIdsSeen + count of provenance keys in the received .meta); the verifier asserts filterIdsSeen=false and metaProvenanceKeys=0 on all four runs (incl. both mid-session host switches), keeping the value-level routing/restamp/phantom-slice proofs (12.50 vs 17.73) that actually demonstrate per-form provenance still steering the data. --- R/multiDataSet.R | 12 ++++-- Tools/verifyMultiDataSetRpc.py | 75 +++++++++++++++------------------- 2 files changed, 42 insertions(+), 45 deletions(-) diff --git a/R/multiDataSet.R b/R/multiDataSet.R index 6fa9cc5..2450283 100644 --- a/R/multiDataSet.R +++ b/R/multiDataSet.R @@ -115,10 +115,16 @@ selectedVariablesTable <- function(jaspResults, options, datasets, dataset) { metaDump <- function(jaspResults, options) { meta <- options[[".meta"]] + metaText <- jsonlite::toJSON(if (is.null(meta)) list() else meta, auto_unbox = TRUE, pretty = 2) + # mds-plan D3.3: R must never see provenance - the transient `.filterIds` sidecar is stripped + # at the engine boundary (C11) and the send-time `.meta` carries no dataSetId/filterId (C8). + # The audit markers echo exactly what arrived so the RPC verifier can assert the contract. + provenanceKeys <- sum(lengths(regmatches(metaText, gregexpr("dataSetId|filterId", metaText)))) + filterIdsSeen <- !is.null(options[[".filterIds"]]) jaspResults[["metaDump"]] <- createJaspHtml(paste0( - "options$.meta
      ",
      -    jsonlite::toJSON(if (is.null(meta)) list() else meta, auto_unbox = TRUE, pretty = 2),
      -    "
      ")) + "PROVENANCE_AUDIT filterIdsSeen=", if (filterIdsSeen) "true" else "false", + " metaProvenanceKeys=", provenanceKeys, "
      ", + "options$.meta
      ", metaText, "
      ")) } readDataSetGuardDemo <- function(jaspResults) { diff --git a/Tools/verifyMultiDataSetRpc.py b/Tools/verifyMultiDataSetRpc.py index 0d6e11a..5376997 100644 --- a/Tools/verifyMultiDataSetRpc.py +++ b/Tools/verifyMultiDataSetRpc.py @@ -11,7 +11,10 @@ - 'Datasets in this run' has 2 rows with titles + row/col counts - 'Selected variables' rows have n=10 and the right means (Score/Score land on DIFFERENT datasets: 12.50 vs 17.73 - no 'variable compared with itself') - - the .meta dump shows per-form provenance (dependentA ds1/f1, dependentB ds2/f2) + - the metaDump echo audits the post-D3.3 provenance contract as R RECEIVED it: + options$.meta carries no dataSetId/filterId (C8) and no .filterIds sidecar survives + the engine-boundary strip (C11); per-form routing is proven at the VALUE level + (each form's variable resolves against its own selection's slice: 12.50 vs 17.73) - no raw JASPColumn_* anywhere (decode on send) And the C3/VERIFY-live block (mds-plan C3/M5): switch one host's selection mid-session and assert the next run only shows the LIVE selections - no phantom slice for the abandoned filter, @@ -213,34 +216,35 @@ def row(src, var): return next((x for x in rows if x["source"] == src and x["var check("analysis references both datasets (dataSetIds in run response)", sorted(r2.get("dataSetIds", [])) == [1, 2], str(r2.get("dataSetIds"))) -# T3c: per-form provenance as the engine SAW it - options$.meta must stamp each control with -# ITS OWN form's dataset/filter (showMeta is on), not one global analysis filter. +# T3c (mds-plan D3.3 flip): provenance as R sees it - NONE. The generated Engine json keeps +# .meta provenance-free (C8) and the transient .filterIds sidecar is stripped at the engine +# boundary (C11); the metaDump html (options$showMeta was on) audits exactly that. Per-form +# provenance still routes the DATA (two same-named columns, two truths) - pinned value-level +# above: A:Score 12.50 vs B:Score 17.73. meta_text = json.dumps(results).replace(chr(92)+chr(34), '"').replace(""", '"').replace(""", '"') -def meta_of(opt): - i = meta_text.find(f'"{opt}"') - while i != -1: - window = meta_text[i:i + 400] - if "dataSetId" in window and "filterId" in window: - m_ds = _re.search(r'"dataSetId":\s*"?(-?\d+)', window) - m_fl = _re.search(r'"filterId":\s*"?(-?\d+)', window) - if m_ds and m_fl: - return m_ds.group(1), m_fl.group(1) - i = meta_text.find(f'"{opt}"', i + 1) - return None -check("meta.dependentA stamped with form A's dataset/filter (1/1)", meta_of("dependentA") == ("1", "1"), str(meta_of("dependentA"))) -check("meta.dependentB stamped with form B's dataset/filter (2/2)", meta_of("dependentB") == ("2", "2"), str(meta_of("dependentB"))) +AUDIT_RE = r'filterIdsSeen=(\w+) metaProvenanceKeys=(\d+)' +def provenance_audit(res): + """(filterIdsSeen, metaProvenanceKeys) from the metaDump echo of a results payload.""" + text = json.dumps(res).replace(chr(92) + chr(34), '"').replace(""", '"').replace(""", '"') + m = _re.search(AUDIT_RE, text) + return (m.group(1), m.group(2)) if m else (None, None) +audit2 = provenance_audit(results) +check("metaDump ran (provenance audit present)", audit2[0] is not None, str(audit2)) +check("R never sees .filterIds (C11 strip at the engine boundary)", audit2 == ("false", audit2[1]), str(audit2)) +check("options$.meta as R received it carries no provenance keys (C8)", audit2[1] == "0", str(audit2)) # ---------------------------------------------------------------------------- # C3/VERIFY-live (mds-plan C3/M5): the phantom-slice class. Switch ONE HOST'S SELECTION # mid-session and check the NEXT run's data shows only the LIVE selections: no slice queued -# for an abandoned filter, no stale filterId stamp left in the options .meta. Scalars and the -# selection options themselves carry no provenance at all now - only column-bearing values do -# (BoundControlBase::createMeta gate), and a switching host restamps those (HostFilterSelection -# ::restampValuesUnderHost). This RPC workspace has one (default) filter per dataset, so the +# for an abandoned filter, and the run's provenance still C8/C11-clean (the per-run audit). +# Since the D3.3 flip the restamp moves the TYPED term filterIds (HostFilterSelection +# ::restampValuesUnderHost -> ListModel::restampTermsFilterId -> generated `.filterIds`), the +# .meta half is dead. This RPC workspace has one (default) filter per dataset, so the # mid-session switch here crosses datasets (form A: f1 -> f2 -> f1); the sharp same-dataset/ -# other-filter case - where no encoded value changes and only the restamp walk can keep .meta -# honest - is pinned C++/QML-side by -# Tests/qmlTests/tst_dataSetSelectionVariablesForm.qml::test_midsession_switch_restamps_column_values. +# other-filter case - where no encoded value changes and only the restamp walk can keep the +# sidecar honest - is pinned C++/QML-side by +# Tests/qmlTests/tst_dataSetSelectionVariablesList.qml::test_midsession_switch_restamps_column_values +# (and tst_generatedOptionsJsonAware.qml::test_aware_midswitch_sidecar_follows_without_stale_ids). # ---------------------------------------------------------------------------- def run_and_results(opts, tag): @@ -256,20 +260,6 @@ def overview_ids(res): fields, fm, rks = field_maps(t) return sorted(str(fm["id"].get(r)) for r in rks), t -def meta_window(res, opt): - """(dataSetId, filterId) of opt's .meta entry as the engine saw it (metaDump), or None.""" - text = json.dumps(res).replace(chr(92) + chr(34), '"').replace(""", '"').replace(""", '"') - i = text.find(f'"{opt}"') - while i != -1: - window = text[i:i + 400] - if "dataSetId" in window and "filterId" in window: - m_ds = _re.search(r'"dataSetId":\s*"?(-?\d+)', window) - m_fl = _re.search(r'"filterId":\s*"?(-?\d+)', window) - if m_ds and m_fl: - return m_ds.group(1), m_fl.group(1) - i = text.find(f'"{opt}"', i + 1) - return None - # switch form A to dataset 2's filter (both forms then sit on DataSet 2): r3, res3 = run_and_results({ "dataSetA": "2", @@ -281,10 +271,10 @@ def meta_window(res, opt): ids3, _ = overview_ids(res3) check("run 3: no phantom slice - only DataSet 2 loaded", ids3 == ["2"], str(ids3)) check("run 3: dataSetIds not over-reported", sorted(r3.get("dataSetIds", [])) == [2], str(r3.get("dataSetIds"))) -check("run 3: dependentA restamped onto the live selection (2/2)", meta_window(res3, "dependentA") == ("2", "2"), str(meta_window(res3, "dependentA"))) -check("run 3: scalar showMeta carries no provenance", meta_window(res3, "showMeta") is None, str(meta_window(res3, "showMeta"))) -check("run 3: scalar guardDemo carries no provenance", meta_window(res3, "guardDemo") is None, str(meta_window(res3, "guardDemo"))) -check("run 3: selection option dataSetA carries no provenance", meta_window(res3, "dataSetA") is None, str(meta_window(res3, "dataSetA"))) +audit3 = provenance_audit(res3) +check("run 3: still no provenance reaches R after the switch (C8/C11)", audit3 == ("false", "0"), str(audit3)) +# (old stamp-window checks retired with the meta stamps, mds-plan D3.3: the C8 audit above +# covers scalars and selection options too - NO dataSetId/filterId key may appear ANYWHERE) sv3 = next((t for t in tables(res3.get("results", res3)).values() if t.get("title") == "Selected variables"), None) rows3 = [] @@ -306,7 +296,8 @@ def meta_window(res, opt): ids4, _ = overview_ids(res4) check("run 4: exactly one slice per dataset again", ids4 == ["1", "2"], str(ids4)) check("run 4: dataSetIds reports both datasets", sorted(r4.get("dataSetIds", [])) == [1, 2], str(r4.get("dataSetIds"))) -check("run 4: dependentA restamped back onto filter 1 (1/1)", meta_window(res4, "dependentA") == ("1", "1"), str(meta_window(res4, "dependentA"))) +audit4 = provenance_audit(res4) +check("run 4: provenance contract held through the switch-back too (C8/C11)", audit4 == ("false", "0"), str(audit4)) a4 = None sv4 = next((t for t in tables(res4.get("results", res4)).values() if t.get("title") == "Selected variables"), None) if sv4: From dcefb965c8f63e65779e8b94fc21e94212021388 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Sat, 17 Oct 2026 20:15:42 +0200 Subject: [PATCH 31/44] mds-plan D6.1/D6.2: the component zoo - testMultiDataSetControls exercises every control family under multi-source provenance New aware analysis (qml testMultiDataSetControls.qml + R multiDataSetControls.R + wrapper): nine families each reporting exactly what they received and where it resolved - assigned list under a selecting available list (A) with its combo sibling (plain string, never provenanced), an assigned list that IS its own selection host (B), a row-bearing ComponentsList with same-name twin rows from two filters keeping their OWN row children (C, the D6.1 TermRowKey re-keying), interaction terms whose components share the term filterId (D), FactorLevelList via encodeThis/extras (E), an R-code textarea with its own host encoding through it (F), a typed bindTo copy across differently-selected hosts (G, keeps the SOURCE filterId and encodes through the source's view), and the M5 scalar contract. R side is a per-family report table + PROVENANCE_AUDIT (mean/level-list per resolved column, no statistics). Description.qml gains the analysis entry (multiDataSetAware, hasWrapper). Companion jasp-desktop commits carry the C++/QML rough edges the zoo flushed out (TermRowKey row-identity keying, containsVariables-by-declaration, textarea encodeValue, RSyntax null-model guard, typed encoder re-mint, self-rebind format); the RPC verifier zoo runs land with D6.3/D6.4. --- R/multiDataSetControls.R | 215 +++++++++++++++++++++++++ R/multiDataSetControlsFuncWrapper.R | 63 ++++++++ inst/Description.qml | 15 ++ inst/qml/testMultiDataSetControls.qml | 223 ++++++++++++++++++++++++++ 4 files changed, 516 insertions(+) create mode 100644 R/multiDataSetControls.R create mode 100644 R/multiDataSetControlsFuncWrapper.R create mode 100644 inst/qml/testMultiDataSetControls.qml diff --git a/R/multiDataSetControls.R b/R/multiDataSetControls.R new file mode 100644 index 0000000..3d60288 --- /dev/null +++ b/R/multiDataSetControls.R @@ -0,0 +1,215 @@ +# mds-plan D6.1 - the component-zoo echo. Receives the generated options + the per-slice +# `datasets` (contract: keyed by SLICE/filter id, attrs dataSetIds/dataSetNames map a key back +# to its dataset - see multiDataSet.R for the full contract notes) and reports, per control +# family, EVERY value exactly as received: the encoded name (results are decoded on the way out, +# which is itself part of what we verify), which slice index it resolved in, and the row +# children. Tools/verifyMultiDataSetRpc.py asserts all of it. Cheap by design: no statistics +# beyond a mean/level-list per resolved column. +# +# Families (see inst/qml/testMultiDataSetControls.qml): +# assigned-list Zoo A - plain assigned list under a selecting available list +# combobox-host Zoo A - DropDown served by the same host: value stays a PLAIN STRING +# assigned-host Zoo B - assigned list that IS its own selection host +# components-rows Zoo C - row-bearing variables list (the TableFields shape; shared +# RowControls/TermRowKey machinery): per-row term + keep + shift; +# same-name twin rows from two filters keep their OWN +# children (the D6.1 TermRowKey re-keying) +# interaction-list Zoo D - combination terms: all components share the term filterId +# factor-levels Zoo E - encodeThis -> extras roundtrip (names never slice columns; +# they must come back decoded, no raw JaspExtraOptions_ leakage) +# textarea-host Zoo F - R-code textarea: used columns encoded through its own host +# bindto-cross-host Zoo G - typed copy bound across differently-selected hosts: keeps the +# SOURCE filterId, encodes through the SOURCE's view (D6.2) +# scalar M5 contract - plain values, no provenance anywhere + +multiDataSetControlsFuncInternal <- function(jaspResults, dataset, options, datasets = NULL) { + aware <- !is.null(datasets) + + jaspResults[["explanation"]] <- createJaspHtml(paste0( + if (aware) + paste0("Aware run. Component zoo received ", length(datasets), + " slice(s) as `datasets`; every family below echoes what it received.") + else + "Non-aware run. No `datasets` argument: this analysis must run multiDataSetAware for the routing tables to mean anything.", + "
      See the check list at the top of Tools/verifyMultiDataSetRpc.py's zoo section for what each family proves.")) + + dataSetOverviewTable(jaspResults, datasets, dataset) + controlsProvenanceTable(jaspResults, options, datasets) + + if (isTRUE(options$showMeta)) + metaDump(jaspResults, options) +} + +# ------------------------------------------------------------------ resolution helpers + +.resolveSlice <- function(token, datasets) { + # an aware option value IS the encoded column name of exactly one slice (C1/C3): index it + if (is.null(datasets) || !is.character(token) || length(token) != 1 || !nzchar(token)) + return(NULL) + hits <- names(datasets)[vapply(names(datasets), + function(k) token %in% names(datasets[[k]]), logical(1))] + if (length(hits) == 0) NULL else hits[[1]] +} + +.sliceMeta <- function(key, datasets) { + if (is.null(key)) return(list(id = "-", name = "-")) + dsId <- attr(datasets, "dataSetIds")[[key]] + dsName <- attr(datasets, "dataSetNames")[[key]] + list(id = if (is.null(dsId)) as.character(key) else as.character(dsId), + name = if (is.null(dsName)) as.character(key) else as.character(dsName)) +} + +.describeValue <- function(key, token, datasets) { + if (is.null(key)) return(list(n = NA_integer_, values = "")) + .fillDescription(list(n = NA_integer_, values = ""), datasets[[key]][[token]]) +} + +# ------------------------------------------------------------------ the echo table + +controlsProvenanceTable <- function(jaspResults, options, datasets) { + table <- createJaspTable("Controls provenance") + jaspResults[["controlsProvenance"]] <- table + + table$addColumnInfo("family", title = "Family") + table$addColumnInfo("option", title = "Option") + table$addColumnInfo("received", title = "Received value (decoded on the way out)") + table$addColumnInfo("rowChild", title = "Row children") + table$addColumnInfo("slice", title = "Resolved slice (filter id)") + table$addColumnInfo("dataSetId", title = "Dataset id") + table$addColumnInfo("n", title = "n") + table$addColumnInfo("values", title = "mean (scale) / levels (factor)") + + rows <- data.frame(family = character(), option = character(), received = character(), + rowChild = character(), slice = character(), dataSetId = character(), + n = integer(), values = character(), stringsAsFactors = FALSE) + + addRow <- function(df, family, option, received, rowChild = "", key = NULL, + n = NA_integer_, values = "") { + meta <- .sliceMeta(key, datasets) + rbind(df, data.frame(family = family, option = option, received = as.character(received), + rowChild = as.character(rowChild), + slice = if (is.null(key)) "-" else as.character(key), + dataSetId = meta$id, n = n, values = as.character(values), + stringsAsFactors = FALSE, check.names = FALSE)) + } + + # --- plain term lists: Zoo A / Zoo B / Zoo G ---------------------------------------------------- + for (entry in list( + list(fam = "assigned-list", opt = "assignedA", vals = .dependentValues(options$assignedA)), + list(fam = "assigned-host", opt = "assignedB", vals = .dependentValues(options$assignedB)), + list(fam = "bindto-cross-host", opt = "boundG", vals = .dependentValues(options$boundG)))) { + for (value in entry$vals) { + key <- .resolveSlice(value, datasets) + desc <- .describeValue(key, value, datasets) + rows <- addRow(rows, entry$fam, entry$opt, value, key = key, n = desc$n, values = desc$values) + } + } + + # --- combobox served by Zoo A's host: PLAIN string value (never term-objectified) --------------- + combo <- trimws(as.character(utils::tail(unlist(options$comboA), 1L))) + if (!is.na(combo) && nzchar(combo) && !identical(combo, "0")) { + key <- .resolveSlice(combo, datasets) + desc <- .describeValue(key, combo, datasets) + rows <- addRow(rows, "combobox-host", "comboA", combo, key = key, n = desc$n, values = desc$values) + } + + # --- ComponentsList rows (Zoo C): per-row term + its OWN row children ---------------------------- + rowsC <- options$rowsC + if (!is.null(rowsC) && is.list(rowsC) && !is.null(rowsC$value) && is.list(rowsC$value)) + rowsC <- rowsC$value # tolerate a {value, ...} wrapper + if (!is.null(rowsC) && is.list(rowsC)) { + for (row in rowsC) { + if (!is.list(row)) next + tokens <- as.character(unlist(row$variable)) + token <- if (length(tokens) > 0) tokens[[1]] else "" + if (!nzchar(token)) next + key <- .resolveSlice(token, datasets) + desc <- .describeValue(key, token, datasets) + child <- paste0("keep=", if (is.null(row$keep)) "NA" else as.logical(row$keep), + ", shift=", if (is.null(row$shift)) "NA" else as.character(row$shift)) + rows <- addRow(rows, "components-rows", "rowsC", token, rowChild = child, + key = key, n = desc$n, values = desc$values) + } + } + + # --- interaction terms (Zoo D): components of one row share the term filterId -------------------- + # jaspBase hands {value,types} options UNWRAPPED to the analysis (the value list directly, + # types as the sibling optionX.types), so accept both shapes. + inter <- options$interactionD + if (!is.null(inter) && is.list(inter)) { + interRows <- if (!is.null(inter$value)) inter$value else inter + for (row in interRows) { + comps <- as.character(unlist(row$components)) + if (length(comps) == 0) next + keys <- unique(vapply(comps, function(c) { + k <- .resolveSlice(c, datasets); if (is.null(k)) "-" else k + }, character(1))) + joint <- if (length(keys) == 1 && keys[[1]] != "-") keys[[1]] else NULL + rows <- addRow(rows, "interaction-list", "interactionD", + paste(comps, collapse = " : "), rowChild = paste0("order=", length(comps)), + key = joint, + n = if (is.null(joint)) NA_integer_ else + sum(!is.na(datasets[[joint]][[comps[[1]]]])), + values = if (is.null(joint)) paste("slices:", paste(keys, collapse = "/")) else + paste0("components share slice ", joint)) + } + } + + # --- factor levels (Zoo E): extras-encoded names, NOT slice columns ------------------------------ + factors <- options$factorsE + if (!is.null(factors) && is.list(factors)) { + for (f in factors) { + if (!is.list(f) || is.null(f$name)) next + levels <- as.character(unlist(f$levels)) + rows <- addRow(rows, "factor-levels", "factorsE", f$name, + rowChild = paste(levels, collapse = ", "), + values = paste0(length(levels), " level(s), extras roundtrip")) + } + } + + # --- textarea (Zoo F): tokens encoded through the textarea's own host ---------------------------- + code <- options$codeF + if (!is.null(code) && is.list(code)) { + used <- unique(as.character(unlist(code$columns))) + # the received model text itself (encoded at bind time through the host view; decoded again + # on the way out - the verifier asserts it round-trips to the original plain text) + if (is.character(code$model) && nzchar(code$model)) + rows <- addRow(rows, "textarea-host", "codeF(text)", code$model, + rowChild = "model as received (decoded on the way out)") + for (token in used) { + if (!nzchar(token)) next + key <- .resolveSlice(token, datasets) + desc <- .describeValue(key, token, datasets) + rows <- addRow(rows, "textarea-host", "codeF", token, rowChild = "detected column", + key = key, n = desc$n, values = desc$values) + } + model <- code$model + if (is.character(model) && nzchar(model)) { + # every column of every slice mentioned in the (encoded) model text resolves to that slice; + # a token encoded through the WRONG namespace would not be found in the selected slice at all + mentioned <- character() + for (key in names(datasets)) + for (column in names(datasets[[key]])) + if (grepl(column, model, fixed = TRUE)) mentioned <- c(mentioned, paste0(key, "|", column)) + if (length(mentioned) > 0) + for (m in mentioned) { + parts <- strsplit(m, "|", fixed = TRUE)[[1]] + key <- parts[[1]]; column <- parts[[2]] + desc <- .describeValue(key, column, datasets) + rows <- addRow(rows, "textarea-host", "codeF(model)", column, + rowChild = "found in model text", key = key, n = desc$n, values = desc$values) + } + else + rows <- addRow(rows, "textarea-host", "codeF(model)", "-", + rowChild = "no slice column mentioned in model text") + } + } + + # --- scalars (M5): plain values, no provenance anywhere ------------------------------------------ + rows <- addRow(rows, "scalar", "plainSwitch", as.character(isTRUE(options$plainSwitch)), + rowChild = "no provenance (C11 scalars unstamped)") + rows <- addRow(rows, "combobox-plain", "comboG", as.character(options$comboG), + rowChild = "plain string (combos never term-objectified)") + + table$setData(rows) +} diff --git a/R/multiDataSetControlsFuncWrapper.R b/R/multiDataSetControlsFuncWrapper.R new file mode 100644 index 0000000..0db14da --- /dev/null +++ b/R/multiDataSetControlsFuncWrapper.R @@ -0,0 +1,63 @@ +# +# Copyright (C) 2013-2025 University of Amsterdam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This is a generated file. Don't change it! (hand-mirrors multiDataSetFuncWrapper.R, the +# mds-plan D6.1 component-zoo analysis) + +#' Test Multi-DataSet Controls +#' +multiDataSetControlsFunc <- function( + datasets = NULL, + version = "1", + assignedA = list(types = list(), value = list()), + assignedB = list(types = list(), value = list()), + boundG = list(types = list(), value = list()), + codeF = list(model = "", modelOriginal = "", columns = list()), + comboA = "", + comboG = "first", + dataSetA = "", + dataSetB = "", + dataSetC = "", + dataSetD = "", + dataSetF = "", + dataSetG = "", + factorsE = list(), + interactionD = list(types = list(), value = list()), + plainSwitch = FALSE, + rowsC = list(), + showMeta = FALSE, + plotHeight = 320, + plotWidth = 480) { + + defaultArgCalls <- formals(jaspTestModule::multiDataSetControlsFunc) + defaultArgs <- lapply(defaultArgCalls, eval) + options <- as.list(match.call())[-1L] + options <- lapply(options, eval) + defaults <- setdiff(names(defaultArgs), names(options)) + options[defaults] <- defaultArgs[defaults] + options[["datasets"]] <- NULL + options[["version"]] <- NULL + + + data <- if (!is.null(datasets) && length(datasets)) datasets[[1]] else NULL + + optionsWithFormula <- character(0) + for (name in optionsWithFormula) { + if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } + + return(jaspBase::runWrappedAnalysis("jaspTestModule", "multiDataSetControlsFunc", "testMultiDataSetControls.qml", options, version, TRUE, datasets = datasets)) +} diff --git a/inst/Description.qml b/inst/Description.qml index 22ee19f..b411f52 100644 --- a/inst/Description.qml +++ b/inst/Description.qml @@ -95,6 +95,21 @@ Description hasWrapper: true } + // mds-plan D6.1/D6.2: the component zoo under multi-source provenance. Every control family + // (assigned list, list-as-host, row-control ComponentsList with same-name twins across filters, + // interaction terms, factor levels via encodeThis, textarea with own host, cross-host typed + // bindTo copy) reports what it received and where it resolved; the RPC verifier asserts it. + Analysis + { + title: "Test Multi-DataSet Controls" + qml: "testMultiDataSetControls.qml" + func: "multiDataSetControlsFunc" + requiresData: true + preloadData: true + multiDataSetAware: true + hasWrapper: true + } + // The old non-aware twin entry (func multiDataSetNonAware / qml testMultiDataSetNonAware) // is gone (mds-plan C4/M8): it existed only to show that selection options stay inert // without multiDataSetAware, but its generated wrapper pointed at the AWARE form and so its diff --git a/inst/qml/testMultiDataSetControls.qml b/inst/qml/testMultiDataSetControls.qml new file mode 100644 index 0000000..b1dcaff --- /dev/null +++ b/inst/qml/testMultiDataSetControls.qml @@ -0,0 +1,223 @@ +// mds-plan D6.1/D6.2 - the component zoo under multi-source provenance. +// +// Every control family a real analysis form uses, each exercising per-item provenance: +// Zoo A plain lists: an AvailableVariablesList carrying its own dataset/filter selection +// (the D2.2 list-level pattern) feeding an AssignedVariablesList, plus a DropDown +// served by the same host (comboboxes stay PLAIN strings - never term objects - +// while their value still resolves through the host namespace). +// Zoo B ASSIGNED list as its own host: an AssignedVariablesList with its own +// dataSetSelectionOption (C10 says any VariablesListBase can host; this is the +// second-source case the design must support). +// Zoo C TableFields-style ROW-CONTROL list: JASP.TableFields does not exist in this +// codebase and ComponentsList (its closest named relative) cannot carry a selection, +// so this is the row family as TableFields actually is it: an AssignedVariablesList +// with a rowComponent (a keep checkbox and a shift integer) plus its own selection. +// Same-name twin rows behind DIFFERENT filters (the C9 twins, set by the RPC verifier +// through the Storage term-object shape) must keep their own children - the D6.1 +// RelatedValuesPerTerm/RowControls re-keying (TermRowKey = value + filterId) is what +// makes that work; pre-fix the second row silently clobbered the first. +// Zoo D interaction terms: a VariablesList in Interaction mode - components of one +// combination term share the term-level filterId (mds-plan C9 uniform-carry rule). +// Zoo E FactorLevelList: encodeThis -> extras-encoder roundtrip (factor/level names encode +// through the JaspExtraOptions_ namespace and must come back decoded and usable). +// Zoo F TextArea with its OWN host: R-code text whose used columns encode through the +// selected filter's view (host-bound resolution, C13). +// Zoo G CROSS-HOST bindTo (D6.2): a list with its own selection whose option is bound +// (bindTo = the JSON->control applier) to a typed copy generated from Zoo A's list - +// the copy's terms carry the SOURCE filterId. The destination rule stamps only +// UNQUALIFIED terms (D5.1 intake guard) and the restamp walk never touches foreign +// stamps (D3.5 owns-the-stamp), so the value must encode and resolve through the +// SOURCE's slice even though this list's own dropdown selects another one. +// +// The R echo (R/multiDataSetControls.R) reports for every received value: the (encoded) name +// as received, WHICH slice index it resolved in, and the row children - so provenance is +// assertable from the results through Tools/verifyMultiDataSetRpc.py. +import QtQuick +import JASP +import JASP.Controls +import "./common" + +Form +{ + columns: 1 + + ExplanationText + { + text: + "Component-zoo provenance test (multiDataSetAware). Open two datasets, then check the " + + "'Controls provenance' table: every control family lists the values it received, the " + + "slice each value resolved in and its row children.
      " + + "
        " + + "
      • Zoo A: selection on the available list drives the assigned list and the combobox.
      • " + + "
      • Zoo B: an ASSIGNED list hosts a selection of its own.
      • " + + "
      • Zoo C: row-control list (row-bearing variables list, the TableFields shape): same-name twin rows " + + "from two filters keep their OWN row values.
      • " + + "
      • Zoo D: interaction terms share one filterId over all components.
      • " + + "
      • Zoo E: factor/level names roundtrip through the extras encoder.
      • " + + "
      • Zoo F: the R-code textarea encodes its tokens through its own host.
      • " + + "
      • Zoo G: a typed copy bound across differently-selected hosts keeps the SOURCE filterId.
      • " + + "
      " + + "'Show options .meta' adds the PROVENANCE_AUDIT (R must never see provenance: C8/C11)." + } + + // ---- Zoo A: list-level selection (D2.2 pattern) + combobox ---------------------------------- + Section { + title: qsTr("Zoo A - plain lists, own selection") + + VariablesForm + { + AvailableVariablesList + { + name: "zooVarsA" + dataSetSelectionOption: "dataSetA" + } + AssignedVariablesList + { + name: "assignedA" + title: qsTr("Assigned A") + depends: "dataSetA" + } + // a combobox served by the same host: values stay PLAIN strings (never term objects) + // while the selection resolves through the host namespace (landmine: no + // term-objectification of combos). + DropDown + { + name: "comboA" + label: qsTr("Column combo") + source: zooVarsA + addEmptyValue: true + } + } + } + + // ---- Zoo B: the ASSIGNED list is its own host ------------------------------------------------ + Section { + title: qsTr("Zoo B - assigned list as second source (own selection)") + + AssignedVariablesList + { + name: "assignedB" + title: qsTr("Assigned B (own Data selection)") + dataSetSelectionOption: "dataSetB" + } + } + + // ---- Zoo C: TableFields-style row controls -------------------------------------------------- + // JASP.TableFields does not exist in this codebase; ComponentsList is its closest NAMED + // relative but is not a VariablesListBase (no dataSetSelectionOption possible, and without a + // source relation its values never encode). The row machinery RowControls/TermRowKey is shared + // with the row-bearing variables list, which is also what TableFields actually is (term rows + + // per-row controls) - so the zoo exercises the row family through an AssignedVariablesList + // with a rowComponent and its own selection host. + Section { + title: qsTr("Zoo C - row controls: per-row term + per-row values (own selection)") + + AssignedVariablesList + { + name: "rowsC" + title: qsTr("Rows (term + keep + shift)") + dataSetSelectionOption: "dataSetC" + optionKey: "variable" + + rowComponent: Row + { + CheckBox + { + name: "keep" + } + IntegerField + { + name: "shift" + min: -999 + max: 999 + } + } + } + } + + // ---- Zoo D: interaction terms ---------------------------------------------------------------- + Section { + title: qsTr("Zoo D - interaction terms (components share one filterId)") + + VariablesForm + { + AvailableVariablesList + { + name: "zooVarsD" + dataSetSelectionOption: "dataSetD" + } + VariablesList + { + name: "interactionD" + title: qsTr("Model terms") + listViewType: JASP.Interaction + depends: "dataSetD" + } + // (no CrossButton/AssignButton chrome needed for the test: the verifier drives + // interactionD through its option; the buttons are pure UI in this codebase) + } + } + + // ---- Zoo E: factor/level names (encodeThis -> extras) --------------------------------------- + Section { + title: qsTr("Zoo E - factor levels (extras roundtrip)") + + FactorLevelList + { + name: "factorsE" + minFactors: 0 + minLevels: 0 + factorName: "Fac" + levelName: "Lev" + } + } + + // ---- Zoo F: textarea with its own host ------------------------------------------------------- + Section { + title: qsTr("Zoo F - R-code textarea, own host") + + TextArea + { + name: "codeF" + dataSetSelectionOption: "dataSetF" + depends: "dataSetF" // the host binds first: the bind-time encodeRScript resolves THIS filter, not the analysis fallback + textType: JASP.TextTypeRcode + } + } + + // ---- Zoo G: cross-host bindTo (D6.2) --------------------------------------------------------- + Section { + title: qsTr("Zoo G - typed copy bound from Zoo A keeps the SOURCE filter") + + AssignedVariablesList + { + name: "boundG" + title: qsTr("Bound from assignedA (typed copy)") + dataSetSelectionOption: "dataSetG" + } + } + + // ---- scalars (M5 contract: never provenanced, never term-objectified) ------------------------ + Section { + title: qsTr("Scalars") + + columns: 2 + + CheckBox + { + name: "showMeta" + label: qsTr("Show options .meta (PROVENANCE_AUDIT)") + } + CheckBox + { + name: "plainSwitch" + label: qsTr("Plain switch") + } + DropDown + { + name: "comboG" + label: qsTr("Plain combo") + values: ["first", "second", "third"] + } + } +} From 6e8877368829428ef53da84dca1dc4930e2a3421 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Sat, 17 Oct 2026 20:16:12 +0200 Subject: [PATCH 32/44] mds-plan D6.3/D6.4: verifier zoo runs - per-control distinct encodings, scalar M5 contract, cross-host requalification; multiDataSet.R doc fix verifyMultiDataSetRpc.py gains the component-zoo runs (8 runs against testMultiDataSetControls): run 1-2 form bring-up with the zoo defaults (twins resolved per-row through their own filters, factor-level names decoded, scalars bare); runs 3-4 the mid-session host switch and switch-back (downstream re-encode follows the host, no phantom slices, dataSetIds pins); runs 5-6 re-send assignedA with the switched host (slice 2 then back to slice 1, 17.73/12.50 means proving per-filter encodings differ) with assertions relaxed to 'slice 2 queued, nothing beyond the workspace's two datasets' (the zoo's primary slice is always queued, so an exact [2] pin is wrong); run 7 the D6.2 typed copy bound into boundG on the OTHER host (keeps the SOURCE stamp, encodes through the source's view, dataSetIds covers both); run 8 the requalification landmine: the SOURCE host switches in a delta parse that does not re-send boundG - the copy must keep its foreign stamp (fixed jasp-desktop side by the BoundControl::selfRebindFormat Storage round-trip, so the delta-parse self-rebind no longer re-mints the terms without their filterIds). Every run asserts its PROVENANCE_AUDIT (filterIdsSeen=false, metaProvenance- Keys=0) and the M5 scalar contract; no raw JaspExtraOptions_ anywhere. R/multiDataSet.R: the stale getSelectionSlice doc line replaced with the real contract (index the datasets list with the selection's filter id directly - the accessor family was retired with the zero-export datasets contract). Live: ALL CHECKS PASSED (84 PASS) against the final jasp-desktop tree. --- R/multiDataSet.R | 4 +- Tools/verifyMultiDataSetRpc.py | 278 +++++++++++++++++++++++++++++++++ 2 files changed, 281 insertions(+), 1 deletion(-) diff --git a/R/multiDataSet.R b/R/multiDataSet.R index 2450283..1b70296 100644 --- a/R/multiDataSet.R +++ b/R/multiDataSet.R @@ -7,7 +7,9 @@ # (each dataset's own ColumnEncoder, prefix embeds the dataset id) and the dataframes' columns are # the very same strings, so a value indexes its column directly: slice[[value]]. Two differently # filtered versions of one dataset are two slices, both available; which one a form uses is the -# filter id in its selection option (jaspBase::getSelectionSlice). Result strings are decoded back +# filter id in its selection option - index the list with that id directly, no accessor needed +# (jaspBase's slice-accessor family, e.g. getSelectionSlice, was retired with the zero-export +# datasets contract). Result strings are decoded back # to the original names on the way to the GUI, so putting encoded names in tables/plots is not # just allowed, it is how provenance survives. # While such a run is active, readDataSet* stops: one 'current dataset' is meaningless here. diff --git a/Tools/verifyMultiDataSetRpc.py b/Tools/verifyMultiDataSetRpc.py index 5376997..96b2821 100644 --- a/Tools/verifyMultiDataSetRpc.py +++ b/Tools/verifyMultiDataSetRpc.py @@ -305,5 +305,283 @@ def overview_ids(res): a4 = next((x for x in ({f: fm4[f].get(r) for f in f4} for r in rk4) if x["source"] == "A" and x["variable"] == "Score"), None) check("run 4: A:Score back on DataSet 1 (mean 12.50)", a4 and str(a4["dataSetId"]) == "1" and "12.50" in str(a4["values"]), json.dumps(a4)) +# ---------------------------------------------------------------------------- +# mds-plan D6.3 - the component zoo (jaspTestModule analysis multiDataSetControlsFunc / +# testMultiDataSetControls.qml). Every control family echoes what it RECEIVED (encoded names, +# decoded again on the way out), which slice index each value resolved in, and its row children, +# so per-item provenance is asserted at the value level. The RPC workspace has one default filter +# per dataset (ids 1 and 2), which is exactly two distinct namespaces for the twin cases. +# Checks per family: +# - same-name columns across two filters in the SAME list (twins) -> distinct encodings, +# correct per-slice values (the 12.50/17.73 trick per family) +# - per-row provenance in the TableFields-style row control (row-bearing variables list): +# twin rows keep their own keep/shift children (the D6.1 TermRowKey re-keying; pre-fix the +# second row clobbered the first) +# - FactorLevelList encodeThis roundtrip: factor/level names arrive usable and NOTHING in the +# final results leaks a raw JaspExtraOptions_ or JASPColumn_ token +# - interaction terms resolve as one unit in a single slice (uniform term-level filterId) +# - textarea host: tokens encode through the textarea's own selection and resolve there +# - cross-host bindTo (D6.2): a typed copy carrying the SOURCE filterId resolves in the +# SOURCE's slice although the destination list selects the other one +# - scalars/combos stay plain; PROVENANCE_AUDIT clean on every zoo run +# ---------------------------------------------------------------------------- + +zoo = call("analysis_create", {"module": "jaspTestModule", "analysis": "multiDataSetControlsFunc"}) +check("zoo form created, analysis multiDataSetAware", zoo.get("status") == "success" and zoo.get("multiDataSetAware") is True) +zid = zoo.get("analysisId") + +def zoo_run(opts, tag): + r = call("analysis_run", {"analysisId": zid, "wait": True, "timeoutMs": 120000, "options": opts}) + check(f"zoo {tag} complete", r.get("status") == "complete", str(r.get("status"))) + res = call("analysis_results", {"analysisId": zid, "wait": True, "timeoutMs": 60000}) + return r, res + +def zoo_rows(res): + t = next((x for x in tables(res.get("results", res)).values() if x.get("title") == "Controls provenance"), None) + if not t: + return None + fields, fm, rks = field_maps(t) + return [{f: fm[f].get(r) for f in fields} for r in rks] + +def zoo_slice_ids(res): + t = next((x for x in tables(res.get("results", res)).values() if x.get("title") == "Datasets in this run"), None) + if not t: + return None + fields, fm, rks = field_maps(t) + return sorted(str(fm["id"].get(r)) for r in rks) + +def find(rows, family, pred=lambda r: True): + return [r for r in (rows or []) if r["family"] == family and pred(r)] + +# analysis_run options are a DELTA (form->parseOptions binds only the given members, everything +# else keeps its current bound value - verified live): every zoo run below sends the FULL explicit +# option set (families emptied, hosts deselected) so slice/dataSetIds expectations are exact. +EMPTY_TERMS = {"types": [], "value": []} +EMPTY_ROWS = {"types": [], "value": [], "optionKey": "variable"} +EMPTY_INTER = {"types": [], "value": [], "optionKey": "components"} +EMPTY_CODEF = {"modelOriginal": "", "model": "", "columns": []} + +def zoo_full_opts(host_a="1", host_b="", host_c="", host_d="", host_f="", host_g="", + assigned_a=None, assigned_b=None, rows_c=None, bound_g=None, + interaction_d=None, factors_e=None, code_f=None, combo_a="", + plain_switch=False, show_meta=True): + opts = { + "dataSetA": host_a, "dataSetB": host_b, "dataSetC": host_c, + "dataSetD": host_d, "dataSetF": host_f, "dataSetG": host_g, + "assignedA": assigned_a or dict(EMPTY_TERMS), + "assignedB": assigned_b or dict(EMPTY_TERMS), + "rowsC": rows_c or dict(EMPTY_ROWS), + "boundG": bound_g or {"value": []}, + "interactionD": interaction_d or dict(EMPTY_INTER), + "factorsE": factors_e or [], + "codeF": code_f or dict(EMPTY_CODEF), + "comboA": combo_a, "comboG": "first", "plainSwitch": plain_switch, + } + if show_meta: + opts["showMeta"] = True + return opts + +# --- zoo run 1: sanity - plain list through host A, audit on ------------------------------------- +zr1, zres1 = zoo_run({ + "dataSetA": "1", + "assignedA": {"types": ["scale"], "value": ["Score"]}, + "showMeta": True}, "run 1 (plain list via host A)") +z1 = zoo_rows(zres1) +check("zoo run 1: provenance table present", z1 is not None) +a1 = find(z1, "assigned-list") +check("zoo run 1: assignedA Score resolves in slice 1 (12.50)", + len(a1) == 1 and str(a1[0]["slice"]) == "1" and "12.50" in str(a1[0]["values"]), json.dumps(a1)) +auditz1 = provenance_audit(zres1) +check("zoo run 1: PROVENANCE_AUDIT clean (C8/C11)", auditz1 == ("false", "0"), str(auditz1)) +zslices1 = zoo_slice_ids(zres1) or [] +check("zoo run 1: slice 1 queued (host A), nothing beyond the workspace's two slices", + "1" in zslices1 and set(zslices1) <= {"1", "2"}, str(zslices1)) + +# --- zoo run 2: the whole zoo --------------------------------------------------------------------- +# rowsC arrives in the STORAGE term-object shape: same-name twin rows 'Score' behind filter 1 +# (keep=true, shift=1) and filter 2 (keep=false, shift=7). The applier must keep BOTH rows' own +# children (D6.1) and each token must encode/resolve through ITS filter's view. +# boundG receives a typed copy of a list on filter 1 (Score@1) while its OWN selection is filter 2: +# the destination rule must not requalify the foreign stamp (D6.2) - it resolves in slice 1. +# codeF's host selects filter 1 while the workspace's SHOWN dataset is B (filter 2): the textarea's +# bind-time encodeRScript must go through the HOST's view (C13 host-bound) - a Score token +# resolving in slice 1 (12.50) proves it; the analysis-level fallback would have yielded 17.73. +zr2, zres2 = zoo_run(zoo_full_opts( + host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", + assigned_a={"types": ["scale"], "value": ["Score"]}, + assigned_b={"types": ["nominal"], "value": ["Condition"]}, + rows_c={"value": [{"variable": {"name": "Score", "types": ["scale"], "filterId": 1}, "keep": True, "shift": 1}, + {"variable": {"name": "Score", "types": ["scale"], "filterId": 2}, "keep": False, "shift": 7}], + "optionKey": "variable"}, + bound_g={"value": [{"name": "Score", "types": ["scale"], "filterId": 1}]}, + interaction_d={"types": [["scale", "scale"]], "value": [{"components": ["Score", "Age"]}], "optionKey": "components"}, + factors_e=[{"name": "Group", "levels": ["A", "B"]}], + code_f={"modelOriginal": "Score + Age", "model": "Score + Age", "columns": []}, + combo_a="Score", plain_switch=True), "run 2 (full zoo)") +z2 = zoo_rows(zres2) +dump2 = json.dumps(z2) if z2 is not None else "" +check("zoo run 2: provenance table present", z2 is not None) +print("zoo run 2 rows:") +for r in (z2 or []): + print(" ", json.dumps(r)) + +# components-rows: the D6.1 flagship - twins with OWN children, distinct slices + values +rc = find(z2, "components-rows") +rc1 = next((r for r in rc if str(r["slice"]) == "1"), None) +rc2 = next((r for r in rc if str(r["slice"]) == "2"), None) +check("zoo run 2: rowsC twin rows kept DISTINCT encodings (two rows, slices 1 and 2)", + len(rc) == 2 and rc1 is not None and rc2 is not None, dump2[:400]) +check("zoo run 2: twin row @1 carries its OWN children keep=true shift=1 (+ 12.50)", + rc1 and "keep=TRUE" in rc1["rowChild"] and "shift=1," in rc1["rowChild"] + "," and "12.50" in rc1["values"], json.dumps(rc1)) +check("zoo run 2: twin row @2 carries its OWN children keep=false shift=7 (+ 17.73)", + rc2 and "keep=FALSE" in rc2["rowChild"] and "shift=7" in rc2["rowChild"] and "17.73" in rc2["values"], json.dumps(rc2)) +check("zoo run 2: decoded names, no raw JASPColumn_* in any row", "JASPColumn" not in dump2, dump2[:300]) + +# cross-host bindTo (D6.2) +bg = find(z2, "bindto-cross-host") +check("zoo run 2: boundG typed copy resolves in the SOURCE slice 1 (12.50) while its own host selects filter 2", + len(bg) == 1 and str(bg[0]["slice"]) == "1" and "12.50" in str(bg[0]["values"]), json.dumps(bg)) + +# assigned-host (Zoo B) +ab = find(z2, "assigned-host") +check("zoo run 2: assignedB (own host) Condition resolves in slice 2 with its levels", + len(ab) == 1 and str(ab[0]["slice"]) == "2" and ab[0]["values"].startswith("X"), json.dumps(ab)) + +# combobox (Zoo A): plain string value; NOTE the namespace finding - a DropDown is not a list +# host (no dataSetSelectionOption, resolvedSelectionHost stays the analysis level per C13), so +# its value encodes through the FORM namespace (shown filter = DataSet 2 here), not zooVarsA's. +# The zoo asserts the honest contract: value stays a plain string (never term-objectified, +# landmine 4) and roundtrips encoded->decoded intact. +ca = find(z2, "combobox-host") +check("zoo run 2: comboA 'Score' stays plain and resolves in exactly one slice (analysis-level namespace)", + len(ca) == 1 and ca[0]["received"] == "Score" and str(ca[0]["slice"]) in ("1", "2"), json.dumps(ca)) + +# interaction (Zoo D): one term, components share the slice +itr = find(z2, "interaction-list") +check("zoo run 2: interaction Score:Age (host D=1) resolves as one unit in slice 1", + len(itr) == 1 and str(itr[0]["slice"]) == "1" and "share slice 1" in str(itr[0]["values"]), json.dumps(itr)) + +# factor levels (Zoo E): extras roundtrip - names usable, decoded, no leakage +fe = find(z2, "factor-levels") +check("zoo run 2: factorsE echoes Group with levels A, B (decoded and usable)", + len(fe) == 1 and fe[0]["received"] == "Group" and "A, B" in fe[0]["rowChild"], json.dumps(fe)) + +# textarea host (Zoo F): tokens resolved through dataSetF's slice (1) - the SHARP host-bound proof: +# the workspace's shown dataset is B (filter 2), so 17.73 here would mean the analysis-level +# fallback won and the host declaration was ignored. +tf = find(z2, "textarea-host") +tf_scores = [r for r in tf if r["received"] == "Score"] +tf_ages = [r for r in tf if r["received"] == "Age"] +tf_texts = [r for r in tf if r["option"] == "codeF(text)"] +check("zoo run 2: codeF host-1 tokens: Score resolves in slice 1 (12.50, NOT the shown-dataset 17.73)", + len(tf_scores) >= 1 and all(str(r["slice"]) == "1" for r in tf_scores) and any("12.50" in r["values"] for r in tf_scores), json.dumps(tf_scores)) +check("zoo run 2: codeF host-1 tokens: Age resolves in slice 1", + len(tf_ages) >= 1 and all(str(r["slice"]) == "1" for r in tf_ages) and any(r["values"] not in ("", "None", None) for r in tf_ages), json.dumps(tf_ages)) +check("zoo run 2: codeF model text round-trips (decoded on the way out)", + len(tf_texts) == 1 and tf_texts[0]["received"] == "Score + Age", json.dumps(tf_texts)) + +# scalars stay plain (M5): echo rows exist with the plain values +sc = find(z2, "scalar") +cg = find(z2, "combobox-plain") +check("zoo run 2: scalar plainSwitch echoes plain true (never provenanced)", len(sc) == 1 and sc[0]["received"].upper() == "TRUE", json.dumps(sc)) +check("zoo run 2: plain combo comboG echoes 'first' (never term-objectified)", len(cg) == 1 and cg[0]["received"] == "first", json.dumps(cg)) + +# slices: hosts on filters 1+2 (A,D on 1; B,F,G on 2) + every referenced value's slice +zslices2 = zoo_slice_ids(zres2) +check("zoo run 2: both slices present, no phantom", zslices2 == ["1", "2"], str(zslices2)) +auditz2 = provenance_audit(zres2) +check("zoo run 2: PROVENANCE_AUDIT clean", auditz2 == ("false", "0"), str(auditz2)) +fullres2 = json.dumps(zres2) +check("zoo run 2: NO raw JASPColumn_* anywhere in results", "JASPColumn" not in fullres2) +check("zoo run 2: NO raw JaspExtraOptions_ leakage anywhere in results", "JaspExtraOptions" not in fullres2) + +# --- zoo run 3: same-name twins in the PLAIN assigned list (storage term objects) ----------------- +zr3, zres3 = zoo_run(zoo_full_opts( + host_a="1", + assigned_a={"value": [{"name": "Score", "types": ["scale"], "filterId": 1}, + {"name": "Score", "types": ["scale"], "filterId": 2}]}, + show_meta=False), "run 3 (plain-list twins, everything else reset)") +z3 = zoo_rows(zres3) +al = find(z3, "assigned-list") +t1 = next((r for r in al if str(r["slice"]) == "1"), None) +t2 = next((r for r in al if str(r["slice"]) == "2"), None) +check("zoo run 3: assignedA twin rows distinct (both 'Score', two slices, two truths)", + len(al) == 2 and t1 and t2 and "12.50" in t1["values"] and "17.73" in t2["values"], json.dumps(al)) +check("zoo run 3: dataSetIds covers both datasets", sorted(zr3.get("dataSetIds", [])) == [1, 2], str(zr3.get("dataSetIds"))) + +# --- zoo run 4: reset to a single plain family on host A (baseline for the mid-session switch) ---- +zr4, zres4 = zoo_run(zoo_full_opts(host_a="1", + assigned_a={"types": ["scale"], "value": ["Score"]}), + "run 4 (reset: plain list only, host A=1)") +z4 = zoo_rows(zres4) +a4z = find(z4, "assigned-list") +check("zoo run 4: assignedA Score on slice 1 (12.50)", + len(a4z) == 1 and "12.50" in a4z[0]["values"] and str(a4z[0]["slice"]) == "1", json.dumps(a4z)) + +# --- zoo run 5: mid-session host switch (delta run: only the host option changes) ----------------- +# NB: the value IS re-sent (same terms as run 4) - analysis_run options are a delta, but the +# point here is the RE-ENCODE through the NEW host namespace, which only happens when the option +# is part of the run (an untouched option keeps its already-encoded value). The zoo's primary +# slice (the analysis' own dataset) is queued besides the sidecar, so only "no dataset beyond +# the workspace's two" can be asserted here - the sharp claim is the row: Score must now resolve +# in slice 2 (17.73), i.e. the restamp walk + sidecar followed host A to DataSet 2. +zr5, zres5 = zoo_run(zoo_full_opts(host_a="2", + assigned_a={"types": ["scale"], "value": ["Score"]}), + "run 5 (mid-session switch host A -> filter 2)") +z5 = zoo_rows(zres5) +a5z = find(z5, "assigned-list") +check("zoo run 5: mid-session switch re-encoded the value to slice 2 (17.73)", + len(a5z) == 1 and str(a5z[0]["slice"]) == "2" and "17.73" in a5z[0]["values"], json.dumps(a5z)) +check("zoo run 5: slice 2 queued, nothing beyond the workspace's two datasets", + "2" in (zoo_slice_ids(zres5) or []) and set(zoo_slice_ids(zres5) or []) <= {"1", "2"}, str(zoo_slice_ids(zres5))) +check("zoo run 5: dataSetIds covers DataSet 2 and nothing else", + 2 in zr5.get("dataSetIds", []) and set(zr5.get("dataSetIds", [])) <= {1, 2}, str(zr5.get("dataSetIds"))) +auditz5 = provenance_audit(zres5) +check("zoo run 5: PROVENANCE_AUDIT clean after the switch", auditz5 == ("false", "0"), str(auditz5)) + +# --- zoo run 6: and back --------------------------------------------------------------------------- +zr6, zres6 = zoo_run(zoo_full_opts(host_a="1", + assigned_a={"types": ["scale"], "value": ["Score"]}), + "run 6 (switch back host A -> filter 1)") +z6 = zoo_rows(zres6) +a6z = find(z6, "assigned-list") +check("zoo run 6: switched back, Score resolves in slice 1 again (12.50)", + len(a6z) == 1 and str(a6z[0]["slice"]) == "1" and "12.50" in a6z[0]["values"], json.dumps(a6z)) + +# --- zoo runs 7/8 (D6.2): the cross-host typed copy against live host switches --------------------- +# run 7: source host A on filter 2, destination host G on filter 1; assignedA binds unqualified +# (intake stamps it @2) and boundG receives the typed copy Score@2. Both values must resolve in +# slice 2 - the copy through its SOURCE stamp, not through its own host's filter 1. +zr7, zres7 = zoo_run(zoo_full_opts( + host_a="2", host_g="1", + assigned_a={"types": ["scale"], "value": ["Score"]}, + bound_g={"value": [{"name": "Score", "types": ["scale"], "filterId": 2}]}), "run 7 (D6.2: copy @2, dest host on 1)") +z7 = zoo_rows(zres7) +a7z = find(z7, "assigned-list") +bg7 = find(z7, "bindto-cross-host") +check("zoo run 7: assignedA (own host A=2) resolves in slice 2 (17.73)", + len(a7z) == 1 and str(a7z[0]["slice"]) == "2" and "17.73" in a7z[0]["values"], json.dumps(a7z)) +check("zoo run 7: boundG typed copy keeps the SOURCE stamp: slice 2 (17.73) although its own host G=1", + len(bg7) == 1 and str(bg7[0]["slice"]) == "2" and "17.73" in bg7[0]["values"], json.dumps(bg7)) +check("zoo run 7: dataSetIds covers source (ds2) and destination host (ds1)", sorted(zr7.get("dataSetIds", [])) == [1, 2], str(zr7.get("dataSetIds"))) +auditz7 = provenance_audit(zres7) +check("zoo run 7: PROVENANCE_AUDIT clean", auditz7 == ("false", "0"), str(auditz7)) + +# run 8: switch the SOURCE host A back to filter 1 (delta run: boundG is NOT re-sent, its typed +# term state carries the foreign stamp 2). The owns-the-stamp rule (D3.5) must restamp ONLY the +# host-owned term: assignedA follows A to slice 1, boundG stays on the SOURCE stamp @2 - the copy +# is a stable stamp, not a live reference; requalification to the (now matching) filter 1 would +# show up as 12.50 and fail here. +zr8, zres8 = zoo_run({"dataSetA": "1", "showMeta": True}, "run 8 (D6.2: source host switched, copy not re-sent)") +z8 = zoo_rows(zres8) +a8z = find(z8, "assigned-list") +bg8 = find(z8, "bindto-cross-host") +check("zoo run 8: assignedA followed its OWN host to slice 1 (12.50)", + len(a8z) == 1 and str(a8z[0]["slice"]) == "1" and "12.50" in a8z[0]["values"], json.dumps(a8z)) +check("zoo run 8: boundG foreign stamp NOT requalified by the source-host switch (still slice 2, 17.73)", + len(bg8) == 1 and str(bg8[0]["slice"]) == "2" and "17.73" in bg8[0]["values"], json.dumps(bg8)) +auditz8 = provenance_audit(zres8) +check("zoo run 8: PROVENANCE_AUDIT clean", auditz8 == ("false", "0"), str(auditz8)) + print("\n" + ("ALL CHECKS PASSED" if not fails else f"{len(fails)} FAILURES: {fails}")) sys.exit(0 if not fails else 1) From 8c516fa1188283c3f463bfc069343c8e7ed730cc Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Sun, 18 Oct 2026 10:17:41 +0200 Subject: [PATCH 33/44] mds-plan D8.3/D8.5: aware wrappers regenerated with the per-slice selection-default template; multiDataSet.R contract rewrite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both aware wrappers (multiDataSetFunc, multiDataSetControlsFunc) regenerated through the bridge (jaspSyntax::generateAnalysisWrapper) with the D8.3 template: the data <- datasets[[1]] ambiguity pin is replaced by per-slice selection defaults keyed off the SELECTION TITLE surface - selectionOptions <- c(...) in the form's host-registration order, resolved at run time from options (exact title, then the bare-title best effort for "Title - filterName" surfaces), a loud stop naming the title-keyed datasets contract when nothing matches, and the documented datasets[[1L]] fallback only for a form with no selection surface. The datasets contract is now stated on every aware wrapper (@param datasets roxygen). multiDataSetControlsFuncWrapper was hand-mirrored before (D6.1) - it is a real generated file now, which also updates its option defaults/order to what the form currently generates (codeF/comboA/interactionD/rowsC shapes, optionsWithFormula over the list options). R/multiDataSet.R header rewritten as the contract document (D8.5): generated .meta carries no provenance and the .filterIds sidecar is stripped at the engine boundary (C8/C11, the PROVENANCE_AUDIT marker); provenance-in-name doctrine (C1/C8, encoded names in results are how provenance survives, decoded on the way out by the one global decode table); the datasets[[filter-id]] contract now stated correctly for BOTH engine runs and jaspTools handouts (the old header's "jaspTools handouts key it by dataset id" was stale - jaspTools passes the bridge's multiDataSetJson, filter-id keyed like the engine); qualified grammar surface (plan §5): term options are parsed C++-side into typed terms, string options receive the qualified form verbatim - the D8.4 v1 stance (pass-through, slice on it via the zoo's echo pattern if needed, never re-implement the grammar in R); both aware analyses named. getSelectionSlice reference was already fixed (D6.4) - verified, no other stale references. --- R/multiDataSet.R | 49 +++++++++++++++++++++-------- R/multiDataSetControlsFuncWrapper.R | 49 ++++++++++++++++++++++------- R/multiDataSetFuncWrapper.R | 30 +++++++++++++++++- 3 files changed, 103 insertions(+), 25 deletions(-) diff --git a/R/multiDataSet.R b/R/multiDataSet.R index 1b70296..939af30 100644 --- a/R/multiDataSet.R +++ b/R/multiDataSet.R @@ -1,18 +1,41 @@ # Multi-dataset aware analyses (jasp-desktop branch dataSetAwareVarForm, jaspBase branch # multiDataSetAware). An analysis whose Description/Analysis entry sets multiDataSetAware gets -# every dataset it references as `datasets`. Engine runs key that list by SLICE id (the resolved -# filter id of each queued dataset+filter pair, attrs "dataSetIds"/"dataSetNames" map a key back), -# jaspTools handouts key it by dataset id - both are indexed safely through jaspBase's helpers. -# Encoding stays PER DATASET: variable options carry names like "JASPColumn___Encoded" -# (each dataset's own ColumnEncoder, prefix embeds the dataset id) and the dataframes' columns are -# the very same strings, so a value indexes its column directly: slice[[value]]. Two differently -# filtered versions of one dataset are two slices, both available; which one a form uses is the -# filter id in its selection option - index the list with that id directly, no accessor needed -# (jaspBase's slice-accessor family, e.g. getSelectionSlice, was retired with the zero-export -# datasets contract). Result strings are decoded back -# to the original names on the way to the GUI, so putting encoded names in tables/plots is not -# just allowed, it is how provenance survives. -# While such a run is active, readDataSet* stops: one 'current dataset' is meaningless here. +# every dataset its options reference as `datasets`. Engine runs AND jaspTools::runAnalysis +# handouts both key that list by SLICE id - the resolved filter id of each queued dataset+filter +# pair (attrs "dataSetIds"/"dataSetNames" map a key back to its dataset) - so both are indexed +# the same way: the form's dataset selection option holds exactly that id, `datasets[[key]]` is +# the whole API, no accessor needed (jaspBase's slice-accessor family, e.g. getSelectionSlice, +# was retired with the zero-export datasets contract). +# Encoding stays PER FILTER: variable options carry names like +# "JASPColumn_D_F__Encoded" (the filter's encoder-view prefix embeds both +# ids) and the dataframes' columns are the very same strings, so a value indexes its column +# directly: slice[[value]]. Two differently filtered versions of one dataset are two slices, +# both available. Result strings are decoded back to the original names on the way to the GUI +# (Engine::sendString and the rbridge callbacks, all consumers of the one global strict decode +# table), so putting encoded names in tables/plots is not just allowed, it is how provenance +# survives: THE PROVENANCE IS IN THE NAME (mds-plan C1/C8), never in a side channel. While such +# a run is active, readDataSet* stops: one 'current dataset' is meaningless here. +# +# Options contract (mds-plan C8/C11, the Phase-3 flip): `options$.meta` is GENERATED at send +# time and carries no dataSetId/filterId; the transient `.filterIds` sidecar is stripped at the +# engine boundary before R sees anything. metaDump() below echoes both facts as the +# PROVENANCE_AUDIT marker the RPC verifier asserts on every run. +# +# Qualified column references (mds-plan plan §5, cards D8.1/D8.2/D8.4): a TERM option may +# arrive with qualified surface forms ("Dataset"."Filter"."Column"("Type"), minimal per +# context); the C++ appliers parse them into typed terms with filterIds before the run, so R +# receives them as ordinary encoded names - no R-side grammar (C14). A STRING option (plain +# text input) receives the qualified form verbatim - the D8.4 v1 stance: it passes through and +# modules slice on it or not, at their choice; module code that needs to interpret one uses +# the echo pattern of multiDataSetControlsInternal (report what was received, then resolve on +# demand) and never re-implements the token grammar. +# +# The two aware analyses here: multiDataSetFuncInternal (form testMultiDataSet.qml - two +# per-list dataset selection options dataSetA/dataSetB, the D8.3 wrapper fixture) and +# multiDataSetControlsInternal (the component zoo testMultiDataSetControls.qml, mds-plan +# D6.1/D6.2: assigned lists, row controls with same-name twins, interactions, factor levels +# via encodeThis, a textarea host, a cross-host typed bindTo copy - each reports what it +# received and where it resolved). multiDataSetFuncInternal <- function(jaspResults, dataset, options, datasets = NULL) { aware <- !is.null(datasets) diff --git a/R/multiDataSetControlsFuncWrapper.R b/R/multiDataSetControlsFuncWrapper.R index 0db14da..91101bf 100644 --- a/R/multiDataSetControlsFuncWrapper.R +++ b/R/multiDataSetControlsFuncWrapper.R @@ -15,19 +15,24 @@ # along with this program. If not, see . # -# This is a generated file. Don't change it! (hand-mirrors multiDataSetFuncWrapper.R, the -# mds-plan D6.1 component-zoo analysis) +# This is a generated file. Don't change it! #' Test Multi-DataSet Controls #' +#' @param datasets a named list of data.frames keyed by DATASET TITLE - the titles the +#' dataset selection options of this form carry (their defaults below, rendered as the +#' selection surface, e.g. "Iris" or "Iris - myfilter"). Every entry is loaded standalone +#' into the bridge workspace (runWrappedAnalysis stores them via jaspSyntax::loadDataSets, +#' each under its own name as the dataset title); the analysis itself receives its slices +#' keyed by filter id - the jaspBase datasets contract (see jaspBase::runJaspResults). multiDataSetControlsFunc <- function( datasets = NULL, version = "1", assignedA = list(types = list(), value = list()), assignedB = list(types = list(), value = list()), boundG = list(types = list(), value = list()), - codeF = list(model = "", modelOriginal = "", columns = list()), - comboA = "", + codeF = list(columns = list(), model = "", modelOriginal = ""), + comboA = list(types = "unknown", value = ""), comboG = "first", dataSetA = "", dataSetB = "", @@ -36,12 +41,12 @@ multiDataSetControlsFunc <- function( dataSetF = "", dataSetG = "", factorsE = list(), - interactionD = list(types = list(), value = list()), + interactionD = list(optionKey = "components", types = list(), value = list()), plainSwitch = FALSE, - rowsC = list(), - showMeta = FALSE, plotHeight = 320, - plotWidth = 480) { + plotWidth = 480, + rowsC = list(optionKey = "variable", types = list(), value = list()), + showMeta = FALSE) { defaultArgCalls <- formals(jaspTestModule::multiDataSetControlsFunc) defaultArgs <- lapply(defaultArgCalls, eval) @@ -53,11 +58,33 @@ multiDataSetControlsFunc <- function( options[["version"]] <- NULL - data <- if (!is.null(datasets) && length(datasets)) datasets[[1]] else NULL + # mds-plan D8.3: `datasets` must be a named list of data.frames keyed by DATASET TITLE - the + # titles the dataset selection options of this form carry (e.g. list(Iris = iris, Sleep = sleep)). + # No positional pin: the dataset that plays `data` (the frame the formula machinery evaluates + # against) is the first selection option whose effective value names one of the loaded datasets, + # resolved at run time from `options`. Every entry is loaded standalone by runWrappedAnalysis + # (jaspBase::storeDataSets -> jaspSyntax::loadDataSets, each under its own name as the dataset + # title); the analysis itself receives its slices keyed by filter id (jaspBase datasets contract). + selectionOptions <- c("dataSetB", "dataSetG", "dataSetF", "dataSetC", "dataSetA", "dataSetD") + data <- NULL + if (!is.null(datasets) && length(datasets)) { + for (selectionOption in selectionOptions) { + selectionValue <- options[[selectionOption]] + if (!is.character(selectionValue) || length(selectionValue) != 1L || is.na(selectionValue) || !nzchar(selectionValue)) next + if (selectionValue %in% names(datasets)) { data <- datasets[[selectionValue]]; break } + bareTitle <- sub(" - [^-]*$", "", selectionValue) # 'Title - filterName' surface: bare-title fallback, best effort + if (bareTitle %in% names(datasets)) { data <- datasets[[bareTitle]]; break } + } + if (is.null(data)) { + if (length(selectionOptions)) + stop("`datasets` must be a named list keyed by dataset title (e.g. list(Iris = iris)): the dataset selection option(s) ", paste(selectionOptions, collapse = ", "), " select a title matching none of: ", paste(names(datasets), collapse = ", ")) + data <- datasets[[1L]] # this form has no dataset selection surface: the first entry is the documented primary (jaspSyntax::loadDataSets) + } + } - optionsWithFormula <- character(0) + optionsWithFormula <- c("assignedA", "assignedB", "boundG", "codeF", "comboA", "comboG", "dataSetA", "dataSetB", "dataSetC", "dataSetD", "dataSetF", "dataSetG", "factorsE", "interactionD", "rowsC") for (name in optionsWithFormula) { if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } return(jaspBase::runWrappedAnalysis("jaspTestModule", "multiDataSetControlsFunc", "testMultiDataSetControls.qml", options, version, TRUE, datasets = datasets)) -} +} \ No newline at end of file diff --git a/R/multiDataSetFuncWrapper.R b/R/multiDataSetFuncWrapper.R index bb11f97..85941ed 100644 --- a/R/multiDataSetFuncWrapper.R +++ b/R/multiDataSetFuncWrapper.R @@ -19,6 +19,12 @@ #' Test Multi-DataSet #' +#' @param datasets a named list of data.frames keyed by DATASET TITLE - the titles the +#' dataset selection options of this form carry (their defaults below, rendered as the +#' selection surface, e.g. "Iris" or "Iris - myfilter"). Every entry is loaded standalone +#' into the bridge workspace (runWrappedAnalysis stores them via jaspSyntax::loadDataSets, +#' each under its own name as the dataset title); the analysis itself receives its slices +#' keyed by filter id - the jaspBase datasets contract (see jaspBase::runJaspResults). multiDataSetFunc <- function( datasets = NULL, version = "1", @@ -41,7 +47,29 @@ multiDataSetFunc <- function( options[["version"]] <- NULL - data <- if (!is.null(datasets) && length(datasets)) datasets[[1]] else NULL + # mds-plan D8.3: `datasets` must be a named list of data.frames keyed by DATASET TITLE - the + # titles the dataset selection options of this form carry (e.g. list(Iris = iris, Sleep = sleep)). + # No positional pin: the dataset that plays `data` (the frame the formula machinery evaluates + # against) is the first selection option whose effective value names one of the loaded datasets, + # resolved at run time from `options`. Every entry is loaded standalone by runWrappedAnalysis + # (jaspBase::storeDataSets -> jaspSyntax::loadDataSets, each under its own name as the dataset + # title); the analysis itself receives its slices keyed by filter id (jaspBase datasets contract). + selectionOptions <- c("dataSetA", "dataSetB") + data <- NULL + if (!is.null(datasets) && length(datasets)) { + for (selectionOption in selectionOptions) { + selectionValue <- options[[selectionOption]] + if (!is.character(selectionValue) || length(selectionValue) != 1L || is.na(selectionValue) || !nzchar(selectionValue)) next + if (selectionValue %in% names(datasets)) { data <- datasets[[selectionValue]]; break } + bareTitle <- sub(" - [^-]*$", "", selectionValue) # 'Title - filterName' surface: bare-title fallback, best effort + if (bareTitle %in% names(datasets)) { data <- datasets[[bareTitle]]; break } + } + if (is.null(data)) { + if (length(selectionOptions)) + stop("`datasets` must be a named list keyed by dataset title (e.g. list(Iris = iris)): the dataset selection option(s) ", paste(selectionOptions, collapse = ", "), " select a title matching none of: ", paste(names(datasets), collapse = ", ")) + data <- datasets[[1L]] # this form has no dataset selection surface: the first entry is the documented primary (jaspSyntax::loadDataSets) + } + } optionsWithFormula <- c("dataSetA", "dataSetB", "dependentA", "dependentB") for (name in optionsWithFormula) { From 760c9cd3b02f42fca63509c56a64261f87c94b51 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Sun, 18 Oct 2026 12:11:43 +0200 Subject: [PATCH 34/44] mds-plan D9.1: TODO-multidataset.md marked SUPERSEDED - it is the pre-plan (2026-10-08) FilterSelect-era status snapshot; several APIs it names (effectiveVarInfo/varInfoForItem, createMeta stamps, .sliceKeyForDataSet) were replaced by the mds-plan architecture; the Done section is a historical commit log and is kept verbatim; current contract doc is R/multiDataSet.R --- TODO-multidataset.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/TODO-multidataset.md b/TODO-multidataset.md index 710ec56..d9b4beb 100644 --- a/TODO-multidataset.md +++ b/TODO-multidataset.md @@ -1,4 +1,16 @@ # Multi-dataset aware feature — status (2026-10-08, post FilterSelect refactor) +> +> **SUPERSEDED (2026-10-18, mds-plan D9.1).** This is a historical snapshot of the pre-plan +> (2026-10-08) FilterSelect-refactor era. Several mechanisms it names were replaced by the +> mds-plan implementation (the typed per-term provenance, per-filter encoder views, the one +> global strict decode table, generated send-time `.meta`): `JASPControl::effectiveVarInfo()` +> / `effectiveSelectionFilter()` / `varInfoForItem()` (walk deleted, D4.1 — successors: +> `resolvedSelectionHost()`/`resolvedVarInfo()`), `createMeta` stamps (generated send-time +> `.meta` + the transient `.filterIds` sidecar, C8/C11), `.sliceKeyForDataSet` (retired with +> the zero-export datasets contract), and "jaspTools handouts keyed by dataset id" (stale — +> both engine runs and jaspTools handouts key the datasets list by the resolved filter id). +> The current module-author contract lives in `R/multiDataSet.R`; the "Done" section below is +> the commit log of that era and is kept verbatim as history. ## Decided design (ground truth, v2 — supersedes the linked-dropdown v1) - Options and dataset slices share the ENCODED namespace (`JASPColumn___Encoded`); From 425b273f10ad3823d1d6ef44aa112a30ba2c6340 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Sun, 18 Oct 2026 17:38:52 +0200 Subject: [PATCH 35/44] mds-plan post: lockfile regenerated for R 4.6.1 env (cloud.r-project.org, current package set via jaspModuleTools::updateLockfile/pkgdepends) - bundles will build against the local multiDataSetAware jaspBase/jaspSyntax/jaspTools --- renv.lock | 3251 ++++++++--------------------------------------------- 1 file changed, 467 insertions(+), 2784 deletions(-) diff --git a/renv.lock b/renv.lock index 5be3507..5fef58b 100644 --- a/renv.lock +++ b/renv.lock @@ -1,9 +1,10 @@ -{ "R": { - "Version": "4.5.3", +{ + "R": { + "Version": "4.6.1", "Repositories": [ { "Name": "CRAN", - "URL": "https://packagemanager.posit.co/cran/latest" + "URL": "https://cloud.r-project.org" } ] }, @@ -12,1387 +13,440 @@ "Package": "R6", "Version": "2.6.1", "Source": "Repository", - "Title": "Encapsulated Classes with Reference Semantics", - "Authors@R": "c( person(\"Winston\", \"Chang\", , \"winston@posit.co\", role = c(\"aut\", \"cre\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "Creates classes with reference semantics, similar to R's built-in reference classes. Compared to reference classes, R6 classes are simpler and lighter-weight, and they are not built on S4 classes so they do not require the methods package. These classes allow public and private members, and they support inheritance, even when the classes are defined in different packages.", - "License": "MIT + file LICENSE", - "URL": "https://r6.r-lib.org, https://github.com/r-lib/R6", - "BugReports": "https://github.com/r-lib/R6/issues", - "Depends": [ - "R (>= 3.6)" - ], - "Suggests": [ - "lobstr", - "testthat (>= 3.0.0)" - ], - "Config/Needs/website": "tidyverse/tidytemplate, ggplot2, microbenchmark, scales", - "Config/testthat/edition": "3", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2", - "NeedsCompilation": "no", - "Author": "Winston Chang [aut, cre], Posit Software, PBC [cph, fnd]", - "Maintainer": "Winston Chang ", - "Repository": "CRAN" + "Requirements": [ + "R" + ] }, "RColorBrewer": { "Package": "RColorBrewer", "Version": "1.1-3", "Source": "Repository", - "Date": "2022-04-03", - "Title": "ColorBrewer Palettes", - "Authors@R": "c(person(given = \"Erich\", family = \"Neuwirth\", role = c(\"aut\", \"cre\"), email = \"erich.neuwirth@univie.ac.at\"))", - "Author": "Erich Neuwirth [aut, cre]", - "Maintainer": "Erich Neuwirth ", - "Depends": [ - "R (>= 2.0.0)" - ], - "Description": "Provides color schemes for maps (and other graphics) designed by Cynthia Brewer as described at http://colorbrewer2.org.", - "License": "Apache License 2.0", - "NeedsCompilation": "no", - "Repository": "CRAN" + "Requirements": [ + "R" + ] }, "Rcpp": { "Package": "Rcpp", - "Version": "1.1.1", - "Source": "Repository", - "Title": "Seamless R and C++ Integration", - "Date": "2026-01-07", - "Authors@R": "c(person(\"Dirk\", \"Eddelbuettel\", role = c(\"aut\", \"cre\"), email = \"edd@debian.org\", comment = c(ORCID = \"0000-0001-6419-907X\")), person(\"Romain\", \"Francois\", role = \"aut\", comment = c(ORCID = \"0000-0002-2444-4226\")), person(\"JJ\", \"Allaire\", role = \"aut\", comment = c(ORCID = \"0000-0003-0174-9868\")), person(\"Kevin\", \"Ushey\", role = \"aut\", comment = c(ORCID = \"0000-0003-2880-7407\")), person(\"Qiang\", \"Kou\", role = \"aut\", comment = c(ORCID = \"0000-0001-6786-5453\")), person(\"Nathan\", \"Russell\", role = \"aut\"), person(\"Iñaki\", \"Ucar\", role = \"aut\", comment = c(ORCID = \"0000-0001-6403-5550\")), person(\"Doug\", \"Bates\", role = \"aut\", comment = c(ORCID = \"0000-0001-8316-9503\")), person(\"John\", \"Chambers\", role = \"aut\"))", - "Description": "The 'Rcpp' package provides R functions as well as C++ classes which offer a seamless integration of R and C++. Many R data types and objects can be mapped back and forth to C++ equivalents which facilitates both writing of new code as well as easier integration of third-party libraries. Documentation about 'Rcpp' is provided by several vignettes included in this package, via the 'Rcpp Gallery' site at , the paper by Eddelbuettel and Francois (2011, ), the book by Eddelbuettel (2013, ) and the paper by Eddelbuettel and Balamuta (2018, ); see 'citation(\"Rcpp\")' for details.", - "Depends": [ - "R (>= 3.5.0)" - ], - "Imports": [ + "Version": "1.1.2", + "Source": "Repository", + "Requirements": [ "methods", + "R", "utils" - ], - "Suggests": [ - "tinytest", - "inline", - "rbenchmark", - "pkgKitten (>= 0.1.2)" - ], - "URL": "https://www.rcpp.org, https://dirk.eddelbuettel.com/code/rcpp.html, https://github.com/RcppCore/Rcpp", - "License": "GPL (>= 2)", - "BugReports": "https://github.com/RcppCore/Rcpp/issues", - "MailingList": "rcpp-devel@lists.r-forge.r-project.org", - "RoxygenNote": "6.1.1", - "Encoding": "UTF-8", - "VignetteBuilder": "Rcpp", - "NeedsCompilation": "yes", - "Author": "Dirk Eddelbuettel [aut, cre] (ORCID: ), Romain Francois [aut] (ORCID: ), JJ Allaire [aut] (ORCID: ), Kevin Ushey [aut] (ORCID: ), Qiang Kou [aut] (ORCID: ), Nathan Russell [aut], Iñaki Ucar [aut] (ORCID: ), Doug Bates [aut] (ORCID: ), John Chambers [aut]", - "Maintainer": "Dirk Eddelbuettel ", - "Repository": "CRAN" + ] }, "S7": { "Package": "S7", - "Version": "0.2.1", + "Version": "0.2.2", "Source": "Repository", - "Title": "An Object Oriented System Meant to Become a Successor to S3 and S4", - "Authors@R": "c( person(\"Object-Oriented Programming Working Group\", role = \"cph\"), person(\"Davis\", \"Vaughan\", role = \"aut\"), person(\"Jim\", \"Hester\", role = \"aut\", comment = c(ORCID = \"0000-0002-2739-7082\")), person(\"Tomasz\", \"Kalinowski\", role = \"aut\"), person(\"Will\", \"Landau\", role = \"aut\"), person(\"Michael\", \"Lawrence\", role = \"aut\"), person(\"Martin\", \"Maechler\", role = \"aut\", comment = c(ORCID = \"0000-0002-8685-9910\")), person(\"Luke\", \"Tierney\", role = \"aut\"), person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0003-4757-117X\")) )", - "Description": "A new object oriented programming system designed to be a successor to S3 and S4. It includes formal class, generic, and method specification, and a limited form of multiple dispatch. It has been designed and implemented collaboratively by the R Consortium Object-Oriented Programming Working Group, which includes representatives from R-Core, 'Bioconductor', 'Posit'/'tidyverse', and the wider R community.", - "License": "MIT + file LICENSE", - "URL": "https://rconsortium.github.io/S7/, https://github.com/RConsortium/S7", - "BugReports": "https://github.com/RConsortium/S7/issues", - "Depends": [ - "R (>= 3.5.0)" - ], - "Imports": [ + "Requirements": [ + "R", "utils" - ], - "Suggests": [ - "bench", - "callr", - "covr", - "knitr", - "methods", - "rmarkdown", - "testthat (>= 3.2.0)", - "tibble" - ], - "VignetteBuilder": "knitr", - "Config/build/compilation-database": "true", - "Config/Needs/website": "sloop", - "Config/testthat/edition": "3", - "Config/testthat/parallel": "TRUE", - "Config/testthat/start-first": "external-generic", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3", - "NeedsCompilation": "yes", - "Author": "Object-Oriented Programming Working Group [cph], Davis Vaughan [aut], Jim Hester [aut] (ORCID: ), Tomasz Kalinowski [aut], Will Landau [aut], Michael Lawrence [aut], Martin Maechler [aut] (ORCID: ), Luke Tierney [aut], Hadley Wickham [aut, cre] (ORCID: )", - "Maintainer": "Hadley Wickham ", - "Repository": "CRAN" + ] }, "askpass": { "Package": "askpass", "Version": "1.2.1", "Source": "Repository", - "Type": "Package", - "Title": "Password Entry Utilities for R, Git, and SSH", - "Authors@R": "person(\"Jeroen\", \"Ooms\", role = c(\"aut\", \"cre\"), email = \"jeroenooms@gmail.com\", comment = c(ORCID = \"0000-0002-4035-0289\"))", - "Description": "Cross-platform utilities for prompting the user for credentials or a passphrase, for example to authenticate with a server or read a protected key. Includes native programs for MacOS and Windows, hence no 'tcltk' is required. Password entry can be invoked in two different ways: directly from R via the askpass() function, or indirectly as password-entry back-end for 'ssh-agent' or 'git-credential' via the SSH_ASKPASS and GIT_ASKPASS environment variables. Thereby the user can be prompted for credentials or a passphrase if needed when R calls out to git or ssh.", - "License": "MIT + file LICENSE", - "URL": "https://r-lib.r-universe.dev/askpass", - "BugReports": "https://github.com/r-lib/askpass/issues", - "Encoding": "UTF-8", - "Imports": [ - "sys (>= 2.1)" - ], - "RoxygenNote": "7.2.3", - "Suggests": [ - "testthat" - ], - "Language": "en-US", - "NeedsCompilation": "yes", - "Author": "Jeroen Ooms [aut, cre] ()", - "Maintainer": "Jeroen Ooms ", - "Repository": "CRAN" + "Requirements": [ + "sys" + ] }, "base64enc": { "Package": "base64enc", "Version": "0.1-6", "Source": "Repository", - "Title": "Tools for 'base64' Encoding", - "Author": "Simon Urbanek [aut, cre, cph] (https://urbanek.nz, ORCID: )", - "Authors@R": "person(\"Simon\", \"Urbanek\", role=c(\"aut\",\"cre\",\"cph\"), email=\"Simon.Urbanek@r-project.org\", comment=c(\"https://urbanek.nz\", ORCID=\"0000-0003-2297-1732\"))", - "Maintainer": "Simon Urbanek ", - "Depends": [ - "R (>= 2.9.0)" - ], - "Enhances": [ - "png" - ], - "Description": "Tools for handling 'base64' encoding. It is more flexible than the orphaned 'base64' package.", - "License": "GPL-2 | GPL-3", - "URL": "https://www.rforge.net/base64enc", - "BugReports": "https://github.com/s-u/base64enc/issues", - "NeedsCompilation": "yes", - "Repository": "CRAN" + "Requirements": [ + "R" + ] }, "bslib": { "Package": "bslib", - "Version": "0.10.0", - "Source": "Repository", - "Title": "Custom 'Bootstrap' 'Sass' Themes for 'shiny' and 'rmarkdown'", - "Authors@R": "c( person(\"Carson\", \"Sievert\", , \"carson@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-4958-2844\")), person(\"Joe\", \"Cheng\", , \"joe@posit.co\", role = \"aut\"), person(\"Garrick\", \"Aden-Buie\", , \"garrick@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0002-7111-0077\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")), person(, \"Bootstrap contributors\", role = \"ctb\", comment = \"Bootstrap library\"), person(, \"Twitter, Inc\", role = \"cph\", comment = \"Bootstrap library\"), person(\"Javi\", \"Aguilar\", role = c(\"ctb\", \"cph\"), comment = \"Bootstrap colorpicker library\"), person(\"Thomas\", \"Park\", role = c(\"ctb\", \"cph\"), comment = \"Bootswatch library\"), person(, \"PayPal\", role = c(\"ctb\", \"cph\"), comment = \"Bootstrap accessibility plugin\") )", - "Description": "Simplifies custom 'CSS' styling of both 'shiny' and 'rmarkdown' via 'Bootstrap' 'Sass'. Supports 'Bootstrap' 3, 4 and 5 as well as their various 'Bootswatch' themes. An interactive widget is also provided for previewing themes in real time.", - "License": "MIT + file LICENSE", - "URL": "https://rstudio.github.io/bslib/, https://github.com/rstudio/bslib", - "BugReports": "https://github.com/rstudio/bslib/issues", - "Depends": [ - "R (>= 2.10)" - ], - "Imports": [ + "Version": "0.12.0", + "Source": "Repository", + "Requirements": [ "base64enc", "cachem", - "fastmap (>= 1.1.1)", + "fastmap", "grDevices", - "htmltools (>= 0.5.8)", - "jquerylib (>= 0.1.3)", + "htmltools", + "jquerylib", "jsonlite", "lifecycle", - "memoise (>= 2.0.1)", + "memoise", "mime", + "R", "rlang", - "sass (>= 0.4.9)" - ], - "Suggests": [ - "brand.yml", - "bsicons", - "curl", - "fontawesome", - "future", - "ggplot2", - "knitr", - "lattice", - "magrittr", - "rappdirs", - "rmarkdown (>= 2.7)", - "shiny (>= 1.11.1)", - "testthat", - "thematic", - "tools", - "utils", - "withr", - "yaml" - ], - "Config/Needs/deploy": "BH, chiflights22, colourpicker, commonmark, cpp11, cpsievert/chiflights22, cpsievert/histoslider, dplyr, DT, ggplot2, ggridges, gt, hexbin, histoslider, htmlwidgets, lattice, leaflet, lubridate, markdown, modelr, plotly, reactable, reshape2, rprojroot, rsconnect, rstudio/shiny, scales, styler, tibble", - "Config/Needs/routine": "chromote, desc, renv", - "Config/Needs/website": "brio, crosstalk, dplyr, DT, ggplot2, glue, htmlwidgets, leaflet, lorem, palmerpenguins, plotly, purrr, rprojroot, rstudio/htmltools, scales, stringr, tidyr, webshot2", - "Config/testthat/edition": "3", - "Config/testthat/parallel": "true", - "Config/testthat/start-first": "zzzz-bs-sass, fonts, zzz-precompile, theme-*, rmd-*", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3", - "Collate": "'accordion.R' 'breakpoints.R' 'bs-current-theme.R' 'bs-dependencies.R' 'bs-global.R' 'bs-remove.R' 'bs-theme-layers.R' 'bs-theme-preset-bootswatch.R' 'bs-theme-preset-brand.R' 'bs-theme-preset-builtin.R' 'bs-theme-preset.R' 'utils.R' 'bs-theme-preview.R' 'bs-theme-update.R' 'bs-theme.R' 'bslib-package.R' 'buttons.R' 'card.R' 'deprecated.R' 'files.R' 'fill.R' 'imports.R' 'input-code-editor.R' 'input-dark-mode.R' 'input-submit.R' 'input-switch.R' 'layout.R' 'nav-items.R' 'nav-update.R' 'navbar_options.R' 'navs-legacy.R' 'navs.R' 'onLoad.R' 'page.R' 'popover.R' 'precompiled.R' 'print.R' 'shiny-devmode.R' 'sidebar.R' 'staticimports.R' 'toast.R' 'tooltip.R' 'utils-deps.R' 'utils-shiny.R' 'utils-tags.R' 'value-box.R' 'version-default.R' 'versions.R'", - "NeedsCompilation": "no", - "Author": "Carson Sievert [aut, cre] (ORCID: ), Joe Cheng [aut], Garrick Aden-Buie [aut] (ORCID: ), Posit Software, PBC [cph, fnd], Bootstrap contributors [ctb] (Bootstrap library), Twitter, Inc [cph] (Bootstrap library), Javi Aguilar [ctb, cph] (Bootstrap colorpicker library), Thomas Park [ctb, cph] (Bootswatch library), PayPal [ctb, cph] (Bootstrap accessibility plugin)", - "Maintainer": "Carson Sievert ", - "Repository": "CRAN" + "sass" + ] }, "cachem": { "Package": "cachem", "Version": "1.1.0", "Source": "Repository", - "Title": "Cache R Objects with Automatic Pruning", - "Description": "Key-value stores with automatic pruning. Caches can limit either their total size or the age of the oldest object (or both), automatically pruning objects to maintain the constraints.", - "Authors@R": "c( person(\"Winston\", \"Chang\", , \"winston@posit.co\", c(\"aut\", \"cre\")), person(family = \"Posit Software, PBC\", role = c(\"cph\", \"fnd\")))", - "License": "MIT + file LICENSE", - "Encoding": "UTF-8", - "ByteCompile": "true", - "URL": "https://cachem.r-lib.org/, https://github.com/r-lib/cachem", - "Imports": [ - "rlang", - "fastmap (>= 1.2.0)" - ], - "Suggests": [ - "testthat" - ], - "RoxygenNote": "7.2.3", - "Config/Needs/routine": "lobstr", - "Config/Needs/website": "pkgdown", - "NeedsCompilation": "yes", - "Author": "Winston Chang [aut, cre], Posit Software, PBC [cph, fnd]", - "Maintainer": "Winston Chang ", - "Repository": "CRAN" + "Requirements": [ + "fastmap", + "rlang" + ] }, "callr": { "Package": "callr", - "Version": "3.7.6", - "Source": "Repository", - "Title": "Call R from R", - "Authors@R": "c( person(\"Gábor\", \"Csárdi\", , \"csardi.gabor@gmail.com\", role = c(\"aut\", \"cre\", \"cph\"), comment = c(ORCID = \"0000-0001-7098-9676\")), person(\"Winston\", \"Chang\", role = \"aut\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")), person(\"Ascent Digital Services\", role = c(\"cph\", \"fnd\")) )", - "Description": "It is sometimes useful to perform a computation in a separate R process, without affecting the current R process at all. This packages does exactly that.", - "License": "MIT + file LICENSE", - "URL": "https://callr.r-lib.org, https://github.com/r-lib/callr", - "BugReports": "https://github.com/r-lib/callr/issues", - "Depends": [ - "R (>= 3.4)" - ], - "Imports": [ - "processx (>= 3.6.1)", + "Version": "3.8.0", + "Source": "Repository", + "Requirements": [ + "otel", + "processx", + "R", "R6", "utils" - ], - "Suggests": [ - "asciicast (>= 2.3.1)", - "cli (>= 1.1.0)", - "mockery", - "ps", - "rprojroot", - "spelling", - "testthat (>= 3.2.0)", - "withr (>= 2.3.0)" - ], - "Config/Needs/website": "r-lib/asciicast, glue, htmlwidgets, igraph, tibble, tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Encoding": "UTF-8", - "Language": "en-US", - "RoxygenNote": "7.3.1.9000", - "NeedsCompilation": "no", - "Author": "Gábor Csárdi [aut, cre, cph] (), Winston Chang [aut], Posit Software, PBC [cph, fnd], Ascent Digital Services [cph, fnd]", - "Maintainer": "Gábor Csárdi ", - "Repository": "CRAN" + ] }, "cli": { "Package": "cli", - "Version": "3.6.5", - "Source": "Repository", - "Title": "Helpers for Developing Command Line Interfaces", - "Authors@R": "c( person(\"Gábor\", \"Csárdi\", , \"gabor@posit.co\", role = c(\"aut\", \"cre\")), person(\"Hadley\", \"Wickham\", role = \"ctb\"), person(\"Kirill\", \"Müller\", role = \"ctb\"), person(\"Salim\", \"Brüggemann\", , \"salim-b@pm.me\", role = \"ctb\", comment = c(ORCID = \"0000-0002-5329-5987\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "A suite of tools to build attractive command line interfaces ('CLIs'), from semantic elements: headings, lists, alerts, paragraphs, etc. Supports custom themes via a 'CSS'-like language. It also contains a number of lower level 'CLI' elements: rules, boxes, trees, and 'Unicode' symbols with 'ASCII' alternatives. It support ANSI colors and text styles as well.", - "License": "MIT + file LICENSE", - "URL": "https://cli.r-lib.org, https://github.com/r-lib/cli", - "BugReports": "https://github.com/r-lib/cli/issues", - "Depends": [ - "R (>= 3.4)" - ], - "Imports": [ + "Version": "3.6.6", + "Source": "Repository", + "Requirements": [ + "R", "utils" - ], - "Suggests": [ - "callr", - "covr", - "crayon", - "digest", - "glue (>= 1.6.0)", - "grDevices", - "htmltools", - "htmlwidgets", - "knitr", - "methods", - "processx", - "ps (>= 1.3.4.9000)", - "rlang (>= 1.0.2.9003)", - "rmarkdown", - "rprojroot", - "rstudioapi", - "testthat (>= 3.2.0)", - "tibble", - "whoami", - "withr" - ], - "Config/Needs/website": "r-lib/asciicast, bench, brio, cpp11, decor, desc, fansi, prettyunits, sessioninfo, tidyverse/tidytemplate, usethis, vctrs", - "Config/testthat/edition": "3", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2", - "NeedsCompilation": "yes", - "Author": "Gábor Csárdi [aut, cre], Hadley Wickham [ctb], Kirill Müller [ctb], Salim Brüggemann [ctb] (), Posit Software, PBC [cph, fnd]", - "Maintainer": "Gábor Csárdi ", - "Repository": "CRAN" + ] }, "codetools": { "Package": "codetools", "Version": "0.2-20", "Source": "Repository", - "Priority": "recommended", - "Author": "Luke Tierney ", - "Description": "Code analysis tools for R.", - "Title": "Code Analysis Tools for R", - "Depends": [ - "R (>= 2.1)" - ], - "Maintainer": "Luke Tierney ", - "URL": "https://gitlab.com/luke-tierney/codetools", - "License": "GPL", - "NeedsCompilation": "no", - "Repository": "CRAN" + "Requirements": [ + "R" + ] }, "cpp11": { "Package": "cpp11", - "Version": "0.5.3", + "Version": "0.5.5", "Source": "Repository", - "Title": "A C++11 Interface for R's C Interface", - "Authors@R": "c( person(\"Davis\", \"Vaughan\", email = \"davis@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0003-4777-038X\")), person(\"Jim\",\"Hester\", role = \"aut\", comment = c(ORCID = \"0000-0002-2739-7082\")), person(\"Romain\", \"François\", role = \"aut\", comment = c(ORCID = \"0000-0002-2444-4226\")), person(\"Benjamin\", \"Kietzman\", role = \"ctb\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "Provides a header only, C++11 interface to R's C interface. Compared to other approaches 'cpp11' strives to be safe against long jumps from the C API as well as C++ exceptions, conform to normal R function semantics and supports interaction with 'ALTREP' vectors.", - "License": "MIT + file LICENSE", - "URL": "https://cpp11.r-lib.org, https://github.com/r-lib/cpp11", - "BugReports": "https://github.com/r-lib/cpp11/issues", - "Depends": [ - "R (>= 4.0.0)" - ], - "Suggests": [ - "bench", - "brio", - "callr", - "cli", - "covr", - "decor", - "desc", - "ggplot2", - "glue", - "knitr", - "lobstr", - "mockery", - "progress", - "rmarkdown", - "scales", - "Rcpp", - "testthat (>= 3.2.0)", - "tibble", - "utils", - "vctrs", - "withr" - ], - "VignetteBuilder": "knitr", - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Config/Needs/cpp11/cpp_register": "brio, cli, decor, desc, glue, tibble, vctrs", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2", - "NeedsCompilation": "no", - "Author": "Davis Vaughan [aut, cre] (ORCID: ), Jim Hester [aut] (ORCID: ), Romain François [aut] (ORCID: ), Benjamin Kietzman [ctb], Posit Software, PBC [cph, fnd]", - "Maintainer": "Davis Vaughan ", - "Repository": "CRAN" + "Requirements": [ + "R" + ] }, "crosstalk": { "Package": "crosstalk", "Version": "1.2.2", "Source": "Repository", - "Type": "Package", - "Title": "Inter-Widget Interactivity for HTML Widgets", - "Authors@R": "c( person(\"Joe\", \"Cheng\", , \"joe@posit.co\", role = \"aut\"), person(\"Carson\", \"Sievert\", , \"carson@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-4958-2844\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")), person(, \"jQuery Foundation\", role = \"cph\", comment = \"jQuery library and jQuery UI library\"), person(, \"jQuery contributors\", role = c(\"ctb\", \"cph\"), comment = \"jQuery library; authors listed in inst/www/shared/jquery-AUTHORS.txt\"), person(\"Mark\", \"Otto\", role = \"ctb\", comment = \"Bootstrap library\"), person(\"Jacob\", \"Thornton\", role = \"ctb\", comment = \"Bootstrap library\"), person(, \"Bootstrap contributors\", role = \"ctb\", comment = \"Bootstrap library\"), person(, \"Twitter, Inc\", role = \"cph\", comment = \"Bootstrap library\"), person(\"Brian\", \"Reavis\", role = c(\"ctb\", \"cph\"), comment = \"selectize.js library\"), person(\"Kristopher Michael\", \"Kowal\", role = c(\"ctb\", \"cph\"), comment = \"es5-shim library\"), person(, \"es5-shim contributors\", role = c(\"ctb\", \"cph\"), comment = \"es5-shim library\"), person(\"Denis\", \"Ineshin\", role = c(\"ctb\", \"cph\"), comment = \"ion.rangeSlider library\"), person(\"Sami\", \"Samhuri\", role = c(\"ctb\", \"cph\"), comment = \"Javascript strftime library\") )", - "Description": "Provides building blocks for allowing HTML widgets to communicate with each other, with Shiny or without (i.e. static .html files). Currently supports linked brushing and filtering.", - "License": "MIT + file LICENSE", - "URL": "https://rstudio.github.io/crosstalk/, https://github.com/rstudio/crosstalk", - "BugReports": "https://github.com/rstudio/crosstalk/issues", - "Imports": [ - "htmltools (>= 0.3.6)", + "Requirements": [ + "htmltools", "jsonlite", "lazyeval", "R6" - ], - "Suggests": [ - "bslib", - "ggplot2", - "sass", - "shiny", - "testthat (>= 2.1.0)" - ], - "Config/Needs/website": "jcheng5/d3scatter, DT, leaflet, rmarkdown", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2", - "NeedsCompilation": "no", - "Author": "Joe Cheng [aut], Carson Sievert [aut, cre] (ORCID: ), Posit Software, PBC [cph, fnd], jQuery Foundation [cph] (jQuery library and jQuery UI library), jQuery contributors [ctb, cph] (jQuery library; authors listed in inst/www/shared/jquery-AUTHORS.txt), Mark Otto [ctb] (Bootstrap library), Jacob Thornton [ctb] (Bootstrap library), Bootstrap contributors [ctb] (Bootstrap library), Twitter, Inc [cph] (Bootstrap library), Brian Reavis [ctb, cph] (selectize.js library), Kristopher Michael Kowal [ctb, cph] (es5-shim library), es5-shim contributors [ctb, cph] (es5-shim library), Denis Ineshin [ctb, cph] (ion.rangeSlider library), Sami Samhuri [ctb, cph] (Javascript strftime library)", - "Maintainer": "Carson Sievert ", - "Repository": "CRAN" + ] }, "curl": { "Package": "curl", - "Version": "7.0.0", - "Source": "Repository", - "Type": "Package", - "Title": "A Modern and Flexible Web Client for R", - "Authors@R": "c( person(\"Jeroen\", \"Ooms\", role = c(\"aut\", \"cre\"), email = \"jeroenooms@gmail.com\", comment = c(ORCID = \"0000-0002-4035-0289\")), person(\"Hadley\", \"Wickham\", role = \"ctb\"), person(\"Posit Software, PBC\", role = \"cph\"))", - "Description": "Bindings to 'libcurl' for performing fully configurable HTTP/FTP requests where responses can be processed in memory, on disk, or streaming via the callback or connection interfaces. Some knowledge of 'libcurl' is recommended; for a more-user-friendly web client see the 'httr2' package which builds on this package with http specific tools and logic.", - "License": "MIT + file LICENSE", - "SystemRequirements": "libcurl (>= 7.73): libcurl-devel (rpm) or libcurl4-openssl-dev (deb)", - "URL": "https://jeroen.r-universe.dev/curl", - "BugReports": "https://github.com/jeroen/curl/issues", - "Suggests": [ - "spelling", - "testthat (>= 1.0.0)", - "knitr", - "jsonlite", - "later", - "rmarkdown", - "httpuv (>= 1.4.4)", - "webutils" - ], - "VignetteBuilder": "knitr", - "Depends": [ - "R (>= 3.0.0)" - ], - "RoxygenNote": "7.3.2", - "Encoding": "UTF-8", - "Language": "en-US", - "NeedsCompilation": "yes", - "Author": "Jeroen Ooms [aut, cre] (ORCID: ), Hadley Wickham [ctb], Posit Software, PBC [cph]", - "Maintainer": "Jeroen Ooms ", - "Repository": "CRAN" + "Version": "8.1.0", + "Source": "Repository", + "Requirements": [ + "R" + ] }, "data.table": { "Package": "data.table", - "Version": "1.18.2.1", + "Version": "1.18.6.1", "Source": "Repository", - "Title": "Extension of `data.frame`", - "Depends": [ - "R (>= 3.4.0)" - ], - "Imports": [ - "methods" - ], - "Suggests": [ - "bit64 (>= 4.0.0)", - "bit (>= 4.0.4)", - "R.utils (>= 2.13.0)", - "xts", - "zoo (>= 1.8-1)", - "yaml", - "knitr", - "markdown" - ], - "Description": "Fast aggregation of large data (e.g. 100GB in RAM), fast ordered joins, fast add/modify/delete of columns by group using no copies at all, list columns, friendly and fast character-separated-value read/write. Offers a natural and flexible syntax, for faster development.", - "License": "MPL-2.0 | file LICENSE", - "URL": "https://r-datatable.com, https://Rdatatable.gitlab.io/data.table, https://github.com/Rdatatable/data.table", - "BugReports": "https://github.com/Rdatatable/data.table/issues", - "VignetteBuilder": "knitr", - "Encoding": "UTF-8", - "ByteCompile": "TRUE", - "Authors@R": "c( person(\"Tyson\",\"Barrett\", role=c(\"aut\",\"cre\"), email=\"t.barrett88@gmail.com\", comment = c(ORCID=\"0000-0002-2137-1391\")), person(\"Matt\",\"Dowle\", role=\"aut\", email=\"mattjdowle@gmail.com\"), person(\"Arun\",\"Srinivasan\", role=\"aut\", email=\"asrini@pm.me\"), person(\"Jan\",\"Gorecki\", role=\"aut\", email=\"j.gorecki@wit.edu.pl\"), person(\"Michael\",\"Chirico\", role=\"aut\", email=\"michaelchirico4@gmail.com\", comment = c(ORCID=\"0000-0003-0787-087X\")), person(\"Toby\",\"Hocking\", role=\"aut\", email=\"toby.hocking@r-project.org\", comment = c(ORCID=\"0000-0002-3146-0865\")), person(\"Benjamin\",\"Schwendinger\",role=\"aut\", comment = c(ORCID=\"0000-0003-3315-8114\")), person(\"Ivan\", \"Krylov\", role=\"aut\", email=\"ikrylov@disroot.org\", comment = c(ORCID=\"0000-0002-0172-3812\")), person(\"Pasha\",\"Stetsenko\", role=\"ctb\"), person(\"Tom\",\"Short\", role=\"ctb\"), person(\"Steve\",\"Lianoglou\", role=\"ctb\"), person(\"Eduard\",\"Antonyan\", role=\"ctb\"), person(\"Markus\",\"Bonsch\", role=\"ctb\"), person(\"Hugh\",\"Parsonage\", role=\"ctb\"), person(\"Scott\",\"Ritchie\", role=\"ctb\"), person(\"Kun\",\"Ren\", role=\"ctb\"), person(\"Xianying\",\"Tan\", role=\"ctb\"), person(\"Rick\",\"Saporta\", role=\"ctb\"), person(\"Otto\",\"Seiskari\", role=\"ctb\"), person(\"Xianghui\",\"Dong\", role=\"ctb\"), person(\"Michel\",\"Lang\", role=\"ctb\"), person(\"Watal\",\"Iwasaki\", role=\"ctb\"), person(\"Seth\",\"Wenchel\", role=\"ctb\"), person(\"Karl\",\"Broman\", role=\"ctb\"), person(\"Tobias\",\"Schmidt\", role=\"ctb\"), person(\"David\",\"Arenburg\", role=\"ctb\"), person(\"Ethan\",\"Smith\", role=\"ctb\"), person(\"Francois\",\"Cocquemas\", role=\"ctb\"), person(\"Matthieu\",\"Gomez\", role=\"ctb\"), person(\"Philippe\",\"Chataignon\", role=\"ctb\"), person(\"Nello\",\"Blaser\", role=\"ctb\"), person(\"Dmitry\",\"Selivanov\", role=\"ctb\"), person(\"Andrey\",\"Riabushenko\", role=\"ctb\"), person(\"Cheng\",\"Lee\", role=\"ctb\"), person(\"Declan\",\"Groves\", role=\"ctb\"), person(\"Daniel\",\"Possenriede\", role=\"ctb\"), person(\"Felipe\",\"Parages\", role=\"ctb\"), person(\"Denes\",\"Toth\", role=\"ctb\"), person(\"Mus\",\"Yaramaz-David\", role=\"ctb\"), person(\"Ayappan\",\"Perumal\", role=\"ctb\"), person(\"James\",\"Sams\", role=\"ctb\"), person(\"Martin\",\"Morgan\", role=\"ctb\"), person(\"Michael\",\"Quinn\", role=\"ctb\"), person(given=\"@javrucebo\", role=\"ctb\", comment=\"GitHub user\"), person(\"Marc\",\"Halperin\", role=\"ctb\"), person(\"Roy\",\"Storey\", role=\"ctb\"), person(\"Manish\",\"Saraswat\", role=\"ctb\"), person(\"Morgan\",\"Jacob\", role=\"ctb\"), person(\"Michael\",\"Schubmehl\", role=\"ctb\"), person(\"Davis\",\"Vaughan\", role=\"ctb\"), person(\"Leonardo\",\"Silvestri\", role=\"ctb\"), person(\"Jim\",\"Hester\", role=\"ctb\"), person(\"Anthony\",\"Damico\", role=\"ctb\"), person(\"Sebastian\",\"Freundt\", role=\"ctb\"), person(\"David\",\"Simons\", role=\"ctb\"), person(\"Elliott\",\"Sales de Andrade\", role=\"ctb\"), person(\"Cole\",\"Miller\", role=\"ctb\"), person(\"Jens Peder\",\"Meldgaard\", role=\"ctb\"), person(\"Vaclav\",\"Tlapak\", role=\"ctb\"), person(\"Kevin\",\"Ushey\", role=\"ctb\"), person(\"Dirk\",\"Eddelbuettel\", role=\"ctb\"), person(\"Tony\",\"Fischetti\", role=\"ctb\"), person(\"Ofek\",\"Shilon\", role=\"ctb\"), person(\"Vadim\",\"Khotilovich\", role=\"ctb\"), person(\"Hadley\",\"Wickham\", role=\"ctb\"), person(\"Bennet\",\"Becker\", role=\"ctb\"), person(\"Kyle\",\"Haynes\", role=\"ctb\"), person(\"Boniface Christian\",\"Kamgang\", role=\"ctb\"), person(\"Olivier\",\"Delmarcell\", role=\"ctb\"), person(\"Josh\",\"O'Brien\", role=\"ctb\"), person(\"Dereck\",\"de Mezquita\", role=\"ctb\"), person(\"Michael\",\"Czekanski\", role=\"ctb\"), person(\"Dmitry\", \"Shemetov\", role=\"ctb\"), person(\"Nitish\", \"Jha\", role=\"ctb\"), person(\"Joshua\", \"Wu\", role=\"ctb\"), person(\"Iago\", \"Giné-Vázquez\", role=\"ctb\"), person(\"Anirban\", \"Chetia\", role=\"ctb\"), person(\"Doris\", \"Amoakohene\", role=\"ctb\"), person(\"Angel\", \"Feliz\", role=\"ctb\"), person(\"Michael\",\"Young\", role=\"ctb\"), person(\"Mark\", \"Seeto\", role=\"ctb\"), person(\"Philippe\", \"Grosjean\", role=\"ctb\"), person(\"Vincent\", \"Runge\", role=\"ctb\"), person(\"Christian\", \"Wia\", role=\"ctb\"), person(\"Elise\", \"Maigné\", role=\"ctb\"), person(\"Vincent\", \"Rocher\", role=\"ctb\"), person(\"Vijay\", \"Lulla\", role=\"ctb\"), person(\"Aljaž\", \"Sluga\", role=\"ctb\"), person(\"Bill\", \"Evans\", role=\"ctb\"), person(\"Reino\", \"Bruner\", role=\"ctb\"), person(given=\"@badasahog\", role=\"ctb\", comment=\"GitHub user\"), person(\"Vinit\", \"Thakur\", role=\"ctb\"), person(\"Mukul\", \"Kumar\", role=\"ctb\"), person(\"Ildikó\", \"Czeller\", role=\"ctb\"), person(\"Manmita\", \"Das\", role=\"ctb\") )", - "NeedsCompilation": "yes", - "Author": "Tyson Barrett [aut, cre] (ORCID: ), Matt Dowle [aut], Arun Srinivasan [aut], Jan Gorecki [aut], Michael Chirico [aut] (ORCID: ), Toby Hocking [aut] (ORCID: ), Benjamin Schwendinger [aut] (ORCID: ), Ivan Krylov [aut] (ORCID: ), Pasha Stetsenko [ctb], Tom Short [ctb], Steve Lianoglou [ctb], Eduard Antonyan [ctb], Markus Bonsch [ctb], Hugh Parsonage [ctb], Scott Ritchie [ctb], Kun Ren [ctb], Xianying Tan [ctb], Rick Saporta [ctb], Otto Seiskari [ctb], Xianghui Dong [ctb], Michel Lang [ctb], Watal Iwasaki [ctb], Seth Wenchel [ctb], Karl Broman [ctb], Tobias Schmidt [ctb], David Arenburg [ctb], Ethan Smith [ctb], Francois Cocquemas [ctb], Matthieu Gomez [ctb], Philippe Chataignon [ctb], Nello Blaser [ctb], Dmitry Selivanov [ctb], Andrey Riabushenko [ctb], Cheng Lee [ctb], Declan Groves [ctb], Daniel Possenriede [ctb], Felipe Parages [ctb], Denes Toth [ctb], Mus Yaramaz-David [ctb], Ayappan Perumal [ctb], James Sams [ctb], Martin Morgan [ctb], Michael Quinn [ctb], @javrucebo [ctb] (GitHub user), Marc Halperin [ctb], Roy Storey [ctb], Manish Saraswat [ctb], Morgan Jacob [ctb], Michael Schubmehl [ctb], Davis Vaughan [ctb], Leonardo Silvestri [ctb], Jim Hester [ctb], Anthony Damico [ctb], Sebastian Freundt [ctb], David Simons [ctb], Elliott Sales de Andrade [ctb], Cole Miller [ctb], Jens Peder Meldgaard [ctb], Vaclav Tlapak [ctb], Kevin Ushey [ctb], Dirk Eddelbuettel [ctb], Tony Fischetti [ctb], Ofek Shilon [ctb], Vadim Khotilovich [ctb], Hadley Wickham [ctb], Bennet Becker [ctb], Kyle Haynes [ctb], Boniface Christian Kamgang [ctb], Olivier Delmarcell [ctb], Josh O'Brien [ctb], Dereck de Mezquita [ctb], Michael Czekanski [ctb], Dmitry Shemetov [ctb], Nitish Jha [ctb], Joshua Wu [ctb], Iago Giné-Vázquez [ctb], Anirban Chetia [ctb], Doris Amoakohene [ctb], Angel Feliz [ctb], Michael Young [ctb], Mark Seeto [ctb], Philippe Grosjean [ctb], Vincent Runge [ctb], Christian Wia [ctb], Elise Maigné [ctb], Vincent Rocher [ctb], Vijay Lulla [ctb], Aljaž Sluga [ctb], Bill Evans [ctb], Reino Bruner [ctb], @badasahog [ctb] (GitHub user), Vinit Thakur [ctb], Mukul Kumar [ctb], Ildikó Czeller [ctb], Manmita Das [ctb]", - "Maintainer": "Tyson Barrett ", - "Repository": "CRAN" + "Requirements": [ + "methods", + "R" + ] }, "desc": { "Package": "desc", "Version": "1.4.3", "Source": "Repository", - "Title": "Manipulate DESCRIPTION Files", - "Authors@R": "c( person(\"Gábor\", \"Csárdi\", , \"csardi.gabor@gmail.com\", role = c(\"aut\", \"cre\")), person(\"Kirill\", \"Müller\", role = \"aut\"), person(\"Jim\", \"Hester\", , \"james.f.hester@gmail.com\", role = \"aut\"), person(\"Maëlle\", \"Salmon\", role = \"ctb\", comment = c(ORCID = \"0000-0002-2815-0399\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Maintainer": "Gábor Csárdi ", - "Description": "Tools to read, write, create, and manipulate DESCRIPTION files. It is intended for packages that create or manipulate other packages.", - "License": "MIT + file LICENSE", - "URL": "https://desc.r-lib.org/, https://github.com/r-lib/desc", - "BugReports": "https://github.com/r-lib/desc/issues", - "Depends": [ - "R (>= 3.4)" - ], - "Imports": [ + "Requirements": [ "cli", + "R", "R6", "utils" - ], - "Suggests": [ - "callr", - "covr", - "gh", - "spelling", - "testthat", - "whoami", - "withr" - ], - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Encoding": "UTF-8", - "Language": "en-US", - "RoxygenNote": "7.2.3", - "Collate": "'assertions.R' 'authors-at-r.R' 'built.R' 'classes.R' 'collate.R' 'constants.R' 'deps.R' 'desc-package.R' 'description.R' 'encoding.R' 'find-package-root.R' 'latex.R' 'non-oo-api.R' 'package-archives.R' 'read.R' 'remotes.R' 'str.R' 'syntax_checks.R' 'urls.R' 'utils.R' 'validate.R' 'version.R'", - "NeedsCompilation": "no", - "Author": "Gábor Csárdi [aut, cre], Kirill Müller [aut], Jim Hester [aut], Maëlle Salmon [ctb] (), Posit Software, PBC [cph, fnd]", - "Repository": "CRAN" + ] }, "digest": { "Package": "digest", "Version": "0.6.39", "Source": "Repository", - "Authors@R": "c(person(\"Dirk\", \"Eddelbuettel\", role = c(\"aut\", \"cre\"), email = \"edd@debian.org\", comment = c(ORCID = \"0000-0001-6419-907X\")), person(\"Antoine\", \"Lucas\", role=\"ctb\", comment = c(ORCID = \"0000-0002-8059-9767\")), person(\"Jarek\", \"Tuszynski\", role=\"ctb\"), person(\"Henrik\", \"Bengtsson\", role=\"ctb\", comment = c(ORCID = \"0000-0002-7579-5165\")), person(\"Simon\", \"Urbanek\", role=\"ctb\", comment = c(ORCID = \"0000-0003-2297-1732\")), person(\"Mario\", \"Frasca\", role=\"ctb\"), person(\"Bryan\", \"Lewis\", role=\"ctb\"), person(\"Murray\", \"Stokely\", role=\"ctb\"), person(\"Hannes\", \"Muehleisen\", role=\"ctb\", comment = c(ORCID = \"0000-0001-8552-0029\")), person(\"Duncan\", \"Murdoch\", role=\"ctb\"), person(\"Jim\", \"Hester\", role=\"ctb\", comment = c(ORCID = \"0000-0002-2739-7082\")), person(\"Wush\", \"Wu\", role=\"ctb\", comment = c(ORCID = \"0000-0001-5180-0567\")), person(\"Qiang\", \"Kou\", role=\"ctb\", comment = c(ORCID = \"0000-0001-6786-5453\")), person(\"Thierry\", \"Onkelinx\", role=\"ctb\", comment = c(ORCID = \"0000-0001-8804-4216\")), person(\"Michel\", \"Lang\", role=\"ctb\", comment = c(ORCID = \"0000-0001-9754-0393\")), person(\"Viliam\", \"Simko\", role=\"ctb\"), person(\"Kurt\", \"Hornik\", role=\"ctb\", comment = c(ORCID = \"0000-0003-4198-9911\")), person(\"Radford\", \"Neal\", role=\"ctb\", comment = c(ORCID = \"0000-0002-2473-3407\")), person(\"Kendon\", \"Bell\", role=\"ctb\", comment = c(ORCID = \"0000-0002-9093-8312\")), person(\"Matthew\", \"de Queljoe\", role=\"ctb\"), person(\"Dmitry\", \"Selivanov\", role=\"ctb\", comment = c(ORCID = \"0000-0003-0492-6647\")), person(\"Ion\", \"Suruceanu\", role=\"ctb\", comment = c(ORCID = \"0009-0005-6446-4909\")), person(\"Bill\", \"Denney\", role=\"ctb\", comment = c(ORCID = \"0000-0002-5759-428X\")), person(\"Dirk\", \"Schumacher\", role=\"ctb\"), person(\"András\", \"Svraka\", role=\"ctb\", comment = c(ORCID = \"0009-0008-8480-1329\")), person(\"Sergey\", \"Fedorov\", role=\"ctb\", comment = c(ORCID = \"0000-0002-5970-7233\")), person(\"Will\", \"Landau\", role=\"ctb\", comment = c(ORCID = \"0000-0003-1878-3253\")), person(\"Floris\", \"Vanderhaeghe\", role=\"ctb\", comment = c(ORCID = \"0000-0002-6378-6229\")), person(\"Kevin\", \"Tappe\", role=\"ctb\"), person(\"Harris\", \"McGehee\", role=\"ctb\"), person(\"Tim\", \"Mastny\", role=\"ctb\"), person(\"Aaron\", \"Peikert\", role=\"ctb\", comment = c(ORCID = \"0000-0001-7813-818X\")), person(\"Mark\", \"van der Loo\", role=\"ctb\", comment = c(ORCID = \"0000-0002-9807-4686\")), person(\"Chris\", \"Muir\", role=\"ctb\", comment = c(ORCID = \"0000-0003-2555-3878\")), person(\"Moritz\", \"Beller\", role=\"ctb\", comment = c(ORCID = \"0000-0003-4852-0526\")), person(\"Sebastian\", \"Campbell\", role=\"ctb\", comment = c(ORCID = \"0009-0000-5948-4503\")), person(\"Winston\", \"Chang\", role=\"ctb\", comment = c(ORCID = \"0000-0002-1576-2126\")), person(\"Dean\", \"Attali\", role=\"ctb\", comment = c(ORCID = \"0000-0002-5645-3493\")), person(\"Michael\", \"Chirico\", role=\"ctb\", comment = c(ORCID = \"0000-0003-0787-087X\")), person(\"Kevin\", \"Ushey\", role=\"ctb\", comment = c(ORCID = \"0000-0003-2880-7407\")), person(\"Carl\", \"Pearson\", role=\"ctb\", comment = c(ORCID = \"0000-0003-0701-7860\")))", - "Date": "2025-11-19", - "Title": "Create Compact Hash Digests of R Objects", - "Description": "Implementation of a function 'digest()' for the creation of hash digests of arbitrary R objects (using the 'md5', 'sha-1', 'sha-256', 'crc32', 'xxhash', 'murmurhash', 'spookyhash', 'blake3', 'crc32c', 'xxh3_64', and 'xxh3_128' algorithms) permitting easy comparison of R language objects, as well as functions such as 'hmac()' to create hash-based message authentication code. Please note that this package is not meant to be deployed for cryptographic purposes for which more comprehensive (and widely tested) libraries such as 'OpenSSL' should be used.", - "URL": "https://github.com/eddelbuettel/digest, https://eddelbuettel.github.io/digest/, https://dirk.eddelbuettel.com/code/digest.html", - "BugReports": "https://github.com/eddelbuettel/digest/issues", - "Depends": [ - "R (>= 3.3.0)" - ], - "Imports": [ + "Requirements": [ + "R", "utils" - ], - "License": "GPL (>= 2)", - "Suggests": [ - "tinytest", - "simplermarkdown", - "rbenchmark" - ], - "VignetteBuilder": "simplermarkdown", - "Encoding": "UTF-8", - "NeedsCompilation": "yes", - "Author": "Dirk Eddelbuettel [aut, cre] (ORCID: ), Antoine Lucas [ctb] (ORCID: ), Jarek Tuszynski [ctb], Henrik Bengtsson [ctb] (ORCID: ), Simon Urbanek [ctb] (ORCID: ), Mario Frasca [ctb], Bryan Lewis [ctb], Murray Stokely [ctb], Hannes Muehleisen [ctb] (ORCID: ), Duncan Murdoch [ctb], Jim Hester [ctb] (ORCID: ), Wush Wu [ctb] (ORCID: ), Qiang Kou [ctb] (ORCID: ), Thierry Onkelinx [ctb] (ORCID: ), Michel Lang [ctb] (ORCID: ), Viliam Simko [ctb], Kurt Hornik [ctb] (ORCID: ), Radford Neal [ctb] (ORCID: ), Kendon Bell [ctb] (ORCID: ), Matthew de Queljoe [ctb], Dmitry Selivanov [ctb] (ORCID: ), Ion Suruceanu [ctb] (ORCID: ), Bill Denney [ctb] (ORCID: ), Dirk Schumacher [ctb], András Svraka [ctb] (ORCID: ), Sergey Fedorov [ctb] (ORCID: ), Will Landau [ctb] (ORCID: ), Floris Vanderhaeghe [ctb] (ORCID: ), Kevin Tappe [ctb], Harris McGehee [ctb], Tim Mastny [ctb], Aaron Peikert [ctb] (ORCID: ), Mark van der Loo [ctb] (ORCID: ), Chris Muir [ctb] (ORCID: ), Moritz Beller [ctb] (ORCID: ), Sebastian Campbell [ctb] (ORCID: ), Winston Chang [ctb] (ORCID: ), Dean Attali [ctb] (ORCID: ), Michael Chirico [ctb] (ORCID: ), Kevin Ushey [ctb] (ORCID: ), Carl Pearson [ctb] (ORCID: )", - "Maintainer": "Dirk Eddelbuettel ", - "Repository": "CRAN" + ] }, "dplyr": { "Package": "dplyr", - "Version": "1.2.0", + "Version": "1.2.1", "Source": "Repository", - "Type": "Package", - "Title": "A Grammar of Data Manipulation", - "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0003-4757-117X\")), person(\"Romain\", \"François\", role = \"aut\", comment = c(ORCID = \"0000-0002-2444-4226\")), person(\"Lionel\", \"Henry\", role = \"aut\"), person(\"Kirill\", \"Müller\", role = \"aut\", comment = c(ORCID = \"0000-0002-1416-3412\")), person(\"Davis\", \"Vaughan\", , \"davis@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0003-4777-038X\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "A fast, consistent tool for working with data frame like objects, both in memory and out of memory.", - "License": "MIT + file LICENSE", - "URL": "https://dplyr.tidyverse.org, https://github.com/tidyverse/dplyr", - "BugReports": "https://github.com/tidyverse/dplyr/issues", - "Depends": [ - "R (>= 4.1.0)" - ], - "Imports": [ - "cli (>= 3.6.2)", + "Requirements": [ + "cli", "generics", - "glue (>= 1.3.2)", - "lifecycle (>= 1.0.5)", - "magrittr (>= 1.5)", + "glue", + "lifecycle", + "magrittr", "methods", - "pillar (>= 1.9.0)", + "pillar", + "R", "R6", - "rlang (>= 1.1.7)", - "tibble (>= 3.2.0)", - "tidyselect (>= 1.2.0)", + "rlang", + "tibble", + "tidyselect", "utils", - "vctrs (>= 0.7.1)" - ], - "Suggests": [ - "broom", - "covr", - "DBI", - "dbplyr (>= 2.2.1)", - "ggplot2", - "knitr", - "Lahman", - "lobstr", - "nycflights13", - "purrr", - "rmarkdown", - "RSQLite", - "stringi (>= 1.7.6)", - "testthat (>= 3.1.5)", - "tidyr (>= 1.3.0)", - "withr" - ], - "VignetteBuilder": "knitr", - "Config/build/compilation-database": "true", - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Encoding": "UTF-8", - "LazyData": "true", - "RoxygenNote": "7.3.3", - "NeedsCompilation": "yes", - "Author": "Hadley Wickham [aut, cre] (ORCID: ), Romain François [aut] (ORCID: ), Lionel Henry [aut], Kirill Müller [aut] (ORCID: ), Davis Vaughan [aut] (ORCID: ), Posit Software, PBC [cph, fnd]", - "Maintainer": "Hadley Wickham ", - "Repository": "CRAN" + "vctrs" + ] }, "evaluate": { "Package": "evaluate", "Version": "1.0.5", "Source": "Repository", - "Type": "Package", - "Title": "Parsing and Evaluation Tools that Provide More Details than the Default", - "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = c(\"aut\", \"cre\")), person(\"Yihui\", \"Xie\", role = \"aut\", comment = c(ORCID = \"0000-0003-0645-5666\")), person(\"Michael\", \"Lawrence\", role = \"ctb\"), person(\"Thomas\", \"Kluyver\", role = \"ctb\"), person(\"Jeroen\", \"Ooms\", role = \"ctb\"), person(\"Barret\", \"Schloerke\", role = \"ctb\"), person(\"Adam\", \"Ryczkowski\", role = \"ctb\"), person(\"Hiroaki\", \"Yutani\", role = \"ctb\"), person(\"Michel\", \"Lang\", role = \"ctb\"), person(\"Karolis\", \"Koncevičius\", role = \"ctb\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "Parsing and evaluation tools that make it easy to recreate the command line behaviour of R.", - "License": "MIT + file LICENSE", - "URL": "https://evaluate.r-lib.org/, https://github.com/r-lib/evaluate", - "BugReports": "https://github.com/r-lib/evaluate/issues", - "Depends": [ - "R (>= 3.6.0)" - ], - "Suggests": [ - "callr", - "covr", - "ggplot2 (>= 3.3.6)", - "lattice", - "methods", - "pkgload", - "ragg (>= 1.4.0)", - "rlang (>= 1.1.5)", - "knitr", - "testthat (>= 3.0.0)", - "withr" - ], - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2", - "NeedsCompilation": "no", - "Author": "Hadley Wickham [aut, cre], Yihui Xie [aut] (ORCID: ), Michael Lawrence [ctb], Thomas Kluyver [ctb], Jeroen Ooms [ctb], Barret Schloerke [ctb], Adam Ryczkowski [ctb], Hiroaki Yutani [ctb], Michel Lang [ctb], Karolis Koncevičius [ctb], Posit Software, PBC [cph, fnd]", - "Maintainer": "Hadley Wickham ", - "Repository": "CRAN" + "Requirements": [ + "R" + ] }, "farver": { "Package": "farver", "Version": "2.1.2", "Source": "Repository", - "Type": "Package", - "Title": "High Performance Colour Space Manipulation", - "Authors@R": "c( person(\"Thomas Lin\", \"Pedersen\", , \"thomas.pedersen@posit.co\", role = c(\"cre\", \"aut\"), comment = c(ORCID = \"0000-0002-5147-4711\")), person(\"Berendea\", \"Nicolae\", role = \"aut\", comment = \"Author of the ColorSpace C++ library\"), person(\"Romain\", \"François\", , \"romain@purrple.cat\", role = \"aut\", comment = c(ORCID = \"0000-0002-2444-4226\")), person(\"Posit, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "The encoding of colour can be handled in many different ways, using different colour spaces. As different colour spaces have different uses, efficient conversion between these representations are important. The 'farver' package provides a set of functions that gives access to very fast colour space conversion and comparisons implemented in C++, and offers speed improvements over the 'convertColor' function in the 'grDevices' package.", - "License": "MIT + file LICENSE", - "URL": "https://farver.data-imaginist.com, https://github.com/thomasp85/farver", - "BugReports": "https://github.com/thomasp85/farver/issues", - "Suggests": [ - "covr", - "testthat (>= 3.0.0)" - ], - "Config/testthat/edition": "3", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.1", - "NeedsCompilation": "yes", - "Author": "Thomas Lin Pedersen [cre, aut] (), Berendea Nicolae [aut] (Author of the ColorSpace C++ library), Romain François [aut] (), Posit, PBC [cph, fnd]", - "Maintainer": "Thomas Lin Pedersen ", - "Repository": "CRAN" + "Requirements": [] }, "fastmap": { "Package": "fastmap", "Version": "1.2.0", "Source": "Repository", - "Title": "Fast Data Structures", - "Authors@R": "c( person(\"Winston\", \"Chang\", email = \"winston@posit.co\", role = c(\"aut\", \"cre\")), person(given = \"Posit Software, PBC\", role = c(\"cph\", \"fnd\")), person(given = \"Tessil\", role = \"cph\", comment = \"hopscotch_map library\") )", - "Description": "Fast implementation of data structures, including a key-value store, stack, and queue. Environments are commonly used as key-value stores in R, but every time a new key is used, it is added to R's global symbol table, causing a small amount of memory leakage. This can be problematic in cases where many different keys are used. Fastmap avoids this memory leak issue by implementing the map using data structures in C++.", - "License": "MIT + file LICENSE", - "Encoding": "UTF-8", - "RoxygenNote": "7.2.3", - "Suggests": [ - "testthat (>= 2.1.1)" - ], - "URL": "https://r-lib.github.io/fastmap/, https://github.com/r-lib/fastmap", - "BugReports": "https://github.com/r-lib/fastmap/issues", - "NeedsCompilation": "yes", - "Author": "Winston Chang [aut, cre], Posit Software, PBC [cph, fnd], Tessil [cph] (hopscotch_map library)", - "Maintainer": "Winston Chang ", - "Repository": "CRAN" + "Requirements": [] }, "fontBitstreamVera": { "Package": "fontBitstreamVera", "Version": "0.1.1", "Source": "Repository", - "Title": "Fonts with 'Bitstream Vera Fonts' License", - "Authors@R": "c( person(\"Lionel\", \"Henry\", , \"lionel.hry@gmail.com\", c(\"cre\", \"aut\")), person(\"Bitstream\", role = \"cph\"))", - "Description": "Provides fonts licensed under the 'Bitstream Vera Fonts' license for the 'fontquiver' package.", - "Depends": [ - "R (>= 3.0.0)" - ], - "License": "file LICENCE", - "Encoding": "UTF-8", - "LazyData": "true", - "RoxygenNote": "5.0.1", - "NeedsCompilation": "no", - "Author": "Lionel Henry [cre, aut], Bitstream [cph]", - "Maintainer": "Lionel Henry ", - "License_is_FOSS": "yes", - "Repository": "CRAN" + "Requirements": [ + "R" + ] }, "fontLiberation": { "Package": "fontLiberation", "Version": "0.1.0", "Source": "Repository", - "Title": "Liberation Fonts", - "Authors@R": "c( person(\"Lionel\", \"Henry\", , \"lionel@rstudio.com\", \"cre\"), person(\"Pravin Satpute\", role = \"aut\"), person(\"Steve Matteson\", role = \"aut\"), person(\"Red Hat, Inc\", role = \"cph\"), person(\"Google Corporation\", role = \"cph\"))", - "Description": "A placeholder for the Liberation fontset intended for the `fontquiver` package. This fontset covers the 12 combinations of families (sans, serif, mono) and faces (plain, bold, italic, bold italic) supported in R graphics devices.", - "Depends": [ - "R (>= 3.0)" - ], - "License": "file LICENSE", - "Encoding": "UTF-8", - "LazyData": "true", - "RoxygenNote": "5.0.1", - "NeedsCompilation": "no", - "Author": "Lionel Henry [cre], Pravin Satpute [aut], Steve Matteson [aut], Red Hat, Inc [cph], Google Corporation [cph]", - "Maintainer": "Lionel Henry ", - "Repository": "CRAN", - "License_is_FOSS": "yes" + "Requirements": [ + "R" + ] }, "fontawesome": { "Package": "fontawesome", "Version": "0.5.3", "Source": "Repository", - "Type": "Package", - "Title": "Easily Work with 'Font Awesome' Icons", - "Description": "Easily and flexibly insert 'Font Awesome' icons into 'R Markdown' documents and 'Shiny' apps. These icons can be inserted into HTML content through inline 'SVG' tags or 'i' tags. There is also a utility function for exporting 'Font Awesome' icons as 'PNG' images for those situations where raster graphics are needed.", - "Authors@R": "c( person(\"Richard\", \"Iannone\", , \"rich@posit.co\", c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0003-3925-190X\")), person(\"Christophe\", \"Dervieux\", , \"cderv@posit.co\", role = \"ctb\", comment = c(ORCID = \"0000-0003-4474-2498\")), person(\"Winston\", \"Chang\", , \"winston@posit.co\", role = \"ctb\"), person(\"Dave\", \"Gandy\", role = c(\"ctb\", \"cph\"), comment = \"Font-Awesome font\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "License": "MIT + file LICENSE", - "URL": "https://github.com/rstudio/fontawesome, https://rstudio.github.io/fontawesome/", - "BugReports": "https://github.com/rstudio/fontawesome/issues", - "Encoding": "UTF-8", - "ByteCompile": "true", - "RoxygenNote": "7.3.2", - "Depends": [ - "R (>= 3.3.0)" - ], - "Imports": [ - "rlang (>= 1.0.6)", - "htmltools (>= 0.5.1.1)" - ], - "Suggests": [ - "covr", - "dplyr (>= 1.0.8)", - "gt (>= 0.9.0)", - "knitr (>= 1.31)", - "testthat (>= 3.0.0)", - "rsvg" - ], - "Config/testthat/edition": "3", - "NeedsCompilation": "no", - "Author": "Richard Iannone [aut, cre] (), Christophe Dervieux [ctb] (), Winston Chang [ctb], Dave Gandy [ctb, cph] (Font-Awesome font), Posit Software, PBC [cph, fnd]", - "Maintainer": "Richard Iannone ", - "Repository": "CRAN" + "Requirements": [ + "htmltools", + "R", + "rlang" + ] }, "fontquiver": { "Package": "fontquiver", "Version": "0.2.1", "Source": "Repository", - "Title": "Set of Installed Fonts", - "Authors@R": "c( person(\"Lionel\", \"Henry\", , \"lionel@rstudio.com\", c(\"cre\", \"aut\")), person(\"RStudio\", role = \"cph\"), person(\"George Douros\", role = \"cph\", comment = \"Symbola font\"))", - "Description": "Provides a set of fonts with permissive licences. This is useful when you want to avoid system fonts to make sure your outputs are reproducible.", - "Depends": [ - "R (>= 3.0.0)" - ], - "Imports": [ - "fontBitstreamVera (>= 0.1.0)", - "fontLiberation (>= 0.1.0)" - ], - "Suggests": [ - "testthat", - "htmltools" - ], - "License": "GPL-3 | file LICENSE", - "Encoding": "UTF-8", - "LazyData": "true", - "RoxygenNote": "5.0.1", - "Collate": "'font-getters.R' 'fontset.R' 'fontset-bitstream-vera.R' 'fontset-dejavu.R' 'fontset-liberation.R' 'fontset-symbola.R' 'html-dependency.R' 'utils.R'", - "NeedsCompilation": "no", - "Author": "Lionel Henry [cre, aut], RStudio [cph], George Douros [cph] (Symbola font)", - "Maintainer": "Lionel Henry ", - "Repository": "CRAN" + "Requirements": [ + "fontBitstreamVera", + "fontLiberation", + "R" + ] }, "fs": { "Package": "fs", - "Version": "1.6.6", - "Source": "Repository", - "Title": "Cross-Platform File System Operations Based on 'libuv'", - "Authors@R": "c( person(\"Jim\", \"Hester\", role = \"aut\"), person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\"), person(\"Gábor\", \"Csárdi\", , \"csardi.gabor@gmail.com\", role = c(\"aut\", \"cre\")), person(\"libuv project contributors\", role = \"cph\", comment = \"libuv library\"), person(\"Joyent, Inc. and other Node contributors\", role = \"cph\", comment = \"libuv library\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "A cross-platform interface to file system operations, built on top of the 'libuv' C library.", - "License": "MIT + file LICENSE", - "URL": "https://fs.r-lib.org, https://github.com/r-lib/fs", - "BugReports": "https://github.com/r-lib/fs/issues", - "Depends": [ - "R (>= 3.6)" - ], - "Imports": [ - "methods" - ], - "Suggests": [ - "covr", - "crayon", - "knitr", - "pillar (>= 1.0.0)", - "rmarkdown", - "spelling", - "testthat (>= 3.0.0)", - "tibble (>= 1.1.0)", - "vctrs (>= 0.3.0)", - "withr" - ], - "VignetteBuilder": "knitr", - "ByteCompile": "true", - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Copyright": "file COPYRIGHTS", - "Encoding": "UTF-8", - "Language": "en-US", - "RoxygenNote": "7.2.3", - "SystemRequirements": "GNU make", - "NeedsCompilation": "yes", - "Author": "Jim Hester [aut], Hadley Wickham [aut], Gábor Csárdi [aut, cre], libuv project contributors [cph] (libuv library), Joyent, Inc. and other Node contributors [cph] (libuv library), Posit Software, PBC [cph, fnd]", - "Maintainer": "Gábor Csárdi ", - "Repository": "CRAN" + "Version": "2.1.0", + "Source": "Repository", + "Requirements": [ + "methods", + "R" + ] }, "gdtools": { "Package": "gdtools", - "Version": "0.5.1", - "Source": "Repository", - "Title": "Font Metrics and Font Management Utilities for R Graphics", - "Authors@R": "c( person(\"David\", \"Gohel\", , \"david.gohel@ardata.fr\", role = c(\"aut\", \"cre\")), person(\"Hadley\", \"Wickham\", , \"hadley@rstudio.com\", role = \"aut\"), person(\"Lionel\", \"Henry\", , \"lionel@rstudio.com\", role = \"aut\"), person(\"Jeroen\", \"Ooms\", , \"jeroen@berkeley.edu\", role = \"aut\", comment = c(ORCID = \"0000-0002-4035-0289\")), person(\"Yixuan\", \"Qiu\", role = \"ctb\"), person(\"R Core Team\", role = \"cph\", comment = \"Cairo code from X11 device\"), person(\"ArData\", role = \"cph\"), person(\"RStudio\", role = \"cph\") )", - "Description": "Compute text metrics (width, ascent, descent) using 'Cairo' and 'FreeType', independently of the active graphic device. Font lookup is delegated to 'systemfonts'. Additional utilities let users register 'Google Fonts' or bundled 'Liberation' fonts, check font availability, and assemble 'htmlDependency' objects so that fonts are correctly embedded in 'Shiny' applications, 'R Markdown' documents, and 'htmlwidgets' outputs such as 'ggiraph'.", - "License": "GPL-3 | file LICENSE", - "URL": "https://davidgohel.github.io/gdtools/", - "BugReports": "https://github.com/davidgohel/gdtools/issues", - "Depends": [ - "R (>= 4.0.0)" - ], - "Imports": [ - "fontquiver (>= 0.2.0)", + "Version": "0.5.2", + "Source": "Repository", + "Requirements": [ + "fontquiver", "htmltools", - "Rcpp (>= 0.12.12)", - "systemfonts (>= 1.3.1)", + "R", + "Rcpp", + "systemfonts", "tools" - ], - "Suggests": [ - "curl", - "gfonts", - "methods", - "testthat" - ], - "LinkingTo": [ - "Rcpp" - ], - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3", - "SystemRequirements": "cairo, freetype2, fontconfig", - "NeedsCompilation": "yes", - "Author": "David Gohel [aut, cre], Hadley Wickham [aut], Lionel Henry [aut], Jeroen Ooms [aut] (ORCID: ), Yixuan Qiu [ctb], R Core Team [cph] (Cairo code from X11 device), ArData [cph], RStudio [cph]", - "Maintainer": "David Gohel ", - "Repository": "CRAN" + ] }, "generics": { "Package": "generics", "Version": "0.1.4", "Source": "Repository", - "Title": "Common S3 Generics not Provided by Base R Methods Related to Model Fitting", - "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0003-4757-117X\")), person(\"Max\", \"Kuhn\", , \"max@posit.co\", role = \"aut\"), person(\"Davis\", \"Vaughan\", , \"davis@posit.co\", role = \"aut\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"https://ror.org/03wc8by49\")) )", - "Description": "In order to reduce potential package dependencies and conflicts, generics provides a number of commonly used S3 generics.", - "License": "MIT + file LICENSE", - "URL": "https://generics.r-lib.org, https://github.com/r-lib/generics", - "BugReports": "https://github.com/r-lib/generics/issues", - "Depends": [ - "R (>= 3.6)" - ], - "Imports": [ - "methods" - ], - "Suggests": [ - "covr", - "pkgload", - "testthat (>= 3.0.0)", - "tibble", - "withr" - ], - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2", - "NeedsCompilation": "no", - "Author": "Hadley Wickham [aut, cre] (ORCID: ), Max Kuhn [aut], Davis Vaughan [aut], Posit Software, PBC [cph, fnd] (ROR: )", - "Maintainer": "Hadley Wickham ", - "Repository": "CRAN" + "Requirements": [ + "methods", + "R" + ] }, "ggplot2": { "Package": "ggplot2", - "Version": "4.0.2", - "Source": "Repository", - "Title": "Create Elegant Data Visualisations Using the Grammar of Graphics", - "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0003-4757-117X\")), person(\"Winston\", \"Chang\", role = \"aut\", comment = c(ORCID = \"0000-0002-1576-2126\")), person(\"Lionel\", \"Henry\", role = \"aut\"), person(\"Thomas Lin\", \"Pedersen\", , \"thomas.pedersen@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-5147-4711\")), person(\"Kohske\", \"Takahashi\", role = \"aut\"), person(\"Claus\", \"Wilke\", role = \"aut\", comment = c(ORCID = \"0000-0002-7470-9261\")), person(\"Kara\", \"Woo\", role = \"aut\", comment = c(ORCID = \"0000-0002-5125-4188\")), person(\"Hiroaki\", \"Yutani\", role = \"aut\", comment = c(ORCID = \"0000-0002-3385-7233\")), person(\"Dewey\", \"Dunnington\", role = \"aut\", comment = c(ORCID = \"0000-0002-9415-4582\")), person(\"Teun\", \"van den Brand\", role = \"aut\", comment = c(ORCID = \"0000-0002-9335-7468\")), person(\"Posit, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", - "Description": "A system for 'declaratively' creating graphics, based on \"The Grammar of Graphics\". You provide the data, tell 'ggplot2' how to map variables to aesthetics, what graphical primitives to use, and it takes care of the details.", - "License": "MIT + file LICENSE", - "URL": "https://ggplot2.tidyverse.org, https://github.com/tidyverse/ggplot2", - "BugReports": "https://github.com/tidyverse/ggplot2/issues", - "Depends": [ - "R (>= 4.1)" - ], - "Imports": [ + "Version": "4.0.3", + "Source": "Repository", + "Requirements": [ "cli", "grDevices", "grid", - "gtable (>= 0.3.6)", + "gtable", "isoband", - "lifecycle (> 1.0.1)", - "rlang (>= 1.1.0)", + "lifecycle", + "R", + "rlang", "S7", - "scales (>= 1.4.0)", + "scales", "stats", - "vctrs (>= 0.6.0)", - "withr (>= 2.5.0)" - ], - "Suggests": [ - "broom", - "covr", - "dplyr", - "ggplot2movies", - "hexbin", - "Hmisc", - "hms", - "knitr", - "mapproj", - "maps", - "MASS", - "mgcv", - "multcomp", - "munsell", - "nlme", - "profvis", - "quantreg", - "quarto", - "ragg (>= 1.2.6)", - "RColorBrewer", - "roxygen2", - "rpart", - "sf (>= 0.7-3)", - "svglite (>= 2.1.2)", - "testthat (>= 3.1.5)", - "tibble", - "vdiffr (>= 1.0.6)", - "xml2" - ], - "Enhances": [ - "sp" - ], - "VignetteBuilder": "quarto", - "Config/Needs/website": "ggtext, tidyr, forcats, tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Config/usethis/last-upkeep": "2025-04-23", - "Encoding": "UTF-8", - "LazyData": "true", - "RoxygenNote": "7.3.3", - "Collate": "'ggproto.R' 'ggplot-global.R' 'aaa-.R' 'aes-colour-fill-alpha.R' 'aes-evaluation.R' 'aes-group-order.R' 'aes-linetype-size-shape.R' 'aes-position.R' 'all-classes.R' 'compat-plyr.R' 'utilities.R' 'aes.R' 'annotation-borders.R' 'utilities-checks.R' 'legend-draw.R' 'geom-.R' 'annotation-custom.R' 'annotation-logticks.R' 'scale-type.R' 'layer.R' 'make-constructor.R' 'geom-polygon.R' 'geom-map.R' 'annotation-map.R' 'geom-raster.R' 'annotation-raster.R' 'annotation.R' 'autolayer.R' 'autoplot.R' 'axis-secondary.R' 'backports.R' 'bench.R' 'bin.R' 'coord-.R' 'coord-cartesian-.R' 'coord-fixed.R' 'coord-flip.R' 'coord-map.R' 'coord-munch.R' 'coord-polar.R' 'coord-quickmap.R' 'coord-radial.R' 'coord-sf.R' 'coord-transform.R' 'data.R' 'docs_layer.R' 'facet-.R' 'facet-grid-.R' 'facet-null.R' 'facet-wrap.R' 'fortify-map.R' 'fortify-models.R' 'fortify-spatial.R' 'fortify.R' 'stat-.R' 'geom-abline.R' 'geom-rect.R' 'geom-bar.R' 'geom-tile.R' 'geom-bin2d.R' 'geom-blank.R' 'geom-boxplot.R' 'geom-col.R' 'geom-path.R' 'geom-contour.R' 'geom-point.R' 'geom-count.R' 'geom-crossbar.R' 'geom-segment.R' 'geom-curve.R' 'geom-defaults.R' 'geom-ribbon.R' 'geom-density.R' 'geom-density2d.R' 'geom-dotplot.R' 'geom-errorbar.R' 'geom-freqpoly.R' 'geom-function.R' 'geom-hex.R' 'geom-histogram.R' 'geom-hline.R' 'geom-jitter.R' 'geom-label.R' 'geom-linerange.R' 'geom-pointrange.R' 'geom-quantile.R' 'geom-rug.R' 'geom-sf.R' 'geom-smooth.R' 'geom-spoke.R' 'geom-text.R' 'geom-violin.R' 'geom-vline.R' 'ggplot2-package.R' 'grob-absolute.R' 'grob-dotstack.R' 'grob-null.R' 'grouping.R' 'properties.R' 'margins.R' 'theme-elements.R' 'guide-.R' 'guide-axis.R' 'guide-axis-logticks.R' 'guide-axis-stack.R' 'guide-axis-theta.R' 'guide-legend.R' 'guide-bins.R' 'guide-colorbar.R' 'guide-colorsteps.R' 'guide-custom.R' 'guide-none.R' 'guide-old.R' 'guides-.R' 'guides-grid.R' 'hexbin.R' 'import-standalone-obj-type.R' 'import-standalone-types-check.R' 'labeller.R' 'labels.R' 'layer-sf.R' 'layout.R' 'limits.R' 'performance.R' 'plot-build.R' 'plot-construction.R' 'plot-last.R' 'plot.R' 'position-.R' 'position-collide.R' 'position-dodge.R' 'position-dodge2.R' 'position-identity.R' 'position-jitter.R' 'position-jitterdodge.R' 'position-nudge.R' 'position-stack.R' 'quick-plot.R' 'reshape-add-margins.R' 'save.R' 'scale-.R' 'scale-alpha.R' 'scale-binned.R' 'scale-brewer.R' 'scale-colour.R' 'scale-continuous.R' 'scale-date.R' 'scale-discrete-.R' 'scale-expansion.R' 'scale-gradient.R' 'scale-grey.R' 'scale-hue.R' 'scale-identity.R' 'scale-linetype.R' 'scale-linewidth.R' 'scale-manual.R' 'scale-shape.R' 'scale-size.R' 'scale-steps.R' 'scale-view.R' 'scale-viridis.R' 'scales-.R' 'stat-align.R' 'stat-bin.R' 'stat-summary-2d.R' 'stat-bin2d.R' 'stat-bindot.R' 'stat-binhex.R' 'stat-boxplot.R' 'stat-connect.R' 'stat-contour.R' 'stat-count.R' 'stat-density-2d.R' 'stat-density.R' 'stat-ecdf.R' 'stat-ellipse.R' 'stat-function.R' 'stat-identity.R' 'stat-manual.R' 'stat-qq-line.R' 'stat-qq.R' 'stat-quantilemethods.R' 'stat-sf-coordinates.R' 'stat-sf.R' 'stat-smooth-methods.R' 'stat-smooth.R' 'stat-sum.R' 'stat-summary-bin.R' 'stat-summary-hex.R' 'stat-summary.R' 'stat-unique.R' 'stat-ydensity.R' 'summarise-plot.R' 'summary.R' 'theme.R' 'theme-defaults.R' 'theme-current.R' 'theme-sub.R' 'utilities-break.R' 'utilities-grid.R' 'utilities-help.R' 'utilities-patterns.R' 'utilities-resolution.R' 'utilities-tidy-eval.R' 'zxx.R' 'zzz.R'", - "NeedsCompilation": "no", - "Author": "Hadley Wickham [aut] (ORCID: ), Winston Chang [aut] (ORCID: ), Lionel Henry [aut], Thomas Lin Pedersen [aut, cre] (ORCID: ), Kohske Takahashi [aut], Claus Wilke [aut] (ORCID: ), Kara Woo [aut] (ORCID: ), Hiroaki Yutani [aut] (ORCID: ), Dewey Dunnington [aut] (ORCID: ), Teun van den Brand [aut] (ORCID: ), Posit, PBC [cph, fnd] (ROR: )", - "Maintainer": "Thomas Lin Pedersen ", - "Repository": "CRAN" + "vctrs", + "withr" + ] }, "glue": { "Package": "glue", - "Version": "1.8.0", - "Source": "Repository", - "Title": "Interpreted String Literals", - "Authors@R": "c( person(\"Jim\", \"Hester\", role = \"aut\", comment = c(ORCID = \"0000-0002-2739-7082\")), person(\"Jennifer\", \"Bryan\", , \"jenny@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-6983-2759\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "An implementation of interpreted string literals, inspired by Python's Literal String Interpolation and Docstrings and Julia's Triple-Quoted String Literals .", - "License": "MIT + file LICENSE", - "URL": "https://glue.tidyverse.org/, https://github.com/tidyverse/glue", - "BugReports": "https://github.com/tidyverse/glue/issues", - "Depends": [ - "R (>= 3.6)" - ], - "Imports": [ - "methods" - ], - "Suggests": [ - "crayon", - "DBI (>= 1.2.0)", - "dplyr", - "knitr", - "magrittr", - "rlang", - "rmarkdown", - "RSQLite", - "testthat (>= 3.2.0)", - "vctrs (>= 0.3.0)", - "waldo (>= 0.5.3)", - "withr" - ], - "VignetteBuilder": "knitr", - "ByteCompile": "true", - "Config/Needs/website": "bench, forcats, ggbeeswarm, ggplot2, R.utils, rprintf, tidyr, tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2", - "NeedsCompilation": "yes", - "Author": "Jim Hester [aut] (), Jennifer Bryan [aut, cre] (), Posit Software, PBC [cph, fnd]", - "Maintainer": "Jennifer Bryan ", - "Repository": "CRAN" + "Version": "1.8.1", + "Source": "Repository", + "Requirements": [ + "methods", + "R" + ] }, "gridExtra": { "Package": "gridExtra", - "Version": "2.3", - "Source": "Repository", - "Authors@R": "c(person(\"Baptiste\", \"Auguie\", email = \"baptiste.auguie@gmail.com\", role = c(\"aut\", \"cre\")), person(\"Anton\", \"Antonov\", email = \"tonytonov@gmail.com\", role = c(\"ctb\")))", - "License": "GPL (>= 2)", - "Title": "Miscellaneous Functions for \"Grid\" Graphics", - "Type": "Package", - "Description": "Provides a number of user-level functions to work with \"grid\" graphics, notably to arrange multiple grid-based plots on a page, and draw tables.", - "VignetteBuilder": "knitr", - "Imports": [ - "gtable", - "grid", - "grDevices", + "Version": "2.3.1", + "Source": "Repository", + "Requirements": [ "graphics", + "grDevices", + "grid", + "gtable", "utils" - ], - "Suggests": [ - "ggplot2", - "egg", - "lattice", - "knitr", - "testthat" - ], - "RoxygenNote": "6.0.1", - "NeedsCompilation": "no", - "Author": "Baptiste Auguie [aut, cre], Anton Antonov [ctb]", - "Maintainer": "Baptiste Auguie ", - "Repository": "CRAN" + ] }, "gridGraphics": { "Package": "gridGraphics", "Version": "0.5-1", "Source": "Repository", - "Title": "Redraw Base Graphics Using 'grid' Graphics", - "Authors@R": "c(person(\"Paul\", \"Murrell\", role = c(\"cre\", \"aut\"), email = \"paul@stat.auckland.ac.nz\"), person(\"Zhijian\", \"Wen\", role = \"aut\", email = \"jwen246@aucklanduni.ac.nz\"))", - "Description": "Functions to convert a page of plots drawn with the 'graphics' package into identical output drawn with the 'grid' package. The result looks like the original 'graphics'-based plot, but consists of 'grid' grobs and viewports that can then be manipulated with 'grid' functions (e.g., edit grobs and revisit viewports).", - "Depends": [ - "grid", - "graphics" - ], - "Imports": [ - "grDevices" - ], - "Suggests": [ - "magick (>= 1.3)", - "pdftools (>= 1.6)" - ], - "License": "GPL (>= 2)", - "URL": "https://github.com/pmur002/gridgraphics", - "NeedsCompilation": "no", - "Author": "Paul Murrell [cre, aut], Zhijian Wen [aut]", - "Maintainer": "Paul Murrell ", - "Repository": "CRAN" + "Requirements": [ + "graphics", + "grDevices", + "grid" + ] }, "gtable": { "Package": "gtable", "Version": "0.3.6", "Source": "Repository", - "Title": "Arrange 'Grobs' in Tables", - "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\"), person(\"Thomas Lin\", \"Pedersen\", , \"thomas.pedersen@posit.co\", role = c(\"aut\", \"cre\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "Tools to make it easier to work with \"tables\" of 'grobs'. The 'gtable' package defines a 'gtable' grob class that specifies a grid along with a list of grobs and their placement in the grid. Further the package makes it easy to manipulate and combine 'gtable' objects so that complex compositions can be built up sequentially.", - "License": "MIT + file LICENSE", - "URL": "https://gtable.r-lib.org, https://github.com/r-lib/gtable", - "BugReports": "https://github.com/r-lib/gtable/issues", - "Depends": [ - "R (>= 4.0)" - ], - "Imports": [ + "Requirements": [ "cli", "glue", "grid", "lifecycle", - "rlang (>= 1.1.0)", + "R", + "rlang", "stats" - ], - "Suggests": [ - "covr", - "ggplot2", - "knitr", - "profvis", - "rmarkdown", - "testthat (>= 3.0.0)" - ], - "VignetteBuilder": "knitr", - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Config/usethis/last-upkeep": "2024-10-25", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2", - "NeedsCompilation": "no", - "Author": "Hadley Wickham [aut], Thomas Lin Pedersen [aut, cre], Posit Software, PBC [cph, fnd]", - "Maintainer": "Thomas Lin Pedersen ", - "Repository": "CRAN" + ] }, "highr": { "Package": "highr", - "Version": "0.11", - "Source": "Repository", - "Type": "Package", - "Title": "Syntax Highlighting for R Source Code", - "Authors@R": "c( person(\"Yihui\", \"Xie\", role = c(\"aut\", \"cre\"), email = \"xie@yihui.name\", comment = c(ORCID = \"0000-0003-0645-5666\")), person(\"Yixuan\", \"Qiu\", role = \"aut\"), person(\"Christopher\", \"Gandrud\", role = \"ctb\"), person(\"Qiang\", \"Li\", role = \"ctb\") )", - "Description": "Provides syntax highlighting for R source code. Currently it supports LaTeX and HTML output. Source code of other languages is supported via Andre Simon's highlight package ().", - "Depends": [ - "R (>= 3.3.0)" - ], - "Imports": [ - "xfun (>= 0.18)" - ], - "Suggests": [ - "knitr", - "markdown", - "testit" - ], - "License": "GPL", - "URL": "https://github.com/yihui/highr", - "BugReports": "https://github.com/yihui/highr/issues", - "VignetteBuilder": "knitr", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.1", - "NeedsCompilation": "no", - "Author": "Yihui Xie [aut, cre] (), Yixuan Qiu [aut], Christopher Gandrud [ctb], Qiang Li [ctb]", - "Maintainer": "Yihui Xie ", - "Repository": "CRAN" + "Version": "0.12", + "Source": "Repository", + "Requirements": [ + "R", + "xfun" + ] }, "htmltools": { "Package": "htmltools", "Version": "0.5.9", "Source": "Repository", - "Type": "Package", - "Title": "Tools for HTML", - "Authors@R": "c( person(\"Joe\", \"Cheng\", , \"joe@posit.co\", role = \"aut\"), person(\"Carson\", \"Sievert\", , \"carson@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-4958-2844\")), person(\"Barret\", \"Schloerke\", , \"barret@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0001-9986-114X\")), person(\"Winston\", \"Chang\", , \"winston@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0002-1576-2126\")), person(\"Yihui\", \"Xie\", , \"yihui@posit.co\", role = \"aut\"), person(\"Jeff\", \"Allen\", role = \"aut\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "Tools for HTML generation and output.", - "License": "GPL (>= 2)", - "URL": "https://github.com/rstudio/htmltools, https://rstudio.github.io/htmltools/", - "BugReports": "https://github.com/rstudio/htmltools/issues", - "Depends": [ - "R (>= 2.14.1)" - ], - "Imports": [ + "Requirements": [ "base64enc", "digest", - "fastmap (>= 1.1.0)", + "fastmap", "grDevices", - "rlang (>= 1.0.0)", + "R", + "rlang", "utils" - ], - "Suggests": [ - "Cairo", - "markdown", - "ragg", - "shiny", - "testthat", - "withr" - ], - "Enhances": [ - "knitr" - ], - "Config/Needs/check": "knitr", - "Config/Needs/website": "rstudio/quillt, bench", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3", - "Collate": "'colors.R' 'fill.R' 'html_dependency.R' 'html_escape.R' 'html_print.R' 'htmltools-package.R' 'images.R' 'known_tags.R' 'selector.R' 'staticimports.R' 'tag_query.R' 'utils.R' 'tags.R' 'template.R'", - "NeedsCompilation": "yes", - "Author": "Joe Cheng [aut], Carson Sievert [aut, cre] (ORCID: ), Barret Schloerke [aut] (ORCID: ), Winston Chang [aut] (ORCID: ), Yihui Xie [aut], Jeff Allen [aut], Posit Software, PBC [cph, fnd]", - "Maintainer": "Carson Sievert ", - "Repository": "CRAN" + ] }, "htmlwidgets": { "Package": "htmlwidgets", "Version": "1.6.4", "Source": "Repository", - "Type": "Package", - "Title": "HTML Widgets for R", - "Authors@R": "c( person(\"Ramnath\", \"Vaidyanathan\", role = c(\"aut\", \"cph\")), person(\"Yihui\", \"Xie\", role = \"aut\"), person(\"JJ\", \"Allaire\", role = \"aut\"), person(\"Joe\", \"Cheng\", , \"joe@posit.co\", role = \"aut\"), person(\"Carson\", \"Sievert\", , \"carson@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-4958-2844\")), person(\"Kenton\", \"Russell\", role = c(\"aut\", \"cph\")), person(\"Ellis\", \"Hughes\", role = \"ctb\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "A framework for creating HTML widgets that render in various contexts including the R console, 'R Markdown' documents, and 'Shiny' web applications.", - "License": "MIT + file LICENSE", - "URL": "https://github.com/ramnathv/htmlwidgets", - "BugReports": "https://github.com/ramnathv/htmlwidgets/issues", - "Imports": [ + "Requirements": [ "grDevices", - "htmltools (>= 0.5.7)", - "jsonlite (>= 0.9.16)", - "knitr (>= 1.8)", + "htmltools", + "jsonlite", + "knitr", "rmarkdown", "yaml" - ], - "Suggests": [ - "testthat" - ], - "Enhances": [ - "shiny (>= 1.1)" - ], - "VignetteBuilder": "knitr", - "Encoding": "UTF-8", - "RoxygenNote": "7.2.3", - "NeedsCompilation": "no", - "Author": "Ramnath Vaidyanathan [aut, cph], Yihui Xie [aut], JJ Allaire [aut], Joe Cheng [aut], Carson Sievert [aut, cre] (), Kenton Russell [aut, cph], Ellis Hughes [ctb], Posit Software, PBC [cph, fnd]", - "Maintainer": "Carson Sievert ", - "Repository": "CRAN" + ] }, "httr": { "Package": "httr", - "Version": "1.4.7", - "Source": "Repository", - "Title": "Tools for Working with URLs and HTTP", - "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = c(\"aut\", \"cre\")), person(\"Posit, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "Useful tools for working with HTTP organised by HTTP verbs (GET(), POST(), etc). Configuration functions make it easy to control additional request components (authenticate(), add_headers() and so on).", - "License": "MIT + file LICENSE", - "URL": "https://httr.r-lib.org/, https://github.com/r-lib/httr", - "BugReports": "https://github.com/r-lib/httr/issues", - "Depends": [ - "R (>= 3.5)" - ], - "Imports": [ - "curl (>= 5.0.2)", + "Version": "1.4.9", + "Source": "Repository", + "Requirements": [ + "curl", "jsonlite", "mime", - "openssl (>= 0.8)", + "openssl", + "R", "R6" - ], - "Suggests": [ - "covr", - "httpuv", - "jpeg", - "knitr", - "png", - "readr", - "rmarkdown", - "testthat (>= 0.8.0)", - "xml2" - ], - "VignetteBuilder": "knitr", - "Config/Needs/website": "tidyverse/tidytemplate", - "Encoding": "UTF-8", - "RoxygenNote": "7.2.3", - "NeedsCompilation": "no", - "Author": "Hadley Wickham [aut, cre], Posit, PBC [cph, fnd]", - "Maintainer": "Hadley Wickham ", - "Repository": "CRAN" + ] }, "isoband": { "Package": "isoband", "Version": "0.3.0", "Source": "Repository", - "Title": "Generate Isolines and Isobands from Regularly Spaced Elevation Grids", - "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0003-4757-117X\")), person(\"Claus O.\", \"Wilke\", , \"wilke@austin.utexas.edu\", role = \"aut\", comment = c(\"Original author\", ORCID = \"0000-0002-7470-9261\")), person(\"Thomas Lin\", \"Pedersen\", , \"thomas.pedersen@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-5147-4711\")), person(\"Posit, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", - "Description": "A fast C++ implementation to generate contour lines (isolines) and contour polygons (isobands) from regularly spaced grids containing elevation data.", - "License": "MIT + file LICENSE", - "URL": "https://isoband.r-lib.org, https://github.com/r-lib/isoband", - "BugReports": "https://github.com/r-lib/isoband/issues", - "Imports": [ + "Requirements": [ "cli", + "cpp11", "grid", "rlang", "utils" - ], - "Suggests": [ - "covr", - "ggplot2", - "knitr", - "magick", - "bench", - "rmarkdown", - "sf", - "testthat (>= 3.0.0)", - "xml2" - ], - "VignetteBuilder": "knitr", - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Config/usethis/last-upkeep": "2025-12-05", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3", - "Config/build/compilation-database": "true", - "LinkingTo": [ - "cpp11" - ], - "NeedsCompilation": "yes", - "Author": "Hadley Wickham [aut] (ORCID: ), Claus O. Wilke [aut] (Original author, ORCID: ), Thomas Lin Pedersen [aut, cre] (ORCID: ), Posit, PBC [cph, fnd] (ROR: )", - "Maintainer": "Thomas Lin Pedersen ", - "Repository": "CRAN" + ] }, "jaspBase": { "Package": "jaspBase", "Version": "0.20.4", "Source": "GitHub", - "Type": "Package", - "Title": "JASP Base", - "Author": "JASP Team", - "Maintainer": "Bruno Boutin ", - "Description": "Package contains the JASP Bayesian and Frequentist analyses. See for more details.", - "License": "GPL2+", - "Imports": [ + "Requirements": [ "cli", "codetools", - "compiler", "ggplot2", - "grDevices", - "grid", "gridExtra", "gridGraphics", "jaspGraphs", "jsonlite", "lifecycle", - "methods", "officer", "pkgbuild", "plyr", "ragg", "R6", - "Rcpp (>= 0.12.14)", + "Rcpp", "rvg", "svglite", "systemfonts", - "withr" - ], - "RoxygenNote": "7.3.3", - "Roxygen": "list(markdown = TRUE)", - "Encoding": "UTF-8", - "LinkingTo": [ - "Rcpp" - ], - "RcppModules": "jaspResults", - "NeedsCompilation": "yes", - "Suggests": [ - "testthat (>= 3.0.0)" + "withr", + "testthat" ], - "Config/testthat/edition": "3", "RemoteType": "github", "RemoteHost": "api.github.com", "RemoteUsername": "jasp-stats", "RemoteRepo": "jaspBase", - "RemoteRef": "master", - "RemoteSha": "72cb31d6c535e841006e8ebca81170f31a418e79", - "Remotes": "jasp-stats/jaspGraphs" + "RemoteSha": "a1f5d9b740c955f3d9bde8106debdf7293539316" }, "jaspGraphs": { "Package": "jaspGraphs", "Version": "0.20.0", "Source": "GitHub", - "Type": "Package", - "Title": "Custom Graphs for JASP", - "Author": "Don van den Bergh", - "Maintainer": "JASP-team ", - "Description": "Graph making functions and wrappers for JASP.", - "License": "GPL", - "Encoding": "UTF-8", - "LazyData": "true", - "Depends": [ - "R (>= 4.1.0)" - ], - "Imports": [ + "Requirements": [ + "R", "cli", - "ggplot2 (>= 3.0.0)", + "ggplot2", "gridExtra", "gtable", "htmlwidgets", @@ -1401,356 +455,114 @@ "plotly", "R6", "rlang", - "scales" - ], - "Suggests": [ + "scales", "testthat", "vdiffr" ], - "Roxygen": "list(markdown = TRUE)", - "Config/roxygen2/version": "8.1.0", "RemoteType": "github", "RemoteHost": "api.github.com", "RemoteUsername": "jasp-stats", "RemoteRepo": "jaspGraphs", - "RemoteRef": "master", "RemoteSha": "288d2751131ee6ac17362bd89d2ed7d9c0c67c7c" }, "jquerylib": { "Package": "jquerylib", "Version": "0.1.4", "Source": "Repository", - "Title": "Obtain 'jQuery' as an HTML Dependency Object", - "Authors@R": "c( person(\"Carson\", \"Sievert\", role = c(\"aut\", \"cre\"), email = \"carson@rstudio.com\", comment = c(ORCID = \"0000-0002-4958-2844\")), person(\"Joe\", \"Cheng\", role = \"aut\", email = \"joe@rstudio.com\"), person(family = \"RStudio\", role = \"cph\"), person(family = \"jQuery Foundation\", role = \"cph\", comment = \"jQuery library and jQuery UI library\"), person(family = \"jQuery contributors\", role = c(\"ctb\", \"cph\"), comment = \"jQuery library; authors listed in inst/lib/jquery-AUTHORS.txt\") )", - "Description": "Obtain any major version of 'jQuery' () and use it in any webpage generated by 'htmltools' (e.g. 'shiny', 'htmlwidgets', and 'rmarkdown'). Most R users don't need to use this package directly, but other R packages (e.g. 'shiny', 'rmarkdown', etc.) depend on this package to avoid bundling redundant copies of 'jQuery'.", - "License": "MIT + file LICENSE", - "Encoding": "UTF-8", - "Config/testthat/edition": "3", - "RoxygenNote": "7.0.2", - "Imports": [ + "Requirements": [ "htmltools" - ], - "Suggests": [ - "testthat" - ], - "NeedsCompilation": "no", - "Author": "Carson Sievert [aut, cre] (), Joe Cheng [aut], RStudio [cph], jQuery Foundation [cph] (jQuery library and jQuery UI library), jQuery contributors [ctb, cph] (jQuery library; authors listed in inst/lib/jquery-AUTHORS.txt)", - "Maintainer": "Carson Sievert ", - "Repository": "CRAN" + ] }, "jsonlite": { "Package": "jsonlite", "Version": "2.0.0", "Source": "Repository", - "Title": "A Simple and Robust JSON Parser and Generator for R", - "License": "MIT + file LICENSE", - "Depends": [ + "Requirements": [ "methods" - ], - "Authors@R": "c( person(\"Jeroen\", \"Ooms\", role = c(\"aut\", \"cre\"), email = \"jeroenooms@gmail.com\", comment = c(ORCID = \"0000-0002-4035-0289\")), person(\"Duncan\", \"Temple Lang\", role = \"ctb\"), person(\"Lloyd\", \"Hilaiel\", role = \"cph\", comment=\"author of bundled libyajl\"))", - "URL": "https://jeroen.r-universe.dev/jsonlite https://arxiv.org/abs/1403.2805", - "BugReports": "https://github.com/jeroen/jsonlite/issues", - "Maintainer": "Jeroen Ooms ", - "VignetteBuilder": "knitr, R.rsp", - "Description": "A reasonably fast JSON parser and generator, optimized for statistical data and the web. Offers simple, flexible tools for working with JSON in R, and is particularly powerful for building pipelines and interacting with a web API. The implementation is based on the mapping described in the vignette (Ooms, 2014). In addition to converting JSON data from/to R objects, 'jsonlite' contains functions to stream, validate, and prettify JSON data. The unit tests included with the package verify that all edge cases are encoded and decoded consistently for use with dynamic data in systems and applications.", - "Suggests": [ - "httr", - "vctrs", - "testthat", - "knitr", - "rmarkdown", - "R.rsp", - "sf" - ], - "RoxygenNote": "7.3.2", - "Encoding": "UTF-8", - "NeedsCompilation": "yes", - "Author": "Jeroen Ooms [aut, cre] (), Duncan Temple Lang [ctb], Lloyd Hilaiel [cph] (author of bundled libyajl)", - "Repository": "CRAN" + ] }, "knitr": { "Package": "knitr", - "Version": "1.51", - "Source": "Repository", - "Type": "Package", - "Title": "A General-Purpose Package for Dynamic Report Generation in R", - "Authors@R": "c( person(\"Yihui\", \"Xie\", role = c(\"aut\", \"cre\"), email = \"xie@yihui.name\", comment = c(ORCID = \"0000-0003-0645-5666\", URL = \"https://yihui.org\")), person(\"Abhraneel\", \"Sarma\", role = \"ctb\"), person(\"Adam\", \"Vogt\", role = \"ctb\"), person(\"Alastair\", \"Andrew\", role = \"ctb\"), person(\"Alex\", \"Zvoleff\", role = \"ctb\"), person(\"Amar\", \"Al-Zubaidi\", role = \"ctb\"), person(\"Andre\", \"Simon\", role = \"ctb\", comment = \"the CSS files under inst/themes/ were derived from the Highlight package http://www.andre-simon.de\"), person(\"Aron\", \"Atkins\", role = \"ctb\"), person(\"Aaron\", \"Wolen\", role = \"ctb\"), person(\"Ashley\", \"Manton\", role = \"ctb\"), person(\"Atsushi\", \"Yasumoto\", role = \"ctb\", comment = c(ORCID = \"0000-0002-8335-495X\")), person(\"Ben\", \"Baumer\", role = \"ctb\"), person(\"Brian\", \"Diggs\", role = \"ctb\"), person(\"Brian\", \"Zhang\", role = \"ctb\"), person(\"Bulat\", \"Yapparov\", role = \"ctb\"), person(\"Cassio\", \"Pereira\", role = \"ctb\"), person(\"Christophe\", \"Dervieux\", role = \"ctb\"), person(\"David\", \"Hall\", role = \"ctb\"), person(\"David\", \"Hugh-Jones\", role = \"ctb\"), person(\"David\", \"Robinson\", role = \"ctb\"), person(\"Doug\", \"Hemken\", role = \"ctb\"), person(\"Duncan\", \"Murdoch\", role = \"ctb\"), person(\"Elio\", \"Campitelli\", role = \"ctb\"), person(\"Ellis\", \"Hughes\", role = \"ctb\"), person(\"Emily\", \"Riederer\", role = \"ctb\"), person(\"Fabian\", \"Hirschmann\", role = \"ctb\"), person(\"Fitch\", \"Simeon\", role = \"ctb\"), person(\"Forest\", \"Fang\", role = \"ctb\"), person(c(\"Frank\", \"E\", \"Harrell\", \"Jr\"), role = \"ctb\", comment = \"the Sweavel package at inst/misc/Sweavel.sty\"), person(\"Garrick\", \"Aden-Buie\", role = \"ctb\"), person(\"Gregoire\", \"Detrez\", role = \"ctb\"), person(\"Hadley\", \"Wickham\", role = \"ctb\"), person(\"Hao\", \"Zhu\", role = \"ctb\"), person(\"Heewon\", \"Jeon\", role = \"ctb\"), person(\"Henrik\", \"Bengtsson\", role = \"ctb\"), person(\"Hiroaki\", \"Yutani\", role = \"ctb\"), person(\"Ian\", \"Lyttle\", role = \"ctb\"), person(\"Hodges\", \"Daniel\", role = \"ctb\"), person(\"Jacob\", \"Bien\", role = \"ctb\"), person(\"Jake\", \"Burkhead\", role = \"ctb\"), person(\"James\", \"Manton\", role = \"ctb\"), person(\"Jared\", \"Lander\", role = \"ctb\"), person(\"Jason\", \"Punyon\", role = \"ctb\"), person(\"Javier\", \"Luraschi\", role = \"ctb\"), person(\"Jeff\", \"Arnold\", role = \"ctb\"), person(\"Jenny\", \"Bryan\", role = \"ctb\"), person(\"Jeremy\", \"Ashkenas\", role = c(\"ctb\", \"cph\"), comment = \"the CSS file at inst/misc/docco-classic.css\"), person(\"Jeremy\", \"Stephens\", role = \"ctb\"), person(\"Jim\", \"Hester\", role = \"ctb\"), person(\"Joe\", \"Cheng\", role = \"ctb\"), person(\"Johannes\", \"Ranke\", role = \"ctb\"), person(\"John\", \"Honaker\", role = \"ctb\"), person(\"John\", \"Muschelli\", role = \"ctb\"), person(\"Jonathan\", \"Keane\", role = \"ctb\"), person(\"JJ\", \"Allaire\", role = \"ctb\"), person(\"Johan\", \"Toloe\", role = \"ctb\"), person(\"Jonathan\", \"Sidi\", role = \"ctb\"), person(\"Joseph\", \"Larmarange\", role = \"ctb\"), person(\"Julien\", \"Barnier\", role = \"ctb\"), person(\"Kaiyin\", \"Zhong\", role = \"ctb\"), person(\"Kamil\", \"Slowikowski\", role = \"ctb\"), person(\"Karl\", \"Forner\", role = \"ctb\"), person(c(\"Kevin\", \"K.\"), \"Smith\", role = \"ctb\"), person(\"Kirill\", \"Mueller\", role = \"ctb\"), person(\"Kohske\", \"Takahashi\", role = \"ctb\"), person(\"Lorenz\", \"Walthert\", role = \"ctb\"), person(\"Lucas\", \"Gallindo\", role = \"ctb\"), person(\"Marius\", \"Hofert\", role = \"ctb\"), person(\"Martin\", \"Modrák\", role = \"ctb\"), person(\"Michael\", \"Chirico\", role = \"ctb\"), person(\"Michael\", \"Friendly\", role = \"ctb\"), person(\"Michal\", \"Bojanowski\", role = \"ctb\"), person(\"Michel\", \"Kuhlmann\", role = \"ctb\"), person(\"Miller\", \"Patrick\", role = \"ctb\"), person(\"Nacho\", \"Caballero\", role = \"ctb\"), person(\"Nick\", \"Salkowski\", role = \"ctb\"), person(\"Niels Richard\", \"Hansen\", role = \"ctb\"), person(\"Noam\", \"Ross\", role = \"ctb\"), person(\"Obada\", \"Mahdi\", role = \"ctb\"), person(\"Pavel N.\", \"Krivitsky\", role = \"ctb\", comment=c(ORCID = \"0000-0002-9101-3362\")), person(\"Pedro\", \"Faria\", role = \"ctb\"), person(\"Qiang\", \"Li\", role = \"ctb\"), person(\"Ramnath\", \"Vaidyanathan\", role = \"ctb\"), person(\"Richard\", \"Cotton\", role = \"ctb\"), person(\"Robert\", \"Krzyzanowski\", role = \"ctb\"), person(\"Rodrigo\", \"Copetti\", role = \"ctb\"), person(\"Romain\", \"Francois\", role = \"ctb\"), person(\"Ruaridh\", \"Williamson\", role = \"ctb\"), person(\"Sagiru\", \"Mati\", role = \"ctb\", comment = c(ORCID = \"0000-0003-1413-3974\")), person(\"Scott\", \"Kostyshak\", role = \"ctb\"), person(\"Sebastian\", \"Meyer\", role = \"ctb\"), person(\"Sietse\", \"Brouwer\", role = \"ctb\"), person(c(\"Simon\", \"de\"), \"Bernard\", role = \"ctb\"), person(\"Sylvain\", \"Rousseau\", role = \"ctb\"), person(\"Taiyun\", \"Wei\", role = \"ctb\"), person(\"Thibaut\", \"Assus\", role = \"ctb\"), person(\"Thibaut\", \"Lamadon\", role = \"ctb\"), person(\"Thomas\", \"Leeper\", role = \"ctb\"), person(\"Tim\", \"Mastny\", role = \"ctb\"), person(\"Tom\", \"Torsney-Weir\", role = \"ctb\"), person(\"Trevor\", \"Davis\", role = \"ctb\"), person(\"Viktoras\", \"Veitas\", role = \"ctb\"), person(\"Weicheng\", \"Zhu\", role = \"ctb\"), person(\"Wush\", \"Wu\", role = \"ctb\"), person(\"Zachary\", \"Foster\", role = \"ctb\"), person(\"Zhian N.\", \"Kamvar\", role = \"ctb\", comment = c(ORCID = \"0000-0003-1458-7108\")), person(given = \"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "Provides a general-purpose tool for dynamic report generation in R using Literate Programming techniques.", - "Depends": [ - "R (>= 3.6.0)" - ], - "Imports": [ - "evaluate (>= 0.15)", - "highr (>= 0.11)", + "Version": "1.52", + "Source": "Repository", + "Requirements": [ + "evaluate", + "highr", "methods", + "R", "tools", - "xfun (>= 0.52)", - "yaml (>= 2.1.19)" - ], - "Suggests": [ - "bslib", - "DBI (>= 0.4-1)", - "digest", - "formatR", - "gifski", - "gridSVG", - "htmlwidgets (>= 0.7)", - "jpeg", - "JuliaCall (>= 0.11.1)", - "magick", - "litedown", - "markdown (>= 1.3)", - "otel", - "otelsdk", - "png", - "ragg", - "reticulate (>= 1.4)", - "rgl (>= 0.95.1201)", - "rlang", - "rmarkdown", - "sass", - "showtext", - "styler (>= 1.2.0)", - "targets (>= 0.6.0)", - "testit", - "tibble", - "tikzDevice (>= 0.10)", - "tinytex (>= 0.56)", - "webshot", - "rstudioapi", - "svglite" - ], - "License": "GPL", - "URL": "https://yihui.org/knitr/", - "BugReports": "https://github.com/yihui/knitr/issues", - "Encoding": "UTF-8", - "VignetteBuilder": "litedown, knitr", - "SystemRequirements": "Package vignettes based on R Markdown v2 or reStructuredText require Pandoc (http://pandoc.org). The function rst2pdf() requires rst2pdf (https://github.com/rst2pdf/rst2pdf).", - "Collate": "'block.R' 'cache.R' 'citation.R' 'hooks-html.R' 'plot.R' 'utils.R' 'defaults.R' 'concordance.R' 'engine.R' 'highlight.R' 'themes.R' 'header.R' 'hooks-asciidoc.R' 'hooks-chunk.R' 'hooks-extra.R' 'hooks-latex.R' 'hooks-md.R' 'hooks-rst.R' 'hooks-textile.R' 'hooks.R' 'otel.R' 'output.R' 'package.R' 'pandoc.R' 'params.R' 'parser.R' 'pattern.R' 'rocco.R' 'spin.R' 'table.R' 'template.R' 'utils-conversion.R' 'utils-rd2html.R' 'utils-string.R' 'utils-sweave.R' 'utils-upload.R' 'utils-vignettes.R' 'zzz.R'", - "RoxygenNote": "7.3.3", - "NeedsCompilation": "no", - "Author": "Yihui Xie [aut, cre] (ORCID: , URL: https://yihui.org), Abhraneel Sarma [ctb], Adam Vogt [ctb], Alastair Andrew [ctb], Alex Zvoleff [ctb], Amar Al-Zubaidi [ctb], Andre Simon [ctb] (the CSS files under inst/themes/ were derived from the Highlight package http://www.andre-simon.de), Aron Atkins [ctb], Aaron Wolen [ctb], Ashley Manton [ctb], Atsushi Yasumoto [ctb] (ORCID: ), Ben Baumer [ctb], Brian Diggs [ctb], Brian Zhang [ctb], Bulat Yapparov [ctb], Cassio Pereira [ctb], Christophe Dervieux [ctb], David Hall [ctb], David Hugh-Jones [ctb], David Robinson [ctb], Doug Hemken [ctb], Duncan Murdoch [ctb], Elio Campitelli [ctb], Ellis Hughes [ctb], Emily Riederer [ctb], Fabian Hirschmann [ctb], Fitch Simeon [ctb], Forest Fang [ctb], Frank E Harrell Jr [ctb] (the Sweavel package at inst/misc/Sweavel.sty), Garrick Aden-Buie [ctb], Gregoire Detrez [ctb], Hadley Wickham [ctb], Hao Zhu [ctb], Heewon Jeon [ctb], Henrik Bengtsson [ctb], Hiroaki Yutani [ctb], Ian Lyttle [ctb], Hodges Daniel [ctb], Jacob Bien [ctb], Jake Burkhead [ctb], James Manton [ctb], Jared Lander [ctb], Jason Punyon [ctb], Javier Luraschi [ctb], Jeff Arnold [ctb], Jenny Bryan [ctb], Jeremy Ashkenas [ctb, cph] (the CSS file at inst/misc/docco-classic.css), Jeremy Stephens [ctb], Jim Hester [ctb], Joe Cheng [ctb], Johannes Ranke [ctb], John Honaker [ctb], John Muschelli [ctb], Jonathan Keane [ctb], JJ Allaire [ctb], Johan Toloe [ctb], Jonathan Sidi [ctb], Joseph Larmarange [ctb], Julien Barnier [ctb], Kaiyin Zhong [ctb], Kamil Slowikowski [ctb], Karl Forner [ctb], Kevin K. Smith [ctb], Kirill Mueller [ctb], Kohske Takahashi [ctb], Lorenz Walthert [ctb], Lucas Gallindo [ctb], Marius Hofert [ctb], Martin Modrák [ctb], Michael Chirico [ctb], Michael Friendly [ctb], Michal Bojanowski [ctb], Michel Kuhlmann [ctb], Miller Patrick [ctb], Nacho Caballero [ctb], Nick Salkowski [ctb], Niels Richard Hansen [ctb], Noam Ross [ctb], Obada Mahdi [ctb], Pavel N. Krivitsky [ctb] (ORCID: ), Pedro Faria [ctb], Qiang Li [ctb], Ramnath Vaidyanathan [ctb], Richard Cotton [ctb], Robert Krzyzanowski [ctb], Rodrigo Copetti [ctb], Romain Francois [ctb], Ruaridh Williamson [ctb], Sagiru Mati [ctb] (ORCID: ), Scott Kostyshak [ctb], Sebastian Meyer [ctb], Sietse Brouwer [ctb], Simon de Bernard [ctb], Sylvain Rousseau [ctb], Taiyun Wei [ctb], Thibaut Assus [ctb], Thibaut Lamadon [ctb], Thomas Leeper [ctb], Tim Mastny [ctb], Tom Torsney-Weir [ctb], Trevor Davis [ctb], Viktoras Veitas [ctb], Weicheng Zhu [ctb], Wush Wu [ctb], Zachary Foster [ctb], Zhian N. Kamvar [ctb] (ORCID: ), Posit Software, PBC [cph, fnd]", - "Maintainer": "Yihui Xie ", - "Repository": "CRAN" + "xfun", + "yaml" + ] }, "labeling": { "Package": "labeling", "Version": "0.4.3", "Source": "Repository", - "Type": "Package", - "Title": "Axis Labeling", - "Date": "2023-08-29", - "Author": "Justin Talbot,", - "Maintainer": "Nuno Sempere ", - "Description": "Functions which provide a range of axis labeling algorithms.", - "License": "MIT + file LICENSE | Unlimited", - "Collate": "'labeling.R'", - "NeedsCompilation": "no", - "Imports": [ - "stats", - "graphics" - ], - "Repository": "CRAN" + "Requirements": [ + "graphics", + "stats" + ] }, "later": { "Package": "later", - "Version": "1.4.5", - "Source": "Repository", - "Type": "Package", - "Title": "Utilities for Scheduling Functions to Execute Later with Event Loops", - "Authors@R": "c( person(\"Winston\", \"Chang\", , \"winston@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0002-1576-2126\")), person(\"Joe\", \"Cheng\", , \"joe@posit.co\", role = \"aut\"), person(\"Charlie\", \"Gao\", , \"charlie.gao@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-0750-061X\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")), person(\"Marcus\", \"Geelnard\", role = c(\"ctb\", \"cph\"), comment = \"TinyCThread library, https://tinycthread.github.io/\"), person(\"Evan\", \"Nemerson\", role = c(\"ctb\", \"cph\"), comment = \"TinyCThread library, https://tinycthread.github.io/\") )", - "Description": "Executes arbitrary R or C functions some time after the current time, after the R execution stack has emptied. The functions are scheduled in an event loop.", - "License": "MIT + file LICENSE", - "URL": "https://later.r-lib.org, https://github.com/r-lib/later", - "BugReports": "https://github.com/r-lib/later/issues", - "Depends": [ - "R (>= 3.5)" - ], - "Imports": [ - "Rcpp (>= 1.0.10)", + "Version": "1.4.8", + "Source": "Repository", + "Requirements": [ + "R", + "Rcpp", "rlang" - ], - "Suggests": [ - "knitr", - "nanonext", - "promises", - "rmarkdown", - "testthat (>= 3.0.0)" - ], - "LinkingTo": [ - "Rcpp" - ], - "VignetteBuilder": "knitr", - "Config/build/compilation-database": "true", - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Config/usethis/last-upkeep": "2025-07-18", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3", - "NeedsCompilation": "yes", - "Author": "Winston Chang [aut] (ORCID: ), Joe Cheng [aut], Charlie Gao [aut, cre] (ORCID: ), Posit Software, PBC [cph, fnd] (ROR: ), Marcus Geelnard [ctb, cph] (TinyCThread library, https://tinycthread.github.io/), Evan Nemerson [ctb, cph] (TinyCThread library, https://tinycthread.github.io/)", - "Maintainer": "Charlie Gao ", - "Repository": "CRAN" + ] }, "lazyeval": { "Package": "lazyeval", - "Version": "0.2.2", + "Version": "0.2.3", "Source": "Repository", - "Title": "Lazy (Non-Standard) Evaluation", - "Description": "An alternative approach to non-standard evaluation using formulas. Provides a full implementation of LISP style 'quasiquotation', making it easier to generate code with other code.", - "Authors@R": "c( person(\"Hadley\", \"Wickham\", ,\"hadley@rstudio.com\", c(\"aut\", \"cre\")), person(\"RStudio\", role = \"cph\") )", - "License": "GPL-3", - "LazyData": "true", - "Depends": [ - "R (>= 3.1.0)" - ], - "Suggests": [ - "knitr", - "rmarkdown (>= 0.2.65)", - "testthat", - "covr" - ], - "VignetteBuilder": "knitr", - "RoxygenNote": "6.1.1", - "NeedsCompilation": "yes", - "Author": "Hadley Wickham [aut, cre], RStudio [cph]", - "Maintainer": "Hadley Wickham ", - "Repository": "CRAN" + "Requirements": [ + "R", + "rlang" + ] }, "lifecycle": { "Package": "lifecycle", "Version": "1.0.5", "Source": "Repository", - "Title": "Manage the Life Cycle of your Package Functions", - "Authors@R": "c( person(\"Lionel\", \"Henry\", , \"lionel@posit.co\", role = c(\"aut\", \"cre\")), person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0003-4757-117X\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "Manage the life cycle of your exported functions with shared conventions, documentation badges, and user-friendly deprecation warnings.", - "License": "MIT + file LICENSE", - "URL": "https://lifecycle.r-lib.org/, https://github.com/r-lib/lifecycle", - "BugReports": "https://github.com/r-lib/lifecycle/issues", - "Depends": [ - "R (>= 3.6)" - ], - "Imports": [ - "cli (>= 3.4.0)", - "rlang (>= 1.1.0)" - ], - "Suggests": [ - "covr", - "knitr", - "lintr (>= 3.1.0)", - "rmarkdown", - "testthat (>= 3.0.1)", - "tibble", - "tidyverse", - "tools", - "vctrs", - "withr", - "xml2" - ], - "VignetteBuilder": "knitr", - "Config/Needs/website": "tidyverse/tidytemplate, usethis", - "Config/testthat/edition": "3", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3", - "NeedsCompilation": "no", - "Author": "Lionel Henry [aut, cre], Hadley Wickham [aut] (ORCID: ), Posit Software, PBC [cph, fnd]", - "Maintainer": "Lionel Henry ", - "Repository": "CRAN" + "Requirements": [ + "cli", + "R", + "rlang" + ] }, "magrittr": { "Package": "magrittr", - "Version": "2.0.4", - "Source": "Repository", - "Type": "Package", - "Title": "A Forward-Pipe Operator for R", - "Authors@R": "c( person(\"Stefan Milton\", \"Bache\", , \"stefan@stefanbache.dk\", role = c(\"aut\", \"cph\"), comment = \"Original author and creator of magrittr\"), person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\"), person(\"Lionel\", \"Henry\", , \"lionel@posit.co\", role = \"cre\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", - "Description": "Provides a mechanism for chaining commands with a new forward-pipe operator, %>%. This operator will forward a value, or the result of an expression, into the next function call/expression. There is flexible support for the type of right-hand side expressions. For more information, see package vignette. To quote Rene Magritte, \"Ceci n'est pas un pipe.\"", - "License": "MIT + file LICENSE", - "URL": "https://magrittr.tidyverse.org, https://github.com/tidyverse/magrittr", - "BugReports": "https://github.com/tidyverse/magrittr/issues", - "Depends": [ - "R (>= 3.4.0)" - ], - "Suggests": [ - "covr", - "knitr", - "rlang", - "rmarkdown", - "testthat" - ], - "VignetteBuilder": "knitr", - "ByteCompile": "Yes", - "Config/Needs/website": "tidyverse/tidytemplate", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3", - "NeedsCompilation": "yes", - "Author": "Stefan Milton Bache [aut, cph] (Original author and creator of magrittr), Hadley Wickham [aut], Lionel Henry [cre], Posit Software, PBC [cph, fnd] (ROR: )", - "Maintainer": "Lionel Henry ", - "Repository": "CRAN" + "Version": "2.0.5", + "Source": "Repository", + "Requirements": [ + "R" + ] }, "memoise": { "Package": "memoise", "Version": "2.0.1", "Source": "Repository", - "Title": "'Memoisation' of Functions", - "Authors@R": "c(person(given = \"Hadley\", family = \"Wickham\", role = \"aut\", email = \"hadley@rstudio.com\"), person(given = \"Jim\", family = \"Hester\", role = \"aut\"), person(given = \"Winston\", family = \"Chang\", role = c(\"aut\", \"cre\"), email = \"winston@rstudio.com\"), person(given = \"Kirill\", family = \"Müller\", role = \"aut\", email = \"krlmlr+r@mailbox.org\"), person(given = \"Daniel\", family = \"Cook\", role = \"aut\", email = \"danielecook@gmail.com\"), person(given = \"Mark\", family = \"Edmondson\", role = \"ctb\", email = \"r@sunholo.com\"))", - "Description": "Cache the results of a function so that when you call it again with the same arguments it returns the previously computed value.", - "License": "MIT + file LICENSE", - "URL": "https://memoise.r-lib.org, https://github.com/r-lib/memoise", - "BugReports": "https://github.com/r-lib/memoise/issues", - "Imports": [ - "rlang (>= 0.4.10)", - "cachem" - ], - "Suggests": [ - "digest", - "aws.s3", - "covr", - "googleAuthR", - "googleCloudStorageR", - "httr", - "testthat" - ], - "Encoding": "UTF-8", - "RoxygenNote": "7.1.2", - "NeedsCompilation": "no", - "Author": "Hadley Wickham [aut], Jim Hester [aut], Winston Chang [aut, cre], Kirill Müller [aut], Daniel Cook [aut], Mark Edmondson [ctb]", - "Maintainer": "Winston Chang ", - "Repository": "CRAN" + "Requirements": [ + "cachem", + "rlang" + ] }, "mime": { "Package": "mime", "Version": "0.13", "Source": "Repository", - "Type": "Package", - "Title": "Map Filenames to MIME Types", - "Authors@R": "c( person(\"Yihui\", \"Xie\", role = c(\"aut\", \"cre\"), email = \"xie@yihui.name\", comment = c(ORCID = \"0000-0003-0645-5666\", URL = \"https://yihui.org\")), person(\"Jeffrey\", \"Horner\", role = \"ctb\"), person(\"Beilei\", \"Bian\", role = \"ctb\") )", - "Description": "Guesses the MIME type from a filename extension using the data derived from /etc/mime.types in UNIX-type systems.", - "Imports": [ + "Requirements": [ "tools" - ], - "License": "GPL", - "URL": "https://github.com/yihui/mime", - "BugReports": "https://github.com/yihui/mime/issues", - "RoxygenNote": "7.3.2", - "Encoding": "UTF-8", - "NeedsCompilation": "yes", - "Author": "Yihui Xie [aut, cre] (, https://yihui.org), Jeffrey Horner [ctb], Beilei Bian [ctb]", - "Maintainer": "Yihui Xie ", - "Repository": "CRAN" + ] }, "officer": { "Package": "officer", - "Version": "0.7.3", + "Version": "0.7.7", "Source": "Repository", - "Type": "Package", - "Title": "Manipulation of Microsoft Word and PowerPoint Documents", - "Authors@R": "c( person(\"David\", \"Gohel\", , \"david.gohel@ardata.fr\", role = c(\"aut\", \"cre\")), person(\"Stefan\", \"Moog\", , \"moogs@gmx.de\", role = \"aut\"), person(\"Mark\", \"Heckmann\", , \"heckmann.mark@gmail.com\", role = \"aut\", comment = c(ORCID = \"0000-0002-0736-7417\")), person(\"ArData\", role = \"cph\"), person(\"Frank\", \"Hangler\", , \"frank@plotandscatter.com\", role = \"ctb\", comment = \"function body_replace_all_text\"), person(\"Liz\", \"Sander\", , \"lsander@civisanalytics.com\", role = \"ctb\", comment = \"several documentation fixes\"), person(\"Anton\", \"Victorson\", , \"anton@victorson.se\", role = \"ctb\", comment = \"fixes xml structures\"), person(\"Jon\", \"Calder\", , \"jonmcalder@gmail.com\", role = \"ctb\", comment = \"update vignettes\"), person(\"John\", \"Harrold\", , \"john.m.harrold@gmail.com\", role = \"ctb\", comment = \"function annotate_base\"), person(\"John\", \"Muschelli\", , \"muschellij2@gmail.com\", role = \"ctb\", comment = \"google doc compatibility\"), person(\"Bill\", \"Denney\", , \"wdenney@humanpredictions.com\", role = \"ctb\", comment = c(ORCID = \"0000-0002-5759-428X\", \"function as.matrix.rpptx\")), person(\"Nikolai\", \"Beck\", , \"beck.nikolai@gmail.com\", role = \"ctb\", comment = \"set speaker notes for .pptx documents\"), person(\"Greg\", \"Leleu\", , \"gregoire.leleu@gmail.com\", role = \"ctb\", comment = \"fields functionality in ppt\"), person(\"Majid\", \"Eismann\", role = \"ctb\"), person(\"Wahiduzzaman\", \"Khan\", role = \"ctb\", comment = \"vectorization of remove_slide\"), person(\"Hongyuan\", \"Jia\", , \"hongyuanjia@cqust.edu.cn\", role = \"ctb\", comment = c(ORCID = \"0000-0002-0075-8183\")), person(\"Michael\", \"Stackhouse\", , \"mike.stackhouse@atorusresearch.com\", role = \"ctb\") )", - "Description": "Access and manipulate 'Microsoft Word', 'RTF' and 'Microsoft PowerPoint' documents from R. The package focuses on tabular and graphical reporting from R; it also provides two functions that let users get document content into data objects. A set of functions lets add and remove images, tables and paragraphs of text in new or existing documents. The package does not require any installation of Microsoft products to be able to write Microsoft files.", - "License": "MIT + file LICENSE", - "URL": "https://ardata-fr.github.io/officeverse/, https://davidgohel.github.io/officer/", - "BugReports": "https://github.com/davidgohel/officer/issues", - "Imports": [ + "Requirements": [ "cli", "dplyr", "graphics", @@ -1762,1584 +574,455 @@ "tidyr", "utils", "uuid", - "xml2 (>= 1.1.0)", - "zip (>= 2.1.0)" - ], - "Suggests": [ - "devEMF", - "doconv (>= 0.3.0)", - "gdtools", - "ggplot2", - "knitr", - "magick", - "rmarkdown", - "rsvg", - "testthat", - "withr" - ], - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3", - "Collate": "'core_properties.R' 'custom_properties.R' 'defunct.R' 'dev-utils.R' 'docx_add.R' 'docx_comments.R' 'docx_cursor.R' 'docx_part.R' 'docx_replace.R' 'docx_section.R' 'docx_settings.R' 'docx_styles.R' 'docx_utils_funs.R' 'empty_content.R' 'formatting_properties.R' 'fortify_docx.R' 'fortify_pptx.R' 'knitr_utils.R' 'officer.R' 'ooxml.R' 'ooxml_block_objects.R' 'ooxml_run_objects.R' 'openxml_content_type.R' 'openxml_document.R' 'pack_folder.R' 'ph_location.R' 'post-proc.R' 'ppt_class_dir_collection.R' 'ppt_classes.R' 'ppt_notes.R' 'ppt_ph_dedupe_layout.R' 'ppt_ph_manipulate.R' 'ppt_ph_rename_layout.R' 'ppt_ph_with_methods.R' 'pptx_informations.R' 'pptx_layout_helper.R' 'pptx_matrix.R' 'utils.R' 'pptx_slide_manip.R' 'read_docx.R' 'docx_write.R' 'read_docx_styles.R' 'read_pptx.R' 'read_xlsx.R' 'relationship.R' 'rtf.R' 'shape_properties.R' 'shorcuts.R' 'docx_append_context.R' 'utils-xml.R' 'deprecated.R' 'zzz.R'", - "NeedsCompilation": "no", - "Author": "David Gohel [aut, cre], Stefan Moog [aut], Mark Heckmann [aut] (ORCID: ), ArData [cph], Frank Hangler [ctb] (function body_replace_all_text), Liz Sander [ctb] (several documentation fixes), Anton Victorson [ctb] (fixes xml structures), Jon Calder [ctb] (update vignettes), John Harrold [ctb] (function annotate_base), John Muschelli [ctb] (google doc compatibility), Bill Denney [ctb] (ORCID: , function as.matrix.rpptx), Nikolai Beck [ctb] (set speaker notes for .pptx documents), Greg Leleu [ctb] (fields functionality in ppt), Majid Eismann [ctb], Wahiduzzaman Khan [ctb] (vectorization of remove_slide), Hongyuan Jia [ctb] (ORCID: ), Michael Stackhouse [ctb]", - "Maintainer": "David Gohel ", - "Repository": "CRAN" + "xml2", + "zip" + ] }, "openssl": { "Package": "openssl", - "Version": "2.3.4", - "Source": "Repository", - "Type": "Package", - "Title": "Toolkit for Encryption, Signatures and Certificates Based on OpenSSL", - "Authors@R": "c(person(\"Jeroen\", \"Ooms\", role = c(\"aut\", \"cre\"), email = \"jeroenooms@gmail.com\", comment = c(ORCID = \"0000-0002-4035-0289\")), person(\"Oliver\", \"Keyes\", role = \"ctb\"))", - "Description": "Bindings to OpenSSL libssl and libcrypto, plus custom SSH key parsers. Supports RSA, DSA and EC curves P-256, P-384, P-521, and curve25519. Cryptographic signatures can either be created and verified manually or via x509 certificates. AES can be used in cbc, ctr or gcm mode for symmetric encryption; RSA for asymmetric (public key) encryption or EC for Diffie Hellman. High-level envelope functions combine RSA and AES for encrypting arbitrary sized data. Other utilities include key generators, hash functions (md5, sha1, sha256, etc), base64 encoder, a secure random number generator, and 'bignum' math methods for manually performing crypto calculations on large multibyte integers.", - "License": "MIT + file LICENSE", - "URL": "https://jeroen.r-universe.dev/openssl", - "BugReports": "https://github.com/jeroen/openssl/issues", - "SystemRequirements": "OpenSSL >= 1.0.2", - "VignetteBuilder": "knitr", - "Imports": [ + "Version": "2.4.2", + "Source": "Repository", + "Requirements": [ "askpass" - ], - "Suggests": [ - "curl", - "testthat (>= 2.1.0)", - "digest", - "knitr", - "rmarkdown", - "jsonlite", - "jose", - "sodium" - ], - "RoxygenNote": "7.3.2", - "Encoding": "UTF-8", - "NeedsCompilation": "yes", - "Author": "Jeroen Ooms [aut, cre] (ORCID: ), Oliver Keyes [ctb]", - "Maintainer": "Jeroen Ooms ", - "Repository": "CRAN" + ] }, "otel": { "Package": "otel", "Version": "0.2.0", "Source": "Repository", - "Title": "OpenTelemetry R API", - "Authors@R": "person(\"Gábor\", \"Csárdi\", , \"csardi.gabor@gmail.com\", role = c(\"aut\", \"cre\"))", - "Description": "High-quality, ubiquitous, and portable telemetry to enable effective observability. OpenTelemetry is a collection of tools, APIs, and SDKs used to instrument, generate, collect, and export telemetry data (metrics, logs, and traces) for analysis in order to understand your software's performance and behavior. This package implements the OpenTelemetry API: . Use this package as a dependency if you want to instrument your R package for OpenTelemetry.", - "License": "MIT + file LICENSE", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2.9000", - "Depends": [ - "R (>= 3.6.0)" - ], - "Suggests": [ - "callr", - "cli", - "glue", - "jsonlite", - "otelsdk", - "processx", - "shiny", - "spelling", - "testthat (>= 3.0.0)", - "utils", - "withr" - ], - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "URL": "https://otel.r-lib.org, https://github.com/r-lib/otel", - "Additional_repositories": "https://github.com/r-lib/otelsdk/releases/download/devel", - "BugReports": "https://github.com/r-lib/otel/issues", - "NeedsCompilation": "no", - "Author": "Gábor Csárdi [aut, cre]", - "Maintainer": "Gábor Csárdi ", - "Repository": "CRAN" + "Requirements": [ + "R" + ] }, "pillar": { "Package": "pillar", "Version": "1.11.1", "Source": "Repository", - "Title": "Coloured Formatting for Columns", - "Authors@R": "c(person(given = \"Kirill\", family = \"M\\u00fcller\", role = c(\"aut\", \"cre\"), email = \"kirill@cynkra.com\", comment = c(ORCID = \"0000-0002-1416-3412\")), person(given = \"Hadley\", family = \"Wickham\", role = \"aut\"), person(given = \"RStudio\", role = \"cph\"))", - "Description": "Provides 'pillar' and 'colonnade' generics designed for formatting columns of data using the full range of colours provided by modern terminals.", - "License": "MIT + file LICENSE", - "URL": "https://pillar.r-lib.org/, https://github.com/r-lib/pillar", - "BugReports": "https://github.com/r-lib/pillar/issues", - "Imports": [ - "cli (>= 2.3.0)", + "Requirements": [ + "cli", "glue", "lifecycle", - "rlang (>= 1.0.2)", - "utf8 (>= 1.1.0)", + "rlang", + "utf8", "utils", - "vctrs (>= 0.5.0)" - ], - "Suggests": [ - "bit64", - "DBI", - "debugme", - "DiagrammeR", - "dplyr", - "formattable", - "ggplot2", - "knitr", - "lubridate", - "nanotime", - "nycflights13", - "palmerpenguins", - "rmarkdown", - "scales", - "stringi", - "survival", - "testthat (>= 3.1.1)", - "tibble", - "units (>= 0.7.2)", - "vdiffr", - "withr" - ], - "VignetteBuilder": "knitr", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3.9000", - "Config/testthat/edition": "3", - "Config/testthat/parallel": "true", - "Config/testthat/start-first": "format_multi_fuzz, format_multi_fuzz_2, format_multi, ctl_colonnade, ctl_colonnade_1, ctl_colonnade_2", - "Config/autostyle/scope": "line_breaks", - "Config/autostyle/strict": "true", - "Config/gha/extra-packages": "units=?ignore-before-r=4.3.0", - "Config/Needs/website": "tidyverse/tidytemplate", - "NeedsCompilation": "no", - "Author": "Kirill Mller [aut, cre] (ORCID: ), Hadley Wickham [aut], RStudio [cph]", - "Maintainer": "Kirill Mller ", - "Repository": "CRAN" + "vctrs" + ] }, "pkgbuild": { "Package": "pkgbuild", "Version": "1.4.8", "Source": "Repository", - "Title": "Find Tools Needed to Build R Packages", - "Authors@R": "c( person(\"Hadley\", \"Wickham\", role = \"aut\"), person(\"Jim\", \"Hester\", role = \"aut\"), person(\"Gábor\", \"Csárdi\", , \"csardi.gabor@gmail.com\", role = c(\"aut\", \"cre\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", - "Description": "Provides functions used to build R packages. Locates compilers needed to build R packages on various platforms and ensures the PATH is configured appropriately so R can use them.", - "License": "MIT + file LICENSE", - "URL": "https://github.com/r-lib/pkgbuild, https://pkgbuild.r-lib.org", - "BugReports": "https://github.com/r-lib/pkgbuild/issues", - "Depends": [ - "R (>= 3.5)" - ], - "Imports": [ - "callr (>= 3.2.0)", - "cli (>= 3.4.0)", + "Requirements": [ + "callr", + "cli", "desc", "processx", + "R", "R6" - ], - "Suggests": [ - "covr", - "cpp11", - "knitr", - "Rcpp", - "rmarkdown", - "testthat (>= 3.2.0)", - "withr (>= 2.3.0)" - ], - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Config/usethis/last-upkeep": "2025-04-30", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2", - "NeedsCompilation": "no", - "Author": "Hadley Wickham [aut], Jim Hester [aut], Gábor Csárdi [aut, cre], Posit Software, PBC [cph, fnd] (ROR: )", - "Maintainer": "Gábor Csárdi ", - "Repository": "CRAN" + ] }, "pkgconfig": { "Package": "pkgconfig", "Version": "2.0.3", "Source": "Repository", - "Title": "Private Configuration for 'R' Packages", - "Author": "Gábor Csárdi", - "Maintainer": "Gábor Csárdi ", - "Description": "Set configuration options on a per-package basis. Options set by a given package only apply to that package, other packages are unaffected.", - "License": "MIT + file LICENSE", - "LazyData": "true", - "Imports": [ + "Requirements": [ "utils" - ], - "Suggests": [ - "covr", - "testthat", - "disposables (>= 1.0.3)" - ], - "URL": "https://github.com/r-lib/pkgconfig#readme", - "BugReports": "https://github.com/r-lib/pkgconfig/issues", - "Encoding": "UTF-8", - "NeedsCompilation": "no", - "Repository": "CRAN" + ] }, "plotly": { "Package": "plotly", - "Version": "4.12.0", - "Source": "Repository", - "Title": "Create Interactive Web Graphics via 'plotly.js'", - "Authors@R": "c(person(\"Carson\", \"Sievert\", role = c(\"aut\", \"cre\"), email = \"cpsievert1@gmail.com\", comment = c(ORCID = \"0000-0002-4958-2844\")), person(\"Chris\", \"Parmer\", role = \"aut\", email = \"chris@plot.ly\"), person(\"Toby\", \"Hocking\", role = \"aut\", email = \"tdhock5@gmail.com\"), person(\"Scott\", \"Chamberlain\", role = \"aut\", email = \"myrmecocystus@gmail.com\"), person(\"Karthik\", \"Ram\", role = \"aut\", email = \"karthik.ram@gmail.com\"), person(\"Marianne\", \"Corvellec\", role = \"aut\", email = \"marianne.corvellec@igdore.org\", comment = c(ORCID = \"0000-0002-1994-3581\")), person(\"Pedro\", \"Despouy\", role = \"aut\", email = \"pedro@plot.ly\"), person(\"Salim\", \"Brüggemann\", role = \"ctb\", email = \"salim-b@pm.me\", comment = c(ORCID = \"0000-0002-5329-5987\")), person(\"Plotly Technologies Inc.\", role = \"cph\"))", - "License": "MIT + file LICENSE", - "Description": "Create interactive web graphics from 'ggplot2' graphs and/or a custom interface to the (MIT-licensed) JavaScript library 'plotly.js' inspired by the grammar of graphics.", - "URL": "https://plotly-r.com, https://github.com/plotly/plotly.R, https://plotly.com/r/", - "BugReports": "https://github.com/plotly/plotly.R/issues", - "Depends": [ - "R (>= 3.5.0)", - "ggplot2 (>= 3.0.0)" - ], - "Imports": [ - "tools", - "scales", - "httr (>= 1.3.0)", - "jsonlite (>= 1.6)", - "magrittr", - "digest", - "viridisLite", + "Version": "4.12.1", + "Source": "Repository", + "Requirements": [ "base64enc", - "htmltools (>= 0.3.6)", - "htmlwidgets (>= 1.5.2.9001)", - "tidyr (>= 1.0.0)", - "RColorBrewer", - "dplyr", - "vctrs", - "tibble", - "lazyeval (>= 0.2.0)", - "rlang (>= 1.0.0)", "crosstalk", - "purrr", "data.table", - "promises" - ], - "Suggests": [ - "MASS", - "maps", - "hexbin", - "ggthemes", - "GGally", - "ggalluvial", - "testthat", - "knitr", - "shiny (>= 1.1.0)", - "shinytest2", - "curl", - "rmarkdown", - "Cairo", - "broom", - "webshot", - "listviewer", - "dendextend", - "sf", - "png", - "IRdisplay", - "processx", - "plotlyGeoAssets", - "forcats", - "withr", - "palmerpenguins", - "rversions", - "reticulate", - "rsvg", - "ggridges" - ], - "LazyData": "true", - "RoxygenNote": "7.3.3", - "Encoding": "UTF-8", - "Config/Needs/check": "tidyverse/ggplot2, ggobi/GGally, rcmdcheck, devtools, reshape2, s2", - "NeedsCompilation": "no", - "Author": "Carson Sievert [aut, cre] (ORCID: ), Chris Parmer [aut], Toby Hocking [aut], Scott Chamberlain [aut], Karthik Ram [aut], Marianne Corvellec [aut] (ORCID: ), Pedro Despouy [aut], Salim Brüggemann [ctb] (ORCID: ), Plotly Technologies Inc. [cph]", - "Maintainer": "Carson Sievert ", - "Repository": "CRAN" + "digest", + "dplyr", + "ggplot2", + "htmltools", + "htmlwidgets", + "httr", + "jsonlite", + "magrittr", + "promises", + "purrr", + "R", + "RColorBrewer", + "rlang", + "scales", + "tibble", + "tidyr", + "tools", + "vctrs", + "viridisLite" + ] }, "plyr": { "Package": "plyr", "Version": "1.8.9", "Source": "Repository", - "Title": "Tools for Splitting, Applying and Combining Data", - "Authors@R": "person(\"Hadley\", \"Wickham\", , \"hadley@rstudio.com\", role = c(\"aut\", \"cre\"))", - "Description": "A set of tools that solves a common set of problems: you need to break a big problem down into manageable pieces, operate on each piece and then put all the pieces back together. For example, you might want to fit a model to each spatial location or time point in your study, summarise data by panels or collapse high-dimensional arrays to simpler summary statistics. The development of 'plyr' has been generously supported by 'Becton Dickinson'.", - "License": "MIT + file LICENSE", - "URL": "http://had.co.nz/plyr, https://github.com/hadley/plyr", - "BugReports": "https://github.com/hadley/plyr/issues", - "Depends": [ - "R (>= 3.1.0)" - ], - "Imports": [ - "Rcpp (>= 0.11.0)" - ], - "Suggests": [ - "abind", - "covr", - "doParallel", - "foreach", - "iterators", - "itertools", - "tcltk", - "testthat" - ], - "LinkingTo": [ + "Requirements": [ + "R", "Rcpp" - ], - "Encoding": "UTF-8", - "LazyData": "true", - "RoxygenNote": "7.2.3", - "NeedsCompilation": "yes", - "Author": "Hadley Wickham [aut, cre]", - "Maintainer": "Hadley Wickham ", - "Repository": "CRAN" + ] }, "processx": { "Package": "processx", - "Version": "3.8.6", - "Source": "Repository", - "Title": "Execute and Control System Processes", - "Authors@R": "c( person(\"Gábor\", \"Csárdi\", , \"csardi.gabor@gmail.com\", role = c(\"aut\", \"cre\", \"cph\"), comment = c(ORCID = \"0000-0001-7098-9676\")), person(\"Winston\", \"Chang\", role = \"aut\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")), person(\"Ascent Digital Services\", role = c(\"cph\", \"fnd\")) )", - "Description": "Tools to run system processes in the background. It can check if a background process is running; wait on a background process to finish; get the exit status of finished processes; kill background processes. It can read the standard output and error of the processes, using non-blocking connections. 'processx' can poll a process for standard output or error, with a timeout. It can also poll several processes at once.", - "License": "MIT + file LICENSE", - "URL": "https://processx.r-lib.org, https://github.com/r-lib/processx", - "BugReports": "https://github.com/r-lib/processx/issues", - "Depends": [ - "R (>= 3.4.0)" - ], - "Imports": [ - "ps (>= 1.2.0)", + "Version": "3.9.0", + "Source": "Repository", + "Requirements": [ + "ps", + "R", "R6", "utils" - ], - "Suggests": [ - "callr (>= 3.7.3)", - "cli (>= 3.3.0)", - "codetools", - "covr", - "curl", - "debugme", - "parallel", - "rlang (>= 1.0.2)", - "testthat (>= 3.0.0)", - "webfakes", - "withr" - ], - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.1.9000", - "NeedsCompilation": "yes", - "Author": "Gábor Csárdi [aut, cre, cph] (), Winston Chang [aut], Posit Software, PBC [cph, fnd], Ascent Digital Services [cph, fnd]", - "Maintainer": "Gábor Csárdi ", - "Repository": "CRAN" + ] }, "promises": { "Package": "promises", "Version": "1.5.0", "Source": "Repository", - "Type": "Package", - "Title": "Abstractions for Promise-Based Asynchronous Programming", - "Authors@R": "c( person(\"Joe\", \"Cheng\", , \"joe@posit.co\", role = \"aut\"), person(\"Barret\", \"Schloerke\", , \"barret@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0001-9986-114X\")), person(\"Winston\", \"Chang\", , \"winston@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0002-1576-2126\")), person(\"Charlie\", \"Gao\", , \"charlie.gao@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0002-0750-061X\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", - "Description": "Provides fundamental abstractions for doing asynchronous programming in R using promises. Asynchronous programming is useful for allowing a single R process to orchestrate multiple tasks in the background while also attending to something else. Semantics are similar to 'JavaScript' promises, but with a syntax that is idiomatic R.", - "License": "MIT + file LICENSE", - "URL": "https://rstudio.github.io/promises/, https://github.com/rstudio/promises", - "BugReports": "https://github.com/rstudio/promises/issues", - "Depends": [ - "R (>= 4.1.0)" - ], - "Imports": [ - "fastmap (>= 1.1.0)", + "Requirements": [ + "fastmap", "later", "lifecycle", - "magrittr (>= 1.5)", - "otel (>= 0.2.0)", + "magrittr", + "otel", + "R", "R6", "rlang" - ], - "Suggests": [ - "future (>= 1.21.0)", - "knitr", - "mirai", - "otelsdk (>= 0.2.0)", - "purrr", - "Rcpp", - "rmarkdown", - "spelling", - "testthat (>= 3.0.0)", - "vembedr" - ], - "VignetteBuilder": "knitr", - "Config/Needs/website": "rsconnect, tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Config/usethis/last-upkeep": "2025-05-27", - "Encoding": "UTF-8", - "Language": "en-US", - "RoxygenNote": "7.3.3", - "NeedsCompilation": "no", - "Author": "Joe Cheng [aut], Barret Schloerke [aut, cre] (ORCID: ), Winston Chang [aut] (ORCID: ), Charlie Gao [aut] (ORCID: ), Posit Software, PBC [cph, fnd] (ROR: )", - "Maintainer": "Barret Schloerke ", - "Repository": "CRAN" + ] }, "ps": { "Package": "ps", - "Version": "1.9.1", - "Source": "Repository", - "Title": "List, Query, Manipulate System Processes", - "Authors@R": "c( person(\"Jay\", \"Loden\", role = \"aut\"), person(\"Dave\", \"Daeschler\", role = \"aut\"), person(\"Giampaolo\", \"Rodola'\", role = \"aut\"), person(\"Gábor\", \"Csárdi\", , \"csardi.gabor@gmail.com\", role = c(\"aut\", \"cre\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "List, query and manipulate all system processes, on 'Windows', 'Linux' and 'macOS'.", - "License": "MIT + file LICENSE", - "URL": "https://github.com/r-lib/ps, https://ps.r-lib.org/", - "BugReports": "https://github.com/r-lib/ps/issues", - "Depends": [ - "R (>= 3.4)" - ], - "Imports": [ + "Version": "1.9.3", + "Source": "Repository", + "Requirements": [ + "R", "utils" - ], - "Suggests": [ - "callr", - "covr", - "curl", - "pillar", - "pingr", - "processx (>= 3.1.0)", - "R6", - "rlang", - "testthat (>= 3.0.0)", - "webfakes", - "withr" - ], - "Biarch": "true", - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2", - "NeedsCompilation": "yes", - "Author": "Jay Loden [aut], Dave Daeschler [aut], Giampaolo Rodola' [aut], Gábor Csárdi [aut, cre], Posit Software, PBC [cph, fnd]", - "Maintainer": "Gábor Csárdi ", - "Repository": "CRAN" + ] }, "purrr": { "Package": "purrr", - "Version": "1.2.1", + "Version": "1.2.2", "Source": "Repository", - "Title": "Functional Programming Tools", - "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0003-4757-117X\")), person(\"Lionel\", \"Henry\", , \"lionel@posit.co\", role = \"aut\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"https://ror.org/03wc8by49\")) )", - "Description": "A complete and consistent functional programming toolkit for R.", - "License": "MIT + file LICENSE", - "URL": "https://purrr.tidyverse.org/, https://github.com/tidyverse/purrr", - "BugReports": "https://github.com/tidyverse/purrr/issues", - "Depends": [ - "R (>= 4.1)" - ], - "Imports": [ - "cli (>= 3.6.1)", - "lifecycle (>= 1.0.3)", - "magrittr (>= 1.5.0)", - "rlang (>= 1.1.1)", - "vctrs (>= 0.6.3)" - ], - "Suggests": [ - "carrier (>= 0.3.0)", - "covr", - "dplyr (>= 0.7.8)", - "httr", - "knitr", - "lubridate", - "mirai (>= 2.5.1)", - "rmarkdown", - "testthat (>= 3.0.0)", - "tibble", - "tidyselect" - ], - "LinkingTo": [ - "cli" - ], - "VignetteBuilder": "knitr", - "Biarch": "true", - "Config/build/compilation-database": "true", - "Config/Needs/website": "tidyverse/tidytemplate, tidyr", - "Config/testthat/edition": "3", - "Config/testthat/parallel": "TRUE", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3", - "NeedsCompilation": "yes", - "Author": "Hadley Wickham [aut, cre] (ORCID: ), Lionel Henry [aut], Posit Software, PBC [cph, fnd] (ROR: )", - "Maintainer": "Hadley Wickham ", - "Repository": "CRAN" + "Requirements": [ + "cli", + "lifecycle", + "magrittr", + "R", + "rlang", + "vctrs" + ] }, "ragg": { "Package": "ragg", - "Version": "1.5.0", + "Version": "1.5.2", "Source": "Repository", - "Type": "Package", - "Title": "Graphic Devices Based on AGG", - "Authors@R": "c( person(\"Thomas Lin\", \"Pedersen\", , \"thomas.pedersen@posit.co\", role = c(\"cre\", \"aut\"), comment = c(ORCID = \"0000-0002-5147-4711\")), person(\"Maxim\", \"Shemanarev\", role = c(\"aut\", \"cph\"), comment = \"Author of AGG\"), person(\"Tony\", \"Juricic\", , \"tonygeek@yahoo.com\", role = c(\"ctb\", \"cph\"), comment = \"Contributor to AGG\"), person(\"Milan\", \"Marusinec\", , \"milan@marusinec.sk\", role = c(\"ctb\", \"cph\"), comment = \"Contributor to AGG\"), person(\"Spencer\", \"Garrett\", role = \"ctb\", comment = \"Contributor to AGG\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", - "Maintainer": "Thomas Lin Pedersen ", - "Description": "Anti-Grain Geometry (AGG) is a high-quality and high-performance 2D drawing library. The 'ragg' package provides a set of graphic devices based on AGG to use as alternative to the raster devices provided through the 'grDevices' package.", - "License": "MIT + file LICENSE", - "URL": "https://ragg.r-lib.org, https://github.com/r-lib/ragg", - "BugReports": "https://github.com/r-lib/ragg/issues", - "Imports": [ - "systemfonts (>= 1.0.3)", - "textshaping (>= 0.3.0)" - ], - "Suggests": [ - "covr", - "graphics", - "grid", - "testthat (>= 3.0.0)" - ], - "LinkingTo": [ + "Requirements": [ "systemfonts", "textshaping" - ], - "Config/build/compilation-database": "true", - "Config/Needs/website": "ggplot2, devoid, magick, bench, tidyr, ggridges, hexbin, sessioninfo, pkgdown, tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Config/usethis/last-upkeep": "2025-04-25", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2", - "SystemRequirements": "freetype2, libpng, libtiff, libjpeg, libwebp, libwebpmux", - "NeedsCompilation": "yes", - "Author": "Thomas Lin Pedersen [cre, aut] (ORCID: ), Maxim Shemanarev [aut, cph] (Author of AGG), Tony Juricic [ctb, cph] (Contributor to AGG), Milan Marusinec [ctb, cph] (Contributor to AGG), Spencer Garrett [ctb] (Contributor to AGG), Posit Software, PBC [cph, fnd] (ROR: )", - "Repository": "CRAN" + ] }, "rappdirs": { "Package": "rappdirs", "Version": "0.3.4", "Source": "Repository", - "Type": "Package", - "Title": "Application Directories: Determine Where to Save Data, Caches, and Logs", - "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = c(\"trl\", \"cre\", \"cph\")), person(\"Sridhar\", \"Ratnakumar\", role = \"aut\"), person(\"Trent\", \"Mick\", role = \"aut\"), person(\"ActiveState\", role = \"cph\", comment = \"R/appdir.r, R/cache.r, R/data.r, R/log.r translated from appdirs\"), person(\"Eddy\", \"Petrisor\", role = \"ctb\"), person(\"Trevor\", \"Davis\", role = c(\"trl\", \"aut\"), comment = c(ORCID = \"0000-0001-6341-4639\")), person(\"Gabor\", \"Csardi\", role = \"ctb\"), person(\"Gregory\", \"Jefferis\", role = \"ctb\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", - "Description": "An easy way to determine which directories on the users computer you should use to save data, caches and logs. A port of Python's 'Appdirs' () to R.", - "License": "MIT + file LICENSE", - "URL": "https://rappdirs.r-lib.org, https://github.com/r-lib/rappdirs", - "BugReports": "https://github.com/r-lib/rappdirs/issues", - "Depends": [ - "R (>= 4.1)" - ], - "Suggests": [ - "covr", - "roxygen2", - "testthat (>= 3.2.0)", - "withr" - ], - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Config/usethis/last-upkeep": "2025-05-05", - "Copyright": "Original python appdirs module copyright (c) 2010 ActiveState Software Inc. R port copyright Hadley Wickham, Posit, PBC. See file LICENSE for details.", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3", - "NeedsCompilation": "yes", - "Author": "Hadley Wickham [trl, cre, cph], Sridhar Ratnakumar [aut], Trent Mick [aut], ActiveState [cph] (R/appdir.r, R/cache.r, R/data.r, R/log.r translated from appdirs), Eddy Petrisor [ctb], Trevor Davis [trl, aut] (ORCID: ), Gabor Csardi [ctb], Gregory Jefferis [ctb], Posit Software, PBC [cph, fnd] (ROR: )", - "Maintainer": "Hadley Wickham ", - "Repository": "CRAN" - }, - "renv": { - "Package": "renv", - "Version": "1.1.7", - "Source": "Repository", - "Type": "Package", - "Title": "Project Environments", - "Authors@R": "c( person(\"Kevin\", \"Ushey\", role = c(\"aut\", \"cre\"), email = \"kevin@rstudio.com\", comment = c(ORCID = \"0000-0003-2880-7407\")), person(\"Hadley\", \"Wickham\", role = c(\"aut\"), email = \"hadley@rstudio.com\", comment = c(ORCID = \"0000-0003-4757-117X\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "A dependency management toolkit for R. Using 'renv', you can create and manage project-local R libraries, save the state of these libraries to a 'lockfile', and later restore your library as required. Together, these tools can help make your projects more isolated, portable, and reproducible.", - "License": "MIT + file LICENSE", - "URL": "https://rstudio.github.io/renv/, https://github.com/rstudio/renv", - "BugReports": "https://github.com/rstudio/renv/issues", - "Imports": [ - "utils" - ], - "Suggests": [ - "BiocManager", - "cli", - "compiler", - "covr", - "cpp11", - "curl", - "devtools", - "generics", - "gitcreds", - "jsonlite", - "jsonvalidate", - "knitr", - "miniUI", - "modules", - "packrat", - "pak", - "R6", - "remotes", - "reticulate", - "rmarkdown", - "rstudioapi", - "shiny", - "testthat", - "uuid", - "waldo", - "yaml", - "webfakes" - ], - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3", - "VignetteBuilder": "knitr", - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Config/testthat/parallel": "true", - "Config/testthat/start-first": "bioconductor,python,install,restore,snapshot,retrieve,remotes", - "NeedsCompilation": "no", - "Author": "Kevin Ushey [aut, cre] (ORCID: ), Hadley Wickham [aut] (ORCID: ), Posit Software, PBC [cph, fnd]", - "Maintainer": "Kevin Ushey ", - "Repository": "CRAN" + "Requirements": [ + "R" + ] }, "rlang": { "Package": "rlang", - "Version": "1.1.7", - "Source": "Repository", - "Title": "Functions for Base Types and Core R and 'Tidyverse' Features", - "Description": "A toolbox for working with base types, core R features like the condition system, and core 'Tidyverse' features like tidy evaluation.", - "Authors@R": "c( person(\"Lionel\", \"Henry\", ,\"lionel@posit.co\", c(\"aut\", \"cre\")), person(\"Hadley\", \"Wickham\", ,\"hadley@posit.co\", \"aut\"), person(given = \"mikefc\", email = \"mikefc@coolbutuseless.com\", role = \"cph\", comment = \"Hash implementation based on Mike's xxhashlite\"), person(given = \"Yann\", family = \"Collet\", role = \"cph\", comment = \"Author of the embedded xxHash library\"), person(given = \"Posit, PBC\", role = c(\"cph\", \"fnd\")) )", - "License": "MIT + file LICENSE", - "ByteCompile": "true", - "Biarch": "true", - "Depends": [ - "R (>= 4.0.0)" - ], - "Imports": [ + "Version": "1.3.0", + "Source": "Repository", + "Requirements": [ + "R", "utils" - ], - "Suggests": [ - "cli (>= 3.1.0)", - "covr", - "crayon", - "desc", - "fs", - "glue", - "knitr", - "magrittr", - "methods", - "pillar", - "pkgload", - "rmarkdown", - "stats", - "testthat (>= 3.2.0)", - "tibble", - "usethis", - "vctrs (>= 0.2.3)", - "withr" - ], - "Enhances": [ - "winch" - ], - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3", - "URL": "https://rlang.r-lib.org, https://github.com/r-lib/rlang", - "BugReports": "https://github.com/r-lib/rlang/issues", - "Config/build/compilation-database": "true", - "Config/testthat/edition": "3", - "Config/Needs/website": "dplyr, tidyverse/tidytemplate", - "NeedsCompilation": "yes", - "Author": "Lionel Henry [aut, cre], Hadley Wickham [aut], mikefc [cph] (Hash implementation based on Mike's xxhashlite), Yann Collet [cph] (Author of the embedded xxHash library), Posit, PBC [cph, fnd]", - "Maintainer": "Lionel Henry ", - "Repository": "CRAN" + ] }, "rmarkdown": { "Package": "rmarkdown", - "Version": "2.30", - "Source": "Repository", - "Type": "Package", - "Title": "Dynamic Documents for R", - "Authors@R": "c( person(\"JJ\", \"Allaire\", , \"jj@posit.co\", role = \"aut\"), person(\"Yihui\", \"Xie\", , \"xie@yihui.name\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0003-0645-5666\")), person(\"Christophe\", \"Dervieux\", , \"cderv@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0003-4474-2498\")), person(\"Jonathan\", \"McPherson\", , \"jonathan@posit.co\", role = \"aut\"), person(\"Javier\", \"Luraschi\", role = \"aut\"), person(\"Kevin\", \"Ushey\", , \"kevin@posit.co\", role = \"aut\"), person(\"Aron\", \"Atkins\", , \"aron@posit.co\", role = \"aut\"), person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\"), person(\"Joe\", \"Cheng\", , \"joe@posit.co\", role = \"aut\"), person(\"Winston\", \"Chang\", , \"winston@posit.co\", role = \"aut\"), person(\"Richard\", \"Iannone\", , \"rich@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0003-3925-190X\")), person(\"Andrew\", \"Dunning\", role = \"ctb\", comment = c(ORCID = \"0000-0003-0464-5036\")), person(\"Atsushi\", \"Yasumoto\", role = c(\"ctb\", \"cph\"), comment = c(ORCID = \"0000-0002-8335-495X\", cph = \"Number sections Lua filter\")), person(\"Barret\", \"Schloerke\", role = \"ctb\"), person(\"Carson\", \"Sievert\", role = \"ctb\", comment = c(ORCID = \"0000-0002-4958-2844\")), person(\"Devon\", \"Ryan\", , \"dpryan79@gmail.com\", role = \"ctb\", comment = c(ORCID = \"0000-0002-8549-0971\")), person(\"Frederik\", \"Aust\", , \"frederik.aust@uni-koeln.de\", role = \"ctb\", comment = c(ORCID = \"0000-0003-4900-788X\")), person(\"Jeff\", \"Allen\", , \"jeff@posit.co\", role = \"ctb\"), person(\"JooYoung\", \"Seo\", role = \"ctb\", comment = c(ORCID = \"0000-0002-4064-6012\")), person(\"Malcolm\", \"Barrett\", role = \"ctb\"), person(\"Rob\", \"Hyndman\", , \"Rob.Hyndman@monash.edu\", role = \"ctb\"), person(\"Romain\", \"Lesur\", role = \"ctb\"), person(\"Roy\", \"Storey\", role = \"ctb\"), person(\"Ruben\", \"Arslan\", , \"ruben.arslan@uni-goettingen.de\", role = \"ctb\"), person(\"Sergio\", \"Oller\", role = \"ctb\"), person(given = \"Posit Software, PBC\", role = c(\"cph\", \"fnd\")), person(, \"jQuery UI contributors\", role = c(\"ctb\", \"cph\"), comment = \"jQuery UI library; authors listed in inst/rmd/h/jqueryui/AUTHORS.txt\"), person(\"Mark\", \"Otto\", role = \"ctb\", comment = \"Bootstrap library\"), person(\"Jacob\", \"Thornton\", role = \"ctb\", comment = \"Bootstrap library\"), person(, \"Bootstrap contributors\", role = \"ctb\", comment = \"Bootstrap library\"), person(, \"Twitter, Inc\", role = \"cph\", comment = \"Bootstrap library\"), person(\"Alexander\", \"Farkas\", role = c(\"ctb\", \"cph\"), comment = \"html5shiv library\"), person(\"Scott\", \"Jehl\", role = c(\"ctb\", \"cph\"), comment = \"Respond.js library\"), person(\"Ivan\", \"Sagalaev\", role = c(\"ctb\", \"cph\"), comment = \"highlight.js library\"), person(\"Greg\", \"Franko\", role = c(\"ctb\", \"cph\"), comment = \"tocify library\"), person(\"John\", \"MacFarlane\", role = c(\"ctb\", \"cph\"), comment = \"Pandoc templates\"), person(, \"Google, Inc.\", role = c(\"ctb\", \"cph\"), comment = \"ioslides library\"), person(\"Dave\", \"Raggett\", role = \"ctb\", comment = \"slidy library\"), person(, \"W3C\", role = \"cph\", comment = \"slidy library\"), person(\"Dave\", \"Gandy\", role = c(\"ctb\", \"cph\"), comment = \"Font-Awesome\"), person(\"Ben\", \"Sperry\", role = \"ctb\", comment = \"Ionicons\"), person(, \"Drifty\", role = \"cph\", comment = \"Ionicons\"), person(\"Aidan\", \"Lister\", role = c(\"ctb\", \"cph\"), comment = \"jQuery StickyTabs\"), person(\"Benct Philip\", \"Jonsson\", role = c(\"ctb\", \"cph\"), comment = \"pagebreak Lua filter\"), person(\"Albert\", \"Krewinkel\", role = c(\"ctb\", \"cph\"), comment = \"pagebreak Lua filter\") )", - "Description": "Convert R Markdown documents into a variety of formats.", - "License": "GPL-3", - "URL": "https://github.com/rstudio/rmarkdown, https://pkgs.rstudio.com/rmarkdown/", - "BugReports": "https://github.com/rstudio/rmarkdown/issues", - "Depends": [ - "R (>= 3.0)" - ], - "Imports": [ - "bslib (>= 0.2.5.1)", - "evaluate (>= 0.13)", - "fontawesome (>= 0.5.0)", - "htmltools (>= 0.5.1)", + "Version": "2.32", + "Source": "Repository", + "Requirements": [ + "bslib", + "evaluate", + "fontawesome", + "htmltools", "jquerylib", "jsonlite", - "knitr (>= 1.43)", + "knitr", "methods", - "tinytex (>= 0.31)", + "R", + "tinytex", "tools", "utils", - "xfun (>= 0.36)", - "yaml (>= 2.1.19)" - ], - "Suggests": [ - "digest", - "dygraphs", - "fs", - "rsconnect", - "downlit (>= 0.4.0)", - "katex (>= 1.4.0)", - "sass (>= 0.4.0)", - "shiny (>= 1.6.0)", - "testthat (>= 3.0.3)", - "tibble", - "vctrs", - "cleanrmd", - "withr (>= 2.4.2)", - "xml2" - ], - "VignetteBuilder": "knitr", - "Config/Needs/website": "rstudio/quillt, pkgdown", - "Config/testthat/edition": "3", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2", - "SystemRequirements": "pandoc (>= 1.14) - http://pandoc.org", - "NeedsCompilation": "no", - "Author": "JJ Allaire [aut], Yihui Xie [aut, cre] (ORCID: ), Christophe Dervieux [aut] (ORCID: ), Jonathan McPherson [aut], Javier Luraschi [aut], Kevin Ushey [aut], Aron Atkins [aut], Hadley Wickham [aut], Joe Cheng [aut], Winston Chang [aut], Richard Iannone [aut] (ORCID: ), Andrew Dunning [ctb] (ORCID: ), Atsushi Yasumoto [ctb, cph] (ORCID: , cph: Number sections Lua filter), Barret Schloerke [ctb], Carson Sievert [ctb] (ORCID: ), Devon Ryan [ctb] (ORCID: ), Frederik Aust [ctb] (ORCID: ), Jeff Allen [ctb], JooYoung Seo [ctb] (ORCID: ), Malcolm Barrett [ctb], Rob Hyndman [ctb], Romain Lesur [ctb], Roy Storey [ctb], Ruben Arslan [ctb], Sergio Oller [ctb], Posit Software, PBC [cph, fnd], jQuery UI contributors [ctb, cph] (jQuery UI library; authors listed in inst/rmd/h/jqueryui/AUTHORS.txt), Mark Otto [ctb] (Bootstrap library), Jacob Thornton [ctb] (Bootstrap library), Bootstrap contributors [ctb] (Bootstrap library), Twitter, Inc [cph] (Bootstrap library), Alexander Farkas [ctb, cph] (html5shiv library), Scott Jehl [ctb, cph] (Respond.js library), Ivan Sagalaev [ctb, cph] (highlight.js library), Greg Franko [ctb, cph] (tocify library), John MacFarlane [ctb, cph] (Pandoc templates), Google, Inc. [ctb, cph] (ioslides library), Dave Raggett [ctb] (slidy library), W3C [cph] (slidy library), Dave Gandy [ctb, cph] (Font-Awesome), Ben Sperry [ctb] (Ionicons), Drifty [cph] (Ionicons), Aidan Lister [ctb, cph] (jQuery StickyTabs), Benct Philip Jonsson [ctb, cph] (pagebreak Lua filter), Albert Krewinkel [ctb, cph] (pagebreak Lua filter)", - "Maintainer": "Yihui Xie ", - "Repository": "CRAN" + "xfun", + "yaml" + ] }, "rvg": { "Package": "rvg", - "Version": "0.4.0", - "Source": "Repository", - "Type": "Package", - "Title": "R Graphics Devices for 'Office' Vector Graphics Output", - "Authors@R": "c( person(\"David\", \"Gohel\", , \"david.gohel@ardata.fr\", role = c(\"aut\", \"cre\")), person(\"ArData\", role = \"cph\"), person(\"Bob\", \"Rudis\", role = \"ctb\", comment = \"the javascript code used by function set_attr\"), person(\"Francois\", \"Brunetti\", , \"francois.brunetti@lysis-consultants.fr\", role = \"ctb\", comment = \"clipping algorithms\") )", - "Description": "Vector Graphics devices for 'Microsoft PowerPoint' and 'Microsoft Excel'. Functions extending package 'officer' are provided to embed 'DrawingML' graphics into 'Microsoft PowerPoint' presentations and 'Microsoft Excel' workbooks.", - "License": "GPL-3", - "URL": "https://ardata-fr.github.io/officeverse/, https://davidgohel.github.io/rvg/", - "BugReports": "https://github.com/davidgohel/rvg/issues", - "Depends": [ - "R (>= 3.0)" - ], - "Imports": [ - "gdtools (>= 0.3.3)", + "Version": "0.4.2", + "Source": "Repository", + "Requirements": [ + "gdtools", "grDevices", - "officer (>= 0.6.2)", - "Rcpp (>= 0.12.12)", + "officer", + "R", + "Rcpp", "rlang", "systemfonts", - "xml2 (>= 1.0.0)" - ], - "Suggests": [ - "grid", - "testthat" - ], - "LinkingTo": [ - "Rcpp", - "systemfonts" - ], - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3", - "SystemRequirements": "libpng", - "NeedsCompilation": "yes", - "Author": "David Gohel [aut, cre], ArData [cph], Bob Rudis [ctb] (the javascript code used by function set_attr), Francois Brunetti [ctb] (clipping algorithms)", - "Maintainer": "David Gohel ", - "Repository": "CRAN" + "xml2" + ] }, "sass": { "Package": "sass", "Version": "0.4.10", "Source": "Repository", - "Type": "Package", - "Title": "Syntactically Awesome Style Sheets ('Sass')", - "Description": "An 'SCSS' compiler, powered by the 'LibSass' library. With this, R developers can use variables, inheritance, and functions to generate dynamic style sheets. The package uses the 'Sass CSS' extension language, which is stable, powerful, and CSS compatible.", - "Authors@R": "c( person(\"Joe\", \"Cheng\", , \"joe@rstudio.com\", \"aut\"), person(\"Timothy\", \"Mastny\", , \"tim.mastny@gmail.com\", \"aut\"), person(\"Richard\", \"Iannone\", , \"rich@rstudio.com\", \"aut\", comment = c(ORCID = \"0000-0003-3925-190X\")), person(\"Barret\", \"Schloerke\", , \"barret@rstudio.com\", \"aut\", comment = c(ORCID = \"0000-0001-9986-114X\")), person(\"Carson\", \"Sievert\", , \"carson@rstudio.com\", c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-4958-2844\")), person(\"Christophe\", \"Dervieux\", , \"cderv@rstudio.com\", c(\"ctb\"), comment = c(ORCID = \"0000-0003-4474-2498\")), person(family = \"RStudio\", role = c(\"cph\", \"fnd\")), person(family = \"Sass Open Source Foundation\", role = c(\"ctb\", \"cph\"), comment = \"LibSass library\"), person(\"Greter\", \"Marcel\", role = c(\"ctb\", \"cph\"), comment = \"LibSass library\"), person(\"Mifsud\", \"Michael\", role = c(\"ctb\", \"cph\"), comment = \"LibSass library\"), person(\"Hampton\", \"Catlin\", role = c(\"ctb\", \"cph\"), comment = \"LibSass library\"), person(\"Natalie\", \"Weizenbaum\", role = c(\"ctb\", \"cph\"), comment = \"LibSass library\"), person(\"Chris\", \"Eppstein\", role = c(\"ctb\", \"cph\"), comment = \"LibSass library\"), person(\"Adams\", \"Joseph\", role = c(\"ctb\", \"cph\"), comment = \"json.cpp\"), person(\"Trifunovic\", \"Nemanja\", role = c(\"ctb\", \"cph\"), comment = \"utf8.h\") )", - "License": "MIT + file LICENSE", - "URL": "https://rstudio.github.io/sass/, https://github.com/rstudio/sass", - "BugReports": "https://github.com/rstudio/sass/issues", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2", - "SystemRequirements": "GNU make", - "Imports": [ - "fs (>= 1.2.4)", - "rlang (>= 0.4.10)", - "htmltools (>= 0.5.1)", + "Requirements": [ + "fs", + "htmltools", "R6", - "rappdirs" - ], - "Suggests": [ - "testthat", - "knitr", - "rmarkdown", - "withr", - "shiny", - "curl" - ], - "VignetteBuilder": "knitr", - "Config/testthat/edition": "3", - "NeedsCompilation": "yes", - "Author": "Joe Cheng [aut], Timothy Mastny [aut], Richard Iannone [aut] (), Barret Schloerke [aut] (), Carson Sievert [aut, cre] (), Christophe Dervieux [ctb] (), RStudio [cph, fnd], Sass Open Source Foundation [ctb, cph] (LibSass library), Greter Marcel [ctb, cph] (LibSass library), Mifsud Michael [ctb, cph] (LibSass library), Hampton Catlin [ctb, cph] (LibSass library), Natalie Weizenbaum [ctb, cph] (LibSass library), Chris Eppstein [ctb, cph] (LibSass library), Adams Joseph [ctb, cph] (json.cpp), Trifunovic Nemanja [ctb, cph] (utf8.h)", - "Maintainer": "Carson Sievert ", - "Repository": "CRAN" + "rappdirs", + "rlang" + ] }, "scales": { "Package": "scales", "Version": "1.4.0", "Source": "Repository", - "Title": "Scale Functions for Visualization", - "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\"), person(\"Thomas Lin\", \"Pedersen\", , \"thomas.pedersen@posit.co\", role = c(\"cre\", \"aut\"), comment = c(ORCID = \"0000-0002-5147-4711\")), person(\"Dana\", \"Seidel\", role = \"aut\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", - "Description": "Graphical scales map data to aesthetics, and provide methods for automatically determining breaks and labels for axes and legends.", - "License": "MIT + file LICENSE", - "URL": "https://scales.r-lib.org, https://github.com/r-lib/scales", - "BugReports": "https://github.com/r-lib/scales/issues", - "Depends": [ - "R (>= 4.1)" - ], - "Imports": [ + "Requirements": [ "cli", - "farver (>= 2.0.3)", + "farver", "glue", "labeling", "lifecycle", + "R", "R6", "RColorBrewer", - "rlang (>= 1.1.0)", + "rlang", "viridisLite" - ], - "Suggests": [ - "bit64", - "covr", - "dichromat", - "ggplot2", - "hms (>= 0.5.0)", - "stringi", - "testthat (>= 3.0.0)" - ], - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Config/usethis/last-upkeep": "2025-04-23", - "Encoding": "UTF-8", - "LazyLoad": "yes", - "RoxygenNote": "7.3.2", - "NeedsCompilation": "no", - "Author": "Hadley Wickham [aut], Thomas Lin Pedersen [cre, aut] (), Dana Seidel [aut], Posit Software, PBC [cph, fnd] (03wc8by49)", - "Maintainer": "Thomas Lin Pedersen ", - "Repository": "CRAN" + ] }, "stringi": { "Package": "stringi", - "Version": "1.8.7", - "Source": "Repository", - "Date": "2025-03-27", - "Title": "Fast and Portable Character String Processing Facilities", - "Description": "A collection of character string/text/natural language processing tools for pattern searching (e.g., with 'Java'-like regular expressions or the 'Unicode' collation algorithm), random string generation, case mapping, string transliteration, concatenation, sorting, padding, wrapping, Unicode normalisation, date-time formatting and parsing, and many more. They are fast, consistent, convenient, and - thanks to 'ICU' (International Components for Unicode) - portable across all locales and platforms. Documentation about 'stringi' is provided via its website at and the paper by Gagolewski (2022, ).", - "URL": "https://stringi.gagolewski.com/, https://github.com/gagolews/stringi, https://icu.unicode.org/", - "BugReports": "https://github.com/gagolews/stringi/issues", - "SystemRequirements": "ICU4C (>= 61, optional)", - "Type": "Package", - "Depends": [ - "R (>= 3.4)" - ], - "Imports": [ + "Version": "1.8.9", + "Source": "Repository", + "Requirements": [ + "R", + "stats", "tools", - "utils", - "stats" - ], - "Biarch": "TRUE", - "License": "file LICENSE", - "Authors@R": "c(person(given = \"Marek\", family = \"Gagolewski\", role = c(\"aut\", \"cre\", \"cph\"), email = \"marek@gagolewski.com\", comment = c(ORCID = \"0000-0003-0637-6028\")), person(given = \"Bartek\", family = \"Tartanus\", role = \"ctb\"), person(\"Unicode, Inc. and others\", role=\"ctb\", comment = \"ICU4C source code, Unicode Character Database\") )", - "RoxygenNote": "7.3.2", - "Encoding": "UTF-8", - "NeedsCompilation": "yes", - "Author": "Marek Gagolewski [aut, cre, cph] (), Bartek Tartanus [ctb], Unicode, Inc. and others [ctb] (ICU4C source code, Unicode Character Database)", - "Maintainer": "Marek Gagolewski ", - "License_is_FOSS": "yes", - "Repository": "CRAN" + "utils" + ] }, "stringr": { "Package": "stringr", "Version": "1.6.0", "Source": "Repository", - "Title": "Simple, Consistent Wrappers for Common String Operations", - "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = c(\"aut\", \"cre\", \"cph\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "A consistent, simple and easy to use set of wrappers around the fantastic 'stringi' package. All function and argument names (and positions) are consistent, all functions deal with \"NA\"'s and zero length vectors in the same way, and the output from one function is easy to feed into the input of another.", - "License": "MIT + file LICENSE", - "URL": "https://stringr.tidyverse.org, https://github.com/tidyverse/stringr", - "BugReports": "https://github.com/tidyverse/stringr/issues", - "Depends": [ - "R (>= 4.1.0)" - ], - "Imports": [ + "Requirements": [ "cli", - "glue (>= 1.6.1)", - "lifecycle (>= 1.0.3)", + "glue", + "lifecycle", "magrittr", - "rlang (>= 1.0.0)", - "stringi (>= 1.5.3)", - "vctrs (>= 0.4.0)" - ], - "Suggests": [ - "covr", - "dplyr", - "gt", - "htmltools", - "htmlwidgets", - "knitr", - "rmarkdown", - "testthat (>= 3.0.0)", - "tibble" - ], - "VignetteBuilder": "knitr", - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/potools/style": "explicit", - "Config/testthat/edition": "3", - "Encoding": "UTF-8", - "LazyData": "true", - "RoxygenNote": "7.3.3", - "NeedsCompilation": "no", - "Author": "Hadley Wickham [aut, cre, cph], Posit Software, PBC [cph, fnd]", - "Maintainer": "Hadley Wickham ", - "Repository": "CRAN" + "R", + "rlang", + "stringi", + "vctrs" + ] }, "svglite": { "Package": "svglite", "Version": "2.2.2", "Source": "Repository", - "Title": "An 'SVG' Graphics Device", - "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\"), person(\"Lionel\", \"Henry\", , \"lionel@posit.co\", role = \"aut\"), person(\"Thomas Lin\", \"Pedersen\", , \"thomas.pedersen@posit.co\", role = c(\"cre\", \"aut\"), comment = c(ORCID = \"0000-0002-5147-4711\")), person(\"T Jake\", \"Luciani\", , \"jake@apache.org\", role = \"aut\"), person(\"Matthieu\", \"Decorde\", , \"matthieu.decorde@ens-lyon.fr\", role = \"aut\"), person(\"Vaudor\", \"Lise\", , \"lise.vaudor@ens-lyon.fr\", role = \"aut\"), person(\"Tony\", \"Plate\", role = \"ctb\", comment = \"Early line dashing code\"), person(\"David\", \"Gohel\", role = \"ctb\", comment = \"Line dashing code and early raster code\"), person(\"Yixuan\", \"Qiu\", role = \"ctb\", comment = \"Improved styles; polypath implementation\"), person(\"Håkon\", \"Malmedal\", role = \"ctb\", comment = \"Opacity code\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", - "Description": "A graphics device for R that produces 'Scalable Vector Graphics'. 'svglite' is a fork of the older 'RSvgDevice' package.", - "License": "GPL (>= 2)", - "URL": "https://svglite.r-lib.org, https://github.com/r-lib/svglite", - "BugReports": "https://github.com/r-lib/svglite/issues", - "Depends": [ - "R (>= 4.1)" - ], - "Imports": [ + "Requirements": [ "base64enc", "cli", - "lifecycle", - "rlang (>= 1.1.0)", - "systemfonts (>= 1.3.0)", - "textshaping (>= 0.3.0)" - ], - "Suggests": [ - "covr", - "fontquiver (>= 0.2.0)", - "htmltools", - "knitr", - "rmarkdown", - "testthat (>= 3.0.0)", - "xml2 (>= 1.0.0)" - ], - "LinkingTo": [ "cpp11", + "lifecycle", + "R", + "rlang", "systemfonts", "textshaping" - ], - "VignetteBuilder": "knitr", - "Config/build/compilation-database": "true", - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Config/usethis/last-upkeep": "2025-04-25", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2", - "SystemRequirements": "libpng", - "NeedsCompilation": "yes", - "Author": "Hadley Wickham [aut], Lionel Henry [aut], Thomas Lin Pedersen [cre, aut] (ORCID: ), T Jake Luciani [aut], Matthieu Decorde [aut], Vaudor Lise [aut], Tony Plate [ctb] (Early line dashing code), David Gohel [ctb] (Line dashing code and early raster code), Yixuan Qiu [ctb] (Improved styles; polypath implementation), Håkon Malmedal [ctb] (Opacity code), Posit Software, PBC [cph, fnd] (ROR: )", - "Maintainer": "Thomas Lin Pedersen ", - "Repository": "CRAN" + ] }, "sys": { "Package": "sys", "Version": "3.4.3", "Source": "Repository", - "Type": "Package", - "Title": "Powerful and Reliable Tools for Running System Commands in R", - "Authors@R": "c(person(\"Jeroen\", \"Ooms\", role = c(\"aut\", \"cre\"), email = \"jeroenooms@gmail.com\", comment = c(ORCID = \"0000-0002-4035-0289\")), person(\"Gábor\", \"Csárdi\", , \"csardi.gabor@gmail.com\", role = \"ctb\"))", - "Description": "Drop-in replacements for the base system2() function with fine control and consistent behavior across platforms. Supports clean interruption, timeout, background tasks, and streaming STDIN / STDOUT / STDERR over binary or text connections. Arguments on Windows automatically get encoded and quoted to work on different locales.", - "License": "MIT + file LICENSE", - "URL": "https://jeroen.r-universe.dev/sys", - "BugReports": "https://github.com/jeroen/sys/issues", - "Encoding": "UTF-8", - "RoxygenNote": "7.1.1", - "Suggests": [ - "unix (>= 1.4)", - "spelling", - "testthat" - ], - "Language": "en-US", - "NeedsCompilation": "yes", - "Author": "Jeroen Ooms [aut, cre] (), Gábor Csárdi [ctb]", - "Maintainer": "Jeroen Ooms ", - "Repository": "CRAN" + "Requirements": [] }, "systemfonts": { "Package": "systemfonts", - "Version": "1.3.1", - "Source": "Repository", - "Type": "Package", - "Title": "System Native Font Finding", - "Authors@R": "c( person(\"Thomas Lin\", \"Pedersen\", , \"thomas.pedersen@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-5147-4711\")), person(\"Jeroen\", \"Ooms\", , \"jeroen@berkeley.edu\", role = \"aut\", comment = c(ORCID = \"0000-0002-4035-0289\")), person(\"Devon\", \"Govett\", role = \"aut\", comment = \"Author of font-manager\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", - "Description": "Provides system native access to the font catalogue. As font handling varies between systems it is difficult to correctly locate installed fonts across different operating systems. The 'systemfonts' package provides bindings to the native libraries on Windows, macOS and Linux for finding font files that can then be used further by e.g. graphic devices. The main use is intended to be from compiled code but 'systemfonts' also provides access from R.", - "License": "MIT + file LICENSE", - "URL": "https://github.com/r-lib/systemfonts, https://systemfonts.r-lib.org", - "BugReports": "https://github.com/r-lib/systemfonts/issues", - "Depends": [ - "R (>= 3.2.0)" - ], - "Imports": [ + "Version": "1.3.2", + "Source": "Repository", + "Requirements": [ "base64enc", + "cpp11", "grid", "jsonlite", "lifecycle", + "R", "tools", "utils" - ], - "Suggests": [ - "covr", - "farver", - "ggplot2", - "graphics", - "knitr", - "ragg", - "rmarkdown", - "svglite", - "testthat (>= 2.1.0)" - ], - "LinkingTo": [ - "cpp11 (>= 0.2.1)" - ], - "VignetteBuilder": "knitr", - "Config/build/compilation-database": "true", - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/usethis/last-upkeep": "2025-04-23", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2", - "SystemRequirements": "fontconfig, freetype2", - "NeedsCompilation": "yes", - "Author": "Thomas Lin Pedersen [aut, cre] (ORCID: ), Jeroen Ooms [aut] (ORCID: ), Devon Govett [aut] (Author of font-manager), Posit Software, PBC [cph, fnd] (ROR: )", - "Maintainer": "Thomas Lin Pedersen ", - "Repository": "CRAN" + ] }, "textshaping": { "Package": "textshaping", - "Version": "1.0.4", - "Source": "Repository", - "Title": "Bindings to the 'HarfBuzz' and 'Fribidi' Libraries for Text Shaping", - "Authors@R": "c( person(\"Thomas Lin\", \"Pedersen\", , \"thomas.pedersen@posit.co\", role = c(\"cre\", \"aut\"), comment = c(ORCID = \"0000-0002-5147-4711\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", - "Description": "Provides access to the text shaping functionality in the 'HarfBuzz' library and the bidirectional algorithm in the 'Fribidi' library. 'textshaping' is a low-level utility package mainly for graphic devices that expands upon the font tool-set provided by the 'systemfonts' package.", - "License": "MIT + file LICENSE", - "URL": "https://github.com/r-lib/textshaping", - "BugReports": "https://github.com/r-lib/textshaping/issues", - "Depends": [ - "R (>= 3.2.0)" - ], - "Imports": [ + "Version": "1.0.5", + "Source": "Repository", + "Requirements": [ + "cpp11", "lifecycle", + "R", "stats", "stringi", - "systemfonts (>= 1.3.0)", + "systemfonts", "utils" - ], - "Suggests": [ - "covr", - "grDevices", - "grid", - "knitr", - "rmarkdown", - "testthat (>= 3.0.0)" - ], - "LinkingTo": [ - "cpp11 (>= 0.2.1)", - "systemfonts (>= 1.0.0)" - ], - "VignetteBuilder": "knitr", - "Config/build/compilation-database": "true", - "Config/testthat/edition": "3", - "Config/usethis/last-upkeep": "2025-04-23", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2", - "SystemRequirements": "freetype2, harfbuzz, fribidi", - "NeedsCompilation": "yes", - "Author": "Thomas Lin Pedersen [cre, aut] (ORCID: ), Posit Software, PBC [cph, fnd] (ROR: )", - "Maintainer": "Thomas Lin Pedersen ", - "Repository": "CRAN" + ] }, "tibble": { "Package": "tibble", "Version": "3.3.1", "Source": "Repository", - "Title": "Simple Data Frames", - "Authors@R": "c( person(\"Kirill\", \"Mller\", , \"kirill@cynkra.com\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-1416-3412\")), person(\"Hadley\", \"Wickham\", , \"hadley@rstudio.com\", role = \"aut\"), person(\"Romain\", \"Francois\", , \"romain@r-enthusiasts.com\", role = \"ctb\"), person(\"Jennifer\", \"Bryan\", , \"jenny@rstudio.com\", role = \"ctb\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", - "Description": "Provides a 'tbl_df' class (the 'tibble') with stricter checking and better formatting than the traditional data frame.", - "License": "MIT + file LICENSE", - "URL": "https://tibble.tidyverse.org/, https://github.com/tidyverse/tibble", - "BugReports": "https://github.com/tidyverse/tibble/issues", - "Depends": [ - "R (>= 3.4.0)" - ], - "Imports": [ + "Requirements": [ "cli", - "lifecycle (>= 1.0.0)", + "lifecycle", "magrittr", "methods", - "pillar (>= 1.8.1)", + "pillar", "pkgconfig", - "rlang (>= 1.0.2)", + "R", + "rlang", "utils", - "vctrs (>= 0.5.0)" - ], - "Suggests": [ - "bench", - "bit64", - "blob", - "brio", - "callr", - "DiagrammeR", - "dplyr", - "evaluate", - "formattable", - "ggplot2", - "here", - "hms", - "htmltools", - "knitr", - "lubridate", - "nycflights13", - "pkgload", - "purrr", - "rmarkdown", - "stringi", - "testthat (>= 3.0.2)", - "tidyr", - "withr" - ], - "VignetteBuilder": "knitr", - "Config/autostyle/rmd": "false", - "Config/autostyle/scope": "line_breaks", - "Config/autostyle/strict": "true", - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Config/testthat/parallel": "true", - "Config/testthat/start-first": "vignette-formats, as_tibble, add, invariants", - "Config/usethis/last-upkeep": "2025-06-07", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3.9000", - "NeedsCompilation": "yes", - "Author": "Kirill Mller [aut, cre] (ORCID: ), Hadley Wickham [aut], Romain Francois [ctb], Jennifer Bryan [ctb], Posit Software, PBC [cph, fnd] (ROR: )", - "Maintainer": "Kirill Mller ", - "Repository": "CRAN" + "vctrs" + ] }, "tidyr": { "Package": "tidyr", "Version": "1.3.2", "Source": "Repository", - "Title": "Tidy Messy Data", - "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = c(\"aut\", \"cre\")), person(\"Davis\", \"Vaughan\", , \"davis@posit.co\", role = \"aut\"), person(\"Maximilian\", \"Girlich\", role = \"aut\"), person(\"Kevin\", \"Ushey\", , \"kevin@posit.co\", role = \"ctb\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "Tools to help to create tidy data, where each column is a variable, each row is an observation, and each cell contains a single value. 'tidyr' contains tools for changing the shape (pivoting) and hierarchy (nesting and 'unnesting') of a dataset, turning deeply nested lists into rectangular data frames ('rectangling'), and extracting values out of string columns. It also includes tools for working with missing values (both implicit and explicit).", - "License": "MIT + file LICENSE", - "URL": "https://tidyr.tidyverse.org, https://github.com/tidyverse/tidyr", - "BugReports": "https://github.com/tidyverse/tidyr/issues", - "Depends": [ - "R (>= 4.1.0)" - ], - "Imports": [ - "cli (>= 3.4.1)", - "dplyr (>= 1.1.0)", + "Requirements": [ + "cli", + "cpp11", + "dplyr", "glue", - "lifecycle (>= 1.0.3)", + "lifecycle", "magrittr", - "purrr (>= 1.0.1)", - "rlang (>= 1.1.1)", - "stringr (>= 1.5.0)", - "tibble (>= 2.1.1)", - "tidyselect (>= 1.2.1)", + "purrr", + "R", + "rlang", + "stringr", + "tibble", + "tidyselect", "utils", - "vctrs (>= 0.5.2)" - ], - "Suggests": [ - "covr", - "data.table", - "knitr", - "readr", - "repurrrsive (>= 1.1.0)", - "rmarkdown", - "testthat (>= 3.0.0)" - ], - "LinkingTo": [ - "cpp11 (>= 0.4.0)" - ], - "VignetteBuilder": "knitr", - "Config/build/compilation-database": "true", - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Encoding": "UTF-8", - "LazyData": "true", - "RoxygenNote": "7.3.3", - "NeedsCompilation": "yes", - "Author": "Hadley Wickham [aut, cre], Davis Vaughan [aut], Maximilian Girlich [aut], Kevin Ushey [ctb], Posit Software, PBC [cph, fnd]", - "Maintainer": "Hadley Wickham ", - "Repository": "CRAN" + "vctrs" + ] }, "tidyselect": { "Package": "tidyselect", "Version": "1.2.1", "Source": "Repository", - "Title": "Select from a Set of Strings", - "Authors@R": "c( person(\"Lionel\", \"Henry\", , \"lionel@posit.co\", role = c(\"aut\", \"cre\")), person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "A backend for the selecting functions of the 'tidyverse'. It makes it easy to implement select-like functions in your own packages in a way that is consistent with other 'tidyverse' interfaces for selection.", - "License": "MIT + file LICENSE", - "URL": "https://tidyselect.r-lib.org, https://github.com/r-lib/tidyselect", - "BugReports": "https://github.com/r-lib/tidyselect/issues", - "Depends": [ - "R (>= 3.4)" - ], - "Imports": [ - "cli (>= 3.3.0)", - "glue (>= 1.3.0)", - "lifecycle (>= 1.0.3)", - "rlang (>= 1.0.4)", - "vctrs (>= 0.5.2)", + "Requirements": [ + "cli", + "glue", + "lifecycle", + "R", + "rlang", + "vctrs", "withr" - ], - "Suggests": [ - "covr", - "crayon", - "dplyr", - "knitr", - "magrittr", - "rmarkdown", - "stringr", - "testthat (>= 3.1.1)", - "tibble (>= 2.1.3)" - ], - "VignetteBuilder": "knitr", - "ByteCompile": "true", - "Config/testthat/edition": "3", - "Config/Needs/website": "tidyverse/tidytemplate", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.0.9000", - "NeedsCompilation": "yes", - "Author": "Lionel Henry [aut, cre], Hadley Wickham [aut], Posit Software, PBC [cph, fnd]", - "Maintainer": "Lionel Henry ", - "Repository": "CRAN" + ] }, "tinytex": { "Package": "tinytex", - "Version": "0.58", - "Source": "Repository", - "Type": "Package", - "Title": "Helper Functions to Install and Maintain TeX Live, and Compile LaTeX Documents", - "Authors@R": "c( person(\"Yihui\", \"Xie\", role = c(\"aut\", \"cre\", \"cph\"), email = \"xie@yihui.name\", comment = c(ORCID = \"0000-0003-0645-5666\")), person(given = \"Posit Software, PBC\", role = c(\"cph\", \"fnd\")), person(\"Christophe\", \"Dervieux\", role = \"ctb\", comment = c(ORCID = \"0000-0003-4474-2498\")), person(\"Devon\", \"Ryan\", role = \"ctb\", email = \"dpryan79@gmail.com\", comment = c(ORCID = \"0000-0002-8549-0971\")), person(\"Ethan\", \"Heinzen\", role = \"ctb\"), person(\"Fernando\", \"Cagua\", role = \"ctb\"), person() )", - "Description": "Helper functions to install and maintain the 'LaTeX' distribution named 'TinyTeX' (), a lightweight, cross-platform, portable, and easy-to-maintain version of 'TeX Live'. This package also contains helper functions to compile 'LaTeX' documents, and install missing 'LaTeX' packages automatically.", - "Imports": [ - "xfun (>= 0.48)" - ], - "Suggests": [ - "testit", - "rstudioapi" - ], - "License": "MIT + file LICENSE", - "URL": "https://github.com/rstudio/tinytex", - "BugReports": "https://github.com/rstudio/tinytex/issues", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3", - "NeedsCompilation": "no", - "Author": "Yihui Xie [aut, cre, cph] (ORCID: ), Posit Software, PBC [cph, fnd], Christophe Dervieux [ctb] (ORCID: ), Devon Ryan [ctb] (ORCID: ), Ethan Heinzen [ctb], Fernando Cagua [ctb]", - "Maintainer": "Yihui Xie ", - "Repository": "CRAN" + "Version": "0.61", + "Source": "Repository", + "Requirements": [ + "xfun" + ] }, "utf8": { "Package": "utf8", "Version": "1.2.6", "Source": "Repository", - "Title": "Unicode Text Processing", - "Authors@R": "c(person(given = c(\"Patrick\", \"O.\"), family = \"Perry\", role = c(\"aut\", \"cph\")), person(given = \"Kirill\", family = \"M\\u00fcller\", role = \"cre\", email = \"kirill@cynkra.com\", comment = c(ORCID = \"0000-0002-1416-3412\")), person(given = \"Unicode, Inc.\", role = c(\"cph\", \"dtc\"), comment = \"Unicode Character Database\"))", - "Description": "Process and print 'UTF-8' encoded international text (Unicode). Input, validate, normalize, encode, format, and display.", - "License": "Apache License (== 2.0) | file LICENSE", - "URL": "https://krlmlr.github.io/utf8/, https://github.com/krlmlr/utf8", - "BugReports": "https://github.com/krlmlr/utf8/issues", - "Depends": [ - "R (>= 2.10)" - ], - "Suggests": [ - "cli", - "covr", - "knitr", - "rlang", - "rmarkdown", - "testthat (>= 3.0.0)", - "withr" - ], - "VignetteBuilder": "knitr, rmarkdown", - "Config/testthat/edition": "3", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2.9000", - "NeedsCompilation": "yes", - "Author": "Patrick O. Perry [aut, cph], Kirill Mller [cre] (ORCID: ), Unicode, Inc. [cph, dtc] (Unicode Character Database)", - "Maintainer": "Kirill Mller ", - "Repository": "CRAN" + "Requirements": [ + "R" + ] }, "uuid": { "Package": "uuid", "Version": "1.2-2", "Source": "Repository", - "Title": "Tools for Generating and Handling of UUIDs", - "Author": "Simon Urbanek [aut, cre, cph] (https://urbanek.org, ORCID: ), Theodore Ts'o [aut, cph] (libuuid)", - "Maintainer": "Simon Urbanek ", - "Authors@R": "c(person(\"Simon\", \"Urbanek\", role=c(\"aut\",\"cre\",\"cph\"), email=\"Simon.Urbanek@r-project.org\", comment=c(\"https://urbanek.org\", ORCID=\"0000-0003-2297-1732\")), person(\"Theodore\",\"Ts'o\", email=\"tytso@thunk.org\", role=c(\"aut\",\"cph\"), comment=\"libuuid\"))", - "Depends": [ - "R (>= 2.9.0)" - ], - "Description": "Tools for generating and handling of UUIDs (Universally Unique Identifiers).", - "License": "MIT + file LICENSE", - "URL": "https://www.rforge.net/uuid", - "BugReports": "https://github.com/s-u/uuid/issues", - "NeedsCompilation": "yes", - "Repository": "CRAN" + "Requirements": [ + "R" + ] }, "vctrs": { "Package": "vctrs", - "Version": "0.7.1", - "Source": "Repository", - "Title": "Vector Helpers", - "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\"), person(\"Lionel\", \"Henry\", , \"lionel@posit.co\", role = \"aut\"), person(\"Davis\", \"Vaughan\", , \"davis@posit.co\", role = c(\"aut\", \"cre\")), person(\"data.table team\", role = \"cph\", comment = \"Radix sort based on data.table's forder() and their contribution to R's order()\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "Defines new notions of prototype and size that are used to provide tools for consistent and well-founded type-coercion and size-recycling, and are in turn connected to ideas of type- and size-stability useful for analysing function interfaces.", - "License": "MIT + file LICENSE", - "URL": "https://vctrs.r-lib.org/, https://github.com/r-lib/vctrs", - "BugReports": "https://github.com/r-lib/vctrs/issues", - "Depends": [ - "R (>= 4.0.0)" - ], - "Imports": [ - "cli (>= 3.4.0)", + "Version": "0.7.3", + "Source": "Repository", + "Requirements": [ + "cli", "glue", - "lifecycle (>= 1.0.3)", - "rlang (>= 1.1.7)" - ], - "Suggests": [ - "bit64", - "covr", - "crayon", - "dplyr (>= 0.8.5)", - "generics", - "knitr", - "pillar (>= 1.4.4)", - "pkgdown (>= 2.0.1)", - "rmarkdown", - "testthat (>= 3.0.0)", - "tibble (>= 3.1.3)", - "waldo (>= 0.2.0)", - "withr", - "xml2", - "zeallot" - ], - "VignetteBuilder": "knitr", - "Config/build/compilation-database": "true", - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Config/testthat/parallel": "true", - "Encoding": "UTF-8", - "Language": "en-GB", - "RoxygenNote": "7.3.3", - "NeedsCompilation": "yes", - "Author": "Hadley Wickham [aut], Lionel Henry [aut], Davis Vaughan [aut, cre], data.table team [cph] (Radix sort based on data.table's forder() and their contribution to R's order()), Posit Software, PBC [cph, fnd]", - "Maintainer": "Davis Vaughan ", - "Repository": "CRAN" + "lifecycle", + "R", + "rlang" + ] }, "viridisLite": { "Package": "viridisLite", "Version": "0.4.3", "Source": "Repository", - "Type": "Package", - "Title": "Colorblind-Friendly Color Maps (Lite Version)", - "Date": "2026-02-03", - "Authors@R": "c( person(\"Simon\", \"Garnier\", email = \"garnier@njit.edu\", role = c(\"aut\", \"cre\")), person(\"Noam\", \"Ross\", email = \"noam.ross@gmail.com\", role = c(\"ctb\", \"cph\")), person(\"Bob\", \"Rudis\", email = \"bob@rud.is\", role = c(\"ctb\", \"cph\")), person(\"Marco\", \"Sciaini\", email = \"sciaini.marco@gmail.com\", role = c(\"ctb\", \"cph\")), person(\"Antônio Pedro\", \"Camargo\", role = c(\"ctb\", \"cph\")), person(\"Cédric\", \"Scherer\", email = \"scherer@izw-berlin.de\", role = c(\"ctb\", \"cph\")) )", - "Maintainer": "Simon Garnier ", - "Description": "Color maps designed to improve graph readability for readers with common forms of color blindness and/or color vision deficiency. The color maps are also perceptually-uniform, both in regular form and also when converted to black-and-white for printing. This is the 'lite' version of the 'viridis' package that also contains 'ggplot2' bindings for discrete and continuous color and fill scales and can be found at .", - "License": "MIT + file LICENSE", - "Encoding": "UTF-8", - "Depends": [ - "R (>= 2.10)" - ], - "Suggests": [ - "hexbin (>= 1.27.0)", - "ggplot2 (>= 1.0.1)", - "testthat", - "covr" - ], - "URL": "https://sjmgarnier.github.io/viridisLite/, https://github.com/sjmgarnier/viridisLite/", - "BugReports": "https://github.com/sjmgarnier/viridisLite/issues/", - "RoxygenNote": "7.3.3", - "NeedsCompilation": "no", - "Author": "Simon Garnier [aut, cre], Noam Ross [ctb, cph], Bob Rudis [ctb, cph], Marco Sciaini [ctb, cph], Antônio Pedro Camargo [ctb, cph], Cédric Scherer [ctb, cph]", - "Repository": "CRAN" + "Requirements": [ + "R" + ] }, "withr": { "Package": "withr", - "Version": "3.0.2", + "Version": "3.0.3", "Source": "Repository", - "Title": "Run Code 'With' Temporarily Modified Global State", - "Authors@R": "c( person(\"Jim\", \"Hester\", role = \"aut\"), person(\"Lionel\", \"Henry\", , \"lionel@posit.co\", role = c(\"aut\", \"cre\")), person(\"Kirill\", \"Müller\", , \"krlmlr+r@mailbox.org\", role = \"aut\"), person(\"Kevin\", \"Ushey\", , \"kevinushey@gmail.com\", role = \"aut\"), person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"aut\"), person(\"Winston\", \"Chang\", role = \"aut\"), person(\"Jennifer\", \"Bryan\", role = \"ctb\"), person(\"Richard\", \"Cotton\", role = \"ctb\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")) )", - "Description": "A set of functions to run code 'with' safely and temporarily modified global state. Many of these functions were originally a part of the 'devtools' package, this provides a simple package with limited dependencies to provide access to these functions.", - "License": "MIT + file LICENSE", - "URL": "https://withr.r-lib.org, https://github.com/r-lib/withr#readme", - "BugReports": "https://github.com/r-lib/withr/issues", - "Depends": [ - "R (>= 3.6.0)" - ], - "Imports": [ + "Requirements": [ "graphics", - "grDevices" - ], - "Suggests": [ - "callr", - "DBI", - "knitr", - "methods", - "rlang", - "rmarkdown (>= 2.12)", - "RSQLite", - "testthat (>= 3.0.0)" - ], - "VignetteBuilder": "knitr", - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2", - "Collate": "'aaa.R' 'collate.R' 'connection.R' 'db.R' 'defer-exit.R' 'standalone-defer.R' 'defer.R' 'devices.R' 'local_.R' 'with_.R' 'dir.R' 'env.R' 'file.R' 'language.R' 'libpaths.R' 'locale.R' 'makevars.R' 'namespace.R' 'options.R' 'par.R' 'path.R' 'rng.R' 'seed.R' 'wrap.R' 'sink.R' 'tempfile.R' 'timezone.R' 'torture.R' 'utils.R' 'with.R'", - "NeedsCompilation": "no", - "Author": "Jim Hester [aut], Lionel Henry [aut, cre], Kirill Müller [aut], Kevin Ushey [aut], Hadley Wickham [aut], Winston Chang [aut], Jennifer Bryan [ctb], Richard Cotton [ctb], Posit Software, PBC [cph, fnd]", - "Maintainer": "Lionel Henry ", - "Repository": "CRAN" + "grDevices", + "R" + ] }, "xfun": { "Package": "xfun", - "Version": "0.56", - "Source": "Repository", - "Type": "Package", - "Title": "Supporting Functions for Packages Maintained by 'Yihui Xie'", - "Authors@R": "c( person(\"Yihui\", \"Xie\", role = c(\"aut\", \"cre\", \"cph\"), email = \"xie@yihui.name\", comment = c(ORCID = \"0000-0003-0645-5666\", URL = \"https://yihui.org\")), person(\"Wush\", \"Wu\", role = \"ctb\"), person(\"Daijiang\", \"Li\", role = \"ctb\"), person(\"Xianying\", \"Tan\", role = \"ctb\"), person(\"Salim\", \"Brüggemann\", role = \"ctb\", email = \"salim-b@pm.me\", comment = c(ORCID = \"0000-0002-5329-5987\")), person(\"Christophe\", \"Dervieux\", role = \"ctb\"), person() )", - "Description": "Miscellaneous functions commonly used in other packages maintained by 'Yihui Xie'.", - "Depends": [ - "R (>= 3.2.0)" - ], - "Imports": [ + "Version": "0.62", + "Source": "Repository", + "Requirements": [ "grDevices", + "R", "stats", "tools" - ], - "Suggests": [ - "testit", - "parallel", - "codetools", - "methods", - "rstudioapi", - "tinytex (>= 0.30)", - "mime", - "litedown (>= 0.6)", - "commonmark", - "knitr (>= 1.50)", - "remotes", - "pak", - "curl", - "xml2", - "jsonlite", - "magick", - "yaml", - "data.table", - "qs2" - ], - "License": "MIT + file LICENSE", - "URL": "https://github.com/yihui/xfun", - "BugReports": "https://github.com/yihui/xfun/issues", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3", - "VignetteBuilder": "litedown", - "NeedsCompilation": "yes", - "Author": "Yihui Xie [aut, cre, cph] (ORCID: , URL: https://yihui.org), Wush Wu [ctb], Daijiang Li [ctb], Xianying Tan [ctb], Salim Brüggemann [ctb] (ORCID: ), Christophe Dervieux [ctb]", - "Maintainer": "Yihui Xie ", - "Repository": "CRAN" + ] }, "xml2": { "Package": "xml2", - "Version": "1.5.2", + "Version": "1.6.0", "Source": "Repository", - "Title": "Parse XML", - "Authors@R": "c( person(\"Hadley\", \"Wickham\", role = \"aut\"), person(\"Jim\", \"Hester\", role = \"aut\"), person(\"Jeroen\", \"Ooms\", email = \"jeroenooms@gmail.com\", role = c(\"aut\", \"cre\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\")), person(\"R Foundation\", role = \"ctb\", comment = \"Copy of R-project homepage cached as example\") )", - "Description": "Bindings to 'libxml2' for working with XML data using a simple, consistent interface based on 'XPath' expressions. Also supports XML schema validation; for 'XSLT' transformations see the 'xslt' package.", - "License": "MIT + file LICENSE", - "URL": "https://xml2.r-lib.org, https://r-lib.r-universe.dev/xml2", - "BugReports": "https://github.com/r-lib/xml2/issues", - "Depends": [ - "R (>= 3.6.0)" - ], - "Imports": [ + "Requirements": [ "cli", "methods", - "rlang (>= 1.1.0)" - ], - "Suggests": [ - "covr", - "curl", - "httr", - "knitr", - "mockery", - "rmarkdown", - "testthat (>= 3.2.0)", - "xslt" - ], - "VignetteBuilder": "knitr", - "Config/Needs/website": "tidyverse/tidytemplate", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3", - "SystemRequirements": "libxml2: libxml2-dev (deb), libxml2-devel (rpm)", - "Collate": "'S4.R' 'as_list.R' 'xml_parse.R' 'as_xml_document.R' 'classes.R' 'format.R' 'import-standalone-obj-type.R' 'import-standalone-purrr.R' 'import-standalone-types-check.R' 'init.R' 'nodeset_apply.R' 'paths.R' 'utils.R' 'xml2-package.R' 'xml_attr.R' 'xml_children.R' 'xml_document.R' 'xml_find.R' 'xml_missing.R' 'xml_modify.R' 'xml_name.R' 'xml_namespaces.R' 'xml_node.R' 'xml_nodeset.R' 'xml_path.R' 'xml_schema.R' 'xml_serialize.R' 'xml_structure.R' 'xml_text.R' 'xml_type.R' 'xml_url.R' 'xml_write.R' 'zzz.R'", - "Config/testthat/edition": "3", - "NeedsCompilation": "yes", - "Author": "Hadley Wickham [aut], Jim Hester [aut], Jeroen Ooms [aut, cre], Posit Software, PBC [cph, fnd], R Foundation [ctb] (Copy of R-project homepage cached as example)", - "Maintainer": "Jeroen Ooms ", - "Repository": "CRAN" + "R", + "rlang" + ] }, "yaml": { "Package": "yaml", "Version": "2.3.12", "Source": "Repository", - "Type": "Package", - "Title": "Methods to Convert R Data to YAML and Back", - "Authors@R": "c( person(\"Hadley\", \"Wickham\", , \"hadley@posit.co\", role = \"cre\", comment = c(ORCID = \"0000-0003-4757-117X\")), person(\"Shawn\", \"Garbett\", , \"shawn.garbett@vumc.org\", role = \"ctb\", comment = c(ORCID = \"0000-0003-4079-5621\")), person(\"Jeremy\", \"Stephens\", role = c(\"aut\", \"ctb\")), person(\"Kirill\", \"Simonov\", role = \"aut\"), person(\"Yihui\", \"Xie\", role = \"ctb\", comment = c(ORCID = \"0000-0003-0645-5666\")), person(\"Zhuoer\", \"Dong\", role = \"ctb\"), person(\"Jeffrey\", \"Horner\", role = \"ctb\"), person(\"reikoch\", role = \"ctb\"), person(\"Will\", \"Beasley\", role = \"ctb\", comment = c(ORCID = \"0000-0002-5613-5006\")), person(\"Brendan\", \"O'Connor\", role = \"ctb\"), person(\"Michael\", \"Quinn\", role = \"ctb\"), person(\"Charlie\", \"Gao\", role = \"ctb\"), person(c(\"Gregory\", \"R.\"), \"Warnes\", role = \"ctb\"), person(c(\"Zhian\", \"N.\"), \"Kamvar\", role = \"ctb\") )", - "Description": "Implements the 'libyaml' 'YAML' 1.1 parser and emitter () for R.", - "License": "BSD_3_clause + file LICENSE", - "URL": "https://yaml.r-lib.org, https://github.com/r-lib/yaml/", - "BugReports": "https://github.com/r-lib/yaml/issues", - "Suggests": [ - "knitr", - "rmarkdown", - "testthat (>= 3.0.0)" - ], - "Config/testthat/edition": "3", - "Config/Needs/website": "tidyverse/tidytemplate", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.3", - "VignetteBuilder": "knitr", - "NeedsCompilation": "yes", - "Author": "Hadley Wickham [cre] (ORCID: ), Shawn Garbett [ctb] (ORCID: ), Jeremy Stephens [aut, ctb], Kirill Simonov [aut], Yihui Xie [ctb] (ORCID: ), Zhuoer Dong [ctb], Jeffrey Horner [ctb], reikoch [ctb], Will Beasley [ctb] (ORCID: ), Brendan O'Connor [ctb], Michael Quinn [ctb], Charlie Gao [ctb], Gregory R. Warnes [ctb], Zhian N. Kamvar [ctb]", - "Maintainer": "Hadley Wickham ", - "Repository": "CRAN" + "Requirements": [] }, "zip": { "Package": "zip", - "Version": "2.3.3", - "Source": "Repository", - "Title": "Cross-Platform 'zip' Compression", - "Authors@R": "c( person(\"Gábor\", \"Csárdi\", , \"csardi.gabor@gmail.com\", role = c(\"aut\", \"cre\")), person(\"Kuba\", \"Podgórski\", role = \"ctb\"), person(\"Rich\", \"Geldreich\", role = \"ctb\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )", - "Description": "Cross-Platform 'zip' Compression Library. A replacement for the 'zip' function, that does not require any additional external tools on any platform.", - "License": "MIT + file LICENSE", - "URL": "https://github.com/r-lib/zip, https://r-lib.github.io/zip/", - "BugReports": "https://github.com/r-lib/zip/issues", - "Suggests": [ - "covr", - "pillar", - "processx", - "R6", - "testthat", - "withr" - ], - "Config/Needs/website": "tidyverse/tidytemplate", - "Config/testthat/edition": "3", - "Config/usethis/last-upkeep": "2025-05-07", - "Encoding": "UTF-8", - "RoxygenNote": "7.3.2.9000", - "NeedsCompilation": "yes", - "Author": "Gábor Csárdi [aut, cre], Kuba Podgórski [ctb], Rich Geldreich [ctb], Posit Software, PBC [cph, fnd] (ROR: )", - "Maintainer": "Gábor Csárdi ", - "Repository": "CRAN" + "Version": "3.0.2", + "Source": "Repository", + "Requirements": [ + "cli" + ] } } } From 58b1393917ed7f0cf748296be93adeffec2019d2 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Mon, 19 Oct 2026 15:51:06 +0200 Subject: [PATCH 36/44] mds-zoo: expand the zoo analysis (Zoo H-L) and make it results-rich Per the owner brief: 'make the multidataset zoo test much more expansive, add way more results (also show the loaded data!) and check for sensible option flows.' New control families (Zoo A-G existed before): - Zoo H: scalar block - TextField (textH), IntegerField (rowsH 1-10, default 5), DoubleField (doubleH), Slider (sliderH), RadioButtonGroup (radioH mean/sum) and a CheckBox gate (gateH) with a nested gated DoubleField (gatedH, enabled only while the gate is open). - Zoo I: DEPENDENT DropDown (comboI) whose option list follows Zoo A's assigned list. Also fixes a silent QML bug found on the way: Zoo A's combobox used `source: zooVarsA` - a bare id, a QML ReferenceError, so the dropdown silently had NO option list; a source must be the string name ("zooVarsA"). With a working source a dropdown inherits its SOURCE's host namespace (pinned by the verifier: comboA/comboI resolve in the source host's slice, not the analysis level). - Zoo J: AssignedPairsVariablesList (pairsJ) - pairs its two columns into 2-component terms; RPC must send the flat value ["Score","Age"] with flat types, the desktop pairs them. - Zoo K: FactorsForm (factorsK) fed by assignedK. Findings pinned in comments + R docs: inner panels are NOT hosts (terms encode through the ANALYSIS level, D3.2 deferral); a factor term must be a member of the form's SOURCE list or the model sync silently drops it; kept terms are RE-TYPED by the form (Age arrived ordinal); and the previously-fatal nominal-term case is fixed C++-side (mds-plan post in jasp-desktop, run 18 pins the honest row). - Zoo L: bound SimpleTableView (tableL) whose row count follows rowsH; its cells are USED by the cross-slice computation. Results side (R/multiDataSetControls.R) - the loaded data is now shown: - dataPreviewTables: one 'Loaded data' table PER SLICE with every column's decoded name and its first rowsH values (the whole qualified-name -> encoded-token -> correct-slice chain made visible); duplicate decoded labels get a ' (k)' suffix so the schema never collides. - dataSetOverview, controlsProvenance (extended with the new families), scalarEchoTable (echo + used-in, slider documented as echo-only by design), flowsTable (5 flow demonstrations with observed values), crossSliceTable (interaction product, cross-host product, tableview multipliers, real-field scaling, radio-driven statistic - value-level), encodeDemoTable (Zoo E's levels APPLIED to a real column; matches the factor name against DECODED column names - the slices carry encoded tokens), gatedResultTable with dependencies="gateH" so a closed gate means the child is ABSENT, not empty (jaspBase destroys it on the option change). Wrapper regenerated (jaspSyntax::generateAnalysisWrapper, no datasets loaded - clean defaults, empty host selections). --- R/multiDataSetControls.R | 540 +++++++++++++++++++++++++- R/multiDataSetControlsFuncWrapper.R | 20 +- inst/qml/testMultiDataSetControls.qml | 180 ++++++++- 3 files changed, 732 insertions(+), 8 deletions(-) diff --git a/R/multiDataSetControls.R b/R/multiDataSetControls.R index 3d60288..59b9309 100644 --- a/R/multiDataSetControls.R +++ b/R/multiDataSetControls.R @@ -20,7 +20,55 @@ # textarea-host Zoo F - R-code textarea: used columns encoded through its own host # bindto-cross-host Zoo G - typed copy bound across differently-selected hosts: keeps the # SOURCE filterId, encodes through the SOURCE's view (D6.2) +# dropdown-dependent Zoo I - dependent dropdown: option LIST driven by assignedA's value; its +# value encodes through the SOURCE's host namespace (a dropdown +# with a source inherits the source's selection host) +# pairs-list Zoo J - AssignedPairsVariablesList: its 2 columns are PAIRED into +# 2-component terms (bound value [["Score","Age"]]); per-pair +# resolution + correlation +# factors-form Zoo K - FactorsForm (the FactorsList family): per factor name/title + +# terms. FINDING: the form's inner panels are not hosts - their +# terms encode through the ANALYSIS-level namespace, NOT the +# source list's host (nested provenance is explicitly future +# work, see factorsformbase.cpp D3.2 NOTE); pinned, not faked. +# REGRESSION PINNED C++-SIDE (mds-plan post): a factor term whose +# column the analysis-level namespace lacks (e.g. a NOMINAL Group +# term while the shown dataset is DataSet 2) used to FATAL the +# whole run - rbridge_readDataSetRequested refused "Group.nominal" +# ("not a columnname at all") because the collector inserted +# explicitly-typed terms without an existence check (pre-existing +# gap, reachable only since the aware stamping). The collector now +# refuses to queue columns the resolved namespace does not have +# (Common/columnencoder.cpp) and the zoo asserts the honest +# unresolved row instead of a fatal. +# tableview Zoo L - bound TableView (row count driven by Zoo H's integer); cells +# are plain values (no provenance - not a Terms option) and are +# USED in the cross-slice computation, not just echoed # scalar M5 contract - plain values, no provenance anywhere +# +# RESULTS INVENTORY (the point of the zoo: data and flows must be VISIBLE, mds-plan "show the +# loaded data"): +# dataSetOverview one row per slice (unchanged) +# dataPreview_ THE LOADED DATA: per slice, every column's name AND first `rowsH` VALUES +# (head of data per slice) - proves the qualified-name -> decoded-token -> +# correct-slice chain end to end, visible in the results panel. Row count +# driven by Zoo H's integer field (rowsH). +# controlsProvenance per family: every received value, its row children, resolved slice +# scalarEchoes scalar families as received + WHERE each is used +# optionFlows the flow demonstrations: dependent dropdown's effective selection, +# checkbox gate state, row-count effect, radio statistic +# crossSlice computations ACROSS slices/lists of different provenance: interaction +# product, cross-host column product, pair correlation, tableL multipliers +# applied per slice, scale factor, radio-driven statistic +# encodeDemo the encodeThis/FactorLevelList transformation APPLIED: factor/level +# names from Zoo E used to recode an actual column +# gatedResult the checkbox-gated sub-result: created ONLY when gateH is on (a gated +# result must be absent, not an empty table) +# metaDump the PROVENANCE_AUDIT (unchanged) +# +# Where a family cannot produce a computation for a principled reason it says so in its row +# (sliderH: a bare fraction has no sensible slice computation - echo only; tableL cells: plain +# values by design) - documented, not faked. multiDataSetControlsFuncInternal <- function(jaspResults, dataset, options, datasets = NULL) { aware <- !is.null(datasets) @@ -28,13 +76,24 @@ multiDataSetControlsFuncInternal <- function(jaspResults, dataset, options, data jaspResults[["explanation"]] <- createJaspHtml(paste0( if (aware) paste0("Aware run. Component zoo received ", length(datasets), - " slice(s) as `datasets`; every family below echoes what it received.") + " slice(s) as `datasets`; every family below echoes what it received, the loaded ", + "data is shown per slice (first ", .intOption(options$rowsH, 5L), " rows) and the ", + "option flows are demonstrated with real computations.") else "Non-aware run. No `datasets` argument: this analysis must run multiDataSetAware for the routing tables to mean anything.", "
      See the check list at the top of Tools/verifyMultiDataSetRpc.py's zoo section for what each family proves.")) dataSetOverviewTable(jaspResults, datasets, dataset) + dataPreviewTables(jaspResults, datasets, options) controlsProvenanceTable(jaspResults, options, datasets) + scalarEchoTable(jaspResults, options) + flowsTable(jaspResults, options, datasets) + crossSliceTable(jaspResults, options, datasets) + encodeDemoTable(jaspResults, options, datasets) + + # the checkbox-gated sub-result: a GATE, so the object must not exist while closed + if (isTRUE(options$gateH)) + gatedResultTable(jaspResults, options, datasets) if (isTRUE(options$showMeta)) metaDump(jaspResults, options) @@ -64,6 +123,82 @@ multiDataSetControlsFuncInternal <- function(jaspResults, dataset, options, data .fillDescription(list(n = NA_integer_, values = ""), datasets[[key]][[token]]) } +# scalar option readers: scalars are never provenanced (M5), so they arrive as plain values +.intOption <- function(x, default = NA_integer_) { + if (is.null(x) || length(x) != 1 || is.na(x)) return(default) + v <- suppressWarnings(as.integer(x)) + if (is.na(v)) default else v +} +.numOption <- function(x, default = NA_real_) { + if (is.null(x) || length(x) != 1 || is.na(x)) return(default) + v <- suppressWarnings(as.numeric(x)) + if (is.na(v)) default else v +} +.strOption <- function(x, default = "") { + if (is.null(x) || length(x) != 1 || is.na(x)) return(default) + x <- trimws(as.character(x)) + if (!nzchar(x) || identical(x, "0")) default else x # '0' is the empty combobox sentinel +} + +# the first scale column of a slice (for computations), with its provenance label +.firstScaleColumn <- function(datasets, keys = names(datasets)) { + for (key in keys) { + df <- datasets[[key]] + for (cn in names(df)) { + col <- df[[cn]] + if (is.numeric(col) && sum(!is.na(col)) > 1) + return(list(key = key, column = cn, values = as.numeric(col))) + } + } + NULL +} + +.statLabel <- function(stat, values) { + if (is.null(values) || !length(values)) return("n/a") + if (identical(stat, "sum")) sprintf("sum %.2f", sum(values, na.rm = TRUE)) + else sprintf("mean %.2f", mean(values, na.rm = TRUE)) +} + +# ------------------------------------------------------------------ THE LOADED DATA (a) +# One table per slice: every column's name (decoded via jaspBase::decodeName - the engine keeps +# the same deterministic strict table as the desktop) and its first `rowsH` VALUES - the whole +# qualified-name -> decoded-token -> correct-slice chain, visible. Field names are passed DECODED +# (a non-token passes through the strict table untouched), with a " (k)" suffix when one slice +# holds several type-variant tokens for the same column (e.g. the same column encoded once as +# scale and once as ordinal) so the schema never collides. +dataPreviewTables <- function(jaspResults, datasets, options) { + if (is.null(datasets)) return(invisible()) + maxRows <- max(.intOption(options$rowsH, 5L), 1L) + + for (key in names(datasets)) { + df <- datasets[[key]] + meta <- .sliceMeta(key, datasets) + table <- createJaspTable(paste0("Loaded data - ", meta$name, " (slice ", key, ", ", + nrow(df), " rows), first ", min(maxRows, nrow(df)), " rows")) + jaspResults[[paste0("dataPreview_", key)]] <- table + + table$addColumnInfo("row", title = "row #") + seen <- character() + fields <- character() + for (cn in names(df)) { + label <- tryCatch(jaspBase::decodeName(cn), error = function(e) cn) + if (label %in% seen) label <- paste0(label, " (", sum(seen == label) + 1, ")") + seen <- c(seen, label) + fields[[cn]] <- label + table$addColumnInfo(label, title = label) + } + + n <- min(maxRows, nrow(df)) + rows <- data.frame(row = seq_len(n)) + for (cn in names(df)) { + col <- df[[cn]] + if (is.factor(col)) col <- as.character(col) + rows[[fields[[cn]]]] <- utils::head(col, n) + } + table$setData(rows) + } +} + # ------------------------------------------------------------------ the echo table controlsProvenanceTable <- function(jaspResults, options, datasets) { @@ -93,10 +228,11 @@ controlsProvenanceTable <- function(jaspResults, options, datasets) { stringsAsFactors = FALSE, check.names = FALSE)) } - # --- plain term lists: Zoo A / Zoo B / Zoo G ---------------------------------------------------- + # --- plain term lists: Zoo A / Zoo B / Zoo G / Zoo K's source ----------------------------------- for (entry in list( list(fam = "assigned-list", opt = "assignedA", vals = .dependentValues(options$assignedA)), list(fam = "assigned-host", opt = "assignedB", vals = .dependentValues(options$assignedB)), + list(fam = "factors-source", opt = "assignedK", vals = .dependentValues(options$assignedK)), list(fam = "bindto-cross-host", opt = "boundG", vals = .dependentValues(options$boundG)))) { for (value in entry$vals) { key <- .resolveSlice(value, datasets) @@ -105,6 +241,68 @@ controlsProvenanceTable <- function(jaspResults, options, datasets) { } } + # --- dependent dropdown (Zoo I): plain string value whose OPTION LIST is driven by assignedA ---- + comboI <- .strOption(options$comboI, default = "") + if (nzchar(comboI)) { + key <- .resolveSlice(comboI, datasets) + desc <- .describeValue(key, comboI, datasets) + rows <- addRow(rows, "dropdown-dependent", "comboI", comboI, key = key, n = desc$n, values = desc$values) + } + + # --- paired list (Zoo J): AssignedPairsVariablesList pairs its 2 columns into 2-component + # terms (bound value [["Score","Age"]]) - one row per PAIR, both components resolved, and a + # real computation per pair (correlation of the two paired columns in their shared slice). + pairsJ <- options$pairsJ + if (!is.null(pairsJ) && is.list(pairsJ)) { + pairRows <- if (!is.null(pairsJ$value)) pairsJ$value else pairsJ + pairIdx <- 0 + for (row in pairRows) { + comps <- as.character(unlist(row)) + if (length(comps) == 0) next + pairIdx <- pairIdx + 1 + keys <- vapply(comps, function(c) { k <- .resolveSlice(c, datasets); if (is.null(k)) "-" else k }, character(1)) + joint <- if (length(unique(keys)) == 1 && keys[[1]] != "-") keys[[1]] else NULL + child <- paste0("pair ", pairIdx, ": ", + paste(comps, "(", keys, ")", sep = "", collapse = " + ")) + corTxt <- "" + if (!is.null(joint) && length(comps) == 2) { + c1 <- datasets[[joint]][[comps[[1]]]]; c2 <- datasets[[joint]][[comps[[2]]]] + if (is.numeric(c1) && is.numeric(c2) && sum(complete.cases(c1, c2)) > 2) + corTxt <- sprintf("r = %.3f", stats::cor(c1, c2, use = "complete.obs")) + } + rows <- addRow(rows, "pairs-list", "pairsJ", paste(comps, collapse = " + "), + rowChild = child, key = joint, values = if (nzchar(corTxt)) corTxt else + paste0("slices: ", paste(unique(keys), collapse = "/"))) + } + } + + # --- factors form (Zoo K): per factor name + title + its terms' slices --------------------------- + factorsK <- options$factorsK + if (!is.null(factorsK) && is.list(factorsK)) { + for (f in factorsK) { + if (!is.list(f) || is.null(f$name)) next + vars <- as.character(unlist(f$variables %||% f$indicators %||% f$components %||% list())) + keys <- vapply(vars, function(v) { k <- .resolveSlice(v, datasets); if (is.null(k)) "-" else k }, character(1)) + child <- paste0("title=", if (is.null(f$title)) "-" else f$title, + "; vars: ", if (length(vars)) paste(vars, "(", keys, ")", sep = "", collapse = ", ") else "(none)") + rows <- addRow(rows, "factors-form", "factorsK", f$name, rowChild = child, + key = if (length(unique(keys)) == 1 && keys[[1]] != "-") keys[[1]] else NULL, + values = if (length(vars)) + paste0(length(vars), " var(s), slices: ", paste(unique(keys), collapse = "/")) + else "empty factor") + } + } + + # --- bound table view (Zoo L): plain cells (not a Terms option - no provenance by design) -------- + tableL <- .tableViewOption(options$tableL) + if (!is.null(tableL)) { + rows <- addRow(rows, "tableview", "tableL", + paste0(nrow(tableL$values), " rows x ", ncol(tableL$values), " cols"), + rowChild = paste0("cells: ", paste(utils::head(as.character(tableL$values), 6), collapse = ", "), + if (nrow(tableL$values) * ncol(tableL$values) > 6) ", ..." else ""), + values = "plain cells (no provenance: a tableview option is not a Terms option)") + } + # --- combobox served by Zoo A's host: PLAIN string value (never term-objectified) --------------- combo <- trimws(as.character(utils::tail(unlist(options$comboA), 1L))) if (!is.na(combo) && nzchar(combo) && !identical(combo, "0")) { @@ -213,3 +411,341 @@ controlsProvenanceTable <- function(jaspResults, options, datasets) { table$setData(rows) } + +# ------------------------------------------------------------------ scalar echoes (b) +# Every scalar family AS RECEIVED plus where it is USED - the "echo" half of the scalar contract; +# the "use" half lives in the flow/cross-slice tables. +scalarEchoTable <- function(jaspResults, options) { + table <- createJaspTable("Scalar & simple control echoes") + jaspResults[["scalarEchoes"]] <- table + + table$addColumnInfo("option", title = "Option") + table$addColumnInfo("family", title = "Family") + table$addColumnInfo("received", title = "Received value (as received)") + table$addColumnInfo("usedIn", title = "Used in") + + gated <- if (isTRUE(options$gateH)) "gate OPEN" else "gate closed (received, but control disabled in the form)" + + rows <- data.frame( + option = c("textH", "rowsH", "doubleH", "sliderH", "radioH", "gateH", "gatedH"), + family = c("text (BoundControlTextBox)", "integer (BoundControlNumberField)", + "real (BoundControlNumberField)", "slider", "radio (RadioButtonGroup)", + "checkbox (BoundControlCheckBox)", "real, checkbox-gated"), + received = c(.strOption(options$textH), as.character(.intOption(options$rowsH)), + as.character(.numOption(options$doubleH)), as.character(.numOption(options$sliderH)), + as.character(.strOption(options$radioH)), as.character(isTRUE(options$gateH)), + paste0(as.character(.numOption(options$gatedH)), " (", gated, ")")), + usedIn = c("echoed verbatim (no computation has a use for free text)", + "data-preview row count + Zoo L row count", + "cross-slice scale factor", + "echo only by design: a bare fraction has no sensible slice computation (documented, not faked)", + "flow statistic (cross-slice table)", + "gates the gated sub-result", + "gated sub-result threshold"), + stringsAsFactors = FALSE) + + table$setData(rows) +} + +# ------------------------------------------------------------------ flow demonstrations (c) +flowsTable <- function(jaspResults, options, datasets) { + table <- createJaspTable("Option flows") + jaspResults[["optionFlows"]] <- table + + table$addColumnInfo("flow", title = "Flow") + table$addColumnInfo("trigger", title = "Trigger (control)") + table$addColumnInfo("effect", title = "Effect") + table$addColumnInfo("observed", title = "Observed in this run") + + # 1. dependent dropdown: assignedA's content drives comboI's option list + assignedA <- .dependentValues(options$assignedA) + comboI <- .strOption(options$comboI, "") + comboIKey <- .resolveSlice(comboI, datasets) + rows <- data.frame( + flow = "dependent dropdown", + trigger = "assignedA (Zoo A's list)", + effect = "comboI's option list = assignedA's terms; its effective selection follows", + observed = paste0("source list: [", paste(assignedA, collapse = ", "), "]; selection: ", + if (nzchar(comboI)) paste0("\"", comboI, "\" -> slice ", + if (is.null(comboIKey)) "?" else comboIKey) else "(empty)"), + stringsAsFactors = FALSE) + + # 2. checkbox gate: gateH toggles the existence of the gated sub-result + rows <- rbind(rows, data.frame( + flow = "checkbox gate", + trigger = "gateH (Zoo H checkbox)", + effect = "gated sub-result exists only while the gate is open; gatedH is its threshold", + observed = paste0("gate ", + if (isTRUE(options$gateH)) + paste0("OPEN -> 'Gated sub-result' table produced (threshold ", .numOption(options$gatedH), ")") + else + "closed -> no gated sub-result"), + stringsAsFactors = FALSE)) + + # 3. number field: rowsH controls the shown row count (previews + Zoo L) + tableL <- .tableViewOption(options$tableL) + previewRows <- if (is.null(datasets)) 0 else + min(.intOption(options$rowsH, 5L), min(vapply(datasets, nrow, integer(1)))) + rows <- rbind(rows, data.frame( + flow = "number field -> shown rows", + trigger = "rowsH (Zoo H integer)", + effect = "row count of every 'Loaded data' preview and of the Zoo L table view", + observed = paste0("rowsH=", .intOption(options$rowsH), "; previews show ", previewRows, + " rows; tableL has ", + if (is.null(tableL)) "?" else nrow(tableL$values), " rows"), + stringsAsFactors = FALSE)) + + # 4. radio: picks the statistic used by the cross-slice computation + statCol <- .columnByToken(.strOption(options$comboA, ""), datasets) + rows <- rbind(rows, data.frame( + flow = "radio -> statistic", + trigger = "radioH (Zoo H radio group)", + effect = "the 'radio-driven statistic' row of the cross-slice table switches mean/sum", + observed = .statLabel(.strOption(options$radioH, "mean"), + if (is.null(statCol)) NULL else statCol$values), + stringsAsFactors = FALSE)) + + # 5. variables list -> interaction terms: the terms (and their computation) follow the list + inter <- options$interactionD + nTerms <- 0L + if (!is.null(inter) && is.list(inter)) { + interRows <- if (!is.null(inter$value)) inter$value else inter + nTerms <- length(interRows) + } + rows <- rbind(rows, data.frame( + flow = "variables list -> interaction terms", + trigger = "interactionD (Zoo D list)", + effect = "the 'interaction product' rows of the cross-slice table follow the list's terms", + observed = paste0(nTerms, " term(s) received"), + stringsAsFactors = FALSE)) + + table$setData(rows) +} + +# ------------------------------------------------------------------ cross-slice computations (c) +# The results-side point of provenance: computations that COMBINE values of different slices / +# lists, each row carrying its inputs' provenance as dataset titles + slice keys (never tokens). +crossSliceTable <- function(jaspResults, options, datasets) { + table <- createJaspTable("Cross-slice computations") + jaspResults[["crossSlice"]] <- table + + table$addColumnInfo("computation", title = "Computation") + table$addColumnInfo("inputs", title = "Inputs (with provenance)") + table$addColumnInfo("value", title = "Value") + + rows <- data.frame(computation = character(), inputs = character(), value = character(), + stringsAsFactors = FALSE) + addRow2 <- function(computation, inputs, value) { + rbind(rows, data.frame(computation = computation, inputs = inputs, value = as.character(value), + stringsAsFactors = FALSE)) + } + + label <- function(key) { + if (is.null(key)) return("?") + meta <- .sliceMeta(key, datasets) + paste0(meta$name, " (slice ", key, ")") + } + + # 1. interaction terms (Zoo D): product of the components within their shared slice + inter <- options$interactionD + if (!is.null(inter) && is.list(inter)) { + interRows <- if (!is.null(inter$value)) inter$value else inter + for (row in interRows) { + comps <- as.character(unlist(row$components %||% list())) + if (length(comps) < 2) next + keys <- unique(vapply(comps, function(c) { k <- .resolveSlice(c, datasets) + if (is.null(k)) "-" else k }, character(1))) + joint <- if (length(keys) == 1 && keys[[1]] != "-") keys[[1]] else NULL + value <- "" + if (!is.null(joint)) { + cols <- lapply(comps, function(c) .columnByToken(c, datasets, joint)) + if (all(vapply(cols, function(x) !is.null(x) && is.numeric(x$values), logical(1))) && + length(unique(vapply(cols, function(x) length(x$values), integer(1)))) == 1) { + prod <- Reduce(`*`, lapply(cols, function(x) x$values)) + value <- sprintf("mean of product %.3f (n=%d)", mean(prod, na.rm = TRUE), sum(!is.na(prod))) + } else value <- "components not numeric / unequal lengths" + } else value <- paste("components span slices:", paste(keys, collapse = "/")) + rows <- addRow2("interaction product", + paste0(paste(comps, collapse = " : "), " in ", label(joint)), value) + } + } + + # 2. cross-host column product: assignedA's first scale column x assignedB's first scale column + colA <- .firstAssignedScale(options$assignedA, datasets) + colB <- .firstAssignedScale(options$assignedB, datasets) + if (!is.null(colA) && !is.null(colB)) { + value <- if (length(colA$values) != length(colB$values)) + paste0("row lengths differ (", length(colA$values), " vs ", length(colB$values), + "): no join semantics (mds-plan D7 O3) - whole-column aggregates only") + else sprintf("mean of product %.3f", mean(colA$values * colB$values, na.rm = TRUE)) + rows <- addRow2("cross-host column product", + paste0(colA$column, " of ", label(colA$key), " x ", colB$column, " of ", label(colB$key)), + value) + } + + # 3. tableL multipliers (Zoo L) applied to every slice's first scale column + tableL <- .tableViewOption(options$tableL) + if (!is.null(tableL) && ncol(tableL$values) >= 1) { + mult <- mean(tableL$values[, 1], na.rm = TRUE) + scaleCol <- .firstScaleColumn(datasets) + if (!is.null(scaleCol) && is.finite(mult)) + rows <- addRow2("tableview multipliers", + paste0("mean(multiplier)=", sprintf("%.2f", mult), " x ", scaleCol$column, + " of ", label(scaleCol$key)), + sprintf("scaled mean %.3f", mult * mean(scaleCol$values, na.rm = TRUE))) + } + + # 4. scale factor (Zoo H real field) + scaleCol <- .firstScaleColumn(datasets) + doubleH <- .numOption(options$doubleH) + if (!is.null(scaleCol) && is.finite(doubleH)) + rows <- addRow2("real-field scaling", + paste0("doubleH=", doubleH, " x ", scaleCol$column, " of ", label(scaleCol$key)), + sprintf("scaled mean %.3f", doubleH * mean(scaleCol$values, na.rm = TRUE))) + + # 5. radio-driven statistic (Zoo H radio group) on the comboA column + statCol <- .columnByToken(.strOption(options$comboA, ""), datasets) + if (!is.null(statCol) && is.numeric(statCol$values)) + rows <- addRow2("radio-driven statistic", + paste0(.strOption(options$radioH, "mean"), " of ", statCol$column, " of ", label(statCol$key)), + .statLabel(.strOption(options$radioH, "mean"), statCol$values)) + + if (nrow(rows) == 0) + rows <- addRow2("(no computation)", "(nothing assigned yet)", + "assign columns/lists to see cross-slice computations here") + + table$setData(rows) +} + +# ------------------------------------------------------------------ encodeThis applied (c) +# Zoo E's factor/level names (extras roundtrip) APPLIED to a real column: the recode proves the +# names came back decoded AND usable. The factor name arrives extras-encoded (JaspExtraOptions_*, +# decoded on the way out); to USE it R-side it is decoded through the same strict table +# (jaspBase::decodeName) - the sanctioned helper, not a re-implementation of the token grammar. +encodeDemoTable <- function(jaspResults, options, datasets) { + table <- createJaspTable("encodeThis recode demo (Zoo E levels applied)") + jaspResults[["encodeDemo"]] <- table + + table$addColumnInfo("factor", title = "Factor (decoded)") + table$addColumnInfo("slice", title = "Column found in slice") + table$addColumnInfo("oldValue", title = "Original value") + table$addColumnInfo("newLabel", title = "Recoded to level") + table$addColumnInfo("n", title = "n") + + rows <- data.frame(factor = character(), slice = character(), oldValue = character(), + newLabel = character(), n = integer(), stringsAsFactors = FALSE) + + .decodeNameSafe <- function(x) tryCatch(jaspBase::decodeName(x), error = function(e) x) + + factors <- options$factorsE + if (!is.null(factors) && is.list(factors)) + for (f in factors) { + if (!is.list(f) || is.null(f$name)) next + fname <- .decodeNameSafe(as.character(f$name)) + levels <- as.character(unlist(f$levels %||% list())) + levels <- if (length(levels)) vapply(levels, .decodeNameSafe, character(1)) else levels + # The slice data.frames carry ENCODED column tokens (that is what R receives, C1) - the + # decoded factor name must be matched against the DECODED column names, not the raw + # colnames (the verifier caught the first version matching "Group" against tokens). + hitKey <- NULL; hitTok <- NULL + for (k in names(datasets)) { + toks <- names(datasets[[k]]) + dec <- vapply(toks, function(t) .decodeNameSafe(t), character(1)) + i <- match(fname, dec) + if (!is.na(i)) { hitKey <- k; hitTok <- toks[[i]]; break } + } + if (is.null(hitKey)) { + rows <- rbind(rows, data.frame(factor = fname, slice = "(no column of this name in any slice)", + oldValue = "-", newLabel = "-", n = NA_integer_, + stringsAsFactors = FALSE)) + next + } + values <- as.character(stats::na.omit(datasets[[hitKey]][[hitTok]])) + uniq <- unique(values) + recode <- if (length(levels)) levels[pmin(match(values, uniq), length(levels))] else values + for (u in uniq) { + n <- sum(values == u) + rows <- rbind(rows, data.frame(factor = fname, slice = hitKey, oldValue = u, + newLabel = recode[match(u, values)][1], n = n, + stringsAsFactors = FALSE)) + } + } + + if (nrow(rows) == 0) + rows <- data.frame(factor = "(no factors)", slice = "-", oldValue = "-", newLabel = "-", + n = NA_integer_, stringsAsFactors = FALSE) + + table$setData(rows) +} + +# ------------------------------------------------------------------ checkbox-gated sub-result (c) +# Exists ONLY while gateH is open - a gate means absence, not an empty table. +gatedResultTable <- function(jaspResults, options, datasets) { + threshold <- .numOption(options$gatedH, 0) + # dependencies = "gateH": the gate family's destruction semantics. jaspBase destroys an object + # whose dependencies changed before the next run, so flipping gateH off removes the table and + # the gate being closed means this child is ABSENT - not an empty table. (Without deps the + # object from the previous run persists forever, which the verifier caught.) + table <- createJaspTable(paste0("Gated sub-result (threshold ", threshold, ")"), dependencies = "gateH") + jaspResults[["gatedResult"]] <- table + + table$addColumnInfo("slice", title = "Slice") + table$addColumnInfo("column", title = "First scale column") + table$addColumnInfo("nAbove", title = "n above threshold") + table$addColumnInfo("meanAbove", title = "mean above threshold") + + rows <- data.frame(slice = character(), column = character(), nAbove = integer(), + meanAbove = numeric(), stringsAsFactors = FALSE) + if (!is.null(datasets)) + for (key in names(datasets)) { + col <- .firstScaleColumn(datasets, key) + if (is.null(col)) next + above <- col$values[col$values > threshold & !is.na(col$values)] + rows <- rbind(rows, data.frame( + slice = paste0(key, " (", .sliceMeta(key, datasets)$name, ")"), column = col$column, + nAbove = length(above), + meanAbove = if (length(above)) mean(above) else NA_real_, + stringsAsFactors = FALSE)) + } + + table$setData(rows) +} + +# ------------------------------------------------------------------ more internals + +# a bound tableview option as R receives it: list of columns {name, levels, values} -> matrix +.tableViewOption <- function(tv) { + if (is.null(tv) || !is.list(tv) || !length(tv)) return(NULL) + vals <- lapply(tv, function(col) { + v <- if (is.list(col)) col$values else col + suppressWarnings(as.numeric(unlist(v))) + }) + lens <- vapply(vals, length, integer(1)) + len <- max(lens) + if (is.null(len) || is.na(len) || len == 0) return(NULL) + vals <- lapply(vals, function(v) { length(v) <- len; v }) + m <- matrix(unlist(vals), nrow = len, ncol = length(vals)) + list(values = m, + colNames = vapply(tv, function(c) if (is.list(c) && !is.null(c$name)) as.character(c$name) else "-", + character(1))) +} + +# a column by its (encoded) token, optionally within one slice, with its provenance +.columnByToken <- function(token, datasets, key = NULL) { + if (!nzchar(token) || is.null(datasets)) return(NULL) + key <- if (is.null(key)) .resolveSlice(token, datasets) else key + if (is.null(key)) return(NULL) + col <- datasets[[key]][[token]] + if (is.null(col)) return(NULL) + list(key = key, column = token, values = if (is.factor(col)) as.numeric(col) else col) +} + +# the first numeric column of an assigned list, with its slice provenance +.firstAssignedScale <- function(option, datasets) { + for (token in .dependentValues(option)) { + col <- .columnByToken(token, datasets) + if (!is.null(col) && is.numeric(col$values) && sum(!is.na(col$values)) > 1) + return(col) + } + NULL +} diff --git a/R/multiDataSetControlsFuncWrapper.R b/R/multiDataSetControlsFuncWrapper.R index 91101bf..d971de7 100644 --- a/R/multiDataSetControlsFuncWrapper.R +++ b/R/multiDataSetControlsFuncWrapper.R @@ -30,23 +30,37 @@ multiDataSetControlsFunc <- function( version = "1", assignedA = list(types = list(), value = list()), assignedB = list(types = list(), value = list()), + assignedK = list(types = list(), value = list()), boundG = list(types = list(), value = list()), codeF = list(columns = list(), model = "", modelOriginal = ""), comboA = list(types = "unknown", value = ""), comboG = "first", + comboI = list(types = "unknown", value = ""), dataSetA = "", dataSetB = "", dataSetC = "", dataSetD = "", dataSetF = "", dataSetG = "", + dataSetJ = "", + dataSetK = "", + doubleH = 1.5, factorsE = list(), + factorsK = list(list(indicators = list(), name = "Fac1", title = "Factor 1")), + gateH = FALSE, + gatedH = 10, interactionD = list(optionKey = "components", types = list(), value = list()), + pairsJ = list(), plainSwitch = FALSE, plotHeight = 320, plotWidth = 480, + radioH = "mean", rowsC = list(optionKey = "variable", types = list(), value = list()), - showMeta = FALSE) { + rowsH = 5, + showMeta = FALSE, + sliderH = 0.5, + tableL = list(list(levels = list("Row 0", "Row 1", "Row 2", "Row 3", "Row 4"), name = "data 1", values = list(1, 2, 3, 4, 5)), list(levels = list("Row 0", "Row 1", "Row 2", "Row 3", "Row 4"), name = "data 2", values = list(0.5, 1, 1.5, 2, 2.5))), + textH = "") { defaultArgCalls <- formals(jaspTestModule::multiDataSetControlsFunc) defaultArgs <- lapply(defaultArgCalls, eval) @@ -65,7 +79,7 @@ multiDataSetControlsFunc <- function( # resolved at run time from `options`. Every entry is loaded standalone by runWrappedAnalysis # (jaspBase::storeDataSets -> jaspSyntax::loadDataSets, each under its own name as the dataset # title); the analysis itself receives its slices keyed by filter id (jaspBase datasets contract). - selectionOptions <- c("dataSetB", "dataSetG", "dataSetF", "dataSetC", "dataSetA", "dataSetD") + selectionOptions <- c("dataSetB", "dataSetK", "dataSetG", "dataSetF", "dataSetJ", "dataSetC", "dataSetA", "dataSetD") data <- NULL if (!is.null(datasets) && length(datasets)) { for (selectionOption in selectionOptions) { @@ -82,7 +96,7 @@ multiDataSetControlsFunc <- function( } } - optionsWithFormula <- c("assignedA", "assignedB", "boundG", "codeF", "comboA", "comboG", "dataSetA", "dataSetB", "dataSetC", "dataSetD", "dataSetF", "dataSetG", "factorsE", "interactionD", "rowsC") + optionsWithFormula <- c("assignedA", "assignedB", "assignedK", "boundG", "codeF", "comboA", "comboG", "comboI", "dataSetA", "dataSetB", "dataSetC", "dataSetD", "dataSetF", "dataSetG", "dataSetJ", "dataSetK", "factorsE", "factorsK", "interactionD", "pairsJ", "rowsC", "tableL") for (name in optionsWithFormula) { if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } diff --git a/inst/qml/testMultiDataSetControls.qml b/inst/qml/testMultiDataSetControls.qml index b1dcaff..1b96a24 100644 --- a/inst/qml/testMultiDataSetControls.qml +++ b/inst/qml/testMultiDataSetControls.qml @@ -56,7 +56,17 @@ Form "
    • Zoo E: factor/level names roundtrip through the extras encoder.
    • " + "
    • Zoo F: the R-code textarea encodes its tokens through its own host.
    • " + "
    • Zoo G: a typed copy bound across differently-selected hosts keeps the SOURCE filterId.
    • " + + "
    • Zoo H: scalar families (text / integer / real / slider / radio / checkbox-gated field) echo " + + "as plain values AND drive results: the integer sets the preview row count, the radio picks the " + + "flow statistic, the checkbox gates a sub-result.
    • " + + "
    • Zoo I: a DEPENDENT dropdown - its option list is driven by Zoo A's assigned list.
    • " + + "
    • Zoo J: a paired variables list (own selection); the pair is one term over two columns.
    • " + + "
    • Zoo K: a FactorsForm (the FactorsList family) fed by an assigned list with its own selection.
    • " + + "
    • Zoo L: a bound TableView whose row count follows Zoo H's integer field.
    • " + "
    " + + "The results panel shows the LOADED DATA per slice (head of every column, row count = Zoo H's " + + "integer), the per-family provenance echo, the scalar echoes, the flow demonstrations and the " + + "cross-slice computations." + "'Show options .meta' adds the PROVENANCE_AUDIT (R must never see provenance: C8/C11)." } @@ -79,12 +89,15 @@ Form } // a combobox served by the same host: values stay PLAIN strings (never term objects) // while the selection resolves through the host namespace (landmine: no - // term-objectification of combos). + // term-objectification of combos). NB: the source must be the STRING NAME of the + // control - a bare `source: zooVarsA` is a QML ReferenceError (the id does not exist; + // found via the runtime log: "ReferenceError: zooVarsA is not defined") and the + // dropdown silently ran with an empty option list until fixed. DropDown { name: "comboA" - label: qsTr("Column combo") - source: zooVarsA + label: qsTr("Column combo (options from the available list)") + source: "zooVarsA" addEmptyValue: true } } @@ -197,6 +210,167 @@ Form } } + // ---- Zoo H: scalar control families ----------------------------------------------------------- + // Every scalar family a real form binds, each with an R-side consumer that actually USES the + // value (not just echoes it): the integer drives the data-preview row count and Zoo L's row + // count, the radio picks the flow statistic, the checkbox gates a whole sub-result, the real + // and the gated field enter the cross-slice computations. Slider is echo-only (a fraction has + // no sensible computation here - documented in R/multiDataSetControls.R, not faked). + Section { + title: qsTr("Zoo H - scalar controls (text / integer / real / slider / radio / gated)") + + columns: 2 + + TextField + { + name: "textH" + label: qsTr("Text field (echoed verbatim)") + placeholderText: qsTr("echo me") + } + IntegerField + { + id: rowsH + name: "rowsH" + label: qsTr("Preview rows (drives data previews + Zoo L)") + defaultValue: 5 + min: 1 + max: 10 + } + DoubleField + { + name: "doubleH" + label: qsTr("Scale factor (real number)") + defaultValue: 1.5 + min: 0 + max: 100 + decimals: 2 + } + Slider + { + name: "sliderH" + label: qsTr("Slider fraction (echo only)") + value: 0.5 // Slider has no defaultValue property: the initial bound value IS `value` + min: 0 + max: 1 + stepSize: 0.1 + } + RadioButtonGroup + { + name: "radioH" + title: qsTr("Flow statistic") + + RadioButton { value: "mean"; label: qsTr("Mean"); checked: true } + RadioButton { value: "sum"; label: qsTr("Sum") } + } + CheckBox + { + name: "gateH" + label: qsTr("Gate: produce the gated sub-result") + + DoubleField + { + name: "gatedH" + label: qsTr("Gated threshold") + defaultValue: 10.0 + min: 0 + max: 100 + decimals: 1 + } + } + } + + // ---- Zoo I: dependent dropdown ----------------------------------------------------------------- + // The option LIST of this dropdown is driven by another control's value: source is Zoo A's + // assigned list, so its choices are exactly the terms currently in assignedA (and follow every + // change of that list - the RPC verifier flips assignedA and asserts the effective selection + // follows). Value stays a PLAIN string (never term-objectified, landmine 4). + Section { + title: qsTr("Zoo I - dependent dropdown (options driven by Zoo A's assigned list)") + + DropDown + { + name: "comboI" + label: qsTr("Pick a column from Assigned A") + source: "assignedA" + addEmptyValue: true + } + } + + // ---- Zoo J: paired variables list --------------------------------------------------------------- + // The paired-list family: AssignedPairsVariablesList is a VariablesList preset (2 columns, + // drop-replace) whose model PAIRS its columns into 2-component terms (bound value + // [["Score","Age"]]). Own selection host, so the pair's components stamp together under that + // host's filter (C9 uniform-carry); the R side echoes each pair and computes per pair. + Section { + title: qsTr("Zoo J - paired variables list (own selection)") + + AssignedPairsVariablesList + { + name: "pairsJ" + title: qsTr("Pairs (own Data selection)") + dataSetSelectionOption: "dataSetJ" + } + } + + // ---- Zoo K: factors form (the FactorsList family) ------------------------------------------------ + // FactorsList is one panel of a FactorsForm; the serialized shape is the form's: per factor + // {name, title, indicators}. Two findings (pinned by the verifier): + // 1. The form's inner panels are NOT hosts and do not inherit the source list's filter stamp - + // their unqualified terms encode through the ANALYSIS-level namespace (the D3.2 NOTE in + // factorsformbase.cpp leaves nested per-factor provenance to the repeated-measures exercise). + // 2. A factor term must be a member of the form's SOURCE list (assignedK): a term outside it is + // silently dropped by the form's model sync, and a term the form keeps is RE-TYPED by the + // form (Age arrived ordinal here although sent scale, so the engine slice receives it as an + // R factor). A term whose column the analysis-level namespace lacks (e.g. nominal Group + // while DataSet 2 is shown) used to FATAL the run - the mds-plan post existence guard in + // Common/columnencoder.cpp now refuses to queue columns a namespace does not have, and the + // verifier pins the honest unresolved row (run 18) instead of a fatal. + Section { + title: qsTr("Zoo K - factors form (FactorsList panels over an assigned source)") + + AssignedVariablesList + { + name: "assignedK" + title: qsTr("Assigned K (factor source, own Data selection)") + dataSetSelectionOption: "dataSetK" + } + FactorsForm + { + name: "factorsK" + initNumberFactors: 1 + baseName: "Fac" + baseTitle: "Factor" + allowedColumns: ["scale", "ordinal", "nominal"] + availableVariablesList.source: "assignedK" + } + } + + // ---- Zoo L: bound table view --------------------------------------------------------------------- + // The BoundControlTableView family: a bound SimpleTableView whose ROW COUNT is driven by Zoo + // H's integer field (initialRowCount + tableView.rowCount follow rowsH, exactly the + // testTableView.qml pattern). Its cells are plain values (no provenance - a tableview option + // is not a Terms option) and the R side USES them: the multiplier column rescales the assigned + // columns of every slice in the cross-slice computation. + Section { + title: qsTr("Zoo L - bound table view (row count driven by Zoo H's integer field)") + + SimpleTableView + { + name: "tableL" + cornerText: qsTr("Par") + initialRowCount: rowsH.value + tableView.rowCount: rowsH.value + initialColumnCount: 2 + columnNames: [qsTr("Multiplier"), qsTr("Offset")] + itemTypePerColumn: [JASP.Double, JASP.Double] + + function getDefaultValue(columnIndex, rowIndex) + { + return columnIndex === 0 ? 1.0 + rowIndex : 0.5 * (rowIndex + 1); + } + } + } + // ---- scalars (M5 contract: never provenanced, never term-objectified) ------------------------ Section { title: qsTr("Scalars") From c03528a8cdaf13c95ca48663697ee08699701f98 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch Date: Mon, 19 Oct 2026 15:52:06 +0200 Subject: [PATCH 37/44] mds-zoo: verifier expansion, 84 -> 177 checks, results-side assertions The existing checks proved option roundtrips; the expansion asserts the RESULTS side: what the loaded data looks like per slice, what each control family echoed, and that the option flows actually MOVE the numbers. New coverage (zoo runs 9-18, each a full explicit option set on the RPC): - run 9 (expanded zoo): per-slice loaded-data previews (decoded column names + real values from BOTH datasets, row count follows rowsH), the provenance echoes of all new families (dependent dropdown resolves in its SOURCE host's slice; pairsJ one pair + its correlation r=-0.457 computed from the CSVs; factorsK term resolving through the ANALYSIS-level namespace per the D3.2 hostless-panel deferral; tableview echo), all 7 scalar echoes, all 5 flow rows, cross-slice computations at value level (interaction product 461.830, cross-host product 228.990, tableview multipliers 95.000, real-field scaling 76.000, radio statistic sum 125.00), the encodeThis recode demo applying levels A/B to the Group column (n=4/3/3), the gated sub-result (Age@1 10/38.00, Score@2 8/19.45), PROVENANCE_AUDIT clean and no raw tokens. - run 10 (F1): assignedA flips to Condition -> comboI's option list and effective selection follow the source into slice 2 (levels X, Y, Z). - runs 11/12 (F2): gate closed -> the gated sub-result must be ABSENT (a gate means absence; caught the missing dependencies="gateH"); gate open with threshold 40 -> the numbers move (Age@1 n=4 mean 46.50). - runs 13/14 (F3): rowsH 3 -> 7 -> preview row counts and Zoo L's table row count follow in both directions. - run 15 (F6): radioH sum -> mean switches the statistic (125.00 -> 12.50). - run 16 (F4): reducing interactionD to a single component removes the interaction-product row. - run 17 (F5): explicit tableL cells (multipliers 2,2,2,2) are USED, not just echoed (scaled mean 95.000 -> 76.000). - run 18 (regression, pairs with the mds-plan post fix in jasp-desktop): a nominal factor term whose column the analysis-level namespace lacks used to FATAL the whole run; it must complete with an honest unresolved row. Findings encoded as checks/comments: the FactorsForm drops factor terms that are not in its SOURCE list and re-types kept terms (Age arrived ordinal -> the slice receives an R factor, so the gated "first scale column" differs per slice); jaspBase HTML-escapes string cells (assertions unescape); expected statistics are computed from the same CSVs the datasets load, so assertions are value-level. All 84 pre-existing checks unchanged and green; PROVENANCE_AUDIT clean on every meta-bearing run. --- Tools/verifyMultiDataSetRpc.py | 440 ++++++++++++++++++++++++++++++++- 1 file changed, 432 insertions(+), 8 deletions(-) diff --git a/Tools/verifyMultiDataSetRpc.py b/Tools/verifyMultiDataSetRpc.py index 96b2821..d3b49ec 100644 --- a/Tools/verifyMultiDataSetRpc.py +++ b/Tools/verifyMultiDataSetRpc.py @@ -362,21 +362,33 @@ def find(rows, family, pred=lambda r: True): EMPTY_CODEF = {"modelOriginal": "", "model": "", "columns": []} def zoo_full_opts(host_a="1", host_b="", host_c="", host_d="", host_f="", host_g="", - assigned_a=None, assigned_b=None, rows_c=None, bound_g=None, - interaction_d=None, factors_e=None, code_f=None, combo_a="", + host_j="", host_k="", + assigned_a=None, assigned_b=None, assigned_k=None, rows_c=None, bound_g=None, + interaction_d=None, factors_e=None, factors_k=None, pairs_j=None, code_f=None, + combo_a="", combo_i="", text_h="", rows_h=5, double_h=1.5, slider_h=0.5, + radio_h="mean", gate_h=False, gated_h=10.0, table_l=None, plain_switch=False, show_meta=True): opts = { "dataSetA": host_a, "dataSetB": host_b, "dataSetC": host_c, "dataSetD": host_d, "dataSetF": host_f, "dataSetG": host_g, + "dataSetJ": host_j, "dataSetK": host_k, "assignedA": assigned_a or dict(EMPTY_TERMS), "assignedB": assigned_b or dict(EMPTY_TERMS), + "assignedK": assigned_k or dict(EMPTY_TERMS), "rowsC": rows_c or dict(EMPTY_ROWS), "boundG": bound_g or {"value": []}, "interactionD": interaction_d or dict(EMPTY_INTER), "factorsE": factors_e or [], + "factorsK": factors_k or [], + "pairsJ": pairs_j or dict(EMPTY_TERMS), "codeF": code_f or dict(EMPTY_CODEF), - "comboA": combo_a, "comboG": "first", "plainSwitch": plain_switch, + "comboA": combo_a, "comboI": combo_i, "comboG": "first", + "textH": text_h, "rowsH": rows_h, "doubleH": double_h, "sliderH": slider_h, + "radioH": radio_h, "gateH": gate_h, "gatedH": gated_h, + "plainSwitch": plain_switch, } + if table_l is not None: + opts["tableL"] = table_l if show_meta: opts["showMeta"] = True return opts @@ -447,11 +459,13 @@ def zoo_full_opts(host_a="1", host_b="", host_c="", host_d="", host_f="", host_g check("zoo run 2: assignedB (own host) Condition resolves in slice 2 with its levels", len(ab) == 1 and str(ab[0]["slice"]) == "2" and ab[0]["values"].startswith("X"), json.dumps(ab)) -# combobox (Zoo A): plain string value; NOTE the namespace finding - a DropDown is not a list -# host (no dataSetSelectionOption, resolvedSelectionHost stays the analysis level per C13), so -# its value encodes through the FORM namespace (shown filter = DataSet 2 here), not zooVarsA's. -# The zoo asserts the honest contract: value stays a plain string (never term-objectified, -# landmine 4) and roundtrips encoded->decoded intact. +# combobox (Zoo A): plain string value. NB the namespace finding, UPDATED by the mds-zoo +# expansion: comboA's `source` used to be a broken QML binding (`source: zooVarsA` - a bare +# identifier that does not exist; ReferenceError in the runtime log), so the dropdown silently +# had no option list and its value encoded through the analysis-level namespace. With the source +# fixed to the string-name form ("zooVarsA") a dropdown INHERITS ITS SOURCE's host namespace: +# the value now encodes through zooVarsA's host (dataSetA). Run 9 pins that strictly; this +# legacy check keeps its tolerance (the value stays plain and roundtrips either way). ca = find(z2, "combobox-host") check("zoo run 2: comboA 'Score' stays plain and resolves in exactly one slice (analysis-level namespace)", len(ca) == 1 and ca[0]["received"] == "Score" and str(ca[0]["slice"]) in ("1", "2"), json.dumps(ca)) @@ -583,5 +597,415 @@ def zoo_full_opts(host_a="1", host_b="", host_c="", host_d="", host_f="", host_g auditz8 = provenance_audit(zres8) check("zoo run 8: PROVENANCE_AUDIT clean", auditz8 == ("false", "0"), str(auditz8)) +# ---------------------------------------------------------------------------- +# mds-zoo expansion - the NEW control families (Zoo H-L), the RESULTS side and +# the option flows. The point of the expansion: data and flows must be VISIBLE +# and the verifier must assert RESULTS-SIDE content, not just option roundtrips. +# +# Results inventory asserted below: +# "Loaded data - (slice <key>...)" the actual loaded DATA per slice: +# every column's decoded name AND its first `rowsH` values (head per slice) +# "Controls provenance" extended with the new families +# "Scalar & simple control echoes" scalar families as received + used-in +# "Option flows" the flow demonstrations +# "Cross-slice computations" computations across slices/lists of different provenance +# "encodeThis recode demo" Zoo E's levels APPLIED to a real column +# "Gated sub-result" EXISTS ONLY while gateH is on (a gate means absence) +# +# Flows (trigger -> effect, each verified by a delta run + results-side diff): +# F1 assignedA content -> comboI's option list & effective selection (dependent dropdown) +# F2 gateH checkbox -> the gated sub-result exists / vanishes; gatedH shifts its numbers +# F3 rowsH integer -> preview row count AND Zoo L's table row count +# F4 interactionD terms -> the interaction-product rows of the cross-slice table +# F5 tableL cells -> the multiplier statistic (cells are USED, not just echoed) +# F6 radioH -> the radio-driven statistic (sum vs mean) +# +# Expected statistics are computed from the same CSVs the datasets load, so the +# assertions are value-level. Slice column composition follows from the stamped +# namespaces (each asserted row documents where its term was stamped). +# ---------------------------------------------------------------------------- + +import csv as _csvmod + +def _read_columns(path): + with open(path) as f: + rows = list(_csvmod.DictReader(f)) + cols = {} + for k in rows[0].keys(): + vals = [r[k] for r in rows] + try: + cols[k] = [float(v) for v in vals] + except ValueError: + cols[k] = vals + return cols + +A = _read_columns(A_CSV) +B = _read_columns(B_CSV) + +def fmt(x, nd=2): return f"{x:.{nd}f}" +def mean(v): return sum(v) / len(v) + +# the analysis-level namespace of a fresh RPC session: the SHOWN dataset is the last loaded one +# (DataSet 2, filter 2) - hostless panels (the FactorsForm's) stamp there. +ANALYSIS_SLICE = "2" + +def find_table(res, title_prefix): + return next((t for t in tables(res.get("results", res)).values() + if str(t.get("title", "")).startswith(title_prefix)), None) + +import html as _htmlmod + +def table_rows(t): + if not t: return [] + fields, fm, rks = field_maps(t) + # jaspBase HTML-escapes string cells ("->" arrives as "->"); unescape for assertions + return [{f: _htmlmod.unescape(str(fm[f].get(r))) if isinstance(fm[f].get(r), str) else fm[f].get(r) + for f in fields} for r in rks] + +def no_raw_tokens(res, tag): + raw = json.dumps(res) + check(f"{tag}: no raw JASPColumn_* in results", "JASPColumn" not in raw) + check(f"{tag}: no raw JaspExtraOptions_* in results", "JaspExtraOptions" not in raw) + +# --- zoo run 9: the expanded zoo, all new families populated -------------------------------------- +# Provenance story (fresh RPC session, shown dataset = DataSet 2): +# assignedA Score via host A(1) -> slice 1 | assignedB Score via host B(2) -> slice 2 +# assignedK Group+Age via host K(1) -> slice 1 (Group feeds the encodeThis demo, Age is the +# factor term's source) | pairsJ Score+Age via host J(1) -> slice 1 +# interactionD Score:Age via host D(1) -> slice 1 | factorsK Age -> hostless panel -> +# ANALYSIS-level namespace (slice 2) - the D3.2 deferral, pinned as behaviour +zr9, zres9 = zoo_run(zoo_full_opts( + host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + assigned_a={"types": ["scale"], "value": ["Score"]}, + assigned_b={"types": ["scale"], "value": ["Score"]}, + # NB: the FactorsForm's factors can only hold terms that are in its SOURCE list (assignedK): + # a term outside the source is silently dropped by the form's model sync (verified live, run + # 9 originally sent Age here while assignedK held only Group -> "(none)"). Group is needed in + # the slice for the encodeThis demo, Age for the factor term, so the source carries both. + assigned_k={"types": ["nominal", "scale"], "value": ["Group", "Age"]}, + interaction_d={"types": [["scale", "scale"]], "value": [{"components": ["Score", "Age"]}], "optionKey": "components"}, + factors_e=[{"name": "Group", "levels": ["A", "B"]}], + factors_k=[{"name": "Fac1", "title": "Factor 1", "indicators": {"value": ["Age"], "types": [["scale"]]}}], + pairs_j={"types": ["scale", "scale"], "value": ["Score", "Age"]}, + combo_a="Score", combo_i="Score", text_h="hello zoo", rows_h=4, double_h=2.0, slider_h=0.3, + radio_h="sum", gate_h=True, gated_h=12.0), "run 9 (expanded zoo: all new families)") + +z9 = zoo_rows(zres9) +check("zoo run 9: provenance table present", z9 is not None) + +# (a) THE LOADED DATA - per-slice previews with real column names and values +pv1 = find_table(zres9, "Loaded data - DataSet 1") +pv2 = find_table(zres9, "Loaded data - DataSet 2") +check("zoo run 9: loaded-data preview for slice 1 present", pv1 is not None, + str([t.get('title') for t in tables(zres9.get('results', zres9)).values()])) +check("zoo run 9: loaded-data preview for slice 2 present", pv2 is not None) +if pv1 and pv2: + check("zoo run 9: preview titles say 'first 4 rows' (rowsH flow)", + "first 4 rows" in pv1["title"] and "first 4 rows" in pv2["title"], + f"{pv1['title']!r} / {pv2['title']!r}") + r1, r2 = table_rows(pv1), table_rows(pv2) + check("zoo run 9: preview row count == rowsH (4)", len(r1) == 4 and len(r2) == 4, + f"{len(r1)}/{len(r2)}") + # values: slice 1 is DataSetA, slice 2 is DataSetB - the whole point + check("zoo run 9: slice 1 shows DataSetA's Score values (12.5, 9.1, ...)", + any(abs((row.get("Score") or -999) - A["Score"][i]) < 1e-9 for i, row in enumerate(r1)), + json.dumps(r1)[:200]) + check("zoo run 9: slice 2 shows DataSetB's Score values (22.4, 14.8, ...) - NOT a repeat of A", + any(abs((row.get("Score") or -999) - B["Score"][i]) < 1e-9 for i, row in enumerate(r2)), + json.dumps(r2)[:200]) + check("zoo run 9: slice 1 shows the Group levels (nominal values, not numbers)", + any(row.get("Group") in ("A", "B", "C") for row in r1), json.dumps(r1)[:200]) + check("zoo run 9: previews carry decoded names, no raw tokens (fields)", + all("JASPColumn" not in f for f in [f["name"] for f in pv1["schema"]["fields"]] + + [f["name"] for f in pv2["schema"]["fields"]])) + +# (b) per-family provenance echoes for the new families +dep = find(z9, "dropdown-dependent") +check("zoo run 9: dependent dropdown comboI='Score' resolves in slice 1 via its SOURCE host (12.50)", + len(dep) == 1 and dep[0]["received"] == "Score" and str(dep[0]["slice"]) == "1" and "12.50" in dep[0]["values"], + json.dumps(dep)) +ca9 = find(z9, "combobox-host") +check("zoo run 9: comboA (fixed source binding) encodes through its SOURCE host namespace -> slice 1", + len(ca9) == 1 and str(ca9[0]["slice"]) == "1" and "12.50" in ca9[0]["values"], json.dumps(ca9)) + +pj = find(z9, "pairs-list") +check("zoo run 9: pairsJ one PAIR 'Score + Age', both components in slice 1 (host J)", + len(pj) == 1 and pj[0]["received"] == "Score + Age" and str(pj[0]["slice"]) == "1", json.dumps(pj)) +import statistics as _stats +_r = _stats.correlation(A["Score"], A["Age"]) +check("zoo run 9: pairsJ echo computes the pair correlation r=%.3f" % _r, + len(pj) == 1 and f"r = {fmt(_r, 3)}" in str(pj[0]["values"]), json.dumps(pj)) + +fk = find(z9, "factors-form") +check("zoo run 9: factorsK Fac1 received with title and var Age", + len(fk) == 1 and fk[0]["received"] == "Fac1" and "title=Factor 1" in fk[0]["rowChild"] + and "Age" in fk[0]["rowChild"], json.dumps(fk)) +check("zoo run 9: factorsK var Age resolves in the ANALYSIS-level namespace (slice 2) - the D3.2 hostless-panel deferral", + len(fk) == 1 and str(fk[0]["slice"]) == ANALYSIS_SLICE and "slices: 2" in str(fk[0]["values"]), json.dumps(fk)) + +fs = [r for r in z9 if r["family"] == "factors-source" and r["received"] == "Group"] +check("zoo run 9: assignedK Group (the factor SOURCE) resolves in ITS OWN host's slice 1 with levels", + len(fs) == 1 and str(fs[0]["slice"]) == "1" and fs[0]["values"].startswith("A, B, C"), json.dumps(fs)) +fs_age = [r for r in z9 if r["family"] == "factors-source" and r["received"] == "Age"] +check("zoo run 9: assignedK Age (second source var) resolves in slice 1 with its values (31, 45, ...)", + len(fs_age) == 1 and str(fs_age[0]["slice"]) == "1" and str(fs_age[0]["values"]).startswith("31, 45, 28"), + json.dumps(fs_age)) + +tv = find(z9, "tableview") +check("zoo run 9: tableL echo '4 rows x 2 cols' (row count follows rowsH=4 via the QML binding)", + len(tv) == 1 and "4 rows x 2 cols" in tv[0]["received"], json.dumps(tv)) + +# (c) scalar echoes +se = table_rows(find_table(zres9, "Scalar & simple control echoes")) +se_map = {r["option"]: r for r in se} +check("zoo run 9: scalar echo table has all 7 scalar families", + set(se_map) == {"textH", "rowsH", "doubleH", "sliderH", "radioH", "gateH", "gatedH"}, str(sorted(se_map))) +check("zoo run 9: textH echoes verbatim", se_map.get("textH", {}).get("received") == "hello zoo", json.dumps(se)) +check("zoo run 9: rowsH/doubleH/sliderH/radioH echo as received", + se_map.get("rowsH", {}).get("received") == "4" and abs(float(se_map.get("doubleH", {}).get("received") or 0) - 2.0) < 1e-9 + and se_map.get("sliderH", {}).get("received") == "0.3" and se_map.get("radioH", {}).get("received") == "sum", + json.dumps([se_map.get(k) for k in ("rowsH", "doubleH", "sliderH", "radioH")])) +check("zoo run 9: gateH true and gatedH annotated 'gate OPEN'", + se_map.get("gateH", {}).get("received") == "TRUE" and "gate OPEN" in str(se_map.get("gatedH", {}).get("received")), + json.dumps([se_map.get("gateH"), se_map.get("gatedH")])) + +# (d) flows table +fl = table_rows(find_table(zres9, "Option flows")) +fl_map = {r["flow"]: r for r in fl} +check("zoo run 9: flows table has the 5 flow rows", + set(fl_map) == {"dependent dropdown", "checkbox gate", "number field -> shown rows", + "radio -> statistic", "variables list -> interaction terms"}, str(sorted(fl_map))) +check("zoo run 9: dependent-dropdown flow observed 'Score -> slice 1'", + '"Score" -> slice 1' in str(fl_map.get("dependent dropdown", {}).get("observed")), json.dumps(fl)) +check("zoo run 9: gate flow observed OPEN", "gate OPEN" in str(fl_map.get("checkbox gate", {}).get("observed")), json.dumps(fl)) +check("zoo run 9: rows flow observed rowsH=4, previews 4 rows, tableL 4 rows", + "rowsH=4" in str(fl_map.get("number field -> shown rows", {}).get("observed")) + and "4 rows" in str(fl_map.get("number field -> shown rows", {}).get("observed")), json.dumps(fl)) + +# (e) cross-slice computations - value-level, computed from the CSVs +cs = table_rows(find_table(zres9, "Cross-slice computations")) +cs_map = {r["computation"]: r for r in cs} +_exp_prod = mean([a * b for a, b in zip(A["Score"], A["Age"])]) +check("zoo run 9: interaction product mean(Score*Age) in slice 1 = %s" % fmt(_exp_prod, 3), + f"mean of product {fmt(_exp_prod, 3)}" in str(cs_map.get("interaction product", {}).get("value")), json.dumps(cs)) +_exp_cross = mean([a * b for a, b in zip(A["Score"], B["Score"])]) +check("zoo run 9: cross-host product Score@1 x Score@2 = %s (two different truths, one number)" % fmt(_exp_cross, 3), + f"mean of product {fmt(_exp_cross, 3)}" in str(cs_map.get("cross-host column product", {}).get("value")) + and "DataSet 1" in str(cs_map.get("cross-host column product", {}).get("inputs")) + and "DataSet 2" in str(cs_map.get("cross-host column product", {}).get("inputs")), json.dumps(cs)) +# slice 1's first numeric column is Age (wanted-cols are set-sorted): Age mean 38.00 +check("zoo run 9: tableview multipliers mean([1,2,3,4])=2.5 x mean(Age@1)=38.00 -> 95.000", + "scaled mean 95.000" in str(cs_map.get("tableview multipliers", {}).get("value")), json.dumps(cs)) +check("zoo run 9: doubleH=2.0 scaling -> 76.000", "scaled mean 76.000" in str(cs_map.get("real-field scaling", {}).get("value")), json.dumps(cs)) +check("zoo run 9: radio-driven statistic sum(Score@1) = 125.00", + "sum 125.00" in str(cs_map.get("radio-driven statistic", {}).get("value")), json.dumps(cs)) + +# (f) encodeThis recode demo: factorsE levels APPLIED to the Group column of slice 1 +ed = table_rows(find_table(zres9, "encodeThis recode demo")) +ed_map = {r["oldValue"]: r for r in ed} +check("zoo run 9: encodeDemo decodes the extras factor name to 'Group' and finds it in slice 1", + all(r["slice"] == "1" for r in ed) and len(ed) == 3, json.dumps(ed)) +check("zoo run 9: encodeDemo recode A->A (n=4), B->B (n=3), C->B (n=3) - levels applied in order, capped", + ed_map.get("A", {}).get("newLabel") == "A" and ed_map.get("A", {}).get("n") == 4 + and ed_map.get("B", {}).get("newLabel") == "B" and ed_map.get("B", {}).get("n") == 3 + and ed_map.get("C", {}).get("newLabel") == "B" and ed_map.get("C", {}).get("n") == 3, json.dumps(ed)) + +# (g) gated sub-result EXISTS while the gate is open; first numeric column of each slice is Age +g9 = table_rows(find_table(zres9, "Gated sub-result")) +def _gated_map(g): + # slice cell is "1 (DataSet 1)"; meanAbove arrives as a number + return {(str(r["slice"]).split(" ")[0], r["column"]): r for r in g} + +# (g) gated sub-result EXISTS while the gate is open. "First scale column" per slice: +# slice 1's is Age (scale); slice 2's factor-term Age arrives ORDINAL (the FactorsForm +# re-types its terms - documented finding) hence as an R factor, so firstScaleColumn +# picks Score there. Both deterministic. +g9_map = _gated_map(g9) +check("zoo run 9: gated sub-result present with threshold 12 in the title", + find_table(zres9, "Gated sub-result") is not None + and "12" in find_table(zres9, "Gated sub-result")["title"], json.dumps(g9)) +check("zoo run 9: gated Age@1: all 10 rows above 12 (mean 38.00)", + g9_map.get(("1", "Age"), {}).get("nAbove") == 10 + and abs((g9_map.get(("1", "Age"), {}).get("meanAbove") or -999) - mean(A["Age"])) < 5e-3, json.dumps(g9)) +check("zoo run 9: gated Score@2 (factor-term Age arrives ordinal/factor, skipped): n=8 mean 19.45", + g9_map.get(("2", "Score"), {}).get("nAbove") == 8 + and abs((g9_map.get(("2", "Score"), {}).get("meanAbove") or -999) - mean([s for s in B["Score"] if s > 12])) < 5e-3, + json.dumps(g9)) + +check("zoo run 9: dataSetIds covers both datasets", sorted(zr9.get("dataSetIds", [])) == [1, 2], str(zr9.get("dataSetIds"))) +auditz9 = provenance_audit(zres9) +check("zoo run 9: PROVENANCE_AUDIT clean (C8/C11)", auditz9 == ("false", "0"), str(auditz9)) +no_raw_tokens(zres9, "zoo run 9") + +# --- zoo run 10 (FLOW F1): the dependent dropdown follows its source ------------------------------- +# assignedA flips to Condition (host A=2): comboI's option list must follow, its effective +# selection re-encodes through the source's NEW namespace (slice 2), and the RESULTS change. +zr10, zres10 = zoo_run(zoo_full_opts( + host_a="2", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + assigned_a={"types": ["nominal"], "value": ["Condition"]}, + assigned_k={"types": ["nominal"], "value": ["Group"]}, + factors_e=[{"name": "Group", "levels": ["A", "B"]}], + combo_i="Condition", text_h="hello zoo", rows_h=4, double_h=2.0, slider_h=0.3, + radio_h="sum", gate_h=True, gated_h=12.0), "run 10 (F1: dependent dropdown follows reassigned source)") +z10 = zoo_rows(zres10) +dep10 = find(z10, "dropdown-dependent") +check("zoo run 10: comboI='Condition' received (option list followed assignedA)", + len(dep10) == 1 and dep10[0]["received"] == "Condition", json.dumps(dep10)) +check("zoo run 10: comboI resolves in the source's NEW namespace (slice 2, levels X, Y, Z)", + len(dep10) == 1 and str(dep10[0]["slice"]) == "2" and str(dep10[0]["values"]).startswith("X, Y, Z"), json.dumps(dep10)) +fl10 = {r["flow"]: r for r in table_rows(find_table(zres10, "Option flows"))} +check("zoo run 10: flow row observed the flipped source list and selection", + "[Condition]" in str(fl10.get("dependent dropdown", {}).get("observed")) + and '"Condition" -> slice 2' in str(fl10.get("dependent dropdown", {}).get("observed")), json.dumps(fl10)) +auditz10 = provenance_audit(zres10) +check("zoo run 10: PROVENANCE_AUDIT clean", auditz10 == ("false", "0"), str(auditz10)) +no_raw_tokens(zres10, "zoo run 10") + +# --- zoo run 11 (FLOW F2a): gate closed -> the gated sub-result must NOT exist --------------------- +zr11, zres11 = zoo_run(zoo_full_opts( + host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + assigned_a={"types": ["scale"], "value": ["Score"]}, + assigned_k={"types": ["nominal"], "value": ["Group"]}, + factors_e=[{"name": "Group", "levels": ["A", "B"]}], + combo_a="Score", combo_i="Score", text_h="hello zoo", rows_h=4, double_h=2.0, slider_h=0.3, + radio_h="sum", gate_h=False, gated_h=12.0), "run 11 (F2a: gate closed)") +check("zoo run 11: NO 'Gated sub-result' table while gateH is off (a gate means absence)", + find_table(zres11, "Gated sub-result") is None, + str([t.get("title") for t in tables(zres11.get("results", zres11)).values()])) +se11 = {r["option"]: r for r in table_rows(find_table(zres11, "Scalar & simple control echoes"))} +check("zoo run 11: gatedH still RECEIVED but annotated 'gate closed'", + "gate closed" in str(se11.get("gatedH", {}).get("received")), json.dumps(se11)) +fl11 = {r["flow"]: r for r in table_rows(find_table(zres11, "Option flows"))} +check("zoo run 11: gate flow row says closed -> no gated sub-result", + "gate closed" in str(fl11.get("checkbox gate", {}).get("observed")), json.dumps(fl11)) +no_raw_tokens(zres11, "zoo run 11") + +# --- zoo run 12 (FLOW F2b): gate open with a different threshold -> numbers move -------------------- +zr12, zres12 = zoo_run(zoo_full_opts( + host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + assigned_a={"types": ["scale"], "value": ["Score"]}, + assigned_k={"types": ["nominal", "scale"], "value": ["Group", "Age"]}, + factors_e=[{"name": "Group", "levels": ["A", "B"]}], + factors_k=[{"name": "Fac1", "title": "Factor 1", "indicators": {"value": ["Age"], "types": [["scale"]]}}], + combo_a="Score", combo_i="Score", text_h="hello zoo", rows_h=4, double_h=2.0, slider_h=0.3, + radio_h="sum", gate_h=True, gated_h=40.0), "run 12 (F2b: gate open, threshold 40)") +g12 = table_rows(find_table(zres12, "Gated sub-result")) +g12_map = _gated_map(g12) +_a1_above = [a for a in A["Age"] if a > 40] +check("zoo run 12: threshold 40 in the title", "40" in find_table(zres12, "Gated sub-result")["title"], json.dumps(g12)) +check("zoo run 12: gated Age@1 n=%d mean %s (threshold moved the numbers)" % (len(_a1_above), fmt(mean(_a1_above))), + g12_map.get(("1", "Age"), {}).get("nAbove") == len(_a1_above) + and abs((g12_map.get(("1", "Age"), {}).get("meanAbove") or -999) - mean(_a1_above)) < 5e-3, json.dumps(g12)) +check("zoo run 12: NO slice-2 row (its factor-term Age arrives ordinal/factor; nothing numeric survives the threshold)", + all(not str(r["slice"]).startswith("2") for r in g12), json.dumps(g12)) +no_raw_tokens(zres12, "zoo run 12") + +# --- zoo run 13/14 (FLOW F3): the integer field drives the shown row count -------------------------- +zr13, zres13 = zoo_run(zoo_full_opts( + host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + assigned_a={"types": ["scale"], "value": ["Score"]}, + assigned_k={"types": ["nominal"], "value": ["Group"]}, + factors_e=[{"name": "Group", "levels": ["A", "B"]}], + combo_a="Score", combo_i="Score", text_h="hello zoo", rows_h=3, double_h=2.0, slider_h=0.3, + radio_h="sum", gate_h=True, gated_h=12.0), "run 13 (F3: rowsH=3)") +pv13 = find_table(zres13, "Loaded data - DataSet 1") +check("zoo run 13: preview title says 'first 3 rows'", pv13 is not None and "first 3 rows" in pv13["title"], + pv13["title"] if pv13 else None) +check("zoo run 13: preview DATA has 3 rows", pv13 is not None and len(table_rows(pv13)) == 3) +tv13 = find(z13 := zoo_rows(zres13), "tableview") +check("zoo run 13: Zoo L table shrank to 3 rows (the same integer drives it)", + len(tv13) == 1 and "3 rows x 2 cols" in tv13[0]["received"], json.dumps(tv13)) +fl13 = {r["flow"]: r for r in table_rows(find_table(zres13, "Option flows"))} +check("zoo run 13: rows flow observed rowsH=3", "rowsH=3" in str(fl13.get("number field -> shown rows", {}).get("observed")), json.dumps(fl13)) +no_raw_tokens(zres13, "zoo run 13") + +zr14, zres14 = zoo_run(zoo_full_opts( + host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + assigned_a={"types": ["scale"], "value": ["Score"]}, + assigned_k={"types": ["nominal"], "value": ["Group"]}, + factors_e=[{"name": "Group", "levels": ["A", "B"]}], + combo_a="Score", combo_i="Score", text_h="hello zoo", rows_h=7, double_h=2.0, slider_h=0.3, + radio_h="sum", gate_h=True, gated_h=12.0), "run 14 (F3: rowsH=7)") +pv14 = find_table(zres14, "Loaded data - DataSet 1") +check("zoo run 14: preview back to 7 rows (both directions of the flow verified)", + pv14 is not None and len(table_rows(pv14)) == 7 and "first 7 rows" in pv14["title"]) +no_raw_tokens(zres14, "zoo run 14") + +# --- zoo run 15 (FLOW F6): the radio group switches the statistic ----------------------------------- +zr15, zres15 = zoo_run(zoo_full_opts( + host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + assigned_a={"types": ["scale"], "value": ["Score"]}, + assigned_k={"types": ["nominal"], "value": ["Group"]}, + factors_e=[{"name": "Group", "levels": ["A", "B"]}], + combo_a="Score", combo_i="Score", text_h="hello zoo", rows_h=4, double_h=2.0, slider_h=0.3, + radio_h="mean", gate_h=True, gated_h=12.0), "run 15 (F6: radio -> mean)") +cs15 = {r["computation"]: r for r in table_rows(find_table(zres15, "Cross-slice computations"))} +check("zoo run 15: radio-driven statistic switched to mean(Score@1) = 12.50 (was sum 125.00 in run 9)", + "mean 12.50" in str(cs15.get("radio-driven statistic", {}).get("value")), json.dumps(cs15)) +se15 = {r["option"]: r for r in table_rows(find_table(zres15, "Scalar & simple control echoes"))} +check("zoo run 15: radioH echo shows 'mean'", se15.get("radioH", {}).get("received") == "mean", json.dumps(se15)) +no_raw_tokens(zres15, "zoo run 15") + +# --- zoo run 16 (FLOW F4): the variables list drives the interaction terms -------------------------- +zr16, zres16 = zoo_run(zoo_full_opts( + host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + assigned_a={"types": ["scale"], "value": ["Score"]}, + assigned_k={"types": ["nominal"], "value": ["Group"]}, + factors_e=[{"name": "Group", "levels": ["A", "B"]}], + interaction_d={"types": [["scale"]], "value": [{"components": ["Score"]}], "optionKey": "components"}, + combo_a="Score", combo_i="Score", text_h="hello zoo", rows_h=4, double_h=2.0, slider_h=0.3, + radio_h="sum", gate_h=True, gated_h=12.0), "run 16 (F4: interaction reduced to a single component)") +cs16 = table_rows(find_table(zres16, "Cross-slice computations")) +check("zoo run 16: interaction-product row GONE after the list changed (single-component terms make no product)", + all(r["computation"] != "interaction product" for r in cs16), json.dumps(cs16)) +itr16 = find(zoo_rows(zres16), "interaction-list") +check("zoo run 16: the provenance echo still shows the received term (order=1)", + len(itr16) == 1 and "order=1" in itr16[0]["rowChild"], json.dumps(itr16)) +fl16 = {r["flow"]: r for r in table_rows(find_table(zres16, "Option flows"))} +check("zoo run 16: flow row observed '1 term(s) received'", + "1 term(s) received" in str(fl16.get("variables list -> interaction terms", {}).get("observed")), json.dumps(fl16)) +no_raw_tokens(zres16, "zoo run 16") + +# --- zoo run 17 (FLOW F5): tableL cells are USED, not just echoed ------------------------------------ +zr17, zres17 = zoo_run(zoo_full_opts( + host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + assigned_a={"types": ["scale"], "value": ["Score"]}, + assigned_k={"types": ["nominal"], "value": ["Group"]}, + factors_e=[{"name": "Group", "levels": ["A", "B"]}], + combo_a="Score", combo_i="Score", text_h="hello zoo", rows_h=4, double_h=2.0, slider_h=0.3, + radio_h="sum", gate_h=True, gated_h=12.0, + table_l=[{"name": "Multiplier", "levels": ["r1", "r2", "r3", "r4"], "values": [2, 2, 2, 2]}, + {"name": "Offset", "levels": ["r1", "r2", "r3", "r4"], "values": [0, 0, 0, 0]}]), + "run 17 (F5: explicit tableL multipliers)") +tv17 = find(zoo_rows(zres17), "tableview") +check("zoo run 17: tableL echo shows the SENT cells (2, 2, 2, 2)", + len(tv17) == 1 and "2, 2, 2, 2" in tv17[0]["rowChild"], json.dumps(tv17)) +cs17 = {r["computation"]: r for r in table_rows(find_table(zres17, "Cross-slice computations"))} +check("zoo run 17: multiplier mean 2.00 x mean(Age@1)=38.00 -> scaled mean 76.000 (cells entered the computation)", + "scaled mean 76.000" in str(cs17.get("tableview multipliers", {}).get("value")), json.dumps(cs17)) +no_raw_tokens(zres17, "zoo run 17") + +# --- zoo run 18 (REGRESSION, mds-plan post): nominal factor term in a namespace lacking the column -- +# Pre-fix this FATALed the whole run (rbridge_readDataSetRequested: "gets 'Group.nominal' but its +# not a columnname at all"): the FactorsForm panel is hostless, its term stamps at the ANALYSIS +# level (DataSet 2, which has no Group), and the collector queued a column that dataset does not +# have. The collector now refuses to queue nonexistent columns; the run must complete with an +# honest unresolved row. +zr18, zres18 = zoo_run(zoo_full_opts( + host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + assigned_a={"types": ["scale"], "value": ["Score"]}, + assigned_k={"types": ["nominal"], "value": ["Group"]}, + factors_e=[{"name": "Group", "levels": ["A", "B"]}], + factors_k=[{"name": "Fac1", "title": "Factor 1", "indicators": {"value": ["Group"], "types": [["nominal"]]}}], + combo_a="Score", combo_i="Score", text_h="hello zoo", rows_h=4, double_h=2.0, slider_h=0.3, + radio_h="sum", gate_h=True, gated_h=12.0), "run 18 (regression: nominal factor term, namespace lacks the column)") +check("zoo run 18: the previously-fatal run COMPLETES", zr18.get("status") == "complete", str(zr18.get("status"))) +fk18 = find(zoo_rows(zres18), "factors-form") +check("zoo run 18: the factor's Group term arrives unresolved (honest '-' row, no phantom resolution)", + len(fk18) == 1 and "Group" in fk18[0]["rowChild"] and str(fk18[0]["slice"]) == "-", json.dumps(fk18)) +auditz18 = provenance_audit(zres18) +check("zoo run 18: PROVENANCE_AUDIT clean after the regression case", auditz18 == ("false", "0"), str(auditz18)) +no_raw_tokens(zres18, "zoo run 18") + print("\n" + ("ALL CHECKS PASSED" if not fails else f"{len(fails)} FAILURES: {fails}")) sys.exit(0 if not fails else 1) From 8223eed9a1182985a7bc678d651ce1bb9315244e Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch <virtuoos@jorisgoosen.nl> Date: Mon, 19 Oct 2026 18:54:13 +0200 Subject: [PATCH 38/44] mds-zoo: the FactorsForm D3.2 hostless-panel deferral is closed - verifier pins the per-factor provenance The per-factor sidecar (FactorsFormBase::createFilterIds, jasp-desktop facac80e6) transports each factor term's OWN filterId, so Zoo K's term Age no longer falls back to the analysis-level namespace (slice 2): it encodes through its source host's view (assignedK -> filter 1) and resolves in slice 1. Run 18's nominal Group term resolves in its own namespace too (where the column exists) - the phantom-namespace route is gone entirely; the existence guard stays pinned C++-side. - Verifier: the run-9 deferral check rewritten (slice 1, not the analysis level) + new term-granularity check (rowChild carries the per-term slice key 'Age(1)'); run-18 check rewritten (resolved row instead of the honest unresolved one) + its term-granularity twin; provenance-story and regression comments updated; dead ANALYSIS_SLICE constant removed. 177 -> 179 checks, ALL CHECKS PASSED against the rebuilt app (bundle rebuilt + reinstalled, libSyntaxInterface.so resynced). - R/QML zoo comments (Zoo K findings) rewritten for the closed deferral. --- R/multiDataSetControls.R | 25 +++++++++++++-------- Tools/verifyMultiDataSetRpc.py | 32 ++++++++++++++++----------- inst/qml/testMultiDataSetControls.qml | 18 ++++++++------- 3 files changed, 45 insertions(+), 30 deletions(-) diff --git a/R/multiDataSetControls.R b/R/multiDataSetControls.R index 59b9309..e323899 100644 --- a/R/multiDataSetControls.R +++ b/R/multiDataSetControls.R @@ -27,20 +27,27 @@ # 2-component terms (bound value [["Score","Age"]]); per-pair # resolution + correlation # factors-form Zoo K - FactorsForm (the FactorsList family): per factor name/title + -# terms. FINDING: the form's inner panels are not hosts - their -# terms encode through the ANALYSIS-level namespace, NOT the -# source list's host (nested provenance is explicitly future -# work, see factorsformbase.cpp D3.2 NOTE); pinned, not faked. +# terms. The form's inner panels are not hosts, but the per-factor +# sidecar (FactorsFormBase::createFilterIds, mds-plan post) now +# transports each term's OWN filterId per factor and per term: a +# factor term carries the stamp of the host its source list synced +# it from (here assignedK -> filter 1) and encodes through THAT +# filter's view - resolved slice by slice, no analysis-level +# fallback (the D3.2 hostless-panel deferral is CLOSED; pinned +# here, not faked). # REGRESSION PINNED C++-SIDE (mds-plan post): a factor term whose -# column the analysis-level namespace lacks (e.g. a NOMINAL Group -# term while the shown dataset is DataSet 2) used to FATAL the -# whole run - rbridge_readDataSetRequested refused "Group.nominal" +# column the resolved namespace lacks (e.g. a NOMINAL Group term +# while the shown dataset is DataSet 2) used to FATAL the whole +# run - rbridge_readDataSetRequested refused "Group.nominal" # ("not a columnname at all") because the collector inserted # explicitly-typed terms without an existence check (pre-existing # gap, reachable only since the aware stamping). The collector now # refuses to queue columns the resolved namespace does not have -# (Common/columnencoder.cpp) and the zoo asserts the honest -# unresolved row instead of a fatal. +# (Common/columnencoder.cpp); with the per-factor sidecar the term +# resolves in its OWN namespace (where the column exists), so the +# phantom-namespace route is gone entirely - the zoo pins the +# resolved row + clean audit instead of the old honest unresolved +# row. # tableview Zoo L - bound TableView (row count driven by Zoo H's integer); cells # are plain values (no provenance - not a Terms option) and are # USED in the cross-slice computation, not just echoed diff --git a/Tools/verifyMultiDataSetRpc.py b/Tools/verifyMultiDataSetRpc.py index d3b49ec..e6bc639 100644 --- a/Tools/verifyMultiDataSetRpc.py +++ b/Tools/verifyMultiDataSetRpc.py @@ -645,10 +645,6 @@ def _read_columns(path): def fmt(x, nd=2): return f"{x:.{nd}f}" def mean(v): return sum(v) / len(v) -# the analysis-level namespace of a fresh RPC session: the SHOWN dataset is the last loaded one -# (DataSet 2, filter 2) - hostless panels (the FactorsForm's) stamp there. -ANALYSIS_SLICE = "2" - def find_table(res, title_prefix): return next((t for t in tables(res.get("results", res)).values() if str(t.get("title", "")).startswith(title_prefix)), None) @@ -672,8 +668,10 @@ def no_raw_tokens(res, tag): # assignedA Score via host A(1) -> slice 1 | assignedB Score via host B(2) -> slice 2 # assignedK Group+Age via host K(1) -> slice 1 (Group feeds the encodeThis demo, Age is the # factor term's source) | pairsJ Score+Age via host J(1) -> slice 1 -# interactionD Score:Age via host D(1) -> slice 1 | factorsK Age -> hostless panel -> -# ANALYSIS-level namespace (slice 2) - the D3.2 deferral, pinned as behaviour +# interactionD Score:Age via host D(1) -> slice 1 | factorsK Age -> the per-factor sidecar +# (FactorsFormBase::createFilterIds, mds-plan post) transports the term's OWN filterId per +# factor and per term -> slice 1 (the term's stamp comes from its source host assignedK; the +# D3.2 hostless-panel deferral - analysis-level namespace, slice 2 - is CLOSED, pinned here) zr9, zres9 = zoo_run(zoo_full_opts( host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", assigned_a={"types": ["scale"], "value": ["Score"]}, @@ -740,8 +738,10 @@ def no_raw_tokens(res, tag): check("zoo run 9: factorsK Fac1 received with title and var Age", len(fk) == 1 and fk[0]["received"] == "Fac1" and "title=Factor 1" in fk[0]["rowChild"] and "Age" in fk[0]["rowChild"], json.dumps(fk)) -check("zoo run 9: factorsK var Age resolves in the ANALYSIS-level namespace (slice 2) - the D3.2 hostless-panel deferral", - len(fk) == 1 and str(fk[0]["slice"]) == ANALYSIS_SLICE and "slices: 2" in str(fk[0]["values"]), json.dumps(fk)) +check("zoo run 9: factorsK var Age resolves in ITS OWN term filter's namespace (slice 1) - the per-factor sidecar closed the D3.2 hostless-panel deferral", + len(fk) == 1 and str(fk[0]["slice"]) == "1" and "slices: 1" in str(fk[0]["values"]), json.dumps(fk)) +check("zoo run 9: factorsK resolution is TERM-level inside the factor object (rowChild carries the per-term slice key 'Age(1)')", + len(fk) == 1 and "vars: Age(1)" in str(fk[0]["rowChild"]), json.dumps(fk)) fs = [r for r in z9 if r["family"] == "factors-source" and r["received"] == "Group"] check("zoo run 9: assignedK Group (the factor SOURCE) resolves in ITS OWN host's slice 1 with levels", @@ -987,10 +987,14 @@ def _gated_map(g): # --- zoo run 18 (REGRESSION, mds-plan post): nominal factor term in a namespace lacking the column -- # Pre-fix this FATALed the whole run (rbridge_readDataSetRequested: "gets 'Group.nominal' but its -# not a columnname at all"): the FactorsForm panel is hostless, its term stamps at the ANALYSIS +# not a columnname at all"): the FactorsForm panel is hostless, its term stamped at the ANALYSIS # level (DataSet 2, which has no Group), and the collector queued a column that dataset does not -# have. The collector now refuses to queue nonexistent columns; the run must complete with an -# honest unresolved row. +# have. The collector now refuses to queue nonexistent columns. Since the per-factor sidecar +# (FactorsFormBase::createFilterIds, mds-plan post) transports the term's OWN filterId, the term +# no longer falls back to the analysis-level namespace at all: it encodes through its source +# host's view (filter 1, DataSet 1, which HAS the nominal Group) and resolves in slice 1 - the +# phantom-namespace route is gone. The run completes with a RESOLVED row and a clean audit; the +# existence guard itself stays pinned C++-side (Common/columnencoder.cpp). zr18, zres18 = zoo_run(zoo_full_opts( host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", assigned_a={"types": ["scale"], "value": ["Score"]}, @@ -1001,8 +1005,10 @@ def _gated_map(g): radio_h="sum", gate_h=True, gated_h=12.0), "run 18 (regression: nominal factor term, namespace lacks the column)") check("zoo run 18: the previously-fatal run COMPLETES", zr18.get("status") == "complete", str(zr18.get("status"))) fk18 = find(zoo_rows(zres18), "factors-form") -check("zoo run 18: the factor's Group term arrives unresolved (honest '-' row, no phantom resolution)", - len(fk18) == 1 and "Group" in fk18[0]["rowChild"] and str(fk18[0]["slice"]) == "-", json.dumps(fk18)) +check("zoo run 18: the factor's Group term resolves in ITS OWN term filter's namespace (slice 1) - the phantom-namespace route is gone with the per-factor sidecar", + len(fk18) == 1 and "Group" in fk18[0]["rowChild"] and str(fk18[0]["slice"]) == "1" and "slices: 1" in str(fk18[0]["values"]), json.dumps(fk18)) +check("zoo run 18: the resolution is TERM-level inside the factor object (rowChild carries the per-term slice key 'Group(1)')", + len(fk18) == 1 and "vars: Group(1)" in str(fk18[0]["rowChild"]), json.dumps(fk18)) auditz18 = provenance_audit(zres18) check("zoo run 18: PROVENANCE_AUDIT clean after the regression case", auditz18 == ("false", "0"), str(auditz18)) no_raw_tokens(zres18, "zoo run 18") diff --git a/inst/qml/testMultiDataSetControls.qml b/inst/qml/testMultiDataSetControls.qml index 1b96a24..5d33c50 100644 --- a/inst/qml/testMultiDataSetControls.qml +++ b/inst/qml/testMultiDataSetControls.qml @@ -314,17 +314,19 @@ Form // ---- Zoo K: factors form (the FactorsList family) ------------------------------------------------ // FactorsList is one panel of a FactorsForm; the serialized shape is the form's: per factor - // {name, title, indicators}. Two findings (pinned by the verifier): - // 1. The form's inner panels are NOT hosts and do not inherit the source list's filter stamp - - // their unqualified terms encode through the ANALYSIS-level namespace (the D3.2 NOTE in - // factorsformbase.cpp leaves nested per-factor provenance to the repeated-measures exercise). + // {name, title, indicators}. Findings (pinned by the verifier): + // 1. The form's inner panels are NOT hosts, but the per-factor sidecar + // (FactorsFormBase::createFilterIds, mds-plan post) transports each term's OWN filterId + // per factor and per term: a term keeps the stamp its source list synced onto it + // (assignedK -> filter 1) and encodes through THAT filter's view - resolved slice by + // slice, no analysis-level fallback (the D3.2 hostless-panel deferral is CLOSED). // 2. A factor term must be a member of the form's SOURCE list (assignedK): a term outside it is // silently dropped by the form's model sync, and a term the form keeps is RE-TYPED by the // form (Age arrived ordinal here although sent scale, so the engine slice receives it as an - // R factor). A term whose column the analysis-level namespace lacks (e.g. nominal Group - // while DataSet 2 is shown) used to FATAL the run - the mds-plan post existence guard in - // Common/columnencoder.cpp now refuses to queue columns a namespace does not have, and the - // verifier pins the honest unresolved row (run 18) instead of a fatal. + // R factor). A term whose column the resolved namespace lacks used to FATAL the run - the + // mds-plan post existence guard in Common/columnencoder.cpp refuses to queue columns a + // namespace does not have; with the per-factor sidecar the term resolves in its OWN + // namespace instead (verifier run 18 pins the resolved row + clean audit). Section { title: qsTr("Zoo K - factors form (FactorsList panels over an assigned source)") From ac2e65503f3debc55658552093f5756ec06f4f1b Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch <virtuoos@jorisgoosen.nl> Date: Thu, 22 Oct 2026 00:32:42 +0200 Subject: [PATCH 39/44] mds-zoo: origin-authoritative rebuild - every assigned list over a pool, extra source on the third filter Owner rules applied: the test module contains ONLY assigned variables lists with at least one source (a bare assigned list - no pool relation at all - is a malformed configuration, not a supported one), and the per-list dataSetSelectionOption crutch is gone everywhere: selections live on POOLS. - Zoo B (bare assigned list) REMOVED. Its two real pins moved: the -1 analysis-fallback resolution lives on the default-provider pool's assigned list (assignedDefault, verifier run 19), foreign-stamp survival on the anchor runs. - Zoo C (row controls, the TermRowKey twin pin) is a form over its own default-provider pool (zooPoolC); the pin is unchanged - key -1, so the stamped twins are never swept. - Zoo G (cross-host bindTo), Zoo J (pairs) and Zoo K (factors-form source): the selection moved to a pool (zooPoolG/J/K) and every list takes its source from its form. The pins are unchanged: bindTo keeps the SOURCE filterId, pairs pair into 2-component terms, the per-factor sidecar transports each term's own filterId. - Zoo M: the EXTRA SOURCE is zooPoolThree - an AvailableVariablesList hosted by the bootstrapped third filter of the test dataset (the anchor scenario's namespace); assignedHostless renamed to assignedDefault (hosting is a pool property; assigned lists never host). - Zoo N's derived pool sources assignedA + assignedDefault (mixed provenance, stamps ride the refs). - Verifier re-pinned: the anchor Age rows assert the ordinal values listing (Age imports ORDINAL in DataSetA - data_info -; a term sent as scale is re-typed to the column's real type at intake, the established zoo finding), not a mean. ALL CHECKS PASSED against the rebuilt bundle (21 runs incl. the ds1/f1 -> ds1/f3 anchor there and back). --- R/multiDataSetControls.R | 38 ++-- R/multiDataSetControlsFuncWrapper.R | 11 +- Tools/verifyMultiDataSetRpc.py | 168 ++++++++++++++--- inst/qml/testMultiDataSetControls.qml | 252 ++++++++++++++++++++------ 4 files changed, 373 insertions(+), 96 deletions(-) diff --git a/R/multiDataSetControls.R b/R/multiDataSetControls.R index e323899..7a40e36 100644 --- a/R/multiDataSetControls.R +++ b/R/multiDataSetControls.R @@ -9,11 +9,23 @@ # Families (see inst/qml/testMultiDataSetControls.qml): # assigned-list Zoo A - plain assigned list under a selecting available list # combobox-host Zoo A - DropDown served by the same host: value stays a PLAIN STRING -# assigned-host Zoo B - assigned list that IS its own selection host # components-rows Zoo C - row-bearing variables list (the TableFields shape; shared -# RowControls/TermRowKey machinery): per-row term + keep + shift; -# same-name twin rows from two filters keep their OWN -# children (the D6.1 TermRowKey re-keying) +# RowControls/TermRowKey machinery), sourced from a +# default-provider pool (every assigned list has >= 1 source - +# owner rule; the bare standalone list died with Zoo B): +# per-row term + keep + shift; same-name twin rows from two +# filters keep their OWN children (the D6.1 TermRowKey +# re-keying); the pool's key is -1, so the stamped twins are +# never swept by any reset +# pool-hostless Zoo M - the default-provider pool's companion (no host selection, +# participation key -1): sent-BARE terms resolve through the +# analysis-level fallback (the C13 use-time resolution), +# sent-stamped terms keep their own stamp +# pool-f3 Zoo M - the EXTRA SOURCE's companion: the pool hosted by the THIRD +# filter of the same test dataset (f3-origin terms carry its +# stamp; the anchor scenario's namespace) +# derived-pool Zoo N - the derived pool's companion: refs from assignedA + +# assignedDefault (mixed provenance), each keeping its own stamp # interaction-list Zoo D - combination terms: all components share the term filterId # factor-levels Zoo E - encodeThis -> extras roundtrip (names never slice columns; # they must come back decoded, no raw JaspExtraOptions_ leakage) @@ -235,12 +247,14 @@ controlsProvenanceTable <- function(jaspResults, options, datasets) { stringsAsFactors = FALSE, check.names = FALSE)) } - # --- plain term lists: Zoo A / Zoo B / Zoo G / Zoo K's source ----------------------------------- + # --- plain term lists: Zoo A / Zoo G / Zoo K's source / Zoo M / Zoo N --------------------------- for (entry in list( - list(fam = "assigned-list", opt = "assignedA", vals = .dependentValues(options$assignedA)), - list(fam = "assigned-host", opt = "assignedB", vals = .dependentValues(options$assignedB)), - list(fam = "factors-source", opt = "assignedK", vals = .dependentValues(options$assignedK)), - list(fam = "bindto-cross-host", opt = "boundG", vals = .dependentValues(options$boundG)))) { + list(fam = "assigned-list", opt = "assignedA", vals = .dependentValues(options$assignedA)), + list(fam = "factors-source", opt = "assignedK", vals = .dependentValues(options$assignedK)), + list(fam = "bindto-cross-host", opt = "boundG", vals = .dependentValues(options$boundG)), + list(fam = "pool-hostless", opt = "assignedDefault", vals = .dependentValues(options$assignedDefault)), + list(fam = "pool-f3", opt = "assignedThree", vals = .dependentValues(options$assignedThree)), + list(fam = "derived-pool", opt = "assignedDerived", vals = .dependentValues(options$assignedDerived)))) { for (value in entry$vals) { key <- .resolveSlice(value, datasets) desc <- .describeValue(key, value, datasets) @@ -577,9 +591,11 @@ crossSliceTable <- function(jaspResults, options, datasets) { } } - # 2. cross-host column product: assignedA's first scale column x assignedB's first scale column + # 2. cross-host column product: assignedA's first scale column x assignedDefault's first scale + # column (the default-provider list - a bare -1 reference resolves through the analysis + # fallback, so the product joins a stamped slice with the fallback slice) colA <- .firstAssignedScale(options$assignedA, datasets) - colB <- .firstAssignedScale(options$assignedB, datasets) + colB <- .firstAssignedScale(options$assignedDefault, datasets) if (!is.null(colA) && !is.null(colB)) { value <- if (length(colA$values) != length(colB$values)) paste0("row lengths differ (", length(colA$values), " vs ", length(colB$values), diff --git a/R/multiDataSetControlsFuncWrapper.R b/R/multiDataSetControlsFuncWrapper.R index d971de7..39bb206 100644 --- a/R/multiDataSetControlsFuncWrapper.R +++ b/R/multiDataSetControlsFuncWrapper.R @@ -29,21 +29,22 @@ multiDataSetControlsFunc <- function( datasets = NULL, version = "1", assignedA = list(types = list(), value = list()), - assignedB = list(types = list(), value = list()), + assignedDefault = list(types = list(), value = list()), + assignedDerived = list(types = list(), value = list()), assignedK = list(types = list(), value = list()), + assignedThree = list(types = list(), value = list()), boundG = list(types = list(), value = list()), codeF = list(columns = list(), model = "", modelOriginal = ""), comboA = list(types = "unknown", value = ""), comboG = "first", comboI = list(types = "unknown", value = ""), dataSetA = "", - dataSetB = "", - dataSetC = "", dataSetD = "", dataSetF = "", dataSetG = "", dataSetJ = "", dataSetK = "", + dataSetM3 = "", doubleH = 1.5, factorsE = list(), factorsK = list(list(indicators = list(), name = "Fac1", title = "Factor 1")), @@ -79,7 +80,7 @@ multiDataSetControlsFunc <- function( # resolved at run time from `options`. Every entry is loaded standalone by runWrappedAnalysis # (jaspBase::storeDataSets -> jaspSyntax::loadDataSets, each under its own name as the dataset # title); the analysis itself receives its slices keyed by filter id (jaspBase datasets contract). - selectionOptions <- c("dataSetB", "dataSetK", "dataSetG", "dataSetF", "dataSetJ", "dataSetC", "dataSetA", "dataSetD") + selectionOptions <- c("dataSetK", "dataSetG", "dataSetF", "dataSetJ", "dataSetM3", "dataSetA", "dataSetD") data <- NULL if (!is.null(datasets) && length(datasets)) { for (selectionOption in selectionOptions) { @@ -96,7 +97,7 @@ multiDataSetControlsFunc <- function( } } - optionsWithFormula <- c("assignedA", "assignedB", "assignedK", "boundG", "codeF", "comboA", "comboG", "comboI", "dataSetA", "dataSetB", "dataSetC", "dataSetD", "dataSetF", "dataSetG", "dataSetJ", "dataSetK", "factorsE", "factorsK", "interactionD", "pairsJ", "rowsC", "tableL") + optionsWithFormula <- c("assignedA", "assignedDefault", "assignedDerived", "assignedK", "assignedThree", "boundG", "codeF", "comboA", "comboG", "comboI", "dataSetA", "dataSetD", "dataSetF", "dataSetG", "dataSetJ", "dataSetK", "dataSetM3", "factorsE", "factorsK", "interactionD", "pairsJ", "rowsC", "tableL") for (name in optionsWithFormula) { if ((name %in% optionsWithFormula) && inherits(options[[name]], "formula")) options[[name]] = jaspBase::jaspFormula(options[[name]], data) } diff --git a/Tools/verifyMultiDataSetRpc.py b/Tools/verifyMultiDataSetRpc.py index e6bc639..a26754d 100644 --- a/Tools/verifyMultiDataSetRpc.py +++ b/Tools/verifyMultiDataSetRpc.py @@ -309,8 +309,11 @@ def overview_ids(res): # mds-plan D6.3 - the component zoo (jaspTestModule analysis multiDataSetControlsFunc / # testMultiDataSetControls.qml). Every control family echoes what it RECEIVED (encoded names, # decoded again on the way out), which slice index each value resolved in, and its row children, -# so per-item provenance is asserted at the value level. The RPC workspace has one default filter -# per dataset (ids 1 and 2), which is exactly two distinct namespaces for the twin cases. +# so per-item provenance is asserted at the value level. The RPC workspace starts with one +# default filter per dataset (ids 1 and 2) - exactly two distinct namespaces for the twin cases. +# The zoo form BOOTSTRAPS a third filter on dataset 1 ("Filter 0", all-rows) at form load, because +# the RPC has no filter-creation method and the mds-plan post zoo correction needs a THIRD pool +# hosted by it (the f3-origin namespace for the anchor scenario); its id is discovered below. # Checks per family: # - same-name columns across two filters in the SAME list (twins) -> distinct encodings, # correct per-slice values (the 12.50/17.73 trick per family) @@ -361,20 +364,28 @@ def find(rows, family, pred=lambda r: True): EMPTY_INTER = {"types": [], "value": [], "optionKey": "components"} EMPTY_CODEF = {"modelOriginal": "", "model": "", "columns": []} -def zoo_full_opts(host_a="1", host_b="", host_c="", host_d="", host_f="", host_g="", +def zoo_full_opts(host_a="1", host_m3="", host_d="", host_f="", host_g="", host_j="", host_k="", - assigned_a=None, assigned_b=None, assigned_k=None, rows_c=None, bound_g=None, + assigned_a=None, assigned_default=None, assigned_k=None, rows_c=None, bound_g=None, + assigned_three=None, assigned_derived=None, interaction_d=None, factors_e=None, factors_k=None, pairs_j=None, code_f=None, combo_a="", combo_i="", text_h="", rows_h=5, double_h=1.5, slider_h=0.5, radio_h="mean", gate_h=False, gated_h=10.0, table_l=None, plain_switch=False, show_meta=True): + # NB: dataSetB/dataSetC are GONE (mds-plan post zoo correction): Zoo B is REMOVED entirely (a + # bare assigned list - no source at all - is a malformed configuration, owner rule) and Zoo C + # is a normal form over a default-provider pool now - neither has a host option to send. + # dataSetM3 is Zoo M's extra-source pool (the THIRD filter of dataset 1, bootstrapped by the + # zoo form itself - see the discovery check right after zoo run 1). opts = { - "dataSetA": host_a, "dataSetB": host_b, "dataSetC": host_c, + "dataSetA": host_a, "dataSetM3": host_m3, "dataSetD": host_d, "dataSetF": host_f, "dataSetG": host_g, "dataSetJ": host_j, "dataSetK": host_k, "assignedA": assigned_a or dict(EMPTY_TERMS), - "assignedB": assigned_b or dict(EMPTY_TERMS), "assignedK": assigned_k or dict(EMPTY_TERMS), + "assignedDefault": assigned_default or dict(EMPTY_TERMS), + "assignedThree": assigned_three or dict(EMPTY_TERMS), + "assignedDerived": assigned_derived or dict(EMPTY_TERMS), "rowsC": rows_c or dict(EMPTY_ROWS), "boundG": bound_g or {"value": []}, "interactionD": interaction_d or dict(EMPTY_INTER), @@ -409,6 +420,17 @@ def zoo_full_opts(host_a="1", host_b="", host_c="", host_d="", host_f="", host_g check("zoo run 1: slice 1 queued (host A), nothing beyond the workspace's two slices", "1" in zslices1 and set(zslices1) <= {"1", "2"}, str(zslices1)) +# --- the zoo fixture's THIRD filter (mds-plan post zoo correction) -------------------------------- +# The zoo form bootstraps "Filter 0" on dataset 1 at form load (zoo run 1 forced the form to +# load, so the bootstrap has run by now). Discover its id via data_info - the verifier must not +# hardcode 3 (a re-run in a workspace that already had extra filters would shift it). +_zi1 = call("data_info", {"dataSetId": 1}) +_zf3 = next((f for f in _zi1.get("filters", []) if not f.get("isDefault")), None) +check("zoo fixture: the form bootstrapped a third filter on dataset 1", _zf3 is not None, + json.dumps(_zi1.get("filters", []))) +F3 = str(_zf3["filterId"]) if _zf3 else "3" +check("zoo fixture: third filter id discovered", F3.isdigit() and F3 not in ("1", "2"), F3) + # --- zoo run 2: the whole zoo --------------------------------------------------------------------- # rowsC arrives in the STORAGE term-object shape: same-name twin rows 'Score' behind filter 1 # (keep=true, shift=1) and filter 2 (keep=false, shift=7). The applier must keep BOTH rows' own @@ -419,9 +441,9 @@ def zoo_full_opts(host_a="1", host_b="", host_c="", host_d="", host_f="", host_g # bind-time encodeRScript must go through the HOST's view (C13 host-bound) - a Score token # resolving in slice 1 (12.50) proves it; the analysis-level fallback would have yielded 17.73. zr2, zres2 = zoo_run(zoo_full_opts( - host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", + host_a="1", host_d="1", host_f="1", host_g="2", assigned_a={"types": ["scale"], "value": ["Score"]}, - assigned_b={"types": ["nominal"], "value": ["Condition"]}, + assigned_default={"types": ["nominal"], "value": ["Condition"]}, rows_c={"value": [{"variable": {"name": "Score", "types": ["scale"], "filterId": 1}, "keep": True, "shift": 1}, {"variable": {"name": "Score", "types": ["scale"], "filterId": 2}, "keep": False, "shift": 7}], "optionKey": "variable"}, @@ -454,9 +476,10 @@ def zoo_full_opts(host_a="1", host_b="", host_c="", host_d="", host_f="", host_g check("zoo run 2: boundG typed copy resolves in the SOURCE slice 1 (12.50) while its own host selects filter 2", len(bg) == 1 and str(bg[0]["slice"]) == "1" and "12.50" in str(bg[0]["values"]), json.dumps(bg)) -# assigned-host (Zoo B) -ab = find(z2, "assigned-host") -check("zoo run 2: assignedB (own host) Condition resolves in slice 2 with its levels", +# default-provider pool's list (Zoo M): a bare sent term resolves through the analysis-level +# fallback, which is the shown dataset (DataSet 2, filter 2) +ab = find(z2, "pool-hostless") +check("zoo run 2: assignedDefault (bare) Condition resolves via the analysis fallback in slice 2 with its levels", len(ab) == 1 and str(ab[0]["slice"]) == "2" and ab[0]["values"].startswith("X"), json.dumps(ab)) # combobox (Zoo A): plain string value. NB the namespace finding, UPDATED by the mds-zoo @@ -642,6 +665,15 @@ def _read_columns(path): A = _read_columns(A_CSV) B = _read_columns(B_CSV) +# Age imports ORDINAL in DataSetA (integer-valued column; data_info: Age=ordinal, Score=scale, +# Group=nominal). A term sent as scale is re-typed to the column's real type at intake (the +# established zoo finding, pinned in Zoo K), so any Age slice arrives as an R factor and the +# echo describes it as its unique-value listing - never as a mean. The anchor checks (runs +# 19/20) therefore assert the listing, computed exactly like R's .fillDescription does: +with open(A_CSV) as _f: + _age_raw = [r["Age"] for r in _csvmod.DictReader(_f)] +_age_listing = ", ".join(list(dict.fromkeys(_age_raw))[:5]) + def fmt(x, nd=2): return f"{x:.{nd}f}" def mean(v): return sum(v) / len(v) @@ -665,7 +697,8 @@ def no_raw_tokens(res, tag): # --- zoo run 9: the expanded zoo, all new families populated -------------------------------------- # Provenance story (fresh RPC session, shown dataset = DataSet 2): -# assignedA Score via host A(1) -> slice 1 | assignedB Score via host B(2) -> slice 2 +# assignedA Score via host A(1) -> slice 1 | assignedDefault bare Score -> analysis fallback +# (shown dataset = DataSet 2) -> slice 2 # assignedK Group+Age via host K(1) -> slice 1 (Group feeds the encodeThis demo, Age is the # factor term's source) | pairsJ Score+Age via host J(1) -> slice 1 # interactionD Score:Age via host D(1) -> slice 1 | factorsK Age -> the per-factor sidecar @@ -673,9 +706,9 @@ def no_raw_tokens(res, tag): # factor and per term -> slice 1 (the term's stamp comes from its source host assignedK; the # D3.2 hostless-panel deferral - analysis-level namespace, slice 2 - is CLOSED, pinned here) zr9, zres9 = zoo_run(zoo_full_opts( - host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + host_a="1", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", assigned_a={"types": ["scale"], "value": ["Score"]}, - assigned_b={"types": ["scale"], "value": ["Score"]}, + assigned_default={"types": ["scale"], "value": ["Score"]}, # NB: the FactorsForm's factors can only hold terms that are in its SOURCE list (assignedK): # a term outside the source is silently dropped by the form's model sync (verified live, run # 9 originally sent Age here while assignedK held only Group -> "(none)"). Group is needed in @@ -841,7 +874,7 @@ def _gated_map(g): # assignedA flips to Condition (host A=2): comboI's option list must follow, its effective # selection re-encodes through the source's NEW namespace (slice 2), and the RESULTS change. zr10, zres10 = zoo_run(zoo_full_opts( - host_a="2", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + host_a="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", assigned_a={"types": ["nominal"], "value": ["Condition"]}, assigned_k={"types": ["nominal"], "value": ["Group"]}, factors_e=[{"name": "Group", "levels": ["A", "B"]}], @@ -863,7 +896,7 @@ def _gated_map(g): # --- zoo run 11 (FLOW F2a): gate closed -> the gated sub-result must NOT exist --------------------- zr11, zres11 = zoo_run(zoo_full_opts( - host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + host_a="1", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", assigned_a={"types": ["scale"], "value": ["Score"]}, assigned_k={"types": ["nominal"], "value": ["Group"]}, factors_e=[{"name": "Group", "levels": ["A", "B"]}], @@ -882,7 +915,7 @@ def _gated_map(g): # --- zoo run 12 (FLOW F2b): gate open with a different threshold -> numbers move -------------------- zr12, zres12 = zoo_run(zoo_full_opts( - host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + host_a="1", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", assigned_a={"types": ["scale"], "value": ["Score"]}, assigned_k={"types": ["nominal", "scale"], "value": ["Group", "Age"]}, factors_e=[{"name": "Group", "levels": ["A", "B"]}], @@ -902,7 +935,7 @@ def _gated_map(g): # --- zoo run 13/14 (FLOW F3): the integer field drives the shown row count -------------------------- zr13, zres13 = zoo_run(zoo_full_opts( - host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + host_a="1", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", assigned_a={"types": ["scale"], "value": ["Score"]}, assigned_k={"types": ["nominal"], "value": ["Group"]}, factors_e=[{"name": "Group", "levels": ["A", "B"]}], @@ -920,7 +953,7 @@ def _gated_map(g): no_raw_tokens(zres13, "zoo run 13") zr14, zres14 = zoo_run(zoo_full_opts( - host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + host_a="1", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", assigned_a={"types": ["scale"], "value": ["Score"]}, assigned_k={"types": ["nominal"], "value": ["Group"]}, factors_e=[{"name": "Group", "levels": ["A", "B"]}], @@ -933,7 +966,7 @@ def _gated_map(g): # --- zoo run 15 (FLOW F6): the radio group switches the statistic ----------------------------------- zr15, zres15 = zoo_run(zoo_full_opts( - host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + host_a="1", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", assigned_a={"types": ["scale"], "value": ["Score"]}, assigned_k={"types": ["nominal"], "value": ["Group"]}, factors_e=[{"name": "Group", "levels": ["A", "B"]}], @@ -948,7 +981,7 @@ def _gated_map(g): # --- zoo run 16 (FLOW F4): the variables list drives the interaction terms -------------------------- zr16, zres16 = zoo_run(zoo_full_opts( - host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + host_a="1", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", assigned_a={"types": ["scale"], "value": ["Score"]}, assigned_k={"types": ["nominal"], "value": ["Group"]}, factors_e=[{"name": "Group", "levels": ["A", "B"]}], @@ -968,7 +1001,7 @@ def _gated_map(g): # --- zoo run 17 (FLOW F5): tableL cells are USED, not just echoed ------------------------------------ zr17, zres17 = zoo_run(zoo_full_opts( - host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + host_a="1", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", assigned_a={"types": ["scale"], "value": ["Score"]}, assigned_k={"types": ["nominal"], "value": ["Group"]}, factors_e=[{"name": "Group", "levels": ["A", "B"]}], @@ -996,7 +1029,7 @@ def _gated_map(g): # phantom-namespace route is gone. The run completes with a RESOLVED row and a clean audit; the # existence guard itself stays pinned C++-side (Common/columnencoder.cpp). zr18, zres18 = zoo_run(zoo_full_opts( - host_a="1", host_b="2", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", + host_a="1", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", assigned_a={"types": ["scale"], "value": ["Score"]}, assigned_k={"types": ["nominal"], "value": ["Group"]}, factors_e=[{"name": "Group", "levels": ["A", "B"]}], @@ -1013,5 +1046,94 @@ def _gated_map(g): check("zoo run 18: PROVENANCE_AUDIT clean after the regression case", auditz18 == ("false", "0"), str(auditz18)) no_raw_tokens(zres18, "zoo run 18") +# ---------------------------------------------------------------------------- +# mds-plan post zoo correction - the three pools, the ds1/f1 -> ds1/f3 ANCHOR and the +# derived pool. Zoo M holds three pools over the same test dataset: the hostless pool +# (no host selection, participation key -1), Zoo A's pool on the existing selection +# (dataSetA) and the EXTRA SOURCE zooPoolThree on the bootstrapped third filter +# (dataSetM3 = F3). f3-origin terms carry the filter-3 stamp (JASPColumn_D1_F3_). +# ---------------------------------------------------------------------------- + +# --- zoo run 19: the three pools populated ---------------------------------------------------------- +# assignedThree Score@3 -> the f3 namespace -> slice F3 (12.50: filter 3 is all-rows on ds1) +# assignedDefault bare Score -> the analysis-level fallback (shown = DataSet 2) -> slice 2 +# assignedA is pre-loaded with the anchor's three-term state: Score@1 (f1-origin), Score@2 +# (the ds2 twin) and Age@3 (an f3-ORIGIN term - the one a dataset-scoped sweep would kill). +zr19, zres19 = zoo_run(zoo_full_opts( + host_a="1", host_m3=F3, + assigned_a={"value": [{"name": "Score", "types": ["scale"], "filterId": 1}, + {"name": "Score", "types": ["scale"], "filterId": 2}, + {"name": "Age", "types": ["scale"], "filterId": int(F3)}]}, + assigned_three={"value": [{"name": "Score", "types": ["scale"], "filterId": int(F3)}]}, + assigned_default={"types": ["scale"], "value": ["Score"]}), "run 19 (three pools: f3 pool + default-provider pool + anchor pre-state)") +z19 = zoo_rows(zres19) +f3row = find(z19, "pool-f3") +check("zoo run 19: assignedThree Score@3 resolves in the f3 namespace (slice %s, 12.50)" % F3, + len(f3row) == 1 and str(f3row[0]["slice"]) == F3 and "12.50" in str(f3row[0]["values"]), json.dumps(f3row)) +hl = find(z19, "pool-hostless") +check("zoo run 19: assignedDefault bare Score resolves via the analysis fallback (slice 2, 17.73)", + len(hl) == 1 and str(hl[0]["slice"]) == "2" and "17.73" in str(hl[0]["values"]), json.dumps(hl)) +a19 = find(z19, "assigned-list") +_a19_by = {(r["received"], str(r["slice"])): r for r in a19} +check("zoo run 19: assignedA holds the three-term anchor pre-state (Score@1 12.50, Score@2 17.73, Age@3 as its ordinal values)", + len(a19) == 3 + and "12.50" in str(_a19_by.get(("Score", "1"), {}).get("values")) + and "17.73" in str(_a19_by.get(("Score", "2"), {}).get("values")) + and _age_listing in str(_a19_by.get(("Age", F3), {}).get("values")), json.dumps(a19)) +auditz19 = provenance_audit(zres19) +check("zoo run 19: PROVENANCE_AUDIT clean", auditz19 == ("false", "0"), str(auditz19)) +no_raw_tokens(zres19, "zoo run 19") + +# --- zoo run 20 (THE ANCHOR): switch Zoo A's pool ds1/f1 -> ds1/f3, assignedA NOT re-sent ------------ +# Delta run (only dataSetA changes, exactly like run 8's pattern): the pool's universe becomes +# filter 3's view and availableTermsReset fires with filterIdToUpdate = 1 (the OLD selection - +# the participation key). Expected per the origin-authoritative rule: +# Score@1 participates (1 == 1) -> replaced by the delivered universe term Score@3 (slice F3) +# Score@2 does not (2 != 1) -> survives untouched (slice 2, 17.73) +# Age@3 does not (3 != 1) -> SURVIVES: the anchor. Both filters sit on the SAME dataset, +# so a dataset-scoped participation would have swept it. +zr20, zres20 = zoo_run({"dataSetA": "3", "showMeta": True}, "run 20 (ANCHOR: host A f1->f3, terms not re-sent)") +z20 = zoo_rows(zres20) +a20 = find(z20, "assigned-list") +_a20_by = {(r["received"], str(r["slice"])): r for r in a20} +check("zoo run 20: ANCHOR - Age@3 (f3-origin) NOT swept by the f1->f3 switch (still present, slice %s, ordinal values)" % F3, + ("Age", F3) in _a20_by and _age_listing in str(_a20_by[("Age", F3)].get("values")), json.dumps(a20)) +check("zoo run 20: ANCHOR - Score@2 (foreign stamp) still present (slice 2, 17.73)", + ("Score", "2") in _a20_by and "17.73" in str(_a20_by[("Score", "2")].get("values")), json.dumps(a20)) +check("zoo run 20: Score@1 FOLLOWED the switch (re-stamped to f3: now slice %s, 12.50)" % F3, + ("Score", F3) in _a20_by and "12.50" in str(_a20_by[("Score", F3)].get("values")) + and ("Score", "1") not in _a20_by, json.dumps(a20)) +check("zoo run 20: exactly 3 assignedA rows (one moved, two survived)", + len(a20) == 3, json.dumps(a20)) +check("zoo run 20: dataSetIds still just the workspace's two datasets (filter %s is dataset 1's)" % F3, + sorted(zr20.get("dataSetIds", [])) == [1, 2], str(zr20.get("dataSetIds"))) +auditz20 = provenance_audit(zres20) +check("zoo run 20: PROVENANCE_AUDIT clean after the anchor switch", auditz20 == ("false", "0"), str(auditz20)) +no_raw_tokens(zres20, "zoo run 20") + +# --- zoo run 21: the derived pool (mixed provenance) ------------------------------------------------- +# Zoo N's pool sources assignedA + assignedDefault; its companion assignedDerived receives refs with +# MIXED stamps. Each ref must encode through its OWN stamp (no host of its own): Score@1 in +# slice 1, Condition@2 in slice 2 - one list, two truths. +zr21, zres21 = zoo_run(zoo_full_opts( + host_a="1", + assigned_a={"types": ["scale"], "value": ["Score"]}, + assigned_default={"types": ["nominal"], "value": ["Condition"]}, + assigned_derived={"value": [{"name": "Score", "types": ["scale"], "filterId": 1}, + {"name": "Condition", "types": ["nominal"], "filterId": 2}]}), + "run 21 (derived pool: mixed-provenance refs)") +z21 = zoo_rows(zres21) +dv = find(z21, "derived-pool") +_dv_by = {r["received"]: r for r in dv} +check("zoo run 21: derived ref Score@1 resolves in slice 1 (12.50)", + len(dv) == 2 and "12.50" in str(_dv_by.get("Score", {}).get("values")) + and str(_dv_by.get("Score", {}).get("slice")) == "1", json.dumps(dv)) +check("zoo run 21: derived ref Condition@2 resolves in slice 2 (levels X, Y, Z)", + "X, Y, Z" in str(_dv_by.get("Condition", {}).get("values")) + and str(_dv_by.get("Condition", {}).get("slice")) == "2", json.dumps(dv)) +auditz21 = provenance_audit(zres21) +check("zoo run 21: PROVENANCE_AUDIT clean", auditz21 == ("false", "0"), str(auditz21)) +no_raw_tokens(zres21, "zoo run 21") + print("\n" + ("ALL CHECKS PASSED" if not fails else f"{len(fails)} FAILURES: {fails}")) sys.exit(0 if not fails else 1) diff --git a/inst/qml/testMultiDataSetControls.qml b/inst/qml/testMultiDataSetControls.qml index 5d33c50..f48b7dd 100644 --- a/inst/qml/testMultiDataSetControls.qml +++ b/inst/qml/testMultiDataSetControls.qml @@ -5,17 +5,22 @@ // (the D2.2 list-level pattern) feeding an AssignedVariablesList, plus a DropDown // served by the same host (comboboxes stay PLAIN strings - never term objects - // while their value still resolves through the host namespace). -// Zoo B ASSIGNED list as its own host: an AssignedVariablesList with its own -// dataSetSelectionOption (C10 says any VariablesListBase can host; this is the -// second-source case the design must support). +// Zoo B REMOVED (mds-plan post zoo correction, owner): a bare assigned list - an assigned +// list with no source at all - is a malformed configuration, not a supported one: +// every real assigned list sits in a form with a pool. Its two real pins moved: the +// -1 analysis-fallback resolution is pinned on the default-provider pool's assigned +// list (Zoo M, verifier run 19), foreign-stamp survival on the anchor runs. // Zoo C TableFields-style ROW-CONTROL list: JASP.TableFields does not exist in this // codebase and ComponentsList (its closest named relative) cannot carry a selection, // so this is the row family as TableFields actually is it: an AssignedVariablesList -// with a rowComponent (a keep checkbox and a shift integer) plus its own selection. -// Same-name twin rows behind DIFFERENT filters (the C9 twins, set by the RPC verifier -// through the Storage term-object shape) must keep their own children - the D6.1 +// with a rowComponent (a keep checkbox and a shift integer), in its own form over a +// default-provider pool (every assigned list has >= 1 source - owner rule). Same-name +// twin rows behind DIFFERENT filters (the C9 twins, set by the RPC verifier through +// the Storage term-object shape) must keep their own children - the D6.1 // RelatedValuesPerTerm/RowControls re-keying (TermRowKey = value + filterId) is what -// makes that work; pre-fix the second row silently clobbered the first. +// makes that work; pre-fix the second row silently clobbered the first. The pool's +// key is -1, so the stamped twins are never swept by a universe reset: each twin +// follows only its own stamp. // Zoo D interaction terms: a VariablesList in Interaction mode - components of one // combination term share the term-level filterId (mds-plan C9 uniform-carry rule). // Zoo E FactorLevelList: encodeThis -> extras-encoder roundtrip (factor/level names encode @@ -28,6 +33,24 @@ // UNQUALIFIED terms (D5.1 intake guard) and the restamp walk never touches foreign // stamps (D3.5 owns-the-stamp), so the value must encode and resolve through the // SOURCE's slice even though this list's own dropdown selects another one. +// Zoo M THREE pools over the same test dataset (the mds-plan post zoo correction): a +// hostless pool (no dataSetSelectionOption at all - the default-provider pool whose +// participation key is -1), Zoo A's pool hosted by the existing filter selection +// (dataSetA, filter 1), and the EXTRA SOURCE: a new AvailableVariablesList hosted by +// the THIRD filter of the same test dataset. The third filter does not exist in a +// fresh RPC workspace (data_load mints one default filter per dataset and the RPC has +// no filter-creation method), so this form bootstraps it itself - see the guard in +// Zoo M (module-side test fixture: the test provides its own filters). f3-origin terms +// carry its stamp, which is what makes the anchor scenario assertable: switching Zoo +// A's pool from ds1/f1 to ds1/f3 must move the f1-stamped terms (their participation +// key is the OLD selection id) while an f3-origin term in the same list survives +// untouched - a dataset-scoped sweep would wrongly have swept it, because both +// filters sit on the SAME dataset. +// Zoo N DERIVED pool: an AvailableVariablesList whose source is TWO assigned lists +// (assignedA + assignedDefault, mixed provenance). Its terms are references that keep +// each source term's OWN stamp; the known limitation (the reason the `to` scalar was +// dropped from the reset signal) is that mixed-source refs do not follow a +// source-list host switch this pass. // // The R echo (R/multiDataSetControls.R) reports for every received value: the (encoded) name // as received, WHICH slice index it resolved in, and the row children - so provenance is @@ -49,8 +72,10 @@ Form "slice each value resolved in and its row children.<br>" + "<ul>" + "<li>Zoo A: selection on the available list drives the assigned list and the combobox.</li>" + - "<li>Zoo B: an ASSIGNED list hosts a selection of its own.</li>" + - "<li>Zoo C: row-control list (row-bearing variables list, the TableFields shape): same-name twin rows " + + "<li>Zoo B: removed - a bare assigned list (no source) is malformed; its pins live on the " + + "default-provider pool's list (Zoo M).</li>" + + "<li>Zoo C: row-control list (row-bearing variables list, the TableFields shape), sourced " + + "from the default-provider pool: same-name twin rows " + "from two filters keep their OWN row values.</li>" + "<li>Zoo D: interaction terms share one filterId over all components.</li>" + "<li>Zoo E: factor/level names roundtrip through the extras encoder.</li>" + @@ -60,9 +85,11 @@ Form "as plain values AND drive results: the integer sets the preview row count, the radio picks the " + "flow statistic, the checkbox gates a sub-result.</li>" + "<li>Zoo I: a DEPENDENT dropdown - its option list is driven by Zoo A's assigned list.</li>" + - "<li>Zoo J: a paired variables list (own selection); the pair is one term over two columns.</li>" + - "<li>Zoo K: a FactorsForm (the FactorsList family) fed by an assigned list with its own selection.</li>" + + "<li>Zoo J: a paired variables list over the pool on dataSetJ; the pair is one term over two columns.</li>" + + "<li>Zoo K: a FactorsForm (the FactorsList family) fed by an assigned list over the pool on dataSetK.</li>" + "<li>Zoo L: a bound TableView whose row count follows Zoo H's integer field.</li>" + + "<li>Zoo M: three pools over DataSet 1 - hostless (-1), Zoo A's pool on filter 1, and the extra source on the third filter; switching Zoo A from f1 to f3 must not sweep an f3-origin term.</li>" + + "<li>Zoo N: a derived pool whose options come from Zoo A's and the default pool's assigned lists (mixed provenance).</li>" + "</ul>" + "The results panel shows the LOADED DATA per slice (head of every column, row count = Zoo H's " + "integer), the per-family provenance echo, the scalar echoes, the flow demonstrations and the " + @@ -103,46 +130,42 @@ Form } } - // ---- Zoo B: the ASSIGNED list is its own host ------------------------------------------------ - Section { - title: qsTr("Zoo B - assigned list as second source (own selection)") - - AssignedVariablesList - { - name: "assignedB" - title: qsTr("Assigned B (own Data selection)") - dataSetSelectionOption: "dataSetB" - } - } - // ---- Zoo C: TableFields-style row controls -------------------------------------------------- // JASP.TableFields does not exist in this codebase; ComponentsList is its closest NAMED - // relative but is not a VariablesListBase (no dataSetSelectionOption possible, and without a - // source relation its values never encode). The row machinery RowControls/TermRowKey is shared + // relative but is not a VariablesListBase. The row machinery RowControls/TermRowKey is shared // with the row-bearing variables list, which is also what TableFields actually is (term rows + // per-row controls) - so the zoo exercises the row family through an AssignedVariablesList - // with a rowComponent and its own selection host. + // with a rowComponent, in its own form over a default-provider pool (every assigned list has + // >= 1 source - owner rule; the bare standalone form died with Zoo B). Its pool's key is -1, + // so the stamped twin rows below are never swept by any reset - each twin follows only its + // own stamp, which is exactly the C9 case. Section { - title: qsTr("Zoo C - row controls: per-row term + per-row values (own selection)") + title: qsTr("Zoo C - row controls: per-row term + per-row values (default-provider source)") - AssignedVariablesList + VariablesForm { - name: "rowsC" - title: qsTr("Rows (term + keep + shift)") - dataSetSelectionOption: "dataSetC" - optionKey: "variable" - - rowComponent: Row + AvailableVariablesList { - CheckBox - { - name: "keep" - } - IntegerField + name: "zooPoolC" // default-provider pool: no selection, participation key -1 + } + AssignedVariablesList + { + name: "rowsC" + title: qsTr("Rows (term + keep + shift)") + optionKey: "variable" + + rowComponent: Row { - name: "shift" - min: -999 - max: 999 + CheckBox + { + name: "keep" + } + IntegerField + { + name: "shift" + min: -999 + max: 999 + } } } } @@ -199,14 +222,28 @@ Form } // ---- Zoo G: cross-host bindTo (D6.2) --------------------------------------------------------- + // The selection lives on the POOL now (the per-list dataSetSelectionOption crutch is gone, + // same correction as B/C): boundG sits in a form over the pool hosted by dataSetG's filter, + // so every assigned list has its source. The cross-host pin is unchanged: the verifier binds + // a typed copy of a filter-1 term into boundG while the pool selects filter 2 - the copy + // keeps the SOURCE filterId and encodes through the SOURCE's slice (D6.2), origin + // -authoritative: a stamp is born at Storage/bindTo and is not requalified by a destination. Section { title: qsTr("Zoo G - typed copy bound from Zoo A keeps the SOURCE filter") - AssignedVariablesList + VariablesForm { - name: "boundG" - title: qsTr("Bound from assignedA (typed copy)") - dataSetSelectionOption: "dataSetG" + AvailableVariablesList + { + name: "zooPoolG" + dataSetSelectionOption: "dataSetG" + } + AssignedVariablesList + { + name: "boundG" + title: qsTr("Bound from assignedA (typed copy)") + depends: "dataSetG" + } } } @@ -299,16 +336,25 @@ Form // ---- Zoo J: paired variables list --------------------------------------------------------------- // The paired-list family: AssignedPairsVariablesList is a VariablesList preset (2 columns, // drop-replace) whose model PAIRS its columns into 2-component terms (bound value - // [["Score","Age"]]). Own selection host, so the pair's components stamp together under that - // host's filter (C9 uniform-carry); the R side echoes each pair and computes per pair. + // [["Score","Age"]]). Served by the pool hosted on dataSetJ's filter, so the pair's + // components stamp together under that pool's filter (C9 uniform-carry); the R side echoes + // each pair and computes per pair. Section { - title: qsTr("Zoo J - paired variables list (own selection)") + title: qsTr("Zoo J - paired variables list (pool on dataSetJ)") - AssignedPairsVariablesList + VariablesForm { - name: "pairsJ" - title: qsTr("Pairs (own Data selection)") - dataSetSelectionOption: "dataSetJ" + AvailableVariablesList + { + name: "zooPoolJ" + dataSetSelectionOption: "dataSetJ" + } + AssignedPairsVariablesList + { + name: "pairsJ" + title: qsTr("Pairs (pool on dataSetJ)") + depends: "dataSetJ" + } } } @@ -330,11 +376,19 @@ Form Section { title: qsTr("Zoo K - factors form (FactorsList panels over an assigned source)") - AssignedVariablesList + VariablesForm { - name: "assignedK" - title: qsTr("Assigned K (factor source, own Data selection)") - dataSetSelectionOption: "dataSetK" + AvailableVariablesList + { + name: "zooPoolK" + dataSetSelectionOption: "dataSetK" + } + AssignedVariablesList + { + name: "assignedK" + title: qsTr("Assigned K (factor source)") + depends: "dataSetK" + } } FactorsForm { @@ -373,6 +427,90 @@ Form } } + // ---- Zoo M: three pools over one dataset (hostless / f1 / f3) -------------------------------- + // The mds-plan post zoo correction. The EXTRA SOURCE is zooPoolThree: an AvailableVariablesList + // whose host selection is the THIRD filter of the same test dataset. A fresh RPC workspace has + // only the two default filters (data_load mints one per dataset; the RPC has no filter-creation + // method), so the form bootstraps the third filter itself, once, guarded: only when the + // workspace is exactly the zoo fixture shape (a dataset with id 1 that has no "Filter 0" yet). + // filterModel/workspace are root-context properties and module QML loads under the root + // context, so both are visible here. filterModel.addFilter(1) is the same invokable the filter + // editor uses (DataSet::addFilter -> named "Filter 0", default all-rows rFilter, its own + // data-table column and its own encoder namespace JASPColumn_D1_F3_). + // + // The three pools and their participation keys: + // zooPoolHostless no dataSetSelectionOption -> key -1 (default provider) + // zooVarsA (Zoo A) dataSetA -> key = whatever dataSetA holds (f1 -> f3 anchor) + // zooPoolThree dataSetM3 -> key 3 (the f3-origin namespace) + Component.onCompleted: { + // Zoo fixture bootstrap (test-only): ensure the third filter exists on the existing test + // dataset. No-op in any other workspace shape; no-op once it exists (verifier re-runs). + // NB: the 'workspace' ROOT CONTEXT PROPERTY is only set from onWorkspaceChanged() and was + // observed UNDEFINED at form load in an RPC session - route through dataSetPackage's + // workspace Q_PROPERTY instead, which loadQML() always installs. + var ws = (typeof dataSetPackage !== "undefined" && dataSetPackage) ? dataSetPackage.workspace : null + if (typeof filterModel !== "undefined" && ws && ws.filterIdByName(1, "Filter 0") < 0) + console.log("ZOO BOOTSTRAP: creating the third filter on dataset 1 -> id " + + (function(){ filterModel.addFilter(1); return ws.filterIdByName(1, "Filter 0"); })()) + } + + Section { + title: qsTr("Zoo M - the extra source: pool on the third filter (+ the hostless pool)") + + VariablesForm + { + // the default-provider pool: NO host selection, participation key -1 + AvailableVariablesList + { + name: "zooPoolHostless" + } + AssignedVariablesList + { + name: "assignedDefault" + title: qsTr("Assigned (default-provider pool)") + } + } + VariablesForm + { + // THE EXTRA SOURCE: hosted by the third filter of the same test dataset; its universe + // terms carry the f3 stamp (JASPColumn_D1_F3_ namespace) + AvailableVariablesList + { + name: "zooPoolThree" + dataSetSelectionOption: "dataSetM3" + } + AssignedVariablesList + { + name: "assignedThree" + title: qsTr("Assigned (third-filter pool)") + } + } + } + + // ---- Zoo N: derived pool (mixed sources) ------------------------------------------------------ + // An AvailableVariablesList whose source is TWO assigned lists: its listed terms are + // references carrying each source term's OWN stamp (mixed provenance). Known limitation + // (documented in the mds-plan post): mixed-source refs do not follow a source-list host + // switch this pass - that is exactly why the reset signal carries the participation key + // instead of a `to` scalar. + Section { + title: qsTr("Zoo N - derived pool (source: assignedA + assignedDefault)") + + VariablesForm + { + AvailableVariablesList + { + name: "zooPoolDerived" + source: ["assignedA", "assignedDefault"] + } + AssignedVariablesList + { + name: "assignedDerived" + title: qsTr("Assigned (derived pool)") + } + } + } + // ---- scalars (M5 contract: never provenanced, never term-objectified) ------------------------ Section { title: qsTr("Scalars") From 54ce10385b05e8812ca6f0b82b3ecce3333df3c2 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch <virtuoos@jorisgoosen.nl> Date: Thu, 22 Oct 2026 03:03:39 +0200 Subject: [PATCH 40/44] redact mds-plan references from comments (zoo R/qml headers, verifier docs) --- R/multiDataSet.R | 12 ++++++------ R/multiDataSetControls.R | 10 +++++----- R/multiDataSetControlsFuncWrapper.R | 2 +- R/multiDataSetFuncWrapper.R | 2 +- Tools/testMultiDataSet.R | 6 +++--- Tools/verifyMultiDataSetRpc.py | 26 +++++++++++++------------- inst/Description.qml | 4 ++-- inst/qml/testMultiDataSet.qml | 2 +- inst/qml/testMultiDataSetControls.qml | 16 ++++++++-------- 9 files changed, 40 insertions(+), 40 deletions(-) diff --git a/R/multiDataSet.R b/R/multiDataSet.R index 939af30..e69ca46 100644 --- a/R/multiDataSet.R +++ b/R/multiDataSet.R @@ -13,15 +13,15 @@ # both available. Result strings are decoded back to the original names on the way to the GUI # (Engine::sendString and the rbridge callbacks, all consumers of the one global strict decode # table), so putting encoded names in tables/plots is not just allowed, it is how provenance -# survives: THE PROVENANCE IS IN THE NAME (mds-plan C1/C8), never in a side channel. While such +# survives: THE PROVENANCE IS IN THE NAME, never in a side channel. While such # a run is active, readDataSet* stops: one 'current dataset' is meaningless here. # -# Options contract (mds-plan C8/C11, the Phase-3 flip): `options$.meta` is GENERATED at send +# Options contract (the Phase-3 flip): `options$.meta` is GENERATED at send # time and carries no dataSetId/filterId; the transient `.filterIds` sidecar is stripped at the # engine boundary before R sees anything. metaDump() below echoes both facts as the # PROVENANCE_AUDIT marker the RPC verifier asserts on every run. # -# Qualified column references (mds-plan plan §5, cards D8.1/D8.2/D8.4): a TERM option may +# Qualified column references: a TERM option may # arrive with qualified surface forms ("Dataset"."Filter"."Column"("Type"), minimal per # context); the C++ appliers parse them into typed terms with filterIds before the run, so R # receives them as ordinary encoded names - no R-side grammar (C14). A STRING option (plain @@ -32,7 +32,7 @@ # # The two aware analyses here: multiDataSetFuncInternal (form testMultiDataSet.qml - two # per-list dataset selection options dataSetA/dataSetB, the D8.3 wrapper fixture) and -# multiDataSetControlsInternal (the component zoo testMultiDataSetControls.qml, mds-plan +# multiDataSetControlsInternal (the component zoo testMultiDataSetControls.qml, # D6.1/D6.2: assigned lists, row controls with same-name twins, interactions, factor levels # via encodeThis, a textarea host, a cross-host typed bindTo copy - each reports what it # received and where it resolved). @@ -141,7 +141,7 @@ selectedVariablesTable <- function(jaspResults, options, datasets, dataset) { metaDump <- function(jaspResults, options) { meta <- options[[".meta"]] metaText <- jsonlite::toJSON(if (is.null(meta)) list() else meta, auto_unbox = TRUE, pretty = 2) - # mds-plan D3.3: R must never see provenance - the transient `.filterIds` sidecar is stripped + # R must never see provenance - the transient `.filterIds` sidecar is stripped # at the engine boundary (C11) and the send-time `.meta` carries no dataSetId/filterId (C8). # The audit markers echo exactly what arrived so the RPC verifier can assert the contract. provenanceKeys <- sum(lengths(regmatches(metaText, gregexpr("dataSetId|filterId", metaText)))) @@ -165,7 +165,7 @@ readDataSetGuardDemo <- function(jaspResults) { } # The non-aware counterpart (multiDataSetNonAwareInternal + Description entry) was removed by -# mds-plan C4/M8: every other analysis here is already a plain single-dataset one, and the +# every other analysis here is already a plain single-dataset one, and the # twin's generated wrapper had a stale qml pointer so its R entry point always errored. The # "Non-aware run." branch above stays: it renders whenever `datasets` never arrives (old # jaspBase, direct Internal calls). diff --git a/R/multiDataSetControls.R b/R/multiDataSetControls.R index 7a40e36..0dcaea1 100644 --- a/R/multiDataSetControls.R +++ b/R/multiDataSetControls.R @@ -1,4 +1,4 @@ -# mds-plan D6.1 - the component-zoo echo. Receives the generated options + the per-slice +# the component-zoo echo. Receives the generated options + the per-slice # `datasets` (contract: keyed by SLICE/filter id, attrs dataSetIds/dataSetNames map a key back # to its dataset - see multiDataSet.R for the full contract notes) and reports, per control # family, EVERY value exactly as received: the encoded name (results are decoded on the way out, @@ -40,14 +40,14 @@ # resolution + correlation # factors-form Zoo K - FactorsForm (the FactorsList family): per factor name/title + # terms. The form's inner panels are not hosts, but the per-factor -# sidecar (FactorsFormBase::createFilterIds, mds-plan post) now +# sidecar (FactorsFormBase::createFilterIds) now # transports each term's OWN filterId per factor and per term: a # factor term carries the stamp of the host its source list synced # it from (here assignedK -> filter 1) and encodes through THAT # filter's view - resolved slice by slice, no analysis-level # fallback (the D3.2 hostless-panel deferral is CLOSED; pinned # here, not faked). -# REGRESSION PINNED C++-SIDE (mds-plan post): a factor term whose +# REGRESSION PINNED C++-SIDE: a factor term whose # column the resolved namespace lacks (e.g. a NOMINAL Group term # while the shown dataset is DataSet 2) used to FATAL the whole # run - rbridge_readDataSetRequested refused "Group.nominal" @@ -65,7 +65,7 @@ # USED in the cross-slice computation, not just echoed # scalar M5 contract - plain values, no provenance anywhere # -# RESULTS INVENTORY (the point of the zoo: data and flows must be VISIBLE, mds-plan "show the +# RESULTS INVENTORY (the point of the zoo: data and flows must be VISIBLE, "show the # loaded data"): # dataSetOverview one row per slice (unchanged) # dataPreview_<key> THE LOADED DATA: per slice, every column's name AND first `rowsH` VALUES @@ -599,7 +599,7 @@ crossSliceTable <- function(jaspResults, options, datasets) { if (!is.null(colA) && !is.null(colB)) { value <- if (length(colA$values) != length(colB$values)) paste0("row lengths differ (", length(colA$values), " vs ", length(colB$values), - "): no join semantics (mds-plan D7 O3) - whole-column aggregates only") + "): no join semantics - whole-column aggregates only") else sprintf("mean of product %.3f", mean(colA$values * colB$values, na.rm = TRUE)) rows <- addRow2("cross-host column product", paste0(colA$column, " of ", label(colA$key), " x ", colB$column, " of ", label(colB$key)), diff --git a/R/multiDataSetControlsFuncWrapper.R b/R/multiDataSetControlsFuncWrapper.R index 39bb206..cbb3818 100644 --- a/R/multiDataSetControlsFuncWrapper.R +++ b/R/multiDataSetControlsFuncWrapper.R @@ -73,7 +73,7 @@ multiDataSetControlsFunc <- function( options[["version"]] <- NULL - # mds-plan D8.3: `datasets` must be a named list of data.frames keyed by DATASET TITLE - the + # `datasets` must be a named list of data.frames keyed by DATASET TITLE - the # titles the dataset selection options of this form carry (e.g. list(Iris = iris, Sleep = sleep)). # No positional pin: the dataset that plays `data` (the frame the formula machinery evaluates # against) is the first selection option whose effective value names one of the loaded datasets, diff --git a/R/multiDataSetFuncWrapper.R b/R/multiDataSetFuncWrapper.R index 85941ed..dfbcfa6 100644 --- a/R/multiDataSetFuncWrapper.R +++ b/R/multiDataSetFuncWrapper.R @@ -47,7 +47,7 @@ multiDataSetFunc <- function( options[["version"]] <- NULL - # mds-plan D8.3: `datasets` must be a named list of data.frames keyed by DATASET TITLE - the + # `datasets` must be a named list of data.frames keyed by DATASET TITLE - the # titles the dataset selection options of this form carry (e.g. list(Iris = iris, Sleep = sleep)). # No positional pin: the dataset that plays `data` (the frame the formula machinery evaluates # against) is the first selection option whose effective value names one of the loaded datasets, diff --git a/Tools/testMultiDataSet.R b/Tools/testMultiDataSet.R index d8ec831..99faf19 100644 --- a/Tools/testMultiDataSet.R +++ b/Tools/testMultiDataSet.R @@ -127,7 +127,7 @@ check(grepl("not available in multi-dataset aware analyses", raw1, fixed = TRUE) # ------------------------------------------------------------------ Bridge surface (C4) ---- cat("\n== Bridge boundary: loadDataSets returns ids (informational); selections are TITLES ==\n") -# mds-plan C4: the syntax surface of the dataset selection options is human strings ONLY - +# the syntax surface of the dataset selection options is human strings ONLY - # the bare TITLE for a dataset's default filter, "title - filterName" for a saved named one. # The integer ids the loaders hand back stay useful for logging and for slicing results by, # but writing one into a selection option is now a hard error (M4: the old numeric-id @@ -158,7 +158,7 @@ check(identical(as.character(msj$dataSetIds[[keyA]]), as.character(ids[[1]])), errId <- tryCatch({ parseSelection(as.integer(ids[[1]]), as.integer(ids[[2]])); "" }, error = function(e) conditionMessage(e)) check(grepl("matches no dataset selection string", errId), - "integer dataset ids are rejected on the syntax surface (mds-plan C4/M4: no numeric-id acceptance anymore)") + "integer dataset ids are rejected on the syntax surface (no numeric-id acceptance anymore)") errBadTitle <- tryCatch({ parseSelection("NoSuchDataSet", "DataSetB"); "" }, error = function(e) conditionMessage(e)) check(grepl("matches no dataset selection string", errBadTitle) && grepl("available:", errBadTitle), "an unknown title is rejected loudly and the error lists what IS selectable") @@ -193,7 +193,7 @@ check(any(grepl("MULTIDATASET-WRAPPER-OK", status2, fixed = TRUE)), "wrapper sub cat("\n== Path 3: legacy non-aware run (single dataset, plain names) ==\n") # The aware analysis ALWAYS gets the bridge slice queue now (engine semantics: multiDataSetJson # is produced whenever the Description says aware), so the legacy dataset/readDataSet path runs -# through a plain non-aware analysis instead of the removed twin (mds-plan C4/M8): +# through a plain non-aware analysis instead of the removed twin: # testEncodingFunc has no preloadData, so its R body really calls .readDataSetToEnd() and # echoes the column values - a live witness that the readDataSet guard stays silent outside # aware runs and that data survives the bridge encode->decode round trip. diff --git a/Tools/verifyMultiDataSetRpc.py b/Tools/verifyMultiDataSetRpc.py index a26754d..81075f9 100644 --- a/Tools/verifyMultiDataSetRpc.py +++ b/Tools/verifyMultiDataSetRpc.py @@ -16,7 +16,7 @@ the engine-boundary strip (C11); per-form routing is proven at the VALUE level (each form's variable resolves against its own selection's slice: 12.50 vs 17.73) - no raw JASPColumn_* anywhere (decode on send) -And the C3/VERIFY-live block (mds-plan C3/M5): switch one host's selection mid-session and +And the live-verification block: switch one host's selection mid-session and assert the next run only shows the LIVE selections - no phantom slice for the abandoned filter, no provenance stamps on scalars or on the selection options themselves, column values restamped onto the new filter (there and back). @@ -66,7 +66,7 @@ def wait_up(max_wait=120): import sys, json, time, os, re as _re -# Locations (mds-plan C4/S9): nothing machine-specific. The module dir is resolved as +# Locations: nothing machine-specific. The module dir is resolved as # --module=<dir> (argv) > $JASP_TESTMODULE_DIR > the directory containing this script # (it lives in <module>/Tools/, so the default is self-locating). def _findModuleDir(): @@ -216,7 +216,7 @@ def row(src, var): return next((x for x in rows if x["source"] == src and x["var check("analysis references both datasets (dataSetIds in run response)", sorted(r2.get("dataSetIds", [])) == [1, 2], str(r2.get("dataSetIds"))) -# T3c (mds-plan D3.3 flip): provenance as R sees it - NONE. The generated Engine json keeps +# Provenance as R sees it - NONE. The generated Engine json keeps # .meta provenance-free (C8) and the transient .filterIds sidecar is stripped at the engine # boundary (C11); the metaDump html (options$showMeta was on) audits exactly that. Per-form # provenance still routes the DATA (two same-named columns, two truths) - pinned value-level @@ -234,7 +234,7 @@ def provenance_audit(res): check("options$.meta as R received it carries no provenance keys (C8)", audit2[1] == "0", str(audit2)) # ---------------------------------------------------------------------------- -# C3/VERIFY-live (mds-plan C3/M5): the phantom-slice class. Switch ONE HOST'S SELECTION +# The phantom-slice class. Switch ONE HOST'S SELECTION # mid-session and check the NEXT run's data shows only the LIVE selections: no slice queued # for an abandoned filter, and the run's provenance still C8/C11-clean (the per-run audit). # Since the D3.3 flip the restamp moves the TYPED term filterIds (HostFilterSelection @@ -273,7 +273,7 @@ def overview_ids(res): check("run 3: dataSetIds not over-reported", sorted(r3.get("dataSetIds", [])) == [2], str(r3.get("dataSetIds"))) audit3 = provenance_audit(res3) check("run 3: still no provenance reaches R after the switch (C8/C11)", audit3 == ("false", "0"), str(audit3)) -# (old stamp-window checks retired with the meta stamps, mds-plan D3.3: the C8 audit above +# (old stamp-window checks retired with the meta stamps, the C8 audit above # covers scalars and selection options too - NO dataSetId/filterId key may appear ANYWHERE) sv3 = next((t for t in tables(res3.get("results", res3)).values() if t.get("title") == "Selected variables"), None) @@ -306,13 +306,13 @@ def overview_ids(res): check("run 4: A:Score back on DataSet 1 (mean 12.50)", a4 and str(a4["dataSetId"]) == "1" and "12.50" in str(a4["values"]), json.dumps(a4)) # ---------------------------------------------------------------------------- -# mds-plan D6.3 - the component zoo (jaspTestModule analysis multiDataSetControlsFunc / +# the component zoo (jaspTestModule analysis multiDataSetControlsFunc / # testMultiDataSetControls.qml). Every control family echoes what it RECEIVED (encoded names, # decoded again on the way out), which slice index each value resolved in, and its row children, # so per-item provenance is asserted at the value level. The RPC workspace starts with one # default filter per dataset (ids 1 and 2) - exactly two distinct namespaces for the twin cases. # The zoo form BOOTSTRAPS a third filter on dataset 1 ("Filter 0", all-rows) at form load, because -# the RPC has no filter-creation method and the mds-plan post zoo correction needs a THIRD pool +# the RPC has no filter-creation method and the zoo correction needs a THIRD pool # hosted by it (the f3-origin namespace for the anchor scenario); its id is discovered below. # Checks per family: # - same-name columns across two filters in the SAME list (twins) -> distinct encodings, @@ -372,7 +372,7 @@ def zoo_full_opts(host_a="1", host_m3="", host_d="", host_f="", host_g="", combo_a="", combo_i="", text_h="", rows_h=5, double_h=1.5, slider_h=0.5, radio_h="mean", gate_h=False, gated_h=10.0, table_l=None, plain_switch=False, show_meta=True): - # NB: dataSetB/dataSetC are GONE (mds-plan post zoo correction): Zoo B is REMOVED entirely (a + # NB: dataSetB/dataSetC are GONE (zoo correction): Zoo B is REMOVED entirely (a # bare assigned list - no source at all - is a malformed configuration, owner rule) and Zoo C # is a normal form over a default-provider pool now - neither has a host option to send. # dataSetM3 is Zoo M's extra-source pool (the THIRD filter of dataset 1, bootstrapped by the @@ -420,7 +420,7 @@ def zoo_full_opts(host_a="1", host_m3="", host_d="", host_f="", host_g="", check("zoo run 1: slice 1 queued (host A), nothing beyond the workspace's two slices", "1" in zslices1 and set(zslices1) <= {"1", "2"}, str(zslices1)) -# --- the zoo fixture's THIRD filter (mds-plan post zoo correction) -------------------------------- +# --- the zoo fixture's THIRD filter (zoo correction) -------------------------------- # The zoo form bootstraps "Filter 0" on dataset 1 at form load (zoo run 1 forced the form to # load, so the bootstrap has run by now). Discover its id via data_info - the verifier must not # hardcode 3 (a re-run in a workspace that already had extra filters would shift it). @@ -702,7 +702,7 @@ def no_raw_tokens(res, tag): # assignedK Group+Age via host K(1) -> slice 1 (Group feeds the encodeThis demo, Age is the # factor term's source) | pairsJ Score+Age via host J(1) -> slice 1 # interactionD Score:Age via host D(1) -> slice 1 | factorsK Age -> the per-factor sidecar -# (FactorsFormBase::createFilterIds, mds-plan post) transports the term's OWN filterId per +# (FactorsFormBase::createFilterIds) transports the term's OWN filterId per # factor and per term -> slice 1 (the term's stamp comes from its source host assignedK; the # D3.2 hostless-panel deferral - analysis-level namespace, slice 2 - is CLOSED, pinned here) zr9, zres9 = zoo_run(zoo_full_opts( @@ -1018,12 +1018,12 @@ def _gated_map(g): "scaled mean 76.000" in str(cs17.get("tableview multipliers", {}).get("value")), json.dumps(cs17)) no_raw_tokens(zres17, "zoo run 17") -# --- zoo run 18 (REGRESSION, mds-plan post): nominal factor term in a namespace lacking the column -- +# --- zoo run 18 (REGRESSION): nominal factor term in a namespace lacking the column -- # Pre-fix this FATALed the whole run (rbridge_readDataSetRequested: "gets 'Group.nominal' but its # not a columnname at all"): the FactorsForm panel is hostless, its term stamped at the ANALYSIS # level (DataSet 2, which has no Group), and the collector queued a column that dataset does not # have. The collector now refuses to queue nonexistent columns. Since the per-factor sidecar -# (FactorsFormBase::createFilterIds, mds-plan post) transports the term's OWN filterId, the term +# (FactorsFormBase::createFilterIds) transports the term's OWN filterId, the term # no longer falls back to the analysis-level namespace at all: it encodes through its source # host's view (filter 1, DataSet 1, which HAS the nominal Group) and resolves in slice 1 - the # phantom-namespace route is gone. The run completes with a RESOLVED row and a clean audit; the @@ -1047,7 +1047,7 @@ def _gated_map(g): no_raw_tokens(zres18, "zoo run 18") # ---------------------------------------------------------------------------- -# mds-plan post zoo correction - the three pools, the ds1/f1 -> ds1/f3 ANCHOR and the +# zoo correction - the three pools, the ds1/f1 -> ds1/f3 ANCHOR and the # derived pool. Zoo M holds three pools over the same test dataset: the hostless pool # (no host selection, participation key -1), Zoo A's pool on the existing selection # (dataSetA) and the EXTRA SOURCE zooPoolThree on the bootstrapped third filter diff --git a/inst/Description.qml b/inst/Description.qml index b411f52..5cf12ff 100644 --- a/inst/Description.qml +++ b/inst/Description.qml @@ -95,7 +95,7 @@ Description hasWrapper: true } - // mds-plan D6.1/D6.2: the component zoo under multi-source provenance. Every control family + // the component zoo under multi-source provenance. Every control family // (assigned list, list-as-host, row-control ComponentsList with same-name twins across filters, // interaction terms, factor levels via encodeThis, textarea with own host, cross-host typed // bindTo copy) reports what it received and where it resolved; the RPC verifier asserts it. @@ -111,7 +111,7 @@ Description } // The old non-aware twin entry (func multiDataSetNonAware / qml testMultiDataSetNonAware) - // is gone (mds-plan C4/M8): it existed only to show that selection options stay inert + // is gone: it existed only to show that selection options stay inert // without multiDataSetAware, but its generated wrapper pointed at the AWARE form and so its // R entry point always errored (REPORT M8). The non-aware code path is covered by the other // (plain, single-dataset) analyses in this module. diff --git a/inst/qml/testMultiDataSet.qml b/inst/qml/testMultiDataSet.qml index 2c5aa0a..a73f22a 100644 --- a/inst/qml/testMultiDataSet.qml +++ b/inst/qml/testMultiDataSet.qml @@ -35,7 +35,7 @@ Form AvailableVariablesList { name: "allVariablesA" - //List-level dataset selection (mds-plan C10): an option carrying the dataset name + //List-level dataset selection: an option carrying the dataset name //to select for this list (jaspSyntax gates the bound dropdown on it; desktop never //sees it). The assigned lists below inherit this list's namespace. dataSetSelectionOption: "dataSetA" diff --git a/inst/qml/testMultiDataSetControls.qml b/inst/qml/testMultiDataSetControls.qml index f48b7dd..15f361f 100644 --- a/inst/qml/testMultiDataSetControls.qml +++ b/inst/qml/testMultiDataSetControls.qml @@ -1,11 +1,11 @@ -// mds-plan D6.1/D6.2 - the component zoo under multi-source provenance. +// the component zoo under multi-source provenance. // // Every control family a real analysis form uses, each exercising per-item provenance: // Zoo A plain lists: an AvailableVariablesList carrying its own dataset/filter selection // (the D2.2 list-level pattern) feeding an AssignedVariablesList, plus a DropDown // served by the same host (comboboxes stay PLAIN strings - never term objects - // while their value still resolves through the host namespace). -// Zoo B REMOVED (mds-plan post zoo correction, owner): a bare assigned list - an assigned +// Zoo B REMOVED (zoo correction, owner): a bare assigned list - an assigned // list with no source at all - is a malformed configuration, not a supported one: // every real assigned list sits in a form with a pool. Its two real pins moved: the // -1 analysis-fallback resolution is pinned on the default-provider pool's assigned @@ -22,7 +22,7 @@ // key is -1, so the stamped twins are never swept by a universe reset: each twin // follows only its own stamp. // Zoo D interaction terms: a VariablesList in Interaction mode - components of one -// combination term share the term-level filterId (mds-plan C9 uniform-carry rule). +// combination term share the term-level filterId (uniform-carry rule). // Zoo E FactorLevelList: encodeThis -> extras-encoder roundtrip (factor/level names encode // through the JaspExtraOptions_ namespace and must come back decoded and usable). // Zoo F TextArea with its OWN host: R-code text whose used columns encode through the @@ -33,7 +33,7 @@ // UNQUALIFIED terms (D5.1 intake guard) and the restamp walk never touches foreign // stamps (D3.5 owns-the-stamp), so the value must encode and resolve through the // SOURCE's slice even though this list's own dropdown selects another one. -// Zoo M THREE pools over the same test dataset (the mds-plan post zoo correction): a +// Zoo M THREE pools over the same test dataset (the zoo correction): a // hostless pool (no dataSetSelectionOption at all - the default-provider pool whose // participation key is -1), Zoo A's pool hosted by the existing filter selection // (dataSetA, filter 1), and the EXTRA SOURCE: a new AvailableVariablesList hosted by @@ -362,7 +362,7 @@ Form // FactorsList is one panel of a FactorsForm; the serialized shape is the form's: per factor // {name, title, indicators}. Findings (pinned by the verifier): // 1. The form's inner panels are NOT hosts, but the per-factor sidecar - // (FactorsFormBase::createFilterIds, mds-plan post) transports each term's OWN filterId + // (FactorsFormBase::createFilterIds) transports each term's OWN filterId // per factor and per term: a term keeps the stamp its source list synced onto it // (assignedK -> filter 1) and encodes through THAT filter's view - resolved slice by // slice, no analysis-level fallback (the D3.2 hostless-panel deferral is CLOSED). @@ -370,7 +370,7 @@ Form // silently dropped by the form's model sync, and a term the form keeps is RE-TYPED by the // form (Age arrived ordinal here although sent scale, so the engine slice receives it as an // R factor). A term whose column the resolved namespace lacks used to FATAL the run - the - // mds-plan post existence guard in Common/columnencoder.cpp refuses to queue columns a + // existence guard in Common/columnencoder.cpp refuses to queue columns a // namespace does not have; with the per-factor sidecar the term resolves in its OWN // namespace instead (verifier run 18 pins the resolved row + clean audit). Section { @@ -428,7 +428,7 @@ Form } // ---- Zoo M: three pools over one dataset (hostless / f1 / f3) -------------------------------- - // The mds-plan post zoo correction. The EXTRA SOURCE is zooPoolThree: an AvailableVariablesList + // The zoo correction. The EXTRA SOURCE is zooPoolThree: an AvailableVariablesList // whose host selection is the THIRD filter of the same test dataset. A fresh RPC workspace has // only the two default filters (data_load mints one per dataset; the RPC has no filter-creation // method), so the form bootstraps the third filter itself, once, guarded: only when the @@ -490,7 +490,7 @@ Form // ---- Zoo N: derived pool (mixed sources) ------------------------------------------------------ // An AvailableVariablesList whose source is TWO assigned lists: its listed terms are // references carrying each source term's OWN stamp (mixed provenance). Known limitation - // (documented in the mds-plan post): mixed-source refs do not follow a source-list host + // (documented in the): mixed-source refs do not follow a source-list host // switch this pass - that is exactly why the reset signal carries the participation key // instead of a `to` scalar. Section { From 5ad29791f79b57fd3a30eef8c5939cd93b259860 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch <virtuoos@jorisgoosen.nl> Date: Thu, 22 Oct 2026 12:34:23 +0200 Subject: [PATCH 41/44] Replace internal card-ID cross-references with self-contained prose Same cleanup as the desktop side: the planning document's card IDs in comments, verifier labels and R/qml headers are rewritten as standing prose carrying the same information. Labels and comments only; no code paths change. --- R/multiDataSet.R | 10 ++--- R/multiDataSetControls.R | 20 ++++----- Tools/testMultiDataSet.R | 4 +- Tools/verifyMultiDataSetRpc.py | 60 +++++++++++++-------------- inst/Description.qml | 2 +- inst/qml/testMultiDataSetControls.qml | 30 +++++++------- 6 files changed, 63 insertions(+), 63 deletions(-) diff --git a/R/multiDataSet.R b/R/multiDataSet.R index e69ca46..3da3dd1 100644 --- a/R/multiDataSet.R +++ b/R/multiDataSet.R @@ -24,16 +24,16 @@ # Qualified column references: a TERM option may # arrive with qualified surface forms ("Dataset"."Filter"."Column"("Type"), minimal per # context); the C++ appliers parse them into typed terms with filterIds before the run, so R -# receives them as ordinary encoded names - no R-side grammar (C14). A STRING option (plain -# text input) receives the qualified form verbatim - the D8.4 v1 stance: it passes through and +# receives them as ordinary encoded names - no R-side grammar. A STRING option (plain +# text input) receives the qualified form verbatim - the v1 stance: it passes through and # modules slice on it or not, at their choice; module code that needs to interpret one uses # the echo pattern of multiDataSetControlsInternal (report what was received, then resolve on # demand) and never re-implements the token grammar. # # The two aware analyses here: multiDataSetFuncInternal (form testMultiDataSet.qml - two -# per-list dataset selection options dataSetA/dataSetB, the D8.3 wrapper fixture) and +# per-list dataset selection options dataSetA/dataSetB, the wrapper fixture) and # multiDataSetControlsInternal (the component zoo testMultiDataSetControls.qml, -# D6.1/D6.2: assigned lists, row controls with same-name twins, interactions, factor levels +# assigned lists, row controls with same-name twins, interactions, factor levels # via encodeThis, a textarea host, a cross-host typed bindTo copy - each reports what it # received and where it resolved). @@ -142,7 +142,7 @@ metaDump <- function(jaspResults, options) { meta <- options[[".meta"]] metaText <- jsonlite::toJSON(if (is.null(meta)) list() else meta, auto_unbox = TRUE, pretty = 2) # R must never see provenance - the transient `.filterIds` sidecar is stripped - # at the engine boundary (C11) and the send-time `.meta` carries no dataSetId/filterId (C8). + # at the engine boundary and the send-time `.meta` carries no dataSetId/filterId. # The audit markers echo exactly what arrived so the RPC verifier can assert the contract. provenanceKeys <- sum(lengths(regmatches(metaText, gregexpr("dataSetId|filterId", metaText)))) filterIdsSeen <- !is.null(options[[".filterIds"]]) diff --git a/R/multiDataSetControls.R b/R/multiDataSetControls.R index 0dcaea1..b022190 100644 --- a/R/multiDataSetControls.R +++ b/R/multiDataSetControls.R @@ -14,12 +14,12 @@ # default-provider pool (every assigned list has >= 1 source - # owner rule; the bare standalone list died with Zoo B): # per-row term + keep + shift; same-name twin rows from two -# filters keep their OWN children (the D6.1 TermRowKey +# filters keep their OWN children (the TermRowKey # re-keying); the pool's key is -1, so the stamped twins are # never swept by any reset # pool-hostless Zoo M - the default-provider pool's companion (no host selection, # participation key -1): sent-BARE terms resolve through the -# analysis-level fallback (the C13 use-time resolution), +# analysis-level fallback (the use-time resolution), # sent-stamped terms keep their own stamp # pool-f3 Zoo M - the EXTRA SOURCE's companion: the pool hosted by the THIRD # filter of the same test dataset (f3-origin terms carry its @@ -31,7 +31,7 @@ # they must come back decoded, no raw JaspExtraOptions_ leakage) # textarea-host Zoo F - R-code textarea: used columns encoded through its own host # bindto-cross-host Zoo G - typed copy bound across differently-selected hosts: keeps the -# SOURCE filterId, encodes through the SOURCE's view (D6.2) +# SOURCE filterId, encodes through the SOURCE's view # dropdown-dependent Zoo I - dependent dropdown: option LIST driven by assignedA's value; its # value encodes through the SOURCE's host namespace (a dropdown # with a source inherits the source's selection host) @@ -45,7 +45,7 @@ # factor term carries the stamp of the host its source list synced # it from (here assignedK -> filter 1) and encodes through THAT # filter's view - resolved slice by slice, no analysis-level -# fallback (the D3.2 hostless-panel deferral is CLOSED; pinned +# fallback (the hostless-panel deferral is CLOSED; pinned # here, not faked). # REGRESSION PINNED C++-SIDE: a factor term whose # column the resolved namespace lacks (e.g. a NOMINAL Group term @@ -63,7 +63,7 @@ # tableview Zoo L - bound TableView (row count driven by Zoo H's integer); cells # are plain values (no provenance - not a Terms option) and are # USED in the cross-slice computation, not just echoed -# scalar M5 contract - plain values, no provenance anywhere +# scalar plain values, no provenance anywhere # # RESULTS INVENTORY (the point of the zoo: data and flows must be VISIBLE, "show the # loaded data"): @@ -121,7 +121,7 @@ multiDataSetControlsFuncInternal <- function(jaspResults, dataset, options, data # ------------------------------------------------------------------ resolution helpers .resolveSlice <- function(token, datasets) { - # an aware option value IS the encoded column name of exactly one slice (C1/C3): index it + # an aware option value IS the encoded column name of exactly one slice: index it if (is.null(datasets) || !is.character(token) || length(token) != 1 || !nzchar(token)) return(NULL) hits <- names(datasets)[vapply(names(datasets), @@ -142,7 +142,7 @@ multiDataSetControlsFuncInternal <- function(jaspResults, dataset, options, data .fillDescription(list(n = NA_integer_, values = ""), datasets[[key]][[token]]) } -# scalar option readers: scalars are never provenanced (M5), so they arrive as plain values +# scalar option readers: scalars are never provenanced, so they arrive as plain values .intOption <- function(x, default = NA_integer_) { if (is.null(x) || length(x) != 1 || is.na(x)) return(default) v <- suppressWarnings(as.integer(x)) @@ -424,9 +424,9 @@ controlsProvenanceTable <- function(jaspResults, options, datasets) { } } - # --- scalars (M5): plain values, no provenance anywhere ------------------------------------------ + # --- scalars: plain values, no provenance anywhere ------------------------------------------ rows <- addRow(rows, "scalar", "plainSwitch", as.character(isTRUE(options$plainSwitch)), - rowChild = "no provenance (C11 scalars unstamped)") + rowChild = "no provenance (scalars unstamped)") rows <- addRow(rows, "combobox-plain", "comboG", as.character(options$comboG), rowChild = "plain string (combos never term-objectified)") @@ -667,7 +667,7 @@ encodeDemoTable <- function(jaspResults, options, datasets) { fname <- .decodeNameSafe(as.character(f$name)) levels <- as.character(unlist(f$levels %||% list())) levels <- if (length(levels)) vapply(levels, .decodeNameSafe, character(1)) else levels - # The slice data.frames carry ENCODED column tokens (that is what R receives, C1) - the + # The slice data.frames carry ENCODED column tokens (that is what R receives) - the # decoded factor name must be matched against the DECODED column names, not the raw # colnames (the verifier caught the first version matching "Group" against tokens). hitKey <- NULL; hitTok <- NULL diff --git a/Tools/testMultiDataSet.R b/Tools/testMultiDataSet.R index 99faf19..6a671ae 100644 --- a/Tools/testMultiDataSet.R +++ b/Tools/testMultiDataSet.R @@ -125,12 +125,12 @@ check(!grepl("JASPColumn_", raw1), "no encoded tokens anywhere in the returned r check(grepl("not available in multi-dataset aware analyses", raw1, fixed = TRUE), "guard demo: readDataSet() hard-stopped during the aware run") -# ------------------------------------------------------------------ Bridge surface (C4) ---- +# ------------------------------------------------------------------ Bridge surface ---- cat("\n== Bridge boundary: loadDataSets returns ids (informational); selections are TITLES ==\n") # the syntax surface of the dataset selection options is human strings ONLY - # the bare TITLE for a dataset's default filter, "title - filterName" for a saved named one. # The integer ids the loaders hand back stay useful for logging and for slicing results by, -# but writing one into a selection option is now a hard error (M4: the old numeric-id +# but writing one into a selection option is now a hard error (the old numeric-id # acceptance let a title shadow an id and vice versa - gone). ids <- jaspSyntax::loadDataSets(list(DataSetA = csvA, DataSetB = csvB)) check(is.integer(ids) && length(ids) == 2L && identical(names(ids), c("DataSetA", "DataSetB")) && all(ids > 0), diff --git a/Tools/verifyMultiDataSetRpc.py b/Tools/verifyMultiDataSetRpc.py index 81075f9..e00d1fb 100644 --- a/Tools/verifyMultiDataSetRpc.py +++ b/Tools/verifyMultiDataSetRpc.py @@ -11,9 +11,9 @@ - 'Datasets in this run' has 2 rows with titles + row/col counts - 'Selected variables' rows have n=10 and the right means (Score/Score land on DIFFERENT datasets: 12.50 vs 17.73 - no 'variable compared with itself') - - the metaDump echo audits the post-D3.3 provenance contract as R RECEIVED it: - options$.meta carries no dataSetId/filterId (C8) and no .filterIds sidecar survives - the engine-boundary strip (C11); per-form routing is proven at the VALUE level + - the metaDump echo audits the provenance contract as R RECEIVED it: + options$.meta carries no dataSetId/filterId and no .filterIds sidecar survives + the engine-boundary strip; per-form routing is proven at the VALUE level (each form's variable resolves against its own selection's slice: 12.50 vs 17.73) - no raw JASPColumn_* anywhere (decode on send) And the live-verification block: switch one host's selection mid-session and @@ -217,8 +217,8 @@ def row(src, var): return next((x for x in rows if x["source"] == src and x["var check("analysis references both datasets (dataSetIds in run response)", sorted(r2.get("dataSetIds", [])) == [1, 2], str(r2.get("dataSetIds"))) # Provenance as R sees it - NONE. The generated Engine json keeps -# .meta provenance-free (C8) and the transient .filterIds sidecar is stripped at the engine -# boundary (C11); the metaDump html (options$showMeta was on) audits exactly that. Per-form +# .meta provenance-free and the transient .filterIds sidecar is stripped at the engine +# boundary; the metaDump html (options$showMeta was on) audits exactly that. Per-form # provenance still routes the DATA (two same-named columns, two truths) - pinned value-level # above: A:Score 12.50 vs B:Score 17.73. meta_text = json.dumps(results).replace(chr(92)+chr(34), '"').replace(""", '"').replace(""", '"') @@ -230,14 +230,14 @@ def provenance_audit(res): return (m.group(1), m.group(2)) if m else (None, None) audit2 = provenance_audit(results) check("metaDump ran (provenance audit present)", audit2[0] is not None, str(audit2)) -check("R never sees .filterIds (C11 strip at the engine boundary)", audit2 == ("false", audit2[1]), str(audit2)) -check("options$.meta as R received it carries no provenance keys (C8)", audit2[1] == "0", str(audit2)) +check("R never sees .filterIds (strip at the engine boundary)", audit2 == ("false", audit2[1]), str(audit2)) +check("options$.meta as R received it carries no provenance keys", audit2[1] == "0", str(audit2)) # ---------------------------------------------------------------------------- # The phantom-slice class. Switch ONE HOST'S SELECTION # mid-session and check the NEXT run's data shows only the LIVE selections: no slice queued -# for an abandoned filter, and the run's provenance still C8/C11-clean (the per-run audit). -# Since the D3.3 flip the restamp moves the TYPED term filterIds (HostFilterSelection +# for an abandoned filter, and the run's provenance still clean (the per-run audit). +# Since the sidecar flip the restamp moves the TYPED term filterIds (HostFilterSelection # ::restampValuesUnderHost -> ListModel::restampTermsFilterId -> generated `.filterIds`), the # .meta half is dead. This RPC workspace has one (default) filter per dataset, so the # mid-session switch here crosses datasets (form A: f1 -> f2 -> f1); the sharp same-dataset/ @@ -272,8 +272,8 @@ def overview_ids(res): check("run 3: no phantom slice - only DataSet 2 loaded", ids3 == ["2"], str(ids3)) check("run 3: dataSetIds not over-reported", sorted(r3.get("dataSetIds", [])) == [2], str(r3.get("dataSetIds"))) audit3 = provenance_audit(res3) -check("run 3: still no provenance reaches R after the switch (C8/C11)", audit3 == ("false", "0"), str(audit3)) -# (old stamp-window checks retired with the meta stamps, the C8 audit above +check("run 3: still no provenance reaches R after the switch", audit3 == ("false", "0"), str(audit3)) +# (old stamp-window checks retired with the meta stamps, the provenance audit above # covers scalars and selection options too - NO dataSetId/filterId key may appear ANYWHERE) sv3 = next((t for t in tables(res3.get("results", res3)).values() if t.get("title") == "Selected variables"), None) @@ -297,7 +297,7 @@ def overview_ids(res): check("run 4: exactly one slice per dataset again", ids4 == ["1", "2"], str(ids4)) check("run 4: dataSetIds reports both datasets", sorted(r4.get("dataSetIds", [])) == [1, 2], str(r4.get("dataSetIds"))) audit4 = provenance_audit(res4) -check("run 4: provenance contract held through the switch-back too (C8/C11)", audit4 == ("false", "0"), str(audit4)) +check("run 4: provenance contract held through the switch-back too", audit4 == ("false", "0"), str(audit4)) a4 = None sv4 = next((t for t in tables(res4.get("results", res4)).values() if t.get("title") == "Selected variables"), None) if sv4: @@ -318,13 +318,13 @@ def overview_ids(res): # - same-name columns across two filters in the SAME list (twins) -> distinct encodings, # correct per-slice values (the 12.50/17.73 trick per family) # - per-row provenance in the TableFields-style row control (row-bearing variables list): -# twin rows keep their own keep/shift children (the D6.1 TermRowKey re-keying; pre-fix the +# twin rows keep their own keep/shift children (the TermRowKey re-keying; pre-fix the # second row clobbered the first) # - FactorLevelList encodeThis roundtrip: factor/level names arrive usable and NOTHING in the # final results leaks a raw JaspExtraOptions_ or JASPColumn_ token # - interaction terms resolve as one unit in a single slice (uniform term-level filterId) # - textarea host: tokens encode through the textarea's own selection and resolve there -# - cross-host bindTo (D6.2): a typed copy carrying the SOURCE filterId resolves in the +# - cross-host bindTo: a typed copy carrying the SOURCE filterId resolves in the # SOURCE's slice although the destination list selects the other one # - scalars/combos stay plain; PROVENANCE_AUDIT clean on every zoo run # ---------------------------------------------------------------------------- @@ -415,7 +415,7 @@ def zoo_full_opts(host_a="1", host_m3="", host_d="", host_f="", host_g="", check("zoo run 1: assignedA Score resolves in slice 1 (12.50)", len(a1) == 1 and str(a1[0]["slice"]) == "1" and "12.50" in str(a1[0]["values"]), json.dumps(a1)) auditz1 = provenance_audit(zres1) -check("zoo run 1: PROVENANCE_AUDIT clean (C8/C11)", auditz1 == ("false", "0"), str(auditz1)) +check("zoo run 1: PROVENANCE_AUDIT clean", auditz1 == ("false", "0"), str(auditz1)) zslices1 = zoo_slice_ids(zres1) or [] check("zoo run 1: slice 1 queued (host A), nothing beyond the workspace's two slices", "1" in zslices1 and set(zslices1) <= {"1", "2"}, str(zslices1)) @@ -434,11 +434,11 @@ def zoo_full_opts(host_a="1", host_m3="", host_d="", host_f="", host_g="", # --- zoo run 2: the whole zoo --------------------------------------------------------------------- # rowsC arrives in the STORAGE term-object shape: same-name twin rows 'Score' behind filter 1 # (keep=true, shift=1) and filter 2 (keep=false, shift=7). The applier must keep BOTH rows' own -# children (D6.1) and each token must encode/resolve through ITS filter's view. +# children and each token must encode/resolve through ITS filter's view. # boundG receives a typed copy of a list on filter 1 (Score@1) while its OWN selection is filter 2: -# the destination rule must not requalify the foreign stamp (D6.2) - it resolves in slice 1. +# the destination rule must not requalify the foreign stamp - it resolves in slice 1. # codeF's host selects filter 1 while the workspace's SHOWN dataset is B (filter 2): the textarea's -# bind-time encodeRScript must go through the HOST's view (C13 host-bound) - a Score token +# bind-time encodeRScript must go through the HOST's view (host-bound) - a Score token # resolving in slice 1 (12.50) proves it; the analysis-level fallback would have yielded 17.73. zr2, zres2 = zoo_run(zoo_full_opts( host_a="1", host_d="1", host_f="1", host_g="2", @@ -459,7 +459,7 @@ def zoo_full_opts(host_a="1", host_m3="", host_d="", host_f="", host_g="", for r in (z2 or []): print(" ", json.dumps(r)) -# components-rows: the D6.1 flagship - twins with OWN children, distinct slices + values +# components-rows: the flagship - twins with OWN children, distinct slices + values rc = find(z2, "components-rows") rc1 = next((r for r in rc if str(r["slice"]) == "1"), None) rc2 = next((r for r in rc if str(r["slice"]) == "2"), None) @@ -471,7 +471,7 @@ def zoo_full_opts(host_a="1", host_m3="", host_d="", host_f="", host_g="", rc2 and "keep=FALSE" in rc2["rowChild"] and "shift=7" in rc2["rowChild"] and "17.73" in rc2["values"], json.dumps(rc2)) check("zoo run 2: decoded names, no raw JASPColumn_* in any row", "JASPColumn" not in dump2, dump2[:300]) -# cross-host bindTo (D6.2) +# cross-host bindTo bg = find(z2, "bindto-cross-host") check("zoo run 2: boundG typed copy resolves in the SOURCE slice 1 (12.50) while its own host selects filter 2", len(bg) == 1 and str(bg[0]["slice"]) == "1" and "12.50" in str(bg[0]["values"]), json.dumps(bg)) @@ -482,7 +482,7 @@ def zoo_full_opts(host_a="1", host_m3="", host_d="", host_f="", host_g="", check("zoo run 2: assignedDefault (bare) Condition resolves via the analysis fallback in slice 2 with its levels", len(ab) == 1 and str(ab[0]["slice"]) == "2" and ab[0]["values"].startswith("X"), json.dumps(ab)) -# combobox (Zoo A): plain string value. NB the namespace finding, UPDATED by the mds-zoo +# combobox (Zoo A): plain string value. NB the namespace finding, UPDATED by the zoo # expansion: comboA's `source` used to be a broken QML binding (`source: zooVarsA` - a bare # identifier that does not exist; ReferenceError in the runtime log), so the dropdown silently # had no option list and its value encoded through the analysis-level namespace. With the source @@ -517,7 +517,7 @@ def zoo_full_opts(host_a="1", host_m3="", host_d="", host_f="", host_g="", check("zoo run 2: codeF model text round-trips (decoded on the way out)", len(tf_texts) == 1 and tf_texts[0]["received"] == "Score + Age", json.dumps(tf_texts)) -# scalars stay plain (M5): echo rows exist with the plain values +# scalars stay plain: echo rows exist with the plain values sc = find(z2, "scalar") cg = find(z2, "combobox-plain") check("zoo run 2: scalar plainSwitch echoes plain true (never provenanced)", len(sc) == 1 and sc[0]["received"].upper() == "TRUE", json.dumps(sc)) @@ -585,14 +585,14 @@ def zoo_full_opts(host_a="1", host_m3="", host_d="", host_f="", host_g="", check("zoo run 6: switched back, Score resolves in slice 1 again (12.50)", len(a6z) == 1 and str(a6z[0]["slice"]) == "1" and "12.50" in a6z[0]["values"], json.dumps(a6z)) -# --- zoo runs 7/8 (D6.2): the cross-host typed copy against live host switches --------------------- +# --- zoo runs 7/8: the cross-host typed copy against live host switches --------------------- # run 7: source host A on filter 2, destination host G on filter 1; assignedA binds unqualified # (intake stamps it @2) and boundG receives the typed copy Score@2. Both values must resolve in # slice 2 - the copy through its SOURCE stamp, not through its own host's filter 1. zr7, zres7 = zoo_run(zoo_full_opts( host_a="2", host_g="1", assigned_a={"types": ["scale"], "value": ["Score"]}, - bound_g={"value": [{"name": "Score", "types": ["scale"], "filterId": 2}]}), "run 7 (D6.2: copy @2, dest host on 1)") + bound_g={"value": [{"name": "Score", "types": ["scale"], "filterId": 2}]}), "run 7 (copy @2, dest host on 1)") z7 = zoo_rows(zres7) a7z = find(z7, "assigned-list") bg7 = find(z7, "bindto-cross-host") @@ -605,11 +605,11 @@ def zoo_full_opts(host_a="1", host_m3="", host_d="", host_f="", host_g="", check("zoo run 7: PROVENANCE_AUDIT clean", auditz7 == ("false", "0"), str(auditz7)) # run 8: switch the SOURCE host A back to filter 1 (delta run: boundG is NOT re-sent, its typed -# term state carries the foreign stamp 2). The owns-the-stamp rule (D3.5) must restamp ONLY the +# term state carries the foreign stamp 2). The owns-the-stamp rule must restamp ONLY the # host-owned term: assignedA follows A to slice 1, boundG stays on the SOURCE stamp @2 - the copy # is a stable stamp, not a live reference; requalification to the (now matching) filter 1 would # show up as 12.50 and fail here. -zr8, zres8 = zoo_run({"dataSetA": "1", "showMeta": True}, "run 8 (D6.2: source host switched, copy not re-sent)") +zr8, zres8 = zoo_run({"dataSetA": "1", "showMeta": True}, "run 8 (source host switched, copy not re-sent)") z8 = zoo_rows(zres8) a8z = find(z8, "assigned-list") bg8 = find(z8, "bindto-cross-host") @@ -621,7 +621,7 @@ def zoo_full_opts(host_a="1", host_m3="", host_d="", host_f="", host_g="", check("zoo run 8: PROVENANCE_AUDIT clean", auditz8 == ("false", "0"), str(auditz8)) # ---------------------------------------------------------------------------- -# mds-zoo expansion - the NEW control families (Zoo H-L), the RESULTS side and +# Zoo expansion - the NEW control families (Zoo H-L), the RESULTS side and # the option flows. The point of the expansion: data and flows must be VISIBLE # and the verifier must assert RESULTS-SIDE content, not just option roundtrips. # @@ -704,7 +704,7 @@ def no_raw_tokens(res, tag): # interactionD Score:Age via host D(1) -> slice 1 | factorsK Age -> the per-factor sidecar # (FactorsFormBase::createFilterIds) transports the term's OWN filterId per # factor and per term -> slice 1 (the term's stamp comes from its source host assignedK; the -# D3.2 hostless-panel deferral - analysis-level namespace, slice 2 - is CLOSED, pinned here) +# the hostless-panel deferral - analysis-level namespace, slice 2 - is CLOSED, pinned here) zr9, zres9 = zoo_run(zoo_full_opts( host_a="1", host_d="1", host_f="1", host_g="2", host_j="1", host_k="1", assigned_a={"types": ["scale"], "value": ["Score"]}, @@ -771,7 +771,7 @@ def no_raw_tokens(res, tag): check("zoo run 9: factorsK Fac1 received with title and var Age", len(fk) == 1 and fk[0]["received"] == "Fac1" and "title=Factor 1" in fk[0]["rowChild"] and "Age" in fk[0]["rowChild"], json.dumps(fk)) -check("zoo run 9: factorsK var Age resolves in ITS OWN term filter's namespace (slice 1) - the per-factor sidecar closed the D3.2 hostless-panel deferral", +check("zoo run 9: factorsK var Age resolves in ITS OWN term filter's namespace (slice 1) - the per-factor sidecar closed the hostless-panel deferral", len(fk) == 1 and str(fk[0]["slice"]) == "1" and "slices: 1" in str(fk[0]["values"]), json.dumps(fk)) check("zoo run 9: factorsK resolution is TERM-level inside the factor object (rowChild carries the per-term slice key 'Age(1)')", len(fk) == 1 and "vars: Age(1)" in str(fk[0]["rowChild"]), json.dumps(fk)) @@ -867,7 +867,7 @@ def _gated_map(g): check("zoo run 9: dataSetIds covers both datasets", sorted(zr9.get("dataSetIds", [])) == [1, 2], str(zr9.get("dataSetIds"))) auditz9 = provenance_audit(zres9) -check("zoo run 9: PROVENANCE_AUDIT clean (C8/C11)", auditz9 == ("false", "0"), str(auditz9)) +check("zoo run 9: PROVENANCE_AUDIT clean", auditz9 == ("false", "0"), str(auditz9)) no_raw_tokens(zres9, "zoo run 9") # --- zoo run 10 (FLOW F1): the dependent dropdown follows its source ------------------------------- diff --git a/inst/Description.qml b/inst/Description.qml index 5cf12ff..bcda9bd 100644 --- a/inst/Description.qml +++ b/inst/Description.qml @@ -113,7 +113,7 @@ Description // The old non-aware twin entry (func multiDataSetNonAware / qml testMultiDataSetNonAware) // is gone: it existed only to show that selection options stay inert // without multiDataSetAware, but its generated wrapper pointed at the AWARE form and so its - // R entry point always errored (REPORT M8). The non-aware code path is covered by the other + // R entry point always errored. The non-aware code path is covered by the other // (plain, single-dataset) analyses in this module. Analysis diff --git a/inst/qml/testMultiDataSetControls.qml b/inst/qml/testMultiDataSetControls.qml index 15f361f..ba2bc43 100644 --- a/inst/qml/testMultiDataSetControls.qml +++ b/inst/qml/testMultiDataSetControls.qml @@ -2,7 +2,7 @@ // // Every control family a real analysis form uses, each exercising per-item provenance: // Zoo A plain lists: an AvailableVariablesList carrying its own dataset/filter selection -// (the D2.2 list-level pattern) feeding an AssignedVariablesList, plus a DropDown +// (the list-level pattern) feeding an AssignedVariablesList, plus a DropDown // served by the same host (comboboxes stay PLAIN strings - never term objects - // while their value still resolves through the host namespace). // Zoo B REMOVED (zoo correction, owner): a bare assigned list - an assigned @@ -15,8 +15,8 @@ // so this is the row family as TableFields actually is it: an AssignedVariablesList // with a rowComponent (a keep checkbox and a shift integer), in its own form over a // default-provider pool (every assigned list has >= 1 source - owner rule). Same-name -// twin rows behind DIFFERENT filters (the C9 twins, set by the RPC verifier through -// the Storage term-object shape) must keep their own children - the D6.1 +// twin rows behind DIFFERENT filters (the same-name twins, set by the RPC verifier through +// the Storage term-object shape) must keep their own children - the // RelatedValuesPerTerm/RowControls re-keying (TermRowKey = value + filterId) is what // makes that work; pre-fix the second row silently clobbered the first. The pool's // key is -1, so the stamped twins are never swept by a universe reset: each twin @@ -26,12 +26,12 @@ // Zoo E FactorLevelList: encodeThis -> extras-encoder roundtrip (factor/level names encode // through the JaspExtraOptions_ namespace and must come back decoded and usable). // Zoo F TextArea with its OWN host: R-code text whose used columns encode through the -// selected filter's view (host-bound resolution, C13). -// Zoo G CROSS-HOST bindTo (D6.2): a list with its own selection whose option is bound +// selected filter's view (host-bound resolution). +// Zoo G CROSS-HOST bindTo: a list with its own selection whose option is bound // (bindTo = the JSON->control applier) to a typed copy generated from Zoo A's list - // the copy's terms carry the SOURCE filterId. The destination rule stamps only -// UNQUALIFIED terms (D5.1 intake guard) and the restamp walk never touches foreign -// stamps (D3.5 owns-the-stamp), so the value must encode and resolve through the +// UNQUALIFIED terms (the intake guard) and the restamp walk never touches foreign +// stamps (owns-the-stamp), so the value must encode and resolve through the // SOURCE's slice even though this list's own dropdown selects another one. // Zoo M THREE pools over the same test dataset (the zoo correction): a // hostless pool (no dataSetSelectionOption at all - the default-provider pool whose @@ -94,10 +94,10 @@ Form "The results panel shows the LOADED DATA per slice (head of every column, row count = Zoo H's " + "integer), the per-family provenance echo, the scalar echoes, the flow demonstrations and the " + "cross-slice computations." + - "'Show options .meta' adds the PROVENANCE_AUDIT (R must never see provenance: C8/C11)." + "'Show options .meta' adds the PROVENANCE_AUDIT (R must never see provenance)." } - // ---- Zoo A: list-level selection (D2.2 pattern) + combobox ---------------------------------- + // ---- Zoo A: list-level selection + combobox ---------------------------------- Section { title: qsTr("Zoo A - plain lists, own selection") @@ -138,7 +138,7 @@ Form // with a rowComponent, in its own form over a default-provider pool (every assigned list has // >= 1 source - owner rule; the bare standalone form died with Zoo B). Its pool's key is -1, // so the stamped twin rows below are never swept by any reset - each twin follows only its - // own stamp, which is exactly the C9 case. + // own stamp, which is exactly the same-name-twins case. Section { title: qsTr("Zoo C - row controls: per-row term + per-row values (default-provider source)") @@ -221,12 +221,12 @@ Form } } - // ---- Zoo G: cross-host bindTo (D6.2) --------------------------------------------------------- + // ---- Zoo G: cross-host bindTo --------------------------------------------------------- // The selection lives on the POOL now (the per-list dataSetSelectionOption crutch is gone, // same correction as B/C): boundG sits in a form over the pool hosted by dataSetG's filter, // so every assigned list has its source. The cross-host pin is unchanged: the verifier binds // a typed copy of a filter-1 term into boundG while the pool selects filter 2 - the copy - // keeps the SOURCE filterId and encodes through the SOURCE's slice (D6.2), origin + // keeps the SOURCE filterId and encodes through the SOURCE's slice, origin // -authoritative: a stamp is born at Storage/bindTo and is not requalified by a destination. Section { title: qsTr("Zoo G - typed copy bound from Zoo A keeps the SOURCE filter") @@ -337,7 +337,7 @@ Form // The paired-list family: AssignedPairsVariablesList is a VariablesList preset (2 columns, // drop-replace) whose model PAIRS its columns into 2-component terms (bound value // [["Score","Age"]]). Served by the pool hosted on dataSetJ's filter, so the pair's - // components stamp together under that pool's filter (C9 uniform-carry); the R side echoes + // components stamp together under that pool's filter (uniform carry); the R side echoes // each pair and computes per pair. Section { title: qsTr("Zoo J - paired variables list (pool on dataSetJ)") @@ -365,7 +365,7 @@ Form // (FactorsFormBase::createFilterIds) transports each term's OWN filterId // per factor and per term: a term keeps the stamp its source list synced onto it // (assignedK -> filter 1) and encodes through THAT filter's view - resolved slice by - // slice, no analysis-level fallback (the D3.2 hostless-panel deferral is CLOSED). + // slice, no analysis-level fallback (the hostless-panel deferral is CLOSED). // 2. A factor term must be a member of the form's SOURCE list (assignedK): a term outside it is // silently dropped by the form's model sync, and a term the form keeps is RE-TYPED by the // form (Age arrived ordinal here although sent scale, so the engine slice receives it as an @@ -511,7 +511,7 @@ Form } } - // ---- scalars (M5 contract: never provenanced, never term-objectified) ------------------------ + // ---- scalars (never provenanced, never term-objectified) ------------------------ Section { title: qsTr("Scalars") From 6207bab8f821d05f959305e6eb711d76ad98d776 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch <virtuoos@jorisgoosen.nl> Date: Thu, 22 Oct 2026 15:50:03 +0200 Subject: [PATCH 42/44] Fix stale tool-library pointer in buildBundle.R The not-found error and the header both said the setup instructions are at the bottom of the script, but they live in the header (the bottom documents bundle installation instead) - say "top" so people who hit the error find them. --- Tools/buildBundle.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tools/buildBundle.R b/Tools/buildBundle.R index fee7047..56261a9 100644 --- a/Tools/buildBundle.R +++ b/Tools/buildBundle.R @@ -8,7 +8,7 @@ # involved, so it works on a rolling-release system (Arch) with the R you have. # # Requires the tool library (jaspModuleTools + jaspModuleBundleManager + renv etc.), by default -# ../.toolLib; see the bottom of this file for how it was created. +# ../.toolLib; see the one-time setup instructions in this header below. # # Usage (from the jaspTestModule directory, or anywhere): # Rscript Tools/buildBundle.R # bundle with local jaspBase @@ -48,7 +48,7 @@ jaspVersion <- "development" for (a in args) if (grepl("^--jasp-version=", a)) jaspVersion <- sub("^--jasp-version=", "", a) if (!dir.exists(toolLib)) - stop("Tool library not found at ", toolLib, " - see the setup instructions at the bottom of this script.") + stop("Tool library not found at ", toolLib, " - see the one-time setup instructions at the top of this script.") .libPaths(c(toolLib, .libPaths())) suppressPackageStartupMessages({ library(jsonlite) }) From 1d265edc0c2adc5ce48924565c107eb99e4a2b52 Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch <virtuoos@jorisgoosen.nl> Date: Thu, 22 Oct 2026 16:24:25 +0200 Subject: [PATCH 43/44] buildBundle.R: real pre-flight checks + printed setup instructions The not-found error only pointed at the (misplaced) instructions; now the script checks its environment up front, before touching the filesystem: - --help prints the usage; --check-only exits cleanly after the checks instead of falling through to the bundle verification, where it died on the missing bundle. - A missing or incomplete tool library prints the full one-time setup commands with this checkout's real paths filled in (bundle manager, jaspModuleTools, jaspSyntax; clone hints when the sibling checkouts are absent) instead of a stale pointer. - The pre-flight summary states where the module, the local jaspBase (with branch), the tool library, the workdir and the output land. - Header no longer duplicates the setup block; it points at the printer. No build logic changed: lockfile swap, sandbox farm, compile, the QML smoke check, the bundle/zero-export/wrapper gates and the local install are the same steps, regrouped under section headers. --- Tools/buildBundle.R | 419 ++++++++++++++++++++++++++++---------------- 1 file changed, 266 insertions(+), 153 deletions(-) diff --git a/Tools/buildBundle.R b/Tools/buildBundle.R index 56261a9..ae8ea66 100644 --- a/Tools/buildBundle.R +++ b/Tools/buildBundle.R @@ -7,50 +7,167 @@ # built from source into a private renv library under ModuleBundleBuildDir/ - no JASP cellar # involved, so it works on a rolling-release system (Arch) with the R you have. # -# Requires the tool library (jaspModuleTools + jaspModuleBundleManager + renv etc.), by default -# ../.toolLib; see the one-time setup instructions in this header below. +# Requires a "tool library": a private R library (NOT your user library) holding jaspModuleTools, +# jaspModuleBundleManager, renv and their dependencies - plus jaspSyntax for the build-time QML +# smoke check (optional but recommended). Default location: ../.toolLib, sibling of this checkout; +# override with JASP_MODULE_TOOLLIB. Run this script without one and it prints the exact setup +# commands with your real paths filled in. # # Usage (from the jaspTestModule directory, or anywhere): # Rscript Tools/buildBundle.R # bundle with local jaspBase # Rscript Tools/buildBundle.R --update-lockfile # re-resolve renv.lock first (network) # Rscript Tools/buildBundle.R --remote-jaspbase # use the jaspBase from renv.lock (GitHub) # Rscript Tools/buildBundle.R --jasp-version=development +# Rscript Tools/buildBundle.R --check-only # pre-flight checks only, no build +# Rscript Tools/buildBundle.R --no-install-local # skip the local developer-JASP install +# Rscript Tools/buildBundle.R --help # this usage text # # Result: bundles_out/jaspTestModule.JASPModule (+ manifest json), installable via # JASP: + (add modules) menu -> Add module -> From file. -# -# One-time tool library setup: -# toolLib=/path/to/.toolLib; mkdir -p $toolLib -# Rscript -e '.libPaths(c(toolLib, .libPaths())) -# install.packages(c("renv","fs","pkgdepends","usethis","withr","curl","rjson","stringr", -# "archive","openssl","jsonlite","remotes"), lib=toolLib, -# repos="https://cloud.r-project.org") -# remotes::install_local("<jasp-desktop>/Engine/jaspModuleBundleManager", lib=toolLib, -# upgrade="never", dependencies=FALSE) -# remotes::install_local("<jaspModuleTools checkout>", lib=toolLib, -# upgrade="never", dependencies=FALSE)' args <- commandArgs(trailingOnly = TRUE) -scriptPath <- normalizePath(sub("^--file=", "", grep("^--file=", commandArgs(FALSE), value = TRUE)[[1]])) -moduledir <- normalizePath(file.path(dirname(scriptPath), "..")) -rootdir <- normalizePath(file.path(moduledir, "..")) +scriptPath <- normalizePath(sub("^--file=", "", grep("^--file=", commandArgs(FALSE), value = TRUE)[[1]])) +moduledir <- normalizePath(file.path(dirname(scriptPath), "..")) +rootdir <- normalizePath(file.path(moduledir, "..")) -jaspBaseDir <- Sys.getenv("JASP_BASE_DIR", file.path(rootdir, "jasp-desktop", "Engine", "jaspBase")) +jaspBaseDir <- Sys.getenv("JASP_BASE_DIR", file.path(rootdir, "jasp-desktop", "Engine", "jaspBase")) toolLib <- Sys.getenv("JASP_MODULE_TOOLLIB", file.path(rootdir, ".toolLib")) workdir <- file.path(moduledir, "ModuleBundleBuildDir") resultdir <- file.path(moduledir, "bundles_out") localJaspBase <- !("--remote-jaspbase" %in% args) updateLockfile <- "--update-lockfile" %in% args +checkOnly <- "--check-only" %in% args jaspVersion <- "development" for (a in args) if (grepl("^--jasp-version=", a)) jaspVersion <- sub("^--jasp-version=", "", a) -if (!dir.exists(toolLib)) - stop("Tool library not found at ", toolLib, " - see the one-time setup instructions at the top of this script.") +gitBranchOf <- function(dir) { + branch <- tryCatch(suppressWarnings(system2("git", c("-C", dir, "rev-parse", "--abbrev-ref", "HEAD"), + stdout = TRUE, stderr = TRUE)), + error = function(e) character(0)) + if (!length(branch) || any(grepl("fatal|not a git", branch))) "unknown" else branch[1] +} + +# The single source of tool-library setup truth: printed by the header claim above, by the +# missing-directory pre-flight and by the missing-package pre-flight, always with the real +# paths of THIS checkout filled in. +printToolLibSetup <- function() { + bundleManagerDir <- file.path(rootdir, "jasp-desktop", "Engine", "jaspModuleBundleManager") + moduleToolsDir <- file.path(rootdir, "jaspModuleTools") + jaspSyntaxDir <- file.path(rootdir, "jaspSyntax") + + cat("\n", + "This script builds with a private \"tool library\": a plain R library directory holding\n", + "jaspModuleTools, jaspModuleBundleManager, renv and their dependencies - plus jaspSyntax\n", + "for the build-time QML smoke check. It never touches your user library.\n\n", + "Expected location: ", toolLib, "\n", + "(keep it elsewhere? set the JASP_MODULE_TOOLLIB environment variable)\n\n", + "One-time setup - paste into a shell (adjust the first line to where you want the library):\n\n", + sep = "") + cat(sprintf(" toolLib=%s\n mkdir -p \"$toolLib\"\n\n", toolLib)) + cat(paste0( + " Rscript -e '.libPaths(c(toolLib, .libPaths()))\n", + " install.packages(c(\"renv\",\"fs\",\"pkgdepends\",\"usethis\",\"withr\",\"curl\",\"rjson\",\"stringr\",\n", + " \"archive\",\"openssl\",\"jsonlite\",\"remotes\"), lib=toolLib,\n", + " repos=\"https://cloud.r-project.org\")'\n\n")) + if (dir.exists(bundleManagerDir)) + cat(paste0( + " Rscript -e '.libPaths(c(toolLib, .libPaths()))\n", + " remotes::install_local(\"", bundleManagerDir, "\", lib=toolLib,\n", + " upgrade=\"never\", dependencies=FALSE)'\n\n")) + else + cat(paste0( + " # jaspModuleBundleManager lives in the jasp-desktop checkout (Engine/jaspModuleBundleManager);\n", + " # not found at ", bundleManagerDir, " - point the path at your jasp-desktop checkout:\n", + " Rscript -e '.libPaths(c(toolLib, .libPaths()))\n", + " remotes::install_local(\"<jasp-desktop>/Engine/jaspModuleBundleManager\", lib=toolLib,\n", + " upgrade=\"never\", dependencies=FALSE)'\n\n")) + if (dir.exists(moduleToolsDir)) + cat(paste0( + " Rscript -e '.libPaths(c(toolLib, .libPaths()))\n", + " remotes::install_local(\"", moduleToolsDir, "\", lib=toolLib,\n", + " upgrade=\"never\", dependencies=FALSE)'\n\n")) + else + cat(paste0( + " # jaspModuleTools - no checkout next to this module; clone it first:\n", + " # git clone https://github.com/jasp-stats/jaspModuleTools\n", + " Rscript -e '.libPaths(c(toolLib, .libPaths()))\n", + " remotes::install_local(\"<jaspModuleTools checkout>\", lib=toolLib,\n", + " upgrade=\"never\", dependencies=FALSE)'\n\n")) + if (dir.exists(jaspSyntaxDir)) + cat(paste0( + " # optional but recommended: jaspSyntax (QML smoke check; the test scripts need it too)\n", + " Rscript -e '.libPaths(c(toolLib, .libPaths()))\n", + " remotes::install_local(\"", jaspSyntaxDir, "\", lib=toolLib, upgrade=\"never\")'\n")) + else + cat(paste0( + " # optional but recommended: jaspSyntax (QML smoke check; the test scripts need it too)\n", + " # git clone https://github.com/jasp-stats/jaspSyntax\n", + " Rscript -e '.libPaths(c(toolLib, .libPaths()))\n", + " remotes::install_local(\"<jaspSyntax checkout>\", lib=toolLib, upgrade=\"never\")'\n")) + cat(paste0( + " # (its configure fetches a prebuilt libSyntaxInterface from a GitHub Release; to use a local\n", + " # jasp-desktop build instead, prefix the install with:\n", + " # JASPSYNTAX_LIB_PATH=<build>/SyntaxInterface/libSyntaxInterface.so)\n\n")) +} + +if ("--help" %in% args || "-h" %in% args) { + cat("\nUsage: Rscript Tools/buildBundle.R [options]\n\n") + cat("Builds bundles_out/jaspTestModule.JASPModule from this checkout. By default the renv.lock\n", + "jaspBase record is swapped for the local jasp-desktop checkout (--remote-jaspbase keeps the\n", + "GitHub record instead). Tool packages come from the tool library (see the header).\n\n", sep = "") + cat(" --update-lockfile re-resolve renv.lock with pkgdepends first (needs network)\n") + cat(" --remote-jaspbase keep the renv.lock jaspBase record (GitHub) instead of the local checkout\n") + cat(" --jasp-version=<v> buildforJaspVersion for jaspModuleTools::compile (default: development)\n") + cat(" --check-only run the pre-flight checks and exit without building\n") + cat(" --no-install-local skip installing the bundle into the local developer JASP\n") + cat(" --help this text\n\n") + quit(status = 0) +} + +# ---- pre-flight: fail early with actionable output, before touching the filesystem ---------- + +if (!dir.exists(toolLib)) { + cat("\nTool library not found at ", toolLib, ".\n", sep = "") + printToolLibSetup() + quit(status = 1) +} .libPaths(c(toolLib, .libPaths())) + +missingPkgs <- Filter(function(p) !requireNamespace(p, quietly = TRUE), + c("renv", "fs", "pkgdepends", "jsonlite", "archive", "jaspModuleTools")) +if (length(missingPkgs)) { + cat("\nCould not load (tool library ", toolLib, " is first on the path): ", + paste(missingPkgs, collapse = ", "), "\n", + "Install the missing ones into the tool library (not into your user library):\n", sep = "") + printToolLibSetup() + quit(status = 1) +} + +if (localJaspBase && !checkOnly && !dir.exists(jaspBaseDir)) + stop("No jaspBase checkout at ", jaspBaseDir, " (set JASP_BASE_DIR or use --remote-jaspbase).") + +lockPath <- file.path(moduledir, "renv.lock") +if (!file.exists(lockPath)) + stop("No renv.lock at ", lockPath, " - is ", moduledir, " the jaspTestModule checkout?") + +message("Pre-flight: + module : ", moduledir, " (branch ", gitBranchOf(moduledir), ") + jaspBase : ", if (localJaspBase) paste0(jaspBaseDir, " (branch ", gitBranchOf(jaspBaseDir), ", local swap)") else "from renv.lock (GitHub) via --remote-jaspbase", " + tool library : ", toolLib, " + workdir : ", workdir, " + bundle output: ", resultdir, " + jasp version : ", jaspVersion, + if (checkOnly) "\n check-only: pre-flight passed, not building.") + +if (checkOnly) + quit(status = 0) + +# ---- tool environment ------------------------------------------------------------------------ + suppressPackageStartupMessages({ library(jsonlite) }) library(jaspModuleTools) @@ -58,23 +175,23 @@ library(jaspModuleTools) Sys.setenv(RENV_CONFIG_SANDBOX_ENABLED = "false") options(jaspRemoteCellarRedownload = FALSE) -lockPath <- file.path(moduledir, "renv.lock") -lockBackup <- NULL - restoreLock <- function() { - if (!is.null(lockBackup)) { - writeLines(lockBackup, lockPath) - lockBackup <<- NULL - message("restored original ", lockPath) - } + if (!is.null(lockBackup)) { + writeLines(lockBackup, lockPath) + lockBackup <<- NULL + message("restored original ", lockPath) + } } +lockBackup <- NULL if (updateLockfile) { - message("re-resolving ", lockPath, " with pkgdepends ...") - jaspModuleTools::updateLockfile(moduledir) - lockBackup <- readLines(lockPath) # keep the freshly resolved one; never lose it + message("re-resolving ", lockPath, " with pkgdepends ...") + jaspModuleTools::updateLockfile(moduledir) + lockBackup <- readLines(lockPath) # keep the freshly resolved one; never lose it } +# ---- the renv sandbox that renv cannot build here (see the comment) --------------------------- + dir.create(workdir, recursive = TRUE, showWarnings = FALSE) # Binary R distributions (Arch!) have no R sources, so renv's sandbox can't be built - but @@ -86,41 +203,39 @@ Sys.setenv(RENV_PATHS_SANDBOX = file.path(workdir, "sandbox")) fakeSandbox <- renv:::renv_paths_sandbox() dir.create(fakeSandbox, recursive = TRUE, showWarnings = FALSE) for (lib in unique(c(R.home("library"), .libPaths()))) - for (pkg in list.dirs(lib, recursive = FALSE, full.names = FALSE)) - if (!file.exists(file.path(fakeSandbox, pkg)) && nzchar(pkg) && !grepl("^00LOCK", pkg)) - file.symlink(file.path(lib, pkg), file.path(fakeSandbox, pkg)) - -if (localJaspBase && !("--check-only" %in% args)) { - if (!dir.exists(jaspBaseDir)) - stop("No jaspBase checkout at ", jaspBaseDir, " (set JASP_BASE_DIR or use --remote-jaspbase).") - - baseDesc <- read.dcf(file.path(jaspBaseDir, "DESCRIPTION"))[1, ] - message("swapping jaspBase in renv.lock for local checkout: ", jaspBaseDir, - " (v", baseDesc[["Version"]], ", ", - system2("git", c("-C", jaspBaseDir, "rev-parse", "--abbrev-ref", "HEAD"), stdout = TRUE), ")") - - lockBackup <- readLines(lockPath) - lock <- jsonlite::fromJSON(lockPath, simplifyVector = FALSE) - - depsFields <- baseDesc[c("Depends", "Imports")] - depsFields <- depsFields[!is.na(depsFields)] - requires <- unique(trimws(unlist(strsplit(paste(depsFields, collapse = ","), ",")))) - requires <- gsub("\\s*\\(.*", "", requires) # "Rcpp (>= 0.12.14)" -> "Rcpp" - requires <- setdiff(requires, c("", "R")) - - # Same local-record shape jaspModuleTools uses for the module itself (createLocalRecord). - lock$Packages$jaspBase <- list( - Package = "jaspBase", - Version = baseDesc[["Version"]], - Source = "Local", - Requirements = requires, - RemoteType = "local", - RemoteUrl = normalizePath(jaspBaseDir), - Cacheable = TRUE, - Hash = unname(rlang::hash(rlang::hash_file(fs::dir_ls(jaspBaseDir, type = "file", recurse = TRUE)))) - ) - - writeLines(jsonlite::toJSON(lock, auto_unbox = TRUE, pretty = 2), lockPath) + for (pkg in list.dirs(lib, recursive = FALSE, full.names = FALSE)) + if (!file.exists(file.path(fakeSandbox, pkg)) && nzchar(pkg) && !grepl("^00LOCK", pkg)) + file.symlink(file.path(lib, pkg), file.path(fakeSandbox, pkg)) + +# ---- swap the renv.lock jaspBase record for the local checkout -------------------------------- + +if (localJaspBase) { + baseDesc <- read.dcf(file.path(jaspBaseDir, "DESCRIPTION"))[1, ] + message("swapping jaspBase in renv.lock for local checkout: ", jaspBaseDir, + " (v", baseDesc[["Version"]], ", branch ", gitBranchOf(jaspBaseDir), ")") + + lockBackup <- readLines(lockPath) + lock <- jsonlite::fromJSON(lockPath, simplifyVector = FALSE) + + depsFields <- baseDesc[c("Depends", "Imports")] + depsFields <- depsFields[!is.na(depsFields)] + requires <- unique(trimws(unlist(strsplit(paste(depsFields, collapse = ","), ",")))) + requires <- gsub("\\s*\\(.*", "", requires) # "Rcpp (>= 0.12.14)" -> "Rcpp" + requires <- setdiff(requires, c("", "R")) + + # Same local-record shape jaspModuleTools uses for the module itself (createLocalRecord). + lock$Packages$jaspBase <- list( + Package = "jaspBase", + Version = baseDesc[["Version"]], + Source = "Local", + Requirements = requires, + RemoteType = "local", + RemoteUrl = normalizePath(jaspBaseDir), + Cacheable = TRUE, + Hash = unname(rlang::hash(rlang::hash_file(fs::dir_ls(jaspBaseDir, type = "file", recurse = TRUE)))) + ) + + writeLines(jsonlite::toJSON(lock, auto_unbox = TRUE, pretty = 2), lockPath) } dir.create(resultdir, showWarnings = FALSE) @@ -128,70 +243,71 @@ dir.create(resultdir, showWarnings = FALSE) # renv::restore skips same-version records, so a changed local jaspBase checkout would silently # NOT be rebuilt into the pkglib (the version stays 0.20.4). When bundling against the local # checkout: drop the installed jaspBase first so it is always rebuilt from source. -if (localJaspBase && !("--check-only" %in% args)) { - pkglib <- file.path(workdir, "build_dir", basename(moduledir)) - installedBase <- file.path(pkglib, "jaspBase") - if (dir.exists(installedBase)) { - message("removing stale jaspBase from ", pkglib, " (will rebuild from the local checkout)") - unlink(installedBase, recursive = TRUE) - } +if (localJaspBase) { + pkglib <- file.path(workdir, "build_dir", basename(moduledir)) + installedBase <- file.path(pkglib, "jaspBase") + if (dir.exists(installedBase)) { + message("removing stale jaspBase from ", pkglib, " (will rebuild from the local checkout)") + unlink(installedBase, recursive = TRUE) + } } +# ---- compile + pack the bundle ---------------------------------------------------------------- + message("compiling + bundling (log is chatty, first run compiles all of jaspBase's deps) ...") -if (!("--check-only" %in% args)) tryCatch(jaspModuleTools::compile( - moduledir = moduledir, - workdir = file.path(workdir, "build_dir"), - resultdir = resultdir, - createBundle = TRUE, - bundleAll = TRUE, - buildforJaspVersion = jaspVersion, - useJASPRemoteCellar = FALSE, - localizeJASPModules = "identicalToLockfile" # our lockfile (incl. the swapped jaspBase) is what we want +tryCatch(jaspModuleTools::compile( + moduledir = moduledir, + workdir = file.path(workdir, "build_dir"), + resultdir = resultdir, + createBundle = TRUE, + bundleAll = TRUE, + buildforJaspVersion = jaspVersion, + useJASPRemoteCellar = FALSE, + localizeJASPModules = "identicalToLockfile" # our lockfile (incl. the swapped jaspBase) is what we want ), error = function(e) { - message("compile() failed: ", conditionMessage(e)) - calls <- sys.calls() - for (i in seq_along(calls)) message(sprintf(" #%02d %s", i, substr(paste(deparse(calls[[i]]), collapse = " "), 1, 160))) - stop(e) + message("compile() failed: ", conditionMessage(e)) + calls <- sys.calls() + for (i in seq_along(calls)) message(sprintf(" #%02d %s", i, substr(paste(deparse(calls[[i]]), collapse = " "), 1, 160))) + stop(e) }, finally = restoreLock()) -# Load the module Description and the multi-dataset form through the native SyntaxInterface -# (jaspSyntax, in the tool library): catches QML errors like missing imports or unknown types -# at build time, without firing up JASP. Skipped when jaspSyntax isn't installed. +# ---- build-time QML smoke check through the native SyntaxInterface ---------------------------- +# Load the module Description and the multi-dataset form through jaspSyntax (in the tool +# library): catches QML errors like missing imports or unknown types at build time, without +# firing up JASP. Skipped when jaspSyntax isn't installed. Sys.setenv(QT_QPA_PLATFORM = "offscreen") if (requireNamespace("jaspSyntax", quietly = TRUE)) { - message("checking module Description + QML forms via jaspSyntax ...") - suppressPackageStartupMessages(library(jaspSyntax)) - desc <- readModuleDescription(moduledir) - stopifnot(!is.null(desc)) - for (check in list( - list(func = "multiDataSetFunc", qml = "testMultiDataSet.qml", aware = TRUE))) { - qmlFile <- file.path(moduledir, "inst", "qml", check$qml) - stopifnot(file.exists(qmlFile)) - parseOptions <- NULL - if (check$aware) { - # The aware forms declare dataSetSelectionOptions; the bridge (rightly) demands every one - # of them names a loaded dataset - give the smoke check one dummy to point them all at. - jaspSyntax::loadDataSets(list(bundleCheck = data.frame(ok = 1))) - hits <- Filter(function(h) length(h) == 2L, - regmatches(readLines(qmlFile, warn = FALSE), - regexec('dataSetSelectionOption[[:space:]]*:[[:space:]]*"([^"]+)"', - readLines(qmlFile, warn = FALSE)))) - sels <- vapply(hits, function(h) h[[2]], character(1)) - stopifnot(length(sels) > 0L) - parseOptions <- stats::setNames(as.list(rep("bundleCheck", length(sels))), sels) - } - parsed <- jaspSyntax::parseQmlOptions(qmlFile, options = parseOptions, moduleName = "jaspTestModule", - analysisName = check$func, isolated = FALSE) - stopifnot(!is.null(parsed)) - message(" ", check$func, " -> ", check$qml, " loads OK") - } + message("checking module Description + QML forms via jaspSyntax ...") + suppressPackageStartupMessages(library(jaspSyntax)) + desc <- readModuleDescription(moduledir) + stopifnot(!is.null(desc)) + for (check in list( + list(func = "multiDataSetFunc", qml = "testMultiDataSet.qml", aware = TRUE))) { + qmlFile <- file.path(moduledir, "inst", "qml", check$qml) + stopifnot(file.exists(qmlFile)) + parseOptions <- NULL + if (check$aware) { + # The aware forms declare dataSetSelectionOptions; the bridge (rightly) demands every one + # of them names a loaded dataset - give the smoke check one dummy to point them all at. + jaspSyntax::loadDataSets(list(bundleCheck = data.frame(ok = 1))) + hits <- Filter(function(h) length(h) == 2L, + regmatches(readLines(qmlFile, warn = FALSE), + regexec('dataSetSelectionOption[[:space:]]*:[[:space:]]*"([^"]+)"', + readLines(qmlFile, warn = FALSE)))) + sels <- vapply(hits, function(h) h[[2]], character(1)) + stopifnot(length(sels) > 0L) + parseOptions <- stats::setNames(as.list(rep("bundleCheck", length(sels))), sels) + } + parsed <- jaspSyntax::parseQmlOptions(qmlFile, options = parseOptions, moduleName = "jaspTestModule", + analysisName = check$func, isolated = FALSE) + stopifnot(!is.null(parsed)) + message(" ", check$func, " -> ", check$qml, " loads OK") + } } else { - message("NOTE: jaspSyntax not installed (see github.com/jasp-stats/jaspSyntax) - QML not checked.") + message("NOTE: jaspSyntax not installed (see github.com/jasp-stats/jaspSyntax) - QML not checked.") } -# --------------------------------------------------------------------------------------------- -# Verify: the bundle manifest must list jaspBase, and the packed jaspBase must be OUR checkout -# (guard against silently packing a cached GitHub build instead). +# ---- verify: our jaspBase is in the bundle, not some cached GitHub build ---------------------- bundle <- file.path(resultdir, "jaspTestModule.JASPModule") if (!file.exists(bundle)) stop("compile() did not produce ", bundle) @@ -201,7 +317,7 @@ archive::archive_extract(bundle, dir = checkdir) manifest <- jsonlite::fromJSON(file.path(checkdir, "jaspTestModule_manifest.json")) jaspBaseHash <- sub(" .*", "", grep("=> jaspBase_", manifest$mapping, value = TRUE)) -if (length(jaspBaseHash) != 1) stop("jaspBase not in the bundle manifest (mapping: ", paste(manifest$mapping, collapse=", "), ")") +if (length(jaspBaseHash) != 1) stop("jaspBase not in the bundle manifest (mapping: ", paste(manifest$mapping, collapse = ", "), ")") pkgdir <- file.path(checkdir, "lib", "jaspBase") dir.create(pkgdir, recursive = TRUE, showWarnings = FALSE) @@ -213,27 +329,26 @@ archive::archive_extract(file.path(checkdir, jaspBaseHash), dir = pkgdir) suppressPackageStartupMessages(library(jaspBase, lib.loc = file.path(checkdir, "lib"))) if (localJaspBase) { - # Zero-export contract (CTO 2026-10-09): the multi-dataset machinery ships NO accessor - # exports - analyses index datasets[[filter-id-key]] directly. Any reappearance fails here. - forbidden <- c("getSliceKey", "getSlice", "sliceDataSetId", "sliceTitle", "getDataSetFor", - "getDataSetColumn", "dataSetIdFromEncoded", "dataSetNameFromEncoded", "storeDataSets") - leaked <- intersect(forbidden, getNamespaceExports("jaspBase")) - runBody <- any(grepl("multiDataSetJson", deparse(body(jaspBase::runJaspResults)))) - if (length(leaked) || !runBody) - stop("bundled jaspBase violates the zero-export contract (leaked exports: ", - paste(leaked, collapse = ", "), if (runBody) "" else " + runJaspResults has no multiDataSetJson flow", ")") - message("VERIFIED: bundled jaspBase loads, queue flow present, zero leaked accessor exports.") + # Zero-export contract (CTO 2026-10-09): the multi-dataset machinery ships NO accessor + # exports - analyses index datasets[[filter-id-key]] directly. Any reappearance fails here. + forbidden <- c("getSliceKey", "getSlice", "sliceDataSetId", "sliceTitle", "getDataSetFor", + "getDataSetColumn", "dataSetIdFromEncoded", "dataSetNameFromEncoded", "storeDataSets") + leaked <- intersect(forbidden, getNamespaceExports("jaspBase")) + runBody <- any(grepl("multiDataSetJson", deparse(body(jaspBase::runJaspResults)))) + if (length(leaked) || !runBody) + stop("bundled jaspBase violates the zero-export contract (leaked exports: ", + paste(leaked, collapse = ", "), if (runBody) "" else " + runJaspResults has no multiDataSetJson flow", ")") + message("VERIFIED: bundled jaspBase loads, queue flow present, zero leaked accessor exports.") } else { - message("bundle jaspBase version: ", as.character(utils::packageVersion("jaspBase", lib.loc = file.path(checkdir, "lib")))) + message("bundle jaspBase version: ", as.character(utils::packageVersion("jaspBase", lib.loc = file.path(checkdir, "lib")))) } -# --------------------------------------------------------------------------------------------- -# Wrapper-body gate: every `jaspBase::<name>` reference in the module sources must be EXPORTED -# by the jaspBase this bundle ships. The generator once emitted the @noRd-internal -# jaspBase::storeDataSets into aware wrappers - a regenerated wrapper then died in plain R with -# "'storeDataSets' is not an exported object". The forbidden-exports check above only guards the -# jaspBase side; nothing caught a broken *reference* on the wrapper side. parse() first so -# comments mentioning symbols (deparse drops them) can never false-positive. +# ---- wrapper-body gate: every jaspBase:: reference must be exported by the bundled jaspBase ---- +# The generator once emitted the @noRd-internal jaspBase::storeDataSets into aware wrappers - a +# regenerated wrapper then died in plain R with "'storeDataSets' is not an exported object". The +# forbidden-exports check above only guards the jaspBase side; nothing caught a broken +# *reference* on the wrapper side. parse() first so comments mentioning symbols (deparse drops +# them) can never false-positive. wrapperFiles <- list.files(file.path(moduledir, "R"), pattern = "[.]R$", full.names = TRUE) codeNoComments <- unlist(lapply(wrapperFiles, function(f) deparse(parse(f, keep.source = FALSE))), use.names = FALSE) refs <- unique(unlist(regmatches(codeNoComments, @@ -241,26 +356,24 @@ refs <- unique(unlist(regmatches(codeNoComments, refNames <- sub("^jaspBase::`?", "", refs) unknown <- setdiff(refNames, getNamespaceExports("jaspBase")) if (length(unknown)) - stop("wrapper bodies reference jaspBase symbols the installed jaspBase does not export: ", - paste(unknown, collapse = ", "), " (generated by an outdated generator? regenerate the wrappers)") + stop("wrapper bodies reference jaspBase symbols the installed jaspBase does not export: ", + paste(unknown, collapse = ", "), " (generated by an outdated generator? regenerate the wrappers)") message("VERIFIED: wrapper bodies reference only exported jaspBase symbols (", length(refNames), " distinct).") message("Bundle: ", bundle, " (size: ", format(file.size(bundle) / 2^20, digits = 1), " MiB)") -# --------------------------------------------------------------------------------------------- -# Install into the local developer JASP so the module is simply THERE after every build -# (same result as "Add module -> From file", through JASP's own installer - see -# Tools/installModuleLocal.sh for the mechanism). Skipped with --check-only, -# --no-install-local or SKIP_MODULE_LOCAL_INSTALL=1; a failure here is a warning only: -# the bundle itself is fine (most often JASP is still running). -if (!("--check-only" %in% args) && !("--no-install-local" %in% args) && !nzchar(Sys.getenv("SKIP_MODULE_LOCAL_INSTALL"))) { - installer <- file.path(moduledir, "Tools", "installModuleLocal.sh") - status <- system2(installer, shQuote(bundle)) - if (identical(as.integer(status), 0L)) - message("installed into the local JASP (module_libs + manifest + dev prefs) - restart ./build/Desktop/JASP to pick it up.") - else - warning("bundle built OK, but the local install failed (is JASP running?) - run Tools/installModuleLocal.sh manually.") +# ---- install into the local developer JASP ---------------------------------------------------- +# So the module is simply THERE after every build (same result as "Add module -> From file", +# through JASP's own installer - see Tools/installModuleLocal.sh for the mechanism). A failure +# here is a warning only: the bundle itself is fine (most often JASP is still running). +if (!("--no-install-local" %in% args) && !nzchar(Sys.getenv("SKIP_MODULE_LOCAL_INSTALL"))) { + installer <- file.path(moduledir, "Tools", "installModuleLocal.sh") + status <- system2(installer, shQuote(bundle)) + if (identical(as.integer(status), 0L)) + message("installed into the local JASP (module_libs + manifest + dev prefs) - restart ./build/Desktop/JASP to pick it up.") + else + warning("bundle built OK, but the local install failed (is JASP running?) - run Tools/installModuleLocal.sh manually.") } else { - message("Install in JASP: + menu -> Add module -> From file, pick that .JASPModule (or run Tools/installModuleLocal.sh).") + message("Install in JASP: + menu -> Add module -> From file, pick that .JASPModule (or run Tools/installModuleLocal.sh).") } From 5a767ea29083a281883ccaf93b517d39d48d0f8b Mon Sep 17 00:00:00 2001 From: Virtuoos Automatisch <virtuoos@jorisgoosen.nl> Date: Thu, 22 Oct 2026 23:07:18 +0200 Subject: [PATCH 44/44] Drop the multiDataSetAware declarations: awareness is derived from the form The Analysis finds out by itself once its form loads and a control registers a dataset selection (dataSetSelectionOption), so the two Analysis entries no longer declare it. Comments updated to the new module-author contract. --- R/multiDataSet.R | 4 +++- inst/Description.qml | 11 +++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/R/multiDataSet.R b/R/multiDataSet.R index 3da3dd1..cc8aff3 100644 --- a/R/multiDataSet.R +++ b/R/multiDataSet.R @@ -1,5 +1,7 @@ # Multi-dataset aware analyses (jasp-desktop branch dataSetAwareVarForm, jaspBase branch -# multiDataSetAware). An analysis whose Description/Analysis entry sets multiDataSetAware gets +# multiDataSetAware). An analysis whose form declares a dataset selection (dataSetSelectionOption +# on a list/textarea - the Analysis finds out by itself, Description.qml no longer has a +# multiDataSetAware property) gets # every dataset its options reference as `datasets`. Engine runs AND jaspTools::runAnalysis # handouts both key that list by SLICE id - the resolved filter id of each queued dataset+filter # pair (attrs "dataSetIds"/"dataSetNames" map a key back to its dataset) - so both are indexed diff --git a/inst/Description.qml b/inst/Description.qml index bcda9bd..ba179fd 100644 --- a/inst/Description.qml +++ b/inst/Description.qml @@ -80,10 +80,11 @@ Description GroupTitle { title: "Multi dataset" } - // multiDataSetAware: the analysis gets every dataset its options reference as the `datasets` - // argument (jaspBase >= the multiDataSetAware branch) and VariablesForm's dataSetSelection - // dropdown becomes live. It only kicks in combined with preloadData; without it the engine - // logs that the analysis gets its data like a non-aware one. + // multiDataSetAware is not declared any more: the Analysis finds out by itself once its form + // loads and a control registers a dataset selection (dataSetSelectionOption - see + // testMultiDataSet.qml). The analysis then gets every dataset its options reference as the + // `datasets` argument (jaspBase >= the multiDataSetAware branch) and its FilterSelect dropdowns + // become live. The expander's filter button selects the default (hostless) source. Analysis { title: "Test Multi-DataSet" @@ -91,7 +92,6 @@ Description func: "multiDataSetFunc" requiresData: true preloadData: true - multiDataSetAware: true hasWrapper: true } @@ -106,7 +106,6 @@ Description func: "multiDataSetControlsFunc" requiresData: true preloadData: true - multiDataSetAware: true hasWrapper: true }