diff --git a/.Rbuildignore b/.Rbuildignore index b8d9cda2..a0373518 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -11,7 +11,6 @@ ^CONTRIBUTING\.md$ ^ISSUE_TEMPLATE\.md$ ^SUPPORT\.md$ -^\.github$ ^man-roxygen$ ^appveyor\.yml$ codemeta.json @@ -25,3 +24,8 @@ codemeta.json ^tic\.R$ ^Dockerfile$ ^codemeta\.json$ +^\.devcontainer +^\.github +^\.vscode +^.lintr +^\.pre-commit-config\.yaml$ diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..01eb1ac3 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,25 @@ +FROM ghcr.io/rocker-org/devcontainer/r-ver:4.2 + +# non interactive frontend for locales +ENV DEBIAN_FRONTEND=noninteractive + +# installing texlive and utils +RUN apt-get update && \ + apt-get -y install --no-install-recommends \ + libudunits2-dev libxtst6 libxt6 libmagick++-dev \ + libxml2-dev libjq-dev libudunits2-dev libgdal-dev libgeos-dev libproj-dev \ + libicu-dev libv8-dev libjq-dev libprotobuf-dev protobuf-compiler libgit2-dev \ + rsync mdbtools cargo libavfilter-dev libfontconfig1-dev libopenblas-dev \ + freetds-common libct4 libsybdb5 freetds-bin freetds-dev libsybdb5 tdsodbc \ + unixodbc make git procps locales curl && \ + rm -rf /var/lib/apt/lists/* + +# generating locales +RUN sed -i -e 's/# en_GB.UTF-8 UTF-8/en_GB.UTF-8 UTF-8/' /etc/locale.gen && \ + dpkg-reconfigure --frontend=noninteractive locales && \ + update-locale LANG=en_GB.UTF-8 +ENV LANGUAGE=en_GB.UTF-8 LANG=en_GB.UTF-8 LC_ALL=en_GB.UTF-8 + +# installing cpanm & missing latexindent dependencies +RUN curl -L http://cpanmin.us | perl - --self-upgrade && \ + cpanm Log::Dispatch::File YAML::Tiny File::HomeDir diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..67d202fb --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,40 @@ +{ + "name": "R (rocker/r-ver base)", + "dockerFile": "Dockerfile", + "features": { + "ghcr.io/rocker-org/devcontainer-features/quarto-cli:1": { + "version": "prerelease" + }, + "ghcr.io/rocker-org/devcontainer-features/apt-packages:1": { + "packages": "libudunits2-dev,libxtst6,libxt6,libmagick++-dev" + }, + "ghcr.io/rocker-org/devcontainer-features/r-packages:1": { + "packages": "github::rstudio/renv,tidyverse,git2r,knitr,spelling,languageserver,precommit,clisymbols,crayon,dplyr,fs,glue,httr,httr2,janitor,lifecycle,lubridate,magrittr,purrr,readr,rlang,stringr,semver,tibble,tidyr,xml2,covr,DT,ggplot2,here,knitr,lattice,leaflet,listviewer,leafpop,leafem,mapview,rmarkdown,roxygen2,sf,terra,testthat,tmap,usethis" + }, + "ghcr.io/devcontainers-contrib/features/pre-commit:2": {} + }, + "mounts": [ + "source=${localEnv:HOME}${localEnv:USERPROFILE}/.gitconfig,target=~/.gitconfig,type=bind,consistency=cached" + ], + "onCreateCommand": "pre-commit install && pre-commit run", + "customizations": { + "vscode": { + "extensions": [ + "mechatroner.rainbow-csv", + "REditorSupport.r", + "RDebugger.r-debugger", + "ms-azuretools.vscode-docker", + "usernamehw.errorlens", + "christian-kohler.path-intellisense", + "ms-vscode.live-server" + ] + }, + "codespaces": { + "openFiles": [ + "DESCRIPTION", + "NEWS.md", + "data-raw/make_release.R" + ] + } + } +} diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2b4474c5..259c1b63 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -8,34 +8,34 @@ assignees: florianm --- ## Problem - ## ruODK function(s) used - ## Unexpected behaviour - ## Reproducible example - @@ -47,12 +47,12 @@ assignees: florianm
Session Info - ODK Central version: - - ```{r} diff --git a/.lintr b/.lintr new file mode 100644 index 00000000..9c3ef4aa --- /dev/null +++ b/.lintr @@ -0,0 +1,7 @@ +linters: linters_with_defaults( + line_length_linter(120), + object_name_linter = NULL, + line_length_linter = NULL, + commented_code_linter = NULL + ) +exclusions: list() diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..196abcc8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,54 @@ +exclude: "/migrations/|^venv/|/email/subject/" +default_stages: [commit] + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: trailing-whitespace + name: Drop trailing whitespace + exclude: man + - id: end-of-file-fixer + name: Fix end of files + - id: check-yaml + name: Check YAML syntax + - id: check-toml + name: Check TOML syntax + # - id: pretty-format-json + # name: Format JSON + - id: check-added-large-files + name: Prevent adding large files + args: ['--maxkb=1024'] + - id: check-merge-conflict + name: Check for unhandled merge conflicts + - id: debug-statements + name: Check for debug statements + - id: detect-private-key + name: Detect private keys + + # https://lorenzwalthert.github.io/precommit/articles/available-hooks.html + - repo: https://github.com/lorenzwalthert/precommit + rev: v0.4.2 + hooks: + - id: style-files + name: Style code to tidyverse conventions + args: [--scope=spaces] + - id: readme-rmd-rendered + name: Ensure README.md is up to date + - id: no-debug-statement + name: Ensure that no debug() statement is committed + - id: no-browser-statement + name: Ensure that no browser() statement is committed + # - id: spell-check + # name: Spellcheck + # - id: deps-in-desc + # name: Ensure that DESCRIPTION lists all dependencies + # args: [--allow_private_imports, --root=R] + - id: use-tidy-description + name: Format DESCRIPTION + # - id: pkgdown + # name: Validate pkgdown.yml + # - id: lintr + # name: Lint code but emit only warnings + # args: [--warn_only] + # verbose: true diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index fafeb4c1..17dd94f1 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,6 +1,6 @@ # Contributor Code of Conduct -As contributors and maintainers of this project, we pledge to respect all people who +As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. @@ -13,13 +13,13 @@ imagery, derogatory comments or personal attacks, trolling, public or private ha insults, or other unprofessional conduct. Project maintainers have the right and responsibility to remove, edit, or reject comments, -commits, code, wiki edits, issues, and other contributions that are not aligned to this -Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed +commits, code, wiki edits, issues, and other contributions that are not aligned to this +Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. -Instances of abusive, harassing, or otherwise unacceptable behaviour may be reported by +Instances of abusive, harassing, or otherwise unacceptable behaviour may be reported by opening an issue or contacting one or more of the project maintainers. -This Code of Conduct is adapted from the Contributor Covenant -(), version 1.0.0, available at +This Code of Conduct is adapted from the Contributor Covenant +(), version 1.0.0, available at diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5a28b936..abca8348 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contributing +# Contributing This contributing guide has been derived from the `tidyverse` boilerplate. Where it seems over the top, common sense is appreciated, and every contribution is appreciated. @@ -31,9 +31,9 @@ If you would like to contribute to the code base, follow the process below. * [Code of Conduct](#code-of-conduct) This explains how to propose a change to `ruODK` via a pull request using -Git and GitHub. +Git and GitHub. -For more general info about contributing to `ruODK`, see the +For more general info about contributing to `ruODK`, see the [Resources](#resources) at the end of this document. ### Naming conventions @@ -41,24 +41,24 @@ ruODK names functions after ODK Central endpoints. If there are aliases, such as "Dataset" and "Entity List", choose the alias that is shown to Central users (here, choose "Entity List") over internally used terms. -Function names combine the object name (`project`, `form`, `submission`, -`attachment`, `entitylist`, `entity`, etc.) with the action (`list`, `detail`, -`patch`) as snake case, e.g. `project_list()`. +Function names combine the object name (`project`, `form`, `submission`, +`attachment`, `entitylist`, `entity`, etc.) with the action (`list`, `detail`, +`patch`) as snake case, e.g. `project_list()`. In case of any uncertainty, discussion is welcome. -In contrast, `pyODK` uses a class based approach with the pluralised object name +In contrast, `pyODK` uses a class based approach with the pluralised object name separated from the action `client.entity_lists.list()`. -Documentation should capitalise ODK Central object names: Project, Form, +Documentation should capitalise ODK Central object names: Project, Form, Submission, Entity. ### Prerequisites -To test the package, you will need valid credentials for an existing ODK Central +To test the package, you will need valid credentials for an existing ODK Central instance to be used as a test server. Before you do a pull request, you should always file an issue and make sure -the maintainers agree that it is a problem, and is happy with your basic proposal -for fixing it. +the maintainers agree that it is a problem, and is happy with your basic proposal +for fixing it. If you have found a bug, follow the issue template to create a minimal [reprex](https://www.tidyverse.org/help/#reprex) if you can do so without revealing sensitive information. Never include credentials in your reprex. @@ -67,17 +67,47 @@ revealing sensitive information. Never include credentials in your reprex. Some changes have intricate internal and external dependencies, which are easy to miss and break. These checklists aim to avoid these pitfalls. +#### Adding a function +Discuss and agree on function naming with the `pyODK` developers. + +In the function documentation, include the following components: + +* Title +* Lifecycle badge +* Documentation from the official ODK Central API docs +* Additional paragraphs (see e.g. `entity_detail.R`): Factor out commonly used + text fragments into `man-roxygen` fragments. +* Link the relevant ODK Central API docs and surround the link with `# nolint start / end` mufflers for linter warnings about the line length. +* Link to the correct reference family topic. If adding a new topic, + update `pkgdown.yml`. +* List all parameters and export the function as usual. +* Add examples showing basic usage inside a `\dontrun{}` block. Examples have + no access to the test server and will only work for internal helpers which + do not access the ODK Central API. + +Inside the function: + +* Gatecheck for missing parameters via `yell_if_missing`. +* Gatecheck for the minimum ODK Central version. +* Prepare lengthy components of the `httr` call if it improves legibility. +* Use the native R pipe where possible. `ruODK` still re-exports the magrittr pipe. +* Parse response content as `utf-8`. +* Clean column names with `janitor::clean_names()`. + +Link to tests: +* Add a commented out `# usethis::use_test("entity_detail") # nolint` to functions and a commented out `# usethis::use_r("entity_detail") # nolint` to tests. This serves both to create the correct files and as a convenient shortcut between both. + #### Adding a dependency * Update DESCRIPTION -* Update GH Actions install workflows - do R package deps have system deps? +* Update GH Actions install workflows - do R package deps have system deps? Can GHA install them in all environments? * Update Dockerfile * Update binder install.R * Update installation instructions #### Renaming a vignette -* Search-replace all links to the vignette throughout - * ruODK, +* Search-replace all links to the vignette throughout + * ruODK, * ODK Central "OData" modal * ODK Central docs @@ -103,14 +133,14 @@ to miss and break. These checklists aim to avoid these pitfalls. #### Fork, clone, branch -The first thing you'll need to do is to [fork](https://help.github.com/articles/fork-a-repo/) -the [`ruODK` GitHub repo](https://github.com/ropensci/ruODK), and +The first thing you'll need to do is to [fork](https://help.github.com/articles/fork-a-repo/) +the [`ruODK` GitHub repo](https://github.com/ropensci/ruODK), and then clone it locally. We recommend that you create a branch for each PR. #### Check Before changing anything, make sure the package still passes the below listed -flavours of `R CMD check` locally for you. +flavours of `R CMD check` locally for you. ```r goodpractice::goodpractice(quiet = FALSE) @@ -120,9 +150,9 @@ chk <- rcmdcheck::rcmdcheck(args = c("--as-cran")) #### Style -Match the existing code style. This means you should follow the tidyverse -[style guide](http://style.tidyverse.org). Use the -[styler](https://CRAN.R-project.org/package=styler) package to apply the style +Match the existing code style. This means you should follow the tidyverse +[style guide](http://style.tidyverse.org). Use the +[styler](https://CRAN.R-project.org/package=styler) package to apply the style guide automatically. Be careful to only make style changes to the code you are contributing. If you @@ -140,15 +170,15 @@ spelling::update_wordlist() #### Document -We use [roxygen2](https://cran.r-project.org/package=roxygen2), specifically with the +We use [roxygen2](https://cran.r-project.org/package=roxygen2), specifically with the [Markdown syntax](https://cran.r-project.org/web/packages/roxygen2/vignettes/markdown.html), to create `NAMESPACE` and all `.Rd` files. All edits to documentation should be done in roxygen comments above the associated function or -object. Then, run `devtools::document()` to rebuild the `NAMESPACE` and `.Rd` +object. Then, run `devtools::document()` to rebuild the `NAMESPACE` and `.Rd` files. See the `RoxygenNote` in [DESCRIPTION](DESCRIPTION) for the version of -roxygen2 being used. +roxygen2 being used. ```r spelling::spell_check_package() @@ -165,9 +195,9 @@ if (fs::file_info("README.md")$modification_time < #### Test We use [testthat](https://cran.r-project.org/package=testthat). Contributions -with test cases are easier to review and verify. +with test cases are easier to review and verify. -To run tests and build the vignettes, you'll need access to the +To run tests and build the vignettes, you'll need access to the [ruODK test server](https://odkc.dbca.wa.gov.au/). If you haven't got an account yet, create an [accont request issue](https://github.com/ropensci/ruODK/issues/new/choose) to request access to this ODK Central instance. @@ -205,7 +235,7 @@ ODKC_UN="..." ODKC_PW="..." ``` -Keep in mind that `ruODK` defaults to use `ODKC_{URL,UN,PW}`, so for everyday +Keep in mind that `ruODK` defaults to use `ODKC_{URL,UN,PW}`, so for everyday use outside of contributing, you will want to use your own `ODKC_{URL,UN,PW}` account credentials. @@ -248,27 +278,27 @@ title) to automatically close the issue when the PR is merged. Once you've pushed your commit(s) to a branch in _your_ fork, you're ready to make the pull request. Pull requests should have descriptive titles to remind reviewers/maintainers what the PR is about. You can easily view what exact -changes you are proposing using either the [Git diff](http://r-pkgs.had.co.nz/git.html#git-status) -view in RStudio, or the [branch comparison view](https://help.github.com/articles/creating-a-pull-request/) -you'll be taken to when you go to create a new PR. If the PR is related to an -issue, provide the issue number and slug in the _description_ using +changes you are proposing using either the [Git diff](http://r-pkgs.had.co.nz/git.html#git-status) +view in RStudio, or the [branch comparison view](https://help.github.com/articles/creating-a-pull-request/) +you'll be taken to when you go to create a new PR. If the PR is related to an +issue, provide the issue number and slug in the _description_ using auto-linking syntax (e.g. `#15`). #### Check the docs -Double check the output of the -[rOpenSci documentation CI](https://dev.ropensci.org/job/ruODK/lastBuild/console) +Double check the output of the +[rOpenSci documentation CI](https://dev.ropensci.org/job/ruODK/lastBuild/console) for any breakages or error messages. #### Review, revise, repeat -The latency period between submitting your PR and its review may vary. -When a maintainer does review your contribution, be sure to use the same +The latency period between submitting your PR and its review may vary. +When a maintainer does review your contribution, be sure to use the same conventions described here with any revision commits. ### Resources * [Happy Git and GitHub for the useR](http://happygitwithr.com/) by Jenny Bryan. -* [Contribute to the tidyverse](https://www.tidyverse.org/contribute/) covers +* [Contribute to the tidyverse](https://www.tidyverse.org/contribute/) covers several ways to contribute that _don't_ involve writing code. * [Contributing Code to the Tidyverse](http://www.jimhester.com/2017/08/08/contributing/) by Jim Hester. * [R packages](http://r-pkgs.had.co.nz/) by Hadley Wickham. @@ -276,11 +306,11 @@ conventions described here with any revision commits. * [Automated checking](http://r-pkgs.had.co.nz/check.html) * [Object documentation](http://r-pkgs.had.co.nz/man.html) * [Testing](http://r-pkgs.had.co.nz/tests.html) -* [dplyr’s `NEWS.md`](https://github.com/tidyverse/dplyr/blob/master/NEWS.md) +* [dplyr’s `NEWS.md`](https://github.com/tidyverse/dplyr/blob/master/NEWS.md) is a good source of examples for both content and styling. -* [Closing issues using keywords](https://help.github.com/articles/closing-issues-using-keywords/) +* [Closing issues using keywords](https://help.github.com/articles/closing-issues-using-keywords/) on GitHub. -* [Autolinked references and URLs](https://help.github.com/articles/autolinked-references-and-urls/) +* [Autolinked references and URLs](https://help.github.com/articles/autolinked-references-and-urls/) on GitHub. * [GitHub Guides: Forking Projects](https://guides.github.com/activities/forking/). diff --git a/DESCRIPTION b/DESCRIPTION index cbc80fff..d7191790 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,48 +1,35 @@ Type: Package Package: ruODK Title: An R Client for the ODK Central API -Version: 1.4.9.9002 -Authors@R: - c(person(given = c("Florian", "W."), - family = "Mayer", - role = c("aut", "cre"), - email = "Florian.Mayer@dbca.wa.gov.au", - comment = c(ORCID = "0000-0003-4269-4242")), - person(given = "Maëlle", - family = "Salmon", - role = "rev", - email = "maelle.salmon@yahoo.se", - comment = c(ORCID = "0000-0002-2815-0399")), - person(given = "Karissa", - family = "Whiting", - role = "rev", - comment = c(ORCID = "0000-0002-4683-1868")), - person(given = "Jason", - family = "Taylor", - role = "rev"), - person(given = "Marcelo", - family = "Tyszler", - role = "ctb", - comment = c(ORCID = "0000-0002-4573-0002")), - person(given = "Hélène", - family = "Langet", - role = "ctb", - comment = c(ORCID = "0000-0002-6758-2397")), - person(given = "DBCA", - role = c("cph", "fnd")), - person(given = "NWSFTCP", - role = "fnd")) -Description: Access and tidy up data from the 'ODK Central' API. - 'ODK Central' is a clearinghouse for digitally captured data - . - 'ODK Central' and its API are documented at . +Version: 1.5.0 +Authors@R:c( + person(c("Florian", "W."), "Mayer", , "Florian.Mayer@dpc.wa.gov.au", role = c("aut", "cre"), + comment = c(ORCID = "0000-0003-4269-4242")), + person("Maëlle", "Salmon", , "maelle.salmon@yahoo.se", role = "rev", + comment = c(ORCID = "0000-0002-2815-0399")), + person("Karissa", "Whiting", role = "rev", + comment = c(ORCID = "0000-0002-4683-1868")), + person("Jason", "Taylor", role = "rev"), + person("Marcelo", "Tyszler", role = "ctb", + comment = c(ORCID = "0000-0002-4573-0002")), + person("Hélène", "Langet", role = "ctb", + comment = c(ORCID = "0000-0002-6758-2397")), + person("DBCA", role = c("cph", "fnd")), + person("NWSFTCP", role = "fnd") + ) +Description: Access and tidy up data from the 'ODK Central' API. 'ODK + Central' is a clearinghouse for digitally captured data using ODK + . It manages user accounts + and permissions, stores form definitions, and allows data collection + clients like 'ODK Collect' to connect to it for form download and + submission upload. The 'ODK Central' API is documented at + . License: GPL-3 -URL: https://docs.ropensci.org/ruODK, - https://github.com/ropensci/ruODK +URL: https://docs.ropensci.org/ruODK, https://github.com/ropensci/ruODK BugReports: https://github.com/ropensci/ruODK/issues -Depends: +Depends: R (>= 4.1) -Imports: +Imports: clisymbols (>= 1.2.0), crayon (>= 1.5.2), dplyr (>= 1.0.10), @@ -56,41 +43,43 @@ Imports: purrr (>= 1.0.1), readr (>= 2.1.3), rlang (>= 1.0.6), - stringr (>= 1.5.0), semver (>= 0.2.0), + stringr (>= 1.5.0), tibble (>= 3.1.8), tidyr (>= 1.3.0), xml2 (>= 1.3.3) -Suggests: +Suggests: covr (>= 3.6.1), DT (>= 0.27), ggplot2 (>= 3.4.0), here (>= 1.0.1), knitr (>= 1.42), lattice (>= 0.20-45), + leafem (>= 0.2.0.9012), leaflet (>= 2.1.1), - listviewer (>= 3.0.0), leafpop (>= 0.1.0), - leafem (>= 0.2.0.9012), + listviewer (>= 3.0.0), mapview (>= 2.11.0.9005), rmarkdown (>= 2.20), roxygen2 (>= 7.2.3), sf (>= 1.0-9), + skimr (>= 2.1.5), terra (>= 1.7-3), testthat (>= 3.1.6), tmap (>= 3.3-3), usethis (>= 2.1.6) -VignetteBuilder: +VignetteBuilder: knitr -RdMacros: +RdMacros: lifecycle +Remotes: + r-spatial/leafem, + r-spatial/mapview Encoding: UTF-8 Language: en-AU LazyData: true -RoxygenNote: 7.3.1 -X-schema.org-applicationCategory: Data Access -X-schema.org-keywords: database, open-data, opendatakit, odk, api, data, dataset -Remotes: - r-spatial/leafem, - r-spatial/mapview Roxygen: list(markdown = TRUE) +RoxygenNote: 7.3.2 +X-schema.org-applicationCategory: Data Access +X-schema.org-keywords: database, open-data, opendatakit, odk, api, data, + dataset diff --git a/LICENSE.md b/LICENSE.md index cb9cf75a..b34de8e9 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,7 +1,7 @@ GNU General Public License ========================== -_Version 3, 29 June 2007_ +_Version 3, 29 June 2007_ _Copyright © 2007 Free Software Foundation, Inc. <>_ Everyone is permitted to copy and distribute verbatim copies of this license @@ -193,7 +193,7 @@ You may convey a work based on the Program, or the modifications to produce it f the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: -* **a)** The work must carry prominent notices stating that you modified it, and giving a +* **a)** The work must carry prominent notices stating that you modified it, and giving a relevant date. * **b)** The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the diff --git a/NAMESPACE b/NAMESPACE index 4d3257ef..c843baa1 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -11,6 +11,14 @@ export(encryption_key_list) export(enexpr) export(enquo) export(ensym) +export(entity_audits) +export(entity_changes) +export(entity_create) +export(entity_delete) +export(entity_detail) +export(entity_list) +export(entity_update) +export(entity_versions) export(entitylist_detail) export(entitylist_download) export(entitylist_list) @@ -25,6 +33,7 @@ export(form_schema_parse) export(form_xml) export(get_default_fid) export(get_default_odkc_version) +export(get_default_orders) export(get_default_pid) export(get_default_pp) export(get_default_pw) @@ -33,7 +42,7 @@ export(get_default_un) export(get_default_url) export(get_one_attachment) export(get_one_submission) -export(get_one_submission_attachment_list) +export(get_one_submission_att_list) export(get_one_submission_audit) export(get_retries) export(get_ru_verbose) diff --git a/NEWS.md b/NEWS.md index adcfaf69..81e92096 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,14 +1,16 @@ # ruODK 1.5.0 ## Major changes * Support Entities and Entity Lists (Datasets) (#152) +* Add pre-commit hooks and precommit.ci integration +* Add devcontainer # ruODK 1.4.2 -This release migrates the `ruODK` test suite to a new test server +This release migrates the `ruODK` test suite to a new test server `ruodk.getodk.cloud` which was generously sponsored by GetODK. This release makes `ruODK` aware of the new ODK Central semantic version format: Update your ODK Central version in `.Renviron` or in the credential -helper of your choice to the new format `ODKC_VERSION="2023.5.1"` (with your +helper of your choice to the new format `ODKC_VERSION="2023.5.1"` (with your current version). See the updated vignette "setup". ## Major changes @@ -16,14 +18,14 @@ current version). See the updated vignette "setup". * Migrate to new test server (#149) # ruODK 1.4.0 -This release fixes a few compatibility issues and bumps dependencies to R (4.1) -and imported/suggested packages. +This release fixes a few compatibility issues and bumps dependencies to R (4.1) +and imported/suggested packages. Upgrade carefully and revert to 1.3.12 if things go awry. -* Update to new `tidyselect` syntax: Using vectors of names to select makes - `tidyselect` complain (WARN, soon ERROR). We wrap all programmatic selections - of variable names in `dplyr::all_of()` where we expect a single variable to be - selected, and `dplyr::any_of()` where we select using fuzzy matching +* Update to new `tidyselect` syntax: Using vectors of names to select makes + `tidyselect` complain (WARN, soon ERROR). We wrap all programmatic selections + of variable names in `dplyr::all_of()` where we expect a single variable to be + selected, and `dplyr::any_of()` where we select using fuzzy matching (e.g. `dplyr::starts_with()`). (#146) * Make `ruODK::form_list()` robust against `reviewState` missing from outdated Central versions. (#145, HT @dpagendam and #143, HT @collinschwantes) @@ -52,7 +54,7 @@ Upgrade carefully and revert to 1.3.12 if things go awry. # `ruODK` 1.3.6 ## Minor fixes -* `odata_submission_get` supports parameter `expand` to expand all repeat +* `odata_submission_get` supports parameter `expand` to expand all repeat repetitions. # `ruODK` 1.3.5 @@ -69,7 +71,7 @@ This release supports ODK Central 1.3.0 and represents an over-due version bump to reflect the supported ODK Central version. The test server is now updated to ODK Central 1.3.0, and all tests pass. -There are still some newer and as yet unsupported API endpoints in ODK Central, +There are still some newer and as yet unsupported API endpoints in ODK Central, which serve administrative purposes of the front-end. Contributions are welcome, get started on [these issues](https://github.com/ropensci/ruODK/milestone/2) and the contributing guide. As ruODK focuses on data retrieval, these @@ -81,9 +83,9 @@ administrative endpoints are non-critical to ruODK's purpose. ## Data * Packaged data has been re-created to represent the latest server outputs. ## Maintenance -* All tests pass, GitHub Actions is as per usual brittle at the installation +* All tests pass, GitHub Actions is as per usual brittle at the installation step. - + # `ruODK` 1.2.0.0000 We are shaping up to a release targetting the ODK Central 1.2 release. ODK Central is undergoing some bug fixes and patches, while ruODK's test server @@ -122,7 +124,7 @@ which create/update/delete entities in ODK Central. # `ruODK` 0.10.2 ## Major fixes -* Fix ODK Central v1.2 time out on NULL query parameters `skip` and `top`. +* Fix ODK Central v1.2 time out on NULL query parameters `skip` and `top`. ruODK now only supplies non-NULL query parameters and has an additional seat-belt to drop any query parameter that is an empty string. (#126, thanks @yanokwa, @mtyszler, @thaliehln) @@ -133,8 +135,8 @@ which create/update/delete entities in ODK Central. # `ruODK` 0.10.1 ## Major fixes -* `submission_export` now terminates immediately if an incorrect passphrase is - given. ODK Central can exceed memory limits if `submission_export` is run +* `submission_export` now terminates immediately if an incorrect passphrase is + given. ODK Central can exceed memory limits if `submission_export` is run repeatedly with an incorrect passphrase. (#30, thanks @Thaliehln) ## Minor fixes * Add `encryption_key_list` @@ -159,7 +161,7 @@ which create/update/delete entities in ODK Central. # `ruODK` 0.9.11 ## Major fixes ## Minor fixes -* Add `published_at` to `form_list` and `form_detail`, update examples, tests, +* Add `published_at` to `form_list` and `form_detail`, update examples, tests, test fixtures to show that draft forms can be detected by a NA `published_at` in ODK Central versions having form drafts, and by NA `hash` and `version` in ODK Central versions before form drafts. @@ -168,7 +170,7 @@ which create/update/delete entities in ODK Central. ## Maintenance # `ruODK` 0.9.10 -This is a "everything so far works" release. +This is a "everything so far works" release. There are a few ODK Central API endpoints waiting to be implemented still. ## Major fixes @@ -180,18 +182,18 @@ There are a few ODK Central API endpoints waiting to be implemented still. ## Data ## Maintenance * Updated Zenodo archive at -* Updated Docker image at +* Updated Docker image at (RStudio server with ruODK) # `ruODK` 0.9.9 ## Major fixes * `submission_export` now supports ODK Central v1.1 features to omit media attachments (`media = FALSE`), and to omit repeat data (`include_repeats=FALSE`). - Calling `submission_export` on an older version of ODK Central + Calling `submission_export` on an older version of ODK Central (as determined through `get_default_odkc_version()`) with these new parameters will emit a (verbose only) "noop" message and not act further on them. ## Minor fixes -* Bugfix to `submission_export` on encrypted forms with multiple encryption +* Bugfix to `submission_export` on encrypted forms with multiple encryption keys. (Thanks @Thaliehln #117 #30) # `ruODK` 0.9.8 @@ -201,10 +203,10 @@ There are a few ODK Central API endpoints waiting to be implemented still. # `ruODK` 0.9.7 ## Major fixes -* `odata_submission_get()` bugfix: `handle_ru_attachments()` - now finds and downloads media attachments from both main submissions and +* `odata_submission_get()` bugfix: `handle_ru_attachments()` + now finds and downloads media attachments from both main submissions and nested form groups. (#114) -* `odata_submission_get()` bugfix: missing media attachments (due to upload +* `odata_submission_get()` bugfix: missing media attachments (due to upload error from ODK Collect to ODK Central) are tolerated without interrupting the batch download. A diagnostic warning message will be emitted for each failed download. (#114) @@ -217,15 +219,15 @@ There are a few ODK Central API endpoints waiting to be implemented still. ## Major fixes * Support encryption (#30 #110, @Thaliehln). * Note that `ruODK` only supports one passphrase. When switching between - multiple encrypted forms, it would make sense to store the different + multiple encrypted forms, it would make sense to store the different passphrases in separate environment variables, and refer to these environment variables explicitly in function calls. - * The updated ruODK::submission_export should now export data + * The updated ruODK::submission_export should now export data from both encrypted projects and non-encrypted projects. - The HTTP method is changed from GET to POST and encryption key ID / - passphrase are provided via POST body using a JSON format. + The HTTP method is changed from GET to POST and encryption key ID / + passphrase are provided via POST body using a JSON format. Encrypted forms can be extracted and inspected like non-encrypted forms: - + ```{r, eval=FALSE} se <- submission_export() t <- tempdir() @@ -250,7 +252,7 @@ sub %>% knitr::kable(.) * `form_schema_ext` performance enhancement (#106, thanks @mtyszler). ## Maintenance * Tests use vcr to cache server response (#104). - Delete the local cache `tests/fixtures` to re-generate the vcr cache, or + Delete the local cache `tests/fixtures` to re-generate the vcr cache, or enjoy much faster running tests using cached server response. # `ruODK` 0.9.3 @@ -259,18 +261,18 @@ This is a point release to create a new RStudio Server image. * Form schema now also works on draft forms (#103, HT @dmenne). ## Maintenance * Automated code reviews by . -* GitHub Actions welcomes Ubuntu 20.04 LTS and MacOS X back to the passing +* GitHub Actions welcomes Ubuntu 20.04 LTS and MacOS X back to the passing environments. # `ruODK` 0.9.2 ## Major fixes -* `form_schema_ext()` Shows the extended schema of one form, including +* `form_schema_ext()` Shows the extended schema of one form, including (multi-language) labels and choice lists. (#77, thanks @mtyszler for the PR) * Development continues in the default branch `main`. ## Minor fixes -* `form_list` now handles draft forms with `NA` hash and version (#86, +* `form_list` now handles draft forms with `NA` hash and version (#86, thanks @dmenne for the PR). -* Migrate package tests to a new ODK Central instance and update contributing +* Migrate package tests to a new ODK Central instance and update contributing guidelines with new settings. (#14) * Drop Import of `tidyselect` in favour of using `dplyr::all_of()`. * All calls to `httr::RETRY(times=)` are configurable via setting `retries`. (#94) @@ -285,22 +287,22 @@ a trailing empty coordinate. `ruODK` removes any trailing empty coordinates from both GeoJSON and WKT formats. (#88, HT @TimonWeitkamp for the bug report) ## Documentation -A new vignette "Spatial" demonstrates how to parse spatial data into native +A new vignette "Spatial" demonstrates how to parse spatial data into native formats, such as `sf`, and gives pointers on what to do next with them. - + # `ruODK` 0.9.0 -This is the release on passing +This is the release on passing [rOpenSci peer review](https://github.com/ropensci/software-review/issues/335). -Thanks to the rOpenSci editors and reviewers @maelle, @karissawhiting and +Thanks to the rOpenSci editors and reviewers @maelle, @karissawhiting and @jmt2080ad, as well as to @OdiljonQurbon, @dickoa, @arestrom and @dmenne. A DOI was minted at . # `ruODK` 0.6.6 -This version addresses ROpenSci reviewer comments from @karissawhiting and -@jmt2080ad, contributions from @dickoa, as well as ideas and suggestions from +This version addresses ROpenSci reviewer comments from @karissawhiting and +@jmt2080ad, contributions from @dickoa, as well as ideas and suggestions from @OdiljonQurbon, @arestrom and @dmenne. This version supports ODK Central 0.9 while providing backwards compatibility @@ -309,9 +311,9 @@ for ODK Central <= 0.7. ## Major fixes * New environment variables `ODKC_(TEST_)VERSION` allow `ruODK` to toggle between deprecated/removed and new/added API endpoints, e.g. `form_schema`. (#61) -* Split and rename WKT POINT (ODK geopoint) fields with +* Split and rename WKT POINT (ODK geopoint) fields with `odata_submission_get(wkt=T)`. (#31 #7 HT @OdiljonQurbon) -* `submission_get` now accepts a vector of (all or selected) submission instance +* `submission_get` now accepts a vector of (all or selected) submission instance IDs (`iid`), similar to `odata_submission_get()`. (#38) * All `httr::GET()` are now replaced with `httr::RETRY("GET", ...)` (#48) * Refactor `odata_submission_parse()` into `odata_submission_rectangle()`, @@ -323,12 +325,12 @@ for ODK Central <= 0.7. * Drop `. <- NULL` in favour of `utils::globalVariables(".")`. (#35) * Print settings now hides passwords but instructs how to show them. (#37) * `ru_setup()` now prints settings. (#37) -* `parse_datetime()` renamed to `ru_datetime()` to avoid naming conflict with +* `parse_datetime()` renamed to `ru_datetime()` to avoid naming conflict with `readr::parse_datetime()`. (#43) * Add a global default for verbosity. (#51 HT @arestrom) * Add a global default for time zone. (#53 HT @arestrom) * Use `httr::modify_url` to build URLs rather than `glue::glue` (#66) -* Silenced spurious messages from `tibble::as_tibble()` which is called from +* Silenced spurious messages from `tibble::as_tibble()` which is called from `odata_submission_rectangle()`. Use `ru_verbose` to toggle useful diagnostic messages. (#79 HT @dmenne) * Renamed `master` branch to `main`, updated docs (HT @arestrom #81) @@ -340,11 +342,11 @@ for ODK Central <= 0.7. latest bug fixes. ## Data -* Use canned data in all vignettes, so they can build without authentication. +* Use canned data in all vignettes, so they can build without authentication. (#33) * Update canned data (and `make-data.R`) to work with CI-built pkgdown site. (#52) -* Remove nested list `form_schema_raw` which is only generated from legacy +* Remove nested list `form_schema_raw` which is only generated from legacy ODK Central (< 0.8) (#61) * Added ODK Central < v0.7 form schema for tests. @@ -352,15 +354,15 @@ for ODK Central <= 0.7. * Updated workshop companion package [urODK](https://github.com/dbca-wa/urODK). * Rename vignettes to `odata-api` and `restful-api`. (#34) * Warn against using plain text credentials in vignette `setup`. (#34) -* More documentation improvements at +* More documentation improvements at [#34](https://github.com/ropensci/ruODK/issues/34). * Add screencast to the README. HT to asciicast! (#45) * Improve logo - more turtles, but questionable photoshopping. * Add examples where missing. (#32) * Build pkgdown site via GH actions. (#52) * Minor typographic changes: end every function title with a full stop. -* Broken links and other inconsistencies fixed after contributions from the - ODK Forum, @dickoa, @arestrom, @dmenne. +* Broken links and other inconsistencies fixed after contributions from the + ODK Forum, @dickoa, @arestrom, @dmenne. Thanks for the first community feedback! (#76 #74 #79 #80 #81) ## Docker @@ -370,7 +372,7 @@ for ODK Central <= 0.7. to launch a hosted RStudio instance in Binderhub. (#83) # `ruODK` 0.6.6 -* The big one has landed: `odata_submission_get()` now defaults to parse +* The big one has landed: `odata_submission_get()` now defaults to parse submissions from nested lists into a tibble, parse dates and datetimes, download and link attachments. (#6) @@ -378,11 +380,11 @@ for ODK Central <= 0.7. ## Documentation * Use lifecycle badges on functions. Add lifecycle to dependencies, version bump `usethis`. (#29) - + ## Code -* Refactor list wrangling code to use `map_*(.default=NA)`, removing some +* Refactor list wrangling code to use `map_*(.default=NA)`, removing some internal helpers (thanks to @jennybc) -* Use dummy imports to silence R CMD check NOTES as per [googledrive](https://github.com/tidyverse/googledrive/blob/050a982cba630503702bdde05a77d727baa36d48/R/googledrive-package.R)'s +* Use dummy imports to silence R CMD check NOTES as per [googledrive](https://github.com/tidyverse/googledrive/blob/050a982cba630503702bdde05a77d727baa36d48/R/googledrive-package.R)'s example (thanks to @jennybc) * Drop unused internal helper functions @@ -401,12 +403,12 @@ for ODK Central <= 0.7. * [tidyr 1.0.0](https://www.tidyverse.org/articles/2019/09/tidyr-1-0-0/) is out! Move `{tidyr}` dependency from GitHub master to CRAN version (#27) * Add `{usethis}` to Suggests, it is used in the setup step - + ## Documentation -* Add [David Henry](https://github.com/schemetrica)'s - [Pentaho Kettle tutorial](https://forum.opendatakit.org/t/automating-data-delivery-using-the-odata-endpoint-in-odk-central/22010) +* Add [David Henry](https://github.com/schemetrica)'s + [Pentaho Kettle tutorial](https://forum.opendatakit.org/t/automating-data-delivery-using-the-odata-endpoint-in-odk-central/22010) to the software review in the README (#28) - + ## DIY for workshops * Add inaugural RMarkdown template "odata" (#26) * Add [binder](https://mybinder.org/) launch button (one click start for #26) @@ -418,25 +420,25 @@ for ODK Central <= 0.7. moving project ID (pid) and form ID (fid) to kwargs. This changes all examples, tests, vignettes, READMEs. * Reduce installed package size by sharing attachment files. Add new parameter - `separate=FALSE` to `attachment_get` to prevent separating attachment files + `separate=FALSE` to `attachment_get` to prevent separating attachment files into subfolders named after their submission `uuid` (#22) - + ## Documentation * Add a high level overview diagram to README and `inst/joss/paper.md` to illustrate `ruODK`'s intended purpose in the ODK ecosystem (#19) -* Added link to explain - [environment variables and R startup](https://whattheyforgot.org/r-startup.html) +* Added link to explain + [environment variables and R startup](https://whattheyforgot.org/r-startup.html) to vignette "setup". @maelle * Add comparison of similar software to README (#25) # `ruODK` 0.6.1 -* ROpenSci submission review [milestone](https://github.com/ropensci/ruODK/milestone/3), +* ROpenSci submission review [milestone](https://github.com/ropensci/ruODK/milestone/3), [discussion](https://github.com/ropensci/software-review/issues/335). * Updates to documentation (#13 #15 #17) * Group function docs (#18) * Update contribution guidelines and add account request issue template: How to run `ruODK` tests and build the vignettes (#15 #20) -* Add dedicated `ru_setup()` and `ru_settings()`. +* Add dedicated `ru_setup()` and `ru_settings()`. Pat down functions for missing credentials and yell loudly but clearly about httr errors. (#16) * Drop `@importFrom` to reduce duplication. All external functions are prefixed @@ -461,29 +463,29 @@ for ODK Central <= 0.7. ## Preparation for ROpenSci pre-submission * Check name with [`available::available("ruODK")`](https://devguide.ropensci.org/building.html#naming-your-package): * Name valid: ✔ - * Available on CRAN: ✔ + * Available on CRAN: ✔ * Available on Bioconductor: ✔ - * Available on GitHub: ✔ + * Available on GitHub: ✔ * Rude misinterpretations: none * In summary: Package name seems to be OK. Well, ODK. OK, ruODK. -* Added metadata via +* Added metadata via [`codemetar::write_codemeta("ruODK")`](https://devguide.ropensci.org/building.html#creating-metadata-for-your-package). -* [Cross-platform](https://devguide.ropensci.org/building.html#platforms): +* [Cross-platform](https://devguide.ropensci.org/building.html#platforms): Runs on GNU/Linux (TravisCI) and on Windows (AppVeyor) * [Function naming](https://devguide.ropensci.org/building.html#function-and-argument-naming) - follows `object_verb`. - * `ruODK` uses verb singulars (e.g. `submission_list` to + follows `object_verb`. + * `ruODK` uses verb singulars (e.g. `submission_list` to list multiple submissions), while ODK Central's API URLs use verb plurals. * `ruODK` uses `snake_case`. - * Exception to `object_verb`: + * Exception to `object_verb`: Functions operating on the OData endpoints are named `odata_object_verb`. - Helper functions not related to API endpoints are named `verb_object`. + Helper functions not related to API endpoints are named `verb_object`. * [Code style](https://devguide.ropensci.org/building.html#code-style) done by `styler::style_package()`, see section "Release" in `README.md`. -* `ruODK` [has a `README.Rmd`](https://devguide.ropensci.org/building.html#readme) - and has a +* `ruODK` [has a `README.Rmd`](https://devguide.ropensci.org/building.html#readme) + and has a [website generated by `pkgdown`](https://devguide.ropensci.org/building.html#website). -* `ruODK` has documentation +* `ruODK` has documentation [generated by `roxygen2`](https://devguide.ropensci.org/building.html#documentation). * [Test coverage](https://devguide.ropensci.org/building.html#testing) 100%, but could use more edge cases. @@ -494,7 +496,7 @@ for ODK Central <= 0.7. function names to `inst/WORDLIST`. * Added citation and section in `README`. * Added `inst/joss/paper.md` for submission to JOSS. -* Added [examples](https://devguide.ropensci.org/building.html#examples) to +* Added [examples](https://devguide.ropensci.org/building.html#examples) to function docs. ## TODO @@ -506,12 +508,12 @@ for ODK Central <= 0.7. * Created vignette "Setup" * Add AppVeyor * Refactor storage path of attachments to not contain "uuid:" (for Windows compat) -* Started on REST API: `project_list`, `project_detail`, `form_list`, - `form_detail`. Naming scheme is `object_verb`. +* Started on REST API: `project_list`, `project_detail`, `form_list`, + `form_detail`. Naming scheme is `object_verb`. * For now, functions related to the OData endpoint are named `verb_object`, maybe we should rename them to `odata_object_verb`. * Refactor URLs - build from project and form IDs - + # `ruODK` 0.2.4 * Cleaned up logo, thanks to @issa-tseng for suggestions @@ -530,7 +532,7 @@ for ODK Central <= 0.7. and to handle attachments. # `ruODK` 0.1.0 -* Parses metadata, submissions, +* Parses metadata, submissions, and handles attachments (retaining already downloaded attachments). * Includes example forms as both `.xml` and `.odbbuild` in `inst/extdata`. * Includes example data as retrieved from ODK Central. diff --git a/R/attachment_list.R b/R/attachment_list.R index 0c5eb65f..ca45d130 100644 --- a/R/attachment_list.R +++ b/R/attachment_list.R @@ -41,7 +41,7 @@ #' #' sl <- submission_list() #' -#' al <- get_one_submission_attachment_list(sl$instance_id[[1]]) +#' al <- get_one_submission_att_list(sl$instance_id[[1]]) #' al %>% knitr::kable(.) #' #' # attachment_list returns a tibble @@ -52,13 +52,13 @@ #' names(al) #' # > "name" "exists" #' } -get_one_submission_attachment_list <- function(iid, - pid = get_default_pid(), - fid = get_default_fid(), - url = get_default_url(), - un = get_default_un(), - pw = get_default_pw(), - retries = get_retries()) { +get_one_submission_att_list <- function(iid, + pid = get_default_pid(), + fid = get_default_fid(), + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries()) { yell_if_missing(url, un, pw) httr::RETRY( "GET", @@ -114,7 +114,7 @@ get_one_submission_attachment_list <- function(iid, #' sl <- submission_list() #' #' # Step 3a: Get attachment list for first submission -#' al <- get_one_submission_attachment_list(sl$instance_id[[1]]) +#' al <- get_one_submission_att_list(sl$instance_id[[1]]) #' #' # Ste 3b: Get all attachments for all submissions #' all <- attachment_list(sl$instance_id) @@ -136,7 +136,7 @@ attachment_list <- function(iid, pw = pw, retries = retries ) %>% - purrr::pmap(ruODK::get_one_submission_attachment_list) %>% + purrr::pmap(ruODK::get_one_submission_att_list) %>% dplyr::bind_rows() } diff --git a/R/encryption_key_list.R b/R/encryption_key_list.R index 2c9800f1..eff1d70e 100644 --- a/R/encryption_key_list.R +++ b/R/encryption_key_list.R @@ -45,14 +45,7 @@ encryption_key_list <- function(pid = get_default_pid(), un = get_default_un(), pw = get_default_pw(), retries = get_retries(), - orders = c( - "YmdHMS", - "YmdHMSz", - "Ymd HMS", - "Ymd HMSz", - "Ymd", - "ymd" - ), + orders = get_default_orders(), tz = get_default_tz()) { yell_if_missing(url, un, pw) httr::RETRY( diff --git a/R/entity_audits.R b/R/entity_audits.R new file mode 100644 index 00000000..18bd7bd3 --- /dev/null +++ b/R/entity_audits.R @@ -0,0 +1,119 @@ +#' Return server audit logs of one Entity. +#' +#' `r lifecycle::badge("maturing")` +#' +#' +#' This returns +#' [Server Audit Logs](https://docs.getodk.org/central-api-system-endpoints/#server-audit-logs) +#' relating to an Entity. The most recent log is returned first. +#' +#' The authenticated user must have permissions on ODK Central to retrieve +#' audit logs. +#' +#' @template tpl-structure-nested +#' @template tpl-names-cleaned-top-level +#' @template tpl-auth-missing +#' @template tpl-compat-2022-3 +#' @template param-pid +#' @template param-did +#' @template param-eid +#' @template param-url +#' @template param-auth +#' @template param-retries +#' @template param-odkcv +#' @template param-orders +#' @template param-tz +#' @return A tibble with one row per audit log. +#' See +#' for the full schema. +#' Top level list elements are renamed from ODK's `camelCase` to `snake_case`. +# nolint start +#' @seealso \url{https://docs.getodk.org/central-api-entity-management/#entity-audit-log} +# nolint end +#' @family entity-management +#' @export +#' @examples +#' \dontrun{ +#' # See vignette("setup") for setup and authentication options +#' # ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") +#' +#' el <- entitylist_list() +#' +#' # Entity List name (dataset ID, did) +#' did <- el$name[1] +#' +#' # All Entities of Entity List +#' en <- entity_list(did = did) +#' +#' ed <- entity_detail(did = did, eid = en$uuid[1]) +#' +#' e_label <- ed$current_version$label +#' +#' # This example updates one field which exists in the example form. +#' # Your own Entity will have different fields to update. +#' e_data <- list( +#' details = paste0( +#' ed$current_version$data$details, ". Updated on ", Sys.time() +#' ) +#' ) +#' +#' # Update the Entity (implicitly forced update) +#' eu <- entity_update( +#' did = did, +#' eid = en$uuid[1], +#' label = e_label, +#' data = e_data +#' ) +#' +#' # Return the server audit logs +#' ea <- entity_audits(did = did, eid = en$uuid[1]) +#' ea +#' } +entity_audits <- function(pid = get_default_pid(), + did = "", + eid = "", + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = get_default_orders(), + tz = get_default_tz()) { + yell_if_missing(url, + un, + pw, + pid = pid, + did = did, + eid = eid + ) + + if (odkc_version |> semver_lt("2022.3")) { + ru_msg_warn("entity_audits is supported from v2022.3") + } + + pth <- glue::glue( + "v1/projects/{pid}/datasets/{URLencode(did, reserved = TRUE)}/", + "entities/{eid}/audits" + ) + + httr::RETRY( + "GET", + httr::modify_url(url, path = pth), + httr::add_headers( + "Accept" = "application/json", + "X-Extended-Metadata" = "true" + ), + httr::authenticate(un, pw), + times = retries + ) |> + yell_if_error(url, un, pw) |> + httr::content(encoding = "utf-8") |> + purrr::list_transpose() |> + tibble::as_tibble(.name_repair = "universal") |> + tidyr::unnest_wider("details", names_sep = "_") |> + tidyr::unnest_wider("notes", names_sep = "_") |> + tidyr::unnest_wider("actor", names_sep = "_") |> + janitor::clean_names() +} + +# usethis::use_test("entity_update") # nolint diff --git a/R/entity_changes.R b/R/entity_changes.R new file mode 100644 index 00000000..0baa7898 --- /dev/null +++ b/R/entity_changes.R @@ -0,0 +1,111 @@ +#' List changes to one Entity. +#' +#' `r lifecycle::badge("maturing")` +#' +#' This returns the changes, or edits, between different versions of an Entity. +#' These changes are returned as a list of lists. +#' Between two Entities, there is a list of objects representing how each +#' property changed. This change object contains the old and new values, as well +#' as the property name. +#' +#' @template tpl-structure-nested +#' @template tpl-names-cleaned-top-level +#' @template tpl-auth-missing +#' @template tpl-compat-2022-3 +#' @template param-pid +#' @template param-did +#' @template param-eid +#' @template param-url +#' @template param-auth +#' @template param-retries +#' @template param-odkcv +#' @template param-orders +#' @template param-tz +#' @return A nested list of lists. +#' The first nesting level corresponds to entity updates, the second level +#' lists each updated property. Within the nested list, the names are +#' `old` (old value), `new` (new value), `propertyName` (name of changed +#' entity property). +# nolint start +#' @seealso \url{https://docs.getodk.org/central-api-entity-management/#getting-changes-between-versions} +# nolint end +#' @family entity-management +#' @export +#' @examples +#' \dontrun{ +#' # See vignette("setup") for setup and authentication options +#' # ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") +#' +#' el <- entitylist_list() +#' +#' # Entity List name (dataset ID, did) +#' did <- el$name[1] +#' +#' # All Entities of Entity List +#' en <- entity_list(did = did) +#' +#' ed <- entity_detail(did = did, eid = en$uuid[1]) +#' +#' e_label <- ed$current_version$label +#' +#' # This example updates one field which exists in the example form. +#' # Your own Entity will have different fields to update. +#' e_data <- list( +#' details = paste0( +#' ed$current_version$data$details, ". Updated on ", Sys.time() +#' ) +#' ) +#' +#' # Update the Entity (implicitly forced update) +#' eu <- entity_update( +#' did = did, +#' eid = en$uuid[1], +#' label = e_label, +#' data = e_data +#' ) +#' +#' ec <- entity_changes(did = did, eid = en$uuid[1]) +#' ec +#' } +entity_changes <- function(pid = get_default_pid(), + did = "", + eid = "", + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = get_default_orders(), + tz = get_default_tz()) { + yell_if_missing(url, + un, + pw, + pid = pid, + did = did, + eid = eid + ) + + if (odkc_version |> semver_lt("2022.3")) { + ru_msg_warn("entity_changes is supported from v2022.3") + } + + pth <- glue::glue( + "v1/projects/{pid}/datasets/{URLencode(did, reserved = TRUE)}/", + "entities/{eid}/diffs" + ) + + httr::RETRY( + "GET", + httr::modify_url(url, path = pth), + httr::add_headers( + "Accept" = "application/json", + "X-Extended-Metadata" = "true" + ), + httr::authenticate(un, pw), + times = retries + ) |> + yell_if_error(url, un, pw) |> + httr::content(encoding = "utf-8") +} + +# usethis::use_test("entity_update") # nolint diff --git a/R/entity_create.R b/R/entity_create.R new file mode 100644 index 00000000..80a2e798 --- /dev/null +++ b/R/entity_create.R @@ -0,0 +1,203 @@ +#' Creates exactly one Entity in the Dataset. +#' +#' `r lifecycle::badge("experimental")` +#' +#' ### Creating a single Entity +#' +#' For creating a single Entity, include +#' +#' - An optional `uuid`. If skipped, Central will create a UUID for the Entity. +#' - The Entity `label` must be non-empty. +#' - A named list `data` representing the Entity's fields. The value type of +#' all properties is string. +#' +#' `uuid = "..."` +#' `label = "John Doe"` +#' `data = list("firstName" = "John", "age" = "22")` +#' +#' This translates to JSON +#' +#' `{ "label": "John Doe", "data": { "firstName": "John", "age": "22" } }` +#' +#' ### Creating multiple Entities +#' +#' For creating multiple Entities in bulk, the request body takes an array +#' entities containing a list of Entity objects as described above. +#' The bulk entity version also takes a source property with a required name +#' field and optional size, for example to capture the file name and size of a +#' bulk upload source (in MB). +#' +#' ``` +#' data=list( +#' "entities" = c( +#' list("label" = "Entity 1", "data" = list("field1" = "value1")), +#' list("label" = "Entity 2", "data" = list("field1" = "value2")) +#' ), +#' "source" = list("name" = "file.csv", "size" = 100) +#' ) +#' ``` +#' +#' This translates to JSON +#' +#' `{ "entities": [...], "source": {"name": "file.csv", "size": 100} }` +#' +#' +#' You can provide `notes` to store the metadata about the request. +#' The metadata is included in the POST request as header `X-Action-Notes` and +#' can retrieved using Entity Audit Log. +#' +#' +#' @template tpl-structure-nested +#' @template tpl-names-cleaned-top-level +#' @template tpl-auth-missing +#' @template tpl-compat-2022-3 +#' @template param-pid +#' @template param-did +#' @param label (character) The Entity label which must be a non-empty string. +#' If the label is given, a single entity is created using `data`, `notes`, +#' and `uuid` if given. +#' If the label is kept at the default (or omitted), multiple entities are +#' created using `data` and `notes` and ignoring `uuid`. +#' Default: `""`. +#' @param uuid (character) A single UUID to assign to the entity. +#' Default: `""`. With the default, Central will create and assign a UUID. +#' This parameter is only used when creating a single entity (`label` +#' non-empty) and ignored when creating multiple entities (`label` empty). +#' @param notes (character) Metadata about the request which can be retrieved +#' using the entity audit log. +#' Default: `""`. +#' @param data (list) A named list of Entity properties to create a single +#' Entity, or a nested list with an array of Entity data to create multiple +#' Entities. The nested lists representing individual entities must be valid +#' as in they must contain a label, valid data for the respective entity +#' properties, and can contain an optional UUID. +#' See details and the ODK documentation for the exact format. +#' Default: `list()`. +#' @template param-url +#' @template param-auth +#' @template param-retries +#' @template param-odkcv +#' @template param-orders +#' @template param-tz +#' @return A nested list identical to the return value of `entity_detail`. +#' See +#' for the full schema. +#' Top level list elements are renamed from ODK's `camelCase` to `snake_case`. +#' Nested list elements have the original `camelCase`. +# nolint start +#' @seealso \url{https://docs.getodk.org/central-api-entity-management/#creating-entities} +# nolint end +#' @family entity-management +#' @export +#' @examples +#' \dontrun{ +#' # See vignette("setup") for setup and authentication options +#' # ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") +#' +#' el <- entitylist_list() +#' +#' # Entity List name (dataset ID, did) +#' did <- el$name[1] +#' +#' # All Entities of Entity List +#' en <- entity_list(did = did) +#' +#' # Create a single entity +#' ec <- entity_create( +#' did = did, +#' label = "Entity label", +#' notes = "Metadata about the created entity", +#' data = list("field1" = "value1", "field2" = "value1") +#' ) +#' ec +#' +#' # Create multiple entities, example: test form "problems" +#' label <- c( +#' glue::glue( +#' "Entity {nrow(en) + 1} created by ruODK package test on {Sys.time()}" +#' ), +#' glue::glue( +#' "Entity {nrow(en) + 2} created by ruODK package test on {Sys.time()}" +#' ) +#' ) +#' notes <- glue::glue("Two entities created by ruODK package test on {Sys.time()}") +#' status <- c("needs_followup", "needs_followup") +#' details <- c("ruODK package test", "ruODK package test") +#' geometry <- c("-33.2 115.0 0.0 0.0", "-33.2 115.0 0.0 0.0") +#' data <- data.frame(status, details, geometry, stringsAsFactors = FALSE) +#' request_data <- list( +#' "entities" = data.frame(label, data = I(data), stringsAsFactors = FALSE), +#' "source" = list("name" = "file.csv", "size" = 100) +#' ) +#' +#' # Compare request_data to the schema expected by Central +#' # https://docs.getodk.org/central-api-entity-management/#creating-entities +#' # jsonlite::toJSON(request_data, pretty = TRUE, auto_unbox = TRUE) +#' +#' ec <- entity_create( +#' did = did, +#' notes = notes, +#' data = request_data +#' ) +#' } +entity_create <- function(pid = get_default_pid(), + did = "", + label = "", + uuid = "", + notes = "", + data = list(), + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = get_default_orders(), + tz = get_default_tz()) { + yell_if_missing(url, un, pw, pid = pid, did = did) + + if (odkc_version |> semver_lt("2022.3")) { + ru_msg_warn("entity_create is supported from v2022.3") + } + + # Triage whether data is single or multiple entity + body <- list() + + if (label == "") { + ru_msg_info("Empty label: creating multiple entities.") + + # Gate check: data must have key "entities" + if (!("entities" %in% names(data))) { + ru_msg_abort("Malformed argument 'data': Must include 'entities'.") + } + + body <- data + } else { + ru_msg_info("Non-empty label: creating single entity.") + # If uuid is not NULL, include in body + if (uuid != "") { + body <- list("uuid" = uuid, "label" = label, "data" = data) + } else { + body <- list("label" = label, "data" = data) + } + } + + pth <- glue::glue( + "v1/projects/{pid}/datasets/{URLencode(did, reserved = TRUE)}/", + "entities/" + ) + + httr::RETRY( + "POST", + httr::modify_url(url, path = pth), + httr::add_headers("Accept" = "application/json", "X-Action-Notes" = notes), + encode = "json", + body = body, + httr::authenticate(un, pw), + times = retries + ) |> + yell_if_error(url, un, pw) |> + httr::content(encoding = "utf-8") |> + janitor::clean_names() +} + +# usethis::use_test("entity_create") # nolint diff --git a/R/entity_delete.R b/R/entity_delete.R new file mode 100644 index 00000000..2c039a82 --- /dev/null +++ b/R/entity_delete.R @@ -0,0 +1,80 @@ +#' Delete one Entity. +#' +#' `r lifecycle::badge("maturing")` +#' +#' +#' This function soft-deletes one Entity, , which means it is still in Central's +#' database and you can retrieve it via `entity_list(deleted=TRUE)`. +#' +#' @template tpl-structure-nested +#' @template tpl-names-cleaned-top-level +#' @template tpl-auth-missing +#' @template tpl-compat-2022-3 +#' @template param-pid +#' @template param-did +#' @template param-eid +#' @template param-url +#' @template param-auth +#' @template param-retries +#' @template param-odkcv +#' @template param-orders +#' @template param-tz +#' @return A list with the key "success" (lgl) indicating whether the entity +#' was deleted. +# nolint start +#' @seealso \url{https://docs.getodk.org/central-api-entity-management/#deleting-an-entity} +# nolint end +#' @family entity-management +#' @export +#' @examples +#' \dontrun{ +#' # See vignette("setup") for setup and authentication options +#' # ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") +#' +#' el <- entitylist_list() +#' +#' # Entity List name (dataset ID, did) +#' did <- el$name[1] +#' +#' # All Entities of Entity List +#' en <- entity_list(did = did) +#' +#' # View details of one Entity +#' ed <- entity_detail(did = did, eid = en$uuid[1]) +#' +#' # Delete the Entity +#' ed_deleted <- entity_delete(did = did, eid = ed$id) +#' } +entity_delete <- function(pid = get_default_pid(), + did = "", + eid = "", + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = get_default_orders(), + tz = get_default_tz()) { + yell_if_missing(url, un, pw, pid = pid, did = did, eid = eid) + + if (odkc_version |> semver_lt("2022.3")) { + ru_msg_warn("entity_detail is supported from v2022.3") + } + + pth <- glue::glue( + "v1/projects/{pid}/datasets/{URLencode(did, reserved = TRUE)}/entities/{eid}" + ) + + httr::RETRY( + "DELETE", + httr::modify_url(url, path = pth), + httr::add_headers("Accept" = "application/json"), + httr::authenticate(un, pw), + times = retries + ) |> + yell_if_error(url, un, pw) |> + httr::content(encoding = "utf-8") |> + janitor::clean_names() +} + +# usethis::use_test("entity_create") # nolint diff --git a/R/entity_detail.R b/R/entity_detail.R new file mode 100644 index 00000000..58efc4a8 --- /dev/null +++ b/R/entity_detail.R @@ -0,0 +1,117 @@ +#' Show metadata and current data of one Entity. +#' +#' `r lifecycle::badge("maturing")` +#' +#' +#' This function returns the metadata and current data of an Entity. +#' +#' The data and label of an Entity are found in the `current_version` columns +#' under data and label respectively. +#' The `current_version` column contains version-specific metadata fields +#' such as version, baseVersion, details about conflicts that version +#' introduced, etc. More details are available from `entity_versions()`. +#' The Entity also contains top-level system metadata such as `uuid`, +#' `created_at` and `updated_at` timestamps, and `creator_id` (or full creator +#' if extended metadata is requested). +#' +#' ## Conflicts +#' An Entity's metadata also has a conflict field, which indicates the current +#' conflict status of the Entity. The value of a conflict can either be: +#' +#' - null. The Entity does not have conflicting versions. +#' - soft. The Entity has a version that was based on a version other than the +#' version immediately prior to it. (The specified `base_version` was not the +#' latest version on the server.) +#' - hard. The Entity has a version that was based on a version other than the +#' version immediately prior to it. Further, that version updated the same +#' property as another update. +#' +#' If an Entity has a conflict, it can be marked as resolved. +#' After that, the conflict field will be null until a new conflicting version +#' is received. +#' +#' @template tpl-structure-nested +#' @template tpl-names-cleaned-top-level +#' @template tpl-auth-missing +#' @template tpl-compat-2022-3 +#' @template param-pid +#' @template param-did +#' @template param-eid +#' @template param-url +#' @template param-auth +#' @template param-retries +#' @template param-odkcv +#' @template param-orders +#' @template param-tz +#' @return A nested list identical to the return value of `entity_update`. +#' See +#' for the full schema. +#' Top level list elements are renamed from ODK's `camelCase` to `snake_case`. +#' Nested list elements have the original `camelCase`. +# nolint start +#' @seealso \url{https://docs.getodk.org/central-api-entity-management/#entities-metadata} +# nolint end +#' @family entity-management +#' @export +#' @examples +#' \dontrun{ +#' # See vignette("setup") for setup and authentication options +#' # ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") +#' +#' el <- entitylist_list() +#' +#' # Entity List name (dataset ID, did) +#' did <- el$name[1] +#' +#' # All Entities of Entity List +#' en <- entity_list(did = did) +#' +#' ed <- entity_detail(did = did, eid = en$uuid[1]) +#' +#' # The current version of the first Entity +#' ev <- en$current_version_version[1] +#' } +entity_detail <- function(pid = get_default_pid(), + did = "", + eid = "", + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = get_default_orders(), + tz = get_default_tz()) { + yell_if_missing(url, un, pw, pid = pid, did = did, eid = eid) + + if (odkc_version |> semver_lt("2022.3")) { + ru_msg_warn("entity_detail is supported from v2022.3") + } + + pth <- glue::glue( + "v1/projects/{pid}/datasets/{URLencode(did, reserved = TRUE)}/entities/{eid}" + ) + + httr::RETRY( + "GET", + httr::modify_url(url, path = pth), + httr::add_headers("Accept" = "application/json"), + httr::authenticate(un, pw), + times = retries + ) |> + yell_if_error(url, un, pw) |> + httr::content(encoding = "utf-8") |> + # purrr::list_transpose() |> + # tibble::enframe() |> + # tibble::as_tibble(.name_repair = "universal") |> + # tidyr::pivot_wider(names_from = "name", values_from = "value") |> + # tidyr::unnest_wider("conflict", names_sep = "_") |> + # janitor::clean_names() |> + # tidyr::unnest_wider("current_version", names_sep = "_") |> + janitor::clean_names() + # dplyr::mutate_at( + # dplyr::vars(dplyr::contains("_at")), + # ~ isodt_to_local(., orders = orders, tz = tz) + # ) +} + +# usethis::use_test("entity_detail") # nolint diff --git a/R/entity_list.R b/R/entity_list.R new file mode 100644 index 00000000..877235ee --- /dev/null +++ b/R/entity_list.R @@ -0,0 +1,97 @@ +#' List all Entities of a kind. +#' +#' `r lifecycle::badge("maturing")` +#' +#' This function returns a list of the Entities of a kind (belonging to an +#' Entity List or Dataset). +#' Please note that this endpoint only returns metadata of the entities, not the +#' data. If you want to get the data of all entities then please refer to the +#' OData Dataset Service. +#' +#' You can get only deleted entities with `deleted=TRUE`. +#' +#' @template tpl-auth-missing +#' @template tpl-compat-2022-3 +#' @template param-pid +#' @template param-did +#' @param deleted (lgl) Whether to get only deleted entities (`TRUE`) or not +#' (`FALSE`). Default: `FALSE`. +#' @template param-url +#' @template param-auth +#' @template param-retries +#' @template param-odkcv +#' @template param-orders +#' @template param-tz +#' @return A tibble with exactly one row for each Entity of the given project +#' as per ODK Central API docs. +#' Column names are renamed from ODK's `camelCase` to `snake_case`. +# nolint start +#' @seealso \url{https://docs.getodk.org/central-api-entity-management/#entities-metadata} +# nolint end +#' @family entity-management +#' @export +#' @examples +#' \dontrun{ +#' # See vignette("setup") for setup and authentication options +#' # ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") +#' +#' el <- entitylist_list() +#' +#' # Entity List name (dataset ID) +#' did <- el$name[1] +#' +#' # All Entities of Entity List +#' en <- entity_list(did = el$name[1]) +#' +#' # The UUID of the first Entity +#' eid <- en$uuid[1] +#' +#' # The current version of the first Entity +#' ev <- en$current_version_version[1] +#' } +entity_list <- function(pid = get_default_pid(), + did = "", + deleted = FALSE, + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = get_default_orders(), + tz = get_default_tz()) { + yell_if_missing(url, un, pw, pid = pid, did = did) + + if (odkc_version |> semver_lt("2022.3")) { + ru_msg_warn("entity_list is supported from v2022.3") + } + + pth <- glue::glue( + "v1/projects/{pid}/datasets/{URLencode(did, reserved = TRUE)}/entities" + ) + + if (deleted == TRUE) { + pth <- glue::glue("{pth}?deleted=true") + } + + httr::RETRY( + "GET", + httr::modify_url(url, path = pth), + httr::add_headers("Accept" = "application/json"), + httr::authenticate(un, pw), + times = retries + ) |> + yell_if_error(url, un, pw) |> + httr::content(encoding = "utf-8") |> + purrr::list_transpose() |> + tibble::as_tibble() |> + janitor::clean_names() |> + tidyr::unnest_wider("current_version", names_sep = "_") |> + tidyr::unnest_wider("conflict", names_sep = "_") |> + janitor::clean_names() |> + dplyr::mutate_at( + dplyr::vars(dplyr::contains("_at")), + ~ isodt_to_local(., orders = orders, tz = tz) + ) +} + +# usethis::use_test("entity_list") # nolint diff --git a/R/entity_update.R b/R/entity_update.R new file mode 100644 index 00000000..acfadadc --- /dev/null +++ b/R/entity_update.R @@ -0,0 +1,173 @@ +#' Update one Entity. +#' +#' `r lifecycle::badge("experimental")` +#' +#' This endpoint is used to update the label or the properties (passed as JSON +#' in the request body) of an Entity. +#' You only need to include the properties you wish to update. +#' To unset the value of any property, you can set it to empty string (""). +#' The label must be a non-empty string. +#' Setting a property to null will throw an error. +#' Attempting to update a property that doesn't exist in the Dataset will throw +#' an error. +#' +#' ## Specifying a base version +#' You must either provide a `base_version` or use `force=TRUE` query parameter. +#' You cannot cause a new Entity conflict via the API, which is why when +#' specifying `base_version`, it must match the current version of the Entity on +#' the server. This acts as a check to ensure you are not trying to update based +#' on stale data. If you wish to update the Entity regardless of the current +#' state, then you can use the force flag. +#' +#' ## Resolving a conflict +#' You can also use this endpoint to resolve an Entity conflict by passing +#' `resolve=true`, in which case providing `data` is optional. +#' When not providing new data, only the conflict status from +#' the Entity will be cleared and no new version will be created. +#' When providing data, the conflict will be cleared and an updated version of +#' the Entity will be added. +#' +#' @template tpl-structure-nested +#' @template tpl-names-cleaned-top-level +#' @template tpl-auth-missing +#' @template tpl-compat-2022-3 +#' @template param-pid +#' @template param-did +#' @template param-eid +#' @param label (character) The Entity label which must be a non-empty string. +#' Default: `""`. +#' @param data (list) A named list of Entity properties to update. See details. +#' Default: `list()`. +#' @param base_version If given, must be the current version of the Entity on +#' the server. Optional. +#' @param force (lgl) Whether to force an update. Defaults to be `FALSE` if a +#' `base_version` is specified, else defaults to `TRUE`. +#' Using `force=TRUE` and specifying a `base_version` will emit a warning. +#' @param resolve (lgl) +#' @template param-url +#' @template param-auth +#' @template param-retries +#' @template param-odkcv +#' @template param-orders +#' @template param-tz +#' @return A nested list identical to the return value of `entity_detail`. +#' See +#' for the full schema. +#' Top level list elements are renamed from ODK's `camelCase` to `snake_case`. +#' Nested list elements have the original `camelCase`. +# nolint start +#' @seealso \url{https://docs.getodk.org/central-api-entity-management/#updating-an-entity} +# nolint end +#' @family entity-management +#' @export +#' @examples +#' \dontrun{ +#' # See vignette("setup") for setup and authentication options +#' # ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") +#' +#' el <- entitylist_list() +#' +#' # Entity List name (dataset ID, did) +#' did <- el$name[1] +#' +#' # All Entities of Entity List +#' en <- entity_list(did = did) +#' +#' ed <- entity_detail(did = did, eid = en$uuid[1]) +#' +#' e_label <- ed$current_version$label +#' +#' # This example updates one field which exists in the example form. +#' # Your own Entity will have different fields to update. +#' e_data <- list( +#' details = paste0( +#' ed$current_version$data$details, ". Updated on ", Sys.time() +#' ) +#' ) +#' +#' # Update the Entity (implicitly forced update) +#' eu <- entity_update( +#' did = did, +#' eid = en$uuid[1], +#' label = e_label, +#' data = e_data +#' ) +#' eu +#' } +entity_update <- function(pid = get_default_pid(), + did = "", + eid = "", + label = "", + data = list(), + base_version = NULL, + force = is.null(base_version), + resolve = FALSE, + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = get_default_orders(), + tz = get_default_tz()) { + yell_if_missing(url, un, pw, pid = pid, did = did, eid = eid) + + if (odkc_version |> semver_lt("2022.3")) { + ru_msg_warn("entity_detail is supported from v2022.3") + } + + if (label == "") { + ru_msg_abort("The Entity label must be given as a non-empty string.") + } + + if (force == TRUE && !is.null(base_version)) { + ru_msg_warn( + glue::glue( + "You have specified force=TRUE and provided a base_version ", + "{base_version}. force=TRUE forces an update, while base_version is ", + "used to check that you are updating the latest version on the server.", + " Most use cases need only one of these two parameters." + ) + ) + } + + force_val <- ifelse(force == TRUE, "true", "false") + resolve_val <- ifelse(resolve == TRUE, "true", "false") + + pth <- glue::glue( + "v1/projects/{pid}/datasets/{URLencode(did, reserved = TRUE)}/", + "entities/{eid}?force={force_val}&resolve={resolve_val}" + ) + + if (!is.null(base_version)) { + if (!is.integer(as.integer(base_version))) { + ru_msg_abort("base_version must be an integer.") + } + pth <- glue::glue("{pth}&baseVersion={as.integer(base_version)}") + } + + httr::RETRY( + "PATCH", + httr::modify_url(url, path = pth), + httr::add_headers("Accept" = "application/json"), + encode = "json", + body = list(label = as.character(label), data = data), + httr::authenticate(un, pw), + times = retries + ) |> + yell_if_error(url, un, pw) |> + httr::content(encoding = "utf-8") |> + # purrr::list_transpose() |> + # tibble::enframe() |> + # tibble::as_tibble(.name_repair = "universal") |> + # tidyr::pivot_wider(names_from = "name", values_from = "value") |> + # tidyr::unnest_wider("conflict", names_sep = "_") |> + # janitor::clean_names() |> + # tidyr::unnest_wider("current_version", names_sep = "_") |> + janitor::clean_names() + # dplyr::mutate_at( + # dplyr::vars(dplyr::contains("_at")), + # ~ isodt_to_local(., orders = orders, tz = tz) + # ) +} + +# usethis::use_test("entity_update") # nolint diff --git a/R/entity_versions.R b/R/entity_versions.R new file mode 100644 index 00000000..d9af7c9d --- /dev/null +++ b/R/entity_versions.R @@ -0,0 +1,117 @@ +#' List versions of one Entity. +#' +#' `r lifecycle::badge("maturing")` +#' +#' +#' This returns the Entity metadata and data for every version of this Entity +#' in ascending creation order. +#' +#' The ODK Central endpoint supports retrieving extended metadata which this +#' function always returns. +#' +#' There is an optional query flag `relevantToConflict` that returns the subset +#' of past versions of an Entity that are relevant to the Entity's current +#' conflict. This includes the latest version, the base version, the previous +#' server version, and any other versions since the last time the Entity was +#' in a conflict-free state. If the Entity is not in conflict, zero versions +#' are returned. +#' +#' @template tpl-structure-nested +#' @template tpl-names-cleaned-top-level +#' @template tpl-auth-missing +#' @template tpl-compat-2022-3 +#' @template param-pid +#' @template param-did +#' @template param-eid +#' @param conflict (lgl) Whether to return all versions (`FALSE`) or +#' whether to returns the subset of past versions of an Entity that are +#' relevant to the Entity's current conflict (`TRUE`). +#' This includes the latest version, the base version, the previous +#' server version, and any other versions since the last time the Entity was +#' in a conflict-free state. If the Entity is not in conflict, zero versions +#' are returned. +#' Default: `FALSE` (return all versions) +#' @template param-url +#' @template param-auth +#' @template param-retries +#' @template param-odkcv +#' @template param-orders +#' @template param-tz +#' @return A tibble with one row per version. List columns contain unstructured +#' data. +#' See +#' for the full schema. +#' Top level list elements are renamed from ODK's `camelCase` to `snake_case`. +#' Nested list elements have the original `camelCase`. +# nolint start +#' @seealso \url{https://docs.getodk.org/central-api-entity-management/#listing-versions} +# nolint end +#' @family entity-management +#' @export +#' @examples +#' \dontrun{ +#' # See vignette("setup") for setup and authentication options +#' # ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") +#' +#' el <- entitylist_list() +#' +#' # Entity List name (dataset ID, did) +#' did <- el$name[1] +#' +#' # All Entities of Entity List +#' en <- entity_list(did = did) +#' +#' ed <- entity_detail(did = did, eid = en$uuid[1]) +#' +#' # The current version of the first Entity +#' ev <- en$current_version_version[1] +#' } +entity_versions <- function(pid = get_default_pid(), + did = "", + eid = "", + conflict = FALSE, + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = get_default_orders(), + tz = get_default_tz()) { + yell_if_missing(url, + un, + pw, + pid = pid, + did = did, + eid = eid + ) + + if (odkc_version |> semver_lt("2022.3")) { + ru_msg_warn("entity_detail is supported from v2022.3") + } + + pth <- glue::glue( + "v1/projects/{pid}/datasets/{URLencode(did, reserved = TRUE)}/", + "entities/{eid}/versions" + ) + + qry <- list(relevantToConflict = ifelse(conflict == TRUE, "True", "False")) + + httr::RETRY( + "GET", + httr::modify_url(url, path = pth), + httr::add_headers( + "Accept" = "application/json", + "X-Extended-Metadata" = "true" + ), + httr::authenticate(un, pw), + query = qry, + times = retries + ) |> + yell_if_error(url, un, pw) |> + httr::content(encoding = "utf-8") |> + purrr::list_transpose() |> + tibble::as_tibble(.name_repair = "universal") |> + janitor::clean_names() +} + +# usethis::use_test("entity_update") # nolint diff --git a/R/entitylist_detail.R b/R/entitylist_detail.R index 78a770ee..89561e21 100644 --- a/R/entitylist_detail.R +++ b/R/entitylist_detail.R @@ -2,14 +2,12 @@ #' #' `r lifecycle::badge("maturing")` #' -#' An Entity List is a named collection of Entities that have the same -#' properties. -#' Entity List can be linked to Forms as Attachments. -#' This will make it available to clients as an automatically-updating CSV. -#' -#' This function is supported from ODK Central v2022.3 and will warn if the -#' given odkc_version is lower. -#' +#' @template tpl-structure-nested +#' @template tpl-names-cleaned-top-level +#' @template tpl-def-entitylist +#' @template tpl-entitylist-dataset +#' @template tpl-auth-missing +#' @template tpl-compat-2022-3 #' @template param-pid #' @template param-did #' @template param-url @@ -32,40 +30,33 @@ #' # ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") #' #' ds <- entitylist_list(pid = get_default_pid()) +#' #' ds1 <- entitylist_detail(pid = get_default_pid(), did = ds$name[1]) #' #' ds1 |> listviewer::jsonedit() -#' ds1$linkedForms |> +#' +#' ds1$linked_forms |> #' purrr::list_transpose() |> #' tibble::as_tibble() -#' ds1$sourceForms |> +#' +#' ds1$source_forms |> #' purrr::list_transpose() |> #' tibble::as_tibble() +#' #' ds1$properties |> #' purrr::list_transpose() |> #' tibble::as_tibble() #' } entitylist_detail <- function(pid = get_default_pid(), - did = NULL, + did = "", url = get_default_url(), un = get_default_un(), pw = get_default_pw(), retries = get_retries(), odkc_version = get_default_odkc_version(), - orders = c( - "YmdHMS", - "YmdHMSz", - "Ymd HMS", - "Ymd HMSz", - "Ymd", - "ymd" - ), + orders = get_default_orders(), tz = get_default_tz()) { - yell_if_missing(url, un, pw, pid = pid) - - if (is.null(did)) { - ru_msg_abort("entitylist_detail requires the Entity List name as 'did=\"name\"'.") - } + yell_if_missing(url, un, pw, pid = pid, did = did) if (odkc_version |> semver_lt("2022.3")) { ru_msg_warn("entitylist_detail is supported from v2022.3") @@ -80,14 +71,14 @@ entitylist_detail <- function(pid = get_default_pid(), ) ), httr::add_headers( - "Accept" = "application/json", - "X-Extended-Metadata" = "true" + "Accept" = "application/json" ), httr::authenticate(un, pw), times = retries ) |> yell_if_error(url, un, pw) |> - httr::content(encoding = "utf-8") + httr::content(encoding = "utf-8") |> + janitor::clean_names() } # usethis::use_test("entitylist_detail") # nolint diff --git a/R/entitylist_download.R b/R/entitylist_download.R index f1b4da61..3342d77a 100644 --- a/R/entitylist_download.R +++ b/R/entitylist_download.R @@ -2,33 +2,33 @@ #' #' `r lifecycle::badge("maturing")` #' +#' ## CSV file #' The downloaded CSV file is named after the entity list name. #' The download location defaults to the current workdir, but can be modified #' to a different folder path which will be created if it doesn't exist. #' -#' An Entity List is a named collection of Entities that have the same -#' properties. -#' Entity List can be linked to Forms as Attachments. -#' This will make it available to clients as an automatically-updating CSV. -#' #' Entity Lists can be used as Attachments in other Forms, but they can also be #' downloaded directly as a CSV file. +#' #' The CSV format closely matches the OData Dataset (Entity List) Service #' format, with columns for system properties such as `__id` (the Entity UUID), -#' `__createdAt`, `__creatorName`, etc., the Entity Label label, and the -#' Dataset (Entity List )/Entity Properties themselves. +#' `__createdAt`, `__creatorName`, etc., the Entity Label, and the +#' Dataset (Entity List) or Entity Properties themselves. #' If any Property for an given Entity is blank (e.g. it was not captured by #' that Form or was left blank), that field of the CSV is blank. #' -#' The ODK Central `$filter` querystring parameter can be used to filter on +#' ## Filter +#' The ODK Central `$filter` query string parameter can be used to filter on #' system-level properties, similar to how filtering in the OData Dataset #' (Entity List) Service works. -#' Of the [OData filter specs](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#_Toc31358948) +#' Of the [OData filter specs +#' ](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#_Toc31358948) #' ODK Central implements a [growing set of features #' ](https://docs.getodk.org/central-api-odata-endpoints/#data-document). #' `ruODK` provides the parameter `filter` (str) which, if set, will be passed #' on to the ODK Central endpoint as is. #' +#' ## Resuming downloads through ETag #' The ODK Central endpoint supports the [`ETag` header #' ](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag), which can #' be used to avoid downloading the same content more than once. @@ -44,6 +44,10 @@ #' a previous call to `entitylist_download()`. `ruODK` strips the `W/\"` and #' `\"` from the returned etag and expects the stripped etag as parameter. #' +#' @template tpl-def-entitylist +#' @template tpl-entitylist-dataset +#' @template tpl-auth-missing +#' @template tpl-compat-2022-3 #' @template param-pid #' @template param-did #' @template param-url @@ -56,11 +60,11 @@ #' which is the format of the etag returned by `entitylist_download()`. #' If provided, only new entities will be returned. #' If the same `local_dir` is chosen and `overwrite` is set to `TRUE`, -#' the downloaded CSV will also be overwritte, losing the Entities downloaded -#' earlier. -#' Default: NULL (no filtering, all entities returned). +#' the downloaded CSV will also be overwritten, losing the previously +#' downloaded Entities. +#' Default: NULL (no filtering, all Entities returned). #' @param filter (str) A valid filter string. -#' Default: NULL (no filtering, all entities returned). +#' Default: NULL (no filtering, all Entities returned). #' @param overwrite Whether to overwrite previously downloaded file, #' default: FALSE #' @template param-retries @@ -74,7 +78,7 @@ #' 200 if OK, 304 if a given etag finds no new entities created. #' - etag (str) The ETag to use in subsequent calls to `entitylist_download()` #' - downloaded_to (fs_path) The path to the downloaded CSV file -#' - downloaded_on (POSIXct) The time of download in the local timezome +#' - downloaded_on (POSIXct) The time of download in the local timezone # nolint start #' @seealso \url{https://docs.getodk.org/central-api-dataset-management/#datasets} # nolint end @@ -86,6 +90,7 @@ #' # ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") #' #' ds <- entitylist_list(pid = get_default_pid()) +#' #' ds1 <- entitylist_download(pid = get_default_pid(), did = ds$name[1]) #' # ds1$entities #' # ds1$etag @@ -107,7 +112,7 @@ #' ) #' } entitylist_download <- function(pid = get_default_pid(), - did = NULL, + did = "", url = get_default_url(), un = get_default_un(), pw = get_default_pw(), @@ -117,24 +122,11 @@ entitylist_download <- function(pid = get_default_pid(), overwrite = TRUE, retries = get_retries(), odkc_version = get_default_odkc_version(), - orders = c( - "YmdHMS", - "YmdHMSz", - "Ymd HMS", - "Ymd HMSz", - "Ymd", - "ymd" - ), + orders = get_default_orders(), tz = get_default_tz(), verbose = get_ru_verbose()) { # Gatecheck params - yell_if_missing(url, un, pw, pid = pid) - - if (is.null(did)) { - ru_msg_abort( - "entitylist_download requires the Entity List name as 'did=\"name\"'." - ) - } + yell_if_missing(url, un, pw, pid = pid, did = did) # Gatecheck ODKC version if (odkc_version |> semver_lt("2022.3")) { diff --git a/R/entitylist_list.R b/R/entitylist_list.R index 66dc833a..7a9855db 100644 --- a/R/entitylist_list.R +++ b/R/entitylist_list.R @@ -2,21 +2,13 @@ #' #' `r lifecycle::badge("maturing")` #' -#' While the API endpoint will return all Entity Lists for one Project, -#' \code{\link{entitylist_list}} will fail with incorrect or missing -#' authentication. -#' -#' An Entity List is a named collection of Entities that have the same properties. -#' An Entity List can be linked to Forms as Attachments. -#' This will make it available to clients as an automatically-updating CSV. -#' -#' ODK Central calls Entity Lists internally Datasets. `ruODK` chooses the term -#' Entity Lists as it is used in the ODK Central user interface and conveys -#' its relation to Entities better than the term Dataset. -#' -#' This function is supported from ODK Central v2022.3 and will warn if the -#' given odkc_version is lower. +#' The returned list is useful to retrieve the valid name of an Entity List for +#' further use by functions of the Entity Management family. #' +#' @template tpl-def-entitylist +#' @template tpl-entitylist-dataset +#' @template tpl-auth-missing +#' @template tpl-compat-2022-3 #' @template param-pid #' @template param-url #' @template param-auth @@ -24,9 +16,9 @@ #' @template param-odkcv #' @template param-orders #' @template param-tz -#' @return A tibble with exactly one row for each dataset of the given project -#' as per ODK Central API docs. -#' Column names are renamed from ODK's `camelCase` to `snake_case`. +#' @return A tibble with exactly one row for each Entity List of the given +#' Project as per ODK Central API docs. +#' Column names are renamed from ODK Central's `camelCase` to `snake_case`. # nolint start #' @seealso \url{ https://docs.getodk.org/central-api-dataset-management/#datasets} # nolint end @@ -35,7 +27,7 @@ #' @examples #' \dontrun{ #' # See vignette("setup") for setup and authentication options -#' # ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") +#' # ruODK::ru_setup(svc = "... .svc", un = "me@email.com", pw = "...") #' #' ds <- entitylist_list(pid = get_default_pid()) #' @@ -47,14 +39,7 @@ entitylist_list <- function(pid = get_default_pid(), pw = get_default_pw(), retries = get_retries(), odkc_version = get_default_odkc_version(), - orders = c( - "YmdHMS", - "YmdHMSz", - "Ymd HMS", - "Ymd HMSz", - "Ymd", - "ymd" - ), + orders = get_default_orders(), tz = get_default_tz()) { yell_if_missing(url, un, pw, pid = pid) diff --git a/R/entitylist_update.R b/R/entitylist_update.R index 128d48c6..69441032 100644 --- a/R/entitylist_update.R +++ b/R/entitylist_update.R @@ -3,31 +3,25 @@ #' `r lifecycle::badge("maturing")` #' #' You can only update `approvalRequired` using this endpoint. -#' The approvalRequired flag controls the Entity creation flow; +#' The `approvalRequired` flag controls the Entity creation flow; #' if it is true then the Submission must be approved before an Entity can be #' created from it and if it is false then an Entity is created as soon as the #' Submission is received by the ODK Central. #' By default `approvalRequired` is false for the Entity Lists created after -#' v2023.3. Entity Lists created prior to that will have approvalRequired set to -#' true. -#' -#' An Entity List is a named collection of Entities that have the same -#' properties. -#' An Entity List can be linked to Forms as Attachments. -#' This will make it available to clients as an automatically-updating CSV. -#' -#' This function is supported from ODK Central v2022.3 and will warn if the -#' given odkc_version is lower. -#' -#' `r lifecycle::badge("maturing")` +#' v2023.3. Entity Lists created prior to that will have `approvalRequired` set +#' to true. #' +#' @template tpl-def-entitylist +#' @template tpl-entitylist-dataset +#' @template tpl-auth-missing +#' @template tpl-compat-2022-3 #' @template param-pid #' @template param-did -#' @param approval_required (lgl) The value to set approvalRequired to. -#' If TRUE, a submission must be approved before an entity is created, -#' if FALSE, an entity is created as soon as the submission is received by +#' @param approval_required (lgl) The value to set `approvalRequired` to. +#' If TRUE, a Submission must be approved before an Entity is created, +#' if FALSE, an Entity is created as soon as the Submission is received by #' ODK Central. -#' Default: FALSE. +#' Default: `FALSE`. #' @template param-url #' @template param-auth #' @template param-retries @@ -41,7 +35,7 @@ # nolint start #' @seealso \url{ https://docs.getodk.org/central-api-dataset-management/#datasets} # nolint end -#' @family dataset-management +#' @family entity-management #' @export #' @examples #' \dontrun{ @@ -64,27 +58,16 @@ #' ds3$approvalRequired # FALSE #' } entitylist_update <- function(pid = get_default_pid(), - did = NULL, + did = "", approval_required = FALSE, url = get_default_url(), un = get_default_un(), pw = get_default_pw(), retries = get_retries(), odkc_version = get_default_odkc_version(), - orders = c( - "YmdHMS", - "YmdHMSz", - "Ymd HMS", - "Ymd HMSz", - "Ymd", - "ymd" - ), + orders = get_default_orders(), tz = get_default_tz()) { - yell_if_missing(url, un, pw, pid = pid) - - if (is.null(did)) { - ru_msg_abort("entitylist_update requires the Entity List name as 'did=\"name\"'.") - } + yell_if_missing(url, un, pw, pid = pid, did = did) if (odkc_version |> semver_lt("2022.3")) { ru_msg_warn("entitylist_update is supported from v2022.3") @@ -107,7 +90,8 @@ entitylist_update <- function(pid = get_default_pid(), times = retries ) |> yell_if_error(url, un, pw) |> - httr::content(encoding = "utf-8") + httr::content(encoding = "utf-8") |> + janitor::clean_names() } # usethis::use_test("entitylist_update") # nolint diff --git a/R/form_list.R b/R/form_list.R index d5c96833..b0a8ab5f 100644 --- a/R/form_list.R +++ b/R/form_list.R @@ -45,14 +45,7 @@ form_list <- function(pid = get_default_pid(), un = get_default_un(), pw = get_default_pw(), retries = get_retries(), - orders = c( - "YmdHMS", - "YmdHMSz", - "Ymd HMS", - "Ymd HMSz", - "Ymd", - "ymd" - ), + orders = get_default_orders(), tz = get_default_tz()) { yell_if_missing(url, un, pw, pid = pid) httr::RETRY( diff --git a/R/form_schema_ext.R b/R/form_schema_ext.R index ea3e2869..8e44f982 100644 --- a/R/form_schema_ext.R +++ b/R/form_schema_ext.R @@ -335,8 +335,12 @@ form_schema_ext <- function(flatten = FALSE, ) } else { # check if language already exists in the dataframe - if (!(paste0("choices_", this_choicelang) %in% - colnames(extension))) { + if ( + !( + paste0("choices_", this_choicelang) %in% + colnames(extension) + ) + ) { # if not, create new column extension <- cbind(extension, data.frame( new_choicelang = rep(NA, nrow(extension)) @@ -473,7 +477,6 @@ form_schema_ext <- function(flatten = FALSE, all_translations_ids == id_choice ] - # iterate through choice translations for (kk in seq_along(choice_translations)) { # read translation @@ -504,8 +507,12 @@ form_schema_ext <- function(flatten = FALSE, ) } else { # check if language already exists in the dataframe - if (!(paste0("choices_", this_choicelang) %in% - colnames(extension))) { + if ( + !( + paste0("choices_", this_choicelang) %in% + colnames(extension) + ) + ) { # if not, create new column extension <- cbind(extension, data.frame( new_choicelang = rep(NA, nrow(extension)) diff --git a/R/handle_ru_datetimes.R b/R/handle_ru_datetimes.R index 83a6faa7..220b441d 100644 --- a/R/handle_ru_datetimes.R +++ b/R/handle_ru_datetimes.R @@ -33,14 +33,7 @@ #' } handle_ru_datetimes <- function(data, form_schema, - orders = c( - "YmdHMS", - "YmdHMSz", - "Ymd HMS", - "Ymd HMSz", - "Ymd", - "ymd" - ), + orders = get_default_orders(), tz = get_default_tz(), verbose = get_ru_verbose()) { # Find all date/time columns in form_schema diff --git a/R/isodt_to_local.R b/R/isodt_to_local.R index 5fdad363..e85e313a 100644 --- a/R/isodt_to_local.R +++ b/R/isodt_to_local.R @@ -2,22 +2,28 @@ #' #' `r lifecycle::badge("stable")` #' -#' This function is used internally by \code{ruODK} to parse ISO timestamps +#' This function is used internally by `ruODK` to parse ISO timestamps #' to timezone-aware local times. #' +#' Warnings are suppressed through `lubridate::parse_date_time(quiet=TRUE)`. +#' #' @param datetime_string (character) An ISO8601 datetime string as produced by #' XForms exported from ODK Central. #' @param orders (vector of character) Orders of datetime elements for -#' lubridate. +#' `lubridate`. #' Default: \code{c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz")}. #' @template param-tz -#' @return A lubridate PosixCT datetime in the given timezone. +#' @param quiet (lgl) Used in `lubridate::parse_date_time(quiet=quiet)` to +#' suppress warnings from attempting to parse all empty values or columns. +#' Run with `quiet=FALSE` to show any `lubridate` warnings. +#' @return A `lubridate` PosixCT datetime in the given timezone. #' @family utilities #' @keywords internal isodt_to_local <- function(datetime_string, orders = c("YmdHMS", "YmdHMSz"), - tz = get_default_tz()) { + tz = get_default_tz(), + quiet = TRUE) { datetime_string %>% - lubridate::parse_date_time(orders = orders) %>% + lubridate::parse_date_time(orders = orders, quiet = quiet) %>% lubridate::with_tz(., tzone = tz) } diff --git a/R/odata_submission_get.R b/R/odata_submission_get.R index f1373de9..7efa87e8 100644 --- a/R/odata_submission_get.R +++ b/R/odata_submission_get.R @@ -173,14 +173,7 @@ odata_submission_get <- function(table = "Submissions", filter = NULL, parse = TRUE, download = TRUE, - orders = c( - "YmdHMS", - "YmdHMSz", - "Ymd HMS", - "Ymd HMSz", - "Ymd", - "ymd" - ), + orders = get_default_orders(), local_dir = "media", pid = get_default_pid(), fid = get_default_fid(), diff --git a/R/project_list.R b/R/project_list.R index f44b1d6b..4f3e32e9 100644 --- a/R/project_list.R +++ b/R/project_list.R @@ -40,14 +40,7 @@ project_list <- function(url = get_default_url(), un = get_default_un(), pw = get_default_pw(), retries = get_retries(), - orders = c( - "YmdHMS", - "YmdHMSz", - "Ymd HMS", - "Ymd HMSz", - "Ymd", - "ymd" - ), + orders = get_default_orders(), tz = get_default_tz()) { yell_if_missing(url, un, pw) httr::RETRY( diff --git a/R/ru_setup.R b/R/ru_setup.R index e2469b90..1d126283 100644 --- a/R/ru_setup.R +++ b/R/ru_setup.R @@ -396,6 +396,20 @@ get_default_tz <- function() { x } +#' `r lifecycle::badge("stable")` +#' @export +#' @rdname ru_settings +get_default_orders <- function() { + c( + "YmdHMS", + "YmdHMSz", + "Ymd HMS", + "Ymd HMSz", + "Ymd", + "ymd" + ) +} + #' `r lifecycle::badge("stable")` #' @export #' @rdname ru_settings @@ -696,6 +710,8 @@ get_retries <- function() { #' @param pw A password (character) #' @param pid A project ID (numeric, optional) #' @param fid A form ID (character, optional) +#' @param did An Entity List (dataset) name (character, optional) +#' @param eid An Entity UUID (character, optional) #' @details This is a helper function to pat down \code{\link{ruODK}} functions #' for missing credentials and stop with a loud but informative yell. #' @param iid A submission instance ID (character, optional) @@ -710,7 +726,10 @@ get_retries <- function() { #' testthat::expect_error(yell_if_missing("", "", "", "")) #' testthat::expect_error(yell_if_missing("", "", "", "", "")) #' testthat::expect_error(yell_if_missing("", "", "", "", "", "")) -yell_if_missing <- function(url, un, pw, pid = NULL, fid = NULL, iid = NULL) { +#' testthat::expect_error(yell_if_missing("", "", "", "", "", "", "")) +#' testthat::expect_error(yell_if_missing("", "", "", "", "", "", "", "")) +yell_if_missing <- function( + url, un, pw, pid = NULL, fid = NULL, iid = NULL, did = NULL, eid = NULL) { if (is.null(url) || identical(url, "")) { ru_msg_abort("Missing ODK Central URL. ru_setup()?") } @@ -729,6 +748,12 @@ yell_if_missing <- function(url, un, pw, pid = NULL, fid = NULL, iid = NULL) { if (!is.null(iid) && identical(iid, "")) { ru_msg_abort("Missing ODK Central submission instance ID.") } + if (!is.null(did) && identical(did, "")) { + ru_msg_abort("Missing ODK Central Entity List (dataset) name.") + } + if (!is.null(eid) && identical(eid, "")) { + ru_msg_abort("Missing ODK Central Entity UUID.") + } } #' Warn about failed web requests and give helpful troubleshooting tips. diff --git a/R/submission_list.R b/R/submission_list.R index 232534e3..15a65a11 100644 --- a/R/submission_list.R +++ b/R/submission_list.R @@ -57,14 +57,7 @@ submission_list <- function(pid = get_default_pid(), un = get_default_un(), pw = get_default_pw(), retries = get_retries(), - orders = c( - "YmdHMS", - "YmdHMSz", - "Ymd HMS", - "Ymd HMSz", - "Ymd", - "ymd" - ), + orders = get_default_orders(), tz = get_default_tz()) { yell_if_missing(url, un, pw, pid = pid, fid = fid) url <- httr::modify_url( diff --git a/R/user_list.R b/R/user_list.R index 172654f2..08cb4dad 100644 --- a/R/user_list.R +++ b/R/user_list.R @@ -69,14 +69,7 @@ user_list <- function(qry = NULL, un = get_default_un(), pw = get_default_pw(), retries = get_retries(), - orders = c( - "YmdHMS", - "YmdHMSz", - "Ymd HMS", - "Ymd HMSz", - "Ymd", - "ymd" - ), + orders = get_default_orders(), tz = get_default_tz(), verbose = get_ru_verbose()) { yell_if_missing(url, un, pw) diff --git a/R/utils-tidy-eval.R b/R/utils-tidy-eval.R index 72e43f5d..021d7396 100644 --- a/R/utils-tidy-eval.R +++ b/R/utils-tidy-eval.R @@ -3,8 +3,6 @@ #' These functions provide tidy eval-compatible ways to capture #' symbols (`sym()`, `syms()`, `ensym()`), expressions (`expr()`, #' `exprs()`, `enexpr()`), and quosures (`quo()`, `quos()`, `enquo()`). -#' To learn more about tidy eval and how to use these tools, read -#' #' #' @name tidyeval #' @keywords internal diff --git a/README.Rmd b/README.Rmd index 80e851d3..2221bb0a 100644 --- a/README.Rmd +++ b/README.Rmd @@ -20,6 +20,7 @@ knitr::opts_chunk$set( [![GitHub issues](https://img.shields.io/github/issues/ropensci/ruodk.svg?style=popout)](https://github.com/ropensci/ruODK/issues/) [![Tests](https://github.com/ropensci/ruODK/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/ropensci/ruODK/actions/workflows/R-CMD-check.yaml) [![Test coverage](https://codecov.io/gh/ropensci/ruODK/branch/main/graph/badge.svg)](https://app.codecov.io/gh/ropensci/ruODK?branch=main) +[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/ropensci/ruODK/main.svg)](https://results.pre-commit.ci/latest/github/ropensci/ruODK/main) [![CodeFactor](https://www.codefactor.io/repository/github/ropensci/ruodk/badge)](https://www.codefactor.io/repository/github/ropensci/ruodk) [![Hosted JupyterLab with ruODK](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/ropensci/ruODK/main?urlpath=lab) [![Hosted RStudio with ruODK](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/ropensci/ruODK/main?urlpath=rstudio) @@ -29,13 +30,13 @@ Especially in these trying times, it is important to ask "r u ODK?". `ruODK` is an R client to access and parse data from ODK Central. -[OpenDataKit](https://getodk.org/) (ODK) is -[free-and open-source software](https://getodk.org/software/) -that helps millions of people collect data quickly, accurately, offline, -and at scale. The software is in active use in every country in the world and is +[OpenDataKit](https://getodk.org/) (ODK) is +[free-and open-source software](https://getodk.org/software/) +that helps millions of people collect data quickly, accurately, offline, +and at scale. The software is in active use in every country in the world and is supported by a large and helpful community. -`ruODK` is a community contribution to the ODK ecosystem, but not directly +`ruODK` is a community contribution to the ODK ecosystem, but not directly affiliated with ODK. `ruODK` assumes some familiarity of its users with the ODK ecosystem and workflows. @@ -43,11 +44,11 @@ For a detailed overview, read the extensive [ODK documentation](https://docs.get and visit the friendly [ODK forum](https://forum.getodk.org/). [ODK Central](https://docs.getodk.org/central-intro/) is a cloud-based -data clearinghouse for digitally captured data, replacing the older software -[ODK Aggregate](https://docs.getodk.org/aggregate-intro/). -ODK Central manages user accounts and permissions, stores form definitions, -and allows data collection clients like -[ODK Collect](https://docs.getodk.org/collect-intro/) to connect to it for +data clearinghouse for digitally captured data, replacing the older software +[ODK Aggregate](https://docs.getodk.org/aggregate-intro/). +ODK Central manages user accounts and permissions, stores form definitions, +and allows data collection clients like +[ODK Collect](https://docs.getodk.org/collect-intro/) to connect to it for form download and submission upload. ![An ODK setup with ODK Build, Central, Collect, and ruODK]( @@ -55,17 +56,17 @@ https://www.lucidchart.com/publicSegments/view/952c1350-3003-48c1-a2c8-94bad74cd A typical [ODK workflow](https://docs.getodk.org/#how-is-odk-used): An XForm is designed e.g. in [ODK Build](https://build.getodk.org/), -[published to ODK Central](https://docs.getodk.org/central-forms/), +[published to ODK Central](https://docs.getodk.org/central-forms/), and downloaded onto an Android device running ODK Collect. -After data have been captured digitally using +After data have been captured digitally using [ODK Collect](https://docs.getodk.org/collect-intro/), the data are uploaded -and stored in ODK Central. The next step from there is to extract the data, -optionally upload it into another data warehouse, and then to analyse and -generate insight from it. +and stored in ODK Central. The next step from there is to extract the data, +optionally upload it into another data warehouse, and then to analyse and +generate insight from it. -While data can be retrieved in bulk through the GUI, ODK Central's API provides -access to its data and functionality through both an OData and a RESTful API -with a comprehensive and interactive +While data can be retrieved in bulk through the GUI, ODK Central's API provides +access to its data and functionality through both an OData and a RESTful API +with a comprehensive and interactive [documentation](https://docs.getodk.org/central-api-odata-endpoints/). `ruODK` is aimed at the technically minded researcher who wishes to access and @@ -76,24 +77,24 @@ Benefits of using the R ecosystem in combination with ODK: * Scalability: Both R and ODK are free and open source software. Scaling to many users does not incur license fees. * Ubiquity: R is known to many scientists and is widely taught at universities. -* Automation: The entire data access and analysis workflow can be automated +* Automation: The entire data access and analysis workflow can be automated through R scripts. -* Reproducible reporting (e.g. - [Sweave](https://support.rstudio.com/hc/en-us/articles/200552056-Using-Sweave-and-knitr), - [RMarkdown](https://rmarkdown.rstudio.com/)), interactive web apps - ([Shiny](https://shiny.rstudio.com/)), +* Reproducible reporting (e.g. + [Sweave](https://support.rstudio.com/hc/en-us/articles/200552056-Using-Sweave-and-knitr), + [RMarkdown](https://rmarkdown.rstudio.com/)), interactive web apps + ([Shiny](https://shiny.rstudio.com/)), workflow scaling ([drake](https://docs.ropensci.org/drake/)). -* Rstudio-as-a-Service (RaaS) at +* Rstudio-as-a-Service (RaaS) at [![Hosted RStudio with ruODK](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/ropensci/ruODK/main?urlpath=rstudio) `ruODK`'s scope: * To wrap all ODK Central API endpoints with a focus on **data access**. * To provide working examples of interacting with the ODK Central API. -* To provide convenience helpers for the day to day tasks when working with - ODK Central data in R: **data munging** the ODK Central API output into tidy +* To provide convenience helpers for the day to day tasks when working with + ODK Central data in R: **data munging** the ODK Central API output into tidy R formats. - + `ruODK`'s use cases: @@ -101,9 +102,9 @@ Benefits of using the R ecosystem in combination with ODK: 1. Data collection: ODK Collect 2. Data clearinghouse: ODK Central 3. Data analysis and reporting: `Rmd` (ruODK) - 4. Publishing and dissemination: [`ckanr`](https://docs.ropensci.org/ckanr/), + 4. Publishing and dissemination: [`ckanr`](https://docs.ropensci.org/ckanr/), [`CKAN`](https://ckan.org/) - + * Larger projects: 1. Data collection: ODK Collect 2. Data clearinghouse: ODK Central @@ -112,14 +113,14 @@ Benefits of using the R ecosystem in combination with ODK: 5. Reporting: `Rmd` 6. Publishing and dissemination: [`ckanr`](https://docs.ropensci.org/ckanr/), [`CKAN`](https://ckan.org/) - + Out of scope: * To wrap "management" API endpoints. ODK Central is a - [VueJS/NodeJS application](https://github.com/getodk/central-frontend/) - which provides a comprehensive graphical user interface for the management of + [VueJS/NodeJS application](https://github.com/getodk/central-frontend/) + which provides a comprehensive graphical user interface for the management of users, roles, permissions, projects, and forms. -* To provide extensive data visualisation. We show only minimal examples of data +* To provide extensive data visualisation. We show only minimal examples of data visualisation and presentation, mainly to illustrate the example data. Once the data is in your hands as tidy tibbles... urODK! @@ -127,7 +128,7 @@ Out of scope: ruODK screencast ## Install -You can install the latest release of `ruODK` from the +You can install the latest release of `ruODK` from the [rOpenSci R-Universe](https://ropensci.r-universe.dev): ```{r r-universe, eval = FALSE} @@ -163,10 +164,10 @@ remotes::install_github( If the install fails, read the error messages carefully and install any unmet dependencies (system libraries or R packages). -If the install fails on building the vignettes, you can set +If the install fails on building the vignettes, you can set `build_vignettes=FALSE` and read the vignettes from the online docs instead. -If the installation still fails, or the above does not make any sense, +If the installation still fails, or the above does not make any sense, feel free to submit a [bug report](https://github.com/ropensci/ruODK/issues/new/choose). ## Try `ruODK` @@ -174,14 +175,14 @@ You can also run `ruODK` through hosted or self-built Docker images. In decreasing order of simplicity: -* Launch a hosted RStudio Server +* Launch a hosted RStudio Server [![Hosted RStudio with ruODK](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/ropensci/ruODK/main?urlpath=rstudio) -* Launch a hosted JupyterLab server (with all kernel options available) +* Launch a hosted JupyterLab server (with all kernel options available) [![Hosted JupyterLab with ruODK](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/ropensci/ruODK/main?urlpath=lab) -* Download the pre-built - [ruODK Docker image](https://github.com/ropensci/ruODK/pkgs/container/ruodk) +* Download the pre-built + [ruODK Docker image](https://github.com/ropensci/ruODK/pkgs/container/ruodk) based on the last tagged `ruODK` version - + ``` docker pull ghcr.io/ropensci/ruodk:latest docker run ghcr.io/ropensci/ruodk:latest @@ -201,29 +202,29 @@ From there, you can run any available kernel, amongst others are RStudio and a plain R shell. ## Configure `ruODK` -For all available detailed options to configure authentication for `ruODK`, read +For all available detailed options to configure authentication for `ruODK`, read [`vignette("setup", package = "ruODK")`](https://docs.ropensci.org/ruODK/articles/setup.html). ## Use `ruODK` -A detailed walk-through with some data visualisation examples is available in the +A detailed walk-through with some data visualisation examples is available in the [`vignette("odata-api", package="ruODK")`](https://docs.ropensci.org/ruODK/articles/odata-api.html). See also [`vignette("restful-api", package="ruODK")`](https://docs.ropensci.org/ruODK/articles/restful-api.html) for examples using the alternative RESTful API. -`urODK`, a sing-along `ruODK` workshop about you, R, and ODK, -is available on +`urODK`, a sing-along `ruODK` workshop about you, R, and ODK, +is available on [![Hosted RStudio with ruODK](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/ropensci/ruODK/main?urlpath=rstudio). ## Contribute -Contributions through [issues](https://github.com/ropensci/ruODK/issues) and PRs +Contributions through [issues](https://github.com/ropensci/ruODK/issues) and PRs are welcome! See the [contributing guide](https://docs.ropensci.org/ruODK/CONTRIBUTING.html) on best practices and further readings for code contributions. ## Attribution -`ruODK` was developed by Florian Mayer for the Western Australian +`ruODK` was developed by Florian Mayer for the Western Australian [Department of Biodiversity, Conservation and Attractions (DBCA)](https://www.dbca.wa.gov.au/). The development was funded both by DBCA core funding and external funds from the [North West Shelf Flatback Turtle Conservation Program](https://flatbacks.dbca.wa.gov.au/). @@ -237,13 +238,13 @@ citation("ruODK") ``` ## Acknowledgements -The Department of Biodiversity, Conservation and Attractions (DBCA) acknowledges -the traditional owners of country throughout Western Australia and their continuing -connection to the land, waters and community. We pay our respects to them, their +The Department of Biodiversity, Conservation and Attractions (DBCA) acknowledges +the traditional owners of country throughout Western Australia and their continuing +connection to the land, waters and community. We pay our respects to them, their culture and to their Elders past and present. -This software was created on Whadjuk boodja (ground) both as a contribution to -the ODK ecosystem and for the conservation of the biodiversity of +This software was created on Whadjuk boodja (ground) both as a contribution to +the ODK ecosystem and for the conservation of the biodiversity of Western Australia, and in doing so, caring for country. ## Package functionality diff --git a/README.md b/README.md index 2671d2e2..a91c0586 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ issues](https://img.shields.io/github/issues/ropensci/ruodk.svg?style=popout)](h [![Tests](https://github.com/ropensci/ruODK/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/ropensci/ruODK/actions/workflows/R-CMD-check.yaml) [![Test coverage](https://codecov.io/gh/ropensci/ruODK/branch/main/graph/badge.svg)](https://app.codecov.io/gh/ropensci/ruODK?branch=main) +[![pre-commit.ci +status](https://results.pre-commit.ci/badge/github/ropensci/ruODK/main.svg)](https://results.pre-commit.ci/latest/github/ropensci/ruODK/main) [![CodeFactor](https://www.codefactor.io/repository/github/ropensci/ruodk/badge)](https://www.codefactor.io/repository/github/ropensci/ruodk) [![Hosted JupyterLab with ruODK](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/ropensci/ruODK/main?urlpath=lab) @@ -83,7 +85,7 @@ Benefits of using the R ecosystem in combination with ODK: universities. - Automation: The entire data access and analysis workflow can be automated through R scripts. -- Reproducible reporting (e.g.  +- Reproducible reporting (e.g. [Sweave](https://support.rstudio.com/hc/en-us/articles/200552056-Using-Sweave-and-knitr), [RMarkdown](https://rmarkdown.rstudio.com/)), interactive web apps ([Shiny](https://shiny.rstudio.com/)), workflow scaling @@ -245,25 +247,25 @@ practices and further readings for code contributions. ## Attribution -`ruODK` was developed, and is maintained, by Florian Mayer for the -Western Australian [Department of Biodiversity, Conservation and -Attractions (DBCA)](https://www.dbca.wa.gov.au/). The development was -funded both by DBCA core funding and external funds from the [North West -Shelf Flatback Turtle Conservation -Program](https://flatbacks.dbca.wa.gov.au/). +`ruODK` was developed by Florian Mayer for the Western Australian +[Department of Biodiversity, Conservation and Attractions +(DBCA)](https://www.dbca.wa.gov.au/). The development was funded both by +DBCA core funding and external funds from the [North West Shelf Flatback +Turtle Conservation Program](https://flatbacks.dbca.wa.gov.au/). + +ruODK is maintained and extended by Florian Mayer. To cite package `ruODK` in publications use: ``` r citation("ruODK") -#> To cite ruODK in publications use (with the respective version number: -#> -#> Mayer, Florian Wendelin. (2020, Nov 19). ruODK: An R Client for the -#> ODK Central API (Version X.X.X). Zenodo. +#> To cite ruODK in publications use (with the correct version number: +#> +#> Mayer, Florian Wendelin. (2020, Nov 19). ruODK: An R Client for the ODK Central API (Version X.X.X). Zenodo. #> https://doi.org/10.5281/zenodo.5559164 -#> +#> #> A BibTeX entry for LaTeX users is -#> +#> #> @Misc{, #> title = {ruODK: Client for the ODK Central API}, #> author = {Florian W. Mayer}, diff --git a/_pkgdown.yml b/_pkgdown.yml index de1a1b52..bf9cd612 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -1,6 +1,7 @@ home: title: An R Client for the ODK Central API description: R, you, and ODK - Interact with ODK Central from R +url: https://docs.ropensci.org/ruODK/ template: opengraph: image: diff --git a/codecov.yml b/codecov.yml index 891c2471..63952aaa 100644 --- a/codecov.yml +++ b/codecov.yml @@ -12,4 +12,3 @@ coverage: threshold: 1% ignore: - "R/utils-lifecycle.R" - diff --git a/codemeta.json b/codemeta.json index f184ec9b..0bcec291 100644 --- a/codemeta.json +++ b/codemeta.json @@ -2,25 +2,25 @@ "@context": "https://doi.org/10.5063/schema/codemeta-2.0", "@type": "SoftwareSourceCode", "identifier": "ruODK", - "description": "Access and tidy up data from the 'ODK Central' API. 'ODK Central' is a clearinghouse for digitally captured data . 'ODK Central' and its API are documented at .", + "description": "Access and tidy up data from the 'ODK Central' API. 'ODK Central' is a clearinghouse for digitally captured data using ODK . It manages user accounts and permissions, stores form definitions, and allows data collection clients like 'ODK Collect' to connect to it for form download and submission upload. The 'ODK Central' API is documented at .", "name": "ruODK: An R Client for the ODK Central API", "relatedLink": "https://docs.ropensci.org/ruODK", "codeRepository": "https://github.com/ropensci/ruODK", "issueTracker": "https://github.com/ropensci/ruODK/issues", "license": "https://spdx.org/licenses/GPL-3.0", - "version": "1.4.2", + "version": "1.5.0", "programmingLanguage": { "@type": "ComputerLanguage", "name": "R", "url": "https://r-project.org" }, - "runtimePlatform": "R version 4.3.2 (2023-10-31 ucrt)", + "runtimePlatform": "R version 4.4.1 (2024-06-14 ucrt)", "author": [ { "@type": "Person", "givenName": ["Florian", "W."], "familyName": "Mayer", - "email": "Florian.Mayer@dbca.wa.gov.au", + "email": "Florian.Mayer@dpc.wa.gov.au", "@id": "https://orcid.org/0000-0003-4269-4242" } ], @@ -59,7 +59,7 @@ "@type": "Person", "givenName": ["Florian", "W."], "familyName": "Mayer", - "email": "Florian.Mayer@dbca.wa.gov.au", + "email": "Florian.Mayer@dpc.wa.gov.au", "@id": "https://orcid.org/0000-0003-4269-4242" } ], @@ -144,29 +144,29 @@ }, { "@type": "SoftwareApplication", - "identifier": "leaflet", - "name": "leaflet", - "version": ">= 2.1.1", + "identifier": "leafem", + "name": "leafem", + "version": ">= 0.2.0.9012", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "sameAs": "https://CRAN.R-project.org/package=leaflet" + "sameAs": "https://github.com/r-spatial/leafem" }, { "@type": "SoftwareApplication", - "identifier": "listviewer", - "name": "listviewer", - "version": ">= 3.0.0", + "identifier": "leaflet", + "name": "leaflet", + "version": ">= 2.1.1", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "sameAs": "https://CRAN.R-project.org/package=listviewer" + "sameAs": "https://CRAN.R-project.org/package=leaflet" }, { "@type": "SoftwareApplication", @@ -183,16 +183,16 @@ }, { "@type": "SoftwareApplication", - "identifier": "leafem", - "name": "leafem", - "version": ">= 0.2.0.9012", + "identifier": "listviewer", + "name": "listviewer", + "version": ">= 3.0.0", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "sameAs": "https://github.com/r-spatial/leafem" + "sameAs": "https://CRAN.R-project.org/package=listviewer" }, { "@type": "SoftwareApplication", @@ -246,6 +246,19 @@ }, "sameAs": "https://CRAN.R-project.org/package=sf" }, + { + "@type": "SoftwareApplication", + "identifier": "skimr", + "name": "skimr", + "version": ">= 2.1.5", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=skimr" + }, { "@type": "SoftwareApplication", "identifier": "terra", @@ -477,29 +490,29 @@ }, "15": { "@type": "SoftwareApplication", - "identifier": "stringr", - "name": "stringr", - "version": ">= 1.5.0", + "identifier": "semver", + "name": "semver", + "version": ">= 0.2.0", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "sameAs": "https://CRAN.R-project.org/package=stringr" + "sameAs": "https://CRAN.R-project.org/package=semver" }, "16": { "@type": "SoftwareApplication", - "identifier": "semver", - "name": "semver", - "version": ">= 0.2.0", + "identifier": "stringr", + "name": "stringr", + "version": ">= 1.5.0", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "sameAs": "https://CRAN.R-project.org/package=semver" + "sameAs": "https://CRAN.R-project.org/package=stringr" }, "17": { "@type": "SoftwareApplication", @@ -544,7 +557,7 @@ }, "applicationCategory": "DataAccess", "keywords": ["database", "open-data", "opendatakit", "odk", "api", "data", "dataset", "odata", "odata-client", "odk-central", "rstats", "r-package"], - "fileSize": "16434.814KB", + "fileSize": "18980.056KB", "citation": [ { "@type": "CreativeWork", diff --git a/cran-comments.md b/cran-comments.md index 8793d3d3..ea799672 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -22,19 +22,19 @@ Resolved NOTE comments: * Possibly invalid URLs: - * The package comparison section in the README contains Markdown badges with + * The package comparison section in the README contains Markdown badges with CRAN links to packages that are not yet or not any more on CRAN. These - links are correct, and while they currently do not resolve, they will do so + links are correct, and while they currently do not resolve, they will do so once the packages are (re-)submitted to CRAN. Currently removed. - * The README contains an ODK Central form OData service URL to illustrate + * The README contains an ODK Central form OData service URL to illustrate setting up ruODK. The URL redirects to a login screen if followed directly. This is expected behaviour. Currently not appearing as warning. * The PDF version of the manual is now included. -* The example data contains UTF-8 strings. This is a realistic scenario. +* The example data contains UTF-8 strings. This is a realistic scenario. The note has disappeared after the R version 4 release. -* Test coverage: All functionality supporting the current ODK Central release is - covered by tests. - The only exception is `form_schema{_parse}`, which supports a breaking +* Test coverage: All functionality supporting the current ODK Central release is + covered by tests. + The only exception is `form_schema{_parse}`, which supports a breaking change between ODK Central 0.7 and 0.8. The test server runs ODK Central 0.8, - a production server (used by the package author, but not accessible to other + a production server (used by the package author, but not accessible to other maintainers) runs 0.7 successfully. The tests for v 0.7 use packaged data. diff --git a/data/fq_attachments.rda b/data/fq_attachments.rda index 3b65be37..1afdf606 100644 Binary files a/data/fq_attachments.rda and b/data/fq_attachments.rda differ diff --git a/data/fq_data.rda b/data/fq_data.rda index 988a5ea2..459e2e7e 100644 Binary files a/data/fq_data.rda and b/data/fq_data.rda differ diff --git a/data/fq_data_strata.rda b/data/fq_data_strata.rda index cc5853b6..0e27072e 100644 Binary files a/data/fq_data_strata.rda and b/data/fq_data_strata.rda differ diff --git a/data/fq_data_taxa.rda b/data/fq_data_taxa.rda index ef25e543..43be4dbf 100644 Binary files a/data/fq_data_taxa.rda and b/data/fq_data_taxa.rda differ diff --git a/data/fq_form_detail.rda b/data/fq_form_detail.rda index 46219927..971abec9 100644 Binary files a/data/fq_form_detail.rda and b/data/fq_form_detail.rda differ diff --git a/data/fq_form_list.rda b/data/fq_form_list.rda index e56b214d..3db814f8 100644 Binary files a/data/fq_form_list.rda and b/data/fq_form_list.rda differ diff --git a/data/fq_form_schema.rda b/data/fq_form_schema.rda index 4ffad3d5..d95d249b 100644 Binary files a/data/fq_form_schema.rda and b/data/fq_form_schema.rda differ diff --git a/data/fq_form_xml.rda b/data/fq_form_xml.rda index 3e9d0acf..d78fdc77 100644 Binary files a/data/fq_form_xml.rda and b/data/fq_form_xml.rda differ diff --git a/data/fq_meta.rda b/data/fq_meta.rda index 04b8c1c6..8d8bcd90 100644 Binary files a/data/fq_meta.rda and b/data/fq_meta.rda differ diff --git a/data/fq_project_detail.rda b/data/fq_project_detail.rda index da2a8250..9fbf0ebc 100644 Binary files a/data/fq_project_detail.rda and b/data/fq_project_detail.rda differ diff --git a/data/fq_project_list.rda b/data/fq_project_list.rda index f61daff0..cb7113cf 100644 Binary files a/data/fq_project_list.rda and b/data/fq_project_list.rda differ diff --git a/data/fq_raw.rda b/data/fq_raw.rda index ad221377..42bb1a04 100644 Binary files a/data/fq_raw.rda and b/data/fq_raw.rda differ diff --git a/data/fq_raw_strata.rda b/data/fq_raw_strata.rda index 33c2a861..92dc0633 100644 Binary files a/data/fq_raw_strata.rda and b/data/fq_raw_strata.rda differ diff --git a/data/fq_raw_taxa.rda b/data/fq_raw_taxa.rda index 790124dc..5aea8c69 100644 Binary files a/data/fq_raw_taxa.rda and b/data/fq_raw_taxa.rda differ diff --git a/data/fq_submission_list.rda b/data/fq_submission_list.rda index 6f6d6122..fc4660d5 100644 Binary files a/data/fq_submission_list.rda and b/data/fq_submission_list.rda differ diff --git a/data/fq_submissions.rda b/data/fq_submissions.rda index 0f5cf279..1c499991 100644 Binary files a/data/fq_submissions.rda and b/data/fq_submissions.rda differ diff --git a/data/fq_svc.rda b/data/fq_svc.rda index 7b8b48c6..bb877369 100644 Binary files a/data/fq_svc.rda and b/data/fq_svc.rda differ diff --git a/data/fq_zip_data.rda b/data/fq_zip_data.rda index fb17284f..b38994f1 100644 Binary files a/data/fq_zip_data.rda and b/data/fq_zip_data.rda differ diff --git a/data/fq_zip_strata.rda b/data/fq_zip_strata.rda index d1185980..919c971d 100644 Binary files a/data/fq_zip_strata.rda and b/data/fq_zip_strata.rda differ diff --git a/data/fq_zip_taxa.rda b/data/fq_zip_taxa.rda index 7851abc0..a672777e 100644 Binary files a/data/fq_zip_taxa.rda and b/data/fq_zip_taxa.rda differ diff --git a/data/geo_fs.rda b/data/geo_fs.rda index af702ead..47faeb33 100644 Binary files a/data/geo_fs.rda and b/data/geo_fs.rda differ diff --git a/data/geo_gj.rda b/data/geo_gj.rda index e36b189c..b52c28ea 100644 Binary files a/data/geo_gj.rda and b/data/geo_gj.rda differ diff --git a/data/geo_gj_raw.rda b/data/geo_gj_raw.rda index 7756faca..e6c47454 100644 Binary files a/data/geo_gj_raw.rda and b/data/geo_gj_raw.rda differ diff --git a/data/geo_wkt.rda b/data/geo_wkt.rda index af68d10d..f0b2fb30 100644 Binary files a/data/geo_wkt.rda and b/data/geo_wkt.rda differ diff --git a/data/geo_wkt_raw.rda b/data/geo_wkt_raw.rda index fa359cda..eb4424d9 100644 Binary files a/data/geo_wkt_raw.rda and b/data/geo_wkt_raw.rda differ diff --git a/inst/CITATION b/inst/CITATION index e404f96e..d4792358 100644 --- a/inst/CITATION +++ b/inst/CITATION @@ -1,7 +1,7 @@ -citHeader("To cite ruODK in publications use (with the respective version number:") +citHeader("To cite ruODK in publications use (with the correct version number:") -citEntry( - entry = "Misc", +bibentry( + bibtype = "Misc", title = "ruODK: Client for the ODK Central API", author = "Florian W. Mayer", note = "R package version X.X.X", @@ -12,4 +12,4 @@ citEntry( "ruODK: An R Client for the ODK Central API (Version X.X.X). ", "Zenodo. https://doi.org/10.5281/zenodo.5559164" ) -) \ No newline at end of file +) diff --git a/inst/WORDLIST b/inst/WORDLIST index 7c1ba2e3..17b767de 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -1,119 +1,32 @@ -AppVeyor -Arefin -Binderhub -Bugfix -CI -CMD -CodeFactor -DIY -DOI -Datatable -Dockerfile -ETL -Edmx -EntityType -Flatback -GGplot -GH -GeoJSON -Geoshapes -Geospatial -Geotraces -GetODK -Gimenez -GitHub -HT -IDs -JDBC -JSON -JupyterLab -LINESTRING -LTS -Linestring -Linestrings -M -MacOS -Mapview -Metapackage -Multipolygon -Muntashir -NVIS -NodeJS -ODK -ODKC -OData -ORCID -OpenDataKit -OpenRosa -POSIXct -PRs -Passphrases -Pentaho -PosixCT -PowerBI -QA -README -READMEs -RESTful -RJDBC -RJSONIO -RMarkdown -ROpenSci -RSpatial -RStudio -RaaS -RapidSurveys -Rectangling -Refactor -Rmd -Rowlingson -Rstudio -STATA -Sadler -Schemetrica -Setup -SimpleFeature -SimpleFeatures -Simplifiy -Spellchecks -Stata -Tidyverse -TravisCI -UNHCR -URLs -UUID -UUIDs -Ubuntu -Un -VueJS -W -WKT -Whadjuk -Wickam -XForm -XForms -Xforms -XlsForm -XlsForms -Zenodo acc actee alt +analysed api app apps +AppVeyor +Arefin asciicast attchment attrs +baseVersion +behaviour +Binderhub boodja +Bugfix bugfix ca changedate cheatsheet chr +CI +ci ckanr clearinghouse +CMD codecov +CodeFactor codefactor colname compat @@ -121,88 +34,174 @@ composable config crosslinks csv +Datatable datatable datatypes dateTime datetime datetimes depencency +devcontainer df +DIY +Dockerfile +doesn +DOI dttm +Edmx +EntityType +ETag +etag etc +ETL eval extdata +FID fid +Flatback fn +fs funder geofield geofields +GeoJSON geopoint geopoints geoshape +Geoshapes geoshapes +Geospatial geospatial geotrace +geotrace's +Geotraces geotraces gepshapes +GetODK getodk +GGplot +GH +Gimenez +GitHub googledrive +HT https httr idential +IDs imagemagick instanceId +int introspect introspects io +JDBC jpg +JSON jsonlite +JupyterLab kwargs lang languange lat +lgl lifecycle +LINESTRING +Linestring +Linestrings ll lon +LTS lubridate +M m +MacOS +Mapview mapview max md +Metapackage metapackage +Multipolygon munging +Muntashir +NodeJS noop +NVIS +OData odata +ODK odkbuild +ODKC odkc offline +OpenDataKit +OpenRosa +ORCID org passphrase +Passphrases passphrases +Pentaho permissioning photoshopping +PID pid pkgdown popup popups +POSIXct +PosixCT +PowerBI +pre +precommit +PRs purrr +QA quosures -rOpenSci -rOzCBI +RaaS +RapidSurveys +README +READMEs rectangled +Rectangling rectangling +Refactor repo +RESTful +RJDBC +RJSONIO rlang +RMarkdown +Rmd +ROpenSci +rOpenSci +Rowlingson +rOzCBI +RSpatial +RStudio +Rstudio ru ruReady +Sadler +Schemetrica screencast screenshots searchable semver +Setup setup sf +SimpleFeature +SimpleFeatures +Simplifiy spec +specs +Spellchecks src +STATA +Stata +str subfolder subfolders subresource @@ -214,22 +213,40 @@ tbl tibble tibbles tidyr -tidyselect +Tidyverse tidyverse timezone +TravisCI trigram tuples +Ubuntu +Un un +UNHCR unnest unnested unnesting unnests -urODK +URLs urls +urODK +UUID uuid +UUIDs vcr vectorised +VueJS +W +Whadjuk whattheyforgot +Wickam +WKT workdir workflow workflows +XForm +XForms +Xforms +XlsForm +XlsForms +Zenodo diff --git a/inst/binder/README.md b/inst/binder/README.md index 29785124..a6ce0584 100644 --- a/inst/binder/README.md +++ b/inst/binder/README.md @@ -1,5 +1,5 @@ # urODK - You, R, and ODK: A sing-along ruODK workshop -This document contains the outline of a workshop where within about one hour, +This document contains the outline of a workshop where within about one hour, we’ll build an electronic data capture pipeline from form design to data analysis and dissemination using ODK and `ruODK`. @@ -99,7 +99,7 @@ See the ruODK README for other installation options. ### Configure ruODK -Add your ODK Central credentials to `.Renviron` via +Add your ODK Central credentials to `.Renviron` via `usethis::edit_r_environ()` (with your own `un` and `pw`): ``` r diff --git a/inst/binder/apt.txt b/inst/binder/apt.txt index af5411d7..72252ab0 100644 --- a/inst/binder/apt.txt +++ b/inst/binder/apt.txt @@ -16,13 +16,11 @@ cargo libavfilter-dev libfontconfig1-dev libopenblas-dev -freetds-common libct4 libsybdb5 freetds-bin freetds-common freetds-dev -libct4 libsybdb5 tdsodbc -unixodbc \ No newline at end of file +unixodbc diff --git a/inst/extdata/FloraQuadrat04.odkbuild b/inst/extdata/FloraQuadrat04.odkbuild index a2589d67..e6b95f8b 100644 --- a/inst/extdata/FloraQuadrat04.odkbuild +++ b/inst/extdata/FloraQuadrat04.odkbuild @@ -1 +1 @@ -{"title":"Flora Quadrat 0.4","controls":[{"name":"encounter_start_datetime","kind":"Start Time","metadata":{},"type":"metadata"},{"name":"reporter","kind":"Username","metadata":{},"type":"metadata"},{"name":"device_id","kind":"Device ID","metadata":{},"type":"metadata"},{"name":"location","label":{"0":"Location"},"loop":false,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"area_name","label":{"0":"Quadrat name"},"hint":{"0":""},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"},{"name":"quadrat_photo","label":{"0":"Quadrat Photo"},"hint":{"0":"Capture a memorable overview"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Image","metadata":{},"type":"inputMedia"},{"name":"corner1","label":{"0":"Corner 1"},"hint":{"0":"First recorded corner of quadrat"},"defaultValue":"","readOnly":false,"required":true,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Point","appearance":"Default (GPS)","metadata":{},"type":"inputLocation"}]},{"name":"habitat","label":{"0":"Habitat"},"loop":false,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"morphological_type","label":{"0":"Morphological Type"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","options":[{"val":"crest","cascade":[],"text":{"0":"Crest"}},{"val":"upper-slope","cascade":[],"text":{"0":"Upper Slope"}},{"val":"mid-slope","cascade":[],"text":{"0":"Mid Slope"}},{"val":"lower-slope","cascade":[],"text":{"0":"Lower Slope"}},{"val":"flat","cascade":[],"text":{"0":"Flat"}},{"val":"open-depression","cascade":[],"text":{"0":"Open Depression"}},{"val":"closed-depression","cascade":[],"text":{"0":"Closed Depression"}},{"val":"hill","cascade":[],"text":{"0":"Hill"}},{"val":"ridge","cascade":[],"text":{"0":"Ridge"}}],"cascading":false,"other":false,"appearance":"Minimal (spinner)","metadata":{},"type":"inputSelectOne"},{"name":"morphological_type_photo","label":{"0":"Morphological Type Photo"},"hint":{"0":"Take photo in landscape format"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Image","metadata":{},"type":"inputMedia"}]},{"name":"vegetation_stratum","label":{"0":"Vegetation Stratum"},"loop":true,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"nvis_level3_broad_floristic_group","label":{"0":"Broad Floristic Group"},"hint":{"0":"NVIS Level III"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","options":[{"val":"w1.0_trees_with_dominant_genus","cascade":[],"text":{"0":"Trees: Dominant Genus"}},{"val":"w1.1_trees_rainforest","cascade":[],"text":{"0":"Trees: Rainforest"}},{"val":"w1.2_trees_cultivated_non_food","cascade":[],"text":{"0":"Trees: Cultivated Non-food"}},{"val":"w1.3_trees_cultivated_food","cascade":[],"text":{"0":"Trees: Cultivated Food"}},{"val":"w1.4_trees_cultivated_food","cascade":[],"text":{"0":"Trees: Planted / Cultivated"}},{"val":"w2.0_woody_plant","cascade":[],"text":{"0":"Woody Plant"}},{"val":"w2.1_mallee","cascade":[],"text":{"0":"Mallee"}},{"val":"w3.0_shrub","cascade":[],"text":{"0":"Shrub"}},{"val":"w3.1_heath_shrub","cascade":[],"text":{"0":"Heath Shrub"}},{"val":"w3.2_chenopod_shrub","cascade":[],"text":{"0":"Chenopod Shrub"}},{"val":"w3.3_samphire_shrub","cascade":[],"text":{"0":"Samphire Shrub"}},{"val":"g1.0_hummock_grass","cascade":[],"text":{"0":"Hummock Grass"}},{"val":"g2.0_tussock_grass","cascade":[],"text":{"0":"Tussock Grass"}},{"val":"g3.0_other_grass","cascade":[],"text":{"0":"Other Grass"}},{"val":"g4.0_sedge","cascade":[],"text":{"0":"Sedge"}},{"val":"g5.0_rush","cascade":[],"text":{"0":"Rush"}},{"val":"h1.0_forb","cascade":[],"text":{"0":"Forb"}},{"val":"f1.0_fern","cascade":[],"text":{"0":"Fern"}},{"val":"m1.0_bryophyte","cascade":[],"text":{"0":"Bryophyte"}},{"val":"l1.0_lichen","cascade":[],"text":{"0":"Lichen"}},{"val":"c1.0_surface_crusts","cascade":[],"text":{"0":"Surface Crusts"}},{"val":"v1.0_vine","cascade":[],"text":{"0":"Vine"}},{"val":"a1.0_aquatic_non_woody","cascade":[],"text":{"0":"Aquatic Non-woody"}},{"val":"a2.0_seagrass_marine","cascade":[],"text":{"0":"Seagrass Marine"}},{"val":"a3.0_algae_fresh_brackish","cascade":[],"text":{"0":"Algae Fresh or Brackish"}},{"val":"a4.0_algae_marine","cascade":[],"text":{"0":"Algae Marine"}},{"val":"b1.0_bare_surface","cascade":[],"text":{"0":"Bare surface"}}],"cascading":false,"other":false,"appearance":"Minimal (spinner)","metadata":{},"type":"inputSelectOne"},{"name":"max_height_m","label":{"0":"Max height of stratum [m]"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","range":{"min":"0","max":"100","minInclusive":true,"maxInclusive":true},"appearance":"Textbox","kind":"Decimal","selectRange":{"min":"1","max":"10"},"selectStep":"1","sliderTicks":true,"metadata":{},"type":"inputNumeric"},{"name":"foliage_cover","label":{"0":"Foliage cover"},"hint":{"0":"of all dominant species combined"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","options":[{"text":{"0":"D 100-70%"},"cascade":[],"val":"100-70"},{"text":{"0":"O 70-30%"},"cascade":[],"val":"70-30"},{"text":{"0":"S 30-10%"},"cascade":[],"val":"30-10"},{"text":{"0":"V 10-5%"},"cascade":[],"val":"10-5"},{"text":{"0":"L 5-0%"},"cascade":[],"val":"5-0"},{"text":{"0":"I 0%"},"cascade":[],"val":"0"}],"cascading":false,"other":false,"appearance":"Minimal (spinner)","metadata":{},"type":"inputSelectOne"},{"name":"dominant_species_1","label":{"0":"Dominant species 1"},"hint":{"0":"Canonical name"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"},{"name":"dominant_species_2","label":{"0":"Dominant species 2"},"hint":{"0":"Canonical name"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"},{"name":"dominant_species_3","label":{"0":"Dominant species 3"},"hint":{"0":"Canonical name"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"},{"name":"dominant_species_4","label":{"0":"Dominant species 4"},"hint":{"0":"Canonical name"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"}]},{"name":"perimeter","label":{"0":"Quadrat Perimeter"},"loop":false,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"corner2","label":{"0":"Corner 2"},"hint":{"0":"Second recorded corner of quadrat"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Point","appearance":"Default (GPS)","metadata":{},"type":"inputLocation"},{"name":"corner3","label":{"0":"Corner 3"},"hint":{"0":"Third recorded corner of quadrat"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Point","appearance":"Default (GPS)","metadata":{},"type":"inputLocation"},{"name":"corner4","label":{"0":"Corner 4"},"hint":{"0":"Fourth recorded corner of quadrat"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Point","appearance":"Default (GPS)","metadata":{},"type":"inputLocation"},{"name":"mudmap_photo","label":{"0":"Photo of mudmap"},"hint":{"0":"Hand-drawn mudmap if required"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Image","metadata":{},"type":"inputMedia"}]},{"name":"taxon_encounter","label":{"0":"Plant Record"},"loop":true,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"field_name","label":{"0":"Taxon"},"hint":{"0":"Field collection name"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"},{"name":"photo_in_situ","label":{"0":"Photo in situ"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Image","metadata":{},"type":"inputMedia"},{"name":"taxon_encounter_location","label":{"0":"Plant location"},"hint":{"0":"Optional"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Point","appearance":"Default (GPS)","metadata":{},"type":"inputLocation"},{"name":"life_form","label":{"0":"Life form"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","options":[{"val":"w1.0_trees_with_dominant_genus","cascade":[],"text":{"0":"Trees: Dominant Genus"}},{"val":"w1.1_trees_rainforest","cascade":[],"text":{"0":"Trees: Rainforest"}},{"val":"w1.2_trees_cultivated_non_food","cascade":[],"text":{"0":"Trees: Cultivated Non-food"}},{"val":"w1.3_trees_cultivated_food","cascade":[],"text":{"0":"Trees: Cultivated Food"}},{"val":"w1.4_trees_cultivated_food","cascade":[],"text":{"0":"Trees: Planted / Cultivated"}},{"val":"w2.0_woody_plant","cascade":[],"text":{"0":"Woody Plant"}},{"val":"w2.1_mallee","cascade":[],"text":{"0":"Mallee"}},{"val":"w3.0_shrub","cascade":[],"text":{"0":"Shrub"}},{"val":"w3.1_heath_shrub","cascade":[],"text":{"0":"Heath Shrub"}},{"val":"w3.2_chenopod_shrub","cascade":[],"text":{"0":"Chenopod Shrub"}},{"val":"w3.3_samphire_shrub","cascade":[],"text":{"0":"Samphire Shrub"}},{"val":"g1.0_hummock_grass","cascade":[],"text":{"0":"Hummock Grass"}},{"val":"g2.0_tussock_grass","cascade":[],"text":{"0":"Tussock Grass"}},{"val":"g3.0_other_grass","cascade":[],"text":{"0":"Other Grass"}},{"val":"g4.0_sedge","cascade":[],"text":{"0":"Sedge"}},{"val":"g5.0_rush","cascade":[],"text":{"0":"Rush"}},{"val":"h1.0_forb","cascade":[],"text":{"0":"Forb"}},{"val":"f1.0_fern","cascade":[],"text":{"0":"Fern"}},{"val":"m1.0_bryophyte","cascade":[],"text":{"0":"Bryophyte"}},{"val":"l1.0_lichen","cascade":[],"text":{"0":"Lichen"}},{"val":"c1.0_surface_crusts","cascade":[],"text":{"0":"Surface Crusts"}},{"val":"v1.0_vine","cascade":[],"text":{"0":"Vine"}},{"val":"a1.0_aquatic_non_woody","cascade":[],"text":{"0":"Aquatic Non-woody"}},{"val":"a2.0_seagrass_marine","cascade":[],"text":{"0":"Seagrass Marine"}},{"val":"a3.0_algae_fresh_brackish","cascade":[],"text":{"0":"Algae Fresh or Brackish"}},{"val":"a4.0_algae_marine","cascade":[],"text":{"0":"Algae Marine"}},{"val":"b1.0_bare_surface","cascade":[],"text":{"0":"Bare surface"}}],"cascading":false,"other":false,"appearance":"Minimal (spinner)","metadata":{},"type":"inputSelectOne"},{"name":"voucher_specimen_barcode","label":{"0":"Voucher specimen barcode"},"hint":{"0":""},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","metadata":{},"type":"inputBarcode"},{"name":"voucher_specimen_label","label":{"0":"Voucher specimen label"},"hint":{"0":"If barcode not available"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"}]},{"name":"encounter_end_datetime","kind":"End Time","metadata":{},"type":"metadata"}],"metadata":{"version":2,"activeLanguages":{"0":"English","_counter":0,"_display":"0"},"optionsPresets":[{"name":"nvis_l3_broad_floristic_groups","options":[{"val":"w1.0_trees_with_dominant_genus","cascade":[],"text":{"0":"Trees: Dominant Genus"}},{"val":"w1.1_trees_rainforest","cascade":[],"text":{"0":"Trees: Rainforest"}},{"val":"w1.2_trees_cultivated_non_food","cascade":[],"text":{"0":"Trees: Cultivated Non-food"}},{"val":"w1.3_trees_cultivated_food","cascade":[],"text":{"0":"Trees: Cultivated Food"}},{"val":"w1.4_trees_cultivated_food","cascade":[],"text":{"0":"Trees: Planted / Cultivated"}},{"val":"w2.0_woody_plant","cascade":[],"text":{"0":"Woody Plant"}},{"val":"w2.1_mallee","cascade":[],"text":{"0":"Mallee"}},{"val":"w3.0_shrub","cascade":[],"text":{"0":"Shrub"}},{"val":"w3.1_heath_shrub","cascade":[],"text":{"0":"Heath Shrub"}},{"val":"w3.2_chenopod_shrub","cascade":[],"text":{"0":"Chenopod Shrub"}},{"val":"w3.3_samphire_shrub","cascade":[],"text":{"0":"Samphire Shrub"}},{"val":"g1.0_hummock_grass","cascade":[],"text":{"0":"Hummock Grass"}},{"val":"g2.0_tussock_grass","cascade":[],"text":{"0":"Tussock Grass"}},{"val":"g3.0_other_grass","cascade":[],"text":{"0":"Other Grass"}},{"val":"g4.0_sedge","cascade":[],"text":{"0":"Sedge"}},{"val":"g5.0_rush","cascade":[],"text":{"0":"Rush"}},{"val":"h1.0_forb","cascade":[],"text":{"0":"Forb"}},{"val":"f1.0_fern","cascade":[],"text":{"0":"Fern"}},{"val":"m1.0_bryophyte","cascade":[],"text":{"0":"Bryophyte"}},{"val":"l1.0_lichen","cascade":[],"text":{"0":"Lichen"}},{"val":"c1.0_surface_crusts","cascade":[],"text":{"0":"Surface Crusts"}},{"val":"v1.0_vine","cascade":[],"text":{"0":"Vine"}},{"val":"a1.0_aquatic_non_woody","cascade":[],"text":{"0":"Aquatic Non-woody"}},{"val":"a2.0_seagrass_marine","cascade":[],"text":{"0":"Seagrass Marine"}},{"val":"a3.0_algae_fresh_brackish","cascade":[],"text":{"0":"Algae Fresh or Brackish"}},{"val":"a4.0_algae_marine","cascade":[],"text":{"0":"Algae Marine"}},{"val":"b1.0_bare_surface","cascade":[],"text":{"0":"Bare surface"}}]}],"htitle":null,"instance_name":"","public_key":"","submission_url":""}} \ No newline at end of file +{"title":"Flora Quadrat 0.4","controls":[{"name":"encounter_start_datetime","kind":"Start Time","metadata":{},"type":"metadata"},{"name":"reporter","kind":"Username","metadata":{},"type":"metadata"},{"name":"device_id","kind":"Device ID","metadata":{},"type":"metadata"},{"name":"location","label":{"0":"Location"},"loop":false,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"area_name","label":{"0":"Quadrat name"},"hint":{"0":""},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"},{"name":"quadrat_photo","label":{"0":"Quadrat Photo"},"hint":{"0":"Capture a memorable overview"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Image","metadata":{},"type":"inputMedia"},{"name":"corner1","label":{"0":"Corner 1"},"hint":{"0":"First recorded corner of quadrat"},"defaultValue":"","readOnly":false,"required":true,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Point","appearance":"Default (GPS)","metadata":{},"type":"inputLocation"}]},{"name":"habitat","label":{"0":"Habitat"},"loop":false,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"morphological_type","label":{"0":"Morphological Type"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","options":[{"val":"crest","cascade":[],"text":{"0":"Crest"}},{"val":"upper-slope","cascade":[],"text":{"0":"Upper Slope"}},{"val":"mid-slope","cascade":[],"text":{"0":"Mid Slope"}},{"val":"lower-slope","cascade":[],"text":{"0":"Lower Slope"}},{"val":"flat","cascade":[],"text":{"0":"Flat"}},{"val":"open-depression","cascade":[],"text":{"0":"Open Depression"}},{"val":"closed-depression","cascade":[],"text":{"0":"Closed Depression"}},{"val":"hill","cascade":[],"text":{"0":"Hill"}},{"val":"ridge","cascade":[],"text":{"0":"Ridge"}}],"cascading":false,"other":false,"appearance":"Minimal (spinner)","metadata":{},"type":"inputSelectOne"},{"name":"morphological_type_photo","label":{"0":"Morphological Type Photo"},"hint":{"0":"Take photo in landscape format"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Image","metadata":{},"type":"inputMedia"}]},{"name":"vegetation_stratum","label":{"0":"Vegetation Stratum"},"loop":true,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"nvis_level3_broad_floristic_group","label":{"0":"Broad Floristic Group"},"hint":{"0":"NVIS Level III"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","options":[{"val":"w1.0_trees_with_dominant_genus","cascade":[],"text":{"0":"Trees: Dominant Genus"}},{"val":"w1.1_trees_rainforest","cascade":[],"text":{"0":"Trees: Rainforest"}},{"val":"w1.2_trees_cultivated_non_food","cascade":[],"text":{"0":"Trees: Cultivated Non-food"}},{"val":"w1.3_trees_cultivated_food","cascade":[],"text":{"0":"Trees: Cultivated Food"}},{"val":"w1.4_trees_cultivated_food","cascade":[],"text":{"0":"Trees: Planted / Cultivated"}},{"val":"w2.0_woody_plant","cascade":[],"text":{"0":"Woody Plant"}},{"val":"w2.1_mallee","cascade":[],"text":{"0":"Mallee"}},{"val":"w3.0_shrub","cascade":[],"text":{"0":"Shrub"}},{"val":"w3.1_heath_shrub","cascade":[],"text":{"0":"Heath Shrub"}},{"val":"w3.2_chenopod_shrub","cascade":[],"text":{"0":"Chenopod Shrub"}},{"val":"w3.3_samphire_shrub","cascade":[],"text":{"0":"Samphire Shrub"}},{"val":"g1.0_hummock_grass","cascade":[],"text":{"0":"Hummock Grass"}},{"val":"g2.0_tussock_grass","cascade":[],"text":{"0":"Tussock Grass"}},{"val":"g3.0_other_grass","cascade":[],"text":{"0":"Other Grass"}},{"val":"g4.0_sedge","cascade":[],"text":{"0":"Sedge"}},{"val":"g5.0_rush","cascade":[],"text":{"0":"Rush"}},{"val":"h1.0_forb","cascade":[],"text":{"0":"Forb"}},{"val":"f1.0_fern","cascade":[],"text":{"0":"Fern"}},{"val":"m1.0_bryophyte","cascade":[],"text":{"0":"Bryophyte"}},{"val":"l1.0_lichen","cascade":[],"text":{"0":"Lichen"}},{"val":"c1.0_surface_crusts","cascade":[],"text":{"0":"Surface Crusts"}},{"val":"v1.0_vine","cascade":[],"text":{"0":"Vine"}},{"val":"a1.0_aquatic_non_woody","cascade":[],"text":{"0":"Aquatic Non-woody"}},{"val":"a2.0_seagrass_marine","cascade":[],"text":{"0":"Seagrass Marine"}},{"val":"a3.0_algae_fresh_brackish","cascade":[],"text":{"0":"Algae Fresh or Brackish"}},{"val":"a4.0_algae_marine","cascade":[],"text":{"0":"Algae Marine"}},{"val":"b1.0_bare_surface","cascade":[],"text":{"0":"Bare surface"}}],"cascading":false,"other":false,"appearance":"Minimal (spinner)","metadata":{},"type":"inputSelectOne"},{"name":"max_height_m","label":{"0":"Max height of stratum [m]"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","range":{"min":"0","max":"100","minInclusive":true,"maxInclusive":true},"appearance":"Textbox","kind":"Decimal","selectRange":{"min":"1","max":"10"},"selectStep":"1","sliderTicks":true,"metadata":{},"type":"inputNumeric"},{"name":"foliage_cover","label":{"0":"Foliage cover"},"hint":{"0":"of all dominant species combined"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","options":[{"text":{"0":"D 100-70%"},"cascade":[],"val":"100-70"},{"text":{"0":"O 70-30%"},"cascade":[],"val":"70-30"},{"text":{"0":"S 30-10%"},"cascade":[],"val":"30-10"},{"text":{"0":"V 10-5%"},"cascade":[],"val":"10-5"},{"text":{"0":"L 5-0%"},"cascade":[],"val":"5-0"},{"text":{"0":"I 0%"},"cascade":[],"val":"0"}],"cascading":false,"other":false,"appearance":"Minimal (spinner)","metadata":{},"type":"inputSelectOne"},{"name":"dominant_species_1","label":{"0":"Dominant species 1"},"hint":{"0":"Canonical name"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"},{"name":"dominant_species_2","label":{"0":"Dominant species 2"},"hint":{"0":"Canonical name"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"},{"name":"dominant_species_3","label":{"0":"Dominant species 3"},"hint":{"0":"Canonical name"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"},{"name":"dominant_species_4","label":{"0":"Dominant species 4"},"hint":{"0":"Canonical name"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"}]},{"name":"perimeter","label":{"0":"Quadrat Perimeter"},"loop":false,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"corner2","label":{"0":"Corner 2"},"hint":{"0":"Second recorded corner of quadrat"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Point","appearance":"Default (GPS)","metadata":{},"type":"inputLocation"},{"name":"corner3","label":{"0":"Corner 3"},"hint":{"0":"Third recorded corner of quadrat"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Point","appearance":"Default (GPS)","metadata":{},"type":"inputLocation"},{"name":"corner4","label":{"0":"Corner 4"},"hint":{"0":"Fourth recorded corner of quadrat"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Point","appearance":"Default (GPS)","metadata":{},"type":"inputLocation"},{"name":"mudmap_photo","label":{"0":"Photo of mudmap"},"hint":{"0":"Hand-drawn mudmap if required"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Image","metadata":{},"type":"inputMedia"}]},{"name":"taxon_encounter","label":{"0":"Plant Record"},"loop":true,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"field_name","label":{"0":"Taxon"},"hint":{"0":"Field collection name"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"},{"name":"photo_in_situ","label":{"0":"Photo in situ"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Image","metadata":{},"type":"inputMedia"},{"name":"taxon_encounter_location","label":{"0":"Plant location"},"hint":{"0":"Optional"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Point","appearance":"Default (GPS)","metadata":{},"type":"inputLocation"},{"name":"life_form","label":{"0":"Life form"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","options":[{"val":"w1.0_trees_with_dominant_genus","cascade":[],"text":{"0":"Trees: Dominant Genus"}},{"val":"w1.1_trees_rainforest","cascade":[],"text":{"0":"Trees: Rainforest"}},{"val":"w1.2_trees_cultivated_non_food","cascade":[],"text":{"0":"Trees: Cultivated Non-food"}},{"val":"w1.3_trees_cultivated_food","cascade":[],"text":{"0":"Trees: Cultivated Food"}},{"val":"w1.4_trees_cultivated_food","cascade":[],"text":{"0":"Trees: Planted / Cultivated"}},{"val":"w2.0_woody_plant","cascade":[],"text":{"0":"Woody Plant"}},{"val":"w2.1_mallee","cascade":[],"text":{"0":"Mallee"}},{"val":"w3.0_shrub","cascade":[],"text":{"0":"Shrub"}},{"val":"w3.1_heath_shrub","cascade":[],"text":{"0":"Heath Shrub"}},{"val":"w3.2_chenopod_shrub","cascade":[],"text":{"0":"Chenopod Shrub"}},{"val":"w3.3_samphire_shrub","cascade":[],"text":{"0":"Samphire Shrub"}},{"val":"g1.0_hummock_grass","cascade":[],"text":{"0":"Hummock Grass"}},{"val":"g2.0_tussock_grass","cascade":[],"text":{"0":"Tussock Grass"}},{"val":"g3.0_other_grass","cascade":[],"text":{"0":"Other Grass"}},{"val":"g4.0_sedge","cascade":[],"text":{"0":"Sedge"}},{"val":"g5.0_rush","cascade":[],"text":{"0":"Rush"}},{"val":"h1.0_forb","cascade":[],"text":{"0":"Forb"}},{"val":"f1.0_fern","cascade":[],"text":{"0":"Fern"}},{"val":"m1.0_bryophyte","cascade":[],"text":{"0":"Bryophyte"}},{"val":"l1.0_lichen","cascade":[],"text":{"0":"Lichen"}},{"val":"c1.0_surface_crusts","cascade":[],"text":{"0":"Surface Crusts"}},{"val":"v1.0_vine","cascade":[],"text":{"0":"Vine"}},{"val":"a1.0_aquatic_non_woody","cascade":[],"text":{"0":"Aquatic Non-woody"}},{"val":"a2.0_seagrass_marine","cascade":[],"text":{"0":"Seagrass Marine"}},{"val":"a3.0_algae_fresh_brackish","cascade":[],"text":{"0":"Algae Fresh or Brackish"}},{"val":"a4.0_algae_marine","cascade":[],"text":{"0":"Algae Marine"}},{"val":"b1.0_bare_surface","cascade":[],"text":{"0":"Bare surface"}}],"cascading":false,"other":false,"appearance":"Minimal (spinner)","metadata":{},"type":"inputSelectOne"},{"name":"voucher_specimen_barcode","label":{"0":"Voucher specimen barcode"},"hint":{"0":""},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","metadata":{},"type":"inputBarcode"},{"name":"voucher_specimen_label","label":{"0":"Voucher specimen label"},"hint":{"0":"If barcode not available"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"}]},{"name":"encounter_end_datetime","kind":"End Time","metadata":{},"type":"metadata"}],"metadata":{"version":2,"activeLanguages":{"0":"English","_counter":0,"_display":"0"},"optionsPresets":[{"name":"nvis_l3_broad_floristic_groups","options":[{"val":"w1.0_trees_with_dominant_genus","cascade":[],"text":{"0":"Trees: Dominant Genus"}},{"val":"w1.1_trees_rainforest","cascade":[],"text":{"0":"Trees: Rainforest"}},{"val":"w1.2_trees_cultivated_non_food","cascade":[],"text":{"0":"Trees: Cultivated Non-food"}},{"val":"w1.3_trees_cultivated_food","cascade":[],"text":{"0":"Trees: Cultivated Food"}},{"val":"w1.4_trees_cultivated_food","cascade":[],"text":{"0":"Trees: Planted / Cultivated"}},{"val":"w2.0_woody_plant","cascade":[],"text":{"0":"Woody Plant"}},{"val":"w2.1_mallee","cascade":[],"text":{"0":"Mallee"}},{"val":"w3.0_shrub","cascade":[],"text":{"0":"Shrub"}},{"val":"w3.1_heath_shrub","cascade":[],"text":{"0":"Heath Shrub"}},{"val":"w3.2_chenopod_shrub","cascade":[],"text":{"0":"Chenopod Shrub"}},{"val":"w3.3_samphire_shrub","cascade":[],"text":{"0":"Samphire Shrub"}},{"val":"g1.0_hummock_grass","cascade":[],"text":{"0":"Hummock Grass"}},{"val":"g2.0_tussock_grass","cascade":[],"text":{"0":"Tussock Grass"}},{"val":"g3.0_other_grass","cascade":[],"text":{"0":"Other Grass"}},{"val":"g4.0_sedge","cascade":[],"text":{"0":"Sedge"}},{"val":"g5.0_rush","cascade":[],"text":{"0":"Rush"}},{"val":"h1.0_forb","cascade":[],"text":{"0":"Forb"}},{"val":"f1.0_fern","cascade":[],"text":{"0":"Fern"}},{"val":"m1.0_bryophyte","cascade":[],"text":{"0":"Bryophyte"}},{"val":"l1.0_lichen","cascade":[],"text":{"0":"Lichen"}},{"val":"c1.0_surface_crusts","cascade":[],"text":{"0":"Surface Crusts"}},{"val":"v1.0_vine","cascade":[],"text":{"0":"Vine"}},{"val":"a1.0_aquatic_non_woody","cascade":[],"text":{"0":"Aquatic Non-woody"}},{"val":"a2.0_seagrass_marine","cascade":[],"text":{"0":"Seagrass Marine"}},{"val":"a3.0_algae_fresh_brackish","cascade":[],"text":{"0":"Algae Fresh or Brackish"}},{"val":"a4.0_algae_marine","cascade":[],"text":{"0":"Algae Marine"}},{"val":"b1.0_bare_surface","cascade":[],"text":{"0":"Bare surface"}}]}],"htitle":null,"instance_name":"","public_key":"","submission_url":""}} diff --git a/inst/extdata/Locations.odkbuild b/inst/extdata/Locations.odkbuild index b12a4edf..1be7d0a3 100644 --- a/inst/extdata/Locations.odkbuild +++ b/inst/extdata/Locations.odkbuild @@ -1 +1 @@ -{"title":"Locations","controls":[{"name":"device_id","kind":"Device ID","metadata":{},"type":"metadata"},{"name":"start_time","kind":"Start Time","metadata":{},"type":"metadata"},{"name":"username","kind":"Username","metadata":{},"type":"metadata"},{"name":"subscriber_id","kind":"Subscriber ID","metadata":{},"type":"metadata"},{"name":"point","label":{"0":"Point"},"loop":false,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"location_point_gps","label":{"0":"Point (GPS)"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Point","appearance":"Default (GPS)","metadata":{},"type":"inputLocation"},{"name":"location_point_map","label":{"0":"Point (map)"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Point","appearance":"Show Map (GPS)","metadata":{},"type":"inputLocation"},{"name":"location_point_manual","label":{"0":"Point (manual)"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Point","appearance":"Manual (No GPS)","metadata":{},"type":"inputLocation"}]},{"name":"path","label":{"0":"Path"},"loop":false,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"location_path_gps","label":{"0":"Path (GPS)"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Path","appearance":"Default (GPS)","metadata":{},"type":"inputLocation"},{"name":"location_path_map","label":{"0":"Path (map)"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Path","appearance":"Show Map (GPS)","metadata":{},"type":"inputLocation"},{"name":"location_path_manual","label":{"0":"Path (manual)"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Path","appearance":"Manual (No GPS)","metadata":{},"type":"inputLocation"}]},{"name":"shape","label":{"0":"Shape"},"loop":false,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"location_shape_gps","label":{"0":"Shape (GPS)"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Shape","appearance":"Default (GPS)","metadata":{},"type":"inputLocation"},{"name":"location_shape_map","label":{"0":"Shape (map)"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Shape","appearance":"Show Map (GPS)","metadata":{},"type":"inputLocation"},{"name":"location_shape_manual","label":{"0":"Shape (manual)"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Shape","appearance":"Manual (No GPS)","metadata":{},"type":"inputLocation"}]},{"name":"end_time","kind":"End Time","metadata":{},"type":"metadata"}],"metadata":{"version":2,"activeLanguages":{"0":"English","_counter":0,"_display":"0"},"optionsPresets":[],"htitle":null,"instance_name":"","public_key":"","submission_url":""}} \ No newline at end of file +{"title":"Locations","controls":[{"name":"device_id","kind":"Device ID","metadata":{},"type":"metadata"},{"name":"start_time","kind":"Start Time","metadata":{},"type":"metadata"},{"name":"username","kind":"Username","metadata":{},"type":"metadata"},{"name":"subscriber_id","kind":"Subscriber ID","metadata":{},"type":"metadata"},{"name":"point","label":{"0":"Point"},"loop":false,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"location_point_gps","label":{"0":"Point (GPS)"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Point","appearance":"Default (GPS)","metadata":{},"type":"inputLocation"},{"name":"location_point_map","label":{"0":"Point (map)"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Point","appearance":"Show Map (GPS)","metadata":{},"type":"inputLocation"},{"name":"location_point_manual","label":{"0":"Point (manual)"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Point","appearance":"Manual (No GPS)","metadata":{},"type":"inputLocation"}]},{"name":"path","label":{"0":"Path"},"loop":false,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"location_path_gps","label":{"0":"Path (GPS)"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Path","appearance":"Default (GPS)","metadata":{},"type":"inputLocation"},{"name":"location_path_map","label":{"0":"Path (map)"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Path","appearance":"Show Map (GPS)","metadata":{},"type":"inputLocation"},{"name":"location_path_manual","label":{"0":"Path (manual)"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Path","appearance":"Manual (No GPS)","metadata":{},"type":"inputLocation"}]},{"name":"shape","label":{"0":"Shape"},"loop":false,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"location_shape_gps","label":{"0":"Shape (GPS)"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Shape","appearance":"Default (GPS)","metadata":{},"type":"inputLocation"},{"name":"location_shape_map","label":{"0":"Shape (map)"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Shape","appearance":"Show Map (GPS)","metadata":{},"type":"inputLocation"},{"name":"location_shape_manual","label":{"0":"Shape (manual)"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Shape","appearance":"Manual (No GPS)","metadata":{},"type":"inputLocation"}]},{"name":"end_time","kind":"End Time","metadata":{},"type":"metadata"}],"metadata":{"version":2,"activeLanguages":{"0":"English","_counter":0,"_display":"0"},"optionsPresets":[],"htitle":null,"instance_name":"","public_key":"","submission_url":""}} diff --git a/inst/extdata/Spotlighting06.odkbuild b/inst/extdata/Spotlighting06.odkbuild index 481be706..b3b1409d 100644 --- a/inst/extdata/Spotlighting06.odkbuild +++ b/inst/extdata/Spotlighting06.odkbuild @@ -1 +1 @@ -{"title":"Spotlighting 0.6","controls":[{"name":"encounter_start_datetime","kind":"Start Time","metadata":{},"type":"metadata"},{"name":"reporter","kind":"Username","metadata":{},"type":"metadata"},{"name":"device_id","kind":"Device ID","metadata":{},"type":"metadata"},{"name":"animal_details","label":{"0":"Animal details"},"loop":false,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"distance_to_animal_metres","label":{"0":"Distance to animal [m]"},"hint":{"0":""},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","range":{"min":"0","max":"","minInclusive":true,"maxInclusive":false},"appearance":"Textbox","kind":"Decimal","selectRange":{"min":"1","max":"10"},"selectStep":"1","sliderTicks":true,"metadata":{},"type":"inputNumeric"},{"name":"bearing_to_animal_degrees","label":{"0":"Bearing to animal [deg]"},"hint":{"0":"Degrees"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","range":false,"appearance":"Textbox","kind":"Integer","selectRange":{"min":"1","max":"10"},"selectStep":"1","sliderTicks":true,"metadata":{},"type":"inputNumeric"},{"name":"bearing_to_road_degrees","label":{"0":"Bearing to road [deg]"},"hint":{"0":"Degrees"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","range":false,"appearance":"Textbox","kind":"Integer","selectRange":{"min":"1","max":"10"},"selectStep":"1","sliderTicks":true,"metadata":{},"type":"inputNumeric"},{"name":"animal_height_above_ground_estimate_metres","label":{"0":"Height above ground [m]"},"hint":{"0":"Estimate to nearest metre"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","range":false,"appearance":"Textbox","kind":"Decimal","selectRange":{"min":"1","max":"10"},"selectStep":"1","sliderTicks":true,"metadata":{},"type":"inputNumeric"},{"name":"species","label":{"0":"Species"},"hint":{},"defaultValue":"","readOnly":false,"required":true,"requiredText":{"0":"Must select species"},"relevance":"","constraint":"","invalidText":{},"calculate":"","options":[{"val":"25544-australian-owlet-nightjar","cascade":[],"text":{"0":"Australian Owlet-nightjar"}},{"val":"25762-barn-owl","cascade":[],"text":{"0":"Barn Owl"}},{"val":"48016-boobook-owl","cascade":[],"text":{"0":"Boobook Owl"}},{"val":"48070-sw-brush-tailed-phascogale","cascade":[],"text":{"0":"Brush-tailed Phascogale, Wambenger"}},{"val":"24041-cat","cascade":[],"text":{"0":"Cat"}},{"val":"24092-western-quoll-chuditch","cascade":[],"text":{"0":"Chuditch, Western Quoll"}},{"val":"48144-common-brushtail-possum","cascade":[],"text":{"0":"Koomal, Common Brushtail Possum"}},{"val":"24855-masked-owl-southwest","cascade":[],"text":{"0":"Masked Owl"}},{"val":"24166-western-ringtail-possum","cascade":[],"text":{"0":"Ngwayir, Western Ringtail Possum"}},{"val":"24153-southern-brown-bandicoot","cascade":[],"text":{"0":"Quenda, Southern Brown Bandicoot"}},{"val":"24145-quokka","cascade":[],"text":{"0":"Quokka"}},{"val":"24085-rabbit","cascade":[],"text":{"0":"Rabbit"}},{"val":"24040-red-fox","cascade":[],"text":{"0":"Red Fox"}},{"val":"48024-tammar-wallaby","cascade":[],"text":{"0":"Tammar Wallaby"}},{"val":"24679-tawny-frogmouth","cascade":[],"text":{"0":"Tawny Frogmouth"}},{"val":"48022-western-brush-wallaby","cascade":[],"text":{"0":"Western Brush Wallaby"}},{"val":"24132-western-grey-kangaroo","cascade":[],"text":{"0":"Western Grey Kangaroo"}},{"val":"24162-brush-tailed-bettong","cascade":[],"text":{"0":"Woylie, Brush-tailed Bettong"}},{"val":"0-other","cascade":[],"text":{"0":"Other, specify"}}],"cascading":false,"other":false,"appearance":"Default","metadata":{},"type":"inputSelectOne"},{"name":"species_id_certainty","label":{"0":"Species ID certainty"},"hint":{},"defaultValue":"very-certain","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","options":[{"text":{"0":"Very certain"},"cascade":[],"val":"very-certain"},{"text":{"0":"Moderately certain"},"cascade":[],"val":"moderately-certain"},{"text":{"0":"Uncertain"},"cascade":[],"val":"uncertain"}],"cascading":false,"other":false,"appearance":"Default","metadata":{},"type":"inputSelectOne"},{"name":"number_of_individuals","label":{"0":"Number of individuals"},"hint":{},"defaultValue":"1","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","range":{"min":"0","max":"","minInclusive":true,"maxInclusive":false},"appearance":"Textbox","kind":"Integer","selectRange":{"min":"1","max":"10"},"selectStep":"1","sliderTicks":true,"metadata":{},"type":"inputNumeric"}]},{"name":"surroundings","label":{"0":"Substrate and surroundings"},"loop":false,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"tree_species","label":{"0":"Tree species"},"hint":{"0":"If animal on tree"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","options":[{"val":"5708-jarrah","cascade":[],"text":{"0":"Jarrah"}},{"val":"17104-marri","cascade":[],"text":{"0":"Marri"}},{"val":"21316-banksia","cascade":[],"text":{"0":"Banksia"}},{"val":"22431-gastrolobium","cascade":[],"text":{"0":"Gastrolobium"}},{"val":"0-other","cascade":[],"text":{"0":"Other, specify"}}],"cascading":false,"other":false,"appearance":"Default","metadata":{},"type":"inputSelectOne"},{"name":"tree_species_other","label":{"0":"Tree species"},"hint":{"0":"If not in shortlist"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"},{"name":"substrate_under_animal","label":{"0":"Animal Position"},"hint":{"0":"The substrate under the animal"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","options":[{"text":{"0":"Tree mature (>40cm dbh)"},"cascade":[],"val":"tree-mature"},{"text":{"0":"Pole (15-40cm dbh, >5m ht)"},"cascade":[],"val":"pole"},{"text":{"0":"Tree sapling (<15cm dbh)"},"cascade":[],"val":"tree-sapling"},{"text":{"0":"Over mature tree"},"cascade":[],"val":"over-mature-tree"},{"text":{"0":"Ground bush"},"cascade":[],"val":"ground-bush"},{"text":{"0":"Ground form"},"cascade":[],"val":"ground-form"},{"text":{"0":"Flying"},"cascade":[],"val":"flying"},{"text":{"0":"Shrub tall (>1.5m ht)"},"cascade":[],"val":"shrub-tall"},{"text":{"0":"Shrub small (<1.5m ht)"},"cascade":[],"val":"shrub-small"},{"text":{"0":"Log spoil or pile"},"cascade":[],"val":"log-spoil"},{"text":{"0":"Log, stump, or fence post"},"cascade":[],"val":"log-stump-post"}],"cascading":false,"other":false,"appearance":"Default","metadata":{},"type":"inputSelectOne"},{"name":"site_type","label":{"0":"Site type"},"hint":{"0":""},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","options":[{"text":{"0":"Unlogged / Control site"},"cascade":[],"val":"unlogged-control"},{"text":{"0":"Gap"},"cascade":[],"val":"gap"},{"text":{"0":"Shelterwood"},"cascade":[],"val":"shelterwood"},{"text":{"0":"80s harvested"},"cascade":[],"val":"harvested-80s"},{"text":{"0":"Gap 2012"},"cascade":[],"val":"gap-2012"},{"text":{"0":"Shelterwood 2012"},"cascade":[],"val":"shelterwood-2012"}],"cascading":false,"other":false,"appearance":"Default","metadata":{},"type":"inputSelectOne"},{"name":"animal_activities","label":{"0":"Animal activities"},"hint":{"0":"Anything worth mentioning"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"}]},{"name":"location_group","label":{"0":"Location"},"loop":false,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"location","label":{"0":"Location of observer"},"hint":{},"defaultValue":"","readOnly":false,"required":true,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Point","appearance":"Default (GPS)","metadata":{},"type":"inputLocation"},{"name":"gps_reference_point","label":{"0":"GPS reference point"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"},{"name":"distance_odometer","label":{"0":"Distance Odometer"},"hint":{"0":"Metres driven since start"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","range":false,"appearance":"Textbox","kind":"Integer","selectRange":{"min":"1","max":"10"},"selectStep":"1","sliderTicks":true,"metadata":{},"type":"inputNumeric"}]},{"name":"encounter_end_datetime","kind":"End Time","metadata":{},"type":"metadata"}],"metadata":{"version":2,"activeLanguages":{"0":"English","_counter":0,"_display":"0"},"optionsPresets":[],"htitle":null,"instance_name":"","public_key":"","submission_url":""}} \ No newline at end of file +{"title":"Spotlighting 0.6","controls":[{"name":"encounter_start_datetime","kind":"Start Time","metadata":{},"type":"metadata"},{"name":"reporter","kind":"Username","metadata":{},"type":"metadata"},{"name":"device_id","kind":"Device ID","metadata":{},"type":"metadata"},{"name":"animal_details","label":{"0":"Animal details"},"loop":false,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"distance_to_animal_metres","label":{"0":"Distance to animal [m]"},"hint":{"0":""},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","range":{"min":"0","max":"","minInclusive":true,"maxInclusive":false},"appearance":"Textbox","kind":"Decimal","selectRange":{"min":"1","max":"10"},"selectStep":"1","sliderTicks":true,"metadata":{},"type":"inputNumeric"},{"name":"bearing_to_animal_degrees","label":{"0":"Bearing to animal [deg]"},"hint":{"0":"Degrees"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","range":false,"appearance":"Textbox","kind":"Integer","selectRange":{"min":"1","max":"10"},"selectStep":"1","sliderTicks":true,"metadata":{},"type":"inputNumeric"},{"name":"bearing_to_road_degrees","label":{"0":"Bearing to road [deg]"},"hint":{"0":"Degrees"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","range":false,"appearance":"Textbox","kind":"Integer","selectRange":{"min":"1","max":"10"},"selectStep":"1","sliderTicks":true,"metadata":{},"type":"inputNumeric"},{"name":"animal_height_above_ground_estimate_metres","label":{"0":"Height above ground [m]"},"hint":{"0":"Estimate to nearest metre"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","range":false,"appearance":"Textbox","kind":"Decimal","selectRange":{"min":"1","max":"10"},"selectStep":"1","sliderTicks":true,"metadata":{},"type":"inputNumeric"},{"name":"species","label":{"0":"Species"},"hint":{},"defaultValue":"","readOnly":false,"required":true,"requiredText":{"0":"Must select species"},"relevance":"","constraint":"","invalidText":{},"calculate":"","options":[{"val":"25544-australian-owlet-nightjar","cascade":[],"text":{"0":"Australian Owlet-nightjar"}},{"val":"25762-barn-owl","cascade":[],"text":{"0":"Barn Owl"}},{"val":"48016-boobook-owl","cascade":[],"text":{"0":"Boobook Owl"}},{"val":"48070-sw-brush-tailed-phascogale","cascade":[],"text":{"0":"Brush-tailed Phascogale, Wambenger"}},{"val":"24041-cat","cascade":[],"text":{"0":"Cat"}},{"val":"24092-western-quoll-chuditch","cascade":[],"text":{"0":"Chuditch, Western Quoll"}},{"val":"48144-common-brushtail-possum","cascade":[],"text":{"0":"Koomal, Common Brushtail Possum"}},{"val":"24855-masked-owl-southwest","cascade":[],"text":{"0":"Masked Owl"}},{"val":"24166-western-ringtail-possum","cascade":[],"text":{"0":"Ngwayir, Western Ringtail Possum"}},{"val":"24153-southern-brown-bandicoot","cascade":[],"text":{"0":"Quenda, Southern Brown Bandicoot"}},{"val":"24145-quokka","cascade":[],"text":{"0":"Quokka"}},{"val":"24085-rabbit","cascade":[],"text":{"0":"Rabbit"}},{"val":"24040-red-fox","cascade":[],"text":{"0":"Red Fox"}},{"val":"48024-tammar-wallaby","cascade":[],"text":{"0":"Tammar Wallaby"}},{"val":"24679-tawny-frogmouth","cascade":[],"text":{"0":"Tawny Frogmouth"}},{"val":"48022-western-brush-wallaby","cascade":[],"text":{"0":"Western Brush Wallaby"}},{"val":"24132-western-grey-kangaroo","cascade":[],"text":{"0":"Western Grey Kangaroo"}},{"val":"24162-brush-tailed-bettong","cascade":[],"text":{"0":"Woylie, Brush-tailed Bettong"}},{"val":"0-other","cascade":[],"text":{"0":"Other, specify"}}],"cascading":false,"other":false,"appearance":"Default","metadata":{},"type":"inputSelectOne"},{"name":"species_id_certainty","label":{"0":"Species ID certainty"},"hint":{},"defaultValue":"very-certain","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","options":[{"text":{"0":"Very certain"},"cascade":[],"val":"very-certain"},{"text":{"0":"Moderately certain"},"cascade":[],"val":"moderately-certain"},{"text":{"0":"Uncertain"},"cascade":[],"val":"uncertain"}],"cascading":false,"other":false,"appearance":"Default","metadata":{},"type":"inputSelectOne"},{"name":"number_of_individuals","label":{"0":"Number of individuals"},"hint":{},"defaultValue":"1","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","range":{"min":"0","max":"","minInclusive":true,"maxInclusive":false},"appearance":"Textbox","kind":"Integer","selectRange":{"min":"1","max":"10"},"selectStep":"1","sliderTicks":true,"metadata":{},"type":"inputNumeric"}]},{"name":"surroundings","label":{"0":"Substrate and surroundings"},"loop":false,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"tree_species","label":{"0":"Tree species"},"hint":{"0":"If animal on tree"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","options":[{"val":"5708-jarrah","cascade":[],"text":{"0":"Jarrah"}},{"val":"17104-marri","cascade":[],"text":{"0":"Marri"}},{"val":"21316-banksia","cascade":[],"text":{"0":"Banksia"}},{"val":"22431-gastrolobium","cascade":[],"text":{"0":"Gastrolobium"}},{"val":"0-other","cascade":[],"text":{"0":"Other, specify"}}],"cascading":false,"other":false,"appearance":"Default","metadata":{},"type":"inputSelectOne"},{"name":"tree_species_other","label":{"0":"Tree species"},"hint":{"0":"If not in shortlist"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"},{"name":"substrate_under_animal","label":{"0":"Animal Position"},"hint":{"0":"The substrate under the animal"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","options":[{"text":{"0":"Tree mature (>40cm dbh)"},"cascade":[],"val":"tree-mature"},{"text":{"0":"Pole (15-40cm dbh, >5m ht)"},"cascade":[],"val":"pole"},{"text":{"0":"Tree sapling (<15cm dbh)"},"cascade":[],"val":"tree-sapling"},{"text":{"0":"Over mature tree"},"cascade":[],"val":"over-mature-tree"},{"text":{"0":"Ground bush"},"cascade":[],"val":"ground-bush"},{"text":{"0":"Ground form"},"cascade":[],"val":"ground-form"},{"text":{"0":"Flying"},"cascade":[],"val":"flying"},{"text":{"0":"Shrub tall (>1.5m ht)"},"cascade":[],"val":"shrub-tall"},{"text":{"0":"Shrub small (<1.5m ht)"},"cascade":[],"val":"shrub-small"},{"text":{"0":"Log spoil or pile"},"cascade":[],"val":"log-spoil"},{"text":{"0":"Log, stump, or fence post"},"cascade":[],"val":"log-stump-post"}],"cascading":false,"other":false,"appearance":"Default","metadata":{},"type":"inputSelectOne"},{"name":"site_type","label":{"0":"Site type"},"hint":{"0":""},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","options":[{"text":{"0":"Unlogged / Control site"},"cascade":[],"val":"unlogged-control"},{"text":{"0":"Gap"},"cascade":[],"val":"gap"},{"text":{"0":"Shelterwood"},"cascade":[],"val":"shelterwood"},{"text":{"0":"80s harvested"},"cascade":[],"val":"harvested-80s"},{"text":{"0":"Gap 2012"},"cascade":[],"val":"gap-2012"},{"text":{"0":"Shelterwood 2012"},"cascade":[],"val":"shelterwood-2012"}],"cascading":false,"other":false,"appearance":"Default","metadata":{},"type":"inputSelectOne"},{"name":"animal_activities","label":{"0":"Animal activities"},"hint":{"0":"Anything worth mentioning"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"}]},{"name":"location_group","label":{"0":"Location"},"loop":false,"fieldList":true,"relevance":"","metadata":{},"type":"group","children":[{"name":"location","label":{"0":"Location of observer"},"hint":{},"defaultValue":"","readOnly":false,"required":true,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","kind":"Point","appearance":"Default (GPS)","metadata":{},"type":"inputLocation"},{"name":"gps_reference_point","label":{"0":"GPS reference point"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"},{"name":"distance_odometer","label":{"0":"Distance Odometer"},"hint":{"0":"Metres driven since start"},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","range":false,"appearance":"Textbox","kind":"Integer","selectRange":{"min":"1","max":"10"},"selectStep":"1","sliderTicks":true,"metadata":{},"type":"inputNumeric"}]},{"name":"encounter_end_datetime","kind":"End Time","metadata":{},"type":"metadata"}],"metadata":{"version":2,"activeLanguages":{"0":"English","_counter":0,"_display":"0"},"optionsPresets":[],"htitle":null,"instance_name":"","public_key":"","submission_url":""}} diff --git a/inst/extdoc/ruODK.pdf b/inst/extdoc/ruODK.pdf index ddf67626..d5b89d3f 100644 Binary files a/inst/extdoc/ruODK.pdf and b/inst/extdoc/ruODK.pdf differ diff --git a/inst/joss/paper.bib b/inst/joss/paper.bib index 2a51e029..7ec0a1b6 100644 --- a/inst/joss/paper.bib +++ b/inst/joss/paper.bib @@ -55,4 +55,4 @@ @inproceedings{hartung publisher = {ACM}, address = {New York, NY, USA}, keywords = {ICTD, client-server distributed systems, mobile computing, mobile phones}, -} \ No newline at end of file +} diff --git a/inst/joss/paper.md b/inst/joss/paper.md index 37817c78..46e60d32 100644 --- a/inst/joss/paper.md +++ b/inst/joss/paper.md @@ -26,7 +26,7 @@ ruODK [@ruodk] is an R Client for the ODK Central API. # Background -Open Data Kit (ODK, [@odk], [@hartung]) is a suite of open source tools that +Open Data Kit (ODK, [@odk], [@hartung]) is a suite of open source tools that help organizations collect and manage data. The core ODK tools are [@odkdocs]: @@ -40,7 +40,7 @@ The core ODK tools are [@odkdocs]: The core workflow of ODK is: -- A form for digital data capture is designed, either by hand, or using form +- A form for digital data capture is designed, either by hand, or using form builders like ODK Build. - A data clearinghouse like ODK Aggregate or ODK Central disseminates these form templates to authorised data collection devices (Android devices running @@ -71,38 +71,38 @@ automate data access and analysis (ruODK). `ruODK` aims: -- To wrap all ODK Central API endpoints with a focus on **data access**. +- To wrap all ODK Central API endpoints with a focus on **data access**. While this is mostly not a hard task, there is still a small barrier to novice R users, and some duplication of code. - To provide working examples of interacting with the ODK Central API. -- To provide convenience helpers for the day to day tasks when working with - ODK Central data in R: transforming the ODK Central API output into tidy +- To provide convenience helpers for the day to day tasks when working with + ODK Central data in R: transforming the ODK Central API output into tidy R formats. - + ## Out of scope -- To wrap "management" API endpoints. The ODK Central GUI already provides a - highly capable interface for the management of users, roles, permissions, +- To wrap "management" API endpoints. The ODK Central GUI already provides a + highly capable interface for the management of users, roles, permissions, projects, and forms. - ODK Central is a [VueJS application](https://github.com/getodk/central-frontend/) + ODK Central is a [VueJS application](https://github.com/getodk/central-frontend/) working on the "management" API endpoints of the ODK Central back-end. -- To provide extensive data visualisation capability. - We show only minimal examples of data visualisation and presentation, mainly +- To provide extensive data visualisation capability. + We show only minimal examples of data visualisation and presentation, mainly to illustrate the example data. - + # Typical use cases ## Smaller projects -Smaller projects, such as ephemeral research projects or proof of concept studies, +Smaller projects, such as ephemeral research projects or proof of concept studies, may generate a manageable number of form submissions over a limited amount of time. Here, it may be sufficient to export the entire set of submissions, analyse and visualise it, and generate some products such as a CSV export of the data, vector or raster figures and maps for publications, and a rendered report. -The vignettes "odata-api" and "restful-api" show examples of this use case. -They differ in that the vignette "odata-api" retrieves submissions through the -OData API, while the vignette "restful-api" retrieves submissions through the +The vignettes "odata-api" and "restful-api" show examples of this use case. +They differ in that the vignette "odata-api" retrieves submissions through the +OData API, while the vignette "restful-api" retrieves submissions through the RESTful API. ## Larger projects @@ -113,11 +113,11 @@ Such projects typically store their data in dedicated databases. There, the data can be value-added through QA/QC/review, through integration with other data sources, and through further data processing. This requires the data to be regularly and incrementally exported from ODK Central, transformed into the target -data formats (which may differ from ODK Central's output), and loaded into the +data formats (which may differ from ODK Central's output), and loaded into the target database. -In this case, ruODK assists with the retrieval of data, inspection of new vs. -existing submissions, and export into an intermediary format, such as CSV -snapshots. +In this case, ruODK assists with the retrieval of data, inspection of new vs. +existing submissions, and export into an intermediary format, such as CSV +snapshots. -# References \ No newline at end of file +# References diff --git a/inst/rmarkdown/templates/odata/skeleton/skeleton.Rmd b/inst/rmarkdown/templates/odata/skeleton/skeleton.Rmd index e8470d39..bd767dc3 100644 --- a/inst/rmarkdown/templates/odata/skeleton/skeleton.Rmd +++ b/inst/rmarkdown/templates/odata/skeleton/skeleton.Rmd @@ -15,13 +15,13 @@ output: latex_engine: xelatex word_document: default --- - @@ -50,8 +50,8 @@ CKAN_KEY <- "my-ckan-api-key" ``` - -```{r data_vis} -skimr::skim(data) +```{r data_vis_dplyr} dplyr::glimpse(data) +``` + +For the next examples, have `skimr` and `DT` installed. + +```{r data_vis_skimr, eval = requireNamespace("skimr")} +skimr::skim(data) +``` + +```{r data_vis_dt, eval = requireNamespace("DT")} DT::datatable(head(data)) ``` @@ -190,10 +198,10 @@ The form submissions are now extracted and visualised. What's next: * Upload these artifacts to a CKAN data catalogue. * Upload same artifacts to Google Drive. -Notes: +Notes: -* Generate the HTML report once off without the next chunk (`eval=F`), -as the chunk refers to the rendered output file (HTML) before the file is +* Generate the HTML report once off without the next chunk (`eval=F`), +as the chunk refers to the rendered output file (HTML) before the file is created initially. * Run report always twice to generate (run 1) and upload (run 2) the latest HTML. --> @@ -219,8 +227,8 @@ zip(zipfile = zip_fn, files = fs::dir_ls(loc)) # CKAN # # Upload to a CKAN data catalogue -# Needs url and API key of a write permitted user -# See ROpenSci package ckanr +# Needs CKAN_URL and API CKAN_KEY with org editor permissions +# See https://docs.ropensci.org/ckanr/ ckanr::ckanr_setup(url = Sys.getenv("CKAN_URL"), key = Sys.getenv("CKAN_KEY")) ckan_ds_name <- "my-ckan-dataset-slug" diff --git a/inst/schemaorg.json b/inst/schemaorg.json index 0d8a827d..b6fe4598 100644 --- a/inst/schemaorg.json +++ b/inst/schemaorg.json @@ -4,7 +4,7 @@ "author": { "id": "https://orcid.org/0000-0003-4269-4242", "type": "Person", - "email": "Florian.Mayer@dbca.wa.gov.au", + "email": "Florian.Mayer@dpc.wa.gov.au", "familyName": "Mayer", "givenName": [ "Florian", @@ -30,7 +30,7 @@ "type": "Organization", "name": "DBCA" }, - "description": "Access and tidy up data from the 'ODK Central' API. 'ODK Central' is a clearinghouse for digitally captured data . The 'ODK Central' API is documented at .", + "description": "Access and tidy up data from the 'ODK Central' API. 'ODK Central' is a clearinghouse for digitally captured data using ODK . It manages user accounts and permissions, stores form definitions, and allows data collection clients like 'ODK Collect' to connect to it for form download and submission upload. The 'ODK Central' API is documented at .", "funder": [ { "type": "Organization", @@ -48,6 +48,6 @@ "name": "R", "url": "https://r-project.org" }, - "runtimePlatform": "R version 4.2.1 (2022-06-23)", - "version": "1.3.12" + "runtimePlatform": "R version 4.4.1 (2024-06-14 ucrt)", + "version": "1.5.0" } diff --git a/man-roxygen/param-did.R b/man-roxygen/param-did.R index f26e7e48..2bba39ca 100644 --- a/man-roxygen/param-did.R +++ b/man-roxygen/param-did.R @@ -1 +1,3 @@ #' @param did (chr) The name of the Entity List, internally called Dataset. +#' The function will error if this parameter is not given. +#' Default: "". diff --git a/man-roxygen/param-eid.R b/man-roxygen/param-eid.R new file mode 100644 index 00000000..53837cdc --- /dev/null +++ b/man-roxygen/param-eid.R @@ -0,0 +1,4 @@ +#' @param eid (chr) The UUID of an Entity, which can be retrieved by +#' `entity_list()`. +#' The function will error if this parameter is not given. +#' Default: "". diff --git a/man-roxygen/tpl-auth-missing.R b/man-roxygen/tpl-auth-missing.R new file mode 100644 index 00000000..3576f265 --- /dev/null +++ b/man-roxygen/tpl-auth-missing.R @@ -0,0 +1,3 @@ +#' ## Authentication +#' This function will fail with incorrect or missing authentication. +#' diff --git a/man-roxygen/tpl-compat-2022-3.R b/man-roxygen/tpl-compat-2022-3.R new file mode 100644 index 00000000..d15d1874 --- /dev/null +++ b/man-roxygen/tpl-compat-2022-3.R @@ -0,0 +1,4 @@ +#' ## Compatibility +#' This function is supported from ODK Central v2022.3 and will warn if the +#' given `odkc_version` is lower. +#' diff --git a/man-roxygen/tpl-def-entitylist.R b/man-roxygen/tpl-def-entitylist.R new file mode 100644 index 00000000..511813d8 --- /dev/null +++ b/man-roxygen/tpl-def-entitylist.R @@ -0,0 +1,4 @@ +#' An Entity List is a named collection of Entities that have the same properties. +#' An Entity List can be linked to Forms as Attachments. +#' This will make it available to clients as an automatically-updating CSV. +#' diff --git a/man-roxygen/tpl-entitylist-dataset.R b/man-roxygen/tpl-entitylist-dataset.R new file mode 100644 index 00000000..fff8f483 --- /dev/null +++ b/man-roxygen/tpl-entitylist-dataset.R @@ -0,0 +1,5 @@ +#' ## Entity Lists are Datasets +#' ODK Central calls Entity Lists internally Datasets. `ruODK` chooses the term +#' Entity Lists as it is used in the ODK Central user interface and conveys +#' its relation to Entities better than the term Dataset. +#' diff --git a/man-roxygen/tpl-names-cleaned-top-level.R b/man-roxygen/tpl-names-cleaned-top-level.R new file mode 100644 index 00000000..41f74cb8 --- /dev/null +++ b/man-roxygen/tpl-names-cleaned-top-level.R @@ -0,0 +1,4 @@ +#' ## Names +#' Names are cleaned at the top level only. List columns contain original +#' `camelCase` names. +#' diff --git a/man-roxygen/tpl-rusetup.R b/man-roxygen/tpl-rusetup.R index 3700d9d1..512ccfdb 100644 --- a/man-roxygen/tpl-rusetup.R +++ b/man-roxygen/tpl-rusetup.R @@ -1,2 +1,2 @@ #' # See vignette("setup") for setup and authentication options -#' # ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") +#' # ruODK::ru_setup(svc = "... .svc", un = "...", pw = "...") diff --git a/man-roxygen/tpl-structure-nested.R b/man-roxygen/tpl-structure-nested.R new file mode 100644 index 00000000..7378a0b7 --- /dev/null +++ b/man-roxygen/tpl-structure-nested.R @@ -0,0 +1,8 @@ +#' ## Structure +#' The response from ODK Central from this endpoint is irregular and dynamic. +#' Depending on the life history of records in ODK Central, some parts may be +#' populated with deeply nested structures, empty, or missing. +#' `ruODK` preserves the original structure as not to introduce additional +#' complexity. If a use case exists to decompose the original structure further +#' please create a GitHub issue. +#' diff --git a/man/attachment_get.Rd b/man/attachment_get.Rd index 891bf069..f1ee4869 100644 --- a/man/attachment_get.Rd +++ b/man/attachment_get.Rd @@ -138,7 +138,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/attachment_link.Rd b/man/attachment_link.Rd index 1692caf0..8ad14898 100644 --- a/man/attachment_link.Rd +++ b/man/attachment_link.Rd @@ -64,7 +64,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/attachment_list.Rd b/man/attachment_list.Rd index 1ef8e9bc..2eac9551 100644 --- a/man/attachment_list.Rd +++ b/man/attachment_list.Rd @@ -80,7 +80,7 @@ ruODK::ru_setup(svc = "...") sl <- submission_list() # Step 3a: Get attachment list for first submission -al <- get_one_submission_attachment_list(sl$instance_id[[1]]) +al <- get_one_submission_att_list(sl$instance_id[[1]]) # Ste 3b: Get all attachments for all submissions all <- attachment_list(sl$instance_id) diff --git a/man/attachment_url.Rd b/man/attachment_url.Rd index fa53df53..d179a99b 100644 --- a/man/attachment_url.Rd +++ b/man/attachment_url.Rd @@ -73,7 +73,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/drop_null_coords.Rd b/man/drop_null_coords.Rd index 87eac056..58a3e90b 100644 --- a/man/drop_null_coords.Rd +++ b/man/drop_null_coords.Rd @@ -66,7 +66,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/encryption_key_list.Rd b/man/encryption_key_list.Rd index c046cade..fdf916de 100644 --- a/man/encryption_key_list.Rd +++ b/man/encryption_key_list.Rd @@ -11,7 +11,7 @@ encryption_key_list( un = get_default_un(), pw = get_default_pw(), retries = get_retries(), - orders = c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd"), + orders = get_default_orders(), tz = get_default_tz() ) } diff --git a/man/entity_audits.Rd b/man/entity_audits.Rd new file mode 100644 index 00000000..4429f826 --- /dev/null +++ b/man/entity_audits.Rd @@ -0,0 +1,156 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/entity_audits.R +\name{entity_audits} +\alias{entity_audits} +\title{Return server audit logs of one Entity.} +\usage{ +entity_audits( + pid = get_default_pid(), + did = "", + eid = "", + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = get_default_orders(), + tz = get_default_tz() +) +} +\arguments{ +\item{pid}{The numeric ID of the project, e.g.: 2. + +Default: \code{\link{get_default_pid}}. + +Set default \code{pid} through \code{ru_setup(pid="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{did}{(chr) The name of the Entity List, internally called Dataset. +The function will error if this parameter is not given. +Default: "".} + +\item{eid}{(chr) The UUID of an Entity, which can be retrieved by +\code{entity_list()}. +The function will error if this parameter is not given. +Default: "".} + +\item{url}{The ODK Central base URL without trailing slash. + +Default: \code{\link{get_default_url}}. + +Set default \code{url} through \code{ru_setup(url="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{un}{The ODK Central username (an email address). +Default: \code{\link{get_default_un}}. +Set default \code{un} through \code{ru_setup(un="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{pw}{The ODK Central password. +Default: \code{\link{get_default_pw}}. +Set default \code{pw} through \code{ru_setup(pw="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{retries}{The number of attempts to retrieve a web resource. + +This parameter is given to \code{\link[httr]{RETRY}(times = retries)}. + +Default: 3.} + +\item{odkc_version}{The ODK Central version as a semantic version string +(year.minor.patch), e.g. "2023.5.1". The version is shown on ODK Central's +version page \verb{/version.txt}. Discard the "v". +\code{ruODK} uses this parameter to adjust for breaking changes in ODK Central. + +Default: \code{\link{get_default_odkc_version}} or "2023.5.1" if unset. + +Set default \code{get_default_odkc_version} through +\code{ru_setup(odkc_version="2023.5.1")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{orders}{(vector of character) Orders of datetime elements for +lubridate. + +Default: +\code{c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd")}.} + +\item{tz}{A timezone to convert dates and times to. + +Read \code{vignette("setup", package = "ruODK")} to learn how \code{ruODK}'s +timezone can be set globally or per function.} +} +\value{ +A tibble with one row per audit log. +See \url{https://docs.getodk.org/central-api-entity-management/#entity-audit-log} +for the full schema. +Top level list elements are renamed from ODK's \code{camelCase} to \code{snake_case}. +} +\description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#maturing}{\figure{lifecycle-maturing.svg}{options: alt='[Maturing]'}}}{\strong{[Maturing]}} +} +\details{ +This returns +\href{https://docs.getodk.org/central-api-system-endpoints/#server-audit-logs}{Server Audit Logs} +relating to an Entity. The most recent log is returned first. + +The authenticated user must have permissions on ODK Central to retrieve +audit logs. +} +\examples{ +\dontrun{ +# See vignette("setup") for setup and authentication options +# ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") + +el <- entitylist_list() + +# Entity List name (dataset ID, did) +did <- el$name[1] + +# All Entities of Entity List +en <- entity_list(did = did) + +ed <- entity_detail(did = did, eid = en$uuid[1]) + +e_label <- ed$current_version$label + +# This example updates one field which exists in the example form. +# Your own Entity will have different fields to update. +e_data <- list( + details = paste0( + ed$current_version$data$details, ". Updated on ", Sys.time() + ) +) + +# Update the Entity (implicitly forced update) +eu <- entity_update( + did = did, + eid = en$uuid[1], + label = e_label, + data = e_data +) + +# Return the server audit logs +ea <- entity_audits(did = did, eid = en$uuid[1]) +ea +} +} +\seealso{ +\url{https://docs.getodk.org/central-api-entity-management/#entity-audit-log} + +Other entity-management: +\code{\link{entity_changes}()}, +\code{\link{entity_create}()}, +\code{\link{entity_delete}()}, +\code{\link{entity_detail}()}, +\code{\link{entity_list}()}, +\code{\link{entity_update}()}, +\code{\link{entity_versions}()}, +\code{\link{entitylist_detail}()}, +\code{\link{entitylist_download}()}, +\code{\link{entitylist_list}()}, +\code{\link{entitylist_update}()} +} +\concept{entity-management} diff --git a/man/entity_changes.Rd b/man/entity_changes.Rd new file mode 100644 index 00000000..3f5a7cf1 --- /dev/null +++ b/man/entity_changes.Rd @@ -0,0 +1,155 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/entity_changes.R +\name{entity_changes} +\alias{entity_changes} +\title{List changes to one Entity.} +\usage{ +entity_changes( + pid = get_default_pid(), + did = "", + eid = "", + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = get_default_orders(), + tz = get_default_tz() +) +} +\arguments{ +\item{pid}{The numeric ID of the project, e.g.: 2. + +Default: \code{\link{get_default_pid}}. + +Set default \code{pid} through \code{ru_setup(pid="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{did}{(chr) The name of the Entity List, internally called Dataset. +The function will error if this parameter is not given. +Default: "".} + +\item{eid}{(chr) The UUID of an Entity, which can be retrieved by +\code{entity_list()}. +The function will error if this parameter is not given. +Default: "".} + +\item{url}{The ODK Central base URL without trailing slash. + +Default: \code{\link{get_default_url}}. + +Set default \code{url} through \code{ru_setup(url="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{un}{The ODK Central username (an email address). +Default: \code{\link{get_default_un}}. +Set default \code{un} through \code{ru_setup(un="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{pw}{The ODK Central password. +Default: \code{\link{get_default_pw}}. +Set default \code{pw} through \code{ru_setup(pw="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{retries}{The number of attempts to retrieve a web resource. + +This parameter is given to \code{\link[httr]{RETRY}(times = retries)}. + +Default: 3.} + +\item{odkc_version}{The ODK Central version as a semantic version string +(year.minor.patch), e.g. "2023.5.1". The version is shown on ODK Central's +version page \verb{/version.txt}. Discard the "v". +\code{ruODK} uses this parameter to adjust for breaking changes in ODK Central. + +Default: \code{\link{get_default_odkc_version}} or "2023.5.1" if unset. + +Set default \code{get_default_odkc_version} through +\code{ru_setup(odkc_version="2023.5.1")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{orders}{(vector of character) Orders of datetime elements for +lubridate. + +Default: +\code{c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd")}.} + +\item{tz}{A timezone to convert dates and times to. + +Read \code{vignette("setup", package = "ruODK")} to learn how \code{ruODK}'s +timezone can be set globally or per function.} +} +\value{ +A nested list of lists. +The first nesting level corresponds to entity updates, the second level +lists each updated property. Within the nested list, the names are +\code{old} (old value), \code{new} (new value), \code{propertyName} (name of changed +entity property). +} +\description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#maturing}{\figure{lifecycle-maturing.svg}{options: alt='[Maturing]'}}}{\strong{[Maturing]}} +} +\details{ +This returns the changes, or edits, between different versions of an Entity. +These changes are returned as a list of lists. +Between two Entities, there is a list of objects representing how each +property changed. This change object contains the old and new values, as well +as the property name. +} +\examples{ +\dontrun{ +# See vignette("setup") for setup and authentication options +# ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") + +el <- entitylist_list() + +# Entity List name (dataset ID, did) +did <- el$name[1] + +# All Entities of Entity List +en <- entity_list(did = did) + +ed <- entity_detail(did = did, eid = en$uuid[1]) + +e_label <- ed$current_version$label + +# This example updates one field which exists in the example form. +# Your own Entity will have different fields to update. +e_data <- list( + details = paste0( + ed$current_version$data$details, ". Updated on ", Sys.time() + ) +) + +# Update the Entity (implicitly forced update) +eu <- entity_update( + did = did, + eid = en$uuid[1], + label = e_label, + data = e_data +) + +ec <- entity_changes(did = did, eid = en$uuid[1]) +ec +} +} +\seealso{ +\url{https://docs.getodk.org/central-api-entity-management/#getting-changes-between-versions} + +Other entity-management: +\code{\link{entity_audits}()}, +\code{\link{entity_create}()}, +\code{\link{entity_delete}()}, +\code{\link{entity_detail}()}, +\code{\link{entity_list}()}, +\code{\link{entity_update}()}, +\code{\link{entity_versions}()}, +\code{\link{entitylist_detail}()}, +\code{\link{entitylist_download}()}, +\code{\link{entitylist_list}()}, +\code{\link{entitylist_update}()} +} +\concept{entity-management} diff --git a/man/entity_create.Rd b/man/entity_create.Rd new file mode 100644 index 00000000..fe655acf --- /dev/null +++ b/man/entity_create.Rd @@ -0,0 +1,231 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/entity_create.R +\name{entity_create} +\alias{entity_create} +\title{Creates exactly one Entity in the Dataset.} +\usage{ +entity_create( + pid = get_default_pid(), + did = "", + label = "", + uuid = "", + notes = "", + data = list(), + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = get_default_orders(), + tz = get_default_tz() +) +} +\arguments{ +\item{pid}{The numeric ID of the project, e.g.: 2. + +Default: \code{\link{get_default_pid}}. + +Set default \code{pid} through \code{ru_setup(pid="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{did}{(chr) The name of the Entity List, internally called Dataset. +The function will error if this parameter is not given. +Default: "".} + +\item{label}{(character) The Entity label which must be a non-empty string. +If the label is given, a single entity is created using \code{data}, \code{notes}, +and \code{uuid} if given. +If the label is kept at the default (or omitted), multiple entities are +created using \code{data} and \code{notes} and ignoring \code{uuid}. +Default: \code{""}.} + +\item{uuid}{(character) A single UUID to assign to the entity. +Default: \code{""}. With the default, Central will create and assign a UUID. +This parameter is only used when creating a single entity (\code{label} +non-empty) and ignored when creating multiple entities (\code{label} empty).} + +\item{notes}{(character) Metadata about the request which can be retrieved +using the entity audit log. +Default: \code{""}.} + +\item{data}{(list) A named list of Entity properties to create a single +Entity, or a nested list with an array of Entity data to create multiple +Entities. The nested lists representing individual entities must be valid +as in they must contain a label, valid data for the respective entity +properties, and can contain an optional UUID. +See details and the ODK documentation for the exact format. +Default: \code{list()}.} + +\item{url}{The ODK Central base URL without trailing slash. + +Default: \code{\link{get_default_url}}. + +Set default \code{url} through \code{ru_setup(url="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{un}{The ODK Central username (an email address). +Default: \code{\link{get_default_un}}. +Set default \code{un} through \code{ru_setup(un="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{pw}{The ODK Central password. +Default: \code{\link{get_default_pw}}. +Set default \code{pw} through \code{ru_setup(pw="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{retries}{The number of attempts to retrieve a web resource. + +This parameter is given to \code{\link[httr]{RETRY}(times = retries)}. + +Default: 3.} + +\item{odkc_version}{The ODK Central version as a semantic version string +(year.minor.patch), e.g. "2023.5.1". The version is shown on ODK Central's +version page \verb{/version.txt}. Discard the "v". +\code{ruODK} uses this parameter to adjust for breaking changes in ODK Central. + +Default: \code{\link{get_default_odkc_version}} or "2023.5.1" if unset. + +Set default \code{get_default_odkc_version} through +\code{ru_setup(odkc_version="2023.5.1")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{orders}{(vector of character) Orders of datetime elements for +lubridate. + +Default: +\code{c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd")}.} + +\item{tz}{A timezone to convert dates and times to. + +Read \code{vignette("setup", package = "ruODK")} to learn how \code{ruODK}'s +timezone can be set globally or per function.} +} +\value{ +A nested list identical to the return value of \code{entity_detail}. +See \url{https://docs.getodk.org/central-api-entity-management/#creating-entities} +for the full schema. +Top level list elements are renamed from ODK's \code{camelCase} to \code{snake_case}. +Nested list elements have the original \code{camelCase}. +} +\description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} +} +\details{ +\subsection{Creating a single Entity}{ + +For creating a single Entity, include +\itemize{ +\item An optional \code{uuid}. If skipped, Central will create a UUID for the Entity. +\item The Entity \code{label} must be non-empty. +\item A named list \code{data} representing the Entity's fields. The value type of +all properties is string. +} + +\code{uuid = "..."} +\code{label = "John Doe"} +\code{data = list("firstName" = "John", "age" = "22")} + +This translates to JSON + +\verb{\{ "label": "John Doe", "data": \{ "firstName": "John", "age": "22" \} \}} +} + +\subsection{Creating multiple Entities}{ + +For creating multiple Entities in bulk, the request body takes an array +entities containing a list of Entity objects as described above. +The bulk entity version also takes a source property with a required name +field and optional size, for example to capture the file name and size of a +bulk upload source (in MB). + +\if{html}{\out{
}}\preformatted{data=list( + "entities" = c( + list("label" = "Entity 1", "data" = list("field1" = "value1")), + list("label" = "Entity 2", "data" = list("field1" = "value2")) + ), + "source" = list("name" = "file.csv", "size" = 100) + ) +}\if{html}{\out{
}} + +This translates to JSON + +\verb{\{ "entities": [...], "source": \{"name": "file.csv", "size": 100\} \}} + +You can provide \code{notes} to store the metadata about the request. +The metadata is included in the POST request as header \code{X-Action-Notes} and +can retrieved using Entity Audit Log. +} +} +\examples{ +\dontrun{ +# See vignette("setup") for setup and authentication options +# ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") + +el <- entitylist_list() + +# Entity List name (dataset ID, did) +did <- el$name[1] + +# All Entities of Entity List +en <- entity_list(did = did) + +# Create a single entity +ec <- entity_create( + did = did, + label = "Entity label", + notes = "Metadata about the created entity", + data = list("field1" = "value1", "field2" = "value1") +) +ec + +# Create multiple entities, example: test form "problems" +label <- c( + glue::glue( + "Entity {nrow(en) + 1} created by ruODK package test on {Sys.time()}" + ), + glue::glue( + "Entity {nrow(en) + 2} created by ruODK package test on {Sys.time()}" + ) +) +notes <- glue::glue("Two entities created by ruODK package test on {Sys.time()}") +status <- c("needs_followup", "needs_followup") +details <- c("ruODK package test", "ruODK package test") +geometry <- c("-33.2 115.0 0.0 0.0", "-33.2 115.0 0.0 0.0") +data <- data.frame(status, details, geometry, stringsAsFactors = FALSE) +request_data <- list( + "entities" = data.frame(label, data = I(data), stringsAsFactors = FALSE), + "source" = list("name" = "file.csv", "size" = 100) +) + +# Compare request_data to the schema expected by Central +# https://docs.getodk.org/central-api-entity-management/#creating-entities +# jsonlite::toJSON(request_data, pretty = TRUE, auto_unbox = TRUE) + +ec <- entity_create( + did = did, + notes = notes, + data = request_data +) +} +} +\seealso{ +\url{https://docs.getodk.org/central-api-entity-management/#creating-entities} + +Other entity-management: +\code{\link{entity_audits}()}, +\code{\link{entity_changes}()}, +\code{\link{entity_delete}()}, +\code{\link{entity_detail}()}, +\code{\link{entity_list}()}, +\code{\link{entity_update}()}, +\code{\link{entity_versions}()}, +\code{\link{entitylist_detail}()}, +\code{\link{entitylist_download}()}, +\code{\link{entitylist_list}()}, +\code{\link{entitylist_update}()} +} +\concept{entity-management} diff --git a/man/entity_delete.Rd b/man/entity_delete.Rd new file mode 100644 index 00000000..b67be27e --- /dev/null +++ b/man/entity_delete.Rd @@ -0,0 +1,132 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/entity_delete.R +\name{entity_delete} +\alias{entity_delete} +\title{Delete one Entity.} +\usage{ +entity_delete( + pid = get_default_pid(), + did = "", + eid = "", + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = get_default_orders(), + tz = get_default_tz() +) +} +\arguments{ +\item{pid}{The numeric ID of the project, e.g.: 2. + +Default: \code{\link{get_default_pid}}. + +Set default \code{pid} through \code{ru_setup(pid="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{did}{(chr) The name of the Entity List, internally called Dataset. +The function will error if this parameter is not given. +Default: "".} + +\item{eid}{(chr) The UUID of an Entity, which can be retrieved by +\code{entity_list()}. +The function will error if this parameter is not given. +Default: "".} + +\item{url}{The ODK Central base URL without trailing slash. + +Default: \code{\link{get_default_url}}. + +Set default \code{url} through \code{ru_setup(url="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{un}{The ODK Central username (an email address). +Default: \code{\link{get_default_un}}. +Set default \code{un} through \code{ru_setup(un="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{pw}{The ODK Central password. +Default: \code{\link{get_default_pw}}. +Set default \code{pw} through \code{ru_setup(pw="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{retries}{The number of attempts to retrieve a web resource. + +This parameter is given to \code{\link[httr]{RETRY}(times = retries)}. + +Default: 3.} + +\item{odkc_version}{The ODK Central version as a semantic version string +(year.minor.patch), e.g. "2023.5.1". The version is shown on ODK Central's +version page \verb{/version.txt}. Discard the "v". +\code{ruODK} uses this parameter to adjust for breaking changes in ODK Central. + +Default: \code{\link{get_default_odkc_version}} or "2023.5.1" if unset. + +Set default \code{get_default_odkc_version} through +\code{ru_setup(odkc_version="2023.5.1")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{orders}{(vector of character) Orders of datetime elements for +lubridate. + +Default: +\code{c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd")}.} + +\item{tz}{A timezone to convert dates and times to. + +Read \code{vignette("setup", package = "ruODK")} to learn how \code{ruODK}'s +timezone can be set globally or per function.} +} +\value{ +A list with the key "success" (lgl) indicating whether the entity +was deleted. +} +\description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#maturing}{\figure{lifecycle-maturing.svg}{options: alt='[Maturing]'}}}{\strong{[Maturing]}} +} +\details{ +This function soft-deletes one Entity, , which means it is still in Central's +database and you can retrieve it via \code{entity_list(deleted=TRUE)}. +} +\examples{ +\dontrun{ +# See vignette("setup") for setup and authentication options +# ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") + +el <- entitylist_list() + +# Entity List name (dataset ID, did) +did <- el$name[1] + +# All Entities of Entity List +en <- entity_list(did = did) + +# View details of one Entity +ed <- entity_detail(did = did, eid = en$uuid[1]) + +# Delete the Entity +ed_deleted <- entity_delete(did = did, eid = ed$id) +} +} +\seealso{ +\url{https://docs.getodk.org/central-api-entity-management/#deleting-an-entity} + +Other entity-management: +\code{\link{entity_audits}()}, +\code{\link{entity_changes}()}, +\code{\link{entity_create}()}, +\code{\link{entity_detail}()}, +\code{\link{entity_list}()}, +\code{\link{entity_update}()}, +\code{\link{entity_versions}()}, +\code{\link{entitylist_detail}()}, +\code{\link{entitylist_download}()}, +\code{\link{entitylist_list}()}, +\code{\link{entitylist_update}()} +} +\concept{entity-management} diff --git a/man/entity_detail.Rd b/man/entity_detail.Rd new file mode 100644 index 00000000..f7ef608f --- /dev/null +++ b/man/entity_detail.Rd @@ -0,0 +1,160 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/entity_detail.R +\name{entity_detail} +\alias{entity_detail} +\title{Show metadata and current data of one Entity.} +\usage{ +entity_detail( + pid = get_default_pid(), + did = "", + eid = "", + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = get_default_orders(), + tz = get_default_tz() +) +} +\arguments{ +\item{pid}{The numeric ID of the project, e.g.: 2. + +Default: \code{\link{get_default_pid}}. + +Set default \code{pid} through \code{ru_setup(pid="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{did}{(chr) The name of the Entity List, internally called Dataset. +The function will error if this parameter is not given. +Default: "".} + +\item{eid}{(chr) The UUID of an Entity, which can be retrieved by +\code{entity_list()}. +The function will error if this parameter is not given. +Default: "".} + +\item{url}{The ODK Central base URL without trailing slash. + +Default: \code{\link{get_default_url}}. + +Set default \code{url} through \code{ru_setup(url="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{un}{The ODK Central username (an email address). +Default: \code{\link{get_default_un}}. +Set default \code{un} through \code{ru_setup(un="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{pw}{The ODK Central password. +Default: \code{\link{get_default_pw}}. +Set default \code{pw} through \code{ru_setup(pw="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{retries}{The number of attempts to retrieve a web resource. + +This parameter is given to \code{\link[httr]{RETRY}(times = retries)}. + +Default: 3.} + +\item{odkc_version}{The ODK Central version as a semantic version string +(year.minor.patch), e.g. "2023.5.1". The version is shown on ODK Central's +version page \verb{/version.txt}. Discard the "v". +\code{ruODK} uses this parameter to adjust for breaking changes in ODK Central. + +Default: \code{\link{get_default_odkc_version}} or "2023.5.1" if unset. + +Set default \code{get_default_odkc_version} through +\code{ru_setup(odkc_version="2023.5.1")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{orders}{(vector of character) Orders of datetime elements for +lubridate. + +Default: +\code{c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd")}.} + +\item{tz}{A timezone to convert dates and times to. + +Read \code{vignette("setup", package = "ruODK")} to learn how \code{ruODK}'s +timezone can be set globally or per function.} +} +\value{ +A nested list identical to the return value of \code{entity_update}. +See \url{https://docs.getodk.org/central-api-entity-management/#getting-entity-details} +for the full schema. +Top level list elements are renamed from ODK's \code{camelCase} to \code{snake_case}. +Nested list elements have the original \code{camelCase}. +} +\description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#maturing}{\figure{lifecycle-maturing.svg}{options: alt='[Maturing]'}}}{\strong{[Maturing]}} +} +\details{ +This function returns the metadata and current data of an Entity. + +The data and label of an Entity are found in the \code{current_version} columns +under data and label respectively. +The \code{current_version} column contains version-specific metadata fields +such as version, baseVersion, details about conflicts that version +introduced, etc. More details are available from \code{entity_versions()}. +The Entity also contains top-level system metadata such as \code{uuid}, +\code{created_at} and \code{updated_at} timestamps, and \code{creator_id} (or full creator +if extended metadata is requested). +\subsection{Conflicts}{ + +An Entity's metadata also has a conflict field, which indicates the current +conflict status of the Entity. The value of a conflict can either be: +\itemize{ +\item null. The Entity does not have conflicting versions. +\item soft. The Entity has a version that was based on a version other than the +version immediately prior to it. (The specified \code{base_version} was not the +latest version on the server.) +\item hard. The Entity has a version that was based on a version other than the +version immediately prior to it. Further, that version updated the same +property as another update. +} + +If an Entity has a conflict, it can be marked as resolved. +After that, the conflict field will be null until a new conflicting version +is received. +} +} +\examples{ +\dontrun{ +# See vignette("setup") for setup and authentication options +# ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") + +el <- entitylist_list() + +# Entity List name (dataset ID, did) +did <- el$name[1] + +# All Entities of Entity List +en <- entity_list(did = did) + +ed <- entity_detail(did = did, eid = en$uuid[1]) + +# The current version of the first Entity +ev <- en$current_version_version[1] +} +} +\seealso{ +\url{https://docs.getodk.org/central-api-entity-management/#entities-metadata} + +Other entity-management: +\code{\link{entity_audits}()}, +\code{\link{entity_changes}()}, +\code{\link{entity_create}()}, +\code{\link{entity_delete}()}, +\code{\link{entity_list}()}, +\code{\link{entity_update}()}, +\code{\link{entity_versions}()}, +\code{\link{entitylist_detail}()}, +\code{\link{entitylist_download}()}, +\code{\link{entitylist_list}()}, +\code{\link{entitylist_update}()} +} +\concept{entity-management} diff --git a/man/entity_list.Rd b/man/entity_list.Rd new file mode 100644 index 00000000..f5a9bf25 --- /dev/null +++ b/man/entity_list.Rd @@ -0,0 +1,136 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/entity_list.R +\name{entity_list} +\alias{entity_list} +\title{List all Entities of a kind.} +\usage{ +entity_list( + pid = get_default_pid(), + did = "", + deleted = FALSE, + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = get_default_orders(), + tz = get_default_tz() +) +} +\arguments{ +\item{pid}{The numeric ID of the project, e.g.: 2. + +Default: \code{\link{get_default_pid}}. + +Set default \code{pid} through \code{ru_setup(pid="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{did}{(chr) The name of the Entity List, internally called Dataset. +The function will error if this parameter is not given. +Default: "".} + +\item{deleted}{(lgl) Whether to get only deleted entities (\code{TRUE}) or not +(\code{FALSE}). Default: \code{FALSE}.} + +\item{url}{The ODK Central base URL without trailing slash. + +Default: \code{\link{get_default_url}}. + +Set default \code{url} through \code{ru_setup(url="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{un}{The ODK Central username (an email address). +Default: \code{\link{get_default_un}}. +Set default \code{un} through \code{ru_setup(un="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{pw}{The ODK Central password. +Default: \code{\link{get_default_pw}}. +Set default \code{pw} through \code{ru_setup(pw="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{retries}{The number of attempts to retrieve a web resource. + +This parameter is given to \code{\link[httr]{RETRY}(times = retries)}. + +Default: 3.} + +\item{odkc_version}{The ODK Central version as a semantic version string +(year.minor.patch), e.g. "2023.5.1". The version is shown on ODK Central's +version page \verb{/version.txt}. Discard the "v". +\code{ruODK} uses this parameter to adjust for breaking changes in ODK Central. + +Default: \code{\link{get_default_odkc_version}} or "2023.5.1" if unset. + +Set default \code{get_default_odkc_version} through +\code{ru_setup(odkc_version="2023.5.1")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{orders}{(vector of character) Orders of datetime elements for +lubridate. + +Default: +\code{c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd")}.} + +\item{tz}{A timezone to convert dates and times to. + +Read \code{vignette("setup", package = "ruODK")} to learn how \code{ruODK}'s +timezone can be set globally or per function.} +} +\value{ +A tibble with exactly one row for each Entity of the given project +as per ODK Central API docs. +Column names are renamed from ODK's \code{camelCase} to \code{snake_case}. +} +\description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#maturing}{\figure{lifecycle-maturing.svg}{options: alt='[Maturing]'}}}{\strong{[Maturing]}} +} +\details{ +This function returns a list of the Entities of a kind (belonging to an +Entity List or Dataset). +Please note that this endpoint only returns metadata of the entities, not the +data. If you want to get the data of all entities then please refer to the +OData Dataset Service. + +You can get only deleted entities with \code{deleted=TRUE}. +} +\examples{ +\dontrun{ +# See vignette("setup") for setup and authentication options +# ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") + +el <- entitylist_list() + +# Entity List name (dataset ID) +did <- el$name[1] + +# All Entities of Entity List +en <- entity_list(did = el$name[1]) + +# The UUID of the first Entity +eid <- en$uuid[1] + +# The current version of the first Entity +ev <- en$current_version_version[1] +} +} +\seealso{ +\url{https://docs.getodk.org/central-api-entity-management/#entities-metadata} + +Other entity-management: +\code{\link{entity_audits}()}, +\code{\link{entity_changes}()}, +\code{\link{entity_create}()}, +\code{\link{entity_delete}()}, +\code{\link{entity_detail}()}, +\code{\link{entity_update}()}, +\code{\link{entity_versions}()}, +\code{\link{entitylist_detail}()}, +\code{\link{entitylist_download}()}, +\code{\link{entitylist_list}()}, +\code{\link{entitylist_update}()} +} +\concept{entity-management} diff --git a/man/entity_update.Rd b/man/entity_update.Rd new file mode 100644 index 00000000..ebd05b2f --- /dev/null +++ b/man/entity_update.Rd @@ -0,0 +1,195 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/entity_update.R +\name{entity_update} +\alias{entity_update} +\title{Update one Entity.} +\usage{ +entity_update( + pid = get_default_pid(), + did = "", + eid = "", + label = "", + data = list(), + base_version = NULL, + force = is.null(base_version), + resolve = FALSE, + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = get_default_orders(), + tz = get_default_tz() +) +} +\arguments{ +\item{pid}{The numeric ID of the project, e.g.: 2. + +Default: \code{\link{get_default_pid}}. + +Set default \code{pid} through \code{ru_setup(pid="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{did}{(chr) The name of the Entity List, internally called Dataset. +The function will error if this parameter is not given. +Default: "".} + +\item{eid}{(chr) The UUID of an Entity, which can be retrieved by +\code{entity_list()}. +The function will error if this parameter is not given. +Default: "".} + +\item{label}{(character) The Entity label which must be a non-empty string. +Default: \code{""}.} + +\item{data}{(list) A named list of Entity properties to update. See details. +Default: \code{list()}.} + +\item{base_version}{If given, must be the current version of the Entity on +the server. Optional.} + +\item{force}{(lgl) Whether to force an update. Defaults to be \code{FALSE} if a +\code{base_version} is specified, else defaults to \code{TRUE}. +Using \code{force=TRUE} and specifying a \code{base_version} will emit a warning.} + +\item{resolve}{(lgl)} + +\item{url}{The ODK Central base URL without trailing slash. + +Default: \code{\link{get_default_url}}. + +Set default \code{url} through \code{ru_setup(url="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{un}{The ODK Central username (an email address). +Default: \code{\link{get_default_un}}. +Set default \code{un} through \code{ru_setup(un="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{pw}{The ODK Central password. +Default: \code{\link{get_default_pw}}. +Set default \code{pw} through \code{ru_setup(pw="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{retries}{The number of attempts to retrieve a web resource. + +This parameter is given to \code{\link[httr]{RETRY}(times = retries)}. + +Default: 3.} + +\item{odkc_version}{The ODK Central version as a semantic version string +(year.minor.patch), e.g. "2023.5.1". The version is shown on ODK Central's +version page \verb{/version.txt}. Discard the "v". +\code{ruODK} uses this parameter to adjust for breaking changes in ODK Central. + +Default: \code{\link{get_default_odkc_version}} or "2023.5.1" if unset. + +Set default \code{get_default_odkc_version} through +\code{ru_setup(odkc_version="2023.5.1")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{orders}{(vector of character) Orders of datetime elements for +lubridate. + +Default: +\code{c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd")}.} + +\item{tz}{A timezone to convert dates and times to. + +Read \code{vignette("setup", package = "ruODK")} to learn how \code{ruODK}'s +timezone can be set globally or per function.} +} +\value{ +A nested list identical to the return value of \code{entity_detail}. +See \url{https://docs.getodk.org/central-api-entity-management/#updating-an-entity} +for the full schema. +Top level list elements are renamed from ODK's \code{camelCase} to \code{snake_case}. +Nested list elements have the original \code{camelCase}. +} +\description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} +} +\details{ +This endpoint is used to update the label or the properties (passed as JSON +in the request body) of an Entity. +You only need to include the properties you wish to update. +To unset the value of any property, you can set it to empty string (""). +The label must be a non-empty string. +Setting a property to null will throw an error. +Attempting to update a property that doesn't exist in the Dataset will throw +an error. +\subsection{Specifying a base version}{ + +You must either provide a \code{base_version} or use \code{force=TRUE} query parameter. +You cannot cause a new Entity conflict via the API, which is why when +specifying \code{base_version}, it must match the current version of the Entity on +the server. This acts as a check to ensure you are not trying to update based +on stale data. If you wish to update the Entity regardless of the current +state, then you can use the force flag. +} + +\subsection{Resolving a conflict}{ + +You can also use this endpoint to resolve an Entity conflict by passing +\code{resolve=true}, in which case providing \code{data} is optional. +When not providing new data, only the conflict status from +the Entity will be cleared and no new version will be created. +When providing data, the conflict will be cleared and an updated version of +the Entity will be added. +} +} +\examples{ +\dontrun{ +# See vignette("setup") for setup and authentication options +# ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") + +el <- entitylist_list() + +# Entity List name (dataset ID, did) +did <- el$name[1] + +# All Entities of Entity List +en <- entity_list(did = did) + +ed <- entity_detail(did = did, eid = en$uuid[1]) + +e_label <- ed$current_version$label + +# This example updates one field which exists in the example form. +# Your own Entity will have different fields to update. +e_data <- list( + details = paste0( + ed$current_version$data$details, ". Updated on ", Sys.time() + ) +) + +# Update the Entity (implicitly forced update) +eu <- entity_update( + did = did, + eid = en$uuid[1], + label = e_label, + data = e_data +) +eu +} +} +\seealso{ +\url{https://docs.getodk.org/central-api-entity-management/#updating-an-entity} + +Other entity-management: +\code{\link{entity_audits}()}, +\code{\link{entity_changes}()}, +\code{\link{entity_create}()}, +\code{\link{entity_delete}()}, +\code{\link{entity_detail}()}, +\code{\link{entity_list}()}, +\code{\link{entity_versions}()}, +\code{\link{entitylist_detail}()}, +\code{\link{entitylist_download}()}, +\code{\link{entitylist_list}()}, +\code{\link{entitylist_update}()} +} +\concept{entity-management} diff --git a/man/entity_versions.Rd b/man/entity_versions.Rd new file mode 100644 index 00000000..3a211853 --- /dev/null +++ b/man/entity_versions.Rd @@ -0,0 +1,155 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/entity_versions.R +\name{entity_versions} +\alias{entity_versions} +\title{List versions of one Entity.} +\usage{ +entity_versions( + pid = get_default_pid(), + did = "", + eid = "", + conflict = FALSE, + url = get_default_url(), + un = get_default_un(), + pw = get_default_pw(), + retries = get_retries(), + odkc_version = get_default_odkc_version(), + orders = get_default_orders(), + tz = get_default_tz() +) +} +\arguments{ +\item{pid}{The numeric ID of the project, e.g.: 2. + +Default: \code{\link{get_default_pid}}. + +Set default \code{pid} through \code{ru_setup(pid="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{did}{(chr) The name of the Entity List, internally called Dataset. +The function will error if this parameter is not given. +Default: "".} + +\item{eid}{(chr) The UUID of an Entity, which can be retrieved by +\code{entity_list()}. +The function will error if this parameter is not given. +Default: "".} + +\item{conflict}{(lgl) Whether to return all versions (\code{FALSE}) or +whether to returns the subset of past versions of an Entity that are +relevant to the Entity's current conflict (\code{TRUE}). +This includes the latest version, the base version, the previous +server version, and any other versions since the last time the Entity was +in a conflict-free state. If the Entity is not in conflict, zero versions +are returned. +Default: \code{FALSE} (return all versions)} + +\item{url}{The ODK Central base URL without trailing slash. + +Default: \code{\link{get_default_url}}. + +Set default \code{url} through \code{ru_setup(url="...")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{un}{The ODK Central username (an email address). +Default: \code{\link{get_default_un}}. +Set default \code{un} through \code{ru_setup(un="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{pw}{The ODK Central password. +Default: \code{\link{get_default_pw}}. +Set default \code{pw} through \code{ru_setup(pw="...")}. +See \code{vignette("Setup", package = "ruODK")}.} + +\item{retries}{The number of attempts to retrieve a web resource. + +This parameter is given to \code{\link[httr]{RETRY}(times = retries)}. + +Default: 3.} + +\item{odkc_version}{The ODK Central version as a semantic version string +(year.minor.patch), e.g. "2023.5.1". The version is shown on ODK Central's +version page \verb{/version.txt}. Discard the "v". +\code{ruODK} uses this parameter to adjust for breaking changes in ODK Central. + +Default: \code{\link{get_default_odkc_version}} or "2023.5.1" if unset. + +Set default \code{get_default_odkc_version} through +\code{ru_setup(odkc_version="2023.5.1")}. + +See \code{vignette("Setup", package = "ruODK")}.} + +\item{orders}{(vector of character) Orders of datetime elements for +lubridate. + +Default: +\code{c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd")}.} + +\item{tz}{A timezone to convert dates and times to. + +Read \code{vignette("setup", package = "ruODK")} to learn how \code{ruODK}'s +timezone can be set globally or per function.} +} +\value{ +A tibble with one row per version. List columns contain unstructured +data. +See \url{https://docs.getodk.org/central-api-entity-management/#listing-versions} +for the full schema. +Top level list elements are renamed from ODK's \code{camelCase} to \code{snake_case}. +Nested list elements have the original \code{camelCase}. +} +\description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#maturing}{\figure{lifecycle-maturing.svg}{options: alt='[Maturing]'}}}{\strong{[Maturing]}} +} +\details{ +This returns the Entity metadata and data for every version of this Entity +in ascending creation order. + +The ODK Central endpoint supports retrieving extended metadata which this +function always returns. + +There is an optional query flag \code{relevantToConflict} that returns the subset +of past versions of an Entity that are relevant to the Entity's current +conflict. This includes the latest version, the base version, the previous +server version, and any other versions since the last time the Entity was +in a conflict-free state. If the Entity is not in conflict, zero versions +are returned. +} +\examples{ +\dontrun{ +# See vignette("setup") for setup and authentication options +# ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") + +el <- entitylist_list() + +# Entity List name (dataset ID, did) +did <- el$name[1] + +# All Entities of Entity List +en <- entity_list(did = did) + +ed <- entity_detail(did = did, eid = en$uuid[1]) + +# The current version of the first Entity +ev <- en$current_version_version[1] +} +} +\seealso{ +\url{https://docs.getodk.org/central-api-entity-management/#listing-versions} + +Other entity-management: +\code{\link{entity_audits}()}, +\code{\link{entity_changes}()}, +\code{\link{entity_create}()}, +\code{\link{entity_delete}()}, +\code{\link{entity_detail}()}, +\code{\link{entity_list}()}, +\code{\link{entity_update}()}, +\code{\link{entitylist_detail}()}, +\code{\link{entitylist_download}()}, +\code{\link{entitylist_list}()}, +\code{\link{entitylist_update}()} +} +\concept{entity-management} diff --git a/man/entitylist_detail.Rd b/man/entitylist_detail.Rd index b863bb6c..fcd553a4 100644 --- a/man/entitylist_detail.Rd +++ b/man/entitylist_detail.Rd @@ -6,13 +6,13 @@ \usage{ entitylist_detail( pid = get_default_pid(), - did = NULL, + did = "", url = get_default_url(), un = get_default_un(), pw = get_default_pw(), retries = get_retries(), odkc_version = get_default_odkc_version(), - orders = c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd"), + orders = get_default_orders(), tz = get_default_tz() ) } @@ -25,7 +25,9 @@ Set default \code{pid} through \code{ru_setup(pid="...")}. See \code{vignette("Setup", package = "ruODK")}.} -\item{did}{(chr) The name of the Entity List, internally called Dataset.} +\item{did}{(chr) The name of the Entity List, internally called Dataset. +The function will error if this parameter is not given. +Default: "".} \item{url}{The ODK Central base URL without trailing slash. @@ -82,30 +84,25 @@ it is not trivial to rectangle the result into a tibble. \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#maturing}{\figure{lifecycle-maturing.svg}{options: alt='[Maturing]'}}}{\strong{[Maturing]}} } -\details{ -An Entity List is a named collection of Entities that have the same -properties. -Entity List can be linked to Forms as Attachments. -This will make it available to clients as an automatically-updating CSV. - -This function is supported from ODK Central v2022.3 and will warn if the -given odkc_version is lower. -} \examples{ \dontrun{ # See vignette("setup") for setup and authentication options # ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") ds <- entitylist_list(pid = get_default_pid()) + ds1 <- entitylist_detail(pid = get_default_pid(), did = ds$name[1]) ds1 |> listviewer::jsonedit() -ds1$linkedForms |> + +ds1$linked_forms |> purrr::list_transpose() |> tibble::as_tibble() -ds1$sourceForms |> + +ds1$source_forms |> purrr::list_transpose() |> tibble::as_tibble() + ds1$properties |> purrr::list_transpose() |> tibble::as_tibble() @@ -115,7 +112,16 @@ ds1$properties |> \url{ https://docs.getodk.org/central-api-dataset-management/#datasets} Other entity-management: +\code{\link{entity_audits}()}, +\code{\link{entity_changes}()}, +\code{\link{entity_create}()}, +\code{\link{entity_delete}()}, +\code{\link{entity_detail}()}, +\code{\link{entity_list}()}, +\code{\link{entity_update}()}, +\code{\link{entity_versions}()}, \code{\link{entitylist_download}()}, -\code{\link{entitylist_list}()} +\code{\link{entitylist_list}()}, +\code{\link{entitylist_update}()} } \concept{entity-management} diff --git a/man/entitylist_download.Rd b/man/entitylist_download.Rd index 75b43fa9..493e58ab 100644 --- a/man/entitylist_download.Rd +++ b/man/entitylist_download.Rd @@ -6,7 +6,7 @@ \usage{ entitylist_download( pid = get_default_pid(), - did = NULL, + did = "", url = get_default_url(), un = get_default_un(), pw = get_default_pw(), @@ -16,7 +16,7 @@ entitylist_download( overwrite = TRUE, retries = get_retries(), odkc_version = get_default_odkc_version(), - orders = c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd"), + orders = get_default_orders(), tz = get_default_tz(), verbose = get_ru_verbose() ) @@ -30,7 +30,9 @@ Set default \code{pid} through \code{ru_setup(pid="...")}. See \code{vignette("Setup", package = "ruODK")}.} -\item{did}{(chr) The name of the Entity List, internally called Dataset.} +\item{did}{(chr) The name of the Entity List, internally called Dataset. +The function will error if this parameter is not given. +Default: "".} \item{url}{The ODK Central base URL without trailing slash. @@ -55,16 +57,16 @@ default: \code{here::here}. If the folder does not exist it will be created.} \item{filter}{(str) A valid filter string. -Default: NULL (no filtering, all entities returned).} +Default: NULL (no filtering, all Entities returned).} \item{etag}{(str) The etag value from a previous call to \code{entitylist_download()}. The value must be stripped of the \verb{W/\\"} and \verb{\\"}, which is the format of the etag returned by \code{entitylist_download()}. If provided, only new entities will be returned. If the same \code{local_dir} is chosen and \code{overwrite} is set to \code{TRUE}, -the downloaded CSV will also be overwritte, losing the Entities downloaded -earlier. -Default: NULL (no filtering, all entities returned).} +the downloaded CSV will also be overwritten, losing the previously +downloaded Entities. +Default: NULL (no filtering, all Entities returned).} \item{overwrite}{Whether to overwrite previously downloaded file, default: FALSE} @@ -111,38 +113,42 @@ A list of four items: 200 if OK, 304 if a given etag finds no new entities created. \item etag (str) The ETag to use in subsequent calls to \code{entitylist_download()} \item downloaded_to (fs_path) The path to the downloaded CSV file -\item downloaded_on (POSIXct) The time of download in the local timezome +\item downloaded_on (POSIXct) The time of download in the local timezone } } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#maturing}{\figure{lifecycle-maturing.svg}{options: alt='[Maturing]'}}}{\strong{[Maturing]}} } \details{ +\subsection{CSV file}{ + The downloaded CSV file is named after the entity list name. The download location defaults to the current workdir, but can be modified to a different folder path which will be created if it doesn't exist. -An Entity List is a named collection of Entities that have the same -properties. -Entity List can be linked to Forms as Attachments. -This will make it available to clients as an automatically-updating CSV. - Entity Lists can be used as Attachments in other Forms, but they can also be downloaded directly as a CSV file. + The CSV format closely matches the OData Dataset (Entity List) Service format, with columns for system properties such as \verb{__id} (the Entity UUID), -\verb{__createdAt}, \verb{__creatorName}, etc., the Entity Label label, and the -Dataset (Entity List )/Entity Properties themselves. +\verb{__createdAt}, \verb{__creatorName}, etc., the Entity Label, and the +Dataset (Entity List) or Entity Properties themselves. If any Property for an given Entity is blank (e.g. it was not captured by that Form or was left blank), that field of the CSV is blank. +} + +\subsection{Filter}{ -The ODK Central \verb{$filter} querystring parameter can be used to filter on +The ODK Central \verb{$filter} query string parameter can be used to filter on system-level properties, similar to how filtering in the OData Dataset (Entity List) Service works. -Of the \href{https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#_Toc31358948}{OData filter specs} +Of the \href{https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#_Toc31358948}{OData filter specs } ODK Central implements a \href{https://docs.getodk.org/central-api-odata-endpoints/#data-document}{growing set of features }. \code{ruODK} provides the parameter \code{filter} (str) which, if set, will be passed on to the ODK Central endpoint as is. +} + +\subsection{Resuming downloads through ETag}{ The ODK Central endpoint supports the \href{https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag}{\code{ETag} header }, which can be used to avoid downloading the same content more than once. @@ -157,12 +163,14 @@ data. a previous call to \code{entitylist_download()}. \code{ruODK} strips the \verb{W/\\"} and \verb{\\"} from the returned etag and expects the stripped etag as parameter. } +} \examples{ \dontrun{ # See vignette("setup") for setup and authentication options # ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") ds <- entitylist_list(pid = get_default_pid()) + ds1 <- entitylist_download(pid = get_default_pid(), did = ds$name[1]) # ds1$entities # ds1$etag @@ -188,7 +196,16 @@ ds3 <- entitylist_download( \url{https://docs.getodk.org/central-api-dataset-management/#datasets} Other entity-management: +\code{\link{entity_audits}()}, +\code{\link{entity_changes}()}, +\code{\link{entity_create}()}, +\code{\link{entity_delete}()}, +\code{\link{entity_detail}()}, +\code{\link{entity_list}()}, +\code{\link{entity_update}()}, +\code{\link{entity_versions}()}, \code{\link{entitylist_detail}()}, -\code{\link{entitylist_list}()} +\code{\link{entitylist_list}()}, +\code{\link{entitylist_update}()} } \concept{entity-management} diff --git a/man/entitylist_list.Rd b/man/entitylist_list.Rd index 560d5a10..c487433d 100644 --- a/man/entitylist_list.Rd +++ b/man/entitylist_list.Rd @@ -11,7 +11,7 @@ entitylist_list( pw = get_default_pw(), retries = get_retries(), odkc_version = get_default_odkc_version(), - orders = c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd"), + orders = get_default_orders(), tz = get_default_tz() ) } @@ -72,33 +72,21 @@ Read \code{vignette("setup", package = "ruODK")} to learn how \code{ruODK}'s timezone can be set globally or per function.} } \value{ -A tibble with exactly one row for each dataset of the given project -as per ODK Central API docs. -Column names are renamed from ODK's \code{camelCase} to \code{snake_case}. +A tibble with exactly one row for each Entity List of the given +Project as per ODK Central API docs. +Column names are renamed from ODK Central's \code{camelCase} to \code{snake_case}. } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#maturing}{\figure{lifecycle-maturing.svg}{options: alt='[Maturing]'}}}{\strong{[Maturing]}} } \details{ -While the API endpoint will return all Entity Lists for one Project, -\code{\link{entitylist_list}} will fail with incorrect or missing -authentication. - -An Entity List is a named collection of Entities that have the same properties. -An Entity List can be linked to Forms as Attachments. -This will make it available to clients as an automatically-updating CSV. - -ODK Central calls Entity Lists internally Datasets. \code{ruODK} chooses the term -Entity Lists as it is used in the ODK Central user interface and conveys -its relation to Entities better than the term Dataset. - -This function is supported from ODK Central v2022.3 and will warn if the -given odkc_version is lower. +The returned list is useful to retrieve the valid name of an Entity List for +further use by functions of the Entity Management family. } \examples{ \dontrun{ # See vignette("setup") for setup and authentication options -# ruODK::ru_setup(svc = "....svc", un = "me@email.com", pw = "...") +# ruODK::ru_setup(svc = "... .svc", un = "me@email.com", pw = "...") ds <- entitylist_list(pid = get_default_pid()) @@ -109,7 +97,16 @@ ds |> knitr::kable() \url{ https://docs.getodk.org/central-api-dataset-management/#datasets} Other entity-management: +\code{\link{entity_audits}()}, +\code{\link{entity_changes}()}, +\code{\link{entity_create}()}, +\code{\link{entity_delete}()}, +\code{\link{entity_detail}()}, +\code{\link{entity_list}()}, +\code{\link{entity_update}()}, +\code{\link{entity_versions}()}, \code{\link{entitylist_detail}()}, -\code{\link{entitylist_download}()} +\code{\link{entitylist_download}()}, +\code{\link{entitylist_update}()} } \concept{entity-management} diff --git a/man/entitylist_update.Rd b/man/entitylist_update.Rd index 9ad5662a..9b109b20 100644 --- a/man/entitylist_update.Rd +++ b/man/entitylist_update.Rd @@ -6,14 +6,14 @@ \usage{ entitylist_update( pid = get_default_pid(), - did = NULL, + did = "", approval_required = FALSE, url = get_default_url(), un = get_default_un(), pw = get_default_pw(), retries = get_retries(), odkc_version = get_default_odkc_version(), - orders = c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd"), + orders = get_default_orders(), tz = get_default_tz() ) } @@ -26,13 +26,15 @@ Set default \code{pid} through \code{ru_setup(pid="...")}. See \code{vignette("Setup", package = "ruODK")}.} -\item{did}{(chr) The name of the Entity List, internally called Dataset.} +\item{did}{(chr) The name of the Entity List, internally called Dataset. +The function will error if this parameter is not given. +Default: "".} -\item{approval_required}{(lgl) The value to set approvalRequired to. -If TRUE, a submission must be approved before an entity is created, -if FALSE, an entity is created as soon as the submission is received by +\item{approval_required}{(lgl) The value to set \code{approvalRequired} to. +If TRUE, a Submission must be approved before an Entity is created, +if FALSE, an Entity is created as soon as the Submission is received by ODK Central. -Default: FALSE.} +Default: \code{FALSE}.} \item{url}{The ODK Central base URL without trailing slash. @@ -92,23 +94,13 @@ it is not trivial to rectangle the result into a tibble. } \details{ You can only update \code{approvalRequired} using this endpoint. -The approvalRequired flag controls the Entity creation flow; +The \code{approvalRequired} flag controls the Entity creation flow; if it is true then the Submission must be approved before an Entity can be created from it and if it is false then an Entity is created as soon as the Submission is received by the ODK Central. By default \code{approvalRequired} is false for the Entity Lists created after -v2023.3. Entity Lists created prior to that will have approvalRequired set to -true. - -An Entity List is a named collection of Entities that have the same -properties. -An Entity List can be linked to Forms as Attachments. -This will make it available to clients as an automatically-updating CSV. - -This function is supported from ODK Central v2022.3 and will warn if the -given odkc_version is lower. - -\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#maturing}{\figure{lifecycle-maturing.svg}{options: alt='[Maturing]'}}}{\strong{[Maturing]}} +v2023.3. Entity Lists created prior to that will have \code{approvalRequired} set +to true. } \examples{ \dontrun{ @@ -133,5 +125,18 @@ ds3$approvalRequired # FALSE } \seealso{ \url{ https://docs.getodk.org/central-api-dataset-management/#datasets} + +Other entity-management: +\code{\link{entity_audits}()}, +\code{\link{entity_changes}()}, +\code{\link{entity_create}()}, +\code{\link{entity_delete}()}, +\code{\link{entity_detail}()}, +\code{\link{entity_list}()}, +\code{\link{entity_update}()}, +\code{\link{entity_versions}()}, +\code{\link{entitylist_detail}()}, +\code{\link{entitylist_download}()}, +\code{\link{entitylist_list}()} } -\concept{dataset-management} +\concept{entity-management} diff --git a/man/figures/flatbacks.svg b/man/figures/flatbacks.svg index 16bc84f7..258a9db2 100644 --- a/man/figures/flatbacks.svg +++ b/man/figures/flatbacks.svg @@ -229,4 +229,4 @@ - \ No newline at end of file + diff --git a/man/figures/lifecycle-archived.svg b/man/figures/lifecycle-archived.svg index 48f72a6f..a1823251 100644 --- a/man/figures/lifecycle-archived.svg +++ b/man/figures/lifecycle-archived.svg @@ -1 +1 @@ - lifecyclelifecyclearchivedarchived \ No newline at end of file + lifecyclelifecyclearchivedarchived diff --git a/man/figures/lifecycle-defunct.svg b/man/figures/lifecycle-defunct.svg index 01452e5f..e8b72a22 100644 --- a/man/figures/lifecycle-defunct.svg +++ b/man/figures/lifecycle-defunct.svg @@ -1 +1 @@ -lifecyclelifecycledefunctdefunct \ No newline at end of file +lifecyclelifecycledefunctdefunct diff --git a/man/figures/lifecycle-deprecated.svg b/man/figures/lifecycle-deprecated.svg index 4baaee01..02aee08b 100644 --- a/man/figures/lifecycle-deprecated.svg +++ b/man/figures/lifecycle-deprecated.svg @@ -1 +1 @@ -lifecyclelifecycledeprecateddeprecated \ No newline at end of file +lifecyclelifecycledeprecateddeprecated diff --git a/man/figures/lifecycle-experimental.svg b/man/figures/lifecycle-experimental.svg index d1d060e9..5df89276 100644 --- a/man/figures/lifecycle-experimental.svg +++ b/man/figures/lifecycle-experimental.svg @@ -1 +1 @@ -lifecyclelifecycleexperimentalexperimental \ No newline at end of file +lifecyclelifecycleexperimentalexperimental diff --git a/man/figures/lifecycle-maturing.svg b/man/figures/lifecycle-maturing.svg index df713101..8e541e74 100644 --- a/man/figures/lifecycle-maturing.svg +++ b/man/figures/lifecycle-maturing.svg @@ -1 +1 @@ -lifecyclelifecyclematuringmaturing \ No newline at end of file +lifecyclelifecyclematuringmaturing diff --git a/man/figures/lifecycle-questioning.svg b/man/figures/lifecycle-questioning.svg index 08ee0c90..873d0d55 100644 --- a/man/figures/lifecycle-questioning.svg +++ b/man/figures/lifecycle-questioning.svg @@ -1 +1 @@ -lifecyclelifecyclequestioningquestioning \ No newline at end of file +lifecyclelifecyclequestioningquestioning diff --git a/man/figures/lifecycle-retired.svg b/man/figures/lifecycle-retired.svg index 33f406b1..58d3f70d 100644 --- a/man/figures/lifecycle-retired.svg +++ b/man/figures/lifecycle-retired.svg @@ -1 +1 @@ - lifecyclelifecycleretiredretired \ No newline at end of file + lifecyclelifecycleretiredretired diff --git a/man/figures/lifecycle-soft-deprecated.svg b/man/figures/lifecycle-soft-deprecated.svg index 9f014fd1..05be6afc 100644 --- a/man/figures/lifecycle-soft-deprecated.svg +++ b/man/figures/lifecycle-soft-deprecated.svg @@ -1 +1 @@ -lifecyclelifecyclesoft-deprecatedsoft-deprecated \ No newline at end of file +lifecyclelifecyclesoft-deprecatedsoft-deprecated diff --git a/man/figures/lifecycle-stable.svg b/man/figures/lifecycle-stable.svg index e015dc81..89a2b8e8 100644 --- a/man/figures/lifecycle-stable.svg +++ b/man/figures/lifecycle-stable.svg @@ -1 +1 @@ -lifecyclelifecyclestablestable \ No newline at end of file +lifecyclelifecyclestablestable diff --git a/man/figures/lifecycle-superseded.svg b/man/figures/lifecycle-superseded.svg index 75f24f55..a9118b6f 100644 --- a/man/figures/lifecycle-superseded.svg +++ b/man/figures/lifecycle-superseded.svg @@ -1 +1 @@ - lifecyclelifecyclesupersededsuperseded \ No newline at end of file + lifecyclelifecyclesupersededsuperseded diff --git a/man/figures/odata.svg b/man/figures/odata.svg index a4cb7133..6489c636 100644 --- a/man/figures/odata.svg +++ b/man/figures/odata.svg @@ -1 +1 @@ -#Useyourform'sODataServiceURL(Form>Submissions>Analysedata)R>#Readvignette("setup")onsettingusernameandpasswordvia.RenvironR>suppressMessages(library(tidyverse))R>R>library(ruODK)R>ruODK::ru_setup(++svc+svc=+svc=Sys.getenv("ODKC_TEST_SVC"),+un+un=+un=ruODK::get_test_un(),+pw+pw=+pw=ruODK::get_test_pw()+)<ruODKsettings>DefaultODKCentralProjectID:2DefaultODKCentralFormID:Flora-Quadrat-04DefaultODKCentralURL:https://odkc.dbca.wa.gov.auDefaultODKCentralUsername:Florian.Mayer@dbca.wa.gov.auDefaultODKCentralPassword:runruODK::get_default_pw()toshowDefaultODKCentralPassphrase:runruODK::get_default_pp()toshowDefaultTimeZone:Australia/PerthDefaultODKCentralVersion:1.4DefaultHTTPGETretries:3Verbosemessages:TRUETestODKCentralProjectID:2TestODKCentralFormID:Flora-Quadrat-04TestODKCentralFormID(ZIPtests):Spotlighting-06TestODKCentralFormID(Attachmenttests):Flora-Quadrat-04-attTestODKCentralFormID(Parsingtests):Flora-Quadrat-04-gapTestODKCentralFormID(WKTtests):LocationsTestODKCentralURL:https://odkc.dbca.wa.gov.auTestODKCentralUsername:Florian.Mayer@dbca.wa.gov.auTestODKCentralPassword:runruODK::get_test_pw()toshowTestODKCentralPassphrase:runruODK::get_test_pp()toshowTestODKCentralVersion:1.4R>#ListavailablesubmissiondatatablesR>fR>fqR>fq_R>fq_sR>fq_svR>fq_svcR>fq_svc<-R>fq_svc<-ruODK::odata_service_get()#Atibble:3×3namekindurl<chr><chr><chr>1SubmissionsEntitySetSubmissions2Submissions.vegetation_stratumEntitySetSubmissions.vegetation_stratum3Submissions.taxon_encounterEntitySetSubmissions.taxon_encounterR>#DownloadmainsubmissionsandattachmentsR>fq_dR>fq_daR>fq_datR>fq_dataR>fq_data<-R>fq_data<-ruODK::odata_submission_get(+t+ta+tab+tabl+table+table=+table=f+table=fq+table=fq_+table=fq_s+table=fq_sv+table=fq_svc+table=fq_svc$+table=fq_svc$n+table=fq_svc$na+table=fq_svc$nam+table=fq_svc$name+table=fq_svc$name[+table=fq_svc$name[1],+table=fq_svc$name[1],wkt+table=fq_svc$name[1],wkt=+table=fq_svc$name[1],wkt=TRUE,+table=fq_svc$name[1],wkt=TRUE,verbose+table=fq_svc$name[1],wkt=TRUE,verbose=+table=fq_svc$name[1],wkt=TRUE,verbose=TRUEDownloadingsubmissions...Downloadedsubmissions.Readingformschema...Formschemav1.4Parsingsubmissions...Notunnestinggeofields:value_location_corner1,value_perimeter_corner2,value_perimeter_corner3,value_perimeter_corner4,value_taxon_encounter_taxon_encounter_locationUnnesting:valueUnnestingcolumn"value"Unnestingmorelistcols:value_meta,value_location,value_habitat,value_perimeter,value___systemUnnesting:value_meta,value_location,value_habitat,value_perimeter,value___systemUnnestingcolumn"value_meta"Unnestingcolumn"value_location"Unnestingcolumn"value_habitat"Unnestingcolumn"value_perimeter"Unnestingcolumn"value___system"Founddate/times:encounter_start_datetime,encounter_end_datetime.FoundattachmentsinmainSubmissionstable:location_quadrat_photo,habitat_morphological_type_photo,perimeter_mudmap_photo.Downloadingattachments...Usinglocaldirectory"media".Filealreadydownloaded,keeping"media/1604290006239.jpg".Filealreadydownloaded,keeping"media/1604290049411.jpg".Filealreadydownloaded,keeping"media/1604290379613.jpg".Foundgeopoints:location_corner1,perimeter_corner2,perimeter_corner3,perimeter_corner4.Parsinglocation_corner1...Parsingperimeter_corner2...Parsingperimeter_corner3...Parsingperimeter_corner4...Foundgeotraces:.Foundgeoshapes:.Returningparsedsubmissions.R>#Downloadfirstnestedsubtable,jointomainsubmissionsR>fq_data_R>fq_data_strataR>fq_data_strata<-R>fq_data_strata<-ruODK::odata_submission_get(+table=fq_svc$name[2],+table=fq_svc$name[2],wkt+table=fq_svc$name[2],wkt=+table=fq_svc$name[2],wkt=TRUE,+table=fq_svc$name[2],wkt=TRUE,verbose+table=fq_svc$name[2],wkt=TRUE,verbose=+table=fq_svc$name[2],wkt=TRUE,verbose=TRUE+)%+)%>+)%>%+d+dp+dpl+dply+dplyr+dplyr:+dplyr::+dplyr::l+dplyr::le+dplyr::lef+dplyr::left+dplyr::left_+dplyr::left_j+dplyr::left_jo+dplyr::left_joi+dplyr::left_join+dplyr::left_join(+dplyr::left_join(f+dplyr::left_join(fq+dplyr::left_join(fq_+dplyr::left_join(fq_d+dplyr::left_join(fq_da+dplyr::left_join(fq_dat+dplyr::left_join(fq_data+dplyr::left_join(fq_data,+dplyr::left_join(fq_data,b+dplyr::left_join(fq_data,by+dplyr::left_join(fq_data,by=+dplyr::left_join(fq_data,by=c+dplyr::left_join(fq_data,by=c(+dplyr::left_join(fq_data,by=c("+dplyr::left_join(fq_data,by=c("s+dplyr::left_join(fq_data,by=c("su+dplyr::left_join(fq_data,by=c("sub+dplyr::left_join(fq_data,by=c("subm+dplyr::left_join(fq_data,by=c("submi+dplyr::left_join(fq_data,by=c("submis+dplyr::left_join(fq_data,by=c("submiss+dplyr::left_join(fq_data,by=c("submissi+dplyr::left_join(fq_data,by=c("submissio+dplyr::left_join(fq_data,by=c("submission+dplyr::left_join(fq_data,by=c("submissions+dplyr::left_join(fq_data,by=c("submissions_+dplyr::left_join(fq_data,by=c("submissions_i+dplyr::left_join(fq_data,by=c("submissions_id+dplyr::left_join(fq_data,by=c("submissions_id"+dplyr::left_join(fq_data,by=c("submissions_id"=+dplyr::left_join(fq_data,by=c("submissions_id"="+dplyr::left_join(fq_data,by=c("submissions_id"="i+dplyr::left_join(fq_data,by=c("submissions_id"="id+dplyr::left_join(fq_data,by=c("submissions_id"="id"+dplyr::left_join(fq_data,by=c("submissions_id"="id")+dplyr::left_join(fq_data,by=c("submissions_id"="id"))Founddate/times:.Foundgeopoints:.R>#Downloadsecondnestedsubtable,jointomainsubmissionsR>fq_data_taxaR>fq_data_taxa<-R>fq_data_taxa<-ruODK::odata_submission_get(+table=fq_svc$name[3],+table=fq_svc$name[3],wkt+table=fq_svc$name[3],wkt=+table=fq_svc$name[3],wkt=TRUE,+table=fq_svc$name[3],wkt=TRUE,verbose+table=fq_svc$name[3],wkt=TRUE,verbose=+table=fq_svc$name[3],wkt=TRUE,verbose=TRUEFoundattachmentsinnestedsub-table:photo_in_situ.Filealreadydownloaded,keeping"media/1604290400891.jpg".Filealreadydownloaded,keeping"media/1604290499008.jpg".R>#Viewdata[18]"perimeter_corner3"[19]"perimeter_corner3_longitude"[20]"perimeter_corner3_latitude"[21]"perimeter_corner3_altitude"[22]"perimeter_corner4"[23]"perimeter_corner4_longitude"[24]"perimeter_corner4_latitude"[25]"perimeter_corner4_altitude"[26]"perimeter_mudmap_photo"[27]"encounter_end_datetime"[28]"system_submission_date"[29]"system_updated_at"[30]"system_submitter_id"[31]"system_submitter_name"[32]"system_attachments_present"[33]"system_attachments_expected"[34]"system_status"[35]"system_review_state"[36]"system_device_id"[37]"system_edits"[38]"system_form_version"[39]"vegetation_stratum_odata_navigation_link"[40]"taxon_encounter_odata_navigation_link"[41]"odata_context"R>hR>heR>heaR>headR>head(R>head(fR>head(fqR>head(fq_R>head(fq_dR>head(fq_daR>head(fq_datR>head(fq_dataR>head(fq_data)#Atibble:1×41idmeta_instance_idencounter_start_da…reporterdevice_idlocation_area_n…<chr><chr><dttm><lgl><chr><chr>1uuid…uuid:469f71d3-d…2020-11-0212:06:19NA5afb51f3…Testsite1#with35morevariables:location_quadrat_photo<chr>,#location_corner1<chr>,location_corner1_longitude<dbl>,#location_corner1_latitude<dbl>,location_corner1_altitude<dbl>,#habitat_morphological_type<chr>,habitat_morphological_type_photo<chr>,#perimeter_corner2<chr>,perimeter_corner2_longitude<dbl>,#perimeter_corner2_latitude<dbl>,perimeter_corner2_altitude<dbl>,#perimeter_corner3<chr>,perimeter_corner3_longitude<dbl>,R>head(fq_data_R>head(fq_data_strata)#Atibble:3×50nvis_level3_broa…max_height_mfoliage_coverdominant_specie…dominant_specie…<chr><dbl><chr><chr><chr>1c1.0_surface_cru…0.15-0Lichensp1Lichensp22w3.0_shrub0.530-10Shrubsp1Shrubsp23w2.1_mallee25-0Malleesp1Malleesp2#with45morevariables:dominant_species_3<chr>,dominant_species_4<chr>,#id<chr>,submissions_id<chr>,odata_context.x<chr>,#meta_instance_id<chr>,encounter_start_datetime<dttm>,reporter<lgl>,#device_id<chr>,location_area_name<chr>,location_quadrat_photo<chr>,#habitat_morphological_type<chr>,habitat_morphological_type_photo<chr>,R>head(fq_data_taxa)#Atibble:2×49field_namephoto_in_situtaxon_encounter…life_formvoucher_specime…<chr><chr><chr><chr><chr>1Mallee1media/1604290400891.jpgPOINT(115.8844…w2.1_mal…NHA70209-012Shrub1media/1604290499008.jpgPOINT(115.8844…w3.0_shr…14724808#with44morevariables:voucher_specimen_label<chr>,id<chr>,#submissions_id<chr>,odata_context.x<chr>,meta_instance_id<chr>,#encounter_start_datetime<dttm>,reporter<lgl>,device_id<chr>,#location_area_name<chr>,location_quadrat_photo<chr>,R>sR>suR>supR>suppR>supprR>suppreR>suppresR>suppressR>suppressMR>suppressMeR>suppressMesR>suppressMessR>suppressMessaR>suppressMessagR>suppressMessageR>suppressMessagesR>suppressMessages(R>suppressMessages(lR>suppressMessages(liR>suppressMessages(libR>suppressMessages(librR>suppressMessages(libraR>suppressMessages(librarR>suppressMessages(libraryR>suppressMessages(library(R>suppressMessages(library(tR>suppressMessages(library(tiR>suppressMessages(library(tidR>suppressMessages(library(tidyR>suppressMessages(library(tidyvR>suppressMessages(library(tidyveR>suppressMessages(library(tidyverR>suppressMessages(library(tidyversR>suppressMessages(library(tidyverseR>suppressMessages(library(tidyverse)R>lR>liR>libR>librR>libraR>librarR>libraryR>library(R>library(rR>library(ruR>library(ruOR>library(ruODR>library(ruODKR>rR>ruR>ruOR>ruODR>ruODKR>ruODK:R>ruODK::R>ruODK::rR>ruODK::ruR>ruODK::ru_R>ruODK::ru_sR>ruODK::ru_seR>ruODK::ru_setR>ruODK::ru_setuR>ruODK::ru_setup+s+sv+svc=S+svc=Sy+svc=Sys+svc=Sys.+svc=Sys.g+svc=Sys.ge+svc=Sys.get+svc=Sys.gete+svc=Sys.geten+svc=Sys.getenv+svc=Sys.getenv(+svc=Sys.getenv("+svc=Sys.getenv("O+svc=Sys.getenv("OD+svc=Sys.getenv("ODK+svc=Sys.getenv("ODKC+svc=Sys.getenv("ODKC_+svc=Sys.getenv("ODKC_T+svc=Sys.getenv("ODKC_TE+svc=Sys.getenv("ODKC_TES+svc=Sys.getenv("ODKC_TEST+svc=Sys.getenv("ODKC_TEST_+svc=Sys.getenv("ODKC_TEST_S+svc=Sys.getenv("ODKC_TEST_SV+svc=Sys.getenv("ODKC_TEST_SVC+svc=Sys.getenv("ODKC_TEST_SVC"+svc=Sys.getenv("ODKC_TEST_SVC")+u+un=r+un=ru+un=ruO+un=ruOD+un=ruODK+un=ruODK:+un=ruODK::+un=ruODK::g+un=ruODK::ge+un=ruODK::get+un=ruODK::get_+un=ruODK::get_t+un=ruODK::get_te+un=ruODK::get_tes+un=ruODK::get_test+un=ruODK::get_test_+un=ruODK::get_test_u+un=ruODK::get_test_un+un=ruODK::get_test_un(+un=ruODK::get_test_un()+p+pw=r+pw=ru+pw=ruO+pw=ruOD+pw=ruODK+pw=ruODK:+pw=ruODK::+pw=ruODK::g+pw=ruODK::ge+pw=ruODK::get+pw=ruODK::get_+pw=ruODK::get_t+pw=ruODK::get_te+pw=ruODK::get_tes+pw=ruODK::get_test+pw=ruODK::get_test_+pw=ruODK::get_test_p+pw=ruODK::get_test_pw+pw=ruODK::get_test_pw(R>fq_svc<R>fq_svc<-rR>fq_svc<-ruR>fq_svc<-ruOR>fq_svc<-ruODR>fq_svc<-ruODKR>fq_svc<-ruODK:R>fq_svc<-ruODK::R>fq_svc<-ruODK::oR>fq_svc<-ruODK::odR>fq_svc<-ruODK::odaR>fq_svc<-ruODK::odatR>fq_svc<-ruODK::odataR>fq_svc<-ruODK::odata_R>fq_svc<-ruODK::odata_sR>fq_svc<-ruODK::odata_seR>fq_svc<-ruODK::odata_serR>fq_svc<-ruODK::odata_servR>fq_svc<-ruODK::odata_serviR>fq_svc<-ruODK::odata_servicR>fq_svc<-ruODK::odata_serviceR>fq_svc<-ruODK::odata_service_R>fq_svc<-ruODK::odata_service_gR>fq_svc<-ruODK::odata_service_geR>fq_svc<-ruODK::odata_service_getR>fq_svc<-ruODK::odata_service_get(R>fq_data<R>fq_data<-rR>fq_data<-ruR>fq_data<-ruOR>fq_data<-ruODR>fq_data<-ruODKR>fq_data<-ruODK:R>fq_data<-ruODK::R>fq_data<-ruODK::oR>fq_data<-ruODK::odR>fq_data<-ruODK::odaR>fq_data<-ruODK::odatR>fq_data<-ruODK::odataR>fq_data<-ruODK::odata_R>fq_data<-ruODK::odata_sR>fq_data<-ruODK::odata_suR>fq_data<-ruODK::odata_subR>fq_data<-ruODK::odata_submR>fq_data<-ruODK::odata_submiR>fq_data<-ruODK::odata_submisR>fq_data<-ruODK::odata_submissR>fq_data<-ruODK::odata_submissiR>fq_data<-ruODK::odata_submissioR>fq_data<-ruODK::odata_submissionR>fq_data<-ruODK::odata_submission_R>fq_data<-ruODK::odata_submission_gR>fq_data<-ruODK::odata_submission_geR>fq_data<-ruODK::odata_submission_get+table=fq_svc$name[1+table=fq_svc$name[1]+table=fq_svc$name[1],w+table=fq_svc$name[1],wk+table=fq_svc$name[1],wkt=T+table=fq_svc$name[1],wkt=TR+table=fq_svc$name[1],wkt=TRU+table=fq_svc$name[1],wkt=TRUE+table=fq_svc$name[1],wkt=TRUE,v+table=fq_svc$name[1],wkt=TRUE,ve+table=fq_svc$name[1],wkt=TRUE,ver+table=fq_svc$name[1],wkt=TRUE,verb+table=fq_svc$name[1],wkt=TRUE,verbo+table=fq_svc$name[1],wkt=TRUE,verbos+table=fq_svc$name[1],wkt=TRUE,verbose=T+table=fq_svc$name[1],wkt=TRUE,verbose=TR+table=fq_svc$name[1],wkt=TRUE,verbose=TRUR>fq_data_sR>fq_data_stR>fq_data_strR>fq_data_straR>fq_data_stratR>fq_data_strata<R>fq_data_strata<-rR>fq_data_strata<-ruR>fq_data_strata<-ruOR>fq_data_strata<-ruODR>fq_data_strata<-ruODKR>fq_data_strata<-ruODK:R>fq_data_strata<-ruODK::R>fq_data_strata<-ruODK::oR>fq_data_strata<-ruODK::odR>fq_data_strata<-ruODK::odaR>fq_data_strata<-ruODK::odatR>fq_data_strata<-ruODK::odataR>fq_data_strata<-ruODK::odata_R>fq_data_strata<-ruODK::odata_sR>fq_data_strata<-ruODK::odata_suR>fq_data_strata<-ruODK::odata_subR>fq_data_strata<-ruODK::odata_submR>fq_data_strata<-ruODK::odata_submiR>fq_data_strata<-ruODK::odata_submisR>fq_data_strata<-ruODK::odata_submissR>fq_data_strata<-ruODK::odata_submissiR>fq_data_strata<-ruODK::odata_submissioR>fq_data_strata<-ruODK::odata_submissionR>fq_data_strata<-ruODK::odata_submission_R>fq_data_strata<-ruODK::odata_submission_gR>fq_data_strata<-ruODK::odata_submission_geR>fq_data_strata<-ruODK::odata_submission_get+table=fq_svc$name[2+table=fq_svc$name[2]+table=fq_svc$name[2],w+table=fq_svc$name[2],wk+table=fq_svc$name[2],wkt=T+table=fq_svc$name[2],wkt=TR+table=fq_svc$name[2],wkt=TRU+table=fq_svc$name[2],wkt=TRUE+table=fq_svc$name[2],wkt=TRUE,v+table=fq_svc$name[2],wkt=TRUE,ve+table=fq_svc$name[2],wkt=TRUE,ver+table=fq_svc$name[2],wkt=TRUE,verb+table=fq_svc$name[2],wkt=TRUE,verbo+table=fq_svc$name[2],wkt=TRUE,verbos+table=fq_svc$name[2],wkt=TRUE,verbose=T+table=fq_svc$name[2],wkt=TRUE,verbose=TR+table=fq_svc$name[2],wkt=TRUE,verbose=TRUR>fq_data_tR>fq_data_taR>fq_data_taxR>fq_data_taxa<R>fq_data_taxa<-rR>fq_data_taxa<-ruR>fq_data_taxa<-ruOR>fq_data_taxa<-ruODR>fq_data_taxa<-ruODKR>fq_data_taxa<-ruODK:R>fq_data_taxa<-ruODK::R>fq_data_taxa<-ruODK::oR>fq_data_taxa<-ruODK::odR>fq_data_taxa<-ruODK::odaR>fq_data_taxa<-ruODK::odatR>fq_data_taxa<-ruODK::odataR>fq_data_taxa<-ruODK::odata_R>fq_data_taxa<-ruODK::odata_sR>fq_data_taxa<-ruODK::odata_suR>fq_data_taxa<-ruODK::odata_subR>fq_data_taxa<-ruODK::odata_submR>fq_data_taxa<-ruODK::odata_submiR>fq_data_taxa<-ruODK::odata_submisR>fq_data_taxa<-ruODK::odata_submissR>fq_data_taxa<-ruODK::odata_submissiR>fq_data_taxa<-ruODK::odata_submissioR>fq_data_taxa<-ruODK::odata_submissionR>fq_data_taxa<-ruODK::odata_submission_R>fq_data_taxa<-ruODK::odata_submission_gR>fq_data_taxa<-ruODK::odata_submission_geR>fq_data_taxa<-ruODK::odata_submission_get+table=fq_svc$name[3+table=fq_svc$name[3]+table=fq_svc$name[3],w+table=fq_svc$name[3],wk+table=fq_svc$name[3],wkt=T+table=fq_svc$name[3],wkt=TR+table=fq_svc$name[3],wkt=TRU+table=fq_svc$name[3],wkt=TRUE+table=fq_svc$name[3],wkt=TRUE,v+table=fq_svc$name[3],wkt=TRUE,ve+table=fq_svc$name[3],wkt=TRUE,ver+table=fq_svc$name[3],wkt=TRUE,verb+table=fq_svc$name[3],wkt=TRUE,verbo+table=fq_svc$name[3],wkt=TRUE,verbos+table=fq_svc$name[3],wkt=TRUE,verbose=T+table=fq_svc$name[3],wkt=TRUE,verbose=TR+table=fq_svc$name[3],wkt=TRUE,verbose=TRUR>nR>naR>namR>nameR>namesR>names(R>names(fR>names(fqR>names(fq_R>names(fq_dR>names(fq_daR>names(fq_datR>names(fq_dataR>names(fq_data)R>head(fq_data_sR>head(fq_data_stR>head(fq_data_strR>head(fq_data_straR>head(fq_data_stratR>head(fq_data_strataR>head(fq_data_tR>head(fq_data_taR>head(fq_data_taxR>head(fq_data_taxa \ No newline at end of file +#Useyourform'sODataServiceURL(Form>Submissions>Analysedata)R>#Readvignette("setup")onsettingusernameandpasswordvia.RenvironR>suppressMessages(library(tidyverse))R>R>library(ruODK)R>ruODK::ru_setup(++svc+svc=+svc=Sys.getenv("ODKC_TEST_SVC"),+un+un=+un=ruODK::get_test_un(),+pw+pw=+pw=ruODK::get_test_pw()+)<ruODKsettings>DefaultODKCentralProjectID:2DefaultODKCentralFormID:Flora-Quadrat-04DefaultODKCentralURL:https://odkc.dbca.wa.gov.auDefaultODKCentralUsername:Florian.Mayer@dbca.wa.gov.auDefaultODKCentralPassword:runruODK::get_default_pw()toshowDefaultODKCentralPassphrase:runruODK::get_default_pp()toshowDefaultTimeZone:Australia/PerthDefaultODKCentralVersion:1.4DefaultHTTPGETretries:3Verbosemessages:TRUETestODKCentralProjectID:2TestODKCentralFormID:Flora-Quadrat-04TestODKCentralFormID(ZIPtests):Spotlighting-06TestODKCentralFormID(Attachmenttests):Flora-Quadrat-04-attTestODKCentralFormID(Parsingtests):Flora-Quadrat-04-gapTestODKCentralFormID(WKTtests):LocationsTestODKCentralURL:https://odkc.dbca.wa.gov.auTestODKCentralUsername:Florian.Mayer@dbca.wa.gov.auTestODKCentralPassword:runruODK::get_test_pw()toshowTestODKCentralPassphrase:runruODK::get_test_pp()toshowTestODKCentralVersion:1.4R>#ListavailablesubmissiondatatablesR>fR>fqR>fq_R>fq_sR>fq_svR>fq_svcR>fq_svc<-R>fq_svc<-ruODK::odata_service_get()#Atibble:3×3namekindurl<chr><chr><chr>1SubmissionsEntitySetSubmissions2Submissions.vegetation_stratumEntitySetSubmissions.vegetation_stratum3Submissions.taxon_encounterEntitySetSubmissions.taxon_encounterR>#DownloadmainsubmissionsandattachmentsR>fq_dR>fq_daR>fq_datR>fq_dataR>fq_data<-R>fq_data<-ruODK::odata_submission_get(+t+ta+tab+tabl+table+table=+table=f+table=fq+table=fq_+table=fq_s+table=fq_sv+table=fq_svc+table=fq_svc$+table=fq_svc$n+table=fq_svc$na+table=fq_svc$nam+table=fq_svc$name+table=fq_svc$name[+table=fq_svc$name[1],+table=fq_svc$name[1],wkt+table=fq_svc$name[1],wkt=+table=fq_svc$name[1],wkt=TRUE,+table=fq_svc$name[1],wkt=TRUE,verbose+table=fq_svc$name[1],wkt=TRUE,verbose=+table=fq_svc$name[1],wkt=TRUE,verbose=TRUEDownloadingsubmissions...Downloadedsubmissions.Readingformschema...Formschemav1.4Parsingsubmissions...Notunnestinggeofields:value_location_corner1,value_perimeter_corner2,value_perimeter_corner3,value_perimeter_corner4,value_taxon_encounter_taxon_encounter_locationUnnesting:valueUnnestingcolumn"value"Unnestingmorelistcols:value_meta,value_location,value_habitat,value_perimeter,value___systemUnnesting:value_meta,value_location,value_habitat,value_perimeter,value___systemUnnestingcolumn"value_meta"Unnestingcolumn"value_location"Unnestingcolumn"value_habitat"Unnestingcolumn"value_perimeter"Unnestingcolumn"value___system"Founddate/times:encounter_start_datetime,encounter_end_datetime.FoundattachmentsinmainSubmissionstable:location_quadrat_photo,habitat_morphological_type_photo,perimeter_mudmap_photo.Downloadingattachments...Usinglocaldirectory"media".Filealreadydownloaded,keeping"media/1604290006239.jpg".Filealreadydownloaded,keeping"media/1604290049411.jpg".Filealreadydownloaded,keeping"media/1604290379613.jpg".Foundgeopoints:location_corner1,perimeter_corner2,perimeter_corner3,perimeter_corner4.Parsinglocation_corner1...Parsingperimeter_corner2...Parsingperimeter_corner3...Parsingperimeter_corner4...Foundgeotraces:.Foundgeoshapes:.Returningparsedsubmissions.R>#Downloadfirstnestedsubtable,jointomainsubmissionsR>fq_data_R>fq_data_strataR>fq_data_strata<-R>fq_data_strata<-ruODK::odata_submission_get(+table=fq_svc$name[2],+table=fq_svc$name[2],wkt+table=fq_svc$name[2],wkt=+table=fq_svc$name[2],wkt=TRUE,+table=fq_svc$name[2],wkt=TRUE,verbose+table=fq_svc$name[2],wkt=TRUE,verbose=+table=fq_svc$name[2],wkt=TRUE,verbose=TRUE+)%+)%>+)%>%+d+dp+dpl+dply+dplyr+dplyr:+dplyr::+dplyr::l+dplyr::le+dplyr::lef+dplyr::left+dplyr::left_+dplyr::left_j+dplyr::left_jo+dplyr::left_joi+dplyr::left_join+dplyr::left_join(+dplyr::left_join(f+dplyr::left_join(fq+dplyr::left_join(fq_+dplyr::left_join(fq_d+dplyr::left_join(fq_da+dplyr::left_join(fq_dat+dplyr::left_join(fq_data+dplyr::left_join(fq_data,+dplyr::left_join(fq_data,b+dplyr::left_join(fq_data,by+dplyr::left_join(fq_data,by=+dplyr::left_join(fq_data,by=c+dplyr::left_join(fq_data,by=c(+dplyr::left_join(fq_data,by=c("+dplyr::left_join(fq_data,by=c("s+dplyr::left_join(fq_data,by=c("su+dplyr::left_join(fq_data,by=c("sub+dplyr::left_join(fq_data,by=c("subm+dplyr::left_join(fq_data,by=c("submi+dplyr::left_join(fq_data,by=c("submis+dplyr::left_join(fq_data,by=c("submiss+dplyr::left_join(fq_data,by=c("submissi+dplyr::left_join(fq_data,by=c("submissio+dplyr::left_join(fq_data,by=c("submission+dplyr::left_join(fq_data,by=c("submissions+dplyr::left_join(fq_data,by=c("submissions_+dplyr::left_join(fq_data,by=c("submissions_i+dplyr::left_join(fq_data,by=c("submissions_id+dplyr::left_join(fq_data,by=c("submissions_id"+dplyr::left_join(fq_data,by=c("submissions_id"=+dplyr::left_join(fq_data,by=c("submissions_id"="+dplyr::left_join(fq_data,by=c("submissions_id"="i+dplyr::left_join(fq_data,by=c("submissions_id"="id+dplyr::left_join(fq_data,by=c("submissions_id"="id"+dplyr::left_join(fq_data,by=c("submissions_id"="id")+dplyr::left_join(fq_data,by=c("submissions_id"="id"))Founddate/times:.Foundgeopoints:.R>#Downloadsecondnestedsubtable,jointomainsubmissionsR>fq_data_taxaR>fq_data_taxa<-R>fq_data_taxa<-ruODK::odata_submission_get(+table=fq_svc$name[3],+table=fq_svc$name[3],wkt+table=fq_svc$name[3],wkt=+table=fq_svc$name[3],wkt=TRUE,+table=fq_svc$name[3],wkt=TRUE,verbose+table=fq_svc$name[3],wkt=TRUE,verbose=+table=fq_svc$name[3],wkt=TRUE,verbose=TRUEFoundattachmentsinnestedsub-table:photo_in_situ.Filealreadydownloaded,keeping"media/1604290400891.jpg".Filealreadydownloaded,keeping"media/1604290499008.jpg".R>#Viewdata[18]"perimeter_corner3"[19]"perimeter_corner3_longitude"[20]"perimeter_corner3_latitude"[21]"perimeter_corner3_altitude"[22]"perimeter_corner4"[23]"perimeter_corner4_longitude"[24]"perimeter_corner4_latitude"[25]"perimeter_corner4_altitude"[26]"perimeter_mudmap_photo"[27]"encounter_end_datetime"[28]"system_submission_date"[29]"system_updated_at"[30]"system_submitter_id"[31]"system_submitter_name"[32]"system_attachments_present"[33]"system_attachments_expected"[34]"system_status"[35]"system_review_state"[36]"system_device_id"[37]"system_edits"[38]"system_form_version"[39]"vegetation_stratum_odata_navigation_link"[40]"taxon_encounter_odata_navigation_link"[41]"odata_context"R>hR>heR>heaR>headR>head(R>head(fR>head(fqR>head(fq_R>head(fq_dR>head(fq_daR>head(fq_datR>head(fq_dataR>head(fq_data)#Atibble:1×41idmeta_instance_idencounter_start_da…reporterdevice_idlocation_area_n…<chr><chr><dttm><lgl><chr><chr>1uuid…uuid:469f71d3-d…2020-11-0212:06:19NA5afb51f3…Testsite1#with35morevariables:location_quadrat_photo<chr>,#location_corner1<chr>,location_corner1_longitude<dbl>,#location_corner1_latitude<dbl>,location_corner1_altitude<dbl>,#habitat_morphological_type<chr>,habitat_morphological_type_photo<chr>,#perimeter_corner2<chr>,perimeter_corner2_longitude<dbl>,#perimeter_corner2_latitude<dbl>,perimeter_corner2_altitude<dbl>,#perimeter_corner3<chr>,perimeter_corner3_longitude<dbl>,R>head(fq_data_R>head(fq_data_strata)#Atibble:3×50nvis_level3_broa…max_height_mfoliage_coverdominant_specie…dominant_specie…<chr><dbl><chr><chr><chr>1c1.0_surface_cru…0.15-0Lichensp1Lichensp22w3.0_shrub0.530-10Shrubsp1Shrubsp23w2.1_mallee25-0Malleesp1Malleesp2#with45morevariables:dominant_species_3<chr>,dominant_species_4<chr>,#id<chr>,submissions_id<chr>,odata_context.x<chr>,#meta_instance_id<chr>,encounter_start_datetime<dttm>,reporter<lgl>,#device_id<chr>,location_area_name<chr>,location_quadrat_photo<chr>,#habitat_morphological_type<chr>,habitat_morphological_type_photo<chr>,R>head(fq_data_taxa)#Atibble:2×49field_namephoto_in_situtaxon_encounter…life_formvoucher_specime…<chr><chr><chr><chr><chr>1Mallee1media/1604290400891.jpgPOINT(115.8844…w2.1_mal…NHA70209-012Shrub1media/1604290499008.jpgPOINT(115.8844…w3.0_shr…14724808#with44morevariables:voucher_specimen_label<chr>,id<chr>,#submissions_id<chr>,odata_context.x<chr>,meta_instance_id<chr>,#encounter_start_datetime<dttm>,reporter<lgl>,device_id<chr>,#location_area_name<chr>,location_quadrat_photo<chr>,R>sR>suR>supR>suppR>supprR>suppreR>suppresR>suppressR>suppressMR>suppressMeR>suppressMesR>suppressMessR>suppressMessaR>suppressMessagR>suppressMessageR>suppressMessagesR>suppressMessages(R>suppressMessages(lR>suppressMessages(liR>suppressMessages(libR>suppressMessages(librR>suppressMessages(libraR>suppressMessages(librarR>suppressMessages(libraryR>suppressMessages(library(R>suppressMessages(library(tR>suppressMessages(library(tiR>suppressMessages(library(tidR>suppressMessages(library(tidyR>suppressMessages(library(tidyvR>suppressMessages(library(tidyveR>suppressMessages(library(tidyverR>suppressMessages(library(tidyversR>suppressMessages(library(tidyverseR>suppressMessages(library(tidyverse)R>lR>liR>libR>librR>libraR>librarR>libraryR>library(R>library(rR>library(ruR>library(ruOR>library(ruODR>library(ruODKR>rR>ruR>ruOR>ruODR>ruODKR>ruODK:R>ruODK::R>ruODK::rR>ruODK::ruR>ruODK::ru_R>ruODK::ru_sR>ruODK::ru_seR>ruODK::ru_setR>ruODK::ru_setuR>ruODK::ru_setup+s+sv+svc=S+svc=Sy+svc=Sys+svc=Sys.+svc=Sys.g+svc=Sys.ge+svc=Sys.get+svc=Sys.gete+svc=Sys.geten+svc=Sys.getenv+svc=Sys.getenv(+svc=Sys.getenv("+svc=Sys.getenv("O+svc=Sys.getenv("OD+svc=Sys.getenv("ODK+svc=Sys.getenv("ODKC+svc=Sys.getenv("ODKC_+svc=Sys.getenv("ODKC_T+svc=Sys.getenv("ODKC_TE+svc=Sys.getenv("ODKC_TES+svc=Sys.getenv("ODKC_TEST+svc=Sys.getenv("ODKC_TEST_+svc=Sys.getenv("ODKC_TEST_S+svc=Sys.getenv("ODKC_TEST_SV+svc=Sys.getenv("ODKC_TEST_SVC+svc=Sys.getenv("ODKC_TEST_SVC"+svc=Sys.getenv("ODKC_TEST_SVC")+u+un=r+un=ru+un=ruO+un=ruOD+un=ruODK+un=ruODK:+un=ruODK::+un=ruODK::g+un=ruODK::ge+un=ruODK::get+un=ruODK::get_+un=ruODK::get_t+un=ruODK::get_te+un=ruODK::get_tes+un=ruODK::get_test+un=ruODK::get_test_+un=ruODK::get_test_u+un=ruODK::get_test_un+un=ruODK::get_test_un(+un=ruODK::get_test_un()+p+pw=r+pw=ru+pw=ruO+pw=ruOD+pw=ruODK+pw=ruODK:+pw=ruODK::+pw=ruODK::g+pw=ruODK::ge+pw=ruODK::get+pw=ruODK::get_+pw=ruODK::get_t+pw=ruODK::get_te+pw=ruODK::get_tes+pw=ruODK::get_test+pw=ruODK::get_test_+pw=ruODK::get_test_p+pw=ruODK::get_test_pw+pw=ruODK::get_test_pw(R>fq_svc<R>fq_svc<-rR>fq_svc<-ruR>fq_svc<-ruOR>fq_svc<-ruODR>fq_svc<-ruODKR>fq_svc<-ruODK:R>fq_svc<-ruODK::R>fq_svc<-ruODK::oR>fq_svc<-ruODK::odR>fq_svc<-ruODK::odaR>fq_svc<-ruODK::odatR>fq_svc<-ruODK::odataR>fq_svc<-ruODK::odata_R>fq_svc<-ruODK::odata_sR>fq_svc<-ruODK::odata_seR>fq_svc<-ruODK::odata_serR>fq_svc<-ruODK::odata_servR>fq_svc<-ruODK::odata_serviR>fq_svc<-ruODK::odata_servicR>fq_svc<-ruODK::odata_serviceR>fq_svc<-ruODK::odata_service_R>fq_svc<-ruODK::odata_service_gR>fq_svc<-ruODK::odata_service_geR>fq_svc<-ruODK::odata_service_getR>fq_svc<-ruODK::odata_service_get(R>fq_data<R>fq_data<-rR>fq_data<-ruR>fq_data<-ruOR>fq_data<-ruODR>fq_data<-ruODKR>fq_data<-ruODK:R>fq_data<-ruODK::R>fq_data<-ruODK::oR>fq_data<-ruODK::odR>fq_data<-ruODK::odaR>fq_data<-ruODK::odatR>fq_data<-ruODK::odataR>fq_data<-ruODK::odata_R>fq_data<-ruODK::odata_sR>fq_data<-ruODK::odata_suR>fq_data<-ruODK::odata_subR>fq_data<-ruODK::odata_submR>fq_data<-ruODK::odata_submiR>fq_data<-ruODK::odata_submisR>fq_data<-ruODK::odata_submissR>fq_data<-ruODK::odata_submissiR>fq_data<-ruODK::odata_submissioR>fq_data<-ruODK::odata_submissionR>fq_data<-ruODK::odata_submission_R>fq_data<-ruODK::odata_submission_gR>fq_data<-ruODK::odata_submission_geR>fq_data<-ruODK::odata_submission_get+table=fq_svc$name[1+table=fq_svc$name[1]+table=fq_svc$name[1],w+table=fq_svc$name[1],wk+table=fq_svc$name[1],wkt=T+table=fq_svc$name[1],wkt=TR+table=fq_svc$name[1],wkt=TRU+table=fq_svc$name[1],wkt=TRUE+table=fq_svc$name[1],wkt=TRUE,v+table=fq_svc$name[1],wkt=TRUE,ve+table=fq_svc$name[1],wkt=TRUE,ver+table=fq_svc$name[1],wkt=TRUE,verb+table=fq_svc$name[1],wkt=TRUE,verbo+table=fq_svc$name[1],wkt=TRUE,verbos+table=fq_svc$name[1],wkt=TRUE,verbose=T+table=fq_svc$name[1],wkt=TRUE,verbose=TR+table=fq_svc$name[1],wkt=TRUE,verbose=TRUR>fq_data_sR>fq_data_stR>fq_data_strR>fq_data_straR>fq_data_stratR>fq_data_strata<R>fq_data_strata<-rR>fq_data_strata<-ruR>fq_data_strata<-ruOR>fq_data_strata<-ruODR>fq_data_strata<-ruODKR>fq_data_strata<-ruODK:R>fq_data_strata<-ruODK::R>fq_data_strata<-ruODK::oR>fq_data_strata<-ruODK::odR>fq_data_strata<-ruODK::odaR>fq_data_strata<-ruODK::odatR>fq_data_strata<-ruODK::odataR>fq_data_strata<-ruODK::odata_R>fq_data_strata<-ruODK::odata_sR>fq_data_strata<-ruODK::odata_suR>fq_data_strata<-ruODK::odata_subR>fq_data_strata<-ruODK::odata_submR>fq_data_strata<-ruODK::odata_submiR>fq_data_strata<-ruODK::odata_submisR>fq_data_strata<-ruODK::odata_submissR>fq_data_strata<-ruODK::odata_submissiR>fq_data_strata<-ruODK::odata_submissioR>fq_data_strata<-ruODK::odata_submissionR>fq_data_strata<-ruODK::odata_submission_R>fq_data_strata<-ruODK::odata_submission_gR>fq_data_strata<-ruODK::odata_submission_geR>fq_data_strata<-ruODK::odata_submission_get+table=fq_svc$name[2+table=fq_svc$name[2]+table=fq_svc$name[2],w+table=fq_svc$name[2],wk+table=fq_svc$name[2],wkt=T+table=fq_svc$name[2],wkt=TR+table=fq_svc$name[2],wkt=TRU+table=fq_svc$name[2],wkt=TRUE+table=fq_svc$name[2],wkt=TRUE,v+table=fq_svc$name[2],wkt=TRUE,ve+table=fq_svc$name[2],wkt=TRUE,ver+table=fq_svc$name[2],wkt=TRUE,verb+table=fq_svc$name[2],wkt=TRUE,verbo+table=fq_svc$name[2],wkt=TRUE,verbos+table=fq_svc$name[2],wkt=TRUE,verbose=T+table=fq_svc$name[2],wkt=TRUE,verbose=TR+table=fq_svc$name[2],wkt=TRUE,verbose=TRUR>fq_data_tR>fq_data_taR>fq_data_taxR>fq_data_taxa<R>fq_data_taxa<-rR>fq_data_taxa<-ruR>fq_data_taxa<-ruOR>fq_data_taxa<-ruODR>fq_data_taxa<-ruODKR>fq_data_taxa<-ruODK:R>fq_data_taxa<-ruODK::R>fq_data_taxa<-ruODK::oR>fq_data_taxa<-ruODK::odR>fq_data_taxa<-ruODK::odaR>fq_data_taxa<-ruODK::odatR>fq_data_taxa<-ruODK::odataR>fq_data_taxa<-ruODK::odata_R>fq_data_taxa<-ruODK::odata_sR>fq_data_taxa<-ruODK::odata_suR>fq_data_taxa<-ruODK::odata_subR>fq_data_taxa<-ruODK::odata_submR>fq_data_taxa<-ruODK::odata_submiR>fq_data_taxa<-ruODK::odata_submisR>fq_data_taxa<-ruODK::odata_submissR>fq_data_taxa<-ruODK::odata_submissiR>fq_data_taxa<-ruODK::odata_submissioR>fq_data_taxa<-ruODK::odata_submissionR>fq_data_taxa<-ruODK::odata_submission_R>fq_data_taxa<-ruODK::odata_submission_gR>fq_data_taxa<-ruODK::odata_submission_geR>fq_data_taxa<-ruODK::odata_submission_get+table=fq_svc$name[3+table=fq_svc$name[3]+table=fq_svc$name[3],w+table=fq_svc$name[3],wk+table=fq_svc$name[3],wkt=T+table=fq_svc$name[3],wkt=TR+table=fq_svc$name[3],wkt=TRU+table=fq_svc$name[3],wkt=TRUE+table=fq_svc$name[3],wkt=TRUE,v+table=fq_svc$name[3],wkt=TRUE,ve+table=fq_svc$name[3],wkt=TRUE,ver+table=fq_svc$name[3],wkt=TRUE,verb+table=fq_svc$name[3],wkt=TRUE,verbo+table=fq_svc$name[3],wkt=TRUE,verbos+table=fq_svc$name[3],wkt=TRUE,verbose=T+table=fq_svc$name[3],wkt=TRUE,verbose=TR+table=fq_svc$name[3],wkt=TRUE,verbose=TRUR>nR>naR>namR>nameR>namesR>names(R>names(fR>names(fqR>names(fq_R>names(fq_dR>names(fq_daR>names(fq_datR>names(fq_dataR>names(fq_data)R>head(fq_data_sR>head(fq_data_stR>head(fq_data_strR>head(fq_data_straR>head(fq_data_stratR>head(fq_data_strataR>head(fq_data_tR>head(fq_data_taR>head(fq_data_taxR>head(fq_data_taxa diff --git a/man/form_list.Rd b/man/form_list.Rd index 2b116630..69ee1e85 100644 --- a/man/form_list.Rd +++ b/man/form_list.Rd @@ -10,7 +10,7 @@ form_list( un = get_default_un(), pw = get_default_pw(), retries = get_retries(), - orders = c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd"), + orders = get_default_orders(), tz = get_default_tz() ) } diff --git a/man/form_schema_parse.Rd b/man/form_schema_parse.Rd index 8defd030..d8354f68 100644 --- a/man/form_schema_parse.Rd +++ b/man/form_schema_parse.Rd @@ -64,7 +64,7 @@ Other utilities: \code{\link{drop_null_coords}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/get_one_attachment.Rd b/man/get_one_attachment.Rd index 981c39ac..96358992 100644 --- a/man/get_one_attachment.Rd +++ b/man/get_one_attachment.Rd @@ -94,7 +94,7 @@ Other utilities: \code{\link{drop_null_coords}()}, \code{\link{form_schema_parse}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/get_one_submission.Rd b/man/get_one_submission.Rd index 2e78ae38..658dfce6 100644 --- a/man/get_one_submission.Rd +++ b/man/get_one_submission.Rd @@ -107,7 +107,7 @@ Other utilities: \code{\link{drop_null_coords}()}, \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/get_one_submission_attachment_list.Rd b/man/get_one_submission_att_list.Rd similarity index 96% rename from man/get_one_submission_attachment_list.Rd rename to man/get_one_submission_att_list.Rd index 7f9612cd..59dd883b 100644 --- a/man/get_one_submission_attachment_list.Rd +++ b/man/get_one_submission_att_list.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/attachment_list.R -\name{get_one_submission_attachment_list} -\alias{get_one_submission_attachment_list} +\name{get_one_submission_att_list} +\alias{get_one_submission_att_list} \title{List all attachments of one submission.} \usage{ -get_one_submission_attachment_list( +get_one_submission_att_list( iid, pid = get_default_pid(), fid = get_default_fid(), @@ -93,7 +93,7 @@ its filename to the request URL to download only that file. sl <- submission_list() -al <- get_one_submission_attachment_list(sl$instance_id[[1]]) +al <- get_one_submission_att_list(sl$instance_id[[1]]) al \%>\% knitr::kable(.) # attachment_list returns a tibble diff --git a/man/get_one_submission_audit.Rd b/man/get_one_submission_audit.Rd index 92061ec1..a9b0a3a7 100644 --- a/man/get_one_submission_audit.Rd +++ b/man/get_one_submission_audit.Rd @@ -111,7 +111,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, \code{\link{handle_ru_geopoints}()}, diff --git a/man/handle_ru_attachments.Rd b/man/handle_ru_attachments.Rd index dffa4b86..28b116e4 100644 --- a/man/handle_ru_attachments.Rd +++ b/man/handle_ru_attachments.Rd @@ -119,7 +119,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_datetimes}()}, \code{\link{handle_ru_geopoints}()}, diff --git a/man/handle_ru_datetimes.Rd b/man/handle_ru_datetimes.Rd index d5081c19..e57bf3d0 100644 --- a/man/handle_ru_datetimes.Rd +++ b/man/handle_ru_datetimes.Rd @@ -7,7 +7,7 @@ handle_ru_datetimes( data, form_schema, - orders = c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd"), + orders = get_default_orders(), tz = get_default_tz(), verbose = get_ru_verbose() ) @@ -72,7 +72,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_geopoints}()}, diff --git a/man/handle_ru_geopoints.Rd b/man/handle_ru_geopoints.Rd index 42f748f5..0d066d56 100644 --- a/man/handle_ru_geopoints.Rd +++ b/man/handle_ru_geopoints.Rd @@ -70,7 +70,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/handle_ru_geoshapes.Rd b/man/handle_ru_geoshapes.Rd index 97c89a66..7530b93c 100644 --- a/man/handle_ru_geoshapes.Rd +++ b/man/handle_ru_geoshapes.Rd @@ -90,7 +90,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/handle_ru_geotraces.Rd b/man/handle_ru_geotraces.Rd index 9e1f14ee..6d877f98 100644 --- a/man/handle_ru_geotraces.Rd +++ b/man/handle_ru_geotraces.Rd @@ -90,7 +90,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/isodt_to_local.Rd b/man/isodt_to_local.Rd index 15ba65a3..026d8cae 100644 --- a/man/isodt_to_local.Rd +++ b/man/isodt_to_local.Rd @@ -7,7 +7,8 @@ isodt_to_local( datetime_string, orders = c("YmdHMS", "YmdHMSz"), - tz = get_default_tz() + tz = get_default_tz(), + quiet = TRUE ) } \arguments{ @@ -15,16 +16,20 @@ isodt_to_local( XForms exported from ODK Central.} \item{orders}{(vector of character) Orders of datetime elements for -lubridate. +\code{lubridate}. Default: \code{c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz")}.} \item{tz}{A timezone to convert dates and times to. Read \code{vignette("setup", package = "ruODK")} to learn how \code{ruODK}'s timezone can be set globally or per function.} + +\item{quiet}{(lgl) Used in \code{lubridate::parse_date_time(quiet=quiet)} to +suppress warnings from attempting to parse all empty values or columns. +Run with \code{quiet=FALSE} to show any \code{lubridate} warnings.} } \value{ -A lubridate PosixCT datetime in the given timezone. +A \code{lubridate} PosixCT datetime in the given timezone. } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#stable}{\figure{lifecycle-stable.svg}{options: alt='[Stable]'}}}{\strong{[Stable]}} @@ -32,6 +37,8 @@ A lubridate PosixCT datetime in the given timezone. \details{ This function is used internally by \code{ruODK} to parse ISO timestamps to timezone-aware local times. + +Warnings are suppressed through \code{lubridate::parse_date_time(quiet=TRUE)}. } \seealso{ Other utilities: @@ -42,7 +49,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/odata_submission_get.Rd b/man/odata_submission_get.Rd index 5b5d22ea..96db5f6d 100644 --- a/man/odata_submission_get.Rd +++ b/man/odata_submission_get.Rd @@ -15,7 +15,7 @@ odata_submission_get( filter = NULL, parse = TRUE, download = TRUE, - orders = c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd"), + orders = get_default_orders(), local_dir = "media", pid = get_default_pid(), fid = get_default_fid(), diff --git a/man/odata_submission_rectangle.Rd b/man/odata_submission_rectangle.Rd index de40002f..a233c4ec 100644 --- a/man/odata_submission_rectangle.Rd +++ b/man/odata_submission_rectangle.Rd @@ -70,7 +70,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/predict_ruodk_name.Rd b/man/predict_ruodk_name.Rd index 472d81dd..236d7dde 100644 --- a/man/predict_ruodk_name.Rd +++ b/man/predict_ruodk_name.Rd @@ -39,7 +39,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/prepend_uuid.Rd b/man/prepend_uuid.Rd index 2134fa2a..823efb9d 100644 --- a/man/prepend_uuid.Rd +++ b/man/prepend_uuid.Rd @@ -33,7 +33,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/project_list.Rd b/man/project_list.Rd index 4232420c..9d9ee12b 100644 --- a/man/project_list.Rd +++ b/man/project_list.Rd @@ -9,7 +9,7 @@ project_list( un = get_default_un(), pw = get_default_pw(), retries = get_retries(), - orders = c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd"), + orders = get_default_orders(), tz = get_default_tz() ) } diff --git a/man/ruODK-package.Rd b/man/ruODK-package.Rd index 9027a3f5..e14ebdd8 100644 --- a/man/ruODK-package.Rd +++ b/man/ruODK-package.Rd @@ -24,7 +24,7 @@ Useful links: } \author{ -\strong{Maintainer}: Florian W. Mayer \email{Florian.Mayer@dbca.wa.gov.au} (\href{https://orcid.org/0000-0003-4269-4242}{ORCID}) +\strong{Maintainer}: Florian W. Mayer \email{Florian.Mayer@dpc.wa.gov.au} (\href{https://orcid.org/0000-0003-4269-4242}{ORCID}) Other contributors: \itemize{ diff --git a/man/ru_settings.Rd b/man/ru_settings.Rd index f9f39d17..21b145e5 100644 --- a/man/ru_settings.Rd +++ b/man/ru_settings.Rd @@ -9,6 +9,7 @@ \alias{get_default_pw} \alias{get_default_pp} \alias{get_default_tz} +\alias{get_default_orders} \alias{get_test_url} \alias{get_test_un} \alias{get_test_pw} @@ -41,6 +42,8 @@ get_default_pp() get_default_tz() +get_default_orders() + get_test_url() get_test_un() diff --git a/man/split_geopoint.Rd b/man/split_geopoint.Rd index 4090d2f1..64162dfd 100644 --- a/man/split_geopoint.Rd +++ b/man/split_geopoint.Rd @@ -79,7 +79,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/split_geoshape.Rd b/man/split_geoshape.Rd index 8405b53b..a4a65956 100644 --- a/man/split_geoshape.Rd +++ b/man/split_geoshape.Rd @@ -107,7 +107,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/split_geotrace.Rd b/man/split_geotrace.Rd index eaf3b097..fe29e067 100644 --- a/man/split_geotrace.Rd +++ b/man/split_geotrace.Rd @@ -123,7 +123,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/strip_uuid.Rd b/man/strip_uuid.Rd index e761fbee..719ea158 100644 --- a/man/strip_uuid.Rd +++ b/man/strip_uuid.Rd @@ -33,7 +33,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/submission_list.Rd b/man/submission_list.Rd index 65c6019a..6b389ab9 100644 --- a/man/submission_list.Rd +++ b/man/submission_list.Rd @@ -11,7 +11,7 @@ submission_list( un = get_default_un(), pw = get_default_pw(), retries = get_retries(), - orders = c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd"), + orders = get_default_orders(), tz = get_default_tz() ) } diff --git a/man/tidyeval.Rd b/man/tidyeval.Rd index 8e5edb04..44129925 100644 --- a/man/tidyeval.Rd +++ b/man/tidyeval.Rd @@ -17,8 +17,6 @@ These functions provide tidy eval-compatible ways to capture symbols (\code{sym()}, \code{syms()}, \code{ensym()}), expressions (\code{expr()}, \code{exprs()}, \code{enexpr()}), and quosures (\code{quo()}, \code{quos()}, \code{enquo()}). -To learn more about tidy eval and how to use these tools, read -\url{http://rlang.tidyverse.org/articles/tidy-evaluation.html} } \seealso{ Other utilities: @@ -29,7 +27,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/unnest_all.Rd b/man/unnest_all.Rd index a560e136..f25513dc 100644 --- a/man/unnest_all.Rd +++ b/man/unnest_all.Rd @@ -59,7 +59,7 @@ Other utilities: \code{\link{form_schema_parse}()}, \code{\link{get_one_attachment}()}, \code{\link{get_one_submission}()}, -\code{\link{get_one_submission_attachment_list}()}, +\code{\link{get_one_submission_att_list}()}, \code{\link{get_one_submission_audit}()}, \code{\link{handle_ru_attachments}()}, \code{\link{handle_ru_datetimes}()}, diff --git a/man/user_list.Rd b/man/user_list.Rd index 0b33a0fc..5ad4186e 100644 --- a/man/user_list.Rd +++ b/man/user_list.Rd @@ -10,7 +10,7 @@ user_list( un = get_default_un(), pw = get_default_pw(), retries = get_retries(), - orders = c("YmdHMS", "YmdHMSz", "Ymd HMS", "Ymd HMSz", "Ymd", "ymd"), + orders = get_default_orders(), tz = get_default_tz(), verbose = get_ru_verbose() ) diff --git a/man/yell_if_missing.Rd b/man/yell_if_missing.Rd index 544c383c..8bfee947 100644 --- a/man/yell_if_missing.Rd +++ b/man/yell_if_missing.Rd @@ -4,7 +4,16 @@ \alias{yell_if_missing} \title{Abort on missing ODK Central credentials (url, username, password).} \usage{ -yell_if_missing(url, un, pw, pid = NULL, fid = NULL, iid = NULL) +yell_if_missing( + url, + un, + pw, + pid = NULL, + fid = NULL, + iid = NULL, + did = NULL, + eid = NULL +) } \arguments{ \item{url}{A URL (character)} @@ -18,6 +27,10 @@ yell_if_missing(url, un, pw, pid = NULL, fid = NULL, iid = NULL) \item{fid}{A form ID (character, optional)} \item{iid}{A submission instance ID (character, optional)} + +\item{did}{An Entity List (dataset) name (character, optional)} + +\item{eid}{An Entity UUID (character, optional)} } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#stable}{\figure{lifecycle-stable.svg}{options: alt='[Stable]'}}}{\strong{[Stable]}} @@ -35,6 +48,8 @@ testthat::expect_error(yell_if_missing("", "", "")) testthat::expect_error(yell_if_missing("", "", "", "")) testthat::expect_error(yell_if_missing("", "", "", "", "")) testthat::expect_error(yell_if_missing("", "", "", "", "", "")) +testthat::expect_error(yell_if_missing("", "", "", "", "", "", "")) +testthat::expect_error(yell_if_missing("", "", "", "", "", "", "", "")) } \seealso{ Other ru_settings: diff --git a/tests/testthat/test-attachment_list.R b/tests/testthat/test-attachment_list.R index 682f2fae..32ddb2de 100644 --- a/tests/testthat/test-attachment_list.R +++ b/tests/testthat/test-attachment_list.R @@ -11,7 +11,7 @@ test_that("attachment_list works", { pw = get_test_pw() ) - al <- get_one_submission_attachment_list( + al <- get_one_submission_att_list( sl$instance_id[[1]], pid = get_test_pid(), fid = get_test_fid(), diff --git a/tests/testthat/test-entity_create.R b/tests/testthat/test-entity_create.R new file mode 100644 index 00000000..01bfdcaa --- /dev/null +++ b/tests/testthat/test-entity_create.R @@ -0,0 +1,142 @@ +test_that("entity_create creates single entities", { + ru_setup( + pid = get_test_pid(), + url = get_test_url(), + un = get_test_un(), + pw = get_test_pw(), + odkc_version = get_test_odkc_version() + ) + + el <- entitylist_list() + + # Entity List name (dataset ID, did) + did <- el$name[1] + + # All Entities of Entity List + en <- entity_list(did = did) + # ed <- entity_detail(did=did, eid = en$uuid[1]) + + time_before_create <- lubridate::ymd_hms(Sys.time(), tz = "Australia/Perth") + + # Create a single entity + lab <- glue::glue("Entity {nrow(en) + 1} created by ruODK package test on {Sys.time()}") + ec <- entity_create( + did = did, + label = lab, + notes = lab, + data = list( + "status" = "needs_followup", + "details" = "ruODK package test", + "geometry" = "-33.2 115.0 0.0 0.0" + ) + ) + + ec_names <- c( + "uuid", + "creator_id", + "conflict", + "created_at", + "updated_at", + "deleted_at", + "current_version" + ) + testthat::expect_equal(names(ec), ec_names) + + time_created <- lubridate::ymd_hms(ec$created_at, tz = "UTC") + + # For some reason, the server time seems to be in the past, so this test + # does not always find that the entity was created AFTER the timestamp we + # created immediately BEFORE creating the entity. + # testthat::expect_gte(time_created, time_before_create) + + # Test entity_delete + ec_deleted <- entity_delete(did = did, eid = ec$uuid) +}) + +test_that("entity_create creates multiple entities", { + ru_setup( + pid = get_test_pid(), + url = get_test_url(), + un = get_test_un(), + pw = get_test_pw(), + odkc_version = get_test_odkc_version() + ) + + el <- entitylist_list() + + # Entity List name (dataset ID, did) + did <- el$name[1] + + # All Entities of Entity List + en <- entity_list(did = did) + + label <- c( + glue::glue( + "Entity {nrow(en) + 1} created by ruODK package test on {Sys.time()}" + ), + glue::glue( + "Entity {nrow(en) + 2} created by ruODK package test on {Sys.time()}" + ) + ) + notes <- glue::glue("Two entities created by ruODK package test on {Sys.time()}") + status <- c("needs_followup", "needs_followup") + details <- c("ruODK package test", "ruODK package test") + geometry <- c("-33.2 115.0 0.0 0.0", "-33.2 115.0 0.0 0.0") + data <- data.frame(status, details, geometry, stringsAsFactors = FALSE) + request_data <- list( + "entities" = data.frame(label, data = I(data), stringsAsFactors = FALSE), + "source" = list("name" = "file.csv", "size" = 100) + ) + # jsonlite::toJSON(request_data, pretty = TRUE, auto_unbox = TRUE) + + ec <- entity_create( + did = did, + notes = notes, + data = request_data + ) + + testthat::expect_equal(names(ec), "success") + testthat::expect_true(ec$success) +}) + +test_that("entity_create input gatechecks", { + ru_setup( + pid = get_test_pid(), + url = get_test_url(), + un = get_test_un(), + pw = get_test_pw(), + odkc_version = get_test_odkc_version() + ) + + el <- entitylist_list() + + # Entity List name (dataset ID, did) + did <- el$name[1] + + # All Entities of Entity List + en <- entity_list(did = did) + # ed <- entity_detail(did=did, eid = en$uuid[1]) + + time_before_created <- Sys.time() + + # Create a single entity + lab <- glue::glue("Entity {nrow(en) + 1} created by ruODK package test on {Sys.time()}") + + # ODKC version not supported + testthat::expect_warning(entity_create( + did = did, + label = lab, + data = list( + "status" = "needs_followup", + "details" = "ruODK package test", + "geometry" = "-33.2 115.0 0.0 0.0" + ), + odkc_version = "1.5.3" + )) + + # Missing dataset ID + testthat::expect_error(entity_create()) + + # Empty label, malformed data missing "entities" + testthat::expect_error(entity_create(did = did, data = list())) +}) diff --git a/tests/testthat/test-entity_detail.R b/tests/testthat/test-entity_detail.R new file mode 100644 index 00000000..4439e37c --- /dev/null +++ b/tests/testthat/test-entity_detail.R @@ -0,0 +1,87 @@ +test_that("entity_detail works", { + ru_setup( + pid = get_test_pid(), + url = get_test_url(), + un = get_test_un(), + pw = get_test_pw(), + odkc_version = get_test_odkc_version() + ) + + el <- entitylist_list() + + # Entity List name (dataset ID) + did <- el$name[1] + + # All Entities of Entity List + en <- entity_list(did = el$name[1]) + + # Entity detail + ed <- entity_detail(did = el$name[1], eid = en$uuid[1]) + + cn <- c( + "uuid", + "creator_id", + "conflict", + "created_at", + "updated_at", + "deleted_at", + "current_version" + # not unfolded: + # "current_version_created_at", + # "current_version_current", + # "current_version_label", + # "current_version_creator_id", + # "current_version_user_agent", + # "current_version_version", + # "current_version_base_version", + # "current_version_conflicting_properties" + ) + testthat::expect_equal(cn, names(ed)) + + # The UUID of the first Entity + eid <- en$uuid[1] + testthat::expect_is(eid, "character") + + # The current version of the first Entity + ev <- en$current_version_version[1] + testthat::expect_is(ev, "integer") +}) + +test_that("entity_detail errors if did is missing", { + testthat::expect_error( + entity_detail() + ) +}) + +test_that("entity_detail warns if odkc_version too low", { + skip_if(Sys.getenv("ODKC_TEST_URL") == "", + message = "Test server not configured" + ) + + ru_setup( + pid = get_test_pid(), + url = get_test_url(), + un = get_test_un(), + pw = get_test_pw(), + odkc_version = get_test_odkc_version() + ) + + el <- entitylist_list() + + # Entity List name (dataset ID) + did <- el$name[1] + + # All Entities of Entity List + en <- entity_list(did = el$name[1]) + + # Entity detail + ed <- entity_detail(did = el$name[1], eid = en$uuid[1]) + + # Expect error with missing eid + testthat::expect_error( + entity_detail(did = el$name[1]) + ) +}) + + +# usethis::use_r("entity_detail") # nolint diff --git a/tests/testthat/test-entity_list.R b/tests/testthat/test-entity_list.R new file mode 100644 index 00000000..f4f94dff --- /dev/null +++ b/tests/testthat/test-entity_list.R @@ -0,0 +1,43 @@ +test_that("entity_list works", { + skip_if(Sys.getenv("ODKC_TEST_URL") == "", + message = "Test server not configured" + ) + + ru_setup( + pid = get_test_pid(), + url = get_test_url(), + un = get_test_un(), + pw = get_test_pw(), + odkc_version = get_test_odkc_version() + ) + + el <- entitylist_list() + did <- el$name[1] + en <- entity_list(did = el$name[1]) + eid <- en$uuid[1] + + testthat::expect_s3_class(en, "tbl_df") + + cn <- c( + "uuid", + "creator_id", + "created_at", + "updated_at", + "deleted_at", + "current_version_current", + "current_version_label", + "current_version_creator_id", + "current_version_user_agent", + "current_version_version", + "current_version_base_version", + "current_version_conflicting_properties", + "current_version_created_at", + "current_version_branch_id", + "current_version_trunk_version", + "current_version_branch_base_version" + ) + + testthat::expect_equal(names(en), cn) +}) + +# usethis::use_r("entity_list") # nolint diff --git a/tests/testthat/test-entity_update.R b/tests/testthat/test-entity_update.R new file mode 100644 index 00000000..f0faaa6c --- /dev/null +++ b/tests/testthat/test-entity_update.R @@ -0,0 +1,180 @@ +test_that("entity_update works", { + ru_setup( + pid = get_test_pid(), + url = get_test_url(), + un = get_test_un(), + pw = get_test_pw(), + odkc_version = get_test_odkc_version() + ) + + el <- entitylist_list() + + # Entity List name (dataset ID, did) + did <- el$name[1] + + # All Entities of Entity List + en <- entity_list(did = did) + + ed <- entity_detail(did = did, eid = en$uuid[1]) + + e_label <- ed$current_version$label + + + # Update the field "details". + details_0 <- ed$current_version$data$details + details_1 <- paste0(details_0, ". Updated on ", Sys.time()) + details_2 <- paste0(details_0, ". Updated on ", Sys.time()) + + testthat::expect_equal(ed$current_version$data$details, details_0) + + # Update the Entity (implicitly forced update) - update 1 + eu <- entity_update( + did = did, + eid = en$uuid[1], + label = e_label, + data = list(details = details_1) + ) + testthat::expect_equal(eu$current_version$data$details, details_1) + + testthat::expect_equal( + ed$current_version$version, + eu$current_version$baseVersion + ) + + # Interlude: entity_changes after update 1 + ec_1 <- entity_changes(did = did, eid = en$uuid[1]) + testthat::expect_gt(length(ec_1), 0) + + # Update the Entity (implicitly forced update) - update 2 + eu <- entity_update( + did = did, + eid = en$uuid[1], + label = e_label, + data = list(details = details_2) + ) + testthat::expect_equal(eu$current_version$data$details, details_2) + + # Interlude: entity_changes after update 2 + ec_2 <- entity_changes(did = did, eid = en$uuid[1]) + testthat::expect_gt(length(ec_2), length(ec_1)) + + # Test entity_versions without conflicts flag + ev <- entity_versions(did = did, eid = en$uuid[1]) + + ev_names <- c( + "conflict", + "resolved", + "base_diff", + "server_diff", + "last_good_version", + "current", + "label", + "creator_id", + "user_agent", + "data", + "version", + "base_version", + "data_received", + "conflicting_properties", + "created_at", + "branch_id", + "trunk_version", + "branch_base_version", + "creator", + "source", + "relevant_to_conflict" + ) + + testthat::expect_equal(names(ev), ev_names) + + # With conflicts flag + # Prove that the entity has no conflicts + ed <- entity_detail(did = did, eid = en$uuid[1]) + testthat::expect_equal(ed$current_version$conflictingProperties, NULL) + + # Expect no records for entity without conflicts + ev_conflict <- entity_versions(did = did, eid = en$uuid[1], conflict = TRUE) + testthat::expect_equal(nrow(ev_conflict), 0) + + # Interlude 2: server audits + ea <- entity_audits(did = did, eid = en$uuid[1]) + testthat::expect_s3_class(ea, "tbl_df") + + ea_names <- c( + "actor_id", + "action", + "actee_id", + "details_source", + "details_entity", + "details_entity_id", + "details_entity_def_id", + "logged_at", + "actor_id_2", + "actor_type", + "actor_display_name", + "actor_created_at", + "actor_updated_at", + "actor_deleted_at" + ) + testthat::expect_equal(names(ea), ea_names) +}) + +test_that("entitylist_update warns if odkc_version too low", { + ru_setup( + pid = get_test_pid(), + url = get_test_url(), + un = get_test_un(), + pw = get_test_pw(), + odkc_version = get_test_odkc_version() + ) + + el <- entitylist_list() + + # Entity List name (dataset ID, did) + did <- el$name[1] + + # All Entities of Entity List + en <- entity_list(did = did) + + ed <- entity_detail(did = did, eid = en$uuid[1]) + + e_label <- ed$current_version$label + + # Update the field "details". + old_details <- ed$current_version$data$details + new_details <- paste0(old_details, ". Updated on ", Sys.time()) + + e_data <- list(details = new_details) + + testthat::expect_error(entity_update()) + + testthat::expect_warning( + entity_update( + did = did, + eid = en$uuid[1], + label = e_label, + data = e_data, + odkc_version = "1.5.3" + ) + ) + + testthat::expect_error( + entity_update( + did = did, + eid = "", + label = e_label, + data = e_data + ) + ) + + testthat::expect_error( + entity_update( + did = "", + eid = en$uuid[1], + label = e_label, + data = e_data + ) + ) +}) + +# usethis::use_r("entity_update") # nolint diff --git a/tests/testthat/test-entitylist_detail.R b/tests/testthat/test-entitylist_detail.R index b2f502ef..c981beaa 100644 --- a/tests/testthat/test-entitylist_detail.R +++ b/tests/testthat/test-entitylist_detail.R @@ -15,14 +15,14 @@ test_that("entitylist_detail works", { # entitylist_detail returns a list testthat::expect_is(ds1, "list") - # linkedForms contain form xmlFormId and name - lf <- ds1$linkedForms |> + # linked_forms contain form xmlFormId and name + lf <- ds1$linked_forms |> purrr::list_transpose() |> tibble::as_tibble() testthat::expect_equal(names(lf), c("xmlFormId", "name")) - # sourceForms contain form xmlFormId and name - sf <- ds1$sourceForms |> + # source_forms contain form xmlFormId and name + sf <- ds1$source_forms |> purrr::list_transpose() |> tibble::as_tibble() testthat::expect_equal(names(sf), c("xmlFormId", "name")) diff --git a/tests/testthat/test-entitylist_download.R b/tests/testthat/test-entitylist_download.R index f0281f91..9c5613f4 100644 --- a/tests/testthat/test-entitylist_download.R +++ b/tests/testthat/test-entitylist_download.R @@ -122,7 +122,7 @@ test_that("entitylist_download filter works", { newest_entity_date <- as.Date(max(ds1$entities$`__createdAt`)) # Should return all entities (created before or on date of latest entity) - # Currently returns HTTP 501 not implemented + # Currently returns HTTP 501 # ds2 <- entitylist_download( # did = ds$name[1], # filter=glue::glue("__createdAt le {newest_entity_date}") @@ -130,6 +130,9 @@ test_that("entitylist_download filter works", { # testthat::expect_equal(ds2$http_status, 200) # testthat::expect_true(nrow(ds2$entities)) + + # Resolve warning about empty test + testthat::expect_true(TRUE) }) test_that("entitylist_download errors if did is missing", { diff --git a/tests/testthat/test-entitylist_update.R b/tests/testthat/test-entitylist_update.R index ac59c2db..c8368277 100644 --- a/tests/testthat/test-entitylist_update.R +++ b/tests/testthat/test-entitylist_update.R @@ -13,14 +13,14 @@ test_that("entitylist_update works", { did <- ds$name[1] - # Update dataset with opposite approvalRequired - ds2 <- entitylist_update(did = did, approval_required = !ds1$approvalRequired) - testthat::expect_false(ds1$approvalRequired == ds2$approvalRequired) - - # Update dataset with opposite approvalRequired again - ds3 <- entitylist_update(did = did, approval_required = !ds2$approvalRequired) - testthat::expect_false(ds2$approvalRequired == ds3$approvalRequired) - testthat::expect_true(ds1$approvalRequired == ds3$approvalRequired) + # Update dataset with opposite approval_required + ds2 <- entitylist_update(did = did, approval_required = !ds1$approval_required) + testthat::expect_false(ds1$approval_required == ds2$approval_required) + + # Update dataset with opposite approval_required again + ds3 <- entitylist_update(did = did, approval_required = !ds2$approval_required) + testthat::expect_false(ds2$approval_required == ds3$approval_required) + testthat::expect_true(ds1$approval_required == ds3$approval_required) }) test_that("entitylist_update errors if did is missing", { diff --git a/tests/testthat/test-form_list.R b/tests/testthat/test-form_list.R index 409e90ef..6bc2ea3d 100644 --- a/tests/testthat/test-form_list.R +++ b/tests/testthat/test-form_list.R @@ -16,7 +16,7 @@ test_that("form_list works", { "sha", "sha256", "draft_token", "published_at", "name", "submissions", "entity_related", "review_states_received", "review_states_has_issues", "review_states_edited", "last_submission", - "excel_content_type", "created_by_id", "created_by_type", + "excel_content_type", "public_links", "created_by_id", "created_by_type", "created_by_display_name", "created_by_created_at", "created_by_updated_at", "created_by_deleted_at", "fid" ) diff --git a/tests/testthat/test-ru_setup.R b/tests/testthat/test-ru_setup.R index b385cf21..edad4ef8 100644 --- a/tests/testthat/test-ru_setup.R +++ b/tests/testthat/test-ru_setup.R @@ -284,6 +284,17 @@ test_that("yell_if_missing yells loudly", { testthat::expect_error( yell_if_missing("x", "x", "x", pid = "x", fid = "x", iid = "") ) + testthat::expect_error( + yell_if_missing("x", "x", "x", pid = "x", fid = "x", iid = "x", did = "") + ) + testthat::expect_error( + yell_if_missing( + "x", "x", "x", + pid = "x", fid = "x", iid = "x", did = "x", eid = "" + ) + ) + testthat::expect_error(yell_if_missing("", "", "", "", "", "", "")) + testthat::expect_error(yell_if_missing("", "", "", "", "", "", "", "")) }) test_that("odata_svc_parse works", { diff --git a/vignettes/comparison.Rmd b/vignettes/comparison.Rmd index 6e7409a0..4c50a943 100644 --- a/vignettes/comparison.Rmd +++ b/vignettes/comparison.Rmd @@ -2,7 +2,7 @@ title: "Comparison of related software packages" description: > An overview of related software packages in the ODK / OData space. -output: +output: rmarkdown::html_vignette: self_contained: false vignette: > @@ -20,65 +20,65 @@ There are several other R packages interacting with the ODK ecosystem, and/or ## Comparison of ODK related software packages (non-ODK core) -| Package | [`ruODK`](https://docs.ropensci.org/ruODK/) | [`odkr`](https://rapidsurveys.io/odkr/) | [`odk`](https://cran.r-project.org/package=odk) | [`odkmeta`](https://github.com/nap2000/odkmeta) | [`koboloadeR`](https://unhcr.github.io/koboloadeR/docs/index.html) | [Pentaho Kettle tutorial](https://github.com/schemetrica/automating-data-delivery-odk-central) +| Package | [`ruODK`](https://docs.ropensci.org/ruODK/) | [`odkr`](https://github.com/rapidsurveys/odkr) | [`odk`](https://cran.r-project.org/package=odk) | [`odkmeta`](https://github.com/nap2000/odkmeta) | [`koboloadeR`](https://unhcr.github.io/koboloadeR/docs/index.html) | [Pentaho Kettle tutorial](https://github.com/schemetrica/automating-data-delivery-odk-central) |------------------------------|---------------|---------------|---------------|---------------|---------------|---------------| | Elevator pitch | "[ckanr](https://github.com/ropensci/ckanr) for ODK Central" | "Drive ODK Briefcase through R" | "Export ODK Aggregate to SPSS" | "Export ODK Aggregate to STATA" | "Metapackage for the extended ODK ecosystem" | "What ruODK does, but as GUI" | | Last commit | [![Last-changedate](https://img.shields.io/github/last-commit/ropensci/ruODK.svg)](https://github.com/ropensci/ruODK/commits/main) | [![Last-changedate](https://img.shields.io/github/last-commit/rapidsurveys/odkr.svg)](https://github.com/rapidsurveys/odkr/commits/master) | Nov 2017 | [![Last-changedate](https://img.shields.io/github/last-commit/nap2000/odkmeta.svg)](https://github.com/nap2000/odkmeta/commits/master) | [![Last-changedate](https://img.shields.io/github/last-commit/unhcr/koboloadeR.svg)](https://github.com/unhcr/koboloadeR/commits/master) | [![Last-changedate](https://img.shields.io/github/last-commit/schemetrica/automating-data-delivery-odk-central.svg)](https://github.com/schemetrica/automating-data-delivery-odk-central/commits/master) | | Website | [![](https://img.shields.io/static/v1?label=code&message=GitHub&color=brightgreen)](https://github.com/ropensci/ruODK) [![docs](https://img.shields.io/static/v1?label=docs&message=pkgdown&color=brightgreen)](https://docs.ropensci.org/ruODK/) | [![](https://img.shields.io/static/v1?label=code&message=GitHub&color=brightgreen)](https://github.com/rapidsurveys/odkr) [![docs](https://img.shields.io/static/v1?label=docs&message=pkgdown&color=brightgreen)](https://rapidsurveys.io/odkr/) | [![docs](https://img.shields.io/static/v1?label=docs&message=rdrr.io&color=brightgreen)](https://rdrr.io/cran/odk/) | [![](https://img.shields.io/static/v1?label=code&message=GitHub&color=brightgreen)](https://github.com/nap2000/odkmeta) | [![](https://img.shields.io/static/v1?label=code&message=GitHub&color=brightgreen)](https://github.com/unhcr/koboloadeR) [![docs](https://img.shields.io/static/v1?label=docs&message=pkgdown&color=brightgreen)](https://unhcr.github.io/koboloadeR/docs/index.html) | [![](https://img.shields.io/static/v1?label=code&message=GitHub&color=brightgreen)](https://github.com/schemetrica/automating-data-delivery-odk-central) | -| Test coverage | ![tic](https://github.com/ropensci/ruODK/workflows/tic/badge.svg) [![codecov](https://codecov.io/gh/ropensci/ruODK/branch/main/graph/badge.svg)](https://codecov.io/gh/ropensci/ruODK) [![Build status](https://ci.appveyor.com/api/projects/status/1cs19xx0t64bmd2q/branch/main?svg=true)](https://ci.appveyor.com/project/florianm/ruodk/branch/main) | [![codecov](https://codecov.io/gh/rapidsurveys/odkr/branch/master/graph/badge.svg)](https://codecov.io/gh/rapidsurveys/odkr) | ❌ | In repo | [![Travis build status](https://travis-ci.org/unhcr/koboloadeR.svg?branch=gh-pages)](https://travis-ci.org/unhcr/koboloadeR) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/unhcr/koboloadeR?branch=gh-pages&svg=true)](https://ci.appveyor.com/project/unhcr/koboloadeR) [![codecov](https://codecov.io/gh/unhcr/koboloadeR/branch/gh-pages/graph/badge.svg)](https://codecov.io/gh/unhcr/koboloadeR) | NA | +| Test coverage | ![tic](https://github.com/ropensci/ruODK/workflows/tic/badge.svg) [![codecov](https://codecov.io/gh/ropensci/ruODK/branch/main/graph/badge.svg)](https://app.codecov.io/gh/ropensci/ruODK) [![Build status](https://ci.appveyor.com/api/projects/status/1cs19xx0t64bmd2q/branch/main?svg=true)](https://ci.appveyor.com/project/florianm/ruodk/branch/main) | [![codecov](https://codecov.io/gh/rapidsurveys/odkr/branch/master/graph/badge.svg)](https://app.codecov.io/gh/rapidsurveys/odkr) | ❌ | In repo | [![Travis build status](https://travis-ci.org/unhcr/koboloadeR.svg?branch=gh-pages)](https://travis-ci.org/unhcr/koboloadeR) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/unhcr/koboloadeR?branch=gh-pages&svg=true)](https://ci.appveyor.com/project/unhcr/koboloadeR) [![codecov](https://codecov.io/gh/unhcr/koboloadeR/branch/gh-pages/graph/badge.svg)](https://app.codecov.io/gh/unhcr/koboloadeR) | NA | | Working examples | README, 3 vignettes, pkgdown, Rmd templates | README, pkgdown | CRAN PDF | README | README, 9 vignettes, shiny apps, pkgdown | Tutorial with screenshots | | Available on CRAN | [![CRAN status](https://www.r-pkg.org/badges/version/ruODK)](https://cran.r-project.org/package=ruODK) | [![CRAN status](https://www.r-pkg.org/badges/version/odkr)](https://cran.r-project.org/package=odkr) | [![version](http://www.r-pkg.org/badges/version/odk)](https://CRAN.R-project.org/package=odk) | NA | NA | NA | | Technologies | Tidyverse R, XForms | Base R | Base R | Stata | R metapackage, XlsForms | Pentaho Kettle GUI | -| External dependencies | None | Java, ODK Briefcase | SPSS | Stata | Java, ODK Briefcase, wraps `odkr` | [Pentaho Kettle](http://www.ibridge.be/), Java | +| External dependencies | None | Java, ODK Briefcase | SPSS | Stata | Java, ODK Briefcase, wraps `odkr` | [Pentaho Kettle](https://github.com/pentaho/pentaho-kettle), Java | | Affiliation | [ROpenSci](https://ropensci.org/) | [RapidSurveys](https://rapidsurveys.io/) | [Muntashir-Al-Arefin](https://stackoverflow.com/users/8875690/muntashir-al-arefin) | [ODK Central developer Matt White](https://github.com/matthew-white) | [UNHCR](https://github.com/unhcr) | [Schemetrica](https://github.com/schemetrica) | | Covers ODK Central OData API | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | | Covers ODK Central REST API | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | -| Covers ODK Central bulk export | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | -| Covers ODK Central OpenRosa API | ❌ no need, gets all data through OData/REST API | ✅ via ODK Briefcase | ❌ | ❌ | ✅ via ODK Briefcase | ✅ | -| Data post-processing | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | -| Data visualisation examples | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | - +| Covers ODK Central bulk export | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | +| Covers ODK Central OpenRosa API | ❌ no need, gets all data through OData/REST API | ✅ via ODK Briefcase | ❌ | ❌ | ✅ via ODK Briefcase | ✅ | +| Data post-processing | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | +| Data visualisation examples | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | + In summary: `ruODK` provides a dependency-free interface to ODK Central. `koboloadeR` is a metapackage containing lots of ancillary packages, with some heavy dependencies on Java and ODK Briefcase (which in turn can access ODK Central). -Although built around the XlsForm standard and paradigm, `koboloadeR` is well worth +Although built around the XlsForm standard and paradigm, `koboloadeR` is well worth exploring as a larger context to data wrangling in the ODK ecosystem. Schemetrica's tutorial illustrates data ETL from ODK Central and deserves a special mention, as it is both very recent and aimed specifically against ODK Central. The GUI paradigm of Pentaho Kettle addresses a different audience to the scripting -paradigm of `ruODK`. It should be mentioned that Kettle's composable data +paradigm of `ruODK`. It should be mentioned that Kettle's composable data manipulation steps can be used for many other use cases apart from ODK Central. ## Comparison of OData related R packages | Package | [`ruODK`](https://docs.ropensci.org/ruODK/) | [`odataR`](https://github.com/HanOostdijk/odataR) | [`cbsodataR`](https://github.com/edwindj/cbsodataR) | [`OData`](https://cran.r-project.org/package=OData) | [OData JDBC R tutorial](https://www.cdata.com/kb/tech/odata-jdbc-r.rst) |------------------------------|---------------|---------------|---------------|---------------|--------------| -| Elevator pitch | "[ckanr](https://github.com/ropensci/ckanr) for ODK Central" | "OData client for https://opendata.cbs.nl (and similar)" | "OData client for https://www.cbs.nl" | "Minimal OData example" | "Minimal RJDBC example" | +| Elevator pitch | "[ckanr](https://github.com/ropensci/ckanr) for ODK Central" | "OData client for https://opendata.cbs.nl (and similar)" | "OData client for https://www.cbs.nl" | "Minimal OData example" | "Minimal RJDBC example" | | Last commit | [![Last-changedate](https://img.shields.io/github/last-commit/ropensci/ruODK.svg)](https://github.com/ropensci/ruODK/commits/main) | [![Last-changedate](https://img.shields.io/github/last-commit/HanOostdijk/odataR.svg)](https://github.com/HanOostdijk/odataR/commits/master) | [![Last-changedate](https://img.shields.io/github/last-commit/edwindj/cbsodataR.svg)](https://github.com/edwindj/cbsodataR/commits/master) | Dec 2016 | ❓ | | Website | [![](https://img.shields.io/static/v1?label=code&message=GitHub&color=brightgreen)](https://github.com/ropensci/ruODK) [![docs](https://img.shields.io/static/v1?label=docs&message=pkgdown&color=brightgreen)](https://docs.ropensci.org/ruODK/) | [![](https://img.shields.io/static/v1?label=code&message=GitHub&color=brightgreen)](https://github.com/HanOostdijk/odataR) | [![docs](https://img.shields.io/static/v1?label=code&message=CRAN&color=green)](https://cran.r-project.org/package=cbsodataR) [![](https://img.shields.io/static/v1?label=code&message=GitHub&color=brightgreen)](https://github.com/edwindj/cbsodataR) [![docs](https://img.shields.io/static/v1?label=docs&message=pkgdown&color=brightgreen)](https://edwindj.github.io/cbsodataR/) | [![](https://img.shields.io/static/v1?label=code&message=CRAN&color=green)](https://cran.r-project.org/package=OData) | [![docs](https://img.shields.io/static/v1?label=docs&message=html&color=brightgreen)](https://www.cdata.com/kb/tech/odata-jdbc-r.rst) | -| Test coverage | ![tic](https://github.com/ropensci/ruODK/workflows/tic/badge.svg) [![Build status](https://ci.appveyor.com/api/projects/status/1cs19xx0t64bmd2q/branch/main?svg=true)](https://ci.appveyor.com/project/florianm/ruodk/branch/main) [![codecov](https://codecov.io/gh/ropensci/ruODK/branch/main/graph/badge.svg)](https://codecov.io/gh/ropensci/ruODK) | ❌ | [![Travis-CI Build Status](https://travis-ci.org/edwindj/cbsodataR.png?branch=master)](https://travis-ci.org/edwindj/cbsodataR) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/edwindj/cbsodatar?branch=master)](https://ci.appveyor.com/project/edwindj/cbsodatar) | ❌ | ❌ | +| Test coverage | ![tic](https://github.com/ropensci/ruODK/workflows/tic/badge.svg) [![Build status](https://ci.appveyor.com/api/projects/status/1cs19xx0t64bmd2q/branch/main?svg=true)](https://ci.appveyor.com/project/florianm/ruodk/branch/main) [![codecov](https://codecov.io/gh/ropensci/ruODK/branch/main/graph/badge.svg)](https://codecov.io/gh/ropensci/ruODK) | ❌ | [![Travis-CI Build Status](https://travis-ci.org/edwindj/cbsodataR.png?branch=master)](hhttps://app.travis-ci.com/edwindj/cbsodataR) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/edwindj/cbsodatar?branch=master)](https://ci.appveyor.com/project/edwindj/cbsodatar) | ❌ | ❌ | | Targets ODK Central | ✅ | ❌ | ❌ | ❌ | ❌ | | Works with ODK Central | ✅ | ❓ | ❓ | ❌ | ❌ | | Data wrangling helpers for post-processing | ✅ | some | some | ❌ | ❌ | | Actively maintained to work against ODK Central | ✅ | ❌ | ❌ | ❌ | ❌ | | Technologies | R, httr, xml2, tidyr, purrr | R, jsonlite, tidyverse | R, tidyverse | R, XML, RJSONIO | R, RJDBC, Java | -| External dependencies | ✅ None | ✅ None | ✅ None | ✅ None | ❌ JDBC, Java | +| External dependencies | ✅ None | ✅ None | ✅ None | ✅ None | ❌ JDBC, Java | | Available on CRAN | [![CRAN status](https://www.r-pkg.org/badges/version/ruODK)](https://cran.r-project.org/package=ruODK) | NA | [![version](http://www.r-pkg.org/badges/version/cbsodataR)](https://CRAN.R-project.org/package=cbsodataR) | [![version](http://www.r-pkg.org/badges/version/OData)](https://CRAN.R-project.org/package=OData) | NA | -In summary: +In summary: -`ruODK` is the only R package explicitly aimed at ODK Central's OData and -RESTful API, as well as providing context and helpers around specific recurring +`ruODK` is the only R package explicitly aimed at ODK Central's OData and +RESTful API, as well as providing context and helpers around specific recurring data wrangling tasks. -The value of OData lies in its self-descriptive nature, which allows tools to +The value of OData lies in its self-descriptive nature, which allows tools to introspect the data structures and types. Both GUI-driven tools like MS PowerBI and `ruODK` use this introspection to assist users in wrangling their own data. -The script-based approach of `ruODK` allows to automate the data extraction, -transformation, and reporting pipeline, and therefore provide reproducible +The script-based approach of `ruODK` allows to automate the data extraction, +transformation, and reporting pipeline, and therefore provide reproducible reporting. diff --git a/vignettes/odata-api.Rmd b/vignettes/odata-api.Rmd index 7a87177b..33079a0f 100644 --- a/vignettes/odata-api.Rmd +++ b/vignettes/odata-api.Rmd @@ -2,7 +2,7 @@ title: "Accessing the OData API" description: > Accessing submission data via the OData pathway. -output: +output: rmarkdown::html_vignette: self_contained: false vignette: > @@ -23,7 +23,7 @@ knitr::opts_chunk$set(collapse = TRUE, comment = "#>") ```{r pkgs} # This vignette requires the following packages: -library(DT) +# library(DT) library(leaflet) # library(listviewer) library(magrittr) @@ -54,7 +54,7 @@ as documented (with live examples in multiple programming languages) at the # Configure ruODK The OData service URL is shown in the form's "Submissions" tab > -"Analyse via OData" on ODK Central. It contains base URL, project ID, and +"Analyse via OData" on ODK Central. It contains base URL, project ID, and form ID and is used by `ruODK::ru_setup()`. ```{r ru_setup_demo, eval=FALSE} @@ -73,7 +73,7 @@ ruODK::ru_setup( loc <- fs::path("media") ``` -This vignette shows how to access data, but under the bonnet uses the +This vignette shows how to access data, but under the bonnet uses the included package data. This allows to rebuild the vignette offline and without credentials to the originating ODK Central server. @@ -111,17 +111,17 @@ fq_svc %>% knitr::kable(.) ``` `ruODK` provides the names and urls of the service endpoints as `tibble`. -We see the main data available under the url `Submissions`, and repeating -form groups called `taxon_encounter` and `vegetation_stratum` in the ODK form under -the url `Submissions.taxon_encounter` and `Submissions.vegetation_stratum`, +We see the main data available under the url `Submissions`, and repeating +form groups called `taxon_encounter` and `vegetation_stratum` in the ODK form under +the url `Submissions.taxon_encounter` and `Submissions.vegetation_stratum`, respectively. -The main value we get out of the service document are these names of the +The main value we get out of the service document are these names of the form groups, which can differ between forms. ## OData metadata document Let's inspect the form metadata to review our data schema. -While we can download the submission data without it, the metadata document +While we can download the submission data without it, the metadata document contains information about field data types and attachment names. ```{r load_metadata, eval=FALSE} @@ -138,7 +138,7 @@ if (requireNamespace("listviewer")) { As an alternative to the OData metadata document, ODK Central also offers form metadata as a much cleaner JSON document, which `ruODK` can read and parse -into a clean `tibble` of field type, name, and path. +into a clean `tibble` of field type, name, and path. `ruODK` uses this introspection to parse submission data. @@ -162,9 +162,9 @@ With `wkt=TRUE`, we'll receive spatial types as Well Known Text, which `ruODK` p as plain text. With `wkt=FALSE` (the default), we'll receive spatial types as GeoJSON, which `ruODK` parses into a nested list. -`ruODK` retains the original spatial field, and annotates the data with +`ruODK` retains the original spatial field, and annotates the data with extracted longitude, latitude, altitude, and (where given) accuracy. -These additional fields are prefixed with the original field name to prevent +These additional fields are prefixed with the original field name to prevent name collisions between possibly multiple location fields. ```{r load_odata, eval=FALSE} @@ -191,42 +191,42 @@ fq_data_taxa <- ruODK::odata_submission_get( -The function `ruODK::odata_submission_get()` received the original XML response +The function `ruODK::odata_submission_get()` received the original XML response as a nested list of lists. -To analyse and visualise the data, this nested list of lists must be transformed +To analyse and visualise the data, this nested list of lists must be transformed into a rectangular shape. -The function `ruODK::odata_submission_rectangle()` is used internally to +The function `ruODK::odata_submission_rectangle()` is used internally to recursively un-nest list columns using `tidyr::unnest_wider()`. -Unnamed columns, notably the anonymous lat/lon/alt coordinates, are named -automatically to become unique (a feature of `tidyr::unnest_*()`), and then +Unnamed columns, notably the anonymous lat/lon/alt coordinates, are named +automatically to become unique (a feature of `tidyr::unnest_*()`), and then sanitised using the helper `janitor::clean_names()`. -By default, form group names are used as prefix to the field names. +By default, form group names are used as prefix to the field names. This behaviour can be disabled by handing the argument `names_sep=NULL` to -`tidyr::unnest_wider()` through running +`tidyr::unnest_wider()` through running `ruODK::odata_submission_get() %>% ruODK::odata_submission_rectangle(names_sep = NULL)`. -The vectorised function `ruODK::attachment_get()` is then used internally -to download and link attachments like photos and other media to a local, -relative path. This will take some time during the first run. +The vectorised function `ruODK::attachment_get()` is then used internally +to download and link attachments like photos and other media to a local, +relative path. This will take some time during the first run. Once the files exist locally, the download will be skipped. When used through `ruODK::odata_submission_get()`, `ruODK` will introspect the form schema to detect and then parse media attachment fields automatically. -When used manually, field names of media attachment fields can be (partially or +When used manually, field names of media attachment fields can be (partially or fully) specified, see `??ruODK::attachment_get()`. The date formats are parsed from ISO8601 timestamps into POSIXct objects with `ruODK::handle_ru_datetimes()`. We use our local timezone (GMT+08) in this example. -`ruODK` introspects the form schema to detect and then parse date and datetime +`ruODK` introspects the form schema to detect and then parse date and datetime fields automatically. The repeated subgroup `taxon_encounter` is left joined to the main submission data -to receive a (repeated) copy of the main submission data -(such as location, time and habitat description). +to receive a (repeated) copy of the main submission data +(such as location, time and habitat description). We will do the same to the other repeated subgroup `vegetation_stratum`. -For clarity, we enable verbose messages from `ruODK::odata_submission_get()` +For clarity, we enable verbose messages from `ruODK::odata_submission_get()` and preserve the message output in the code chunk options with `message=TRUE`. In real-world use cases, messages can be disabled through the chunk option `message=FALSE`. @@ -235,17 +235,17 @@ We use a custom local path for attachments (`loc`). This results in a smaller installed package size for `ruODK`, as it shares the attachment files with the other vignettes. The default is a local folder `media`. -The raw and unparsed example data is provided as data objects -`fq_raw` (main submissions of form Flora Quadrat 0.4), +The raw and unparsed example data is provided as data objects +`fq_raw` (main submissions of form Flora Quadrat 0.4), `fq_raw_taxa` (repeated group "Taxon Encounter" within a Flora Quadrat), and `fq_raw_strata` (repeated group "Vegetation Stratum" within a Flora Quadrat). -The parsed versions are included as data objects `fq_data`, `fq_data_strata`, +The parsed versions are included as data objects `fq_data`, `fq_data_strata`, and `fq_data_taxa`. To enable users without ODK Central credentials to build this vignette (e.g. on package installation with `build_vignettes=TRUE`), we show the real functions (such as `ruODK::odata_submission_get()`), but do not -evaluate them. Instead, we use "canned data". The `ruODK` test suite ensures -that canned data are equivalent to live data. +evaluate them. Instead, we use "canned data". The `ruODK` test suite ensures +that canned data are equivalent to live data. The result of this code chunk should be exactly the same as the compact version with `odata_submission_get(parse=TRUE)`. @@ -306,7 +306,7 @@ fq_data_strata <- fq_strata %>% Note: A manually resized version of the original photos in this example live in the package source under [`articles/media` -](https://github.com/ropensci/ruODK/tree/main/vignettes/media). +](https://github.com/ropensci/ruODK/tree/main/vignettes/media). To minimise package size, they were resized with imagemagick: ```{sh, eval=FALSE} @@ -322,7 +322,7 @@ stepwise unnests each list column. This requires knowledge of the data structure, which can either be looked up from the metadata, or by inspecting the raw data, `fq_raw`. -The following command has been built by stepwise adding `tidyr::unnest_wider()` +The following command has been built by stepwise adding `tidyr::unnest_wider()` expressions to the pipe until all list columns were eliminated. The trailing `invisible()` allows us to toggle parts of the pipe by catching the @@ -355,7 +355,7 @@ This section provides some examples of standard data visualisations. ## Datatable The package `DT` provides an interactive (and searchable) datatable. -```{r vis_data} +```{r vis_data, eval = requireNamespace("DT")} DT::datatable(fq_data) DT::datatable(fq_data_taxa) DT::datatable(fq_data_strata) @@ -431,7 +431,7 @@ leaflet::leaflet(width = 800, height = 600) %>% ``` ## Summarising data -See Hadley Wickam's +See Hadley Wickam's [R for Data Science](https://r4ds.had.co.nz/exploratory-data-analysis.html) for more ideas on data exploration. @@ -486,7 +486,7 @@ fq_data_taxa %>% ``` # Export -The rectangled data can now be exported. e.g. to CSV. Note that all list columns +The rectangled data can now be exported. e.g. to CSV. Note that all list columns must be either unnested or dropped before exporting to CSV. ```{r export, eval=F} @@ -503,10 +503,10 @@ For your convenience, `ruODK` includes a template RMarkdown workbook with the essential steps of the above workflow and colour-by-numbers instructions, which can be used as a starting point for projects using data from ODK Central. -To create a new RMarkdown document from the `ruODK` template, +To create a new RMarkdown document from the `ruODK` template, run `rmarkdown::draft("test.Rmd", "odata", package="ruODK")`. -Users of RStudio can alternatively "Create a new RMarkdown document" +Users of RStudio can alternatively "Create a new RMarkdown document" "From template" and select `ruODK`'s template "ODK Central via OData". Make sure to install a fresh version of `ruODK` to get the latest and greatest diff --git a/vignettes/restful-api.Rmd b/vignettes/restful-api.Rmd index 20a20e54..f2880bed 100644 --- a/vignettes/restful-api.Rmd +++ b/vignettes/restful-api.Rmd @@ -2,7 +2,7 @@ title: "Accessing the RESTful API" description: > Accessing submission data via the REST pathway. -output: +output: rmarkdown::html_vignette: self_contained: false vignette: > @@ -16,42 +16,42 @@ knitr::opts_chunk$set(collapse = TRUE, comment = "#>") ``` # Scope -This vignette provides a guided walk-through of the "getting data out" +This vignette provides a guided walk-through of the "getting data out" functions of the RESTful API endpoints which list and view details. `ruODK` users would mix and match parts of the demonstrated workflows to build their own data pipelines, e.g.: -* to build a quick analysis from all data, freshly downloaded from a smaller +* to build a quick analysis from all data, freshly downloaded from a smaller project, or * to build an interactive ETL pipeline to selectively download only new submissions for further processing and upload into downstream data warehouses. - -A typical and more stream-lined workflow is provided in the RMarkdown template + +A typical and more stream-lined workflow is provided in the RMarkdown template "ODK Central via OData" which is supplied by `ruODK`. ## Three ways to happiness ODK Central offers no less than three different ways to access data: -* viewing ODK Central data in MS PowerBI, MS Excel, Tableau, or `ruODK` +* viewing ODK Central data in MS PowerBI, MS Excel, Tableau, or `ruODK` through the OData service endpoints, or -* downloading all submissions including attachments as one (possibly gigantic) +* downloading all submissions including attachments as one (possibly gigantic) zip archive either through the "Export Submissions" button in the ODK Central form submissions page or through `ruODK`, or * viewing ODK Central data through `ruODK`'s RESTful API functions. -While the `vignette("odata", package="ruODK")` -(online [here](https://docs.ropensci.org/ruODK/articles/odata-api.html)) +While the `vignette("odata", package="ruODK")` +(online [here](https://docs.ropensci.org/ruODK/articles/odata-api.html)) illustrates the first option, this vignette demonstrates the remaining two. -Not implemented (yet) are the "managing ODK Central" functions which create, -update, and delete projects, forms, users, roles, and permissions. -We haven't yet found a strong use case to automate those functions - +Not implemented (yet) are the "managing ODK Central" functions which create, +update, and delete projects, forms, users, roles, and permissions. +We haven't yet found a strong use case to automate those functions - ODK Central (driven by humans) does those jobs beautifully on an expected scale. # Setup ruODK -See [`vignette("Setup", package = "ruODK")`](https://docs.ropensci.org/ruODK/articles/setup.html) +See [`vignette("Setup", package = "ruODK")`](https://docs.ropensci.org/ruODK/articles/setup.html) for detailed options to configure `ruODK`. Here, we'll grab the OData service URL from the form used in this vignette, @@ -91,7 +91,7 @@ data("fq_attachments") # Projects List projects. We see the project ID, a name, the number of forms and app users, -dates of last form submissions plus project management timestamps (created, +dates of last form submissions plus project management timestamps (created, updated). The important bit here is the project ID. @@ -122,10 +122,10 @@ fq_project_detail %>% fq_project_detail$verbs[[1]] %>% unlist(.) ``` -Nothing apart from the verbs is new compared to the data returned by +Nothing apart from the verbs is new compared to the data returned by `ruODK::project_list`. -To learn more about the functionality behind the verbs, refer to the interactive +To learn more about the functionality behind the verbs, refer to the interactive [ODK Central API documentation](https://docs.getodk.org/central-api/). To retrieve data from ODK Central, the functions shown below will suffice. @@ -138,7 +138,7 @@ To download form submissions, we need to know project ID and form ID. There are several ways of retrieving the form ID: * Browsing forms in the ODK Central's project overviews, -* Stealing the form ID from the OData service endpoint URL as shown on +* Stealing the form ID from the OData service endpoint URL as shown on ODK Central's form submission page, * Listing form metadata for a given project ID with `ruODK::form_list()`. @@ -153,7 +153,7 @@ fq_form_list %>% knitr::kable(.) Further to the metadata shown here, a column `xml` contains the entire XForms definition (originally XML) as nested list. -If the original XML is needed rather than the R equivalent (nested list), +If the original XML is needed rather than the R equivalent (nested list), we can use `ruODK::form_xml` with parameter `parse=FALSE`: ```{r form_xml, eval=F} @@ -172,8 +172,8 @@ if (require(listviewer)) { The `form_schema` represents all form fields of the XForms definition. -See the -[ODK Central API docs](https://docs.getodk.org/central-api-form-management/#getting-form-schema-fields) +See the +[ODK Central API docs](https://docs.getodk.org/central-api-form-management/#getting-form-schema-fields) and the examples of `??ruODK::form_schema()` for more detail. @@ -273,9 +273,9 @@ head(fq_zip_taxa) ## List submissions for one form Not always is it appropriate to download all submissions and all attachments -at once. +at once. -If forms feed into downstream data warehouses, the typical ETL workflow is to +If forms feed into downstream data warehouses, the typical ETL workflow is to * List all submissions from ODK Central * Select the subset of new submissions to download, e.g. @@ -296,17 +296,17 @@ The list of submissions critically contains each submission's unique ID in `instance_id`. If the submissions shall be downloaded and uploaded into another data warehouse, the `instance_id` can be used to determine whether a record already exists in the downstream warehouse or not. -This workflow is preferable where the majority of submissions is already -imported into another downstream data warehouse, and we only want to add new -submissions, as in submissions which are not already imported into the data +This workflow is preferable where the majority of submissions is already +imported into another downstream data warehouse, and we only want to add new +submissions, as in submissions which are not already imported into the data warehouse. -Furthermore, the `instance_id`s can now be used to retrieve the actual +Furthermore, the `instance_id`s can now be used to retrieve the actual submissions. ## Get submission data -In order to import each submission, we need to retrieve the data by +In order to import each submission, we need to retrieve the data by `instance_id`. ```{r submission_data, eval=F} @@ -320,9 +320,9 @@ fq_submissions <- ruODK::submission_get(fq_submission_list$instance_id) ``` ## Parse submissions -The data in `sub` is one row of the bulk downloaded submissions in +The data in `sub` is one row of the bulk downloaded submissions in `data_quadrat`. -The data in `submissions` represents all (or let's pretend, the selected) +The data in `submissions` represents all (or let's pretend, the selected) submissions in `data_quadrat`. The field `xml` contains the actual submission data including repeating groups. @@ -330,7 +330,7 @@ The structure is different to the output of `ruODK::odata_submission_get`, therefore `ruODK::odata_submission_rectangle` does not work for those, as here we might have repeating groups included in a submission. -This structure could be used for upload into data warehouses accepting nested +This structure could be used for upload into data warehouses accepting nested data as e.g. JSON. ```{r view_submission_data, fig.width=7} @@ -343,11 +343,11 @@ if (requireNamespace("listviewer")) { # Outlook The approach shown here yields nested and stand-alone records, which is useful -if the subsequent use requires records in nested JSON or XML format. -Complex forms with repeating sub-groups will result in highly nested lists, +if the subsequent use requires records in nested JSON or XML format. +Complex forms with repeating sub-groups will result in highly nested lists, whose structure heavily depends on the completeness of the submissions. -The other approach shown in -[`vignette("odata-api", package="ruODK")`](https://docs.ropensci.org/ruODK/articles/odata-api.html) -yields rectangled data in several normalised tables, which is useful for +The other approach shown in +[`vignette("odata-api", package="ruODK")`](https://docs.ropensci.org/ruODK/articles/odata-api.html) +yields rectangled data in several normalised tables, which is useful for analysis and visualisation. diff --git a/vignettes/setup.Rmd b/vignettes/setup.Rmd index c3cbba07..207ae0d9 100644 --- a/vignettes/setup.Rmd +++ b/vignettes/setup.Rmd @@ -2,13 +2,13 @@ title: "Setup" description: > Provide sensitive credentials to ruODK through different pathways. -output: +output: rmarkdown::html_vignette: self_contained: false vignette: > %\VignetteIndexEntry{Setup} %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} + %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} @@ -92,7 +92,7 @@ sourced at the beginning of a new session at `ruODK::get_default_{pid,fid,url,un,pw}()`. These getters in turn look up their values from environment variables. -The getters and setters are documented in the "Settings" family of the +The getters and setters are documented in the "Settings" family of the [ruODK function reference](https://docs.ropensci.org/ruODK/reference/index.html). A convenient way to have often used environment variables available is to add @@ -263,9 +263,9 @@ As there is no corresponding API endpoint to determine the ODK Central version, `ODKC_TEST_VERSION`. If unset, `ruODK` defaults to the latest ODK Central version, which is the version deployed to the ODK Central test server. -`ruODK` gracefully handles both the current semantic versioning version format -("year.minor.patch", e.g. "2023.5.1") and the older numeric format (e.g. 0.7, -1.2). If the `ODKC_VERSION` needs repair, `ruODK` will emit a helpful warning +`ruODK` gracefully handles both the current semantic versioning version format +("year.minor.patch", e.g. "2023.5.1") and the older numeric format (e.g. 0.7, +1.2). If the `ODKC_VERSION` needs repair, `ruODK` will emit a helpful warning advising the correct format for the given version. A recent example is the change of the API endpoint for `form_schema`, which diff --git a/vignettes/spatial.Rmd b/vignettes/spatial.Rmd index 061e8a36..9670d846 100644 --- a/vignettes/spatial.Rmd +++ b/vignettes/spatial.Rmd @@ -2,7 +2,7 @@ title: "Spatial data" description: > Examples and conversation starters for spatial data. -output: +output: rmarkdown::html_vignette: self_contained: false vignette: > @@ -15,34 +15,34 @@ vignette: > knitr::opts_chunk$set(collapse = TRUE, comment = "#>") ``` -For forms with spatial types, such as geopoint, geotrace, or geoshape, +For forms with spatial types, such as geopoint, geotrace, or geoshape, `ruODK` gives two options to access the captured spatial data. -Firstly, to make spatial data as simple and accessible as possible, -`ruODK` extracts the lat/lon/alt/acc from geopoints, as well as from +Firstly, to make spatial data as simple and accessible as possible, +`ruODK` extracts the lat/lon/alt/acc from geopoints, as well as from the first coordinate of geotraces and geoshapes into separate columns. This works for both GeoJSON and WKT. -The extracted columns are named as the original geofield appended with +The extracted columns are named as the original geofield appended with `_latitude`, `_longitude`, `_altitude`, and `_accuracy`, respectively. -Secondly, this vignette demonstrates how to turn the spatial data types returned -from ODK Central into spatially enabled objects. +Secondly, this vignette demonstrates how to turn the spatial data types returned +from ODK Central into spatially enabled objects. To do so, we have to address two challenges. The first challenge is to select which of the potentially many spatial fields which an ODK form can capture shall be used as the primary geometry of a native spatial object, such as an `sf` SimpleFeature class. -If several spatial fields are captured, it is up to the user to choose which +If several spatial fields are captured, it is up to the user to choose which field to use as primary geometry. -The second challenge is that the parsed data from ODK Central is a plain table -(`tbl_df`) in which some columns contain spatial data. Well Know Text (WKT) is +The second challenge is that the parsed data from ODK Central is a plain table +(`tbl_df`) in which some columns contain spatial data. Well Know Text (WKT) is parsed as text columns, whereas GeoJSON (nested JSON) is parsed as list columns. Most spatial packages require either atomic coordinates in separate columns, which works well for points (latitude, longitude, altitude), or the data to be spatially enabled. -This vignette shows how to transform a `tbl_df` with a column containing (point, +This vignette shows how to transform a `tbl_df` with a column containing (point, line, or polygon) WKT into a spatially enabled `sf` object. ```{r, message=FALSE, warning=FALSE} @@ -75,8 +75,8 @@ if (require(mapview)) { ## Data -The original data shown in this vignette are hosted on a ODK Central server which -is used for the `ruODK` package tests. The form we show here contains every +The original data shown in this vignette are hosted on a ODK Central server which +is used for the `ruODK` package tests. The form we show here contains every spatial widget supported by ODK Build for every supported spatial field type. With working credentials to the ODK Central test server we could download the data directly. @@ -97,8 +97,8 @@ data_wkt <- ruODK::odata_submission_get(wkt = TRUE) data_gj <- ruODK::odata_submission_get(wkt = FALSE) ``` -To allow users to build this vignette without credentials to the ODK Central -test server, `ruODK` provides above form data also as package data. +To allow users to build this vignette without credentials to the ODK Central +test server, `ruODK` provides above form data also as package data. ```{r} data("geo_wkt", package = "ruODK") @@ -107,11 +107,11 @@ data("geo_gj", package = "ruODK") ## Map geopoints -We can turn data with a text column containing WKT into an `sf` (SimpleFeatures) +We can turn data with a text column containing WKT into an `sf` (SimpleFeatures) object. -In addition, we can leave the `tbl_df` as non-spatial object, and instead -use the separately extracted latitude, longitude, altitude, and accuracy +In addition, we can leave the `tbl_df` as non-spatial object, and instead +use the separately extracted latitude, longitude, altitude, and accuracy individually e.g. to plot a Leaflet map. ```{r, fig.width=9, fig.height=7, eval=can_run} @@ -212,7 +212,7 @@ ggplot2::ggplot() + ### Leaflet using sf and extracted coordinates You can show either first extracted coordinate components from plain `tbl_df` or show the full polygons using `{leafem}`. -See the mapview article on [extra functionality](https://r-spatial.github.io/mapview/articles/articles/mapview_06-add.html). +See the mapview article on [extra functionality](https://r-spatial.github.io/mapview/articles/mapview_06-add.html). ```{r, eval=can_run} leaflet::leaflet(data = geo_wkt) %>% leaflet::addTiles() %>% @@ -230,32 +230,32 @@ The above examples show how to turn spatial data into an `sf` object, and give very rudimentary visualisation examples to bridge the gap between spatial data coming from ODK and creating maps and further spatial analyses in R. -See the [sf homepage](https://r-spatial.github.io/sf/) for more context and examples. -The [sf cheatsheet](https://github.com/rstudio/cheatsheets/blob/master/sf.pdf) +See the [sf homepage](https://r-spatial.github.io/sf/) for more context and examples. +The [sf cheatsheet](https://github.com/rstudio/cheatsheets/blob/master/sf.pdf) deserves a spatial mention. -Review the options for -[mapview popups](https://r-spatial.github.io/mapview/articles/articles/mapview_04-popups.html) -and the whole [mapview](https://r-spatial.github.io/mapview/index.html) homepage for a +Review the options for +[mapview popups](https://r-spatial.github.io/mapview/articles/mapview_04-popups.html) +and the whole [mapview](https://r-spatial.github.io/mapview/index.html) homepage for a comprehensive overview of mapview. The powerful visualisation package [`tmap`](https://cran.r-project.org/package=tmap) supports `sf` objects and produces both printable and static maps as well as interactive `leaflet` maps. See the [vignette "Get started"](https://cran.r-project.org/web/packages/tmap/vignettes/tmap-getstarted.html). -There are several other good entry points for all things R and spatial, +There are several other good entry points for all things R and spatial, including but not limited to: * The [R Spatial CRAN Task View](https://cran.r-project.org/view=Spatial) * The [RSpatial](https://rspatial.org/) website -* [Geospatial data in R and beyond](https://www.maths.lancs.ac.uk/~rowlings/Teaching/UseR2012/) +* [Geospatial data in R and beyond](https://www.maths.lancs.ac.uk/~rowlings/Teaching/UseR2012/) by [Barry Rowlingson](http://barry.rowlingson.com/) -* [GIS with R](https://www.jessesadler.com/post/gis-with-r-intro/) - by [Jesse Sadler](https://www.jessesadler.com/page/cv/) -* GIS and mapping by [Olivier Gimenez](https://oliviergimenez.github.io/): +* [GIS with R](https://www.jessesadler.com/post/gis-with-r-intro/) + by [Jesse Sadler](https://www.jessesadler.com/about) +* GIS and mapping by [Olivier Gimenez](https://oliviergimenez.github.io/): [Slides](https://oliviergimenez.github.io/intro_spatialR/) and [code](https://github.com/oliviergimenez/intro_spatialR) - + The above list of examples and resources is far from comprehensive. -Feel free to [contribute or suggest](https://github.com/ropensci/ruODK/issues) +Feel free to [contribute or suggest](https://github.com/ropensci/ruODK/issues) other working examples for turning data from `ruODK` into spatial formats.