jatosr 0.1.0
First release: an R client for the JATOS REST API, from credentials to an analysis-ready dataset. The JATOS retrieval workflow is modelled on the smartr package by Chenyu Li.
jatos_set_credentials()stores the API token in the credential store of the operating system, through the keyring package, and the server’s URL in a configuration file undertools::R_user_dir()(orJATOSR_CONFIG_DIR). No token is written to disk in the clear. Optionally under a named profile, so several servers or accounts can live side by side (jatos_list_profiles(),jatos_has_credentials(),jatos_token_info()).JATOS_TOKENandJATOS_HOST(or a named profile’s pair) are still read, and take precedence over the credential store, so continuous integration, containers and cluster jobs inject the token the way they inject any secret.jatos_connection()builds a connection that carries the profile name, the host and an opaque session id — never the token, which stays in the session and is fetched only inside the request builder. So a connection can be serialised, cached by knitr or stored by targets without writing a secret to disk, and no print, message or error can show one. This relies onhttr21.2.0 or later, which no longer serialises a redacted header.jatos_credentials_sitrep()reports which of the three places each profile’s host and token would be taken from, without printing a token, and names an.Renvironfile whose variables take precedence over the credential store.jatos_remove_credentials()deletes one profile from the credential store and the configuration again, keeping every other profile. It asks first, and where no prompt can be shown it stops rather than deleting; a script passesconfirm = FALSE.jatos_studies(),jatos_study(),jatos_components(),jatos_batches(),jatos_batch(),jatos_groups()andjatos_study_log()list what the token can see, by id or uuid.jatos_results_metadata()returns result metadata as a tibble with one row per component result, withjatos_flatten_metadata(),jatos_url_query()andjatos_study_results()(one row per run) to reshape it.jatos_filter_metadata()selects runs by state, worker type, start time and explicit exclusions before anything is downloaded.jatos_download_results()andjatos_download_files()fetch result data and participant uploads incrementally into a local cache, skipping what is already there and reporting a per-row status;jatos_cache_status()summarises the cache batch by batch andjatos_read_metadata()rebuilds the metadata from it offline, both for every batch or for those named inbatch_id.jatos_result_files()lists the files participants uploaded, one row per file with the component result it belongs to, beforejatos_download_files()fetches them.jatos_import_results()unpacks a results zip exported from the JATOS GUI into the same cache layout, so data that never came through the API can be used with the rest of the package.A zip, downloaded or imported, with an entry whose name would leave the target directory (a
..component or an absolute name) is refused before anything is extracted.utils::unzip()itself skips such an entry only from R 4.5.1 on.jatos_read_results()reads the cached jsPsych files into one trial-level tibble with the run’s metadata joined to every row,jatos_read_json()reads a single file, and areaderargument handles other formats such as PsychoJS csv.jatos_extract_fields()pulls scalar JSON fields (a participant code, a condition) out of the result files without parsing them in full.jatos_write_results()saves the dataset as.rds,.csv,.csv.gz,.tsv,.parquetor.RData(one file per component on request, with the component in the file and object names), andjatos_write_raw()copies the raw result files under names taken from a metadata column, refusing a name that could leave the directory.jatos_export_results()runs the whole pipeline in one call and writes the trials, the study-result table and a provenance record;download = FALSErebuilds the same dataset from the cache with no network access.reader,split,coerceandon_errorare passed tojatos_read_results(), so a column whose type differs between files or a file that cannot be read need not stop the export.jatos_export_study()andjatos_export_archive()download the study archive and the full results archive for a data deposit.jatos_create_study_codes(),jatos_study_code(),jatos_activate_study_code(),jatos_deactivate_study_code()andjatos_study_links()manage study codes and build the run URLs.Three vignettes:
vignette("jatosr")walks the whole pipeline,vignette("credentials")covers tokens and profiles, andvignette("developer-notes")documents how the package is built.jatos_set_credentials()puts the profile’s entry in the configuration back as it was when the credential store refuses the token, and says so (classjatosr_keyring_write_failed), instead of leaving a host without a token behind the error of the store. On a machine without a persistent store it names the command that creates a file keyring there,keyring::backend_file$new()$keyring_create("system").jatos_credentials_sitrep()starts with the jatosr, keyring, R and operating system versions, and the commit of a package installed from GitHub, so its output can go into a bug report as it is.The examples of the offline functions (reading, extracting, writing, importing, the cache status) run on a small synthetic cache shipped under
inst/extdata/; the examples that need a server or the machine’s credential store say so in a comment.Built against a mock of the OpenAPI spec, then run against a real JATOS reporting
apiVersion1.0.1, which disagrees with that spec in five places; the test suite also runs a second mock profile that answers the way that server does. The changes that followed are the next items.jatos_token_info()reads a token with no expiry asexpires = NAagain when the server spells “never expires” asexpirationDate: 0rather than as an absent field. It used to report1970-01-01, which then made the nextjatos_connection()of the session warn that a working token had expired. A past expiry that the server’s ownisExpiredcalls false no longer warns at all, whatever sentinel a future version invents.jatos_set_credentials(check = TRUE)no longer printsfor "NA"on an API version that sends nousernamefield.Request errors carry the server’s own sentence when it arrives as
text/plainrather than as JSON — every error body of that server does. Token-shaped strings are scrubbed out of any server text before it becomes part of a message, so the guarantee that no token reaches a print, message or error path does not depend on the server’s discretion.A 404 tells the two cases apart: a route this JATOS does not have (an endpoint added in a later version) no longer says “check the id”, and
jatos_batches()namesjatos_studies(with_batches = TRUE)as the way round it. Both it andjatos_groups()document which servers have the endpoint.Request errors name the credential profile and host they used, which is the fact that identifies a call aimed at the wrong one of two accounts on one server.
A failed credential lookup names the profiles that are configured, instead of only advising how to create a default one that the setup deliberately does not have;
jatos_list_profiles()documents thatactiveisFALSEin every row when no profile is selected.The
check_cache_layout()refusal says that nothing in the directory was read or changed, and namesjatos_export_archive()as the way to get the zip thatjatos_import_results()wants.The errors of the credential, argument, cache and metadata layers carry a condition class (
jatosr_no_token,jatosr_file_exists,jatosr_bad_argumentand ten more, listed under?jatosr), so a script can handle one kind withtryCatch()and let the rest through. Errors from the server keep thehttr2classes.
