Support source-module wrapped analysis replay#204
Conversation
| "analysis" = analysisName, | ||
| "version" = version, | ||
| "qmlFileName" = qmlFileName, | ||
| "jaspBaseVersion" = as.character(utils::packageVersion("jaspBase")), |
There was a problem hiding this comment.
Was this already the jaspBase version, or is it the version from the jasp module?
There was a problem hiding this comment.
Good catch. The existing version field is the generated module wrapper version, not the jaspBase version. I kept that field unchanged for compatibility and added jaspBaseVersion as the separate runtime-contract version. I also added an inline comment in f51c8d7 so this distinction is explicit at the call site.
| } | ||
|
|
||
| .wrappedAnalysisQmlFile <- function(moduleName, qmlFileName, modulePath = NULL, qmlFile = NULL) { | ||
| isNonEmptyString <- function(x) is.character(x) && length(x) == 1 && !is.na(x) && nzchar(x) |
There was a problem hiding this comment.
I'm wondering if we shouldn't use rlang for many of these predicates and fs for file system operations. We import those packages no matter what and they might clean up a lot of these verbose helpers and file lookup functions.
There was a problem hiding this comment.
Agreed. I incorporated this in f51c8d7: fs and rlang are now declared imports, the string/list predicates use rlang, and the QML/state path handling uses fs for path construction, existence checks, normalization, and directory creation. The calls are namespace-qualified so the dependency use is explicit.
| } | ||
|
|
||
| .stateFilePath <- function(location) { | ||
| if (is.list(location) && !is.null(location$root) && !is.null(location$relativePath)) |
There was a problem hiding this comment.
This feels very verbose. Perhaps add a comment with why there are so many checks? Are other possible values allowed? If yes, shouldn't this be streamlined elsewhere?
There was a problem hiding this comment.
Agreed. I tightened this in f51c8d7. The helper now validates the callback shape and errors clearly for unsupported values. The intended Desktop/native contract is list(root, relativePath), while a standalone callback may provide only relativePath, which is interpreted relative to the current working directory. I also added a focused test for unsupported callback shapes so this contract is covered.
| .decodeJaspPlotObject <- function(plot) { | ||
| tryCatch( | ||
| decodeplot(plot, returnGrob = FALSE), | ||
| error = function(e) plot | ||
| ) | ||
| } |
There was a problem hiding this comment.
Two issues.
- If a user loads a dataset, then does the analysis, it works. If they load another dataset afterward, and show then print/ save the plot from the previous analysis, this no longer works, no? We need some hook to know which encoding/ decoding object should be used for which plot object.
- If we do save or saveRDS on the output from jasp, then restart the r session and then try to replay the plot, all the information about encoding/ decoding is no longer available because this lives on the C++ side of jaspSyntax. We need a way around this. This could be done by immediately storing the decodedplot and not doing it on demand.
|
Implemented the eager-decoding follow-up from the review/spec discussion. What changed:
Companion jaspTools follow-up:
Verification:
|
|
Follow-up from the final desiderata audit: I found one remaining provenance gap in the live R6 wrapper path. oRObject() called the C++ oRObject() materializer first, and that C++ code can consult the currently active global decoder before the R-side eager decoder sees the object. That meant a live wrapper could still be decoded against the wrong dataset after an analysis/dataset switch, even though saved state was already eager-decoded. Fixed in 17d2533:unJaspResults() stores the per-analysis decode context on the result wrapper and propagates it to child wrappers.
Verification:
|
|
@vandenman this is ready for your review. The final desiderata pass is pushed, including the decode-context provenance follow-up for live result materialization and stored plot replay/export. |
Currently translated at 100.0% (26 of 26 strings) Translation: JASP/jaspBase Translate-URL: https://hosted.weblate.org/projects/jasp/jaspbase/vi/ Co-authored-by: Thành Khôi Lê <lethanhkhoi@gmail.com>
|
Follow-up after the direct I pushed
I also moved the per-analysis decode-context capture until after dataset preload, because that is when Verification after the change:
|
Summary
runWrappedAnalysis()resolve analyses from explicit source module paths and QML files, not only installed module packagesjaspToolsinternals.readFullDatasetToEndto standalone bridge callbacks and make result write/seal/send operations harmless outside Desktop$toRObject(), including table names/footnotes and stored ggplot labels, without changing the encoded backend names used while analyses rundecodeJaspResultState()as the public jaspBase API for decoding stored figure objects in result state payloadsWhy
This is the runtime support layer for the
jaspToolstojaspSyntaxbridge.jaspToolsnow delegates QML/runtime option preparation tojaspSyntaxand then callsjaspBase::runWrappedAnalysis()with explicit source-module provenance. That requiresjaspBaseto accept source paths cleanly and to use the same state callback-file contract that the native bridge exposes.The direct
jaspSyntaxreplay path also exposed a display-boundary gap: modules correctly receive encoded dataset names while fitting models, but R-facing result objects and stored figure state could still expose internal names such asJaspColumn_2_Encoded. The fix keeps encoded names in the backend/runtime path and decodes only R-facing copies returned to callers.jaspToolspreviously had to walkstate$figuresitself and calljaspBase:::decodeplot(). This PR makes that an explicitjaspBase::decodeJaspResultState()responsibility, sojaspToolscan stay a developer orchestration wrapper instead of owning result-state plot semantics or reaching into jaspBase internals.Related PRs
jaspToolspath.Engine/jaspBaseto this runtime support line and exposes the matching SyntaxInterface APIs.Engine/jaspBaseto the result-object/state decoding commits.runWrappedAnalysis()with explicit source module/QML context and delegates result-state decoding tojaspBase.Verification
Rscript -e "pkgload::load_all('C:/JASP-Packages/jasp-desktop/Engine/jaspBase', quiet = TRUE); testthat::test_dir('tests/testthat', reporter = 'summary')"lme4::cake/ABCMixedModelsLMM example throughjaspSyntax::loadDataSet()andres$toRObject(); ANOVA footnote decoded torecipe, plot labels decoded toABCandtemperature, and the returned object had zero scalarjaspColumn/JaspColumn_hits.git diff --check