-
Notifications
You must be signed in to change notification settings - Fork 17
Support source-module wrapped analysis replay #204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
6d6834d
f51c8d7
fc70f1a
c2b96dd
8c4f861
507936c
8ff17b1
972e537
745b5dd
b96880c
3aa9621
310db39
17d2533
1b90919
3eef834
8827c31
0a64ba4
7be9ac3
3189fcc
967e593
322fc31
1b414bf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -629,6 +629,7 @@ jaspResultsStrings <- function() { | |
| ".requestTempFileNameNative", | ||
| ".requestTempRootNameNative", | ||
| ".readDatasetToEndNative", | ||
| ".readFullDatasetToEnd", | ||
| ".readDataSetHeaderNative", | ||
| ".readDataSetRequestedNative", | ||
| ".requestStateFileNameNative", | ||
|
|
@@ -659,19 +660,20 @@ jaspResultsStrings <- function() { | |
|
|
||
| } | ||
|
|
||
| .stateFilePath <- function(location) { | ||
| if (is.list(location) && !is.null(location$root) && !is.null(location$relativePath)) | ||
| return(file.path(location$root, location$relativePath)) | ||
|
|
||
| location$relativePath | ||
| } | ||
|
|
||
| .saveState <- function(state) { | ||
| location <- .fromRCPP(".requestStateFileNameNative") | ||
| relativePath <- location$relativePath | ||
| statePath <- .stateFilePath(location) | ||
| dir.create(dirname(statePath), recursive = TRUE, showWarnings = FALSE) | ||
|
|
||
| # when run through jaspTools do not save the state, but store it internally | ||
| if ("jaspTools" %in% loadedNamespaces()) { | ||
| # fool renv so it does not try to install jaspTools | ||
| .setInternal <- utils::getFromNamespace(".setInternal", asNamespace("jaspTools")) | ||
| .setInternal("state", state) | ||
| return(list(relativePath = relativePath)) | ||
| } | ||
|
|
||
| try(suppressWarnings(base::save(state, file=relativePath, compress=FALSE)), silent = FALSE) | ||
| try(suppressWarnings(base::save(state, file=statePath, compress=FALSE)), silent = FALSE) | ||
|
|
||
| return(list(relativePath = relativePath)) | ||
| } | ||
|
|
@@ -683,9 +685,10 @@ jaspResultsStrings <- function() { | |
| if (base::exists(".requestStateFileNameNative")) { | ||
|
|
||
| location <- .fromRCPP(".requestStateFileNameNative") | ||
| statePath <- .stateFilePath(location) | ||
|
|
||
| base::tryCatch( | ||
| base::load(location$relativePath), | ||
| base::load(statePath), | ||
| error=function(e) e | ||
| #,warning=function(w) w #Commented out because if there *is* a warning, which there of course shouldnt be, the state wont be loaded *at all*. | ||
| ) | ||
|
|
@@ -1163,18 +1166,49 @@ storeDataSet <- function(dataset) { | |
| jaspSyntax::loadDataSet(dataset) | ||
| } | ||
|
|
||
| .wrappedAnalysisQmlFile <- function(moduleName, qmlFileName, modulePath = NULL, qmlFile = NULL) { | ||
| isNonEmptyString <- function(x) is.character(x) && length(x) == 1 && !is.na(x) && nzchar(x) | ||
|
vandenman marked this conversation as resolved.
Outdated
|
||
|
|
||
| if (isNonEmptyString(qmlFile)) | ||
| return(normalizePath(qmlFile, winslash = "/", mustWork = FALSE)) | ||
|
|
||
| if (isNonEmptyString(modulePath)) { | ||
| qmlCandidates <- file.path(modulePath, c("inst/qml", "qml"), qmlFileName) | ||
| existingQml <- qmlCandidates[file.exists(qmlCandidates)] | ||
| if (length(existingQml) > 0) | ||
| return(normalizePath(existingQml[[1]], winslash = "/", mustWork = FALSE)) | ||
|
|
||
| return(normalizePath(qmlCandidates[[1]], winslash = "/", mustWork = FALSE)) | ||
| } | ||
|
|
||
| normalizePath(file.path(find.package(moduleName), "qml", qmlFileName), winslash = "/", mustWork = FALSE) | ||
| } | ||
|
Comment on lines
+1320
to
+1343
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this works, but why not adjust https://github.com/jasp-stats/jasp-desktop/blob/22c167b122776652968722eaf7c09debb24e3203/Common/log.cpp#L18 so that we can disable or enable this in a static jasp-build from jaspSyntax?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, this belongs in Desktop rather than jaspBase. The corresponding Desktop PR now changes
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Small correction to the cross-PR note: the final Desktop implementation no longer changes The current Desktop shape is narrower: SyntaxInterface initializes logging with the same Boost null stream pattern used by Engine/MainWindow, then toggles |
||
|
|
||
| #' @export | ||
| runWrappedAnalysis <- function(moduleName, analysisName, qmlFileName, options, version, preloadData) { | ||
| runWrappedAnalysis <- function(moduleName, analysisName, qmlFileName, options, version, preloadData, modulePath = NULL, qmlFile = NULL) { | ||
| if (jaspResultsCalledFromJasp()) { | ||
| # In this case, it is JASP Desktop that called the wrapper. This was done to parse the R code, and to get the arguments | ||
| # in a structured way. In this way the Desktop can then set the options to the QML controls of the form, and this will run the analysis. | ||
| # So here, just give back the parsed options. | ||
| return(toJSON(list("options" = options, "module" = moduleName, "analysis" = analysisName, "version" = version))) | ||
| response <- list( | ||
| "options" = options, | ||
| "module" = moduleName, | ||
| "analysis" = analysisName, | ||
| "version" = version, | ||
| "qmlFileName" = qmlFileName, | ||
| "jaspBaseVersion" = as.character(utils::packageVersion("jaspBase")), | ||
|
vandenman marked this conversation as resolved.
|
||
| "source" = "jaspBase::runWrappedAnalysis" | ||
| ) | ||
| if (!is.null(modulePath)) | ||
| response[["modulePath"]] <- modulePath | ||
| if (!is.null(qmlFile)) | ||
| response[["qmlFile"]] <- qmlFile | ||
| return(toJSON(response)) | ||
|
|
||
| } else { | ||
| # The wrapper is called inside an R environment (R Studio probably). | ||
| # The options must be parsed and checked by the QML form, and then the real analysis can be called. | ||
| qmlFile <- file.path(find.package(moduleName), "qml", qmlFileName) | ||
| qmlFile <- .wrappedAnalysisQmlFile(moduleName, qmlFileName, modulePath, qmlFile) | ||
| # Load the qml form, and set the right options (formula should be parsed and all logics set in QML should be checked), and run the analysis | ||
| options <- jaspSyntax::loadQmlAndParseOptions(moduleName, analysisName, qmlFile, as.character(toJSON(options)), version, preloadData) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| testthat::test_that("variance checks handle factor variables", { | ||
| dataset <- data.frame(x = factor(c("a", "b", "a"))) | ||
|
|
||
| result <- jaspBase:::.checkVariance(dataset, target = "x", equalTo = 0) | ||
|
|
||
| testthat::expect_false(result$error) | ||
| testthat::expect_null(result$errorVars) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| testthat::test_that("wrapped analysis QML paths prefer explicit files", { | ||
| explicitFile <- tempfile(fileext = ".qml") | ||
| writeLines("import QtQuick", explicitFile) | ||
|
|
||
| qmlFile <- jaspBase:::.wrappedAnalysisQmlFile( | ||
| moduleName = "jaspBase", | ||
| qmlFileName = "Ignored.qml", | ||
| qmlFile = explicitFile | ||
| ) | ||
|
|
||
| testthat::expect_equal(qmlFile, normalizePath(explicitFile, winslash = "/", mustWork = FALSE)) | ||
| }) | ||
|
|
||
| testthat::test_that("wrapped analysis QML paths resolve checkout module paths", { | ||
| modulePath <- tempfile("module") | ||
| qmlDir <- file.path(modulePath, "inst", "qml") | ||
| dir.create(qmlDir, recursive = TRUE) | ||
| qmlPath <- file.path(qmlDir, "Analysis.qml") | ||
| writeLines("import QtQuick", qmlPath) | ||
|
|
||
| qmlFile <- jaspBase:::.wrappedAnalysisQmlFile( | ||
| moduleName = "jaspBase", | ||
| qmlFileName = "Analysis.qml", | ||
| modulePath = modulePath | ||
| ) | ||
|
|
||
| testthat::expect_equal(qmlFile, normalizePath(qmlPath, winslash = "/", mustWork = FALSE)) | ||
| }) | ||
|
|
||
| testthat::test_that("standalone bridge can read the full dataset callback", { | ||
| oldCallback <- if (exists(".readFullDatasetToEnd", envir = .GlobalEnv, inherits = FALSE)) { | ||
| get(".readFullDatasetToEnd", envir = .GlobalEnv, inherits = FALSE) | ||
| } else { | ||
| NULL | ||
| } | ||
| hadCallback <- exists(".readFullDatasetToEnd", envir = .GlobalEnv, inherits = FALSE) | ||
| on.exit({ | ||
| if (hadCallback) { | ||
| assign(".readFullDatasetToEnd", oldCallback, envir = .GlobalEnv) | ||
| } else if (exists(".readFullDatasetToEnd", envir = .GlobalEnv, inherits = FALSE)) { | ||
| rm(".readFullDatasetToEnd", envir = .GlobalEnv) | ||
| } | ||
| }, add = TRUE) | ||
|
|
||
| assign( | ||
| ".readFullDatasetToEnd", | ||
| function() data.frame(x = 1, check.names = FALSE), | ||
| envir = .GlobalEnv | ||
| ) | ||
|
|
||
| testthat::expect_equal( | ||
| jaspBase:::.fromRCPP(".readFullDatasetToEnd"), | ||
| data.frame(x = 1, check.names = FALSE) | ||
| ) | ||
| }) | ||
|
|
||
| testthat::test_that("standalone state saving uses the callback file contract", { | ||
| oldCallback <- if (exists(".requestStateFileNameNative", envir = .GlobalEnv, inherits = FALSE)) { | ||
| get(".requestStateFileNameNative", envir = .GlobalEnv, inherits = FALSE) | ||
| } else { | ||
| NULL | ||
| } | ||
| hadCallback <- exists(".requestStateFileNameNative", envir = .GlobalEnv, inherits = FALSE) | ||
| on.exit({ | ||
| if (hadCallback) { | ||
| assign(".requestStateFileNameNative", oldCallback, envir = .GlobalEnv) | ||
| } else if (exists(".requestStateFileNameNative", envir = .GlobalEnv, inherits = FALSE)) { | ||
| rm(".requestStateFileNameNative", envir = .GlobalEnv) | ||
| } | ||
| }, add = TRUE) | ||
|
|
||
| stateFile <- tempfile("jasp-state-") | ||
| assign( | ||
| ".requestStateFileNameNative", | ||
| function() list(root = dirname(stateFile), relativePath = basename(stateFile)), | ||
| envir = .GlobalEnv | ||
| ) | ||
|
|
||
| savedState <- list(figures = list(), other = list(answer = 42)) | ||
| result <- jaspBase:::.saveState(savedState) | ||
| loaded <- load(stateFile) | ||
|
|
||
| testthat::expect_equal(result$relativePath, basename(stateFile)) | ||
| testthat::expect_identical(loaded, "state") | ||
| testthat::expect_equal(state, savedState) | ||
| }) | ||
|
|
||
| testthat::test_that("standalone state retrieval uses the callback root", { | ||
| oldCallback <- if (exists(".requestStateFileNameNative", envir = .GlobalEnv, inherits = FALSE)) { | ||
| get(".requestStateFileNameNative", envir = .GlobalEnv, inherits = FALSE) | ||
| } else { | ||
| NULL | ||
| } | ||
| hadCallback <- exists(".requestStateFileNameNative", envir = .GlobalEnv, inherits = FALSE) | ||
| on.exit({ | ||
| if (hadCallback) { | ||
| assign(".requestStateFileNameNative", oldCallback, envir = .GlobalEnv) | ||
| } else if (exists(".requestStateFileNameNative", envir = .GlobalEnv, inherits = FALSE)) { | ||
| rm(".requestStateFileNameNative", envir = .GlobalEnv) | ||
| } | ||
| }, add = TRUE) | ||
|
|
||
| stateFile <- tempfile("jasp-state-") | ||
| assign( | ||
| ".requestStateFileNameNative", | ||
| function() list(root = dirname(stateFile), relativePath = basename(stateFile)), | ||
| envir = .GlobalEnv | ||
| ) | ||
|
|
||
| state <- list(figures = list(), other = list(answer = 42)) | ||
| save(state, file = stateFile, compress = FALSE) | ||
| rm(state) | ||
|
|
||
| testthat::expect_equal( | ||
| jaspBase:::.retrieveState(), | ||
| list(figures = list(), other = list(answer = 42)) | ||
| ) | ||
| }) |
Uh oh!
There was an error while loading. Please reload this page.