diff --git a/.circleci/config.yml b/.circleci/config.yml index 3b17dd324..fef27dfd5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,6 @@ version: 2 jobs: - build: + build-odk1: working_directory: ~/work docker: - image: circleci/python:latest @@ -22,35 +22,34 @@ jobs: sudo apt-get install python-enchant sudo pip install -r requirements.txt - run: - name: Style Guide Testing + name: Style Guide Check command: | paths=$(git diff origin/master... --name-only) - sudo python style-test.py $paths + echo $paths + sudo python style-test.py $paths -r odk1-src + sudo python style-test.py $paths -r shared-src - run: - name: Spell Checker - command: | - sphinx-build -b spelling src build/spelling - python util/check-spelling-output.py + name: Build ODK1 Documentation + command: make odk1 - run: - name: Build PDF - command: | - sphinx-build -b latex src build/latex - sudo python util/resize.py + name: Check spelling of ODK1 Documentation + command: make odk1-spell-check - run: - name: Build docs - command: sphinx-build -W -b dirhtml src build + name: Build PDF of ODK1 Documentation + command: | + make odk1-latex - run: name: Compress images - command: pngquant build/_images/*.png --force --ext .png --verbose + command: pngquant odk1-build/_images/*.png --force --ext .png --verbose - store_artifacts: - path: build - destination: build + path: odk1-build + destination: odk1-build - persist_to_workspace: root: ~/work paths: - - build/* - - s3_website.yml - build_pdf: + - odk1-build/* + + build-pdf-odk1: working_directory: ~/work docker: - image: schickling/latex @@ -60,50 +59,97 @@ jobs: - run: name: Build PDF command: | - cd build/latex - xelatex OpenDataKit.tex - xelatex OpenDataKit.tex - mv OpenDataKit.pdf ../_downloads/ODK-Documentation.pdf + cd odk1-build/latex + xelatex OpenDataKit1.tex + mv OpenDataKit1.pdf ../_downloads/ODK1-Documentation.pdf - store_artifacts: - path: build/_downloads/ODK-Documentation.pdf - destination: ODK-Documentation.pdf + path: odk1-build/_downloads/ODK1-Documentation.pdf + destination: ODK1-Documentation.pdf - persist_to_workspace: root: ~/work paths: - - build/* - deploy: + - odk1-build/* + + build-odk2: working_directory: ~/work docker: - - image: circleci/ruby:latest + - image: circleci/python:latest steps: - - attach_workspace: - at: ~/work + - checkout - run: - name: Install deploy requirements + name: Install checkout requirements command: | - if [[ "$CIRCLE_PROJECT_USERNAME" == "opendatakit" ]]; then \ - sudo apt-get install -y openjdk-8-jre-headless - gem install s3_website && s3_website install - fi + curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash + sudo apt-get install git-lfs + git lfs install - run: - name: Push to S3 + name: Checkout binaries + command: git lfs pull + - run: + name: Install build requirements command: | - if [[ "$CIRCLE_PROJECT_USERNAME" == "opendatakit" ]]; then \ - s3_website push - fi + sudo apt-get install pngquant + sudo apt-get install python-enchant + sudo pip install -r requirements.txt + - run: + name: Style Guide Check + command: | + paths=$(git diff origin/master... --name-only) + echo $paths + sudo python style-test.py $paths -r odk2-src + sudo python style-test.py $paths -r shared-src + - run: + name: Build ODK2 Documentation + command: make odk2 + - run: + name: Check spelling of ODK2 Documentation + command: make odk2-spell-check + - run: + name: Build PDF of ODK2 Documentation + command: | + make odk2-latex + - run: + name: Compress images + command: pngquant odk2-build/_images/*.png --force --ext .png --verbose + - store_artifacts: + path: odk2-build + destination: odk2-build + - persist_to_workspace: + root: ~/work + paths: + - odk2-build/* + + build-pdf-odk2: + working_directory: ~/work + docker: + - image: schickling/latex + steps: + - attach_workspace: + at: ~/work + - run: + name: Build PDF + command: | + cd odk2-build/latex + xelatex OpenDataKit2.tex + mkdir ../_downloads + mv OpenDataKit2.pdf ../_downloads/ODK2-Documentation.pdf + - store_artifacts: + path: odk2-build/_downloads/ODK2-Documentation.pdf + destination: ODK2-Documentation.pdf + - persist_to_workspace: + root: ~/work + paths: + - odk2-build/* + workflows: version: 2 build_deploy: jobs: - - build - - build_pdf: + - build-odk1 + - build-pdf-odk1: requires: - - build - - deploy: + - build-odk1 + - build-odk2 + - build-pdf-odk2: requires: - - build - - build_pdf - # We'd like to also filter by username and remove the if/fi above, but username filtering is not supported in CircleCI - filters: - branches: - only: master + - build-odk2 diff --git a/.gitignore b/.gitignore index 17c1c4a14..314a3043a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,10 @@ style-checks.py extra.py build/ +*-build/ +tmp* __pycache__/ .DS_Store \#*.*\# -.\#* \ No newline at end of file +.\#* +.pyc diff --git a/Makefile b/Makefile index 073e6f074..ae4552f02 100644 --- a/Makefile +++ b/Makefile @@ -2,19 +2,75 @@ # # You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = python -msphinx +SPHINXOPTS = +SPHINXBUILD = sphinx-build SPHINXPROJ = OpenDataKit -SOURCEDIR = _src -BUILDDIR = _build +ODK1_SRCDIR = odk1-src +ODK2_SRCDIR = odk2-src +SHARED_SRCDIR = shared-src +COMPILE1_SRCDIR = tmp1-src +COMPILE2_SRCDIR = tmp2-src +ODK1_BUILDDIR = odk1-build +ODK2_BUILDDIR = odk2-build # Put it first so that "make" without argument is like "make help". help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + @$(SPHINXBUILD) -M help .PHONY: help Makefile -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file +odk1_clean: + rm -rf $(COMPILE1_SRCDIR) + rm -rf $(ODK1_BUILDDIR) + +odk2_clean: + rm -rf $(COMPILE2_SRCDIR) + rm -rf $(ODK2_BUILDDIR) + +clean: odk1_clean odk2_clean + +odk1_copy: odk1_clean + mkdir $(COMPILE1_SRCDIR) + cp -rf $(ODK1_SRCDIR)/* $(COMPILE1_SRCDIR) + cp -rf $(SHARED_SRCDIR)/* $(COMPILE1_SRCDIR) + +odk2_copy: odk2_clean + mkdir $(COMPILE2_SRCDIR) + cp -rf $(ODK2_SRCDIR)/* $(COMPILE2_SRCDIR) + cp -rf $(SHARED_SRCDIR)/* $(COMPILE2_SRCDIR) + +odk1: odk1_copy + @$(SPHINXBUILD) -b dirhtml "$(COMPILE1_SRCDIR)" "$(ODK1_BUILDDIR)" $(SPHINXOPTS) + +odk2: odk2_copy + @$(SPHINXBUILD) -b dirhtml "$(COMPILE2_SRCDIR)" "$(ODK2_BUILDDIR)" $(SPHINXOPTS) + +build-all: odk1 odk2 + +odk1-latex: odk1 + @$(SPHINXBUILD) -b latex "$(COMPILE1_SRCDIR)" "$(ODK1_BUILDDIR)"/latex $(SPHINXOPTS) + python util/resize.py "$(ODK1_BUILDDIR)" + +odk2-latex: odk2 + @$(SPHINXBUILD) -b latex "$(COMPILE2_SRCDIR)" "$(ODK2_BUILDDIR)"/latex $(SPHINXOPTS) + python util/resize.py "$(ODK2_BUILDDIR)" + +odk1-style-check: odk1 + python style-test.py -r $(COMPILE1_SRCDIR) + +odk1-spell-check: odk1 + sphinx-build -b spelling $(COMPILE1_SRCDIR) $(ODK1_BUILDDIR)/spelling + python util/check-spelling-output.py $(ODK1_BUILDDIR) + +odk2-style-check: odk2 + python style-test.py -r $(COMPILE2_SRCDIR) + +odk2-spell-check: odk2 + sphinx-build -b spelling $(COMPILE2_SRCDIR) $(ODK2_BUILDDIR)/spelling + python util/check-spelling-output.py $(ODK2_BUILDDIR) + +odk1-check: odk1-style-check odk1-spell-check + +odk2-check: odk2-style-check odk2-spell-check + +check-all: odk1-check odk2-check diff --git a/b.sh b/b.sh deleted file mode 100755 index 44110dca0..000000000 --- a/b.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -rm -rf build/* -python style-test.py -sphinx-build -b spelling src build/spelling -sphinx-build -b dirhtml src build diff --git a/src/aggregate-admin.rst b/odk1-src/aggregate-admin.rst similarity index 100% rename from src/aggregate-admin.rst rename to odk1-src/aggregate-admin.rst diff --git a/src/aggregate-app-engine.rst b/odk1-src/aggregate-app-engine.rst similarity index 100% rename from src/aggregate-app-engine.rst rename to odk1-src/aggregate-app-engine.rst diff --git a/src/aggregate-aws.rst b/odk1-src/aggregate-aws.rst similarity index 100% rename from src/aggregate-aws.rst rename to odk1-src/aggregate-aws.rst diff --git a/src/aggregate-backup.rst b/odk1-src/aggregate-backup.rst similarity index 100% rename from src/aggregate-backup.rst rename to odk1-src/aggregate-backup.rst diff --git a/src/aggregate-best-practices.rst b/odk1-src/aggregate-best-practices.rst similarity index 100% rename from src/aggregate-best-practices.rst rename to odk1-src/aggregate-best-practices.rst diff --git a/src/aggregate-boost-performance.rst b/odk1-src/aggregate-boost-performance.rst similarity index 100% rename from src/aggregate-boost-performance.rst rename to odk1-src/aggregate-boost-performance.rst diff --git a/src/aggregate-data-access.rst b/odk1-src/aggregate-data-access.rst similarity index 100% rename from src/aggregate-data-access.rst rename to odk1-src/aggregate-data-access.rst diff --git a/src/aggregate-data.rst b/odk1-src/aggregate-data.rst similarity index 100% rename from src/aggregate-data.rst rename to odk1-src/aggregate-data.rst diff --git a/src/aggregate-deployment-planning.rst b/odk1-src/aggregate-deployment-planning.rst similarity index 100% rename from src/aggregate-deployment-planning.rst rename to odk1-src/aggregate-deployment-planning.rst diff --git a/src/aggregate-forms.rst b/odk1-src/aggregate-forms.rst similarity index 100% rename from src/aggregate-forms.rst rename to odk1-src/aggregate-forms.rst diff --git a/src/aggregate-help.rst b/odk1-src/aggregate-help.rst similarity index 100% rename from src/aggregate-help.rst rename to odk1-src/aggregate-help.rst diff --git a/src/aggregate-install.rst b/odk1-src/aggregate-install.rst similarity index 100% rename from src/aggregate-install.rst rename to odk1-src/aggregate-install.rst diff --git a/src/aggregate-intro.rst b/odk1-src/aggregate-intro.rst similarity index 100% rename from src/aggregate-intro.rst rename to odk1-src/aggregate-intro.rst diff --git a/src/aggregate-limitations.rst b/odk1-src/aggregate-limitations.rst similarity index 100% rename from src/aggregate-limitations.rst rename to odk1-src/aggregate-limitations.rst diff --git a/src/aggregate-setup.rst b/odk1-src/aggregate-setup.rst similarity index 100% rename from src/aggregate-setup.rst rename to odk1-src/aggregate-setup.rst diff --git a/src/aggregate-tomcat.rst b/odk1-src/aggregate-tomcat.rst similarity index 100% rename from src/aggregate-tomcat.rst rename to odk1-src/aggregate-tomcat.rst diff --git a/src/aggregate-upgrade.rst b/odk1-src/aggregate-upgrade.rst similarity index 100% rename from src/aggregate-upgrade.rst rename to odk1-src/aggregate-upgrade.rst diff --git a/src/aggregate-use.rst b/odk1-src/aggregate-use.rst similarity index 100% rename from src/aggregate-use.rst rename to odk1-src/aggregate-use.rst diff --git a/src/aggregate-visualize.rst b/odk1-src/aggregate-visualize.rst similarity index 100% rename from src/aggregate-visualize.rst rename to odk1-src/aggregate-visualize.rst diff --git a/src/aggregate-vm.rst b/odk1-src/aggregate-vm.rst similarity index 100% rename from src/aggregate-vm.rst rename to odk1-src/aggregate-vm.rst diff --git a/src/briefcase-and-aggregate.rst b/odk1-src/briefcase-and-aggregate.rst similarity index 100% rename from src/briefcase-and-aggregate.rst rename to odk1-src/briefcase-and-aggregate.rst diff --git a/src/briefcase-api.rst b/odk1-src/briefcase-api.rst similarity index 100% rename from src/briefcase-api.rst rename to odk1-src/briefcase-api.rst diff --git a/src/briefcase-install.rst b/odk1-src/briefcase-install.rst similarity index 100% rename from src/briefcase-install.rst rename to odk1-src/briefcase-install.rst diff --git a/src/briefcase-intro.rst b/odk1-src/briefcase-intro.rst similarity index 100% rename from src/briefcase-intro.rst rename to odk1-src/briefcase-intro.rst diff --git a/src/briefcase-using.rst b/odk1-src/briefcase-using.rst similarity index 100% rename from src/briefcase-using.rst rename to odk1-src/briefcase-using.rst diff --git a/src/collect-adb.rst b/odk1-src/collect-adb.rst similarity index 100% rename from src/collect-adb.rst rename to odk1-src/collect-adb.rst diff --git a/src/collect-best-practices.rst b/odk1-src/collect-best-practices.rst similarity index 100% rename from src/collect-best-practices.rst rename to odk1-src/collect-best-practices.rst diff --git a/src/collect-connect-aggregate.rst b/odk1-src/collect-connect-aggregate.rst similarity index 100% rename from src/collect-connect-aggregate.rst rename to odk1-src/collect-connect-aggregate.rst diff --git a/src/collect-connect-google.rst b/odk1-src/collect-connect-google.rst similarity index 100% rename from src/collect-connect-google.rst rename to odk1-src/collect-connect-google.rst diff --git a/src/collect-connect.rst b/odk1-src/collect-connect.rst similarity index 100% rename from src/collect-connect.rst rename to odk1-src/collect-connect.rst diff --git a/src/collect-filling-forms.rst b/odk1-src/collect-filling-forms.rst similarity index 100% rename from src/collect-filling-forms.rst rename to odk1-src/collect-filling-forms.rst diff --git a/src/collect-forms.rst b/odk1-src/collect-forms.rst similarity index 100% rename from src/collect-forms.rst rename to odk1-src/collect-forms.rst diff --git a/src/collect-import-export.rst b/odk1-src/collect-import-export.rst similarity index 100% rename from src/collect-import-export.rst rename to odk1-src/collect-import-export.rst diff --git a/src/collect-install.rst b/odk1-src/collect-install.rst similarity index 100% rename from src/collect-install.rst rename to odk1-src/collect-install.rst diff --git a/src/collect-intro.rst b/odk1-src/collect-intro.rst similarity index 100% rename from src/collect-intro.rst rename to odk1-src/collect-intro.rst diff --git a/src/collect-location.rst b/odk1-src/collect-location.rst similarity index 100% rename from src/collect-location.rst rename to odk1-src/collect-location.rst diff --git a/src/collect-offline-maps.rst b/odk1-src/collect-offline-maps.rst similarity index 100% rename from src/collect-offline-maps.rst rename to odk1-src/collect-offline-maps.rst diff --git a/src/collect-security.rst b/odk1-src/collect-security.rst similarity index 100% rename from src/collect-security.rst rename to odk1-src/collect-security.rst diff --git a/src/collect-settings.rst b/odk1-src/collect-settings.rst similarity index 100% rename from src/collect-settings.rst rename to odk1-src/collect-settings.rst diff --git a/src/collect-setup.rst b/odk1-src/collect-setup.rst similarity index 100% rename from src/collect-setup.rst rename to odk1-src/collect-setup.rst diff --git a/src/collect-shortcut.rst b/odk1-src/collect-shortcut.rst similarity index 100% rename from src/collect-shortcut.rst rename to odk1-src/collect-shortcut.rst diff --git a/src/collect-using.rst b/odk1-src/collect-using.rst similarity index 100% rename from src/collect-using.rst rename to odk1-src/collect-using.rst diff --git a/src/conf.py b/odk1-src/conf.py similarity index 94% rename from src/conf.py rename to odk1-src/conf.py index 79d1e3560..446910dcf 100644 --- a/src/conf.py +++ b/odk1-src/conf.py @@ -55,7 +55,7 @@ master_doc = 'index' # General information about the project. -project = 'Open Data Kit' +project = 'Open Data Kit 1.x' copyright = '2017, Open Data Kit. This document is licensed under a Creative Commons Attribution 4.0 International License' author = 'Open Data Kit' @@ -118,7 +118,7 @@ 'logo_only': True, 'display_version': False, } -html_title = "Open Data Kit Docs" +html_title = "Open Data Kit 1.x Docs" html_favicon = "_static/img/odk-favicon.ico" @@ -140,7 +140,7 @@ # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. -htmlhelp_basename = 'OpenDataKitdoc' +htmlhelp_basename = 'OpenDataKit1doc' # -- Options for LaTeX output --------------------------------------------- @@ -174,8 +174,8 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'OpenDataKit.tex', 'Open Data Kit Documentation', - 'Open Data Kit', 'manual'), + (master_doc, 'OpenDataKit1.tex', 'Open Data Kit 1.x Documentation', + 'Open Data Kit 1.x', 'manual'), ] @@ -184,7 +184,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'opendatakit', 'Open Data Kit Documentation', + (master_doc, 'opendatakit1', 'Open Data Kit 1.x Documentation', [author], 1) ] @@ -195,8 +195,8 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'OpenDataKit', 'Open Data Kit Documentation', - author, 'OpenDataKit', 'One line description of project.', + (master_doc, 'OpenDataKit1', 'Open Data Kit 1.x Documentation', + author, 'OpenDataKit1', 'One line description of project.', 'Miscellaneous'), ] @@ -257,7 +257,7 @@ def setup(app): """ odk_pdf = """ -../_downloads/ODK-Documentation.pdf +../_downloads/ODK1-Documentation.pdf """ prob_in_doc = """ diff --git a/src/downloads/form-interaction/example_1.xlsx b/odk1-src/downloads/form-interaction/example_1.xlsx similarity index 100% rename from src/downloads/form-interaction/example_1.xlsx rename to odk1-src/downloads/form-interaction/example_1.xlsx diff --git a/src/downloads/form-interaction/example_1.xml b/odk1-src/downloads/form-interaction/example_1.xml similarity index 100% rename from src/downloads/form-interaction/example_1.xml rename to odk1-src/downloads/form-interaction/example_1.xml diff --git a/src/downloads/form-interaction/example_2.xlsx b/odk1-src/downloads/form-interaction/example_2.xlsx similarity index 100% rename from src/downloads/form-interaction/example_2.xlsx rename to odk1-src/downloads/form-interaction/example_2.xlsx diff --git a/src/downloads/form-interaction/example_2.xml b/odk1-src/downloads/form-interaction/example_2.xml similarity index 100% rename from src/downloads/form-interaction/example_2.xml rename to odk1-src/downloads/form-interaction/example_2.xml diff --git a/src/downloads/form-update/example_form_v1.0.xlsx b/odk1-src/downloads/form-update/example_form_v1.0.xlsx similarity index 100% rename from src/downloads/form-update/example_form_v1.0.xlsx rename to odk1-src/downloads/form-update/example_form_v1.0.xlsx diff --git a/src/downloads/form-update/example_form_v1.0.xml b/odk1-src/downloads/form-update/example_form_v1.0.xml similarity index 100% rename from src/downloads/form-update/example_form_v1.0.xml rename to odk1-src/downloads/form-update/example_form_v1.0.xml diff --git a/src/downloads/form-update/example_form_v1.1.xlsx b/odk1-src/downloads/form-update/example_form_v1.1.xlsx similarity index 100% rename from src/downloads/form-update/example_form_v1.1.xlsx rename to odk1-src/downloads/form-update/example_form_v1.1.xlsx diff --git a/src/downloads/form-update/example_form_v1.1.xml b/odk1-src/downloads/form-update/example_form_v1.1.xml similarity index 100% rename from src/downloads/form-update/example_form_v1.1.xml rename to odk1-src/downloads/form-update/example_form_v1.1.xml diff --git a/src/encrypted-forms.rst b/odk1-src/encrypted-forms.rst similarity index 100% rename from src/encrypted-forms.rst rename to odk1-src/encrypted-forms.rst diff --git a/src/form-design-intro.rst b/odk1-src/form-design-intro.rst similarity index 100% rename from src/form-design-intro.rst rename to odk1-src/form-design-intro.rst diff --git a/src/form-interaction.rst b/odk1-src/form-interaction.rst similarity index 100% rename from src/form-interaction.rst rename to odk1-src/form-interaction.rst diff --git a/src/form-regex.rst b/odk1-src/form-regex.rst similarity index 100% rename from src/form-regex.rst rename to odk1-src/form-regex.rst diff --git a/src/form-update.rst b/odk1-src/form-update.rst similarity index 100% rename from src/form-update.rst rename to odk1-src/form-update.rst diff --git a/src/form-uploader.rst b/odk1-src/form-uploader.rst similarity index 100% rename from src/form-uploader.rst rename to odk1-src/form-uploader.rst diff --git a/src/form-widgets.rst b/odk1-src/form-widgets.rst similarity index 100% rename from src/form-widgets.rst rename to odk1-src/form-widgets.rst diff --git a/src/getting-started.rst b/odk1-src/getting-started.rst similarity index 100% rename from src/getting-started.rst rename to odk1-src/getting-started.rst diff --git a/src/glossary.rst b/odk1-src/glossary.rst similarity index 100% rename from src/glossary.rst rename to odk1-src/glossary.rst diff --git a/src/img/aggregate-backup/admin.png b/odk1-src/img/aggregate-backup/admin.png similarity index 100% rename from src/img/aggregate-backup/admin.png rename to odk1-src/img/aggregate-backup/admin.png diff --git a/src/img/aggregate-backup/backup-form.png b/odk1-src/img/aggregate-backup/backup-form.png similarity index 100% rename from src/img/aggregate-backup/backup-form.png rename to odk1-src/img/aggregate-backup/backup-form.png diff --git a/src/img/aggregate-backup/backup-info.png b/odk1-src/img/aggregate-backup/backup-info.png similarity index 100% rename from src/img/aggregate-backup/backup-info.png rename to odk1-src/img/aggregate-backup/backup-info.png diff --git a/src/img/aggregate-backup/backup-job.png b/odk1-src/img/aggregate-backup/backup-job.png similarity index 100% rename from src/img/aggregate-backup/backup-job.png rename to odk1-src/img/aggregate-backup/backup-job.png diff --git a/src/img/aggregate-backup/backup-list.png b/odk1-src/img/aggregate-backup/backup-list.png similarity index 100% rename from src/img/aggregate-backup/backup-list.png rename to odk1-src/img/aggregate-backup/backup-list.png diff --git a/src/img/aggregate-backup/backup-select.png b/odk1-src/img/aggregate-backup/backup-select.png similarity index 100% rename from src/img/aggregate-backup/backup-select.png rename to odk1-src/img/aggregate-backup/backup-select.png diff --git a/src/img/aggregate-backup/cloud-console.png b/odk1-src/img/aggregate-backup/cloud-console.png similarity index 100% rename from src/img/aggregate-backup/cloud-console.png rename to odk1-src/img/aggregate-backup/cloud-console.png diff --git a/src/img/aggregate-backup/disable-writes.png b/odk1-src/img/aggregate-backup/disable-writes.png similarity index 100% rename from src/img/aggregate-backup/disable-writes.png rename to odk1-src/img/aggregate-backup/disable-writes.png diff --git a/src/img/aggregate-backup/dropdown.png b/odk1-src/img/aggregate-backup/dropdown.png similarity index 100% rename from src/img/aggregate-backup/dropdown.png rename to odk1-src/img/aggregate-backup/dropdown.png diff --git a/src/img/aggregate-backup/email-select.png b/odk1-src/img/aggregate-backup/email-select.png similarity index 100% rename from src/img/aggregate-backup/email-select.png rename to odk1-src/img/aggregate-backup/email-select.png diff --git a/src/img/aggregate-backup/enable-admin.png b/odk1-src/img/aggregate-backup/enable-admin.png similarity index 100% rename from src/img/aggregate-backup/enable-admin.png rename to odk1-src/img/aggregate-backup/enable-admin.png diff --git a/src/img/aggregate-backup/enable-writes.png b/odk1-src/img/aggregate-backup/enable-writes.png similarity index 100% rename from src/img/aggregate-backup/enable-writes.png rename to odk1-src/img/aggregate-backup/enable-writes.png diff --git a/src/img/aggregate-backup/open-admin.png b/odk1-src/img/aggregate-backup/open-admin.png similarity index 100% rename from src/img/aggregate-backup/open-admin.png rename to odk1-src/img/aggregate-backup/open-admin.png diff --git a/src/img/aggregate-backup/pending-backup.png b/odk1-src/img/aggregate-backup/pending-backup.png similarity index 100% rename from src/img/aggregate-backup/pending-backup.png rename to odk1-src/img/aggregate-backup/pending-backup.png diff --git a/src/img/aggregate-backup/project-select.png b/odk1-src/img/aggregate-backup/project-select.png similarity index 100% rename from src/img/aggregate-backup/project-select.png rename to odk1-src/img/aggregate-backup/project-select.png diff --git a/src/img/aggregate-backup/restore-backup.png b/odk1-src/img/aggregate-backup/restore-backup.png similarity index 100% rename from src/img/aggregate-backup/restore-backup.png rename to odk1-src/img/aggregate-backup/restore-backup.png diff --git a/src/img/aggregate-backup/restore-job.png b/odk1-src/img/aggregate-backup/restore-job.png similarity index 100% rename from src/img/aggregate-backup/restore-job.png rename to odk1-src/img/aggregate-backup/restore-job.png diff --git a/src/img/aggregate-backup/restore-status.png b/odk1-src/img/aggregate-backup/restore-status.png similarity index 100% rename from src/img/aggregate-backup/restore-status.png rename to odk1-src/img/aggregate-backup/restore-status.png diff --git a/src/img/aggregate-install/agreement.png b/odk1-src/img/aggregate-install/agreement.png similarity index 100% rename from src/img/aggregate-install/agreement.png rename to odk1-src/img/aggregate-install/agreement.png diff --git a/src/img/aggregate-install/allow.png b/odk1-src/img/aggregate-install/allow.png similarity index 100% rename from src/img/aggregate-install/allow.png rename to odk1-src/img/aggregate-install/allow.png diff --git a/src/img/aggregate-install/app-engine.png b/odk1-src/img/aggregate-install/app-engine.png similarity index 100% rename from src/img/aggregate-install/app-engine.png rename to odk1-src/img/aggregate-install/app-engine.png diff --git a/src/img/aggregate-install/application-id.png b/odk1-src/img/aggregate-install/application-id.png similarity index 100% rename from src/img/aggregate-install/application-id.png rename to odk1-src/img/aggregate-install/application-id.png diff --git a/src/img/aggregate-install/cancel-tutorial.png b/odk1-src/img/aggregate-install/cancel-tutorial.png similarity index 100% rename from src/img/aggregate-install/cancel-tutorial.png rename to odk1-src/img/aggregate-install/cancel-tutorial.png diff --git a/src/img/aggregate-install/choose-platform.png b/odk1-src/img/aggregate-install/choose-platform.png similarity index 100% rename from src/img/aggregate-install/choose-platform.png rename to odk1-src/img/aggregate-install/choose-platform.png diff --git a/src/img/aggregate-install/cloud-console.png b/odk1-src/img/aggregate-install/cloud-console.png similarity index 100% rename from src/img/aggregate-install/cloud-console.png rename to odk1-src/img/aggregate-install/cloud-console.png diff --git a/src/img/aggregate-install/create-project.png b/odk1-src/img/aggregate-install/create-project.png similarity index 100% rename from src/img/aggregate-install/create-project.png rename to odk1-src/img/aggregate-install/create-project.png diff --git a/src/img/aggregate-install/directory-setup.png b/odk1-src/img/aggregate-install/directory-setup.png similarity index 100% rename from src/img/aggregate-install/directory-setup.png rename to odk1-src/img/aggregate-install/directory-setup.png diff --git a/src/img/aggregate-install/email-select.png b/odk1-src/img/aggregate-install/email-select.png similarity index 100% rename from src/img/aggregate-install/email-select.png rename to odk1-src/img/aggregate-install/email-select.png diff --git a/src/img/aggregate-install/empty-project.png b/odk1-src/img/aggregate-install/empty-project.png similarity index 100% rename from src/img/aggregate-install/empty-project.png rename to odk1-src/img/aggregate-install/empty-project.png diff --git a/src/img/aggregate-install/get-token.png b/odk1-src/img/aggregate-install/get-token.png similarity index 100% rename from src/img/aggregate-install/get-token.png rename to odk1-src/img/aggregate-install/get-token.png diff --git a/src/img/aggregate-install/go-to-project.png b/odk1-src/img/aggregate-install/go-to-project.png similarity index 100% rename from src/img/aggregate-install/go-to-project.png rename to odk1-src/img/aggregate-install/go-to-project.png diff --git a/src/img/aggregate-install/language-select.png b/odk1-src/img/aggregate-install/language-select.png similarity index 100% rename from src/img/aggregate-install/language-select.png rename to odk1-src/img/aggregate-install/language-select.png diff --git a/src/img/aggregate-install/notification.png b/odk1-src/img/aggregate-install/notification.png similarity index 100% rename from src/img/aggregate-install/notification.png rename to odk1-src/img/aggregate-install/notification.png diff --git a/src/img/aggregate-install/prepare-engine.png b/odk1-src/img/aggregate-install/prepare-engine.png similarity index 100% rename from src/img/aggregate-install/prepare-engine.png rename to odk1-src/img/aggregate-install/prepare-engine.png diff --git a/src/img/aggregate-install/project-aggregate.png b/odk1-src/img/aggregate-install/project-aggregate.png similarity index 100% rename from src/img/aggregate-install/project-aggregate.png rename to odk1-src/img/aggregate-install/project-aggregate.png diff --git a/src/img/aggregate-install/project-id.png b/odk1-src/img/aggregate-install/project-id.png similarity index 100% rename from src/img/aggregate-install/project-id.png rename to odk1-src/img/aggregate-install/project-id.png diff --git a/src/img/aggregate-install/project-name.png b/odk1-src/img/aggregate-install/project-name.png similarity index 100% rename from src/img/aggregate-install/project-name.png rename to odk1-src/img/aggregate-install/project-name.png diff --git a/src/img/aggregate-install/project-settings.png b/odk1-src/img/aggregate-install/project-settings.png similarity index 100% rename from src/img/aggregate-install/project-settings.png rename to odk1-src/img/aggregate-install/project-settings.png diff --git a/src/img/aggregate-install/project.png b/odk1-src/img/aggregate-install/project.png similarity index 100% rename from src/img/aggregate-install/project.png rename to odk1-src/img/aggregate-install/project.png diff --git a/src/img/aggregate-install/select-java.png b/odk1-src/img/aggregate-install/select-java.png similarity index 100% rename from src/img/aggregate-install/select-java.png rename to odk1-src/img/aggregate-install/select-java.png diff --git a/src/img/aggregate-install/select-region.png b/odk1-src/img/aggregate-install/select-region.png similarity index 100% rename from src/img/aggregate-install/select-region.png rename to odk1-src/img/aggregate-install/select-region.png diff --git a/src/img/aggregate-install/server.png b/odk1-src/img/aggregate-install/server.png similarity index 100% rename from src/img/aggregate-install/server.png rename to odk1-src/img/aggregate-install/server.png diff --git a/src/img/aggregate-install/set-name.png b/odk1-src/img/aggregate-install/set-name.png similarity index 100% rename from src/img/aggregate-install/set-name.png rename to odk1-src/img/aggregate-install/set-name.png diff --git a/src/img/aggregate-install/setup.png b/odk1-src/img/aggregate-install/setup.png similarity index 100% rename from src/img/aggregate-install/setup.png rename to odk1-src/img/aggregate-install/setup.png diff --git a/src/img/aggregate-install/success-output.png b/odk1-src/img/aggregate-install/success-output.png similarity index 100% rename from src/img/aggregate-install/success-output.png rename to odk1-src/img/aggregate-install/success-output.png diff --git a/src/img/aggregate-install/superuser.png b/odk1-src/img/aggregate-install/superuser.png similarity index 100% rename from src/img/aggregate-install/superuser.png rename to odk1-src/img/aggregate-install/superuser.png diff --git a/src/img/aggregate-install/token.png b/odk1-src/img/aggregate-install/token.png similarity index 100% rename from src/img/aggregate-install/token.png rename to odk1-src/img/aggregate-install/token.png diff --git a/src/img/aggregate-install/upload.png b/odk1-src/img/aggregate-install/upload.png similarity index 100% rename from src/img/aggregate-install/upload.png rename to odk1-src/img/aggregate-install/upload.png diff --git a/src/img/aggregate-intro/aggregate-form-data.png b/odk1-src/img/aggregate-intro/aggregate-form-data.png similarity index 100% rename from src/img/aggregate-intro/aggregate-form-data.png rename to odk1-src/img/aggregate-intro/aggregate-form-data.png diff --git a/src/img/aggregate-intro/aggregate-pie-chart.png b/odk1-src/img/aggregate-intro/aggregate-pie-chart.png similarity index 100% rename from src/img/aggregate-intro/aggregate-pie-chart.png rename to odk1-src/img/aggregate-intro/aggregate-pie-chart.png diff --git a/src/img/aggregate-upgrade/dropdown.png b/odk1-src/img/aggregate-upgrade/dropdown.png similarity index 100% rename from src/img/aggregate-upgrade/dropdown.png rename to odk1-src/img/aggregate-upgrade/dropdown.png diff --git a/src/img/aggregate-upgrade/find-version.png b/odk1-src/img/aggregate-upgrade/find-version.png similarity index 100% rename from src/img/aggregate-upgrade/find-version.png rename to odk1-src/img/aggregate-upgrade/find-version.png diff --git a/src/img/aggregate-upgrade/search-logs.png b/odk1-src/img/aggregate-upgrade/search-logs.png similarity index 100% rename from src/img/aggregate-upgrade/search-logs.png rename to odk1-src/img/aggregate-upgrade/search-logs.png diff --git a/src/img/aggregate-upgrade/select-project.png b/odk1-src/img/aggregate-upgrade/select-project.png similarity index 100% rename from src/img/aggregate-upgrade/select-project.png rename to odk1-src/img/aggregate-upgrade/select-project.png diff --git a/src/img/aggregate-use/add-filter.png b/odk1-src/img/aggregate-use/add-filter.png similarity index 100% rename from src/img/aggregate-use/add-filter.png rename to odk1-src/img/aggregate-use/add-filter.png diff --git a/src/img/aggregate-use/add-form-options.png b/odk1-src/img/aggregate-use/add-form-options.png similarity index 100% rename from src/img/aggregate-use/add-form-options.png rename to odk1-src/img/aggregate-use/add-form-options.png diff --git a/src/img/aggregate-use/add-form.png b/odk1-src/img/aggregate-use/add-form.png similarity index 100% rename from src/img/aggregate-use/add-form.png rename to odk1-src/img/aggregate-use/add-form.png diff --git a/src/img/aggregate-use/balloon-help.png b/odk1-src/img/aggregate-use/balloon-help.png similarity index 100% rename from src/img/aggregate-use/balloon-help.png rename to odk1-src/img/aggregate-use/balloon-help.png diff --git a/src/img/aggregate-use/bar-graph.png b/odk1-src/img/aggregate-use/bar-graph.png similarity index 100% rename from src/img/aggregate-use/bar-graph.png rename to odk1-src/img/aggregate-use/bar-graph.png diff --git a/src/img/aggregate-use/book-help.png b/odk1-src/img/aggregate-use/book-help.png similarity index 100% rename from src/img/aggregate-use/book-help.png rename to odk1-src/img/aggregate-use/book-help.png diff --git a/src/img/aggregate-use/column-filter.png b/odk1-src/img/aggregate-use/column-filter.png similarity index 100% rename from src/img/aggregate-use/column-filter.png rename to odk1-src/img/aggregate-use/column-filter.png diff --git a/src/img/aggregate-use/export-options.png b/odk1-src/img/aggregate-use/export-options.png similarity index 100% rename from src/img/aggregate-use/export-options.png rename to odk1-src/img/aggregate-use/export-options.png diff --git a/src/img/aggregate-use/export-submission.png b/odk1-src/img/aggregate-use/export-submission.png similarity index 100% rename from src/img/aggregate-use/export-submission.png rename to odk1-src/img/aggregate-use/export-submission.png diff --git a/src/img/aggregate-use/exported-submission.png b/odk1-src/img/aggregate-use/exported-submission.png similarity index 100% rename from src/img/aggregate-use/exported-submission.png rename to odk1-src/img/aggregate-use/exported-submission.png diff --git a/src/img/aggregate-use/filter-options.png b/odk1-src/img/aggregate-use/filter-options.png similarity index 100% rename from src/img/aggregate-use/filter-options.png rename to odk1-src/img/aggregate-use/filter-options.png diff --git a/src/img/aggregate-use/form-list.png b/odk1-src/img/aggregate-use/form-list.png similarity index 100% rename from src/img/aggregate-use/form-list.png rename to odk1-src/img/aggregate-use/form-list.png diff --git a/src/img/aggregate-use/google-api-option.png b/odk1-src/img/aggregate-use/google-api-option.png similarity index 100% rename from src/img/aggregate-use/google-api-option.png rename to odk1-src/img/aggregate-use/google-api-option.png diff --git a/src/img/aggregate-use/map.png b/odk1-src/img/aggregate-use/map.png similarity index 100% rename from src/img/aggregate-use/map.png rename to odk1-src/img/aggregate-use/map.png diff --git a/src/img/aggregate-use/permissions.png b/odk1-src/img/aggregate-use/permissions.png similarity index 100% rename from src/img/aggregate-use/permissions.png rename to odk1-src/img/aggregate-use/permissions.png diff --git a/src/img/aggregate-use/pie-chart.png b/odk1-src/img/aggregate-use/pie-chart.png similarity index 100% rename from src/img/aggregate-use/pie-chart.png rename to odk1-src/img/aggregate-use/pie-chart.png diff --git a/src/img/aggregate-use/preferences-options.png b/odk1-src/img/aggregate-use/preferences-options.png similarity index 100% rename from src/img/aggregate-use/preferences-options.png rename to odk1-src/img/aggregate-use/preferences-options.png diff --git a/src/img/aggregate-use/privileges.png b/odk1-src/img/aggregate-use/privileges.png similarity index 100% rename from src/img/aggregate-use/privileges.png rename to odk1-src/img/aggregate-use/privileges.png diff --git a/src/img/aggregate-use/publish-options.png b/odk1-src/img/aggregate-use/publish-options.png similarity index 100% rename from src/img/aggregate-use/publish-options.png rename to odk1-src/img/aggregate-use/publish-options.png diff --git a/src/img/aggregate-use/publish-submission.png b/odk1-src/img/aggregate-use/publish-submission.png similarity index 100% rename from src/img/aggregate-use/publish-submission.png rename to odk1-src/img/aggregate-use/publish-submission.png diff --git a/src/img/aggregate-use/published-data.png b/odk1-src/img/aggregate-use/published-data.png similarity index 100% rename from src/img/aggregate-use/published-data.png rename to odk1-src/img/aggregate-use/published-data.png diff --git a/src/img/aggregate-use/question-mark-help.png b/odk1-src/img/aggregate-use/question-mark-help.png similarity index 100% rename from src/img/aggregate-use/question-mark-help.png rename to odk1-src/img/aggregate-use/question-mark-help.png diff --git a/src/img/aggregate-use/row-filter.png b/odk1-src/img/aggregate-use/row-filter.png similarity index 100% rename from src/img/aggregate-use/row-filter.png rename to odk1-src/img/aggregate-use/row-filter.png diff --git a/src/img/aggregate-use/server-start.png b/odk1-src/img/aggregate-use/server-start.png similarity index 100% rename from src/img/aggregate-use/server-start.png rename to odk1-src/img/aggregate-use/server-start.png diff --git a/src/img/aggregate-use/sign-in.png b/odk1-src/img/aggregate-use/sign-in.png similarity index 100% rename from src/img/aggregate-use/sign-in.png rename to odk1-src/img/aggregate-use/sign-in.png diff --git a/src/img/aggregate-use/submission-admin.png b/odk1-src/img/aggregate-use/submission-admin.png similarity index 100% rename from src/img/aggregate-use/submission-admin.png rename to odk1-src/img/aggregate-use/submission-admin.png diff --git a/src/img/aggregate-use/submission-upload.png b/odk1-src/img/aggregate-use/submission-upload.png similarity index 100% rename from src/img/aggregate-use/submission-upload.png rename to odk1-src/img/aggregate-use/submission-upload.png diff --git a/src/img/aggregate-use/visualize.png b/odk1-src/img/aggregate-use/visualize.png similarity index 100% rename from src/img/aggregate-use/visualize.png rename to odk1-src/img/aggregate-use/visualize.png diff --git a/src/img/aggregate-use/warning.png b/odk1-src/img/aggregate-use/warning.png similarity index 100% rename from src/img/aggregate-use/warning.png rename to odk1-src/img/aggregate-use/warning.png diff --git a/src/img/aggregate-use/xml-viewer.png b/odk1-src/img/aggregate-use/xml-viewer.png similarity index 100% rename from src/img/aggregate-use/xml-viewer.png rename to odk1-src/img/aggregate-use/xml-viewer.png diff --git a/src/img/briefcase-intro/briefcase-pull.png b/odk1-src/img/briefcase-intro/briefcase-pull.png similarity index 100% rename from src/img/briefcase-intro/briefcase-pull.png rename to odk1-src/img/briefcase-intro/briefcase-pull.png diff --git a/src/img/collect-best-practices/about-device.png b/odk1-src/img/collect-best-practices/about-device.png similarity index 100% rename from src/img/collect-best-practices/about-device.png rename to odk1-src/img/collect-best-practices/about-device.png diff --git a/src/img/collect-best-practices/accuracy-mode.png b/odk1-src/img/collect-best-practices/accuracy-mode.png similarity index 100% rename from src/img/collect-best-practices/accuracy-mode.png rename to odk1-src/img/collect-best-practices/accuracy-mode.png diff --git a/src/img/collect-best-practices/add-items.png b/odk1-src/img/collect-best-practices/add-items.png similarity index 100% rename from src/img/collect-best-practices/add-items.png rename to odk1-src/img/collect-best-practices/add-items.png diff --git a/src/img/collect-best-practices/apps.png b/odk1-src/img/collect-best-practices/apps.png similarity index 100% rename from src/img/collect-best-practices/apps.png rename to odk1-src/img/collect-best-practices/apps.png diff --git a/src/img/collect-best-practices/authenticate-option.png b/odk1-src/img/collect-best-practices/authenticate-option.png similarity index 100% rename from src/img/collect-best-practices/authenticate-option.png rename to odk1-src/img/collect-best-practices/authenticate-option.png diff --git a/src/img/collect-best-practices/backup-data.png b/odk1-src/img/collect-best-practices/backup-data.png similarity index 100% rename from src/img/collect-best-practices/backup-data.png rename to odk1-src/img/collect-best-practices/backup-data.png diff --git a/src/img/collect-best-practices/backup-off.png b/odk1-src/img/collect-best-practices/backup-off.png similarity index 100% rename from src/img/collect-best-practices/backup-off.png rename to odk1-src/img/collect-best-practices/backup-off.png diff --git a/src/img/collect-best-practices/backup-reset.png b/odk1-src/img/collect-best-practices/backup-reset.png similarity index 100% rename from src/img/collect-best-practices/backup-reset.png rename to odk1-src/img/collect-best-practices/backup-reset.png diff --git a/src/img/collect-best-practices/display.png b/odk1-src/img/collect-best-practices/display.png similarity index 100% rename from src/img/collect-best-practices/display.png rename to odk1-src/img/collect-best-practices/display.png diff --git a/src/img/collect-best-practices/encrypt-device.png b/odk1-src/img/collect-best-practices/encrypt-device.png similarity index 100% rename from src/img/collect-best-practices/encrypt-device.png rename to odk1-src/img/collect-best-practices/encrypt-device.png diff --git a/src/img/collect-best-practices/encrypt-sdcard.png b/odk1-src/img/collect-best-practices/encrypt-sdcard.png similarity index 100% rename from src/img/collect-best-practices/encrypt-sdcard.png rename to odk1-src/img/collect-best-practices/encrypt-sdcard.png diff --git a/src/img/collect-best-practices/enter-password.png b/odk1-src/img/collect-best-practices/enter-password.png similarity index 100% rename from src/img/collect-best-practices/enter-password.png rename to odk1-src/img/collect-best-practices/enter-password.png diff --git a/src/img/collect-best-practices/example-form.png b/odk1-src/img/collect-best-practices/example-form.png similarity index 100% rename from src/img/collect-best-practices/example-form.png rename to odk1-src/img/collect-best-practices/example-form.png diff --git a/src/img/collect-best-practices/form-list.png b/odk1-src/img/collect-best-practices/form-list.png similarity index 100% rename from src/img/collect-best-practices/form-list.png rename to odk1-src/img/collect-best-practices/form-list.png diff --git a/src/img/collect-best-practices/form-shortcut.png b/odk1-src/img/collect-best-practices/form-shortcut.png similarity index 100% rename from src/img/collect-best-practices/form-shortcut.png rename to odk1-src/img/collect-best-practices/form-shortcut.png diff --git a/src/img/collect-best-practices/google-app.png b/odk1-src/img/collect-best-practices/google-app.png similarity index 100% rename from src/img/collect-best-practices/google-app.png rename to odk1-src/img/collect-best-practices/google-app.png diff --git a/src/img/collect-best-practices/google-menu.png b/odk1-src/img/collect-best-practices/google-menu.png similarity index 100% rename from src/img/collect-best-practices/google-menu.png rename to odk1-src/img/collect-best-practices/google-menu.png diff --git a/src/img/collect-best-practices/google-settings.png b/odk1-src/img/collect-best-practices/google-settings.png similarity index 100% rename from src/img/collect-best-practices/google-settings.png rename to odk1-src/img/collect-best-practices/google-settings.png diff --git a/src/img/collect-best-practices/google-voice.png b/odk1-src/img/collect-best-practices/google-voice.png similarity index 100% rename from src/img/collect-best-practices/google-voice.png rename to odk1-src/img/collect-best-practices/google-voice.png diff --git a/src/img/collect-best-practices/help-describe.png b/odk1-src/img/collect-best-practices/help-describe.png similarity index 100% rename from src/img/collect-best-practices/help-describe.png rename to odk1-src/img/collect-best-practices/help-describe.png diff --git a/src/img/collect-best-practices/help.png b/odk1-src/img/collect-best-practices/help.png similarity index 100% rename from src/img/collect-best-practices/help.png rename to odk1-src/img/collect-best-practices/help.png diff --git a/src/img/collect-best-practices/home-screen.png b/odk1-src/img/collect-best-practices/home-screen.png similarity index 100% rename from src/img/collect-best-practices/home-screen.png rename to odk1-src/img/collect-best-practices/home-screen.png diff --git a/src/img/collect-best-practices/improve-accuracy-mode.png b/odk1-src/img/collect-best-practices/improve-accuracy-mode.png similarity index 100% rename from src/img/collect-best-practices/improve-accuracy-mode.png rename to odk1-src/img/collect-best-practices/improve-accuracy-mode.png diff --git a/src/img/collect-best-practices/improve-accuracy.png b/odk1-src/img/collect-best-practices/improve-accuracy.png similarity index 100% rename from src/img/collect-best-practices/improve-accuracy.png rename to odk1-src/img/collect-best-practices/improve-accuracy.png diff --git a/src/img/collect-best-practices/location-off.png b/odk1-src/img/collect-best-practices/location-off.png similarity index 100% rename from src/img/collect-best-practices/location-off.png rename to odk1-src/img/collect-best-practices/location-off.png diff --git a/src/img/collect-best-practices/location-on.png b/odk1-src/img/collect-best-practices/location-on.png similarity index 100% rename from src/img/collect-best-practices/location-on.png rename to odk1-src/img/collect-best-practices/location-on.png diff --git a/src/img/collect-best-practices/location.png b/odk1-src/img/collect-best-practices/location.png similarity index 100% rename from src/img/collect-best-practices/location.png rename to odk1-src/img/collect-best-practices/location.png diff --git a/src/img/collect-best-practices/lock-automatic.png b/odk1-src/img/collect-best-practices/lock-automatic.png similarity index 100% rename from src/img/collect-best-practices/lock-automatic.png rename to odk1-src/img/collect-best-practices/lock-automatic.png diff --git a/src/img/collect-best-practices/lock-screen.png b/odk1-src/img/collect-best-practices/lock-screen.png similarity index 100% rename from src/img/collect-best-practices/lock-screen.png rename to odk1-src/img/collect-best-practices/lock-screen.png diff --git a/src/img/collect-best-practices/mode.png b/odk1-src/img/collect-best-practices/mode.png similarity index 100% rename from src/img/collect-best-practices/mode.png rename to odk1-src/img/collect-best-practices/mode.png diff --git a/src/img/collect-best-practices/notify-lock-screen.png b/odk1-src/img/collect-best-practices/notify-lock-screen.png similarity index 100% rename from src/img/collect-best-practices/notify-lock-screen.png rename to odk1-src/img/collect-best-practices/notify-lock-screen.png diff --git a/src/img/collect-best-practices/notify-options.png b/odk1-src/img/collect-best-practices/notify-options.png similarity index 100% rename from src/img/collect-best-practices/notify-options.png rename to odk1-src/img/collect-best-practices/notify-options.png diff --git a/src/img/collect-best-practices/odk-form.png b/odk1-src/img/collect-best-practices/odk-form.png similarity index 100% rename from src/img/collect-best-practices/odk-form.png rename to odk1-src/img/collect-best-practices/odk-form.png diff --git a/src/img/collect-best-practices/ok-google-detect.png b/odk1-src/img/collect-best-practices/ok-google-detect.png similarity index 100% rename from src/img/collect-best-practices/ok-google-detect.png rename to odk1-src/img/collect-best-practices/ok-google-detect.png diff --git a/src/img/collect-best-practices/pin-or-password.png b/odk1-src/img/collect-best-practices/pin-or-password.png similarity index 100% rename from src/img/collect-best-practices/pin-or-password.png rename to odk1-src/img/collect-best-practices/pin-or-password.png diff --git a/src/img/collect-best-practices/play-store-menu.png b/odk1-src/img/collect-best-practices/play-store-menu.png similarity index 100% rename from src/img/collect-best-practices/play-store-menu.png rename to odk1-src/img/collect-best-practices/play-store-menu.png diff --git a/src/img/collect-best-practices/play-store-settings.png b/odk1-src/img/collect-best-practices/play-store-settings.png similarity index 100% rename from src/img/collect-best-practices/play-store-settings.png rename to odk1-src/img/collect-best-practices/play-store-settings.png diff --git a/src/img/collect-best-practices/play-store.png b/odk1-src/img/collect-best-practices/play-store.png similarity index 100% rename from src/img/collect-best-practices/play-store.png rename to odk1-src/img/collect-best-practices/play-store.png diff --git a/src/img/collect-best-practices/remove.png b/odk1-src/img/collect-best-practices/remove.png similarity index 100% rename from src/img/collect-best-practices/remove.png rename to odk1-src/img/collect-best-practices/remove.png diff --git a/src/img/collect-best-practices/require-authentication.png b/odk1-src/img/collect-best-practices/require-authentication.png similarity index 100% rename from src/img/collect-best-practices/require-authentication.png rename to odk1-src/img/collect-best-practices/require-authentication.png diff --git a/src/img/collect-best-practices/screen-lock.png b/odk1-src/img/collect-best-practices/screen-lock.png similarity index 100% rename from src/img/collect-best-practices/screen-lock.png rename to odk1-src/img/collect-best-practices/screen-lock.png diff --git a/src/img/collect-best-practices/screen-timeout.png b/odk1-src/img/collect-best-practices/screen-timeout.png similarity index 100% rename from src/img/collect-best-practices/screen-timeout.png rename to odk1-src/img/collect-best-practices/screen-timeout.png diff --git a/src/img/collect-best-practices/security.png b/odk1-src/img/collect-best-practices/security.png similarity index 100% rename from src/img/collect-best-practices/security.png rename to odk1-src/img/collect-best-practices/security.png diff --git a/src/img/collect-best-practices/set-lock-automatic.png b/odk1-src/img/collect-best-practices/set-lock-automatic.png similarity index 100% rename from src/img/collect-best-practices/set-lock-automatic.png rename to odk1-src/img/collect-best-practices/set-lock-automatic.png diff --git a/src/img/collect-best-practices/set-timeout.png b/odk1-src/img/collect-best-practices/set-timeout.png similarity index 100% rename from src/img/collect-best-practices/set-timeout.png rename to odk1-src/img/collect-best-practices/set-timeout.png diff --git a/src/img/collect-best-practices/settings.png b/odk1-src/img/collect-best-practices/settings.png similarity index 100% rename from src/img/collect-best-practices/settings.png rename to odk1-src/img/collect-best-practices/settings.png diff --git a/src/img/collect-best-practices/sound-notification.png b/odk1-src/img/collect-best-practices/sound-notification.png similarity index 100% rename from src/img/collect-best-practices/sound-notification.png rename to odk1-src/img/collect-best-practices/sound-notification.png diff --git a/src/img/collect-best-practices/turn-off-ok-google.png b/odk1-src/img/collect-best-practices/turn-off-ok-google.png similarity index 100% rename from src/img/collect-best-practices/turn-off-ok-google.png rename to odk1-src/img/collect-best-practices/turn-off-ok-google.png diff --git a/src/img/collect-best-practices/unknown-source.png b/odk1-src/img/collect-best-practices/unknown-source.png similarity index 100% rename from src/img/collect-best-practices/unknown-source.png rename to odk1-src/img/collect-best-practices/unknown-source.png diff --git a/src/img/collect-best-practices/update-info.png b/odk1-src/img/collect-best-practices/update-info.png similarity index 100% rename from src/img/collect-best-practices/update-info.png rename to odk1-src/img/collect-best-practices/update-info.png diff --git a/src/img/collect-best-practices/warning-message.png b/odk1-src/img/collect-best-practices/warning-message.png similarity index 100% rename from src/img/collect-best-practices/warning-message.png rename to odk1-src/img/collect-best-practices/warning-message.png diff --git a/src/img/collect-best-practices/widgets.png b/odk1-src/img/collect-best-practices/widgets.png similarity index 100% rename from src/img/collect-best-practices/widgets.png rename to odk1-src/img/collect-best-practices/widgets.png diff --git a/src/img/collect-completing-forms/choose-language.png b/odk1-src/img/collect-completing-forms/choose-language.png similarity index 100% rename from src/img/collect-completing-forms/choose-language.png rename to odk1-src/img/collect-completing-forms/choose-language.png diff --git a/src/img/collect-completing-forms/fill-blank-forms.png b/odk1-src/img/collect-completing-forms/fill-blank-forms.png similarity index 100% rename from src/img/collect-completing-forms/fill-blank-forms.png rename to odk1-src/img/collect-completing-forms/fill-blank-forms.png diff --git a/src/img/collect-completing-forms/general-settings-highlight-user-interface.png b/odk1-src/img/collect-completing-forms/general-settings-highlight-user-interface.png similarity index 100% rename from src/img/collect-completing-forms/general-settings-highlight-user-interface.png rename to odk1-src/img/collect-completing-forms/general-settings-highlight-user-interface.png diff --git a/src/img/collect-completing-forms/geopoint-widget.png b/odk1-src/img/collect-completing-forms/geopoint-widget.png similarity index 100% rename from src/img/collect-completing-forms/geopoint-widget.png rename to odk1-src/img/collect-completing-forms/geopoint-widget.png diff --git a/src/img/collect-completing-forms/image-widget.png b/odk1-src/img/collect-completing-forms/image-widget.png similarity index 100% rename from src/img/collect-completing-forms/image-widget.png rename to odk1-src/img/collect-completing-forms/image-widget.png diff --git a/src/img/collect-completing-forms/long-press-to-remove-0.png b/odk1-src/img/collect-completing-forms/long-press-to-remove-0.png similarity index 100% rename from src/img/collect-completing-forms/long-press-to-remove-0.png rename to odk1-src/img/collect-completing-forms/long-press-to-remove-0.png diff --git a/src/img/collect-completing-forms/long-press-to-remove-1.png b/odk1-src/img/collect-completing-forms/long-press-to-remove-1.png similarity index 100% rename from src/img/collect-completing-forms/long-press-to-remove-1.png rename to odk1-src/img/collect-completing-forms/long-press-to-remove-1.png diff --git a/src/img/collect-completing-forms/long-press-to-remove-2.png b/odk1-src/img/collect-completing-forms/long-press-to-remove-2.png similarity index 100% rename from src/img/collect-completing-forms/long-press-to-remove-2.png rename to odk1-src/img/collect-completing-forms/long-press-to-remove-2.png diff --git a/src/img/collect-completing-forms/long-press-to-remove-3.png b/odk1-src/img/collect-completing-forms/long-press-to-remove-3.png similarity index 100% rename from src/img/collect-completing-forms/long-press-to-remove-3.png rename to odk1-src/img/collect-completing-forms/long-press-to-remove-3.png diff --git a/src/img/collect-completing-forms/long-press-to-remove-4.png b/odk1-src/img/collect-completing-forms/long-press-to-remove-4.png similarity index 100% rename from src/img/collect-completing-forms/long-press-to-remove-4.png rename to odk1-src/img/collect-completing-forms/long-press-to-remove-4.png diff --git a/src/img/collect-completing-forms/long-press-to-remove-5.png b/odk1-src/img/collect-completing-forms/long-press-to-remove-5.png similarity index 100% rename from src/img/collect-completing-forms/long-press-to-remove-5.png rename to odk1-src/img/collect-completing-forms/long-press-to-remove-5.png diff --git a/src/img/collect-completing-forms/long-press-to-remove-6.png b/odk1-src/img/collect-completing-forms/long-press-to-remove-6.png similarity index 100% rename from src/img/collect-completing-forms/long-press-to-remove-6.png rename to odk1-src/img/collect-completing-forms/long-press-to-remove-6.png diff --git a/src/img/collect-completing-forms/long-press-to-remove-7.png b/odk1-src/img/collect-completing-forms/long-press-to-remove-7.png similarity index 100% rename from src/img/collect-completing-forms/long-press-to-remove-7.png rename to odk1-src/img/collect-completing-forms/long-press-to-remove-7.png diff --git a/src/img/collect-completing-forms/long-press-to-remove-8.png b/odk1-src/img/collect-completing-forms/long-press-to-remove-8.png similarity index 100% rename from src/img/collect-completing-forms/long-press-to-remove-8.png rename to odk1-src/img/collect-completing-forms/long-press-to-remove-8.png diff --git a/src/img/collect-completing-forms/long-press-to-remove-9.png b/odk1-src/img/collect-completing-forms/long-press-to-remove-9.png similarity index 100% rename from src/img/collect-completing-forms/long-press-to-remove-9.png rename to odk1-src/img/collect-completing-forms/long-press-to-remove-9.png diff --git a/src/img/collect-completing-forms/long-press-to-remove.gif b/odk1-src/img/collect-completing-forms/long-press-to-remove.gif similarity index 100% rename from src/img/collect-completing-forms/long-press-to-remove.gif rename to odk1-src/img/collect-completing-forms/long-press-to-remove.gif diff --git a/src/img/collect-completing-forms/long-press-to-remove.png b/odk1-src/img/collect-completing-forms/long-press-to-remove.png similarity index 100% rename from src/img/collect-completing-forms/long-press-to-remove.png rename to odk1-src/img/collect-completing-forms/long-press-to-remove.png diff --git a/src/img/collect-completing-forms/main-menu-fill-blank-form.png b/odk1-src/img/collect-completing-forms/main-menu-fill-blank-form.png similarity index 100% rename from src/img/collect-completing-forms/main-menu-fill-blank-form.png rename to odk1-src/img/collect-completing-forms/main-menu-fill-blank-form.png diff --git a/src/img/collect-completing-forms/mark-form-as-finalized.png b/odk1-src/img/collect-completing-forms/mark-form-as-finalized.png similarity index 100% rename from src/img/collect-completing-forms/mark-form-as-finalized.png rename to odk1-src/img/collect-completing-forms/mark-form-as-finalized.png diff --git a/src/img/collect-completing-forms/multi-select-0.png b/odk1-src/img/collect-completing-forms/multi-select-0.png similarity index 100% rename from src/img/collect-completing-forms/multi-select-0.png rename to odk1-src/img/collect-completing-forms/multi-select-0.png diff --git a/src/img/collect-completing-forms/multi-select-1.png b/odk1-src/img/collect-completing-forms/multi-select-1.png similarity index 100% rename from src/img/collect-completing-forms/multi-select-1.png rename to odk1-src/img/collect-completing-forms/multi-select-1.png diff --git a/src/img/collect-completing-forms/multi-select-1.xcf b/odk1-src/img/collect-completing-forms/multi-select-1.xcf similarity index 100% rename from src/img/collect-completing-forms/multi-select-1.xcf rename to odk1-src/img/collect-completing-forms/multi-select-1.xcf diff --git a/src/img/collect-completing-forms/multi-select-2.png b/odk1-src/img/collect-completing-forms/multi-select-2.png similarity index 100% rename from src/img/collect-completing-forms/multi-select-2.png rename to odk1-src/img/collect-completing-forms/multi-select-2.png diff --git a/src/img/collect-completing-forms/multi-select-2.xcf b/odk1-src/img/collect-completing-forms/multi-select-2.xcf similarity index 100% rename from src/img/collect-completing-forms/multi-select-2.xcf rename to odk1-src/img/collect-completing-forms/multi-select-2.xcf diff --git a/src/img/collect-completing-forms/multi-select.gif b/odk1-src/img/collect-completing-forms/multi-select.gif similarity index 100% rename from src/img/collect-completing-forms/multi-select.gif rename to odk1-src/img/collect-completing-forms/multi-select.gif diff --git a/src/img/collect-completing-forms/multi-select.png b/odk1-src/img/collect-completing-forms/multi-select.png similarity index 100% rename from src/img/collect-completing-forms/multi-select.png rename to odk1-src/img/collect-completing-forms/multi-select.png diff --git a/src/img/collect-completing-forms/question-screen-highlight-change-language.png b/odk1-src/img/collect-completing-forms/question-screen-highlight-change-language.png similarity index 100% rename from src/img/collect-completing-forms/question-screen-highlight-change-language.png rename to odk1-src/img/collect-completing-forms/question-screen-highlight-change-language.png diff --git a/src/img/collect-completing-forms/question-screen-highlight-general-settings.png b/odk1-src/img/collect-completing-forms/question-screen-highlight-general-settings.png similarity index 100% rename from src/img/collect-completing-forms/question-screen-highlight-general-settings.png rename to odk1-src/img/collect-completing-forms/question-screen-highlight-general-settings.png diff --git a/src/img/collect-completing-forms/question-screen-highlight-kebab.png b/odk1-src/img/collect-completing-forms/question-screen-highlight-kebab.png similarity index 100% rename from src/img/collect-completing-forms/question-screen-highlight-kebab.png rename to odk1-src/img/collect-completing-forms/question-screen-highlight-kebab.png diff --git a/src/img/collect-completing-forms/question-screen-with-buttons.png b/odk1-src/img/collect-completing-forms/question-screen-with-buttons.png similarity index 100% rename from src/img/collect-completing-forms/question-screen-with-buttons.png rename to odk1-src/img/collect-completing-forms/question-screen-with-buttons.png diff --git a/src/img/collect-completing-forms/rename-form-0.png b/odk1-src/img/collect-completing-forms/rename-form-0.png similarity index 100% rename from src/img/collect-completing-forms/rename-form-0.png rename to odk1-src/img/collect-completing-forms/rename-form-0.png diff --git a/src/img/collect-completing-forms/rename-form-1.png b/odk1-src/img/collect-completing-forms/rename-form-1.png similarity index 100% rename from src/img/collect-completing-forms/rename-form-1.png rename to odk1-src/img/collect-completing-forms/rename-form-1.png diff --git a/src/img/collect-completing-forms/rename-form-2.png b/odk1-src/img/collect-completing-forms/rename-form-2.png similarity index 100% rename from src/img/collect-completing-forms/rename-form-2.png rename to odk1-src/img/collect-completing-forms/rename-form-2.png diff --git a/src/img/collect-completing-forms/rename-form-3.png b/odk1-src/img/collect-completing-forms/rename-form-3.png similarity index 100% rename from src/img/collect-completing-forms/rename-form-3.png rename to odk1-src/img/collect-completing-forms/rename-form-3.png diff --git a/src/img/collect-completing-forms/rename-form-4.png b/odk1-src/img/collect-completing-forms/rename-form-4.png similarity index 100% rename from src/img/collect-completing-forms/rename-form-4.png rename to odk1-src/img/collect-completing-forms/rename-form-4.png diff --git a/src/img/collect-completing-forms/rename-form-5.png b/odk1-src/img/collect-completing-forms/rename-form-5.png similarity index 100% rename from src/img/collect-completing-forms/rename-form-5.png rename to odk1-src/img/collect-completing-forms/rename-form-5.png diff --git a/src/img/collect-completing-forms/rename-form-6.png b/odk1-src/img/collect-completing-forms/rename-form-6.png similarity index 100% rename from src/img/collect-completing-forms/rename-form-6.png rename to odk1-src/img/collect-completing-forms/rename-form-6.png diff --git a/src/img/collect-completing-forms/rename-form-7.png b/odk1-src/img/collect-completing-forms/rename-form-7.png similarity index 100% rename from src/img/collect-completing-forms/rename-form-7.png rename to odk1-src/img/collect-completing-forms/rename-form-7.png diff --git a/src/img/collect-completing-forms/rename-form.gif b/odk1-src/img/collect-completing-forms/rename-form.gif similarity index 100% rename from src/img/collect-completing-forms/rename-form.gif rename to odk1-src/img/collect-completing-forms/rename-form.gif diff --git a/src/img/collect-completing-forms/rename-form.png b/odk1-src/img/collect-completing-forms/rename-form.png similarity index 100% rename from src/img/collect-completing-forms/rename-form.png rename to odk1-src/img/collect-completing-forms/rename-form.png diff --git a/src/img/collect-completing-forms/save-and-exit.png b/odk1-src/img/collect-completing-forms/save-and-exit.png similarity index 100% rename from src/img/collect-completing-forms/save-and-exit.png rename to odk1-src/img/collect-completing-forms/save-and-exit.png diff --git a/src/img/collect-completing-forms/save-partial-filled-form.png b/odk1-src/img/collect-completing-forms/save-partial-filled-form.png similarity index 100% rename from src/img/collect-completing-forms/save-partial-filled-form.png rename to odk1-src/img/collect-completing-forms/save-partial-filled-form.png diff --git a/src/img/collect-completing-forms/saveicon.png b/odk1-src/img/collect-completing-forms/saveicon.png similarity index 100% rename from src/img/collect-completing-forms/saveicon.png rename to odk1-src/img/collect-completing-forms/saveicon.png diff --git a/src/img/collect-completing-forms/select-image.png b/odk1-src/img/collect-completing-forms/select-image.png similarity index 100% rename from src/img/collect-completing-forms/select-image.png rename to odk1-src/img/collect-completing-forms/select-image.png diff --git a/src/img/collect-completing-forms/signature-widget.png b/odk1-src/img/collect-completing-forms/signature-widget.png similarity index 100% rename from src/img/collect-completing-forms/signature-widget.png rename to odk1-src/img/collect-completing-forms/signature-widget.png diff --git a/src/img/collect-completing-forms/single-select.png b/odk1-src/img/collect-completing-forms/single-select.png similarity index 100% rename from src/img/collect-completing-forms/single-select.png rename to odk1-src/img/collect-completing-forms/single-select.png diff --git a/src/img/collect-completing-forms/single-select.xcf b/odk1-src/img/collect-completing-forms/single-select.xcf similarity index 100% rename from src/img/collect-completing-forms/single-select.xcf rename to odk1-src/img/collect-completing-forms/single-select.xcf diff --git a/src/img/collect-completing-forms/swiping.png b/odk1-src/img/collect-completing-forms/swiping.png similarity index 100% rename from src/img/collect-completing-forms/swiping.png rename to odk1-src/img/collect-completing-forms/swiping.png diff --git a/src/img/collect-completing-forms/ui-navigation-buttons.png b/odk1-src/img/collect-completing-forms/ui-navigation-buttons.png similarity index 100% rename from src/img/collect-completing-forms/ui-navigation-buttons.png rename to odk1-src/img/collect-completing-forms/ui-navigation-buttons.png diff --git a/src/img/collect-completing-forms/user-interface-highlight-navigation.png b/odk1-src/img/collect-completing-forms/user-interface-highlight-navigation.png similarity index 100% rename from src/img/collect-completing-forms/user-interface-highlight-navigation.png rename to odk1-src/img/collect-completing-forms/user-interface-highlight-navigation.png diff --git a/src/img/collect-completing-forms/video-widget.png b/odk1-src/img/collect-completing-forms/video-widget.png similarity index 100% rename from src/img/collect-completing-forms/video-widget.png rename to odk1-src/img/collect-completing-forms/video-widget.png diff --git a/src/img/collect-connect/general-settings-server.png b/odk1-src/img/collect-connect/general-settings-server.png similarity index 100% rename from src/img/collect-connect/general-settings-server.png rename to odk1-src/img/collect-connect/general-settings-server.png diff --git a/src/img/collect-connect/kebab-menu-general-settings.png b/odk1-src/img/collect-connect/kebab-menu-general-settings.png similarity index 100% rename from src/img/collect-connect/kebab-menu-general-settings.png rename to odk1-src/img/collect-connect/kebab-menu-general-settings.png diff --git a/src/img/collect-connect/main-menu-highlight-kebab.png b/odk1-src/img/collect-connect/main-menu-highlight-kebab.png similarity index 100% rename from src/img/collect-connect/main-menu-highlight-kebab.png rename to odk1-src/img/collect-connect/main-menu-highlight-kebab.png diff --git a/src/img/collect-connect/server-settings-aggregate-settings.png b/odk1-src/img/collect-connect/server-settings-aggregate-settings.png similarity index 100% rename from src/img/collect-connect/server-settings-aggregate-settings.png rename to odk1-src/img/collect-connect/server-settings-aggregate-settings.png diff --git a/src/img/collect-connect/server-settings-google-account-modal.png b/odk1-src/img/collect-connect/server-settings-google-account-modal.png similarity index 100% rename from src/img/collect-connect/server-settings-google-account-modal.png rename to odk1-src/img/collect-connect/server-settings-google-account-modal.png diff --git a/src/img/collect-connect/server-settings-google-account.png b/odk1-src/img/collect-connect/server-settings-google-account.png similarity index 100% rename from src/img/collect-connect/server-settings-google-account.png rename to odk1-src/img/collect-connect/server-settings-google-account.png diff --git a/src/img/collect-connect/server-settings-odk-password.png b/odk1-src/img/collect-connect/server-settings-odk-password.png similarity index 100% rename from src/img/collect-connect/server-settings-odk-password.png rename to odk1-src/img/collect-connect/server-settings-odk-password.png diff --git a/src/img/collect-connect/server-settings-odk-username.png b/odk1-src/img/collect-connect/server-settings-odk-username.png similarity index 100% rename from src/img/collect-connect/server-settings-odk-username.png rename to odk1-src/img/collect-connect/server-settings-odk-username.png diff --git a/src/img/collect-connect/server-settings-server-url.png b/odk1-src/img/collect-connect/server-settings-server-url.png similarity index 100% rename from src/img/collect-connect/server-settings-server-url.png rename to odk1-src/img/collect-connect/server-settings-server-url.png diff --git a/src/img/collect-connect/server-settings-type-aggregate.png b/odk1-src/img/collect-connect/server-settings-type-aggregate.png similarity index 100% rename from src/img/collect-connect/server-settings-type-aggregate.png rename to odk1-src/img/collect-connect/server-settings-type-aggregate.png diff --git a/src/img/collect-connect/server-settings-type-google.png b/odk1-src/img/collect-connect/server-settings-type-google.png similarity index 100% rename from src/img/collect-connect/server-settings-type-google.png rename to odk1-src/img/collect-connect/server-settings-type-google.png diff --git a/src/img/collect-connect/server-settings-type-modal.png b/odk1-src/img/collect-connect/server-settings-type-modal.png similarity index 100% rename from src/img/collect-connect/server-settings-type-modal.png rename to odk1-src/img/collect-connect/server-settings-type-modal.png diff --git a/src/img/collect-connect/server-settings-type-model-google.png b/odk1-src/img/collect-connect/server-settings-type-model-google.png similarity index 100% rename from src/img/collect-connect/server-settings-type-model-google.png rename to odk1-src/img/collect-connect/server-settings-type-model-google.png diff --git a/src/img/collect-forms/delete-saved-forms-blank-forms.png b/odk1-src/img/collect-forms/delete-saved-forms-blank-forms.png similarity index 100% rename from src/img/collect-forms/delete-saved-forms-blank-forms.png rename to odk1-src/img/collect-forms/delete-saved-forms-blank-forms.png diff --git a/src/img/collect-forms/delete-saved-forms.png b/odk1-src/img/collect-forms/delete-saved-forms.png similarity index 100% rename from src/img/collect-forms/delete-saved-forms.png rename to odk1-src/img/collect-forms/delete-saved-forms.png diff --git a/src/img/collect-forms/deleted-form-in-view-sent-form.png b/odk1-src/img/collect-forms/deleted-form-in-view-sent-form.png similarity index 100% rename from src/img/collect-forms/deleted-form-in-view-sent-form.png rename to odk1-src/img/collect-forms/deleted-form-in-view-sent-form.png diff --git a/src/img/collect-forms/device-settings-storage-explore.png b/odk1-src/img/collect-forms/device-settings-storage-explore.png similarity index 100% rename from src/img/collect-forms/device-settings-storage-explore.png rename to odk1-src/img/collect-forms/device-settings-storage-explore.png diff --git a/src/img/collect-forms/device-settings-storage.png b/odk1-src/img/collect-forms/device-settings-storage.png similarity index 100% rename from src/img/collect-forms/device-settings-storage.png rename to odk1-src/img/collect-forms/device-settings-storage.png diff --git a/src/img/collect-forms/downloading-not-a-form.png b/odk1-src/img/collect-forms/downloading-not-a-form.png similarity index 100% rename from src/img/collect-forms/downloading-not-a-form.png rename to odk1-src/img/collect-forms/downloading-not-a-form.png diff --git a/src/img/collect-forms/edit-saved-form.png b/odk1-src/img/collect-forms/edit-saved-form.png similarity index 100% rename from src/img/collect-forms/edit-saved-form.png rename to odk1-src/img/collect-forms/edit-saved-form.png diff --git a/src/img/collect-forms/get-blank-form-not-a-form.png b/odk1-src/img/collect-forms/get-blank-form-not-a-form.png similarity index 100% rename from src/img/collect-forms/get-blank-form-not-a-form.png rename to odk1-src/img/collect-forms/get-blank-form-not-a-form.png diff --git a/src/img/collect-forms/get-blank-form.png b/odk1-src/img/collect-forms/get-blank-form.png similarity index 100% rename from src/img/collect-forms/get-blank-form.png rename to odk1-src/img/collect-forms/get-blank-form.png diff --git a/src/img/collect-forms/get-forms-google.png b/odk1-src/img/collect-forms/get-forms-google.png similarity index 100% rename from src/img/collect-forms/get-forms-google.png rename to odk1-src/img/collect-forms/get-forms-google.png diff --git a/src/img/collect-forms/jumpicon.png b/odk1-src/img/collect-forms/jumpicon.png similarity index 100% rename from src/img/collect-forms/jumpicon.png rename to odk1-src/img/collect-forms/jumpicon.png diff --git a/src/img/collect-forms/jumpmenu.png b/odk1-src/img/collect-forms/jumpmenu.png similarity index 100% rename from src/img/collect-forms/jumpmenu.png rename to odk1-src/img/collect-forms/jumpmenu.png diff --git a/src/img/collect-forms/jumpscreen.png b/odk1-src/img/collect-forms/jumpscreen.png similarity index 100% rename from src/img/collect-forms/jumpscreen.png rename to odk1-src/img/collect-forms/jumpscreen.png diff --git a/src/img/collect-forms/main-menu-delete-form.png b/odk1-src/img/collect-forms/main-menu-delete-form.png similarity index 100% rename from src/img/collect-forms/main-menu-delete-form.png rename to odk1-src/img/collect-forms/main-menu-delete-form.png diff --git a/src/img/collect-forms/main-menu-edit-saved.png b/odk1-src/img/collect-forms/main-menu-edit-saved.png similarity index 100% rename from src/img/collect-forms/main-menu-edit-saved.png rename to odk1-src/img/collect-forms/main-menu-edit-saved.png diff --git a/src/img/collect-forms/main-menu-highlight-get-blank-form.png b/odk1-src/img/collect-forms/main-menu-highlight-get-blank-form.png similarity index 100% rename from src/img/collect-forms/main-menu-highlight-get-blank-form.png rename to odk1-src/img/collect-forms/main-menu-highlight-get-blank-form.png diff --git a/src/img/collect-forms/main-menu-send-finalized.png b/odk1-src/img/collect-forms/main-menu-send-finalized.png similarity index 100% rename from src/img/collect-forms/main-menu-send-finalized.png rename to odk1-src/img/collect-forms/main-menu-send-finalized.png diff --git a/src/img/collect-forms/not-form-exception.png b/odk1-src/img/collect-forms/not-form-exception.png similarity index 100% rename from src/img/collect-forms/not-form-exception.png rename to odk1-src/img/collect-forms/not-form-exception.png diff --git a/src/img/collect-settings/admin-settings.jpeg b/odk1-src/img/collect-settings/admin-settings.jpeg similarity index 100% rename from src/img/collect-settings/admin-settings.jpeg rename to odk1-src/img/collect-settings/admin-settings.jpeg diff --git a/src/img/collect-settings/form-entry-settings.png b/odk1-src/img/collect-settings/form-entry-settings.png similarity index 100% rename from src/img/collect-settings/form-entry-settings.png rename to odk1-src/img/collect-settings/form-entry-settings.png diff --git a/src/img/collect-settings/form-management.jpeg b/odk1-src/img/collect-settings/form-management.jpeg similarity index 100% rename from src/img/collect-settings/form-management.jpeg rename to odk1-src/img/collect-settings/form-management.jpeg diff --git a/src/img/collect-settings/form-management.png b/odk1-src/img/collect-settings/form-management.png similarity index 100% rename from src/img/collect-settings/form-management.png rename to odk1-src/img/collect-settings/form-management.png diff --git a/src/img/collect-settings/form-management2.png b/odk1-src/img/collect-settings/form-management2.png similarity index 100% rename from src/img/collect-settings/form-management2.png rename to odk1-src/img/collect-settings/form-management2.png diff --git a/src/img/collect-settings/form-metadata.png b/odk1-src/img/collect-settings/form-metadata.png similarity index 100% rename from src/img/collect-settings/form-metadata.png rename to odk1-src/img/collect-settings/form-metadata.png diff --git a/src/img/collect-settings/general-settings.jpeg b/odk1-src/img/collect-settings/general-settings.jpeg similarity index 100% rename from src/img/collect-settings/general-settings.jpeg rename to odk1-src/img/collect-settings/general-settings.jpeg diff --git a/src/img/collect-settings/image-settings.png b/odk1-src/img/collect-settings/image-settings.png similarity index 100% rename from src/img/collect-settings/image-settings.png rename to odk1-src/img/collect-settings/image-settings.png diff --git a/src/img/collect-settings/import-settings.jpg b/odk1-src/img/collect-settings/import-settings.jpg similarity index 100% rename from src/img/collect-settings/import-settings.jpg rename to odk1-src/img/collect-settings/import-settings.jpg diff --git a/src/img/collect-settings/main-menu-settings.png b/odk1-src/img/collect-settings/main-menu-settings.png similarity index 100% rename from src/img/collect-settings/main-menu-settings.png rename to odk1-src/img/collect-settings/main-menu-settings.png diff --git a/src/img/collect-settings/main-menu.jpeg b/odk1-src/img/collect-settings/main-menu.jpeg similarity index 100% rename from src/img/collect-settings/main-menu.jpeg rename to odk1-src/img/collect-settings/main-menu.jpeg diff --git a/src/img/collect-settings/moving-backwards-disabled.png b/odk1-src/img/collect-settings/moving-backwards-disabled.png similarity index 100% rename from src/img/collect-settings/moving-backwards-disabled.png rename to odk1-src/img/collect-settings/moving-backwards-disabled.png diff --git a/src/img/collect-settings/moving-backwards-enabled.png b/odk1-src/img/collect-settings/moving-backwards-enabled.png similarity index 100% rename from src/img/collect-settings/moving-backwards-enabled.png rename to odk1-src/img/collect-settings/moving-backwards-enabled.png diff --git a/src/img/collect-settings/server-settings.jpeg b/odk1-src/img/collect-settings/server-settings.jpeg similarity index 100% rename from src/img/collect-settings/server-settings.jpeg rename to odk1-src/img/collect-settings/server-settings.jpeg diff --git a/src/img/collect-settings/share-icon.jpg b/odk1-src/img/collect-settings/share-icon.jpg similarity index 100% rename from src/img/collect-settings/share-icon.jpg rename to odk1-src/img/collect-settings/share-icon.jpg diff --git a/src/img/collect-settings/ui-settings.jpeg b/odk1-src/img/collect-settings/ui-settings.jpeg similarity index 100% rename from src/img/collect-settings/ui-settings.jpeg rename to odk1-src/img/collect-settings/ui-settings.jpeg diff --git a/src/img/collect-settings/und-settings.jpeg b/odk1-src/img/collect-settings/und-settings.jpeg similarity index 100% rename from src/img/collect-settings/und-settings.jpeg rename to odk1-src/img/collect-settings/und-settings.jpeg diff --git a/src/img/collect-settings/user-settings.png b/odk1-src/img/collect-settings/user-settings.png similarity index 100% rename from src/img/collect-settings/user-settings.png rename to odk1-src/img/collect-settings/user-settings.png diff --git a/src/img/collect-settings/user-settings2.png b/odk1-src/img/collect-settings/user-settings2.png similarity index 100% rename from src/img/collect-settings/user-settings2.png rename to odk1-src/img/collect-settings/user-settings2.png diff --git a/src/img/form-update/get-new-version.png b/odk1-src/img/form-update/get-new-version.png similarity index 100% rename from src/img/form-update/get-new-version.png rename to odk1-src/img/form-update/get-new-version.png diff --git a/src/img/form-update/two-version-form.png b/odk1-src/img/form-update/two-version-form.png similarity index 100% rename from src/img/form-update/two-version-form.png rename to odk1-src/img/form-update/two-version-form.png diff --git a/src/img/form-update/update-error.png b/odk1-src/img/form-update/update-error.png similarity index 100% rename from src/img/form-update/update-error.png rename to odk1-src/img/form-update/update-error.png diff --git a/src/img/form-widgets/acknowledge.png b/odk1-src/img/form-widgets/acknowledge.png similarity index 100% rename from src/img/form-widgets/acknowledge.png rename to odk1-src/img/form-widgets/acknowledge.png diff --git a/src/img/form-widgets/annotate-1.png b/odk1-src/img/form-widgets/annotate-1.png similarity index 100% rename from src/img/form-widgets/annotate-1.png rename to odk1-src/img/form-widgets/annotate-1.png diff --git a/src/img/form-widgets/annotate-2.png b/odk1-src/img/form-widgets/annotate-2.png similarity index 100% rename from src/img/form-widgets/annotate-2.png rename to odk1-src/img/form-widgets/annotate-2.png diff --git a/src/img/form-widgets/annotate-3.png b/odk1-src/img/form-widgets/annotate-3.png similarity index 100% rename from src/img/form-widgets/annotate-3.png rename to odk1-src/img/form-widgets/annotate-3.png diff --git a/src/img/form-widgets/annotate-4.png b/odk1-src/img/form-widgets/annotate-4.png similarity index 100% rename from src/img/form-widgets/annotate-4.png rename to odk1-src/img/form-widgets/annotate-4.png diff --git a/src/img/form-widgets/annotate-5.png b/odk1-src/img/form-widgets/annotate-5.png similarity index 100% rename from src/img/form-widgets/annotate-5.png rename to odk1-src/img/form-widgets/annotate-5.png diff --git a/src/img/form-widgets/annotate-start.png b/odk1-src/img/form-widgets/annotate-start.png similarity index 100% rename from src/img/form-widgets/annotate-start.png rename to odk1-src/img/form-widgets/annotate-start.png diff --git a/src/img/form-widgets/audio-start.png b/odk1-src/img/form-widgets/audio-start.png similarity index 100% rename from src/img/form-widgets/audio-start.png rename to odk1-src/img/form-widgets/audio-start.png diff --git a/src/img/form-widgets/barcode-start.png b/odk1-src/img/form-widgets/barcode-start.png similarity index 100% rename from src/img/form-widgets/barcode-start.png rename to odk1-src/img/form-widgets/barcode-start.png diff --git a/src/img/form-widgets/barcode.jpg b/odk1-src/img/form-widgets/barcode.jpg similarity index 100% rename from src/img/form-widgets/barcode.jpg rename to odk1-src/img/form-widgets/barcode.jpg diff --git a/src/img/form-widgets/barcode1.png b/odk1-src/img/form-widgets/barcode1.png similarity index 100% rename from src/img/form-widgets/barcode1.png rename to odk1-src/img/form-widgets/barcode1.png diff --git a/src/img/form-widgets/barcode2.png b/odk1-src/img/form-widgets/barcode2.png similarity index 100% rename from src/img/form-widgets/barcode2.png rename to odk1-src/img/form-widgets/barcode2.png diff --git a/src/img/form-widgets/bearing-finished.png b/odk1-src/img/form-widgets/bearing-finished.png similarity index 100% rename from src/img/form-widgets/bearing-finished.png rename to odk1-src/img/form-widgets/bearing-finished.png diff --git a/src/img/form-widgets/bearing-in-progress.png b/odk1-src/img/form-widgets/bearing-in-progress.png similarity index 100% rename from src/img/form-widgets/bearing-in-progress.png rename to odk1-src/img/form-widgets/bearing-in-progress.png diff --git a/src/img/form-widgets/bearing-widget-start.png b/odk1-src/img/form-widgets/bearing-widget-start.png similarity index 100% rename from src/img/form-widgets/bearing-widget-start.png rename to odk1-src/img/form-widgets/bearing-widget-start.png diff --git a/src/img/form-widgets/compact-multiselect-selected.png b/odk1-src/img/form-widgets/compact-multiselect-selected.png similarity index 100% rename from src/img/form-widgets/compact-multiselect-selected.png rename to odk1-src/img/form-widgets/compact-multiselect-selected.png diff --git a/src/img/form-widgets/compact-multiselect.png b/odk1-src/img/form-widgets/compact-multiselect.png similarity index 100% rename from src/img/form-widgets/compact-multiselect.png rename to odk1-src/img/form-widgets/compact-multiselect.png diff --git a/src/img/form-widgets/date-calendar-view.png b/odk1-src/img/form-widgets/date-calendar-view.png similarity index 100% rename from src/img/form-widgets/date-calendar-view.png rename to odk1-src/img/form-widgets/date-calendar-view.png diff --git a/src/img/form-widgets/date-completed.png b/odk1-src/img/form-widgets/date-completed.png similarity index 100% rename from src/img/form-widgets/date-completed.png rename to odk1-src/img/form-widgets/date-completed.png diff --git a/src/img/form-widgets/date-no-calendar-in-progress.png b/odk1-src/img/form-widgets/date-no-calendar-in-progress.png similarity index 100% rename from src/img/form-widgets/date-no-calendar-in-progress.png rename to odk1-src/img/form-widgets/date-no-calendar-in-progress.png diff --git a/src/img/form-widgets/date-no-calendar-start.png b/odk1-src/img/form-widgets/date-no-calendar-start.png similarity index 100% rename from src/img/form-widgets/date-no-calendar-start.png rename to odk1-src/img/form-widgets/date-no-calendar-start.png diff --git a/src/img/form-widgets/date-start.png b/odk1-src/img/form-widgets/date-start.png similarity index 100% rename from src/img/form-widgets/date-start.png rename to odk1-src/img/form-widgets/date-start.png diff --git a/src/img/form-widgets/date1.png b/odk1-src/img/form-widgets/date1.png similarity index 100% rename from src/img/form-widgets/date1.png rename to odk1-src/img/form-widgets/date1.png diff --git a/src/img/form-widgets/date2.png b/odk1-src/img/form-widgets/date2.png similarity index 100% rename from src/img/form-widgets/date2.png rename to odk1-src/img/form-widgets/date2.png diff --git a/src/img/form-widgets/datetime-start.png b/odk1-src/img/form-widgets/datetime-start.png similarity index 100% rename from src/img/form-widgets/datetime-start.png rename to odk1-src/img/form-widgets/datetime-start.png diff --git a/src/img/form-widgets/datetime1.png b/odk1-src/img/form-widgets/datetime1.png similarity index 100% rename from src/img/form-widgets/datetime1.png rename to odk1-src/img/form-widgets/datetime1.png diff --git a/src/img/form-widgets/datetime2.png b/odk1-src/img/form-widgets/datetime2.png similarity index 100% rename from src/img/form-widgets/datetime2.png rename to odk1-src/img/form-widgets/datetime2.png diff --git a/src/img/form-widgets/datetime3.png b/odk1-src/img/form-widgets/datetime3.png similarity index 100% rename from src/img/form-widgets/datetime3.png rename to odk1-src/img/form-widgets/datetime3.png diff --git a/src/img/form-widgets/datetime4.png b/odk1-src/img/form-widgets/datetime4.png similarity index 100% rename from src/img/form-widgets/datetime4.png rename to odk1-src/img/form-widgets/datetime4.png diff --git a/src/img/form-widgets/decimal.png b/odk1-src/img/form-widgets/decimal.png similarity index 100% rename from src/img/form-widgets/decimal.png rename to odk1-src/img/form-widgets/decimal.png diff --git a/src/img/form-widgets/default-date-widget.png b/odk1-src/img/form-widgets/default-date-widget.png similarity index 100% rename from src/img/form-widgets/default-date-widget.png rename to odk1-src/img/form-widgets/default-date-widget.png diff --git a/src/img/form-widgets/default-decimal-widget.png b/odk1-src/img/form-widgets/default-decimal-widget.png similarity index 100% rename from src/img/form-widgets/default-decimal-widget.png rename to odk1-src/img/form-widgets/default-decimal-widget.png diff --git a/src/img/form-widgets/default-geopoint.png b/odk1-src/img/form-widgets/default-geopoint.png similarity index 100% rename from src/img/form-widgets/default-geopoint.png rename to odk1-src/img/form-widgets/default-geopoint.png diff --git a/src/img/form-widgets/default-image-widget.png b/odk1-src/img/form-widgets/default-image-widget.png similarity index 100% rename from src/img/form-widgets/default-image-widget.png rename to odk1-src/img/form-widgets/default-image-widget.png diff --git a/src/img/form-widgets/default-integer-widget.png b/odk1-src/img/form-widgets/default-integer-widget.png similarity index 100% rename from src/img/form-widgets/default-integer-widget.png rename to odk1-src/img/form-widgets/default-integer-widget.png diff --git a/src/img/form-widgets/default-multiselect.png b/odk1-src/img/form-widgets/default-multiselect.png similarity index 100% rename from src/img/form-widgets/default-multiselect.png rename to odk1-src/img/form-widgets/default-multiselect.png diff --git a/src/img/form-widgets/default-single-image-select.png b/odk1-src/img/form-widgets/default-single-image-select.png similarity index 100% rename from src/img/form-widgets/default-single-image-select.png rename to odk1-src/img/form-widgets/default-single-image-select.png diff --git a/src/img/form-widgets/default-single-select.png b/odk1-src/img/form-widgets/default-single-select.png similarity index 100% rename from src/img/form-widgets/default-single-select.png rename to odk1-src/img/form-widgets/default-single-select.png diff --git a/src/img/form-widgets/draw-completed.png b/odk1-src/img/form-widgets/draw-completed.png similarity index 100% rename from src/img/form-widgets/draw-completed.png rename to odk1-src/img/form-widgets/draw-completed.png diff --git a/src/img/form-widgets/draw-in-progress.png b/odk1-src/img/form-widgets/draw-in-progress.png similarity index 100% rename from src/img/form-widgets/draw-in-progress.png rename to odk1-src/img/form-widgets/draw-in-progress.png diff --git a/src/img/form-widgets/draw-options.png b/odk1-src/img/form-widgets/draw-options.png similarity index 100% rename from src/img/form-widgets/draw-options.png rename to odk1-src/img/form-widgets/draw-options.png diff --git a/src/img/form-widgets/draw-widget.png b/odk1-src/img/form-widgets/draw-widget.png similarity index 100% rename from src/img/form-widgets/draw-widget.png rename to odk1-src/img/form-widgets/draw-widget.png diff --git a/src/img/form-widgets/ethiopian-start.png b/odk1-src/img/form-widgets/ethiopian-start.png similarity index 100% rename from src/img/form-widgets/ethiopian-start.png rename to odk1-src/img/form-widgets/ethiopian-start.png diff --git a/src/img/form-widgets/ethiopian2.png b/odk1-src/img/form-widgets/ethiopian2.png similarity index 100% rename from src/img/form-widgets/ethiopian2.png rename to odk1-src/img/form-widgets/ethiopian2.png diff --git a/src/img/form-widgets/ethiopian3.png b/odk1-src/img/form-widgets/ethiopian3.png similarity index 100% rename from src/img/form-widgets/ethiopian3.png rename to odk1-src/img/form-widgets/ethiopian3.png diff --git a/src/img/form-widgets/external-app-widget-fallback.png b/odk1-src/img/form-widgets/external-app-widget-fallback.png similarity index 100% rename from src/img/form-widgets/external-app-widget-fallback.png rename to odk1-src/img/form-widgets/external-app-widget-fallback.png diff --git a/src/img/form-widgets/external-app-widget-start.png b/odk1-src/img/form-widgets/external-app-widget-start.png similarity index 100% rename from src/img/form-widgets/external-app-widget-start.png rename to odk1-src/img/form-widgets/external-app-widget-start.png diff --git a/src/img/form-widgets/external-decimal-fallback.png b/odk1-src/img/form-widgets/external-decimal-fallback.png similarity index 100% rename from src/img/form-widgets/external-decimal-fallback.png rename to odk1-src/img/form-widgets/external-decimal-fallback.png diff --git a/src/img/form-widgets/external-decimal-start.png b/odk1-src/img/form-widgets/external-decimal-start.png similarity index 100% rename from src/img/form-widgets/external-decimal-start.png rename to odk1-src/img/form-widgets/external-decimal-start.png diff --git a/src/img/form-widgets/external-integer-widget-start.png b/odk1-src/img/form-widgets/external-integer-widget-start.png similarity index 100% rename from src/img/form-widgets/external-integer-widget-start.png rename to odk1-src/img/form-widgets/external-integer-widget-start.png diff --git a/src/img/form-widgets/external-widget-fallback.png b/odk1-src/img/form-widgets/external-widget-fallback.png similarity index 100% rename from src/img/form-widgets/external-widget-fallback.png rename to odk1-src/img/form-widgets/external-widget-fallback.png diff --git a/src/img/form-widgets/field-list-1.png b/odk1-src/img/form-widgets/field-list-1.png similarity index 100% rename from src/img/form-widgets/field-list-1.png rename to odk1-src/img/form-widgets/field-list-1.png diff --git a/src/img/form-widgets/field-list-2.png b/odk1-src/img/form-widgets/field-list-2.png similarity index 100% rename from src/img/form-widgets/field-list-2.png rename to odk1-src/img/form-widgets/field-list-2.png diff --git a/src/img/form-widgets/geopoint-completed.png b/odk1-src/img/form-widgets/geopoint-completed.png similarity index 100% rename from src/img/form-widgets/geopoint-completed.png rename to odk1-src/img/form-widgets/geopoint-completed.png diff --git a/src/img/form-widgets/geopoint-placement-map.png b/odk1-src/img/form-widgets/geopoint-placement-map.png similarity index 100% rename from src/img/form-widgets/geopoint-placement-map.png rename to odk1-src/img/form-widgets/geopoint-placement-map.png diff --git a/src/img/form-widgets/geopoint-start.png b/odk1-src/img/form-widgets/geopoint-start.png similarity index 100% rename from src/img/form-widgets/geopoint-start.png rename to odk1-src/img/form-widgets/geopoint-start.png diff --git a/src/img/form-widgets/geopoint-working.png b/odk1-src/img/form-widgets/geopoint-working.png similarity index 100% rename from src/img/form-widgets/geopoint-working.png rename to odk1-src/img/form-widgets/geopoint-working.png diff --git a/src/img/form-widgets/geoshape-start.png b/odk1-src/img/form-widgets/geoshape-start.png similarity index 100% rename from src/img/form-widgets/geoshape-start.png rename to odk1-src/img/form-widgets/geoshape-start.png diff --git a/src/img/form-widgets/geoshape1.png b/odk1-src/img/form-widgets/geoshape1.png similarity index 100% rename from src/img/form-widgets/geoshape1.png rename to odk1-src/img/form-widgets/geoshape1.png diff --git a/src/img/form-widgets/geoshape2.png b/odk1-src/img/form-widgets/geoshape2.png similarity index 100% rename from src/img/form-widgets/geoshape2.png rename to odk1-src/img/form-widgets/geoshape2.png diff --git a/src/img/form-widgets/geoshape3.png b/odk1-src/img/form-widgets/geoshape3.png similarity index 100% rename from src/img/form-widgets/geoshape3.png rename to odk1-src/img/form-widgets/geoshape3.png diff --git a/src/img/form-widgets/geoshape4.png b/odk1-src/img/form-widgets/geoshape4.png similarity index 100% rename from src/img/form-widgets/geoshape4.png rename to odk1-src/img/form-widgets/geoshape4.png diff --git a/src/img/form-widgets/geotrace-start.png b/odk1-src/img/form-widgets/geotrace-start.png similarity index 100% rename from src/img/form-widgets/geotrace-start.png rename to odk1-src/img/form-widgets/geotrace-start.png diff --git a/src/img/form-widgets/geotrace1.png b/odk1-src/img/form-widgets/geotrace1.png similarity index 100% rename from src/img/form-widgets/geotrace1.png rename to odk1-src/img/form-widgets/geotrace1.png diff --git a/src/img/form-widgets/geotrace2.png b/odk1-src/img/form-widgets/geotrace2.png similarity index 100% rename from src/img/form-widgets/geotrace2.png rename to odk1-src/img/form-widgets/geotrace2.png diff --git a/src/img/form-widgets/geotrace3.png b/odk1-src/img/form-widgets/geotrace3.png similarity index 100% rename from src/img/form-widgets/geotrace3.png rename to odk1-src/img/form-widgets/geotrace3.png diff --git a/src/img/form-widgets/geotrace4.png b/odk1-src/img/form-widgets/geotrace4.png similarity index 100% rename from src/img/form-widgets/geotrace4.png rename to odk1-src/img/form-widgets/geotrace4.png diff --git a/src/img/form-widgets/geotrace5.png b/odk1-src/img/form-widgets/geotrace5.png similarity index 100% rename from src/img/form-widgets/geotrace5.png rename to odk1-src/img/form-widgets/geotrace5.png diff --git a/src/img/form-widgets/geotrace6.png b/odk1-src/img/form-widgets/geotrace6.png similarity index 100% rename from src/img/form-widgets/geotrace6.png rename to odk1-src/img/form-widgets/geotrace6.png diff --git a/src/img/form-widgets/geotrace7.png b/odk1-src/img/form-widgets/geotrace7.png similarity index 100% rename from src/img/form-widgets/geotrace7.png rename to odk1-src/img/form-widgets/geotrace7.png diff --git a/src/img/form-widgets/image-start.png b/odk1-src/img/form-widgets/image-start.png similarity index 100% rename from src/img/form-widgets/image-start.png rename to odk1-src/img/form-widgets/image-start.png diff --git a/src/img/form-widgets/image1.png b/odk1-src/img/form-widgets/image1.png similarity index 100% rename from src/img/form-widgets/image1.png rename to odk1-src/img/form-widgets/image1.png diff --git a/src/img/form-widgets/image2.png b/odk1-src/img/form-widgets/image2.png similarity index 100% rename from src/img/form-widgets/image2.png rename to odk1-src/img/form-widgets/image2.png diff --git a/src/img/form-widgets/integer.png b/odk1-src/img/form-widgets/integer.png similarity index 100% rename from src/img/form-widgets/integer.png rename to odk1-src/img/form-widgets/integer.png diff --git a/src/img/form-widgets/month-year-spinner.png b/odk1-src/img/form-widgets/month-year-spinner.png similarity index 100% rename from src/img/form-widgets/month-year-spinner.png rename to odk1-src/img/form-widgets/month-year-spinner.png diff --git a/src/img/form-widgets/multi-image-compact-2-selected.png b/odk1-src/img/form-widgets/multi-image-compact-2-selected.png similarity index 100% rename from src/img/form-widgets/multi-image-compact-2-selected.png rename to odk1-src/img/form-widgets/multi-image-compact-2-selected.png diff --git a/src/img/form-widgets/multi-image-compact-2.png b/odk1-src/img/form-widgets/multi-image-compact-2.png similarity index 100% rename from src/img/form-widgets/multi-image-compact-2.png rename to odk1-src/img/form-widgets/multi-image-compact-2.png diff --git a/src/img/form-widgets/multi-select.png b/odk1-src/img/form-widgets/multi-select.png similarity index 100% rename from src/img/form-widgets/multi-select.png rename to odk1-src/img/form-widgets/multi-select.png diff --git a/src/img/form-widgets/multiselect-minimal-expanded.png b/odk1-src/img/form-widgets/multiselect-minimal-expanded.png similarity index 100% rename from src/img/form-widgets/multiselect-minimal-expanded.png rename to odk1-src/img/form-widgets/multiselect-minimal-expanded.png diff --git a/src/img/form-widgets/multiselect-minimal-start.png b/odk1-src/img/form-widgets/multiselect-minimal-start.png similarity index 100% rename from src/img/form-widgets/multiselect-minimal-start.png rename to odk1-src/img/form-widgets/multiselect-minimal-start.png diff --git a/src/img/form-widgets/note.png b/odk1-src/img/form-widgets/note.png similarity index 100% rename from src/img/form-widgets/note.png rename to odk1-src/img/form-widgets/note.png diff --git a/src/img/form-widgets/printer-widget.png b/odk1-src/img/form-widgets/printer-widget.png similarity index 100% rename from src/img/form-widgets/printer-widget.png rename to odk1-src/img/form-widgets/printer-widget.png diff --git a/src/img/form-widgets/select-autocomplete-filtered.png b/odk1-src/img/form-widgets/select-autocomplete-filtered.png similarity index 100% rename from src/img/form-widgets/select-autocomplete-filtered.png rename to odk1-src/img/form-widgets/select-autocomplete-filtered.png diff --git a/src/img/form-widgets/select-autocomplete.png b/odk1-src/img/form-widgets/select-autocomplete.png similarity index 100% rename from src/img/form-widgets/select-autocomplete.png rename to odk1-src/img/form-widgets/select-autocomplete.png diff --git a/src/img/form-widgets/select-one-minimal-expanded.png b/odk1-src/img/form-widgets/select-one-minimal-expanded.png similarity index 100% rename from src/img/form-widgets/select-one-minimal-expanded.png rename to odk1-src/img/form-widgets/select-one-minimal-expanded.png diff --git a/src/img/form-widgets/select-one-minimal-start.png b/odk1-src/img/form-widgets/select-one-minimal-start.png similarity index 100% rename from src/img/form-widgets/select-one-minimal-start.png rename to odk1-src/img/form-widgets/select-one-minimal-start.png diff --git a/src/img/form-widgets/select-one-search-searching.png b/odk1-src/img/form-widgets/select-one-search-searching.png similarity index 100% rename from src/img/form-widgets/select-one-search-searching.png rename to odk1-src/img/form-widgets/select-one-search-searching.png diff --git a/src/img/form-widgets/select-search-start.png b/odk1-src/img/form-widgets/select-search-start.png similarity index 100% rename from src/img/form-widgets/select-search-start.png rename to odk1-src/img/form-widgets/select-search-start.png diff --git a/src/img/form-widgets/selfie-complete.png b/odk1-src/img/form-widgets/selfie-complete.png similarity index 100% rename from src/img/form-widgets/selfie-complete.png rename to odk1-src/img/form-widgets/selfie-complete.png diff --git a/src/img/form-widgets/selfie-in-progress.png b/odk1-src/img/form-widgets/selfie-in-progress.png similarity index 100% rename from src/img/form-widgets/selfie-in-progress.png rename to odk1-src/img/form-widgets/selfie-in-progress.png diff --git a/src/img/form-widgets/selfie-start.png b/odk1-src/img/form-widgets/selfie-start.png similarity index 100% rename from src/img/form-widgets/selfie-start.png rename to odk1-src/img/form-widgets/selfie-start.png diff --git a/src/img/form-widgets/siganture-completed.png b/odk1-src/img/form-widgets/siganture-completed.png similarity index 100% rename from src/img/form-widgets/siganture-completed.png rename to odk1-src/img/form-widgets/siganture-completed.png diff --git a/src/img/form-widgets/signature-completed.png b/odk1-src/img/form-widgets/signature-completed.png similarity index 100% rename from src/img/form-widgets/signature-completed.png rename to odk1-src/img/form-widgets/signature-completed.png diff --git a/src/img/form-widgets/signature-in-progress.png b/odk1-src/img/form-widgets/signature-in-progress.png similarity index 100% rename from src/img/form-widgets/signature-in-progress.png rename to odk1-src/img/form-widgets/signature-in-progress.png diff --git a/src/img/form-widgets/signature-start.png b/odk1-src/img/form-widgets/signature-start.png similarity index 100% rename from src/img/form-widgets/signature-start.png rename to odk1-src/img/form-widgets/signature-start.png diff --git a/src/img/form-widgets/single-image-select-compact-2.png b/odk1-src/img/form-widgets/single-image-select-compact-2.png similarity index 100% rename from src/img/form-widgets/single-image-select-compact-2.png rename to odk1-src/img/form-widgets/single-image-select-compact-2.png diff --git a/src/img/form-widgets/single-select-compact.png b/odk1-src/img/form-widgets/single-select-compact.png similarity index 100% rename from src/img/form-widgets/single-select-compact.png rename to odk1-src/img/form-widgets/single-select-compact.png diff --git a/src/img/form-widgets/single-select.png b/odk1-src/img/form-widgets/single-select.png similarity index 100% rename from src/img/form-widgets/single-select.png rename to odk1-src/img/form-widgets/single-select.png diff --git a/src/img/form-widgets/string-input.png b/odk1-src/img/form-widgets/string-input.png similarity index 100% rename from src/img/form-widgets/string-input.png rename to odk1-src/img/form-widgets/string-input.png diff --git a/src/img/form-widgets/string-number.png b/odk1-src/img/form-widgets/string-number.png similarity index 100% rename from src/img/form-widgets/string-number.png rename to odk1-src/img/form-widgets/string-number.png diff --git a/src/img/form-widgets/text-no-appearance.png b/odk1-src/img/form-widgets/text-no-appearance.png similarity index 100% rename from src/img/form-widgets/text-no-appearance.png rename to odk1-src/img/form-widgets/text-no-appearance.png diff --git a/src/img/form-widgets/time-start.png b/odk1-src/img/form-widgets/time-start.png similarity index 100% rename from src/img/form-widgets/time-start.png rename to odk1-src/img/form-widgets/time-start.png diff --git a/src/img/form-widgets/time1.png b/odk1-src/img/form-widgets/time1.png similarity index 100% rename from src/img/form-widgets/time1.png rename to odk1-src/img/form-widgets/time1.png diff --git a/src/img/form-widgets/time2.png b/odk1-src/img/form-widgets/time2.png similarity index 100% rename from src/img/form-widgets/time2.png rename to odk1-src/img/form-widgets/time2.png diff --git a/src/img/form-widgets/trigger-selected.png b/odk1-src/img/form-widgets/trigger-selected.png similarity index 100% rename from src/img/form-widgets/trigger-selected.png rename to odk1-src/img/form-widgets/trigger-selected.png diff --git a/src/img/form-widgets/trigger-sorry.png b/odk1-src/img/form-widgets/trigger-sorry.png similarity index 100% rename from src/img/form-widgets/trigger-sorry.png rename to odk1-src/img/form-widgets/trigger-sorry.png diff --git a/src/img/form-widgets/trigger.png b/odk1-src/img/form-widgets/trigger.png similarity index 100% rename from src/img/form-widgets/trigger.png rename to odk1-src/img/form-widgets/trigger.png diff --git a/src/img/form-widgets/url-widget.png b/odk1-src/img/form-widgets/url-widget.png similarity index 100% rename from src/img/form-widgets/url-widget.png rename to odk1-src/img/form-widgets/url-widget.png diff --git a/src/img/form-widgets/video-start.png b/odk1-src/img/form-widgets/video-start.png similarity index 100% rename from src/img/form-widgets/video-start.png rename to odk1-src/img/form-widgets/video-start.png diff --git a/src/img/form-widgets/video1.png b/odk1-src/img/form-widgets/video1.png similarity index 100% rename from src/img/form-widgets/video1.png rename to odk1-src/img/form-widgets/video1.png diff --git a/src/img/form-widgets/video2.png b/odk1-src/img/form-widgets/video2.png similarity index 100% rename from src/img/form-widgets/video2.png rename to odk1-src/img/form-widgets/video2.png diff --git a/src/img/form-widgets/year-spinner.png b/odk1-src/img/form-widgets/year-spinner.png similarity index 100% rename from src/img/form-widgets/year-spinner.png rename to odk1-src/img/form-widgets/year-spinner.png diff --git a/src/img/oauth2-service/Api-key.png b/odk1-src/img/oauth2-service/Api-key.png similarity index 100% rename from src/img/oauth2-service/Api-key.png rename to odk1-src/img/oauth2-service/Api-key.png diff --git a/src/img/oauth2-service/Service-account.png b/odk1-src/img/oauth2-service/Service-account.png similarity index 100% rename from src/img/oauth2-service/Service-account.png rename to odk1-src/img/oauth2-service/Service-account.png diff --git a/src/img/oauth2-service/after-enable-create.png b/odk1-src/img/oauth2-service/after-enable-create.png similarity index 100% rename from src/img/oauth2-service/after-enable-create.png rename to odk1-src/img/oauth2-service/after-enable-create.png diff --git a/src/img/oauth2-service/after-enable.png b/odk1-src/img/oauth2-service/after-enable.png similarity index 100% rename from src/img/oauth2-service/after-enable.png rename to odk1-src/img/oauth2-service/after-enable.png diff --git a/src/img/oauth2-service/api-library.png b/odk1-src/img/oauth2-service/api-library.png similarity index 100% rename from src/img/oauth2-service/api-library.png rename to odk1-src/img/oauth2-service/api-library.png diff --git a/src/img/oauth2-service/api-menu.png b/odk1-src/img/oauth2-service/api-menu.png similarity index 100% rename from src/img/oauth2-service/api-menu.png rename to odk1-src/img/oauth2-service/api-menu.png diff --git a/src/img/oauth2-service/change-google-api.png b/odk1-src/img/oauth2-service/change-google-api.png similarity index 100% rename from src/img/oauth2-service/change-google-api.png rename to odk1-src/img/oauth2-service/change-google-api.png diff --git a/src/img/oauth2-service/create-credentials.png b/odk1-src/img/oauth2-service/create-credentials.png similarity index 100% rename from src/img/oauth2-service/create-credentials.png rename to odk1-src/img/oauth2-service/create-credentials.png diff --git a/src/img/oauth2-service/create-service-account.png b/odk1-src/img/oauth2-service/create-service-account.png similarity index 100% rename from src/img/oauth2-service/create-service-account.png rename to odk1-src/img/oauth2-service/create-service-account.png diff --git a/src/img/oauth2-service/create-service-dialog.png b/odk1-src/img/oauth2-service/create-service-dialog.png similarity index 100% rename from src/img/oauth2-service/create-service-dialog.png rename to odk1-src/img/oauth2-service/create-service-dialog.png diff --git a/src/img/oauth2-service/created-account.png b/odk1-src/img/oauth2-service/created-account.png similarity index 100% rename from src/img/oauth2-service/created-account.png rename to odk1-src/img/oauth2-service/created-account.png diff --git a/src/img/oauth2-service/credentials-info.png b/odk1-src/img/oauth2-service/credentials-info.png similarity index 100% rename from src/img/oauth2-service/credentials-info.png rename to odk1-src/img/oauth2-service/credentials-info.png diff --git a/src/img/oauth2-service/credentials.png b/odk1-src/img/oauth2-service/credentials.png similarity index 100% rename from src/img/oauth2-service/credentials.png rename to odk1-src/img/oauth2-service/credentials.png diff --git a/src/img/oauth2-service/enable-api.png b/odk1-src/img/oauth2-service/enable-api.png similarity index 100% rename from src/img/oauth2-service/enable-api.png rename to odk1-src/img/oauth2-service/enable-api.png diff --git a/src/img/oauth2-service/manage-service-account.png b/odk1-src/img/oauth2-service/manage-service-account.png similarity index 100% rename from src/img/oauth2-service/manage-service-account.png rename to odk1-src/img/oauth2-service/manage-service-account.png diff --git a/src/img/oauth2-service/menu-icon.png b/odk1-src/img/oauth2-service/menu-icon.png similarity index 100% rename from src/img/oauth2-service/menu-icon.png rename to odk1-src/img/oauth2-service/menu-icon.png diff --git a/src/img/oauth2-service/my-console.png b/odk1-src/img/oauth2-service/my-console.png similarity index 100% rename from src/img/oauth2-service/my-console.png rename to odk1-src/img/oauth2-service/my-console.png diff --git a/src/img/oauth2-service/myconsole-2.png b/odk1-src/img/oauth2-service/myconsole-2.png similarity index 100% rename from src/img/oauth2-service/myconsole-2.png rename to odk1-src/img/oauth2-service/myconsole-2.png diff --git a/src/img/oauth2-service/oauth.png b/odk1-src/img/oauth2-service/oauth.png similarity index 100% rename from src/img/oauth2-service/oauth.png rename to odk1-src/img/oauth2-service/oauth.png diff --git a/src/img/oauth2-service/restrict-api.png b/odk1-src/img/oauth2-service/restrict-api.png similarity index 100% rename from src/img/oauth2-service/restrict-api.png rename to odk1-src/img/oauth2-service/restrict-api.png diff --git a/src/img/oauth2-service/service-account-id.png b/odk1-src/img/oauth2-service/service-account-id.png similarity index 100% rename from src/img/oauth2-service/service-account-id.png rename to odk1-src/img/oauth2-service/service-account-id.png diff --git a/src/img/oauth2-service/site-admin.png b/odk1-src/img/oauth2-service/site-admin.png similarity index 100% rename from src/img/oauth2-service/site-admin.png rename to odk1-src/img/oauth2-service/site-admin.png diff --git a/src/img/oauth2-service/success.png b/odk1-src/img/oauth2-service/success.png similarity index 100% rename from src/img/oauth2-service/success.png rename to odk1-src/img/oauth2-service/success.png diff --git a/src/img/oauth2-service/upload-api.png b/odk1-src/img/oauth2-service/upload-api.png similarity index 100% rename from src/img/oauth2-service/upload-api.png rename to odk1-src/img/oauth2-service/upload-api.png diff --git a/src/img/odk-build/about.png b/odk1-src/img/odk-build/about.png similarity index 100% rename from src/img/odk-build/about.png rename to odk1-src/img/odk-build/about.png diff --git a/src/img/odk-build/add-translation.png b/odk1-src/img/odk-build/add-translation.png similarity index 100% rename from src/img/odk-build/add-translation.png rename to odk1-src/img/odk-build/add-translation.png diff --git a/src/img/odk-build/collapse-question.png b/odk1-src/img/odk-build/collapse-question.png similarity index 100% rename from src/img/odk-build/collapse-question.png rename to odk1-src/img/odk-build/collapse-question.png diff --git a/src/img/odk-build/display-language.png b/odk1-src/img/odk-build/display-language.png similarity index 100% rename from src/img/odk-build/display-language.png rename to odk1-src/img/odk-build/display-language.png diff --git a/src/img/odk-build/download-xml.png b/odk1-src/img/odk-build/download-xml.png similarity index 100% rename from src/img/odk-build/download-xml.png rename to odk1-src/img/odk-build/download-xml.png diff --git a/src/img/odk-build/edit-menu.png b/odk1-src/img/odk-build/edit-menu.png similarity index 100% rename from src/img/odk-build/edit-menu.png rename to odk1-src/img/odk-build/edit-menu.png diff --git a/src/img/odk-build/file-menu.png b/odk1-src/img/odk-build/file-menu.png similarity index 100% rename from src/img/odk-build/file-menu.png rename to odk1-src/img/odk-build/file-menu.png diff --git a/src/img/odk-build/form-properties.png b/odk1-src/img/odk-build/form-properties.png similarity index 100% rename from src/img/odk-build/form-properties.png rename to odk1-src/img/odk-build/form-properties.png diff --git a/src/img/odk-build/help-menu.png b/odk1-src/img/odk-build/help-menu.png similarity index 100% rename from src/img/odk-build/help-menu.png rename to odk1-src/img/odk-build/help-menu.png diff --git a/src/img/odk-build/information-text.png b/odk1-src/img/odk-build/information-text.png similarity index 100% rename from src/img/odk-build/information-text.png rename to odk1-src/img/odk-build/information-text.png diff --git a/src/img/odk-build/instance-name.png b/odk1-src/img/odk-build/instance-name.png similarity index 100% rename from src/img/odk-build/instance-name.png rename to odk1-src/img/odk-build/instance-name.png diff --git a/src/img/odk-build/open-form.png b/odk1-src/img/odk-build/open-form.png similarity index 100% rename from src/img/odk-build/open-form.png rename to odk1-src/img/odk-build/open-form.png diff --git a/src/img/odk-build/properties.png b/odk1-src/img/odk-build/properties.png similarity index 100% rename from src/img/odk-build/properties.png rename to odk1-src/img/odk-build/properties.png diff --git a/src/img/odk-build/remove-prompt.png b/odk1-src/img/odk-build/remove-prompt.png similarity index 100% rename from src/img/odk-build/remove-prompt.png rename to odk1-src/img/odk-build/remove-prompt.png diff --git a/src/img/odk-build/remove-translation.png b/odk1-src/img/odk-build/remove-translation.png similarity index 100% rename from src/img/odk-build/remove-translation.png rename to odk1-src/img/odk-build/remove-translation.png diff --git a/src/img/odk-build/rename.png b/odk1-src/img/odk-build/rename.png similarity index 100% rename from src/img/odk-build/rename.png rename to odk1-src/img/odk-build/rename.png diff --git a/src/img/odk-build/sign-in.png b/odk1-src/img/odk-build/sign-in.png similarity index 100% rename from src/img/odk-build/sign-in.png rename to odk1-src/img/odk-build/sign-in.png diff --git a/src/img/odk-build/translations.png b/odk1-src/img/odk-build/translations.png similarity index 100% rename from src/img/odk-build/translations.png rename to odk1-src/img/odk-build/translations.png diff --git a/src/img/odk-build/upload-form.png b/odk1-src/img/odk-build/upload-form.png similarity index 100% rename from src/img/odk-build/upload-form.png rename to odk1-src/img/odk-build/upload-form.png diff --git a/src/img/odk-build/view-menu.png b/odk1-src/img/odk-build/view-menu.png similarity index 100% rename from src/img/odk-build/view-menu.png rename to odk1-src/img/odk-build/view-menu.png diff --git a/src/img/project-collect/acceleration.png b/odk1-src/img/project-collect/acceleration.png similarity index 100% rename from src/img/project-collect/acceleration.png rename to odk1-src/img/project-collect/acceleration.png diff --git a/src/img/project-collect/activity.png b/odk1-src/img/project-collect/activity.png similarity index 100% rename from src/img/project-collect/activity.png rename to odk1-src/img/project-collect/activity.png diff --git a/src/img/project-collect/advanced-setting.png b/odk1-src/img/project-collect/advanced-setting.png similarity index 100% rename from src/img/project-collect/advanced-setting.png rename to odk1-src/img/project-collect/advanced-setting.png diff --git a/src/img/project-collect/android-studio.png b/odk1-src/img/project-collect/android-studio.png similarity index 100% rename from src/img/project-collect/android-studio.png rename to odk1-src/img/project-collect/android-studio.png diff --git a/src/img/project-collect/application-name.png b/odk1-src/img/project-collect/application-name.png similarity index 100% rename from src/img/project-collect/application-name.png rename to odk1-src/img/project-collect/application-name.png diff --git a/src/img/project-collect/avd-list.png b/odk1-src/img/project-collect/avd-list.png similarity index 100% rename from src/img/project-collect/avd-list.png rename to odk1-src/img/project-collect/avd-list.png diff --git a/src/img/project-collect/avd-manager.png b/odk1-src/img/project-collect/avd-manager.png similarity index 100% rename from src/img/project-collect/avd-manager.png rename to odk1-src/img/project-collect/avd-manager.png diff --git a/src/img/project-collect/build-number.png b/odk1-src/img/project-collect/build-number.png similarity index 100% rename from src/img/project-collect/build-number.png rename to odk1-src/img/project-collect/build-number.png diff --git a/src/img/project-collect/collect-app.png b/odk1-src/img/project-collect/collect-app.png similarity index 100% rename from src/img/project-collect/collect-app.png rename to odk1-src/img/project-collect/collect-app.png diff --git a/src/img/project-collect/collect-emulator.png b/odk1-src/img/project-collect/collect-emulator.png similarity index 100% rename from src/img/project-collect/collect-emulator.png rename to odk1-src/img/project-collect/collect-emulator.png diff --git a/src/img/project-collect/collect-emulator2.png b/odk1-src/img/project-collect/collect-emulator2.png similarity index 100% rename from src/img/project-collect/collect-emulator2.png rename to odk1-src/img/project-collect/collect-emulator2.png diff --git a/src/img/project-collect/collect-form.png b/odk1-src/img/project-collect/collect-form.png similarity index 100% rename from src/img/project-collect/collect-form.png rename to odk1-src/img/project-collect/collect-form.png diff --git a/src/img/project-collect/customize-activity.png b/odk1-src/img/project-collect/customize-activity.png similarity index 100% rename from src/img/project-collect/customize-activity.png rename to odk1-src/img/project-collect/customize-activity.png diff --git a/src/img/project-collect/developer-options.png b/odk1-src/img/project-collect/developer-options.png similarity index 100% rename from src/img/project-collect/developer-options.png rename to odk1-src/img/project-collect/developer-options.png diff --git a/src/img/project-collect/emulator-screen.png b/odk1-src/img/project-collect/emulator-screen.png similarity index 100% rename from src/img/project-collect/emulator-screen.png rename to odk1-src/img/project-collect/emulator-screen.png diff --git a/src/img/project-collect/emulator-screen1.png b/odk1-src/img/project-collect/emulator-screen1.png similarity index 100% rename from src/img/project-collect/emulator-screen1.png rename to odk1-src/img/project-collect/emulator-screen1.png diff --git a/src/img/project-collect/find-device.png b/odk1-src/img/project-collect/find-device.png similarity index 100% rename from src/img/project-collect/find-device.png rename to odk1-src/img/project-collect/find-device.png diff --git a/src/img/project-collect/hardware.png b/odk1-src/img/project-collect/hardware.png similarity index 100% rename from src/img/project-collect/hardware.png rename to odk1-src/img/project-collect/hardware.png diff --git a/src/img/project-collect/main-window.png b/odk1-src/img/project-collect/main-window.png similarity index 100% rename from src/img/project-collect/main-window.png rename to odk1-src/img/project-collect/main-window.png diff --git a/src/img/project-collect/phone-screen.png b/odk1-src/img/project-collect/phone-screen.png similarity index 100% rename from src/img/project-collect/phone-screen.png rename to odk1-src/img/project-collect/phone-screen.png diff --git a/src/img/project-collect/run-icon.png b/odk1-src/img/project-collect/run-icon.png similarity index 100% rename from src/img/project-collect/run-icon.png rename to odk1-src/img/project-collect/run-icon.png diff --git a/src/img/project-collect/sdk-manager.png b/odk1-src/img/project-collect/sdk-manager.png similarity index 100% rename from src/img/project-collect/sdk-manager.png rename to odk1-src/img/project-collect/sdk-manager.png diff --git a/src/img/project-collect/system-image.png b/odk1-src/img/project-collect/system-image.png similarity index 100% rename from src/img/project-collect/system-image.png rename to odk1-src/img/project-collect/system-image.png diff --git a/src/img/project-collect/target-android.png b/odk1-src/img/project-collect/target-android.png similarity index 100% rename from src/img/project-collect/target-android.png rename to odk1-src/img/project-collect/target-android.png diff --git a/src/img/project-collect/update-virtual.png b/odk1-src/img/project-collect/update-virtual.png similarity index 100% rename from src/img/project-collect/update-virtual.png rename to odk1-src/img/project-collect/update-virtual.png diff --git a/src/img/project-collect/usb-debugging.png b/odk1-src/img/project-collect/usb-debugging.png similarity index 100% rename from src/img/project-collect/usb-debugging.png rename to odk1-src/img/project-collect/usb-debugging.png diff --git a/src/img/project-collect/verify-configuration.png b/odk1-src/img/project-collect/verify-configuration.png similarity index 100% rename from src/img/project-collect/verify-configuration.png rename to odk1-src/img/project-collect/verify-configuration.png diff --git a/src/img/project-collect/vysor-add-app.png b/odk1-src/img/project-collect/vysor-add-app.png similarity index 100% rename from src/img/project-collect/vysor-add-app.png rename to odk1-src/img/project-collect/vysor-add-app.png diff --git a/src/img/project-collect/vysor-chrome.png b/odk1-src/img/project-collect/vysor-chrome.png similarity index 100% rename from src/img/project-collect/vysor-chrome.png rename to odk1-src/img/project-collect/vysor-chrome.png diff --git a/src/img/project-collect/vysor-download.png b/odk1-src/img/project-collect/vysor-download.png similarity index 100% rename from src/img/project-collect/vysor-download.png rename to odk1-src/img/project-collect/vysor-download.png diff --git a/src/img/project-collect/vysor-launch.png b/odk1-src/img/project-collect/vysor-launch.png similarity index 100% rename from src/img/project-collect/vysor-launch.png rename to odk1-src/img/project-collect/vysor-launch.png diff --git a/src/img/project-collect/webgl-enabled.png b/odk1-src/img/project-collect/webgl-enabled.png similarity index 100% rename from src/img/project-collect/webgl-enabled.png rename to odk1-src/img/project-collect/webgl-enabled.png diff --git a/src/img/project-collect/webgl.png b/odk1-src/img/project-collect/webgl.png similarity index 100% rename from src/img/project-collect/webgl.png rename to odk1-src/img/project-collect/webgl.png diff --git a/src/img/validate/invalidform.png b/odk1-src/img/validate/invalidform.png similarity index 100% rename from src/img/validate/invalidform.png rename to odk1-src/img/validate/invalidform.png diff --git a/src/img/validate/validate.png b/odk1-src/img/validate/validate.png similarity index 100% rename from src/img/validate/validate.png rename to odk1-src/img/validate/validate.png diff --git a/src/img/validate/validform.png b/odk1-src/img/validate/validform.png similarity index 100% rename from src/img/validate/validform.png rename to odk1-src/img/validate/validform.png diff --git a/src/img/visualize/aggregate-form.png b/odk1-src/img/visualize/aggregate-form.png similarity index 100% rename from src/img/visualize/aggregate-form.png rename to odk1-src/img/visualize/aggregate-form.png diff --git a/src/img/visualize/api-library.png b/odk1-src/img/visualize/api-library.png similarity index 100% rename from src/img/visualize/api-library.png rename to odk1-src/img/visualize/api-library.png diff --git a/src/img/visualize/data.png b/odk1-src/img/visualize/data.png similarity index 100% rename from src/img/visualize/data.png rename to odk1-src/img/visualize/data.png diff --git a/src/img/visualize/earth-data.png b/odk1-src/img/visualize/earth-data.png similarity index 100% rename from src/img/visualize/earth-data.png rename to odk1-src/img/visualize/earth-data.png diff --git a/src/img/visualize/email-prompt.png b/odk1-src/img/visualize/email-prompt.png similarity index 100% rename from src/img/visualize/email-prompt.png rename to odk1-src/img/visualize/email-prompt.png diff --git a/src/img/visualize/email.png b/odk1-src/img/visualize/email.png similarity index 100% rename from src/img/visualize/email.png rename to odk1-src/img/visualize/email.png diff --git a/src/img/visualize/enable-import.png b/odk1-src/img/visualize/enable-import.png similarity index 100% rename from src/img/visualize/enable-import.png rename to odk1-src/img/visualize/enable-import.png diff --git a/src/img/visualize/error.png b/odk1-src/img/visualize/error.png similarity index 100% rename from src/img/visualize/error.png rename to odk1-src/img/visualize/error.png diff --git a/src/img/visualize/export-submission.png b/odk1-src/img/visualize/export-submission.png similarity index 100% rename from src/img/visualize/export-submission.png rename to odk1-src/img/visualize/export-submission.png diff --git a/src/img/visualize/export.png b/odk1-src/img/visualize/export.png similarity index 100% rename from src/img/visualize/export.png rename to odk1-src/img/visualize/export.png diff --git a/src/img/visualize/feature-info.png b/odk1-src/img/visualize/feature-info.png similarity index 100% rename from src/img/visualize/feature-info.png rename to odk1-src/img/visualize/feature-info.png diff --git a/src/img/visualize/fusion-api.png b/odk1-src/img/visualize/fusion-api.png similarity index 100% rename from src/img/visualize/fusion-api.png rename to odk1-src/img/visualize/fusion-api.png diff --git a/src/img/visualize/google-cloud.png b/odk1-src/img/visualize/google-cloud.png similarity index 100% rename from src/img/visualize/google-cloud.png rename to odk1-src/img/visualize/google-cloud.png diff --git a/src/img/visualize/google-earth.png b/odk1-src/img/visualize/google-earth.png similarity index 100% rename from src/img/visualize/google-earth.png rename to odk1-src/img/visualize/google-earth.png diff --git a/src/img/visualize/import-file.png b/odk1-src/img/visualize/import-file.png similarity index 100% rename from src/img/visualize/import-file.png rename to odk1-src/img/visualize/import-file.png diff --git a/src/img/visualize/import-settings.png b/odk1-src/img/visualize/import-settings.png similarity index 100% rename from src/img/visualize/import-settings.png rename to odk1-src/img/visualize/import-settings.png diff --git a/src/img/visualize/kml-file.png b/odk1-src/img/visualize/kml-file.png similarity index 100% rename from src/img/visualize/kml-file.png rename to odk1-src/img/visualize/kml-file.png diff --git a/src/img/visualize/map.png b/odk1-src/img/visualize/map.png similarity index 100% rename from src/img/visualize/map.png rename to odk1-src/img/visualize/map.png diff --git a/src/img/visualize/my-places.png b/odk1-src/img/visualize/my-places.png similarity index 100% rename from src/img/visualize/my-places.png rename to odk1-src/img/visualize/my-places.png diff --git a/src/img/visualize/publish-form.png b/odk1-src/img/visualize/publish-form.png similarity index 100% rename from src/img/visualize/publish-form.png rename to odk1-src/img/visualize/publish-form.png diff --git a/src/img/visualize/publish-form2.png b/odk1-src/img/visualize/publish-form2.png similarity index 100% rename from src/img/visualize/publish-form2.png rename to odk1-src/img/visualize/publish-form2.png diff --git a/src/img/visualize/search-api.png b/odk1-src/img/visualize/search-api.png similarity index 100% rename from src/img/visualize/search-api.png rename to odk1-src/img/visualize/search-api.png diff --git a/src/incl/briefcase-features.rst b/odk1-src/incl/briefcase-features.rst similarity index 100% rename from src/incl/briefcase-features.rst rename to odk1-src/incl/briefcase-features.rst diff --git a/src/index.rst b/odk1-src/index.rst similarity index 99% rename from src/index.rst rename to odk1-src/index.rst index 892798264..06707384d 100644 --- a/src/index.rst +++ b/odk1-src/index.rst @@ -79,7 +79,7 @@ For a complete list of our projects, check out `Open Data Kit on Github `_ is compatible with ODK Aggregate v1.4.15. The sync protocol has been augmented to cache the user's permissions on the device and, for super-users or administrators, to cache the full set of users and all of their permissions (so that the super-user and/or administrator can assign rows to particular individuals). + +.. _aggregate-tables-extension-server-setup: + +Server Setup +------------------- + +First you’ll have to install ODK Aggregate v1.4.15 to a server (see the ODK 1.x Aggregate Installation instructions). + + #. Install ODK Aggregate v1.4.15 to a server. + #. Log onto your ODK Aggregate v1.4.15 instance. + #. Go to the :menuselection:`Site Admin --> Preferences` page. + #. Check the checkbox for :guilabel:`ODK Tables Synchronization Functionality`. + #. Go to the :menuselection:`Site Admin --> Permissions` page. + #. Add ODK Aggregate usernames or :program:`Gmail` or :program:`Google Apps` account users (do this by typing one or more users' usernames or e-mail addresses into the text area and clicking :guilabel:`Add User`). + #. If you have created an ODK Aggregate username, be sure to :guilabel:`Change Password` on that account to set the initial password for the account. + #. Grant these users the :guilabel:`Synchronize Tables` permissions. + #. Select at least one user to be the administrator and grant them :guilabel:`Administer Tables` permissions. This user will have the ability to :guilabel:`Reset App Server` from the Android device and add or remove tables and configuration files on the server. This is the equivalent of the Form Manager permissions in ODK 1.x deployments. + #. Click :guilabel:`Save Changes`. These changes will not take effect until you do! + +.. _aggregate-tables-extension-changing-appname: + +Changing the AppName +----------------------- + +ODK Aggregate is configured by default to use the **default** application name. To change the name, go to the :menuselection:`Site Admin --> Preferences` screen and click the :guilabel:`Change ODK 2.0 App Name` button, and enter a new application name. For example, the https://opendatakit-surveydemo.appspot.com server is configured with *survey* as its application name. + +.. note:: + + The ODK 2.0 tools are designed to support multiple, independent, ODK 2.0 applications running on the Android device. Each of the tools has the ability to run in the context of either a default application name, or a specified application name. + +By default, all the ODK 2.0 tools run under the default application name. Application names correspond to the name of the directory under :file:`/sdcard/opendatakit` where the data files for that application are stored. + +When you run ODK Services from within ODK Survey, the ODK Survey tool informs ODK Services to run in the context of the application name under which the ODK Survey tool is running. When ODK Services then interacts with ODK Aggregate, it reports that application name to the server. The server must be configured with exactly the same application name or it will reject the requests from ODK Services. The same applies when launching ODK Services from within ODK Tables. + +.. _aggregate-tables-extension-syncing: + +Using Device Synchronization +------------------------------------ + +For more information on syncing, see :ref:`ODK Services Syncing `. diff --git a/odk2-src/app-designer-common-tasks.rst b/odk2-src/app-designer-common-tasks.rst new file mode 100644 index 000000000..e0935553e --- /dev/null +++ b/odk2-src/app-designer-common-tasks.rst @@ -0,0 +1,196 @@ +.. spelling:: + testForm + getHashString + +Application Designer Common Tasks +==================================== + +.. contents:: :local: + +.. _app-designer-common-tasks-designing-a-form: + +ODK Survey: Designing a Form +------------------------------- + +This section assumes that all of the necessary software has been installed and verified. If not, complete the instructions in the :doc:`getting-started-2`. + +When creating a new form, the appropriate directory structure must be created. Once this directory structure is in place, an XLSX form can be created. From this XLSX form, a :file:`formDef.json` file will be generated using the XLSX Converter. This :file:`formDef.json`, in the appropriate directory, is what the system will use to create the survey form. + +.. _app-designer-common-tasks-creating-directory: + +Creating a Canonical Directory Structure for a New Form +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +New forms must be placed under the :file:`app/config/tables/` directory as described in the :file:`app/config/tables/` folder section. Given a form with the name *formId*, it will have a *tableId* of the same name unless you explicitly specify otherwise. The directory structure that should be created is :file:`app/config/tables/tableId/forms/formId` (where, under many circumstances, the value for *tableId* will be the same as the value for *formId*). To get started, for Windows open a:program:`cmd` window within your :file:`Application Designer` folder (click the :program:`cmd` shortcut you created earlier), and for Mac/Unix open a :program:`terminal` window within your :file:`Application Designer` folder. Type: + +.. code-block:: console + + $ grunt addtable:tableId + +This will create the required directory structure for an individual table, including the forms directory. Navigate into that, and create your :file:`formId` directory. Within that directory, ODK Survey expects to find the :file:`formDef.json` that defines the form. We recommend placing the XLSX file used to generate that :file:`formDef.json` in this folder. Any custom screen or prompt templates or other media related to the form should be also placed in this directory (or in a sub-directory). For example, if you wanted to create a new form named :file:`testForm.xlsx`, the directory structure that would be have to created for this form would be :file:`app/config/tables/testForm/forms/testForm`. + +.. _app-designer-common-tasks-creating-survey: + +Creating an ODK Survey +~~~~~~~~~~~~~~~~~~~~~~~~~ + +With the proper directory structure in place, you can now create your survey form. The :doc:`xlsx-converter-intro` documentation extensively details the worksheets that will need to be created within your XLSX file to create a survey. Also, you can use the **File Browser** window of the Application Designer to navigate to example XLSX files under the :file:`app/tables/` directory; it will likely be easier to start with one of the existing example forms and modify it. The key modification would be on the settings page -- changing the values for *table_id* and *form_id* (if present). + +.. _app-designer-common-tasks-generate-formdef: + +Generating a :file:`formDef.json` File with XLSX Converter +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Once you have a saved your survey XLSX file, you can use the XLSX Converter to create a :file:`formDef.json`. Use the :guilabel:`Save to File System` button to save the form definition file back to the file system. + +.. warning:: + + The :guilabel:`Save to File System` button uses the *form_id* and *table_id* within the XLSX file to identify where to write the :file:`formDef.json` file. If you have copied the XLSX file from some other location, and forgot to edit it, it may update back to that older location! If the *form_id* is equal to the *table_id*, two additional files are written that define the table's user data fields and that define the key-value properties for the table. + +You will notice that the form still does not appear in the list of forms found under the **Preview** tab of the Application Designer. In order for the form to appear, the :file:`framework.xlsx` file in the :file:`app/config/assets/framework/forms/framework/` directory must be modified, and the :file:`formDef.json` file in that same directory updated using XLSX Converter. + +The :file:`framework.xlsx` file is just another form definition, but it generally has no persisted data. In this case, it just presents a list of forms and allows you to open them. + +The modifications to the :file:`framework.xlsx` are as follows. Assuming you have created a :file:`testForm.xlsx`, the appropriate directory structures for :file:`testForm.xlsx`, and then properly generated and saved the :file:`formDef.json:`, the following lines would need to be added into the :file:`framework.xlsx` **survey worksheet**. + +.. csv-table:: Example Framework Survey Worksheet + :header: "branch_label", "url", "clause", "condition", "type", "values_list", "display.text", "display.hint" + + "testForm", + , "''?' + opendatakit.getHashString('../config/tables/testForm/forms/testForm/',null)",,, "external_link",,"Open form", + ,,"exit section", + +The following changes will also need to be made to the :file:`framework.xlsx` **choices worksheet** + +.. csv-table:: Example Framework Choices Worksheet + :header: "choice_list_name", "data_value", "display.text" + + "test_forms", "testForm", "testForm" + +The changes to the choices sheet adds the *testForm* form as one of the choices that is shown in the *user_branch* prompt (a user-directed branching prompt type). The changes on the ``survey sheet`` add a branch label, *testForm*, that matches the *data_value* from the ``choices sheet`` (this branch label will be jumped to if the user selects the *testForm* selection on the *user_branch* screen). The new branch label then renders an *external_link* prompt type that has the necessary arguments to open the *testForm*. + +Once you have made these changes and used XLSX Converter on the :file:`framework.xlsx` file to update the :file:`app/config/assets/framework/forms/framework/formDef.json` file, you should see your new form show up in the **Preview** tab of the Application Designer. Clicking on that should open your form. + +.. _app-designer-common-tasks-debugging-survey: + +Debugging your Survey +""""""""""""""""""""""""" + +The XLSX Converter should report most problems with your survey. + +If the form is not being rendered correctly but your survey generates a :file:`formDef.json` without an error, first try purging the database (dropping all the existing data tables) using the :guilabel:`Purge Database` button on the **Preview** tab. You will typically need to purge the database whenever you add or remove fields from your form or change their data type. + +If that does not resolve the issue, try stopping the :program:`grunt` command (on Windows, :kbd:`Control-C` should produce a prompt asking to confirm whether to stop or not. On Mac, :kbd:`Control-C` kill the process with no prompt.), and re-running it. :program:`Grunt` can sometimes get overwhelmed with changes and stop working. After restarting, test your form. + +If there are other problems, the contents of the JavaScript Console will be helpful to the ODK core team for debugging. Open the JavaScript Console by clicking the icon with the three bars in the top right, select :guilabel:`More Tools`, select :guilabel:`Developer Tools`, and then select the :guilabel:`Console` tab. Select all of the debugging output, then copy it, save it to a file, and post it to |forum|_ or create a ticket on the `Github Issue Tracker `_. + +.. _app-designer-common-tasks-move-to-device: + +Moving the New ODK Survey Form to a Device +"""""""""""""""""""""""""""""""""""""""""""" + +.. note:: + You must have USB debugging enabled on your device in order to perform this step. See `these instructions `_ for help. + +In order to see these changes on an Android device, you must first have ODK Survey installed on your device. Then: + + #. Connect the device to your computer via a USB cable + #. Open a :program:`cmd` or :program:`terminal` window within the :guilabel:`Application Designer` directory (the one containing :file:`Gruntfile.js`), as described in the :doc:`app-designer-directories` documentation. + #. Type: + +.. code-block:: console + + $ grunt adbpush + +This will copy all of the files under config onto your device. You should then be able to launch ODK Survey, and it will display your form in its list of forms. Click the form to open it. + +More :program:`grunt` commands can be found in `Pushing updates to the Device`_. + +.. _app-designer-common-tasks-design-view: + +ODK Tables: Designing a Custom View +------------------------------------- + +One of the most powerful aspects of ODK Tables is its ability to run HTML and +JavaScript pages as the skin of the app. Through a JavaScript API presented to these files, you can query the database and control the app. + +Writing an app using html/js yields a lot of power. However, it can lead to a complicated design cycle. + +The html/js files you write rely on the JavaScript API implemented within the ODK Tables APK to vend data-table values into your HTML pages, where they can be displayed as a list of items, as a detail view of a single item, or graphed in any number of ways. This JavaScript API, since it is implemented in the APK, makes it difficult to debug your custom views off the phone. Long-term, we intend to support this through a more capable Application Designer environment. At present, the only way to test your HTML pages is on the device. Fortunately, on Android 4.4 and higher, :program:`Chrome` can access the browser Console and set breakpoints on the device, providing a clumsy but viable debug environment. + +.. _app-designer-common-tasks-understanding-web-file: + +Understanding the Web File +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are several pieces of boilerplate you have to include in your own code in order to debug the files in :program:`Chrome`. + +In the ODK Application Designer, use the file browser to open the :file:`config/tables/Tea_houses/html/Tea_houses_list.html` file for the list view of the *Tea_houses* table. Right-click and select :guilabel:`View Frame Source`. This shows the contents of that file. The important part to note is the following lines in the ````: + +.. code-block:: html + + + + + + +In the first line you are just making the :program:`jQuery` object available to your code; :program:`jQuery` is a very powerful, very commonly used set of functions for accessing and performing actions within a webpage. In the second two lines you are adding the *odkCommon*, *odkTables*, and *odkData* objects if they are not already provided by the browser environment. When running on the device, the ODK Tables APK will provide these, and the contents of these files will be ignored. When running in Application Designer on your computer, these files provide the approximate functionality of the APK, allowing you to create and debug your scripts. However, at the moment, these implementations make use of RequireJS, which the ODK Tables HTML files do not use (RequireJS is extensively used by ODK Survey). This causes these to break in Application Designer. + +.. _app-designer-common-tasks-writing-web-file: + +Writing Your Own Web Files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To write your own file, first decide on the *tableId* for your table and instantiate a directory using the :program:`grunt` command: + +.. code-block:: console + + $ grunt addtable:tableId + +This :program:`grunt` task creates the needed directory structures and also constructs the HTML and JavaScript files with the necessary features for working within the :program:`Chrome` development environment. + +These files need content from your data-table in order enable you to begin creating your custom screens. We recommend that you first design an ODK Survey form to facilitate populating the table and then set up a :file:`tables.init` file to auto-populate the form with test data. Then, as you shut down and restart your app, it will auto-load that test data. + +After that, you can deploy your app to your device and open ODK Tables onto the custom view (see the ``properties`` sheet section of the XLSX Converter for how to specify the HTML file that should be opened). Once it opens, you can use the :program:`Chrome` browser on your computer to inspect for devices and connect to this custom screen on your Android device, and debug from there. + +.. warning:: + The edit-debug cycle is awkward because you must make the HTML or JavaScript change on your computer then push the change to your device, and reload the page (for example, by rotating the screen). When you do rotate the screen, however, it is rendered in a new web page, necessitating connecting to that new page to resume debugging (the prior page sits idle and will eventually be destroyed; if you don't see any activity, it is likely because you are pointing at the wrong web page; return to inspect devices, and select the newest page). + +As with ODK Survey, you can use the JavaScript Console to look for and fix errors in your HTML/JavaScript. If you are having trouble please check on the |forum|_. Keep in mind that the debug objects only emit a subset of the data in your ODK Tables database. + +.. _app-designer-common-tasks-pushing: + +Pushing updates to the Device +------------------------------- + +.. note:: + You must have USB debugging enabled on your device in order to perform this step. See `these instructions `_ for help. + +There are several times during app development where you will need to push and pull files to and from the phone. You will have to open one of the ODK tools on the device before these commands succeed. + + - The :command:`push` command is used to push the entire app directory to the mobile device. + - The :command:`pull` command is used to pull the database or exported CSVs from the device to the desktop computer. + +.. tip:: + Exported CSVs can be used to set up :file:`tables.init` to load test data. + +:program:`Grunt` tasks have been written in :file:`Gruntfile.js` that perform these operations for you. + +These commands can be run anywhere within the :file:`Application Designer` directory. + + - :command:`grunt adbpush`: Pushes everything under the app directory to the device. + - :command:`grunt adbpull-db`: Pulls the database from the device to the PC. + - :command:`grunt adbpull-csv`: Pull the exported CSVs from the device to the PC. + +The pull commands will place the pulled content in the :file:`app/output/` directory. + +The database is a :program:`SQLite` database and can be viewed using :program:`SQLite Browser`. This tool can also be used to view the content of the database used by :program:`Chrome` on your computer (the location of that file is OS dependent). + +If you pull the CSV files, they will be under the :file:`output/csv/` directory. You can then copy them to the :file:`config/assets/csv/` directory and set up the :file:`tables.init` file to read them in order to provision test data for your development effort. If you need any of this data in production, you will want to sync to a server then export the CSV files and copy them to the :file:`config/assets/csv/` directory so that they have all of their metadata field values populated. + +.. tip:: + Running :command:`grunt adbpull` will perform all the pull tasks. + +.. tip:: + There are a number of additional grunt tasks available. Assuming you have installed grunt and node, you can view the available tasks by running :command:`grunt --help` anywhere in the repo. + diff --git a/odk2-src/app-designer-directories.rst b/odk2-src/app-designer-directories.rst new file mode 100644 index 000000000..a29d19715 --- /dev/null +++ b/odk2-src/app-designer-directories.rst @@ -0,0 +1,145 @@ +Application Designer Directory Structure +============================================ + +.. _app-designer-dirs: + +There are many folders and files within the :file:`Application Designer` directory. Fortunately, the only ones that are of interest for a non-software-developer are: + + - :file:`app/` - folder containing everything that will be pushed to the Android device. + - :file:`Gruntfile.js` - contains the definitions of tasks that push files to the Android device, launch the :program:`Chrome` browser, and pull data and log files off the Android device. + +Initially, you will only be concerned with the contents of your :file:`app/` directory -- the set of files that are placed on the Android device. As your sophistication grows, you may want to define your own :program:`grunt` tasks to automate repetitive steps in your deployment and device management processes. Adding or modifying tasks is beyond the scope of this document; please refer to the :program:`grunt` website (see :doc:`getting-started-2` for the link to that site). + +For completeness, here is the full list of the files and sub-folder in this directory. Again, you generally do not need to be concerned with the contents or specifics of any of these: + + - :file:`app/` - folder containing everything that will be pushed to the Android device. + - :file:`devEnv/` - contains the HTML for the 6 tabs of the Application Designer. + - :file:`grunttemplates/` - contains template files used by :program:`grunt` tasks. + - :file:`node_modules/` - contains additional software installed by :program:`npm`, such as external tools used by :program:`grunt`. + - :file:`scanFormDesigner/` - contains the Scan Form Designer tool. + - :file:`test/` - contains tests of the computer-based simulated device environment. + - :file:`themeGenerator/` - contains the HTML and JavaScript for the ODK ThemeGenerator CSS style and theme customization tool (accessed via the **Customize** tab). + - :file:`xlsxconverter/` - contains the HTML and JavaScript for the ODK XLSX Converter tool that converts XLSX form definitions into formDef.json files (accessed via the **XLSX Converter** tab). + - :file:`.bowerrc` - JSON configuration for the :program:`bower` tool. + - :file:`.editorconfig` - when your text editors are configured to use it, enables consistent formatting to files across all contributors to your application design. See `EditorConfig `_. + - :file:`.hgignore` - source code management configuration. + - :file:`.hgtags` - source code management configuration. + - :file:`.jshintrc` - configuration for JSHint - a program that flags suspicious usage in programs written in JavaScript. + - :file:`bower.json` - used to control library management through :program:`bower`. By default, the :file:`.bowerrc` file has been configured to install these libraries in :file:`app/framework/libs/` so that you have access to them when your app is pushed to the phone. + - :file:`deleteDefAndProp.sh` - MacOSX shell script to traverse the relevant parts of the :file:`app/` directory and delete the :file:`definition.csv` and :file:`properties.csv` files. + - :file:`Gruntfile.js` - contains the definitions of tasks that push files to the Android device, launch the :program:`Chrome` browser, and pull data and log files off the Android device. + - :file:`index.html` - the main HTML for the ODK Application Designer web page. + - :file:`macGenConverter.js` - MacOSX command-line wrapper for the XLSX Converter tool (converts a single XLSX file piped into :program:`stdin` into a :file:`formDef.json` on :program:`stdOut`). + - :file:`macGenFormDef.sh` - MacOSX shell script to traverse relevant parts of the :file:`app/` directory and generate :file:`formDef.json` files from XLSX files. + - :file:`package.json` - configuration information for :program:`npm`. + - :file:`README` - description linking back to this document. + +.. _app-designer-dirs-app: + +The :file:`app/` Folder +-------------------------- + +Everything in this folder mimics what is on the Android device. The directory looks as follows: + + - :file:`config` - user-defined configuration for your application. + - :file:`data` - file attachments, and, on the device, the database. + - :file:`output` - on the device, logging files and exported CSV and media files. + - :file:`system` - files managed by the ODK tools (do not modify). + +.. _app-designer-dirs-app-config: + +The :file:`app/config/` Folder +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This folder is synced to the device. It contains all of the form and table configuration files and initialization scripts. This is the sub-folder in which you will be primarily working. + +This folder contains: + + - :file:`assets` + - :file:`tables` + +.. _app-designer-dirs-app-config-assets: + +The :file:`app/config/assets/` Folder +"""""""""""""""""""""""""""""""""""""""""" + + - :file:`css/` - contains the common CSS files for ODK Tables detail, list and home screens, and for app forms in ODK Survey (:file:`odk_survey.css`). + - :file:`csv/` - contains the data files to be initially read and loaded into the ODK Survey and Tables databases. + - :file:`fonts/` - contains the fonts used throughout the application. + - :file:`framework/` - contains the :file:`framework.xlsx` and other relevant framework files. + - :file:`img/` - contains the images used throughout the application. + - :file:`js/` - contains JavaScript used by the ODK Tables custom home screen and/or the ODK Survey custom forms list + - :file:`libs/` - contains the various libraries used throughout the application like jQuery and D3. + - :file:`tables.init` - contains the initialization directives for which data (CSV) files should be loaded at initial start-up of the ODK tools. + - :file:`index.html` - the HTML for the ODK Tables custom home screen, if it is enabled in the ODK Tables configuration settings. + +.. _app-designer-dirs-app-config-tables: + +The :file:`app/config/tables/` Folder +"""""""""""""""""""""""""""""""""""""""""" + +This folder has a predefined directory structure, but the content is entirely dependent upon the needs of your application. + +The zip file for the ODK Application Designer populates this with all the subfolders used by each of the ODK Tables and the ODK Survey demonstration zip files. Ultimately, when you have completed your application design, this folder will contain none of these original folders but would instead contain only the folders which you have created. + +.. note:: + + Unlike ODK Collect, which stores each submission in a separate file, ODK Survey and ODK Tables store their combined collected submission data in data tables (one row per submission). + +ODK Tables can display the contents of a table through one or more custom list views; it can display individual submissions through one or more custom detail views. Graphical views are simply list views in which the data is presented graphically using a library such as D3. All of these custom views are defined here. + +ODK Survey, unlike ODK Collect, has the additional flexibility of supporting multiple forms to create, access and update data within a single common data table. This enables creating multi-stage workflows such as initial screenings and follow-ups, or registrations and status-updates (submission data can be editable, or not, based upon the form used at that workflow stage). + +To accommodate these various capabilities, the :file:`tables` directory is structured such that individual data tables each have their own directory within the :file:`tables` directory. The table's *table_id* is the name of this sub-directory. When defining a new data table, begin with a form whose form id is the table id. + +.. _app-designer-dirs-app-config-tables-id: + +The :file:`app/config/tables/table_id/` Folder +''''''''''''''''''''''''''''''''''''''''''''''' + +A canonical :file:`table_id` sub-folder contains: + + - :file:`definition.csv` - defines the data columns in this table. Generated when the *form_id* XLSX file underneath this :file:`table_id` is processed by the XLSX Converter. + - :file:`properties.csv` - defines the appearance properties for this table. Example properties are the detail view HTML file name, the list view HTML file name, the default view type of the table, etc. Generated when the *form_id* XLSX file underneath this :file:`table_id` is processed by the XLSX Converter. + - :file:`forms/` - contains directories for each ODK Survey form that manipulates this table. The names of these sub-directories are the *form_id* values of those forms. Within each sub-directory, there is a :file:`form_id.xlsx` file defining the ODK Survey form and the :file:`formDef.json` generated by the XLSX Converter when it processed that form definition file. If the form has form-specific images or media files, custom CSS, layouts, or prompt types, those files should reside within the form's sub-directory (nested sub-folders are permitted). + - :file:`html/` the custom HTML files for the ODK Tables list and details views of the table's contents. + - :file:`css/` - contains CSS files specific to this table. + - :file:`js/` the JavaScript files needed for the custom ODK Tables HTML list and detail views (found in the :file:`html/` directory). + +ODK Scan is currently split in where it stores its configuration for mark-sense forms. The current location for the ODK Scan templates is under :file:`app/config/scan/form_templates` directory. This will likely change and lead to additional sub-directories here. + +.. _app-designer-dirs-app-data: + +The :file:`app/data/` Folder +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ODK Application Designer stores user data in this directory. The database itself is in the :file:`webDb` directory. Any data files associated with a row in the database are stored within this folder under the :file:`tables//instances` directory. + +.. _app-designer-dirs-app-output: + +The :file:`app/output/` Folder +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ODK Application Designer provides various :program:`grunt` tasks to pull files off the Android device. These files include JSON objects for debugging, exported CSVs, and the database itself. The :program:`grunt` tasks store these files here. There is also a logging directory which contains logs that are useful for debugging issues. + +.. _app-designer-dirs-app-system: + +The :file:`app/system/` Folder +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This folder contains the files that the ODK tools depend upon and which are expected to be changed only when different versions of the ODK APKs are released. + +.. warning:: + + Files in this folder are managed by the ODK tools. If you change any of these files, the tools may detect the change and restore the file when they next start. The goal is that only the ODK core team should have to modify things in this folder. If you feel you need to modify anything in this directory, please contact us. + +The general structure is: + + - :file:`js/` - contains JavaScript for the Java to JavaScript interfaces common to both ODK Table and ODK Survey. + - :file:`libs/` - contains 3rd party JavaScript libraries used by ODK Tables and ODK Survey. + - :file:`survey/` - contains JavaScript used by ODK Survey to render forms. + - :file:`tables/` - contains JavaScript used by ODK Tables to render the custom home screen, list, detail, and graphical views created by the Application Designer. + - :file:`tables.deleting` - information related to data deletion + - :file:`tables.pending` - information related to pending data changes + - :file:`index.html` - the generic HTML for all ODK Survey forms. + diff --git a/odk2-src/app-designer-install.rst b/odk2-src/app-designer-install.rst new file mode 100644 index 000000000..b39c6251b --- /dev/null +++ b/odk2-src/app-designer-install.rst @@ -0,0 +1,37 @@ +Installing Application Designer +==================================== + +.. _app-designer-install: + +Download the |app_designer_link| zip file. + +.. |app_designer_link| raw:: html + + Application Designer + +Unzip the file you downloaded and move the resulting folder to somewhere other than your :file:`Downloads` directory; such as your :file:`Documents` folder. + +.. admonition:: Windows Users Tip + + You will be opening a :program:`cmd` window and changing your current directory (using the :program:`cd` command) into this directory every time you use this tool. It is therefore useful to create a shortcut that opens a :program:`cmd` window directly into this directory: + + #. Open a file browser and navigate to the unzipped directory containing a number of files and directories, including a :file:`Gruntfile.js`. + #. Click into the top location bar that displays the nested list of folders to this folder. + #. Copy this path to the cut-and-paste buffer. + #. Now, move down to the list of files, right-click. + #. Select :guilabel:`New...`, :guilabel:`Shortcut`. + #. Type :program:`cmd` for the location of the item. + #. Click :guilabel:`Next`, and then :guilabel:`Finish`. + #. Select this newly-created :program:`cmd.exe` shortcut and right-click. + #. Select :guilabel:`Properties`. + #. Click on the :guilabel:`Start in` text box, delete its contents, and paste the path to this folder. + #. Click :guilabel:`OK` to accept the change. + #. Double-click the :program:`cmd.exe` shortcut to open a :program:`cmd` window. + #. Confirm that it opens in the intended directory (you should see the full path to that directory displayed to the left of the blinking cursor). + + +.. admonition:: MacOSX Users Tip + + :program:`Terminal` will open a new :program:`terminal` window if you drag a folder (or pathname) onto the :program:`Terminal` application icon, and you can also drag a folder to the tab bar of an existing window to create a new tab in that folder. + +You have now completed the installation of the ODK Application Designer software. diff --git a/odk2-src/app-designer-intro.rst b/odk2-src/app-designer-intro.rst new file mode 100644 index 000000000..5066b0a80 --- /dev/null +++ b/odk2-src/app-designer-intro.rst @@ -0,0 +1,39 @@ +ODK Application Designer +=============================== + +.. _app-designer-intro: + +:dfn:`ODK Application Designer` is a tool to help you design :dfn:`data management applications` on top of the ODK 2.0 framework. It works in conjunction with :program:`Excel` or :program:`OpenOffice` for form design, the :program:`Chrome` browser for rendering, and your favorite editor for template design. + +In the context of the ODK 2.0 tools, application design consists of: + + - designing the forms used in data collection (by ODK Survey) + - designing the HTML landing pages and screens used for navigating, curating, and visualizing that data on your Android device (within ODK Tables). + - customizing the look-and-feel of both of these via customized images, logos, and CSS rules. + - designing mark-sense forms for paper-based data entry (by ODK Scan) + +.. tip:: + The tools operate independently -- you are not required to use all the tools, or even install them on your device. If you are only interested in data collection, you may only want ODK Survey. Or if you are only interested in data dissemination and visualization, you might only want ODK Tables. + + Simply select the combination or individual tool that fits your needs. However, all of these tools require ODK Services to access the database, sync to a server, and vend HTML files. + +The major goals of the ODK Application Designer are: + + #. Simplify the form-design process by providing a preview of your form with the same screen geometry as your target Android device. You no longer have to copy each iteration of your form onto your device to see how it will look or fit on a smaller screen. + #. Simplify the design and testing of customized list- and detail- views in ODK Tables, and in the design of graphical representations of data within ODK Tables + #. Simplify the customization of the look-and-feel of your forms through a simple visual theme editor / generator where your modifications can be immediately viewed and the resulting CSS styles or theme can be saved to a file for later incorporation into your application deployment. + #. Simplify the conversion of the XLSX file into a form definition by providing a drag-and-drop conversion app running locally on your desktop. + #. Enable the creation of mark-sense forms (ODK Scan Form Designer) that can be scanned by your Android device for data input. The resulting data is available to the other ODK 2.0 tools without need to communicate with a remote server. + +.. _app-designer-intro-learn-more: + +Learn more about ODK Application Designer +------------------------------------------- +.. toctree:: + :maxdepth: 2 + + app-designer-setup + app-designer-using + xlsx-converter-intro + scan-form-designer-intro + diff --git a/odk2-src/app-designer-launching.rst b/odk2-src/app-designer-launching.rst new file mode 100644 index 000000000..01d7bdf19 --- /dev/null +++ b/odk2-src/app-designer-launching.rst @@ -0,0 +1,39 @@ +Launching the Application Designer +===================================== + +.. _app-designer-launching: + +The ODK Application Designer is both + + #. a workspace containing the cohesive interacting set of forms and files you have created and + #. a set of tools used for that development. + +.. tip:: + + We recommend unzipping and creating a new :file:`Application Designer` directory for each new set of ODK Survey forms, ODK Scan forms, and ODK Tables files that are not intended to be deployed as a cohesive unit. If you need to have several of these sets of forms and files co-resident on the same Android device, you would create different application names for each set. The standard set up uses the default application name (appropriately entitled ``default``). To create a new application name, create a folder with that name next to your default app and make sure it is stored on the device in the opendatakit folder. The underlying ODK 2.0 tools will then keep each of these sets of forms and files isolated from each other. + +To launch the application designer, open the :program:`cmd` shortcut or :program:`Terminal` window onto the directory containing :file:`Gruntfile.js` (the unzipped :file:`ODK Application Designer v2.0` directory) and type: + +.. code-block:: console + + $ grunt + +This should automatically open :program:`Chrome` and display the **Preview** tab. When you need to stop the server, return to the :program:`cmd` or :program:`terminal` window where you typed the :program:`grunt` command and press :kbd:`Ctrl+c`. This will stop the process. + +.. warning:: + + If you have Parallels or other virtualization software running, it might try to open :program:`Chrome` in this system by default. If so, you should still be able to navigate to http://localhost:8000/index.html. + +.. warning:: + + If the :program:`Chrome` browser does not open, try opening it yourself and browsing to http://localhost:8000/index.html. + +.. warning:: + If the page never times-out, but never loads (it remains blank or constantly spinning), then stop :program:`grunt` and try this command instead: + + .. code-block:: console + + $ grunt --verbose connect:livereload:keepalive + + This will start :program:`grunt`, but disable the file-change detection mechanisms that automatically reload an HTML page when it or any JavaScript file it uses has been modified. Others have reported that uninstalling :program:`npm` and :program:`node`, and then re-installing them may correct the issue. + diff --git a/odk2-src/app-designer-overview.rst b/odk2-src/app-designer-overview.rst new file mode 100644 index 000000000..47c9b039f --- /dev/null +++ b/odk2-src/app-designer-overview.rst @@ -0,0 +1,92 @@ +ODK Application Designer Overview +================================== + +.. _app-designer-overview: + +This section presents a brief overview of the features of the ODK Application Designer. + +The ODK Application Designer is accessed through a :program:`Chrome` browser. Once launched, it opens :program:`Chrome` to display: + +.. image:: /img/app-designer-overview/app-designer-preview.* + :alt: The Preview tab of the Application Designer rendered in a Chrome web browser. + +This screen has 6 tabs: + + - :ref:`Preview ` (shown above) - used to preview ODK Survey forms and ODK Tables list-, detail- and graph- views (future). Displays these within a user-selected device geometry. + - :ref:`Customize ` - a visual style and visual theme editor. This editor immediately shows the effects of changes to specific settings in your CSS file. This functionality is undergoing changes and not recommended. + - :ref:`XLSX Converter ` - converts the XLSX description of a form into a :file:`formDef.json` representation used by ODK Survey. + - :ref:`File Browser ` - enables browsing of the directory structure that will exist on your Android device so that you can access or view other files (currently necessary for accessing the ODK Tables list- and detail- views). + - :ref:`Scan Form Designer ` - drag-and-drop mark-sense form designer tool. + +.. _app-designer-overview-preview: + +Preview +----------- +The :guilabel:`Preview` tab (shown above) has several controls: + + - :guilabel:`Launch Page` -- what webpage to launch in the window below. + - :guilabel:`Purge Database` -- during development, if you are adding new fields or changing their data types, you will need to purge the database so that the database structure can be re-generated with the proper fields and data types. + - :guilabel:`Device Dimensions` -- what dimensions to make the window below. + - :guilabel:`Back Button` -- this is generally only useful when viewing ODK Tables web pages. + +.. warning:: + + The :guilabel:`Back Button` may cause unpredictable results when used while displaying ODK Survey forms. + +The Launch Page opens the ODK Survey Framework Page. This is the :file:`formDef.json` in the Android device's application frameworks folder (:file:`/sdcard/opendatakit/default/config/assets/framework/forms/framework`). The contents of this form are defined by the :file:`framework.xlsx` file in that same directory. + +For example, if you click on the household test form, and click the :guilabel:`Follow Link` button on the next screen, the *Household Survey* form is launched, yielding this screen: + +.. image:: /img/app-designer-overview/household-survey.* + :alt: Rendering of a survey titled Household Survey in the Application Designer as it would appear on the Android device. + +You can navigate through forms, enter and exit sub-forms, and save results just like on your Android device. + +.. note:: + + The development environment does not allow you to submit data to a server. ODK Deploy (currently under development, not yet released) will provide this functionality. + +.. _app-designer-overview-customize: + +Customize +----------- +The :guilabel:`Customize` tab contains the CSS style and theme generator: + +.. image:: /img/app-designer-overview/theme-generator.* + :alt: The theme generator tab modifying the appearance of the Example Form + +Using this tool, you can change background colors, fonts, and other settings affecting the appearance of a form. The changes are reflected immediately in the form shown to the left of the toolbar. + +This functionality is under active development and not currently recommended. + +.. _app-designer-overview-xlsx: + +XLSX Converter +----------------- +The :guilabel:`XLSX Converter` tab contains the conversion tool that transforms XLSX files produced by Excel or OpenOffice into the :file:`formDef.json` file used by ODK Survey: + +.. image:: /img/app-designer-overview/xlsxconverter.* + :alt: The XLSX Converter tab + +See :doc:`xlsx-converter-intro` documentation for more information about this tool. + +.. _app-designer-overview-file: + +File Browser +---------------- +The :guilabel:`File Browser` tab provides a view into what will become the application's directory on the phone. + +.. image:: /img/app-designer-overview/file-browser.* + :alt: The File Browser tab + +.. _app-designer-overview-scan: + +ODK Scan Form Designer +-------------------------- +The :guilabel:`Scan Form Designer` tab presents a drag-and-drop editor for mark-sense form creation. + +.. image:: /img/app-designer-overview/scan-form-designer.* + :alt: The ODK Scan Form Designer tab + +See :doc:`scan-form-designer-intro` documentation for more information about this tool. + diff --git a/odk2-src/app-designer-prereqs.rst b/odk2-src/app-designer-prereqs.rst new file mode 100644 index 000000000..9f54414cf --- /dev/null +++ b/odk2-src/app-designer-prereqs.rst @@ -0,0 +1,268 @@ +Application Designer Prerequisites +=================================== + +.. _app-designer-prereqs: + +You must install the following software on your computer in order to use Application Designer: + + - |java_link| - Java is required by the Android SDK + - |chrome_link| - Google's :program:`Chrome` browser. + - |nodejs_link| - a framework for easily building fast, scalable applications. Download Version 6.2.2 or higher and install it from NodeJS + - |grunt_link| - a task-based scripting environment (installation is described below). + - |android_sdk_link| - the software development kit for Android devices (installation is described below). + +.. |java_link| raw:: html + + Java + +.. |chrome_link| raw:: html + + Chrome + +.. |nodejs_link| raw:: html + + NodeJS + +.. |grunt_link| raw:: html + + Grunt + +.. |android_sdk_link| raw:: html + + Android SDK + +.. warning:: + + It is tricky to foresee all the issues that can crop up on many different machines and setups. If something in this process does not go as expected, please check the |forum|_. + +.. _app-designer-prereqs-java: + +Java +-------- +Make sure Java 7 or higher is installed on the computer you plan to use. If it is not, `download and install it `_. If you are using MacOSX, it may require special care and attention. See `MacOSX Java install `_ and `MacOSX Java FAQ `_. + +.. _app-designer-prereqs-nodejs: + +NodeJS +--------- +You must use Version 6.2.2 or higher. To avoid directory path problems on Windows, we require :program:`npm` version 3, and that is only available on this node release (or higher). Follow the `instructions to install NodeJS `_. + +.. _app-designer-prereqs-nodejs-unix: + +For Mac/Unix +~~~~~~~~~~~~~~ + +After installing NodeJS, open a :program:`terminal` (which you can do by clicking the spotlight in the top right corner of the screen, typing :program:`terminal`, and clicking the program named :program:`Terminal`) and type: + +.. code-block:: console + + $ npm --version + +.. warning:: + + If a number is not displayed, but you instead receive a message that the command :program:`npm` cannot be found, you will have to perform some additional configuration. + + As of this writing, by default NodeJS installs its commands into :file:`/usr/local/bin/`. In the :program:`terminal`, type: + + .. code-block:: console + + $ ls /usr/local/bin/npm + + If this command outputs something like ``/usr/local/bin/npm``, but you are still unable to run: + + .. code-block:: console + + $ npm --version + + try running: + + .. code-block:: console + + $ /usr/local/bin/npm --version + + If this is successful, then :program:`npm` is successfully installed, and you will just have to add :file:`/usr/local/bin/` to your system *PATH* variable (see below). + + If the command: + + .. code-block:: console + + $ ls /usr/local/bin/npm + + outputs a message telling you permission is denied, then you will have to change the ownership of the :file:`/usr/local/` and :file:`/usr/local/bin/` directories. On Mac, follow the `instructions to take ownership `_ of these directories, or to at least give yourself read permission. On other Unix systems, use the :program:`chown` command or the user-interface appropriate to your distribution to do so. + +.. _app-designer-prereqs-grunt: + +Grunt +--------- +After installing NodeJS, install :program:`grunt` by doing the following: + +.. note:: + + These installation steps are copied from the `Grunt Getting Started guide `_. + +On Windows, open a :program:`cmd` window (go to Start Menu, search for :program:`cmd` and open it); on MacOSX, open a :program:`terminal` window. +Within this window, type: + +.. code-block:: console + + $ npm install -g grunt-cli + +If the above command is unsuccessful, some machines may need to append :command:`sudo` at the beginning of the command. If :program:`grunt` is successfully installed, the following command: + +.. code-block:: console + + $ grunt --version + + +Should display the installed version of :program:`grunt`. For example the version might be ``grunt-cli v1.2.0`` + +.. _app-designer-prereqs-android: + +Android SDK +-------------- +To install the Android SDK: + + 1. Browse to the `Android SDK download page `_. + 2. Scroll down on this page to the section labeled: *Get just the command line tools*. + + .. note:: + + You can alternatively install the full Android Studio if you so wish, in which case you should follow Google's instructions and then skip to step 6 of this guide. + + + 3. Within that section, download the appropriate file(s) based on your operating system. + 4. Accept the license agreement + 5. Wait for the install of the SDK Tools to complete. Windows will need to manually run the :file:`.exe` file previously downloaded to start installation. + 6. Run the SDK Manager + + - On Windows, it is available in the :guilabel:`Start Menu` under Android SDK Tools + - On Mac/Unix, open the SDK folder you downloaded above. In the :file:`bin/` or :file:`tools/` directories (on some versions it is in both places--it doesn't matter which you use), double click the file called :file:`android`. + + 7. Select the latest versions of the following packages (by checking their checkboxes): + + - Android SDK Tools + - Android Platform-tools + - Android Build-tools + + 8. If extra packages are selected, you may unselect them before installation. + 9. Click :guilabel:`Install 3 packages` in the lower right corner of the screen. + 10. A licensing pop-up dialog will appear. Accept the license agreement(s) by selecting the :guilabel:`Accept License` option. If there are multiple licenses, you may need to select each license in the :guilabel:`Packages` window on this dialog and check this :guilabel:`Accept License` option for each of them before the :guilabel:`Install` button will become enabled. + 11. Click the :guilabel:`Install` button on that dialog to begin the installation process. + +Among many other things, this will install the Android Debug Bridge software on your computer. This tool enables the scripted pushing of files and APKs down to your Android device. See `adb (Android Debug Bridge) `_ for a listing of its capabilities. + +Next, on Windows open a :program:`cmd` window (open the Start menu, type :program:`cmd` in the search box, select and open it), and on Mac/Unix open a :program:`terminal` window. Type: + +.. code-block:: console + + $ adb version + +If this displays a version string, then your installation is complete; you are done with this section and can move on to :doc:`app-designer-install`. + +.. warning:: + + If there is an error complaining about Java not being installed, you will need to close this :program:`cmd` or :program:`terminal` window and download and install Java. After installing Java, open a new :program:`cmd` or :program:`terminal` window and type this command again. + +.. warning:: + + If :program:`adb` is not found, then you need to add it to the *PATH* variable of your system. + +.. _app-designer-prereqs-adb: + +Add :program:`adb` to your *PATH* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _app-designer-prereqs-adb-windows: + +For Windows +"""""""""""""" + + #. Open a Windows File Explorer and navigate to the location of your Android SDK. This will typically be at one of: :file:`C:\\Users\\your_username\\android-sdks` or :file:`C:\\Program Files\\Android\\android-sdk` or :file:`C:\\Program Files (x86)\\Android\\android-sdk`. + #. Navigate into the :file:`platform-tools` folder. + #. Click in the file path at the top of the File Explorer window. The path will become a selected text string. Copy it into your copy-and-paste buffer. + #. Open the Start menu. + #. Right-click on :guilabel:`Computer`. + #. Choose :guilabel:`Properties`. The System Control Panel screen opens. + #. Select :guilabel:`Advanced system setting` on the left sidebar. The System Properties dialog opens. + #. Click on the :guilabel:`Environment Variables...` button at the bottom of the screen. The Environment Variables dialog opens. + #. Select the :guilabel:`Path` variable in the bottom System variables scroll window. + #. Click :guilabel:`Edit...` + #. Click into the :guilabel:`Variable value` text box. + #. Press your :guilabel:`End` key to move to the very end of this extremely long string. + #. Enter ';' and paste the :file:`platform-tools` directory path after that semicolon. + #. Click on :guilabel:`OK` and exit all of the windows. + #. Verify that you have made the change by closing all :program:`cmd` windows and open a new one (so it picks up the change), and type + +.. code-block:: console + + $ adb version + +You should now see the version of the :program:`adb` tool. For example: ``Android Debug Bridge version 1.0.31``. You can now move on to :doc:`app-designer-install`. + +.. _app-designer-prereqs-adb-unix: + +For Mac/Unix +""""""""""""""""""""" + +The *PATH* variable is nothing more than a default list of places the system looks for commands. Open a :program:`terminal`. Type: + +.. code-block:: console + + $ echo $PATH + +You will see a colon-separated list of folders on your computer. (echo means just print whatever comes next, and the ``${ }`` means that the system will treat *PATH* as a variable, not a program. You don't need to know this to follow these instructions, but knowledge is power.) For example, you might see something like this: + +.. code-block:: console + + $ echo $PATH + /usr/local/bin:/usr/local/sbin:/usr/bin:/bin + +This means that when you type: + +.. code-block:: console + + $ adb --version + +the system will look for the command called :program:`adb` in the directories :file:`/usr/local/bin/`, :file:`/usr/local/sbin/`, :file:`/usr/bin/`, and :file:`/bin/`. + +Note the location where you downloaded the Android SDK. It should contain a folder called :file:`platform-tools`, which itself contains the program :program:`adb`. If this was in the folder :file:`/Users/someuser/Desktop/android-sdk/` you should be able to run: + +.. code-block:: console + + $ /Users/someuser/Desktop/android-sdk/platform-tools/adb --version + +This works because we're telling the computer exactly where the program :program:`adb` exists. By putting the :file:`platform-tools` directory on the system's *PATH* variable, we will be able to just type :program:`adb` and have the system find it in the :file:`/Users/someuser/Desktop/android-sdk/platform-tools/` directory. + +This process is more involved on Mac/Unix than on Windows. Use a text editor (not :program:`Word`, but something like :program:`TextEdit`), select the option to open a file, and browse to your home directory. You can find your home directory by typing: + +.. code-block:: console + + $ echo ~ + +in a :program:`terminal`. ('~' is a shortcut for the home directory.) Macs use a hidden file called :file:`.bash_profile` in the home directory to set variables like *PATH*. Other Unix systems use files like :file:`.bashrc`. You might have to check the specifics for your distribution to know which you should use. Open the appropriate file. If the file does not already exist, create a new file that will be saved with the appropriate name in your home directory. + +We want to add the location of the :program:`adb` tool to your *PATH* while preserving the existing *PATH* information. Assuming that your :program:`adb` program is in the :file:`/Users/someuser/Desktop/android-sdk/platform-tools/` directory, you would add the following command to the end of the :file:`.bash_profile` file: + +.. code-block:: console + + $ export PATH=${PATH}:/Users/someuser/Desktop/android-sdk/platform-tools + +Save the file, close the :program:`terminal` window, open a new :program:`terminal` window, and type: + +.. code-block:: console + + $ echo $PATH + +You should see your old path with the new directory you added above, and you should now be able to run: + +.. code-block:: console + + $ adb --version + +.. tip:: + + If you are going to be heavily customizing the look-and-feel of the application with a lot of external JavaScript libraries, you might also choose to install :program:`bower`. + + +You can now move on to :doc:`app-designer-install`. + diff --git a/odk2-src/app-designer-setup.rst b/odk2-src/app-designer-setup.rst new file mode 100644 index 000000000..7546f2289 --- /dev/null +++ b/odk2-src/app-designer-setup.rst @@ -0,0 +1,8 @@ +Setting Up ODK Application Designer +===================================== + +.. toctree:: + :maxdepth: 2 + + app-designer-prereqs + app-designer-install diff --git a/odk2-src/app-designer-using.rst b/odk2-src/app-designer-using.rst new file mode 100644 index 000000000..566c34527 --- /dev/null +++ b/odk2-src/app-designer-using.rst @@ -0,0 +1,11 @@ +Using ODK Application Designer +=============================== + +.. toctree:: + :maxdepth: 3 + + app-designer-overview + app-designer-launching + app-designer-directories + app-designer-common-tasks + diff --git a/odk2-src/cloud-endpoints-intro.rst b/odk2-src/cloud-endpoints-intro.rst new file mode 100644 index 000000000..f79c1343b --- /dev/null +++ b/odk2-src/cloud-endpoints-intro.rst @@ -0,0 +1,22 @@ +ODK Cloud Endpoints +=============================== + +.. _cloud-endpoints-intro: + +:dfn:`ODK Cloud Endpoints` are servers that communicate with the ODK 2.0 Android applications. They implement the `ODK 2.0 REST Protocol `_. + +There are currently two options for Cloud Endpoints to communicate with ODK 2 tools. + + - :doc:`sync-endpoint` - Supports the full ODK 2.0 REST Protocol + - :doc:`aggregate-tables-extension` - Supports the majority of the ODK 2.0 REST Protocol; however, is missing group permission filtering support. + +.. _cloud-endpoints_intro_try: + +Try a Cloud Endpoint +----------------------- + +.. toctree:: + + sync-endpoint + aggregate-tables-extension + diff --git a/odk2-src/conf.py b/odk2-src/conf.py new file mode 100644 index 000000000..597fbd204 --- /dev/null +++ b/odk2-src/conf.py @@ -0,0 +1,353 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Open Data Kit documentation build configuration file, created by +# sphinx-quickstart on Wed May 24 09:46:59 2017. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys +sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(0, os.path.abspath('../')) +import video + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = ['sphinx.ext.autodoc', + 'sphinx.ext.doctest', + 'sphinx.ext.intersphinx', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.mathjax', + 'sphinx_tabs.tabs', + 'sphinxcontrib.spelling', + 'video'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'Open Data Kit 2.x' +copyright = '2017, Open Data Kit. This document is licensed under a Creative Commons Attribution 4.0 International License' +author = 'Open Data Kit' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '1.0' +# The full version, including alpha/beta/rc tags. +release = '1.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'incl'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'default' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + +# suppress warnings for unknown options +suppress_warnings = ['ref.option'] + +# Smart (q)uotes, (D)ashes, and (e)llipses +smartquotes = True +smartquotes_action = 'De' + +# Print suggestions for misspelled words. +spelling_show_suggestions = True + +# Change image preference for DirectoryHTMLBuilder +from sphinx.builders.html import DirectoryHTMLBuilder +DirectoryHTMLBuilder.supported_image_types = [ + 'image/svg+xml', + 'image/gif', + 'image/png', + 'image/jpeg' +] + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "sphinx_rtd_theme" + +# Add logo stuff +html_logo = '_static/img/odk-logo-wide.png' +html_theme_options = { + 'logo_only': True, + 'display_version': False, +} +html_title = "Open Data Kit 2.x Docs" + +html_favicon = "_static/img/odk-favicon.ico" + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add paths that contain extra files which are not directly related to the +# documentation and which are copied to the output directory. +# html_extra_path = [] + +# -- Options for HTMLHelp output ------------------------------------------ + +# Output file base name for HTML help builder. +htmlhelp_basename = 'OpenDataKit2doc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + 'pointsize': '12pt', + + # Additional stuff for the LaTeX preamble. + 'preamble': ''' + \usepackage{fontspec} + ''', + + # disable font inclusion + 'fontpkg': '', + 'fontenc': '', + + # Fix Unicode handling by disabling the defaults for a few items + # set by sphinx + 'inputenc': '', + 'utf8extra': '', + + # Latex figure (float) alignment + 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'OpenDataKit2.tex', 'Open Data Kit 2.x Documentation', + 'Open Data Kit 2.x', 'manual'), +] + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'opendatakit2', 'Open Data Kit 2.x Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'OpenDataKit2', 'Open Data Kit 2.x Documentation', + author, 'OpenDataKit2', 'One line description of project.', + 'Miscellaneous'), +] + + + +# -- Options for Epub output ---------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project +epub_author = author +epub_publisher = author +epub_copyright = copyright + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'https://docs.python.org/': None} + +# Add custom CSS + +def setup(app): + app.add_stylesheet('css/custom.css') + app.add_javascript('js/custom.js') + +# At top of every document + +rst_prolog=""" +.. role:: th + :class: th + +.. role:: tc + :class: tc + +.. role:: formstate + :class: formstate + +.. role:: gesture + :class: gesture +""" + +# At bottom of every document +download_pdf = """ + +Download this documentation as a PDF. + +""" +odk_pdf = """ + +../_downloads/ODK2-Documentation.pdf + +""" +prob_in_doc = """ + +If you find a problem with this documentation, please + +""" +file_issue = """ + +file an issue + +""" +file_issue_here = """ + +https://github.com/opendatakit/docs/issues + +""" +contri_start = """ + +You are also encouraged to + +""" +fork_repo = """ + +fork our Github repo + +""" +repo_here = """ + +https://github.com/opendatakit/docs/ + +""" +join = """ + +and + +""" +contri = """ + +become a contributor + +""" +contri_guide = """ + +/contributing/ + +""" +faq_help = """ + +If you still need help, you can ask support questions in the + +""" +forum = """ + +ODK Forum + +""" +forum_here = """ + +https://forum.opendatakit.org/ + +""" + +rst_epilog = """ + +.. |odk-slack| replace:: ODK Slack +.. _odk-slack: https://opendatakit.slack.com + +.. |docs-issue| replace:: issue +.. _docs-issue: https://github.com/opendatakit/docs/issues + +.. |forum| replace:: ODK Forum +.. _forum: https://forum.opendatakit.org + +.. |contrib-guide| replace:: contributors guide +.. _contrib-guide: http://docs.opendatakit.org/contributing/ + +""" + +html_context = {'download_pdf' : download_pdf, + 'odk_pdf' : odk_pdf, + 'prob_in_doc' : prob_in_doc , + 'contri_start' : contri_start , + 'join' : join , + 'faq_help' : faq_help , + 'file_issue' : file_issue , + 'fork_repo' : fork_repo , + 'contri' : contri , + 'forum' : forum , + 'file_issue_here' : file_issue_here , + 'repo_here' : repo_here , + 'contri_guide' : contri_guide , + 'forum_here' : forum_here} diff --git a/odk2-src/data-permission-filters.rst b/odk2-src/data-permission-filters.rst new file mode 100644 index 000000000..03daf93a4 --- /dev/null +++ b/odk2-src/data-permission-filters.rst @@ -0,0 +1,438 @@ +.. spelling:: + rw + rwd + rwdp + defaultAccessOnCreation + unverifiedUserCanCreate + +Data Permission Filters +======================== + +.. _data-permission-filters: + +.. _data-permission-filters-limitations: + +Limitations +---------------------------- + +Traditional access control frameworks provide strong protections for data and the management of which users can modify that data. The permission filtering introduced in ODK 2.0 is weaker. When syncing devices with the server, all data rows for all data tables are currently synced and shared across all devices. Every device gets a full copy of all data. Permission filtering enables a supervisor to restrict the visibility of that data and to manage who can modify or delete the data through the programmatic means provided by the ODK 2.0 tools. + +This is weaker than traditional access control frameworks in that application designers can: + + - Circumvent via software. There are specific ways in which application designers can write their applications to defeat these filters. When those mechanisms are not employed, permission filtering provides equivalent policy enforcement to that of a traditional access control framework. + - Circumvent via external access. The data and attachments are stored as plaintext on the device. Anyone can copy this data off of the device and access it, or write their own apps and directly modify it. + +It is important to understand these limitations when designing your applications. + +.. _data-permission-filters-overview: + +Overview +------------- + +By default, all tables can be altered by all users. + +The ODK 2.0 data access filtering mechanism relies on five interacting features: + + - Verified user identities + - Verified user capabilities + - Table-level security configuration (whether data in the table can be modified by unprivileged users). + - Row-level access filters (to specify whether a row is visible to a given user, whether the user can modify the row's data value, and whether the user can change this row's access filters). + - Sync status of the individual row. + +.. _data-permission-filters-verified-user-identities: + +Verified User Identities +----------------------------- + +Enforcing restrictions on who can see or modify data requires that the identity of the user has been verified. + +When configuring the :guilabel:`Server Settings`, any changes to any of the settings (such as the server URL, type of credential (or anonymous access), username, password or Gmail account) will clear any prior user identity and capability information and flag the user identity as unverified. + +When leaving the :guilabel:`Server Settings` screen, a user-verification screen will then be presented (unless no server sign-on credential is specified, in which case anonymous access to the server will be attempted): + +.. image:: /img/data-permission-filters/verify-user.* + :alt: Verify User Screen + :class: device-screen-vertical + +Clicking the :guilabel:`Verify User Permissions` button on this screen will initiate a series of requests to the configured server. These requests verify that the server URL is correct, that the server works with this application name, and then verify the server sign-on credential that has been configured on the :guilabel:`Server Settings` page. + +.. warning:: + + If the server sign-on credential is rejected, the user identity will be flagged as unverified and any further interactions on the device will be performed as if by an anonymous user. + +.. _data-permission-filters-verified-user-capabilities: + +Verified User Capabilities +-------------------------------- + +As part of the user-verification process, once the user's identity has been verified, the list of groups to which this user belongs and the capabilities (roles) assigned to that user are downloaded from the server. These are cached on the device for use during data access filtering until the user logs out of the ODK 2.0 tools on the device or a different server sign-on credential is specified. + +For the purposes of the data access filtering mechanism, there are 4 user capabilities of interest: + + - **ROLE_USER** -- a user who is able to verify their identity. + - **ROLE_SYNCHRONIZE_TABLES** -- a user who is able to execute the sync protocol. + - **ROLE_SUPER_USER_TABLES** -- a privileged user who can edit all rows, change how rows are visible, and change who has special permission to edit a given row. + - **ROLE_ADMINISTER_TABLES** -- a privileged user who can :guilabel:`Reset App Server` and who can edit all rows, change how rows are visible, and change who has special permission to edit a given row. + +The first two of these identify users that are unprivileged. These users may be granted privileges to individual rows by being designated the owner of that row or through their membership in one or more user groups identified in the row's access filter columns. + +The second two of these identify privileged users that have full control of the device. Additionally, the last of these capabilities (**ROLE_ADMINISTER_TABLES**) identifies a user that can alter the configuration of the Cloud Endpoint. + +Application designers that wish to restrict access by unverified users or manage anonymous access to the server can further restrict table and row access in these scenarios. + +.. _data-permission-filters-row-access-filter-cols: + +Row Access Filter Columns +-------------------------------- + +Management of which unprivileged users can see, modify or manage access to a given row is controlled through five access filter columns. The first of these columns specifies the access to the row that is granted to all unprivileged users. The second identifies the owner of this row. Row owners have modify privileges on a row. The other three are either null or specify a user group that is granted that specific access right: + + - **_DEFAULT_ACCESS** -- one of :tc:`HIDDEN`, :tc:`READ_ONLY`, :tc:`MODIFY` or :tc:`FULL`. + - **_ROW_OWNER** -- this user has :tc:`FULL` privileges on this row. + - **_GROUP_READ_ONLY** -- a user who is a member of this group will be able to read this row of data + - **_GROUP_MODIFY** -- a user who is a member of this group will be able to read and modify this row of data but not delete it. + - **_GROUP_PRIVILEGED** -- a user who is a member of this group will be able to read, modify, delete and change privileges on this row of data. + +.. note:: + + Privileged users are not governed by these settings -- they have unlimited access to all tables on the device. + +Individual users can belong to any number of groups, enabling arbitrarily complex row-level access management. Users may also be assigned a default group. Management of group memberships is dictated by the server being used. Refer to the :doc:`cloud-endpoints-intro` for the capabilities of the different servers. More detail will be given regarding these filter columns in the :ref:`Row-level Access Filters ` section. + +.. _data-permission-filters-obtaining-roles: + +Obtaining a User's Groups and Roles +---------------------------------------- + +Inside ODK Survey and ODK Tables web pages, the groups and roles of the current verified user are available in JavaScript via the API: + +.. code-block:: javascript + + odkData.getRoles(function(result) { + var roles = result.getRoles(); + // roles is an array of capabilities granted to the verified user. + // It will be null for anonymous and unverified users. + }, function(errorMsg) { + // error handler + }); + +.. _data-permission-filters-obtaining-default-group: + +Obtaining a User's Default Group +--------------------------------------- + +Inside ODK Survey and ODK Tables web pages, the default group of the current verified user is available in JavaScript via the API: + +.. code-block:: javascript + + odkData.getDefaultGroup(function(result) { + var defaultGroup = result.getDefaultGroup(); + // defaultGroup is null or a string + }, function(errorMsg) { + // error handler + }); + + +.. note:: + + Default groups are not directly used within the ODK 2.0 framework. These are provided for use by an application designer when crafting their application. + +.. _data-permission-filters-obtaining-other-info: + +Obtaining Information About Other Users +------------------------------------------------ + +Whenever the server is contacted to verify a user's identity, if the user is determined to be a privileged user, the server will, additionally, provide a list of all users configured on the server and all of the groups and roles assigned to those users. This list can be useful when performing task assignments via assigning row ownership. + +This list will contain entries of the form: + +.. code-block:: javascript + + { + user_id: "verified_identity_token", + full_name: "content of the Full Name field on the server", + default_group: "default group of the user" + roles: [...] + } + +The *Full Name* field on the server (on the :menuselection:`Site Admin --> Permissions` sub-tab) is provided here to allow super-users and administrators to select people by *name*. *user_id* should be stored in the :th:`_ROW_OWNER` column to assign ownership to this user. The list of roles (and groups) is provided to allow super-users and administrators to choose users based upon their capabilities. + +If the user has been assigned to a default group it will be provided. Default groups are not directly used within the ODK 2.0 framework. These are provided for use by an application designer when crafting their application. + +Inside ODK Survey and ODK Tables web pages, the list of all configured users is available in JavaScript via the API: + +.. code-block:: javascript + + odkData.getUsers(function(result) { + var users= result.getUsers(); + // users is an array of the above objects. + // It will be null for anonymous and unverified users. + // It will be a singleton list if the user lacks permissions. + }, function(errorMsg) { + // error handler + }); + +.. _data-permission-filters-table-security-config: + +Table-level Security Configuration +------------------------------------------- + +As mentioned earlier, by default, all tables can be altered by all users. + +Data permission filtering introduces the notion of a *locked* table. Only super-users and administrators can create and delete rows in locked tables. Anonymous, unverified, or ordinary users are unable to do so. + +A table property is used to specify that a table is *locked.* + +Two other table properties control the creation of a row. The first property specifies whether an anonymous or unverified user can create a row in the table (this only applies if a table is not *locked;* it has no effect if the table is *locked*, since row creation is prohibited for all but super-users and administrators). The second property specifies the type of row-level access filter to assign to this newly-created row. Row-level access settings are covered more completely in the :ref:`following section `. + +These three table properties can be specified in the properties sheet of the XLSX file. If they are not specified, the default values for these three properties are: + +.. list-table:: + :header-rows: 1 + + * - partition + - aspect + - key + - type + - value + * - Table + - security + - locked + - boolean + - false + * - Table + - security + - unverifiedUserCanCreate + - boolean + - true + * - Table + - security + - defaultAccessOnCreation + - string + - FULL + +.. _data-permission-filters-row-access-filters: + +Row-level Access Filters +----------------------------------- + +Control of who can see, modify, or delete an individual row is governed by the row-level access filter columns of that row and that row's sync status. As described earlier in this page, these filters are stored in the row itself under the :th:`_default_access`, :th:`_row_owner`, :th:`_group_read_only`, :th:`_group_modify`, and :th:`_group_privileged` metadata columns. The sync status of the row is also stored in the row itself under the :th:`_sync_state` metadata column. + +Row-level access will always be one of: + + - Not visible + - **r** -- Read-only access to the row + - **rw** -- Read and modify access to the row. Deletion is not allowed. Modification of the row-level access filter columns is not allowed. + - **rwd** -- Read, modify and delete access to the row. Modification of the row-level access filter columns is not allowed. + - **rwdp** -- Read, modify and delete access, plus the ability to modify the row-level access filter columns. + +The rules for the row-level access filter are as follows (stop at the first rule that applies): + + 1. Super-users and administrators have full read/write/delete(rwd) capabilities on all rows, regardless of their row-level access filters and independent of the table's *locked* status. These privileged users also have the ability to change the row-level access filter column values (ordinary users cannot). + + .. list-table:: + :header-rows: 1 + + * - User Capability + - unlocked table + - *locked* table + * - ROLE_SUPER_USER_TABLE + - rwdp + - rwdp + * - ROLE_ADMINISTER_TABLE + - rwdp + - rwdp + + 2. If a row has not yet been synced to the server, the current user has full read/write/delete (rwd) capabilities on that row. This includes the anonymous and unverified users and is independent of the table's *locked* status. + + .. list-table:: + :header-rows: 1 + + * - _sync_state + - unlocked table + - *locked* table + * - new_row + - rwd + - rwd + + 3. If the :th:`_row_owner` column contain the user_id of the current user, then this user has full read/write/delete (rwd) capability on this row or, for *locked* tables, can modify the row (but cannot delete it). + + .. list-table:: + :header-rows: 1 + + * - _row_owner + - unlocked table + - *locked* table + * - user_id of current verified user + - rwd + - rw + + 4. If the user is a member of one the following groups, their corresponding privileges are shown below. + + .. list-table:: + :header-rows: 1 + + * - group columns + - unlocked table + - *locked* table + * - _group_privileged + - rwdp + - rwdp + * - _group_modify + - rw + - r + * - _group_read_only + - r + - r + + 5. Otherwise, row-level access is governed by the _default_access column and whether or not the table is locked, as follows: + + .. list-table:: + :header-rows: 1 + + * - _default_access + - unlocked table + - *locked* table + * - FULL + - rwd + - r + * - MODIFY + - rw + - r + * - READ_ONLY + - r + - r + * - HIDDEN + - not visible + - not visible + +.. note:: + + :th:`_row_owner` can be null or any arbitrary placeholder string. If you use placeholder strings, it is recommended that they not begin with *username:* or *mailto:* or be *anonymous* to prevent any possible collisions with existing usernames. Placeholder strings might be useful in workflows to designate queues of unassigned-work. + +Super-users and administrators can update the row-level access filters via the JavaScript API: + +.. code-block:: javascript + + odkData.changeAccessFilterOfRow(tableId, defaultAccess, rowOwner, groupReadOnly, + groupModify, groupPrivileged, rowId, + function(result) { + // success outcome + // result holds the result set: SELECT * FROM tableId WHERE _id = "rowId" + }, + function(error) { + // error handler + }); + +Alternatively, super-users and administrators can also use the :code:`updateRow` API. + +Ordinary users will receive a not-authorized error if they attempt to set any of these metadata fields (even if the values they set are unchanged from the current values of those fields). + +.. _data-permission-filters-hidden-filter: + +Implementation of the HIDDEN filter on queries +----------------------------------------------------- + +When a SQL query is processed inside the ODK Services layer, it is first examined to see if the result set contains the columns :th:`_sync_state`, :th:`_default_access`, :th:`_row_owner`, :th:`_group_read_only`, :th:`_group_modify`, and :th:`_group_privileged`. If it contains all six columns, then the query is wrapped with a :code:`where` clause to exclude hidden rows and that, in turn, is wrapped by whatever :code:`limit` and :code:`offset` you have specified for the query. + +.. warning:: + + If you issue a query that omits one or more of these six columns from the result set, then no :tc:`HIDDEN` filtering will be applied. This is one way to circumvent data permission filtering in software -- by crafting queries that omit one or more of these fields. + + For example, queries that return the maximum value in a field: + + .. code-block:: sql + + SELECT MAX(crop_height) as max_height FROM crop_plantings + + Would return the maximum crop height across all crop planting -- even if the current user only had access to the crop height data for their own plantings (and the crop information from other farms was hidden from them). + + If you want to restrict such calculations to just the data visible to the current user, you must manually construct the query to do so. This would be the revised query: + + .. code-block:: sql + + SELECT MAX(crop_height) as max_height FROM crop_plantings WHERE _default_access != ? or _row_owner = ? bind parameters = [ "HIDDEN", odkCommon.getActiveUser() ] + +.. _data-permission-filters-effective-access: + +Effective Access +----------------------- + +As mentioned above, when a SQL query is processed inside the ODK Services layer, it is first examined to see if the result set contains the columns :th:`_sync_state`, :th:`_default_access`, :th:`_row_owner`, :th:`_group_read_only`, :th:`_group_modify`, and :th:`_group_privileged`. If it contains all six columns, then a synthesized column, :th:`_effective_access` is added to the result set. That column returns one of *r*, *rw*, *rwd*, or *rwdp* (with the *p* indicating that a user can change permissions for the row as well) to indicate the level of access the current user has on the rows in the result set. + +Additionally, once a result set is returned for a given table, you can determine whether the current user can create new rows on the table by calling :code:`getCanCreateRow` + +.. code-block:: javascript + + odkData.query(tableId, whereClause, sqlBindParams, groupBy, having, + orderByElementKey, orderByDirection, limit, offset, includeKVS, + function(result) { + // success outcome + // result holds the result set. Assume this has at least one row. + // obtain the effective access for the first row in the result set + // this will be one of "r", "rw", "rwd", or "rwdp" + var effectiveAccess = result.getData(0, "_effective_access"); + // obtain the boolean indicating whether the current user can + // create new rows in this tableId. + var ableToCreate = result.getCanCreateRow(); + }, + function(error) { + // error handler + }); + +.. _data-permission-filters-usage: + +Usages Within Applications +-------------------------------- + +Consider a workflow application where a first group of field agents create work requests, those requests are then sent to a supervisor who assigns them to a different set of field agents for processing. + +In this case, you might configure a work_requests table to create rows with a :tc:`HIDDEN` default access (via :code:`defaultAccessOnCreation`). Then create a form for opening work requests. + +The first group of agents (ordinary users) uses that form to create new work requests. Each agent would only see the work requests they themselves create because all other rows in that table would be hidden due to the :th:`_default_access` being :tc:`HIDDEN` and due to their being ordinary users. + +After the field worker in the first group syncs to the server, and the supervisors sync to the server, the set of work requests the field worker created will have become available on the supervisors' devices. The supervisor (a super-user or administrator) can then see and change the :th:`_row_owner` on each work request to one of the field agents in the second group. + +When the supervisor syncs to the server, and then the field agent in the second group (another ordinary user) syncs to the server, that field agent will see the work items that have been assigned to them (and they will not see any other work items because they are ordinary users of the system). + +When the agent in the first group next syncs, their created work item will disappear from their view because it is :tc:`HIDDEN` and the :th:`_row_owner` no longer matches this field agent's verified user id (it was assigned to the second agent). + +Upon completion of the task and after syncing to the server, after the supervisor next syncs, the supervisor could then change the :th:`_row_owner` to null or to a special placeholder value to remove it from the second agent's list of work items (and that removal would occur when that second agent next syncs with the server after the supervisor syncs his :th:`_row_owner` change). + +.. _data-permission-filters-usage-example: + +Example Application +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The app designer has a row-level access demo using the *geoweather* and *geoweather_conditions* tables and forms. + +.. note:: + + This demo only works on the device. + +To install the demo on the device: + + #. Force close all the apps. + #. Delete the :file:`/sdcard/opendatakit/default/` directory on the device. + #. From the app designer, execute + + .. code-block:: console + + $ grunt adbpush-tables-rowlevelaccessdemo + + #. Start ODK Survey and exit it. + #. Start ODK Tables. + +You will be presented with a demo launch screen. + +At this point, all the rows in all the tables have a :th:`_sync_state` of :tc:`new_row` and are fully editable and deletable. The demo will not become interesting until you set up and sync with a server. + +Set up an ODK Cloud Endpoint or ODK Aggregate 1.4.15 server with 2 ordinary users, 1 super-user and 1 tables administrator. :guilabel:`Reset App Server` to push the configuration and data up to the server. + +You are now an administrator (you needed to be in order to reset the server). You can choose :guilabel:`Change Row-Level Access Filters` to view and perhaps modify the default access and row owner of one or more rows. All rows in all tables are fully editable and deletable. + +Now, change your :guilabel:`Server Settings` to one of the ordinary users (a username other than *olive* or *sue*). Notice that the list of conditions from the *geoweather_conditions* table no longer contains the *Light Rain* option. That was hidden and will only be visible to a username of "olive" or a super-user or administrator. + +Use the table display on the :guilabel:`Change Row-Level Access Filters` page to examine what the :th:`_effective_access` for each row is in the various tables and verify that those settings are enforced. + +Change your :guilabel:`Server Settings` to different users to see how their effective accesses change. diff --git a/odk2-src/getting-started-2.rst b/odk2-src/getting-started-2.rst new file mode 100644 index 000000000..6a1d18d73 --- /dev/null +++ b/odk2-src/getting-started-2.rst @@ -0,0 +1,473 @@ +ODK 2.0 Getting Started Guide +=============================== + +.. _using-odk-2: + +The ODK 2.0 tools are intended to address limitations of the existing tool set. The 2.0 Tool Suite consists of: + +- :doc:`services-intro` - an application that handles database access, file access, and data synchronization services between all of the ODK 2.0 applications. It also allows you to synchronize data collected by the ODK 2.0 tools using the 2.0 protocol with an ODK Aggregate instance. +- :doc:`survey-intro` - a data collection application based upon HTML, CSS, and JavaScript. +- :doc:`tables-intro` - a data collection and visualization application running on your device. +- :doc:`cloud-endpoints-intro` - a ready-to-deploy server and data repository with enhancements to support bi-directional data synchronization across disconnected devices. +- :doc:`app-designer-intro` - a design environment for creating, customizing, and previewing your forms. +- :doc:`suitcase-intro` - a desktop tool for synchronizing data from an ODK 2.0 server so the data can be exported to CSV format. + +This page provides a brief end-to-end walk-through of the ODK 2.0 tools. It will cover the following topics: + +.. contents:: :local: + +.. _using-odk-2-apps: + +ODK Data Management Applications +---------------------------------------- + +The ODK 2.0 Android tools (ODK Survey, ODK Tables, ODK Services, ODK Scan, ODK Sensors Framework, and various ODK Sensor implementations) are APKs that are designed to work together to create a coherent tailored application experience for an end-user. + +.. note:: + + Together the ODK 2.0 tools create a platform, on top of which you can build your own data management applications. + +ODK 2.0 tools access configuration files and store data under sub-directories of the :file:`opendatakit` directory in the :file:`sdcard` root directory (whether your device has a physical SD card or not): :file:`/sdcard/opendatakit`. User applications constructed using the ODK 2.0 tools are identified by the name of the sub-directory holding those configuration and data files. Thus, :file:`/sdcard/opendatakit/mytestapp` would contain all the files and data for the *mytestapp* application. The name of that sub-directory, *mytestapp,* is referred to as the **AppName** of that application. The default **AppName** for the ODK tools is *default.* However, when configured appropriately, the ODK tools can run under another **AppName**, accessing configuration and saving data in a different subdirectory under opendatakit. + +This is handled in such a way that each user application is isolated from all other user applications, with separate configurations, data tables, and server settings. This allows one device to run multiple user applications built on top of the ODK 2.0 tools without any coordination among the teams developing those applications. + +A major goal of the 2.0 tools was to eliminate the need for any software engineering skills (e.g., Java programming, Android software development environment, source code version control systems) when designing data management applications. The skills required to build a data management application range from scripting a form definition in XLSX (similar to constructing ODK Collect forms using XLSX files processed by the XLSForm tool), to simple web programming -- modifying boilerplate HTML and JavaScript for custom presentations of the collected data. Advanced web programmers can also easily implement entirely-custom web pages. + +.. _using-odk-2-joining-a-server: + +Joining a device to an Existing Aggregate Server +------------------------------------------------------ + +The steps for joining a device to an existing Aggregate server are very straightforward. + + #. Install the APKs your application uses. + #. Launch the *home screen* APK, either ODK Survey or ODK Tables. + #. Click on the three vertical dots in the upper right corner of the menu bar and choose :menuselection:`Sync` to launch the ODK Services sync activity in the context of your *home screen* APK. + #. Configure ODK Services to point to the ODK Aggregate instance you want to join. + #. Choose :guilabel:`Sync now` to make the device mirror the contents on that ODK Aggregate server. + +Follow the steps described above to join the ODK Aggregate server hosting our simple demo, which uses ODK Tables as its *home_screen* APK. The detailed steps are: + + #. Download and install ODK Services, ODK Tables, and ODK Survey. + #. Launch ODK Tables (the *home_screen* APK). + #. Click on the three vertical dots in the upper right corner of the menu bar and choose :menuselection:`Sync` to launch the ODK Services. + #. The default Sync Configuration should be *https://open-data-kit.appspot.com* and :menuselection:`None (anonymous access)`. You will need to change that. It will also default to :menuselection:`Fully Sync Attachments`. + #. Click on the three vertical dots in the menu bar, select :menuselection:`Settings --> Server Settings`. + #. Click on :menuselection:`Server URL` and replace the default server with *https://opendatakit-simpledemo.appspot.com* then click :guilabel:`OK`. + #. Back out of settings then choose :guilabel:`Sync Now`. + +The synchronization process will now occur. + +.. note:: + + If there is an error, check to make sure the server URL is correct, then choose :guilabel:`Sync Now` again until it completes successfully. + +Once successful, back out of ODK Services, returning to ODK Tables. And back out of ODK Tables. Then relaunch ODK Tables. + +.. _using-odk-2-demo-tour: + +Tour of the Simple Demo Application +-------------------------------------- + +You should now see the custom home screen for the *Geotagger* demo: + +.. image:: /img/getting-started-2/geo-demo-home.* + :alt: Geotagging Demo Home + :class: device-screen-vertical + +This demo is based upon the *geotagger* data table and form. It allows users to record the date, time, GPS coordinates, description and picture of their current location. + +When you launch the demo by clicking the blue launch button, you see a map showing the collected data points, indicated with markers. By clicking on a marker, you bring its data record to the top of the list of records above the map. Clicking on the record header will expand or contract that item to show the coordinates and photo of that location. For example, if we click on the *Phinney Ridge* marker, its color changes from blue to green, and, if we then touch the *Phinney Ridge* heading, it expands to show the coordinates and image of that location: + +.. image:: /img/getting-started-2/phinney-ridge.* + :alt: Phinney Ridge + :class: device-screen-vertical + +You can add a new data record by choosing the :guilabel:`+` icon in top menu bar. This opens ODK Survey. + +.. note:: + + Since ODK Survey is being opened for the first time, it will initialize itself. This may take a few moments. + +.. image:: /img/getting-started-2/geotagger-new-location.* + :alt: Geotagger New Location + :class: device-screen-vertical + +Advance through and finalize this form. Upon finalizing the form, you will be returned to ODK Tables and its map view. You can then highlight the marker you added and view the image in the list view: + +.. image:: /img/getting-started-2/geotagger-odk-laboratory.* + :alt: Geotagger ODK Lab + :class: device-screen-vertical + +If you then click or tap in the list item details area (on the image), a detail view of the item will be displayed. + +From here, if you were to choose the pencil icon, ODK Survey would be launched to edit this record. + +You can also view the data in a list view or spreadsheet view by choosing the sheet icon in the menu bar and selecting the view you want: + +.. image:: /img/getting-started-2/view-type.* + :alt: View Types + :class: device-screen-vertical + +.. tip:: + + These other views can be useful if you need to access and complete data records that do not yet have location data and cannot therefore be displayed on a map. Try these other views now. + +Now back out of the *geotagger* table view and return to the custom home screen. Choose the three-horizontal-line icon on the top menu bar and choose :menuselection:`Sync`. This opens up ODK Services in its sync activity. Sync your device with the server (choose :guilabel:`Sync Noaw`). This will push your newly-added record to the server. You can see this by browsing to https://opendatakit-simpledemo.appspot.com click on the :guilabel:`ODK Tables` tab, choose the :guilabel:`View Table` sub-tab, and select the *geotagger* table. + +If you then repeat these steps with a different device, you can see that the two devices can share and exchange data, and revisions to this data, whenever they synchronize to the server. + +.. note:: + + During this process, there are two problem-resolution screens you are likely to encounter: + + - :ref:`Checkpoint Resolution ` - if ODK Survey exits without the user explicitly saving their additions or changes. + - :ref:`Conflict Resolution ` - if ODK Services detects a change on the server to a data record that was also changed on the device. + +.. _using-odk-2-demo-tour-checkpoints: + +Checkpoint Resolution +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The checkpoint resolution screen is most easily triggered if you choose the :guilabel:`+` icon then back out of ODK Survey: + +.. image:: /img/getting-started-2/checkpoint-resolution.* + :alt: Checkpoint Resolution + :class: device-screen-vertical + +When presented with this screen, there are three choices: + + - Cancel and continue editing the form. + - Ignore changes and discard the entire partially-filled-out form. + - Save it even though it is incomplete. In this case, since there is no entered data for this record, we can ignore changes. + +In rare cases, a second form of checkpoint resolution screen can be triggered. This most often happens if ODK Survey experiences a failure and closes. In this case, you may have several data records with unsaved checkpoint changes (changes that the user has not explicitly saved as incomplete or finalized). This will lead to a screen like: + +.. image:: /img/getting-started-2/checkpoint-list.* + :alt: Checkpoint List + :class: device-screen-vertical + +Clicking a row will display details about that individual checkpoint: + +.. image:: /img/getting-started-2/checkpoint-detail.* + :alt: Checkpoint Detail + :class: device-screen-vertical + +In all of these screens, you can choose whether to save the changes as incomplete or to discard them. + +.. _using-odk-2-demo-tour-conflicts: + +Conflict Resolution +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The conflict resolution screen is triggered when another device has edited one or more rows and synchronized its changes to the server before your edits to those same rows have been synchronized. In this case, your synchronization attempt will end with an error, and a :guilabel:`Conflicts Detected` error will appear: + +.. image:: /img/getting-started-2/conflict-resolution.* + :alt: Conflicts Resolutino + :class: device-screen-vertical + +Once you click :guilabel:`OK`, the conflict resolution screen will be presented. If there are multiple rows in conflict, this screen will display the rows that are in conflict: + +.. image:: /img/getting-started-2/conflict-list.* + :alt: Conflict List + :class: device-screen-vertical + +Clicking a row will display details about the conflict: + +.. image:: /img/getting-started-2/conflict-detail.* + :alt: Conflict Detail + :class: device-screen-vertical + +And if only a single row is in conflict, the list-of-rows screen will be bypassed. + +The conflict details screen displays the values of the field(s) in conflict, with the field value on the device (Local) appearing first. In this case, the *Description* field is in conflict. The device has *Kite hill at Gasworks* and the server has *Kite Hill ... Gasworks*. You can select either to take your device values (:guilabel:`Take Local Version`) or take the server's values (:guilabel:`Take Server Version`) or pick-and-choose among the changes and merge them (the :guilabel:`Merge Changes as Indicated Below` button will be enabled after all fields have had either their Local or Server value picked for the merge). After selecting the local version or choosing to merge, you must again synchronize with the server to push that change up to the server. + +.. warning:: + + When you resolve a conflict, your decision does not only affect you. The value you choose becomes the new true value and the next time you sync it will be written to the server. + +This concludes the tour of the *Geotagger* example application's screens, and the functionality within ODK Tables. We will now turn to setting up our own ODK Aggregate server, and setting the application up to run on that server. + +.. _using-odk-2-setting-up: + +Migrating / Setting-up an ODK 2.0 application +------------------------------------------------ + +Now that we have seen how a device can join an already-configured application, and synchronize its view of the data with the ODK Aggregate server hosting the application, it is time to set up our own ODK Aggregate server. + +The starting point for this is to have a fully configured application on your device. Only after you have your device configured as you want it to appear, would you proceed with the following steps. In this case, we already have the device configured with the *Geotagger* demo, so let's proceed to create an ODK Aggregate server and configure it to serve that demo to your devices. + +.. _using-odk-2-setting-up-server: + +Setting up the ODK Aggregate server +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Follow the instructions to install Aggregate from the ODK 1.x documentation. You must install the **ODK Aggregate v1.4.15** release. This is because we are transitioning away from Aggregate and towards :doc:`sync-endpoint`, but v1.4.15 will suit the purposes of this demo just fine. + +Once you have installed ODK Aggregate, log in with your super-user account. That process is also covered in the Aggregate installation documentation. + +Once logged in, enable the :doc:`aggregate-tables-extension`. You should grant the user account on your device the :guilabel:`Administer Tables` permissions. + +.. _using-odk-2-setting-up-reset: + +Resetting the Application on the Server +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Resetting the application on the ODK Aggregate server will push the application configuration on your device up to your server, replacing the configuration that is already on your server. Once the configuration is updated, data tables on the server and device will be synced. This process does not destroy data on the server, but instead merges changes on the client with any existing data tables on the server (this enables you to update your configuration without worrying about damaging or destroying the data already captured on the server). + +Return to your device, start ODK Tables: + + #. Click the diminishing-lines icon to leave the custom home screen. + #. Click the three vertical dots and select :guilabel:`Sync` to launch ODK Services onto the sync screen. + #. Choose :menuselection:`Settings --> Server Settings`. + #. Edit the :guilabel:`Server URL` to be the URL for this newly configured ODK Aggregate server (e.g., https://myodk-test.appspot.com). + #. Click on :guilabel:`Server Sign-on Credential` and choose :menuselection:`Username`. + #. Choose :guilabel:`Username` and enter the superuser username for your ODK Aggregate server + #. Choose :guilabel:`Server Password` and enter the ODK Aggregate server password for that superuser username. + #. Click the back button until you have returned to the sync screen. + #. Click on :guilabel:`Reset App Server` to push your device configuration up to your ODK Aggregate server + +After this has completed, you have created your own server that replicates the configuration and contents of the https://opendatakit-simpledemo.appspot.com site. Congratulations! + +.. note:: + + Any device with a user account with :guilabel:`Administer Tables` permissions can reset the app server. If you configure a device with a user account (or Anonymous user) with just the :guilabel:`Synchronize Tables` permissions, they will not be able to reset the app server and will just be able to sync and join into the existing ODK 2.0 application on this ODK Aggregate server. + +.. _using-odk-2-config: + +Configuring your Device with an Application +----------------------------------------------- + +Next, we will work through the steps to configure your device with an ODK 2.0 application (rather than downloading an existing application from a server). + +This task begins with setting up the :doc:`app-designer-intro` on your computer. + +For the purposes of this tutorial, we have created a copy of the Application Designer that only contains the files for this *Geotagger* example (it is otherwise identical). + +.. _using-odk-2-config-setup-app-designer: + +Setting up ODK Application Designer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Read the :ref:`Intro ` and :ref:`Overview ` sections to get a sense of the features and functionality of the ODK 2.0 Application Designer environment (we will install it below). Follow this guide to :doc:`app-designer-setup`. + +Finally, follow this guide to :doc:`app-designer-launching`. + +If successful, the :program:`cmd` window (on Windows) should display some status messages. Below is a screen-shot of my :program:`cmd` window beginning with a :program:`dir` of the contents of the directory, and running :program:`grunt` in that directory: + +.. image:: /img/getting-started-2/geotagger-cmd-window.* + :alt: Geotagger Command Window + +And a :program:`Chrome` browser window should open to display: + +.. image:: /img/getting-started-2/geotagger-chrome-window.* + :alt: Geotagger Chrome Window + +If a :program:`Chrome` browser does not open, try manually launching it and opening http://localhost:8000/index.html. + +You can further verify that the Application Designer works by clicking on the :guilabel:`Geotagger` button, then clicking on :guilabel:`Follow link`. This opens the *Geotagger* form on your computer, and simulates all the features available to you on your device. + +You can also try other things, like choosing different device dimensions to see how the form renders on different screen geometries. + +We will return to this design environment later. + +.. _using-odk-2-config-deploy: + +Deploying to the Device +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now that we have the design environment installed and functioning, and because that environment has a copy of the fully-configured *Geotagger* application that is running on https://opendatakit-simpledemo.appspot.com (minus any data that users have submitted to the server), we can work through the steps of deploying that application to your device, and then resetting your server to push that configuration up to your server. + + +First, confirm that your device has :guilabel:`USB debugging` enabled inside your device's :guilabel:`Settings`. This checkbox is in different places on different devices and may be hidden by default on some. See this guide to `USB debuggin on Android `_ for instructions. + +Return to the :program:`cmd` window on your computer. :kbd:`Control-C` to stop the :program:`grunt` command that popped-open the :program:`Chrome` browser. On Windows, you will be asked to confirm this ``Terminate batch job (Y/N)?``. Enter ``Y`` to confirm. + +Connect your device to your computer via USB. Wait for the storage connection to be established (on Windows, this will generally pop up a file browser or an options box that enables you to select a file browser). + +At the command prompt, type: + +.. code-block:: console + + $ grunt adbpush + +.. warning:: + + This command will force-close ODK Services, Survey, and Tables, and it will clear all ODK 2.0 data from the device. The data you are pushing will overwrite any exiting application or collected data you might have. Be sure to make backups and be sure you are ready before running this command. + +This pushes the configured ODK 2.0 application within this ODK 2.0 Application Designer directory to your device. Because this is a stripped-down version of the Application Designer that only contains the simple demo files, this will copy only those files to the device. When you issue this command, the :program:`cmd` window will display a long series of commands and conclude with a display of overall progress and timings: + +.. image:: /img/getting-started-2/geotagger-cmd-gruntpush.* + :alt: Geotagger Grunt Push + +Now, on your device, launch ODK Tables. + +This will initiate the configuration of ODK Tables and conclude with a :guilabel:`Configuration Summary` pop-up reporting that everything was imported successfully. Click :guilabel:`OK`. + +Everything should now appear as it did with the application you first joined on https://opendatakit-simpledemo.appspot.com, except you will only have the data rows configured by the ODK 2.0 Application Designer zip, and not any added or modified since that time. + +.. _using-odk-2-config-reset-server: + +Resetting the Server +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Once you have the application running on the device, you will typically need to reset the contents of the application server. While the :guilabel:`Reset App Server` button on the device can shuffle the various supporting files between the device and the server, it will not destroy data tables that already exist on the server; this is intentional -- we want to minimize the potential for accidental loss of data. + +.. note:: + + Whenever you are developing an application, and have found a need to add a new column to an existing table, you will need to manually delete the data tables from the server before using the :guilabel:`Reset App Server` button from the device. + +Open a browser window to the server, log in with a user that has :guilabel:`Administer Tables` or :guilabel:`Site Admin` privileges. + +Navigate to the :guilabel:`ODK Tables / Current Tables` sub-tab. + +Delete each of the tables here. In this case, there will be only one, *Geotagger*. The server will now have a set of App-Level files but no data tables, forms for those tables, or data files. Except for the app-level files, it is clean. + +.. note:: + + If your table has a large number of configuration files or data rows, the server may time out during the deletion process. In this case, the next time you try to create the table on the server, it will resume the deletion process, and potentially time out again until such time as it is able to finish the deletion; only then will it re-create the table. + +Now, from your device, launch ODK Tables, click on the sync icon (two curved arrows) to launch ODK Services, make sure you are logging in as a user with :guilabel:`Administer Tables` or :guilabel:`Site Admin` privileges, and choose :guilabel:`Reset App Server`. + +The synchronization process will create the tables and push your content up to this server. Note that the server now only contains the data rows present on the device -- it no longer has any of the additional data records from the demo site. + +You have now successfully set up the Application Designer, used it to deploy an application to a device, and, from that device, configured an ODK Aggregate server to supply that application to other devices you join to that server. + +.. _using-odk-2-modify: + +Modifying an ODK 2.0 application +------------------------------------- + +The final task is to modify the *Geotagger* example by adding a new data field to it. + + +The overall development process is: + + #. :ref:`Revise the data entry form ` + #. :ref:`Update the initialization files needed by ODK Tables ` + #. :ref:`Update the preloaded data values as needed ` + #. :ref:`Update the HTML to include the new field ` + +And then follow the steps in the preceding section to deploy the modified application to the device and push the application up to an ODK Aggregate server. + +.. _using-odk-2-modify-data-entry: + +Modifying the data entry form +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Return to your :program:`cmd` window and once again launch the ODK 2.0 Application Designer environment (and a :program:`Chrome` browser) by typing: + +.. code-block:: console + + $ grunt + +Now, open a file browser and navigate to the directory where you downloaded the Application Designer. Then navigate within that directory to :file:`app/config/tables/geotagger`. Rename the :file:`properties.csv` and :file:`definition.csv` files in this directory to :file:`orig.properties.csv` and :file:`orig.definition.csv`. These were the initialization files needed by ODK Tables and they will need to be regenerated because we are altering the data table to incorporate an additional question. + +Navigate within that directory to :file:`app/config/tables/geotagger/forms/geotagger`. Open the :file:`geotagger.xlsx` file in :program:`Excel` (or :program:`OpenOffice`). This is the form definition used by ODK Survey. + +We will be adding a question to ask the user what direction they were facing when they took the photo. For this example, we will be collecting a text response. A more realistic modification might restrict the user to a set of choices (North, Northwest, West, Southwest, South, etc.). + +On the survey worksheet, after the image-capture prompt, add a row that looks like the following. + +.. list-table:: New Survey Row + :header-rows: 1 + + * - type + - name + - display.text + - display.hint + * - string + - Direction + - Image Direction + - Enter the direction in which the photo was taken (North, South, East, West, etc.) + +Save your changes and go back to the Application Designer. Click on the tab that says :guilabel:`XLSX Converter`. Choose this XLSX file or use your file browser to drag and drop the :file:`geotagger.xlsx` file onto this screen (dragging and dropping is not supported on all operating systems). + +You should now see some JSON in the output window. Hit the :guilabel:`Save to File System` button. This will display three pop-up notifications announcing that the Application Designer is + + 1. Writing the updated ODK Survey form definition into the :file:`formDef.json` file in the same location as the :file:`geotagger.xlsx` file. + 2. Updating the :file:`definition.csv` file. + 3. Updating the :file:`properties.csv` file. + +.. note:: + + The :file:`definition.csv` and :file:`properties.csv` files are updated because the *form_id* is the same as the *table_id*. + +Go back to the :program:`Chrome` Browser and click on the :guilabel:`Preview` tab. Click on :guilabel:`Purge Database`. This will delete the earlier *Geotagger* data table -- a necessary step because we are adding a :th:`Direction` column to that data table. Select :guilabel:`Geotagger` if you do not already have that form open. + +Create a new instance of *Geotagger* and advance through it (this will create the data table with the new :th:`Direction` column); confirm that the new question is displayed. Note that the date and description are required fields and will generate error pop-ups if you attempt to advance through those prompts without supplying a value. + +You have now successfully modified the form. + +.. _using-odk-2-modify-init: + +Updating the Initialization Files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Fortunately, because the geotagger *formId* matches the *tableId*, by simply using the :guilabel:`Save to File System` button on the CSV, the tool will automatically regenerate the :file:`definition.csv` and :file:`properties.csv` files for this form. Furthermore, the configuration that ODK Tables uses to specify what HTML files to use for the list, detail, and map views are all specified within the XLSX file on the properties sheet. No manual actions are required! + +Now, deploy your updated application to your device; launch ODK Tables to initialize and load your application. Confirm that when you edit a data row that you are now asked for the direction in which the photo was taken. + +.. _using-odk-2-modify-preload: + +Updating the Preloaded Data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +At this point, we have added the new field to the data table, but have not yet updated the initial set of *Geotagger* locations with values for that field. + +Return to your :file:`Application Designer` directory. Recall that when an ODK Tables application first starts up, it reads the :file:`assets/tables.init` file; that file identifies CSV files within :file:`config/assets/csv` that should be imported into the data tables upon first start-up. Read more about importing data into a table from a CSV in the :ref:`ODK Tables guide `. + +In this example application, the file being imported is :file:`config/assets/csv/geotagger.updated.csv`. If we wanted to, we could edit this file, add a column for the new data field (:th:`Direction`), and supply values for this field for all of the data rows that form the initial set of *Geotagger* locations. + +Alternatively, we can return to the device and use the CSV export functionality within ODK Tables to export the CSV file (into :file:`/sdcard/opendatakit/default/output/csv`); then pull it off of the device and overwrite the CSV file under the Application Designer at :file:`app/config/assets/csv/geotagger.updated.csv`. Finally, open that file and fill in values for the :th:`Direction` column. + +.. warning:: + + Some CSV editors, like :program:`Office` or :program:`OpenOffice`, may convert or alter the content inappropriately when you save changes. If your edits cause the device to fail to initialize the data fields, you may need to make this edit manually using a less-sophisticated tool or choose different options when saving your changes. + +.. _using-odk-2-modify-html: + +Updating the HTML files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are two areas where image information is displayed, one is in the list view, where you can expand or collapse an item, and the other is in the detail view, which is opened when you click or tap on an expanded item in the list view. We will only modify this detail view to report the image direction; a more comprehensive edit would likely also update the expanded item within the list view. + +To determine all the HTML files, we can begin with the files referenced in the :file:`properties.csv` file we just finished editing. Looking again at that file, we see three files referenced: + + - :file:`tables/geotagger/html/geo_list.html` + - :file:`tables/geotagger/html/geo_list_thumbnail.html` + - :file:`tables/geotagger/html/geo_detail.html` + +Each of these files, or the JavaScript within them, might open or reference other files that might need to be updated. The above files are simply the ones we know are reachable. In general, files for displaying table-specific data are under the :file:`config/tables/tableid` directory. In this example, we will just modify the last of these files and its associated JavaScript file. + +Open a file browser and navigate to the directory where you downloaded the Application Designer. Then navigate within that directory to :file:`app/config/tables/geotagger/html`. Open :file:`geo_detail.html` in a text editor. Insert a line that defines a *DIR* element above the *Latitude* line in the HTML body region. This will be where we will display the value of the *Direction* field. For example: + +.. code-block:: html + +

+

Image Direction:

+

Latitude:

+ +Save the file. Now, navigate to :file:`app/config/tables/geotagger/js`. Open :file:`geo_detail.js` in a text editor. Navigate down to the bottom of the :code:`display()` JavaScript function (to line 44). And add before the closing bracket: + +.. code-block:: javascript + + var dir = geoDetailResultSet.get("Direction"); + document.getElementById("DIR").innerHTML = dir; + +Save the file. Once again, push the application to the device. Confirm that when you expand a item in the map list window, and then tap on that expanded item, that it now shows *Image Direction:*. (See example below.) + +.. image:: /img/getting-started-2/geotagger-image-dir.* + :alt: Geotagging Image Direction + :class: device-screen-vertical + +Congratulations, you have successfully modified this ODK 2.0 application to add a new data field and display it as a field in the HTML detail-view page. + +You could now log onto your server, delete the geotagger table, reset your server, and start collecting geopoints with the new image direction field. + +.. _using-odk-2-next: + +Next Steps +----------------------- + +The XLSX file format and the supplied prompt types are described in the :doc:`xlsx-converter-intro` documentation. The tools allow arbitrary customization and the definition of new prompt types. diff --git a/odk2-src/glossary.rst b/odk2-src/glossary.rst new file mode 100644 index 000000000..289fb5e53 --- /dev/null +++ b/odk2-src/glossary.rst @@ -0,0 +1,97 @@ +Glossary +============== + +.. ODK-compatible tools + +.. glossary:: :sorted: + + participant + + A person being interviewed by a user of :term:`Collect`. (Also sometimes called a "subject".) + + enumerator + + A person who conducts a survey. + + instance + + The file representing a filled-in form. + + Collect + + Part of ODK. + + An Android mobile app that replaces paper-based forms. + + Aggregate + + Part of ODK. + + A server-side data storage and analysis tool. + + Build + + Part of ODK. + + An application that lets you design forms with a drag-and-drop form interface. + + - `Use ODK Build online `_. + - `Download a desktop version of ODK Build `_. + + XLSForm + + Part of ODK. + + A tool for building forms with Microsoft Excel. + + Validate + + Part of ODK. + + A tool for validating forms against the :term:`ODK XForms specification `. + + Form Uploader + + Part of ODK. + + A tool for uploading blank forms and their media files to ODK Aggregate. + + Briefcase + + Part of ODK. + + A tool for packaging and transferring forms and data between instances of Collect and Aggregate. + + ODK XForm + + Part of ODK. + + A specification defining valid XML-based forms for ODK. It is a subset of the `W3C XForms 1.0 specification `_ + + `View ODK XForms Specification `_ + + ODK JavaRosa + + Part of ODK. + + A Java library that renders ODK Compliant XForms. + + `ODK JavaRosa source on Github `_ + + form + + A defined set of questions and answer choices displayed by an application that can render forms written in the XForm standard. + + widget + + A single question, answer set, and attendant GUI elements, as rendered in a XForm compliant app such as :term:`Collect`. + + hint + + Additional help text on a single question, displayed after the label. + + question label + question text + + The main body of a form question or widget. In an :term:`XLSForm`, the contents of the :th:`label`. + diff --git a/odk2-src/img/app-designer-overview/app-designer-preview.png b/odk2-src/img/app-designer-overview/app-designer-preview.png new file mode 100644 index 000000000..3216f039c --- /dev/null +++ b/odk2-src/img/app-designer-overview/app-designer-preview.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:26ee2eb6a27fc4c17cbab50368ec594149f329b44a905ad0d7ae98bad4d8b94e +size 74368 diff --git a/odk2-src/img/app-designer-overview/file-browser.png b/odk2-src/img/app-designer-overview/file-browser.png new file mode 100644 index 000000000..a480df816 --- /dev/null +++ b/odk2-src/img/app-designer-overview/file-browser.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e5c6fba63e81b4bb59e501e3605e8884be45d15f7e393583305f2cbb88c9d305 +size 29166 diff --git a/odk2-src/img/app-designer-overview/household-survey.png b/odk2-src/img/app-designer-overview/household-survey.png new file mode 100644 index 000000000..ac6ff8bb1 --- /dev/null +++ b/odk2-src/img/app-designer-overview/household-survey.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f2a1aad47d73635b04228abb19698aca1c1010d55f476efd566632e031256a0 +size 37109 diff --git a/odk2-src/img/app-designer-overview/scan-form-designer.png b/odk2-src/img/app-designer-overview/scan-form-designer.png new file mode 100644 index 000000000..5bb583fe4 --- /dev/null +++ b/odk2-src/img/app-designer-overview/scan-form-designer.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbfc12ab29ef4915ef518517b7cbc69cc66f8f05567f61c20fe77c16ea295da2 +size 18666 diff --git a/odk2-src/img/app-designer-overview/theme-generator.png b/odk2-src/img/app-designer-overview/theme-generator.png new file mode 100644 index 000000000..0b13dc04e --- /dev/null +++ b/odk2-src/img/app-designer-overview/theme-generator.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c3f91a88626505bfc01bfa3106bf49139797e9cbcfce5741e19a18563df2667 +size 38946 diff --git a/odk2-src/img/app-designer-overview/xlsxconverter.png b/odk2-src/img/app-designer-overview/xlsxconverter.png new file mode 100644 index 000000000..78efb9734 --- /dev/null +++ b/odk2-src/img/app-designer-overview/xlsxconverter.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f7b2c206801b92c0876f9b4b8d4f0c49c5e0733d190db2e290d09ab7dcb3e002 +size 20132 diff --git a/odk2-src/img/data-permission-filters/verify-user.png b/odk2-src/img/data-permission-filters/verify-user.png new file mode 100644 index 000000000..3f22894bd --- /dev/null +++ b/odk2-src/img/data-permission-filters/verify-user.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a119f21468dc696fd748e330773e6147c945e81211e048743351d239beaae0e +size 17944 diff --git a/odk2-src/img/getting-started-2/checkpoint-detail.png b/odk2-src/img/getting-started-2/checkpoint-detail.png new file mode 100644 index 000000000..cdfa4569f --- /dev/null +++ b/odk2-src/img/getting-started-2/checkpoint-detail.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:14048eba5118ba57660913def938e08542777909b0212f9d488fc36acade3475 +size 48496 diff --git a/odk2-src/img/getting-started-2/checkpoint-list.png b/odk2-src/img/getting-started-2/checkpoint-list.png new file mode 100644 index 000000000..3e2373880 --- /dev/null +++ b/odk2-src/img/getting-started-2/checkpoint-list.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:76fe312c40da1999ecf1a854ffc8259f2b5c9d1a9dc8b85630e2c251cff21739 +size 47297 diff --git a/odk2-src/img/getting-started-2/checkpoint-resolution.png b/odk2-src/img/getting-started-2/checkpoint-resolution.png new file mode 100644 index 000000000..91187d1f7 --- /dev/null +++ b/odk2-src/img/getting-started-2/checkpoint-resolution.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f260a8ec67a6dcfd18aedeb1a7f81c77f50357a5906433df5ea29f60702f010d +size 83185 diff --git a/odk2-src/img/getting-started-2/conflict-detail.png b/odk2-src/img/getting-started-2/conflict-detail.png new file mode 100644 index 000000000..37cc4a3ea --- /dev/null +++ b/odk2-src/img/getting-started-2/conflict-detail.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7d504da9ad1f0f508f5ec2e4539c7e8935ba17a1ecacc9ec7cb450152baaac4d +size 54301 diff --git a/odk2-src/img/getting-started-2/conflict-list.png b/odk2-src/img/getting-started-2/conflict-list.png new file mode 100644 index 000000000..2b5eb4336 --- /dev/null +++ b/odk2-src/img/getting-started-2/conflict-list.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d170f34e9c35d6341636ebe9c56b0d39dc3248acf621e7bd4a10c55274a70816 +size 46939 diff --git a/odk2-src/img/getting-started-2/conflict-resolution.png b/odk2-src/img/getting-started-2/conflict-resolution.png new file mode 100644 index 000000000..0244df35b --- /dev/null +++ b/odk2-src/img/getting-started-2/conflict-resolution.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7d59ba69940d7484e9e4b54fbaab1dcf4aee742eefd1b450835573635622dfa7 +size 72378 diff --git a/odk2-src/img/getting-started-2/geo-demo-home.png b/odk2-src/img/getting-started-2/geo-demo-home.png new file mode 100644 index 000000000..ef46b7a7c --- /dev/null +++ b/odk2-src/img/getting-started-2/geo-demo-home.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:edd39b190c9624f5356f8249984a97b60c14bb660bda8dd2819b6ea630f4ef81 +size 1621443 diff --git a/odk2-src/img/getting-started-2/geotagger-chrome-window.png b/odk2-src/img/getting-started-2/geotagger-chrome-window.png new file mode 100644 index 000000000..8128501d7 --- /dev/null +++ b/odk2-src/img/getting-started-2/geotagger-chrome-window.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e10a8f505c52eb57841f193bafbe26983a9304a0366637d42658661f7c59f1cd +size 47571 diff --git a/odk2-src/img/getting-started-2/geotagger-cmd-gruntpush.png b/odk2-src/img/getting-started-2/geotagger-cmd-gruntpush.png new file mode 100644 index 000000000..ec74290dc --- /dev/null +++ b/odk2-src/img/getting-started-2/geotagger-cmd-gruntpush.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6d482cf3012291aaeee04a2aaee1f7f9fccc777fa1e576e7c66427e0451556f +size 78270 diff --git a/odk2-src/img/getting-started-2/geotagger-cmd-window.png b/odk2-src/img/getting-started-2/geotagger-cmd-window.png new file mode 100644 index 000000000..7771284d9 --- /dev/null +++ b/odk2-src/img/getting-started-2/geotagger-cmd-window.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e0483de3ebcad844711068e4b95d3e80a3e0a5c05906408039c43e25fa69ec7e +size 52694 diff --git a/odk2-src/img/getting-started-2/geotagger-image-dir.png b/odk2-src/img/getting-started-2/geotagger-image-dir.png new file mode 100644 index 000000000..f627ed61d --- /dev/null +++ b/odk2-src/img/getting-started-2/geotagger-image-dir.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c5781d0b68c5ea6391e83d2be73d969fa49ada09863334df92532bed224797d +size 1889466 diff --git a/odk2-src/img/getting-started-2/geotagger-new-location.png b/odk2-src/img/getting-started-2/geotagger-new-location.png new file mode 100644 index 000000000..00f3cda61 --- /dev/null +++ b/odk2-src/img/getting-started-2/geotagger-new-location.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a01681cc9be71fe338af8d886887085a0042acfb13ea63b46d0f56a9a2390b21 +size 85475 diff --git a/odk2-src/img/getting-started-2/geotagger-odk-laboratory.png b/odk2-src/img/getting-started-2/geotagger-odk-laboratory.png new file mode 100644 index 000000000..19e3b7e40 --- /dev/null +++ b/odk2-src/img/getting-started-2/geotagger-odk-laboratory.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c50ffccf19774f951a9eba7dec069cadb2d9fb9222a332f981e961691d8f4857 +size 652261 diff --git a/odk2-src/img/getting-started-2/phinney-ridge.png b/odk2-src/img/getting-started-2/phinney-ridge.png new file mode 100644 index 000000000..3ea743c40 --- /dev/null +++ b/odk2-src/img/getting-started-2/phinney-ridge.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4822ebc3ec0457497224ecea9038827eeb92f23a820347f1b430de97de50283 +size 412298 diff --git a/odk2-src/img/getting-started-2/view-type.png b/odk2-src/img/getting-started-2/view-type.png new file mode 100644 index 000000000..45e006a4c --- /dev/null +++ b/odk2-src/img/getting-started-2/view-type.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:907468b9d85d460882fb8a2b7b22af226d207111bc1cc46725cf85d8a53942d6 +size 394381 diff --git a/odk2-src/img/scan-form-designer/create-bubbles-menu.png b/odk2-src/img/scan-form-designer/create-bubbles-menu.png new file mode 100644 index 000000000..14933d1a1 --- /dev/null +++ b/odk2-src/img/scan-form-designer/create-bubbles-menu.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:434ef7815235450f8a045fee680640f3caa9f296c3c78003adbcc44a2e9b8298 +size 32551 diff --git a/odk2-src/img/scan-form-designer/create-checkbox-menu.png b/odk2-src/img/scan-form-designer/create-checkbox-menu.png new file mode 100644 index 000000000..e205484a1 --- /dev/null +++ b/odk2-src/img/scan-form-designer/create-checkbox-menu.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:31c151674ffca08cbce8e793c577a2387f098a9662d0ea5cafc7112b4b0cd8a8 +size 40963 diff --git a/odk2-src/img/scan-form-designer/edit-fields-menu.png b/odk2-src/img/scan-form-designer/edit-fields-menu.png new file mode 100644 index 000000000..85daca8b4 --- /dev/null +++ b/odk2-src/img/scan-form-designer/edit-fields-menu.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ccde6f73ce9121d92a523fcb8163398366a08690e917eca58eb0e86d4f841c9 +size 15575 diff --git a/odk2-src/img/scan-form-designer/print-file-select.png b/odk2-src/img/scan-form-designer/print-file-select.png new file mode 100644 index 000000000..d22f4bf3f --- /dev/null +++ b/odk2-src/img/scan-form-designer/print-file-select.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:da26c3ae283bff792fdd2cd75b5b903d36c400a417f052f63918d22c6fc8bb4a +size 48187 diff --git a/odk2-src/img/scan-form-designer/scan-form-add-image.png b/odk2-src/img/scan-form-designer/scan-form-add-image.png new file mode 100644 index 000000000..427bd6c3f --- /dev/null +++ b/odk2-src/img/scan-form-designer/scan-form-add-image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df30633fa769ff2c348f5d1ba5f7c72abe470087935c0ede67558746dd30e2a7 +size 68878 diff --git a/odk2-src/img/scan-form-designer/scan-form-apply-image.png b/odk2-src/img/scan-form-designer/scan-form-apply-image.png new file mode 100644 index 000000000..b27db467d --- /dev/null +++ b/odk2-src/img/scan-form-designer/scan-form-apply-image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13353b444a869b3ed3123f9cbdf446d2d4d3500440f46cd0f903a1e42e94a4ca +size 64633 diff --git a/odk2-src/img/scan-form-designer/scan-form-blank.png b/odk2-src/img/scan-form-designer/scan-form-blank.png new file mode 100644 index 000000000..53f554bb5 --- /dev/null +++ b/odk2-src/img/scan-form-designer/scan-form-blank.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6a49d79f1dddb14fb7dc77265e2e5a95bf6266086b8b014cfad95f498d2bc521 +size 64319 diff --git a/odk2-src/img/scan-form-designer/scan-form-export.png b/odk2-src/img/scan-form-designer/scan-form-export.png new file mode 100644 index 000000000..0b9173745 --- /dev/null +++ b/odk2-src/img/scan-form-designer/scan-form-export.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:014a2cc80bcad3528d0912b8885baf46814bda190635680c0439e40401bbf26f +size 31222 diff --git a/odk2-src/img/scan-form-designer/scan-form-file-system.png b/odk2-src/img/scan-form-designer/scan-form-file-system.png new file mode 100644 index 000000000..b6d51830a --- /dev/null +++ b/odk2-src/img/scan-form-designer/scan-form-file-system.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e51fda90eafccf9c2ff0842424f78d899b783bd732f3b699aeda2b9d532030ca +size 31619 diff --git a/odk2-src/img/scan-form-designer/scan-form-load.png b/odk2-src/img/scan-form-designer/scan-form-load.png new file mode 100644 index 000000000..deb7cdd97 --- /dev/null +++ b/odk2-src/img/scan-form-designer/scan-form-load.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fbbc5933e16160002a73513c2b042bac53027e2a174bf99b21b388126d2dbbe0 +size 31440 diff --git a/odk2-src/img/scan-form-designer/scan-form-save.png b/odk2-src/img/scan-form-designer/scan-form-save.png new file mode 100644 index 000000000..427799d61 --- /dev/null +++ b/odk2-src/img/scan-form-designer/scan-form-save.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:748b94654308fd90ec8940f37870356acf98aa621e5ea5bb42ad1ed280d571fb +size 31133 diff --git a/odk2-src/img/scan-form-designer/set-page-style.png b/odk2-src/img/scan-form-designer/set-page-style.png new file mode 100644 index 000000000..3e18bfc9c --- /dev/null +++ b/odk2-src/img/scan-form-designer/set-page-style.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d29881f3a550d836ef76fd2b87ecfea72060519335a0464b952295cf50f65acc +size 27510 diff --git a/odk2-src/img/scan-form-designer/written-numbers.jpg b/odk2-src/img/scan-form-designer/written-numbers.jpg new file mode 100644 index 000000000..118c93fab --- /dev/null +++ b/odk2-src/img/scan-form-designer/written-numbers.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36081260ddc4e64a570e1d00ab73610c0e713705cd96c6e984518b539dc10896 +size 10868 diff --git a/odk2-src/img/scan-intro/scan-process.png b/odk2-src/img/scan-intro/scan-process.png new file mode 100644 index 000000000..fd8f1e817 --- /dev/null +++ b/odk2-src/img/scan-intro/scan-process.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cee3d807cfaaedc877bf948a3a505bb7c338b033c7433fa01acdb973c5539264 +size 129842 diff --git a/odk2-src/img/scan-using/scan-camera.jpg b/odk2-src/img/scan-using/scan-camera.jpg new file mode 100644 index 000000000..8fa878f5f --- /dev/null +++ b/odk2-src/img/scan-using/scan-camera.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8aa10ef873eedc22aced0f4c7402a3490b2b6db44a45c731e24bac8d76097b4e +size 13851 diff --git a/odk2-src/img/scan-using/scan-finalize.png b/odk2-src/img/scan-using/scan-finalize.png new file mode 100644 index 000000000..e18ef8573 --- /dev/null +++ b/odk2-src/img/scan-using/scan-finalize.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a20157ee3955efe06178c6556ed4c1a63f17f6f5d7185ab16d48b2a806d80f05 +size 18290 diff --git a/odk2-src/img/scan-using/scan-image-markup.png b/odk2-src/img/scan-using/scan-image-markup.png new file mode 100644 index 000000000..7b94e83d5 --- /dev/null +++ b/odk2-src/img/scan-using/scan-image-markup.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:881dc981b5f7470c98cdd4f0d5a6ae4d5a48afa44d8fb41e388dffdf65e1f960 +size 101899 diff --git a/odk2-src/img/scan-using/scan-review-data.png b/odk2-src/img/scan-using/scan-review-data.png new file mode 100644 index 000000000..a83d86574 --- /dev/null +++ b/odk2-src/img/scan-using/scan-review-data.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:379bad7941e14dd40c667cac33e05774ca44bec72c7591412d94a5351ea87f32 +size 37352 diff --git a/odk2-src/img/scan-using/scan-single-template.png b/odk2-src/img/scan-using/scan-single-template.png new file mode 100644 index 000000000..b47eed035 --- /dev/null +++ b/odk2-src/img/scan-using/scan-single-template.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:272c849bd66b54d6882089a371df2d89799656b4752ca31477518a1f48dc2908 +size 24207 diff --git a/odk2-src/img/scan-using/scan-stand.png b/odk2-src/img/scan-using/scan-stand.png new file mode 100644 index 000000000..1d6eb7c2d --- /dev/null +++ b/odk2-src/img/scan-using/scan-stand.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c3ff8c1b1cac7d209fb7cce7a637e523d517c430b4fc9f2b756b04cb5e22af3a +size 173962 diff --git a/odk2-src/img/scan-using/scan-tables-view.png b/odk2-src/img/scan-using/scan-tables-view.png new file mode 100644 index 000000000..54242c3f7 --- /dev/null +++ b/odk2-src/img/scan-using/scan-tables-view.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e91022354356aad020cf44b1234691ad31011457c5de4adef142549750b9630e +size 41600 diff --git a/odk2-src/img/scan-using/scan-template-list.png b/odk2-src/img/scan-using/scan-template-list.png new file mode 100644 index 000000000..6e593ed5c --- /dev/null +++ b/odk2-src/img/scan-using/scan-template-list.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a4ee80f2c8f53880ad370dbede5d57e19d99cff833e467da14ac511ef81cba2f +size 28728 diff --git a/odk2-src/img/scan-using/scan-transcribe-text.png b/odk2-src/img/scan-using/scan-transcribe-text.png new file mode 100644 index 000000000..af78f50c7 --- /dev/null +++ b/odk2-src/img/scan-using/scan-transcribe-text.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f670aae57f26749216c3a6b05dacd1952407d66c7298d1ed6c24f0568d388bdc +size 43751 diff --git a/odk2-src/img/scan-using/scan-verify-bubble.png b/odk2-src/img/scan-using/scan-verify-bubble.png new file mode 100644 index 000000000..fa26d9b45 --- /dev/null +++ b/odk2-src/img/scan-using/scan-verify-bubble.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a7d4aaea182b7da77c4318893aa1b89d8fc46817b65c83f12ee922a5332692e2 +size 18332 diff --git a/odk2-src/img/scan-using/scan-verify-number.png b/odk2-src/img/scan-using/scan-verify-number.png new file mode 100644 index 000000000..66502c0f1 --- /dev/null +++ b/odk2-src/img/scan-using/scan-verify-number.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5d40cb592d26eb4c6a8401db9a7649d161f290f03bebf46960a9e7e284f727a3 +size 31100 diff --git a/odk2-src/img/xlsxconverter/survey-screen.png b/odk2-src/img/xlsxconverter/survey-screen.png new file mode 100644 index 000000000..fcb8fb6a4 --- /dev/null +++ b/odk2-src/img/xlsxconverter/survey-screen.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64a74a44b063bc18e45bfde3afb7f9e65653b66669b11d602b12abd829c385b0 +size 6265 diff --git a/odk2-src/index.rst b/odk2-src/index.rst new file mode 100644 index 000000000..39ca1c9ce --- /dev/null +++ b/odk2-src/index.rst @@ -0,0 +1,97 @@ +Welcome to Open Data Kit 2.0's documentation! +================================================== + +.. _odk-2-introduction: + +The :dfn:`ODK 2.0 Tool Suite` is a new set of ODK tools that will co-exist with the existing ODK 1.0 Tool Suite. It targets advanced users who find themselves limited by the ODK 1.0 data collection workflows. It provides: + +- **Fully customizable layout of prompts on the Android device**. The 2.0 tools use HTML, JavaScript, and CSS to specify the layout of nearly all the screens viewed by the data collectors. This enables individuals and organizations with basic web development skills to modify and customize the appearance of their surveys and workflow. At the same time, we retain the easy-to-use spreadsheet-based definition of the survey questions (however, this XLSX Converter mechanism is not cross-compatible with XLSForm). +- **More flexible, user-directed, navigation of a survey**. The 2.0 tools do not impose a strict sequential advancement through a form like ODK Collect; form designers can allow users to traverse a form in any order, yet impose validation of collected data prior to traversing into subsequent steps in a workflow. +- **Improved treatment of repeat-groups**. In the 2.0 tools, we have eliminated the concept of a repeat-group. In its place, we provide prompts that enable you to open and edit other surveys with links back to the originating survey (if desired). These prompts can describe a sub-form (nested) relationship among the surveys (e.g., household and household-member) or they can represent arbitrary relational linkages across your data (e.g., tea-houses and tea-types). +- **Bi-directional synchronization of data across devices**. The ODK 2.0 tools support the collaborative sharing of survey data across devices, as well as the updating and submission of changes to previously-collected data (i.e., follow-up surveys) via a bi-directional synchronization protocol; this contrasts with the unidirectional device-to-server submission pathway of ODK Collect / ODK Aggregate / ODK Briefcase. +- **Data curation and visualization on the device**. ODK Tables gives organizations the ability to investigate and visualize entire datasets directly on the Android devices through graphical and non-graphical displays and through filtered views. +- **Row-level access filters**. The visibility of the data and the ability to edit and/or delete data can be restricted for different users and groups. + +.. note:: + + The ODK 2.0 tool suite is targeted at advanced users who are unable to complete their workflows with the ODK 1.0 tools. If you find that the ODK 1.0 tools meet your needs then there is no reason to switch. + +.. _odk-2-intro-learn-more: + +Learn More +-------------- +View a brief feature comparison in the guide for :doc:`select-tool-suite`. + +.. _odk-2-intro-list-of-tools: + +List of Tools +--------------- +The ODK 2.0 Tool Suite consists of: + - :doc:`app-designer-intro` - a design environment for creating, customizing, and previewing your forms, data curation, and visualization applications. + - :doc:`survey-intro` - a data collection application based upon HTML, CSS, JavaScript. + - :doc:`tables-intro` - a data curation and visualization application running on your mobile device. + - :doc:`services-intro` - an application for handling database access, file access, and data synchronization services between all the ODK 2.0 applications. It allows you to synchronize data collected by the ODK 2.0 Android tools with a cloud endpoint. + - :doc:`cloud-endpoints-intro` - a cloud server to host data and application files, and to support bi-directional data synchronization across disconnected mobile devices. + - :doc:`suitcase-intro` - a desktop tool for synchronizing data with a cloud endpoint. + +.. _odk-2-intro-trying-it-out: + +Trying It Out +---------------- +First see the sections for :doc:`survey-intro`, :doc:`tables-intro`, and :doc:`services-intro`. They cover the overview of each tool, setup instructions, and demonstration forms and applications built with these tools. These demonstration applications will give you a good sense of the flexibility and breadth of capabilities of the two tools. + +Next, see the :doc:`getting-started-2` to understand the process for revising and developing your own forms. That guide will walk you through modifying the Geotagger demo app to add an additional field to it. + +Finally, see the :doc:`data-permission-filters` page for how to manage table and row-level access filters. + + +.. toctree:: + :maxdepth: 1 + :hidden: + + getting-started-2 + select-tool-suite + +.. toctree:: + :maxdepth: 2 + :hidden: + :caption: Mobile Tools + + survey-intro + tables-intro + services-intro + scan-intro + +.. toctree:: + :maxdepth: 2 + :hidden: + :caption: Desktop and Server Tools + + app-designer-intro + cloud-endpoints-intro + suitcase-intro + +.. toctree:: + :maxdepth: 2 + :hidden: + :caption: Advanced Topics + + data-permission-filters + odk-2-framework + odk-2-sync-protocol + +.. toctree:: + :maxdepth: 2 + :hidden: + :caption: Contributing + + contributing + +.. toctree:: + :maxdepth: 2 + :hidden: + :caption: Reference + + verify-downloads + faq + glossary diff --git a/odk2-src/odk-2-framework.rst b/odk2-src/odk-2-framework.rst new file mode 100644 index 000000000..043f97ffb --- /dev/null +++ b/odk2-src/odk-2-framework.rst @@ -0,0 +1,23 @@ +ODK 2.0 Framework +====================== + +.. _odk-2-framework: + +The ODK 2.0 Tool Suite is a platform and exposes a variety of interfaces for you to build your data management applications on top of. For a description of these interfaces, as well as the inner working of how ODK processes this information and implements your application, read the `full description on our wiki `_. The main points addressed in this document include: + + - JavaScript Structure describes the form that ODK expects your data management applications to take and how ODK exposes functionality to them. + + - **Injected Interfaces** details the injected JavaScript interfaces that expose the APIs into the Android tools. + - **Configuration File Structure** shows the full file structure and organization that ODK expects from your application. + - **Internationalization** explains how the translation and internationalization system within ODK works and how to add locales to your application. + - **ODK Tables** web pages describes how to format your HTML files and include the injected interfaces in your application. + + - **ODK Survey** form processing details how ODK Survey functions to create the form navigation experience + + - **ODK Survey Calling Contexts (ctxt)** explains how callbacks work in the Survey's control flow + - **ODK Survey JavaScript modules** lists the JavaScript libraries and modules used by Survey and what they do + - **ODK Survey control flow** overview gives an end to end description of control flow in ODK Survey + - **ODK Survey controller actions (details)** lists the 10 actions available in a form definition and what each does. + - **ODK Survey formDef.json structure** is the formDef.json specification + +Full description is available on the `Github wiki `_. diff --git a/odk2-src/odk-2-sync-protocol.rst b/odk2-src/odk-2-sync-protocol.rst new file mode 100644 index 000000000..b69f075f4 --- /dev/null +++ b/odk2-src/odk-2-sync-protocol.rst @@ -0,0 +1,15 @@ +ODK 2.0 Sync Protocol +======================== + +.. _odk-2-sync-protocol: + +The ODK 2.0 Sync Protocol is defined in detail on our `Github wiki `_. + +This document covers: + + - REST URL formats + - REST Data Structures + - Data Groupings + - Directory Hierarchy and Naming Convention + - Overall Sync Workflow + diff --git a/odk2-src/scan-form-designer-intro.rst b/odk2-src/scan-form-designer-intro.rst new file mode 100644 index 000000000..9f4347dac --- /dev/null +++ b/odk2-src/scan-form-designer-intro.rst @@ -0,0 +1,35 @@ +ODK Scan Form Designer +========================= + +.. _scan-form-introduction: + +ODK Scan uses the :dfn:`ODK Scan Form Designer` to create machine-readable forms. Scan is only compatible with forms created in the Scan Form Designer and loaded onto the phone. They can then be chosen among the templates within ODK Scan and successfully processed on the device. + +Find the Scan Form Designer inside a tab in the :doc:`app-designer-intro` using :program:`Google Chrome`. + +.. warning:: + + You muse use :program:`Google Chrome`; other web browsers are not compatible at this time. + +A basic overview of the steps to design a form are: + + #. Set the page style + #. Add images and data fields + #. Save form (for future editing) and Export form to ODK Scan app + #. Print form for users to complete by hand + +You can begin using the Scan app once you've got a form template and forms with handwritten data entered. + +Video tutorials for each of these steps --from designing to exporting your form template-- are available `as a YouTube playlist `_. + +Once you have exported your completed form you can transfer the form template to the ODK Scan app and begin scanning paper forms. See the :doc:`scan-intro` documentation for more on those next steps. + +.. _scan-form-user-guide: + +ODK Scan Form Designer User Guide +--------------------------------------------- + +.. toctree:: + :maxdepth: 2 + + scan-form-designer-using diff --git a/odk2-src/scan-form-designer-using.rst b/odk2-src/scan-form-designer-using.rst new file mode 100644 index 000000000..35b2cfd8b --- /dev/null +++ b/odk2-src/scan-form-designer-using.rst @@ -0,0 +1,363 @@ +Using ODK Scan Form Designer +================================ + +.. _scan-form-using-getting-started: + +Getting Started +------------------------ + +After :doc:`app-designer-launching`, find the Scan Form Designer inside a tab (see the :doc:`app-designer-overview` for a tour of all the tabs). + +.. warning:: + + You must use :program:`Google Chrome`; other web browsers are not compatible at this time. + +The Scan Form designer presents a default page, the toolbar across the top of the screen, and Form Properties in the gray editing area surrounding the the page. + +.. image:: /img/scan-form-designer/scan-form-blank.* + :alt: ODK Scan Form Designer Homepage + +.. _scan-form-using-getting-started-tips: + +Tips for Increased Accuracy +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + - Adding images, especially high resolution images, to your form will provide increased reference points for Scan to help with aligning the form. Adding labels as images, as opposed to text fields, can help improved the accuracy of your scans. + - Fields stretched across the page are more likely to appear curved or warped in the photo taken by Scan, and the misalignment can lead to recognition errors. Ideally, each field should only be a few inches wide. + - Similarly, large sized numbers can also appear stretched or misaligned; small to medium sized numbers are recommended with 2pt spacing between each digit is recommended. + - Fill-in Bubbles can be slightly more accurate than Check Boxes. + - Ink pens are recommended over pencil when users are completing your printed form. + - You don't need to worry about leaving space for a printable border, Scan will automatically create a border around your form template. + - Currently Scan is capable of reading one-page, one-sided forms, so the Form Designer will only allow you to create one-page, one-sided forms. + + +.. _scan-form-using-getting-started-page-style: + +Set the Page Style +~~~~~~~~~~~~~~~~~~~~~~~ + +You must select the page style you want before you add any images or field boxes. You cannot change the page style later. + +.. warning:: + If you switch the page style later everything will be cleared to the default (blank!). + +Choose your page format from the toolbar by selecting :menuselection:`File --> Set page style`. The options for page style are: + + - Letter portrait + - Letter landscape + - A4 portrait + - A4 landscape + - Legal portrait + - 3x5 index card + +.. image:: /img/scan-form-designer/set-page-style.* + :alt: Set Page Style Menu + +.. _scan-form-using-getting-started-form-properties: + +Form Properties +~~~~~~~~~~~~~~~~ + +In the workspace to the left of the form you are creating is a box titled :guilabel:`Form Properties`. This is were you can tailor each field for style and for establishing how the data will be organized and presented after it is scanned and digitized. The key properties to note at this point are: + + - :guilabel:`Name`: An identifier for the ODK tools back end. A name is generated automatically but can be customized if desired. No spaces allowed; if blanks are entered (for example: "Date mo 1" it will be saved with underscores (i.e. "Date_mo1"). If desired, the name can be the same as the display text. + - :guilabel:`Display Text`: A label for the field that relates the nature of the data input and will be a reference point in Survey when looking at the data answers after collection (for example: "PolioVaccDate"). If desired, this can be the same as the name. The display text can include spaces if desired. + - :guilabel:`Verify field`: Choose whether the field requires validation by the user reviewing the scan when transcribing in Survey. + - :guilabel:`Order of fields`: Enter the order that the fields will be presented to the person verifying each field of data in Survey. Provide order by listing number, for example: 1, 2, 3. + - Select :guilabel:`Update Field` to apply any changes. + +.. _scan-form-using-adding-images: + +Adding Images +--------------- + +.. _scan-form-using-adding-images-anchor: + +Anchor Images +~~~~~~~~~~~~~~~~ + +You'll find that the default starting page of the Form Designer has images in each corner. These anchor images act as fiducial markers, or points of reference for the ODK Scan app when the form is eventually photographed with ODK Scan. Points of reference help the app orient the form so it knows which fields on the paper form correspond to the fields in the digital template. Additionally, any typed text fields that you added to the form will be viewed as images by the app and give the app additional points of reference to orient the form for processing. + +.. note:: + + Anchor images are essential for accurate Scan readings + +You can customize the anchor images with your own images: + + - Delete the preloaded anchor images by :guilabel:`Deleting Field` when the image is selected, and follow the instructions below on how to add new images. + - Each corner's anchor image must be unique, and the higher the resolution the better. + +.. _scan-form-using-adding-images-add: + +Add Images +~~~~~~~~~~~~~~ + + 1. To begin adding images, you must first be working on the image layer. From the toolbar, select :menuselection:`Edit --> Images`. + 2. Choose the image from your computer by clicking :guilabel:`New Image`. The image will appear in the image workspace area to the right of the form you are editing. + + .. image:: /img/scan-form-designer/scan-form-add-image.* + :alt: Add an Image to Scan Form + + 3. Use cursor to select the area of the image you want to use; this can be resized later. + 4. :guilabel:`Add Selection` + 5. Selected image will be placed in the upper left-hand corner of the editing layer workspace. Drag the center of the image to place it where you want on the form, and the corners of the image to resize it. + 6. You can keep adding selections from the same image while in :guilabel:`Image Layer` mode. + 7. Return to :menuselection:`Edit --> Field` to add more fields. + 8. If you return to :menuselection:`Edit --> Image` to add more images, you will see the previously uploaded files in the righthand corner of the workspace. Click on a file to quickly load the image for selection. + + .. image:: /img/scan-form-designer/scan-form-apply-image.* + :alt: Apply an Image to Scan Form + +.. _scan-form-using-adding-images-uses: + +Uses for Images +~~~~~~~~~~~~~~~~~~~~~~~~ + +In addition to customizing the anchor images on your form and adding additional points of reference to guide the ODK Scan app, you may also want to use images to: + + - Add a logo or picture + - Add tables or charts to the form + - You want to add text without typing it out in the :guilabel:`Text` field of the form designer. This is helpful if you are working off an existing form and do not want to retype all of the text from the form. You can grab images of the text instead and upload it to use in the form designer. + +.. _scan-form-using-adding-fields: + +Adding Data Fields +------------------ + +To begin adding data fields, you must first be working on the :guilabel:`Fields` layer by selecting from the toolbar :menuselection:`Edit --> Fields`. + +.. image:: /img/scan-form-designer/edit-fields-menu.* + :alt: Editing Data Fields in Scan Form Designer + +There are seven different field inputs that are supported by the ODK Scan Form Designer. Two of these field do NOT support digitization: + + - :ref:`Text Box ` + - :ref:`Text ` + +And five allow for automatic digitization + + - :ref:`QR Code ` + - :ref:`Checkboxes ` + - :ref:`Fill-in Bubbles ` + - :ref:`Number ` + - :ref:`Formatted Number ` + +To add a field, select :menuselection:`Add --> (desired field)`. Once you've added a field, the field will appear in the top left section of the form. You can then drag and drop the field to the placement you want on the form, as well as shrink or expand the field by pulling the corner. + +.. _scan-form-using-adding-fields-text-box: + +Text Box +~~~~~~~~~~~~~~~~~~~~~~~~~ + +This will be a blank field where users will write in information. In the scanning process, text boxes capture an image of what has been written in the box, but they do not automatically digitize the letters. + +.. note:: + + To digitize a text box, a user will manually transcribe the image of the text box into a text prompt in ODK Survey. + +.. _scan-form-using-adding-fields-text: + +Text +~~~~~~~~~~~~~~~~~~~~~~~~~ + +This is one way you add typed text to a form. Text fields are not an input field for users and will not be digitized by scan, but act more as labels for fields that will be automatically digitized. Text fields also help ODK Scan orient the photo of the scanned form to the template file by providing additional points of reference. + +.. tip:: + + Another way to add typed text to a form is as an image. + +.. _scan-form-using-adding-fields-qr-code: + +QR Code +~~~~~~~~~~~~~~~~~~~~~~~~~ + +A matrix barcode that can contained encoded numbers, words, or other data. + +When a form with a QR code box is scanned, the ODK Scan App will process any QR code data inside that area. This is designed for a process such as placing a unique patient ID code sticker on a printed form and then using the ODK Scan app to automatically link the encoded data with the other data elements on the form. The only stipulation is that the QR code must fit inside the box whose size you specify in the form designer. + +To create a custom QR code, you can use an online QR code generator, such as these example: `QR Code Generator `_ or `QR Stuff `_. + +Once you have a QR code saved as an image, you can add it to your form like any other image file. See Adding Images for more information. + +.. _scan-form-using-adding-fields-checkbox-bubble: + +Checkboxes and Fill-in Bubbles +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For ODK Scan, Fill-In Bubbles and Checkboxes have the same functionalities and options; they only vary in how they look. + +.. note:: + + Fill-in bubble option results in slightly more accurate scan results than similar checkboxes. + +With checkboxes or fill-in bubbles there are a few additional elements to consider in :guilabel:`Form Properties`. + +Bubble Type +""""""""""""""" + +The :guilabel:`Bubble type` field allows you to select how to categorize and count user entries. + - :menuselection:`Tally`: Filled bubbles will be read by ODK Scan as one unit each and will be added up to result in a number value. Each filled bubble/checkbox is one tally mark. (for example, one filled bubble for each child vaccinated). + - :menuselection:`Select one`: User chooses only one answer to the prompt. (for example, Male or Female). + - :menuselection:`Select many`: User chooses all applicable answers. (for example, Reasons for extra care: Low birth weight, family history of infant death, twins...). + +.. image:: /img/scan-form-designer/create-bubbles-menu.* + :alt: Adding Bubble Fields in Scan Form Designer + +Grid Values +""""""""""""" + +:guilabel:`Grid Values` are the values designated to each bubble or box. The default value for each bubble or box filled in by the user is 1, and you can customize the answers ODK Scan attributes to each box or bubble. For example, if in a grid of one row and two columns, row 1, col 1 is given the value of "yes," when that box is marked by a user in Survey and Tables the digitized answer will be "yes." + +.. image:: /img/scan-form-designer/create-checkbox-menu.* + :alt: Adding Checkbox Fields in Scan Form Designer + +.. _scan-form-using-adding-fields-number: + +Number and Formatted Number +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Number field is to add a number input that does not need any special formatting (for example, it's not a date, decimal, or a number split up by a dash). It is what you should use for things like number of polio vials in stock, age of child, and patient ID number. + +The Formatted Number field has an option for digits to be split up by delimiters, allowing you to create a date, decimal, and dashed-number input. This is what you should use for things like date of registration and infant weight, and for anything like a serial number or refrigerator product code where the number is broken up by a dash. + +.. note:: + + How to Write in Numbers + + When a person fills out a number field they will be asked to write in the digits by connecting the appropriate dots in each box. The digits will end up looking like the numbers on a digital clock. + + .. image:: /img/scan-form-designer/written-numbers.* + :alt: Writing numbers for Scan Digitization + +.. warning:: + + Scan's accuracy for number digitization is not as high as it is for the other fields. Bubbles and checkboxes have been tested at 99% accuracy in the field, but number accuracy can dip into the 80s or worse depending on form design and field conditions. + + If you plan to use numbers in your form, be sure to review the :ref:`Tips & Recommendations` section and test your form in field conditions. + +.. _scan-form-using-groups: + +Group Options +------------------ + +At the far right of the toolbar is :guilabel:`Group Options`, which allows you to create subforms. With subforms you can link several fields together, useful when wanting to move multiples fields around your form at once and keep them together + + #. While holding the :kbd:`Shift` key, select all the fields you want to group together. + #. From the toolbar, select :menuselection:`Group Options --> Group Fields`. + #. A dialog box will appear asking to confirm that you want to make a subform. After selecting :guilabel:`Yes`, you will need to name this subform. + +If you need to ungroup fields, with the subgroup selected, from the toolbar select :menuselection:`Group Options --> Ungroup Fields`. + +.. _scan-form-using-save-export: + +Save & Export the Form +------------------------- + +.. _scan-form-using-save-export-save-incomplete: + +Save Incomplete +~~~~~~~~~~~~~~~~~~~~~~ + +If you are working on a form and wish to save it for future editing, go to :menuselection:`File --> Save Incomplete` to save the :file:`.zip` file to your computer. + +.. image:: /img/scan-form-designer/scan-form-save.* + :alt: Save a Form Incomplete in Scan Form Designer + +.. _scan-form-using-save-export-load-incomplete: + +Load Incomplete +~~~~~~~~~~~~~~~~~~~~~~ + +When you return to continue working on a saved form, go to :file:`File > Load Incomplete` and select the :file:`.zip` from your computer. Make sure it is still in the :file:`.zip` format and is not an unzipped folder. + +.. image:: /img/scan-form-designer/scan-form-load.* + :alt: Load an Incomplete Form in Scan Form Designer + +.. warning:: + + Always make sure to SAVE your form this way, even if you are also exporting or saving to file system. This is the ONLY way to reload a form if you want to make changes. The exported file will NOT work if you try to load it back into the form designer. + +.. _scan-form-using-save-export-save-file-system: + +Save to File System +~~~~~~~~~~~~~~~~~~~~~~ + +Once your form is complete, you are ready to generate the machine readable files. Go to :menuselection:`File --> Save to File System`. Give the file the name you will want to see it called in the app and in Survey and Tables, as you will not be able to change this name later. + +.. image:: /img/scan-form-designer/scan-form-file-system.* + :alt: Save a Complete Form in Scan Form Designer + +This will generate the JSON template file, JPG form photo, and all other files necessary for the Scan app to read and process your forms. It will save them to the application file system, which can be pushed to the device using :program:`Grunt` with the typical command for pushing your app to your device (performed inside the :file:`Application Designer` directory: + +.. code-block:: console + + $ grunt adbpush + +.. warning:: + + Saving to the file system does NOT save a version that can be edited later. Please use the :ref:`Save Incomplete ` function to get an editable file. + +.. _scan-form-using-save-export-export-complete: + +Export Completed Form +~~~~~~~~~~~~~~~~~~~~~~ + +If you would prefer to export your Scan machine readable files externally from the file system, you can use this option. Go to :menuselection:`File --> Export Completed Form`. Give the export file the name you will want to see it called in the app and in Survey and Tables, as you will not be able to change this name later. + +.. image:: /img/scan-form-designer/scan-form-export.* + :alt: Export a Complete Form in Scan Form Designer + +This will give you a :file:`.zip` file that you can unzip and use to print hard copies of your form and transfer your form :file:`.json` template to the ODK Scan App. + +.. note:: + + This step is NOT necessary. Most people will use the "Save to File System" option. + +.. _scan-form-using-printing: + +Printing the Form +-------------------------- + +After you have saved and exported your form, print hard copies for your user to complete. + + 1. From the location you saved it on your computer, unzip the exported file. + 2. Within the folder, find and open the file called form :file:`.jpg`. This is the image of the form that you created in the Form Designer is the form you will print to hard copy. + + .. image:: /img/scan-form-designer/print-file-select.* + :alt: Print a File to Fill in for ODK Scan + + 3. Print the entire image on one page. Black and white is fine even for forms that were created with colored elements. + +.. _scan-form-using-tips: + +Tips & Recommendations +--------------------------- + +General +~~~~~~~~~~~ + + - Use only :program:`Google Chrome` to access the form designer! Other browsers are not compatible and may cause you to lose the form you're working on. + - Make sure your browser zoom is set to 100%. Zooming out can cause the data fields to appear weird on the form. + - **Do not refresh your browser without first saving your form** -- the form will be reset to the default blank form. + - The :guilabel:`Copy` function, can be an easy shortcut if you need to create multiples of the same field. This could be useful, for example, if on your form you want to collect the date of birth for each child in the family, or need to create multiple entries for dates of treatment. + - With the field you want to copy selected, go to :guilabel:`Copy` on the toolbar, and the new field will appear in the top left of the form. Edit any of the :guilabel:`Form Properties` as needed. + - Grouping fields together can be a shortcut when needing to move multiple fields around as you're working on your form; instead of moving them one at a time. + - You can both :guilabel:`Delete` and :guilabel:`Undo Delete` for fields and images from the toolbar. + +Design Considerations +~~~~~~~~~~~~~~~~~~~~~~~~~~ + + - Currently Scan is capable of reading one-page, one-sided forms, so the Form Designer will only allow you to create one-page, one-sided forms. + - Numbers left blank will be recognized by Scan as "" (the empty string). + - Therefore, if for instance you have a field that can have a range in the number of digits (for example, like Patient ID Numbers where one patient's ID could be 5 digits long, and another's 7 digits) create a text field to give your user instructions to leave any blank digits at the front of the field, so that those blanks will not not alter the final value interpreted by Scan. + - Since Scan cannot digitize handwriting, and text will have to be manually typed in when verifying the data set, if the form you are basing your template on is text heavy think creatively and strategically about the ways you can use bubbles or checkboxes instead. + - For example, instead of asking users to write in their symptoms, you can provide bubbles for the most common symptoms, and leave a Text Box for anything not listed. + - Repeat formatting for forms with multiple sections to make it as easy as possible for those writing in information to navigate the fields and the form. For example, place labels in the same position for each field, group subsections close together and create borders around them, and so on. + - Think through the order that users will be collecting information and try to best replicate that in the order that fields are presented on the form. + - For example, if the person completing the form will ask about the child's age before asking about the vaccines they have had (or if you want them to ask about age first), place the number field for age earlier in the form's progression than fields for the vaccines. + - Be strategic about when using fill-in bubbles or checkboxes. To not confuse your user, it is best to use just one type on the form. Alternatively, you can use both to signal the different types of responses that can be given; for example, use fill-in bubbles for all of your *select one* questions, and checkboxes for *select many*, to signify to your user that they are being asked a different type of question. + - Fields by default are created with borders. In the :guilabel:`Form Properties` box you can change the thickness of borders, number of borders, as well as the margins surrounding the fields. + - Use the arrow keys on your keyboard to move selected fields more exactly. + - You can align fields relative to each other by holding down :kbd:`Shift` to select multiple fields at once, and then go to :guilabel:`Align Field` to select the alignment you want for the selected fields. + - Using the :guilabel:`Change Position` function, located on the toolbar, if fields are placed close enough that they overlap, by sending one field forward or backward, you can overlay them to best fit your form. + diff --git a/odk2-src/scan-intro.rst b/odk2-src/scan-intro.rst new file mode 100644 index 000000000..81ae520a6 --- /dev/null +++ b/odk2-src/scan-intro.rst @@ -0,0 +1,26 @@ +ODK Scan +============ + +.. _scan-intro: + +.. warning:: + + ODK Scan is not yet fully released! It is not guaranteed to work at the same quality level as ODK Survey, Tables, Services, or the rest of the release ODK 2.0 tools. + + It is currently at the **Release Candidate** stage, which mean does not have support for deployments, but the ODK core team does guarantee a migration path (possibly with many tedious manual steps) into future releases. + +:dfn:`ODK Scan` is an Android application that uses the device’s camera and specialized code to automatically digitize written data from paper forms. Using the app, users take pictures of paper forms and ODK Scan detects and collects the fill-in bubble, checkbox, and written number data. It also saves image snippets of handwritten text and displays them on the screen for easy data entry. The digitized data can then be validated, exported, saved into a database, and used for custom data reports. This workflow from paper form to digital database occurs in five processes and is supported through the use of ODK suite tool + +.. image:: /img/scan-intro/scan-process.* + :alt: The ODK Scan Process + +.. _scan-intro-learn-more: + +Learn more about ODK Scan +---------------------------- + +.. toctree:: + :maxdepth: 2 + + scan-setup + scan-using diff --git a/odk2-src/scan-setup.rst b/odk2-src/scan-setup.rst new file mode 100644 index 000000000..252f4f4cc --- /dev/null +++ b/odk2-src/scan-setup.rst @@ -0,0 +1,43 @@ +ODK Scan Setup +================== + +.. _scan-setup: + +Before using ODK Scan, you will already need: + + - :doc:`services-intro` + - :doc:`survey-intro` + - :doc:`tables-intro` + - :doc:`app-designer-intro` + +.. _scan-installing: + +Installing Scan +----------------------- + +.. warning:: + + ODK Scan is only compatible with Android versions 4.4 or newer. + +To install the apk: + + 1. From your device's :guilabel:`Settings`, choose :menuselection:`Security`. + + - Make sure *Unknown Sources* is checked. + - (On older versions of Android, this setting is in :menuselection:`Applications` rather than :menuselection:`Security`) + + 2. Open a web browser on your phone. + 3. Navigate to https://opendatakit.org/downloads/download-category/scan/ and download the ODK Scan APK. + 4. In the download window, you will see ODK_Scan.N.N.apk. - Select it to download the file. + + - On older devices, the APK will automatically install after you approve the security settings. + - On newer devices, you must go to the download list, rename the file to restore the .apk extension (the extension will have been renamed to .man during the download process), then click on it to install it. + +.. note:: + You can also `download the ODK Scan APK `_ to your computer and load it on your device via `adb `_ or another tool like `AirDroid `_. + +.. note:: + To synchronize your data with the cloud you will also need an ODK Cloud Endpoint. + +.. note:: + Before scanning you'll first need to create printable form template using the :doc:`scan-form-designer-intro`. diff --git a/odk2-src/scan-using.rst b/odk2-src/scan-using.rst new file mode 100644 index 000000000..175009136 --- /dev/null +++ b/odk2-src/scan-using.rst @@ -0,0 +1,204 @@ +.. spelling:: + Plexiglass + +Using ODK Scan +==================== + +.. _scan-using: + +ODK Scan works in integration with ODK Survey, Tables, and Services. To be able to take your scanned data through the entire process of scanning, editing, validation, and syncing, (as well as creation of custom reports from the data) please make sure you have all following ODK apps installed. + + - :doc:`services-intro` + - :doc:`survey-intro` + - :doc:`tables-intro` + - :doc:`app-designer-intro` + +To take the data from paper to digital, a basic overview of the steps are: + +.. contents:: :local: + +.. _scan-using-transferring-template: + +Transferring a Form Template to the App +------------------------------------------ + +ODK Scan works with machine readable forms created using the :doc:`scan-form-designer-intro`; refer to the :doc:`scan-form-designer-using` for instructions on how to create these forms. + +After creating a form with Form Designer, you'll have generated the machine readable files. To push them to your device, you will use the same mechanism that is used to push Survey and Tables files to the device. + + #. Create a form using the ODK Scan Form Designer. Save that form with the :guilabel:`Save to File System` option. + #. Follow the instructions in the :ref:`Application Designer Guide ` to push updates to the device. These describe pushing Survey files, but they will push Scan files to the device too with the same procedure. + #. To confirm that the *[your_form]* template has been successfully been transferred, open the ODK Scan app on your device and go to :guilabel:`Settings` (the wheel icon) and select :menuselection:`Templates to Use`. The folder name should appear in the list of templates. + +.. image:: /img/scan-using/scan-template-list.* + :alt: Example list of Scan templates + :class: device-screen-vertical + +.. _scan-using-scanning-form: + +Scanning a Form +------------------------------------------ + +.. _scan-using-scanning-form-prior: + +Prior to scanning +~~~~~~~~~~~~~~~~~~~ + +Have the forms that completed by your users ready. For more information on printing the form created in Form Designer see the :ref:`printing instructions `. + +Open the Scan app, and be sure that the template you want to use this session is selected in the settings. Go to :menuselection:`Settings --> Templates to Use`, make sure the correct form is selected, and click :guilabel:`OK`. + +.. image:: /img/scan-using/scan-single-template.* + :alt: Example of Scan template selection + :class: device-screen-vertical + +.. _scan-using-scanning-form-scanning: + +Scanning the form +~~~~~~~~~~~~~~~~~~~ + + 1. When you are ready to begin scanning, click :guilabel:`Scan New Form` from the main page in Scan. This will bring up a camera window. + 2. Adjust your positioning until there is a good view of the form in the viewfinder. When you are ready to take the picture, **tap the camera icon**. + + - The form should take up 80% of the photo area. + - Make sure that the form is lying as flat as possible so that there will be no curvature in the form. + - Tap anywhere in the viewfinder to focus the camera. + + .. image:: /img/scan-using/scan-camera.* + :alt: Scan camera capturing form image + + 3. If the preview of the photo looks good, tap the checkbox icon to move onto the next step. To retake the photo tap the :guilabel:`Back` button and to exit the camera tap the :guilabel:`X`. + 4. Once you select the check mark to begin photo processing, a small message will pop up saying *Processing photo in background.* + 5. When the photo has been successfully (or unsuccessfully) processed, you will see a notification at the top of the screen in the Android toolbar. Pull the top toolbar down and tap the ODK Scan notification. This will open Scan and pull up the photo of the selected scan. + + - The successfully processed photo will show an overlay of colored boxes that indicate the fields that Scan has detected. Any bubbles or checkboxes recognized as filled will show an overlay of the value that was assigned to them in the form designer. Number fields will show an overlay of the number that the app recognized for each digit. + - If the photo was unsuccessfully processed you will be prompted to retake the photo. + + .. image:: /img/scan-using/scan-image-markup.* + :alt: Scan image with markup overlay + + 6. From this screen, you can choose to either begin reviewing the data from this scan, or save it to review later. Press :guilabel:`Transcribe` to be taken into ODK Survey where you will be able to view and edit data. + + - Or press :guilabel:`Save`. This scan is now accessible by tapping the drop down options (at the top right of the screen), then :menuselection:`Main Menu --> View Scanned Forms`). From the drop down options, you can select :guilabel:`Scan New Form` to continue scanning and saving forms. + +.. tip:: + + To increase accuracy of Scan's results, you can consider building a stand with a clear plastic surface to place your phone or tablet on top off while you take the each photo. The stability can help improve the alignment and reduce blur in photos. Below is an example of a stand built with PVC piping and Plexiglass. + + .. image:: /img/scan-using/scan-stand.* + :alt: Custom build stand for improved Scan accuracy + +.. _scan-using-survey: + +Survey: View, Verify, & Edit Data +------------------------------------------ + +.. _scan-using-survey-review: + +Reviewing Your Data +~~~~~~~~~~~~~~~~~~~~~~ + +You'll be taken to Survey after pressing :guilabel:`Transcribe` on a scan. There you'll see a clickable list of all of the fields pulled from your form template, your :guilabel:`Table of Contents`. You can return to this screen when transcribing data by pressing the button on the top, left (with your form template's name, the example image below being *scan_TB03_Register1*). + +.. image:: /img/scan-using/scan-review-data.* + :alt: View of a scanned form in ODK Survey + :class: device-screen-vertical + +.. _scan-using-survey-verify: + +To verify and edit any of the data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Select the field you want to view, and you'll be taken to a screen where you'll find an image of the field and the data, as interpreted by Scan, and an editable box below. Type in any changes if there are discrepancies between the data digitized by Scan and the ground truth data. + +.. image:: /img/scan-using/scan-verify-number.* + :alt: View of a scanned number field in ODK Survey + :class: device-screen-vertical side-by-side +.. image:: /img/scan-using/scan-verify-bubble.* + :alt: View of a scanned bubble field in ODK Survey + :class: device-screen-vertical side-by-side + +Navigate to the next section to validate and edit either by: + + - Pressing the :guilabel:`Next` or :guilabel:`Back` buttons at the top of the screen, + - Or go to the button with your form name and select :guilabel:`Contents` to return to the main screen of captured data. + +.. note:: + + The order that these fields are presented can be set when originally creating the form template in Form Designer. With a data field selected, in :guilabel:`Form Properties` enter a numbered order (i.e. 1, 2, 3, etc) in :guilabel:`Order of Fields`. + +.. note:: + + Text boxes and text fields cannot be digitized. However, Scan will capture an image of text boxes (not text fields; text fields are to be used primarily as labels on your form), and when verifying data in Survey you can type in the data directly into the app. + + .. image:: /img/scan-using/scan-transcribe-text.* + :alt: View of a scanned text field in ODK Survey + :class: device-screen-vertical + +.. _scan-using-survey-finalize: + +Saving and Finalizing Changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You have the option of saving changes you've made to the data and returning to it later to further review. Go to the :menuselection:`Form Name --> Save Changes + Exit`. You can access this scan's data again from :menuselection:`Scan> --> View Scanned Forms`; they will be arrange in the chronological order they were originally scanned. + +If you've made changes you don't want to keep, :menuselection:`Form Name --> Ignore Changes + Exit`. + +Once you've verified all the fields, select :menuselection:`Form Name --> Finalize Changes + Exit`. You will also have the option to :menuselection:`Finalize Changes` if you are navigating through the data fields by using the next button and reach the end of the data contents. Once you are finished here you will return to Scan, where you can scan a new form or transcribe a saved scan; both options accessible through navigating to Scan's Main Menu. + +.. image:: /img/scan-using/scan-finalize.* + :alt: Finalizing changes in ODK Survey + :class: device-screen-vertical + +.. _scan-using-tables: + +Your Data in Tables +------------------------------------------ + +With each verified and finalized scan, a new line of data will be entered into Tables. To view (on your device) the verified data collected in this instance: open the Tables app and select the line with your form's name listed. This will open up a spreadsheet of your data. If you need to need to edit the data in a record from here: + + 1. Double tap on the cell you want to edit. + 2. You'll be given the option to either :guilabel:`Edit` or :guilabel:`Delete` that row. Choosing :guilabel:`Edit` will launch the form in Survey. + 3. You can change the :guilabel:`View Type`, :guilabel:`Color Settings`, and more by pressing the settings wheel and making any changes you need. + +.. image:: /img/scan-using/scan-tables-view.* + :alt: Viewing scanned data in ODK Tables + +.. _scan-using-syncing: + +Syncing & Aggregating Data +------------------------------------------ + +Syncing your device's records with an :doc:`cloud-endpoints-intro` allows data to be accessible across all your devices, and provides a centralized database for all of the data collected using Scan. This is key if you are collecting data using Scan on multiple devices and/or are continuously scanning new forms. + +.. _scan-using-syncing-prereqs: + +Prerequisites for Syncing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + 1. :doc:`services-intro` must be installed on your device + 2. A compatible :doc:`cloud-endpoints-intro` server must be set up. + +To sync your device-stored data with your ODK Cloud Endpoint, open ODK Services and launch the sync interface (press the circular arrow button along the top bar). Make sure you have the correct settings for your *Endpoint URL* and *Account*. Make sure your device is connected to the Internet. Instructions are available in the :ref:`ODK Services guide `. + +.. _scan-using-syncing-viewing: + +Viewing Data on an ODK Cloud Endpoint +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Once you have synced successfully, you can login to your ODK Cloud Endpoint instance to view the synced data. + + 1. Login to your Endpoint instance + 2. Go to the Tables tab + 3. Find the synced form template from the list and click Table Data. + 4. You should see one row of data for each record that was synced from your device for that form template. This spreadsheet will grow with each synced instance. + +.. _scan-using-suitcase: + +Suitcase +------------------------------------------ + +:doc:`suitcase-intro` is the mechanism for downloading and exporting data from the ODK 2.0 data tables into local :file:`.csv` files. + +ODK Suitcase allows you to gather and aggregate data locally, maintain accessibility after the internet connection is gone, and automatically push data from Suitcase to an ODK Cloud Endpoint when you return to connection. Suitcase has specific options to handle Scan's use cases (paper-to-digital). + diff --git a/odk2-src/select-tool-suite.rst b/odk2-src/select-tool-suite.rst new file mode 100644 index 000000000..002ba9731 --- /dev/null +++ b/odk2-src/select-tool-suite.rst @@ -0,0 +1,78 @@ +Selecting the Appropriate Tool Suite +===================================== + +Generally, we suggest starting with the ODK 1.0 tool suite. If it does not fulfill your requirements then move on to the more flexible, but also more complex, ODK 2.0 tool suite. + +.. note:: + It is tempting to look at the version number and assume the latest is the greatest, but this is not always the case. The ODK 2.0 tool suite was designed to co-exist with the 1.0 tool suite and does not replace any 1.x tools. In general, the 1.0 tools are easier to use, require less setup, and are widely adopted. However, if you have a complex longitudinal study and possess some technical skills, then the 2.0 tools may be better suited to your needs. + + +The feature comparison table below illustrates the differences between the 1.0 and 2.0 tools. + +.. list-table:: Feature Comparison Table + :header-rows: 1 + + * - | Feature + - 1.0 + - 2.0 + * - | Stage of technology lifecycle + - Maturity + - Introductory + * - | Collect data with mobile device + - x + - x + * - | Widely adopted + - x + - + * - | Drag and drop tool to create forms + - x + - + * - | Transmit collected data from device to server + - x + - x + * - | Ability to capture rich data types (e.g. GPS, Images, + | Audio, Video) + - x + - x + * - | One to **one** mapping of a question to database fields + | (except for GPS) + - x + - x + * - | One to **many** mapping of a question to database fields + - | + - x + * - | Static input contraint checks + - x + - x + * - | Dynamic input contraint checks + - + - x + * - | Ability to control basic layout of question prompts on the + | Android device + - x + - x + * - | Fully customizable layout of prompts on the Android device + | using HTML/JavaScript + - + - x + * - | Bi-directional synchronization of collected data across + | devices + - + - x + * - | Visualizations of collected data available on the device + - + - x + * - | Link longitudinal data to collected data + - + - x + * - | User permissions for row filtering of data available on the device + - + - x + +.. _select-tool-suite-trying-them-out: + +Trying Them Out +----------------------------- + - Try the 1.x tools by visiting their getting started guide. + - Try the 2.x tools with the :doc:`getting-started-2` + diff --git a/odk2-src/services-intro.rst b/odk2-src/services-intro.rst new file mode 100644 index 000000000..4516a99a2 --- /dev/null +++ b/odk2-src/services-intro.rst @@ -0,0 +1,18 @@ +ODK Services +============== + +.. _services-intro: + +:dfn:`ODK Services` is a program that handles database access, file access, and data synchronization services between all the ODK 2.0 applications. Mostly this happens behind the scenes, but you will need to install ODK Services as a prerequisite to using the other ODK 2.0 tools. + +It also allows you to sync data collected by the ODK 2.0 tools with an ODK Cloud Endpoint. The Services application can be used to reset the Cloud Endpoint with the data that is on a tablet or to sync the data on the tablet with what is currently on the Cloud Endpoint. + +.. _services-intro-learn-more: + +Learn more about ODK Services +---------------------------------- + +.. toctree:: + + services-setup + services-using diff --git a/odk2-src/services-setup.rst b/odk2-src/services-setup.rst new file mode 100644 index 000000000..834c234b7 --- /dev/null +++ b/odk2-src/services-setup.rst @@ -0,0 +1,36 @@ +Setting Up ODK Services +============================== + +.. _services-setup-install: + +Installing ODK Services +-------------------------------- + + 1. From your device's :guilabel:`Settings`, choose :menuselection:`Security`. + + - Make sure *Unknown Sources* is checked. + - (On older versions of Android, this setting is in :menuselection:`Applications` rather than :menuselection:`Security`) + + 2. Open a web browser on your phone. + 3. Navigate to http://opendatakit-dev.cs.washington.edu/2_0_tools/download and download the latest ODK Services APK. + 4. In the download window, you will see ODK_Services_vN.N.N.apk. - Select it to download the file. + + - On older devices, the APK will automatically install after you approve the security settings. + - On newer devices, you must go to the download list, rename the file to restore the .apk extension (the extension will have been renamed to .man during the download process), then click on it to install it. + +.. note:: + + You can also `download the ODK Services APK `_ to your computer and load it on your device via `adb `_ or another tool like `AirDroid `_. + +.. tip:: + + You can also `install ODK Services on an Android emulator `_. However, this can be slow and is only recommended for developers actively working on Services. + +.. _services-setup-servers: + +Compatible Servers +------------------------- + +The latest version of ODK Services is Version 2.0.2 (December 2017). + +To synchronize data with the latest version of ODK Services (rev218) requires a rev 218, 216 or 214 :doc:`cloud-endpoints-intro`. ODK Aggregate v1.4.15 or Sync Endpoint 2.0.2 rev 218 will work. It will not work with earlier versions of ODK Aggregate. See :doc:`sync-endpoint` or :doc:`aggregate-tables-extension` for further information on how to configure the server to accept ODK 2.0 synchronization requests. diff --git a/odk2-src/services-using.rst b/odk2-src/services-using.rst new file mode 100644 index 000000000..8ddd23b44 --- /dev/null +++ b/odk2-src/services-using.rst @@ -0,0 +1,126 @@ +Using ODK Services +==================== + +.. _services-using: + +.. _services-using-sync: + +Syncing +--------------- + +Use this option to update your device's configuration to match that of the server and to submit and retrieve data. + +Opening up the Services application will take you to the Sync screen. You can also open up this application by pressing on the sync icon (two curved arrows) in the action bar of the Tables and Survey applications. On this screen: + + 1. If you have not configured the application, from the menu, choose the :menuselection:`Settings --> Choose Server Settings` + 2. Choose :guilabel:`Server URL` and specify your server URL (if using :program:`Google App Engine`, be sure to specify :code:`https://...`). + 3. If your server is not configured to allow anonymous access: + + a. Change the :guilabel:`Server Sign-on Credential` to :menuselection:`Username` and enter the appropriate credentials in the other settings fields. + b. Exit out of the :menuselection:`Server Settings` page, and then the :menuselection:`Settings` page, by using the back button. + c. You will then be asked to :guilabel:`Authenticate Credentials`. Select the :guilabel:`Authenticate New User` option. + d. On the next screen :guilabel:`Select Verify User Permissions`. + e. After the verification succeeds, you will see a :guilabel:`Verification Successful` popup, select :guilabel:`OK`. + f. Hit the back button until you see the sync screen with the options to :guilabel:`Change User` and :guilabel:`Sync Now`. + + 4. The sync interaction has four options: + + - :menuselection:`Fully Sync Attachments` - *Default* - Synchronize all row-level data and file attachments with the server. + - :menuselection:`Upload Attachments Only` - Only upload attachments from the device to the server + - :menuselection:`Download Attachments Only` - Only download attachments from the server to the device + - :menuselection:`Do Not Sync Attachments` - Do not sync any attachments + + 5. Click on :guilabel:`Sync Now`. + +Services will contact the Cloud Endpoint and perform the two synchronization phases (detailed above). A progress dialog will be displayed and, alternatively, the status of sync can be obtained by looking at the notifications generated by Services in the notification area. + +.. note:: + + The sync will proceed whether or not you remain on this page and you can use the back button to back out of it and return to your work. + +.. warning:: + + Should you begin modifying data rows while syncing, the changes to those rows will not be synced until you save them as incomplete or finalize the row, and the act of editing will generally mark the sync as having ended with conflicts. This simply means that you must complete your edits and re-issue the sync to ensure that your changes are propagated up to the server. + +.. _services-using-sync-detail: + +Sync Details +~~~~~~~~~~~~~ + +Syncing has two phases. In the first phase, data tables are created on the device that correspond to the data tables on the server, and the form definitions and other files on your device are made to exactly match those available on the server (updating them as needed). + +.. warning:: + + If a data table on the device does not exist on the server, the configuration files and all associated forms for that table will be removed from the device. To prevent data loss, the table itself will not be deleted, but, by removing all of the configuration files for that table, the data will generally be unusable. + +In the second phase, it synchronizes the contents of the local data tables with the contents on the server, including any row-level file attachments associated with individual records in the data table. Row-level file attachments are bundled and synced one row at a time. + +Unlike ODK Collect, where individual forms can be added and removed at will, ODK Services and the ODK 2.0 tools are organized around coherent, complete, *data management applications* consisting of a set of interrelated data tables and forms. All the forms and tables on the server collectively define the *data management application* and ODK Services ensures that the device conforms to that *data management application* definition. You can operate multiple independent *data management applications* on a single device by placing their files and forms under different application folders within the :file:`/sdcard/opendatakit/` folder; each such application will publish to a different ODK Cloud Endpoint. This is a very significant and powerful change from the ODK Collect mindset. + +.. _services-using-reset-app-server: + +Reset App Server +------------------------- + +Resetting your app server pushes the configuration and data on your tablet up to the server. + +.. note:: + + This option should only be used to initialize or update your Cloud Endpoint. + +.. warning:: + + If a data table on the server does not exist on the device, that table, all of its data, and all associated files (e.g., forms) will be deleted from the server. + +If a data table on the server is identical to one on the device, the data in that table will be synced and the files on the server will be updated to be exactly those present on the device (deleting any files associated with this table that existed only on the server). + +Before resetting, it is critical that you first ensure that your device contains all the tables, files, and data you want to preserve in your application (by :ref:`Syncing `). + +Opening up the Sync application will take you to the Sync screen. You can also open up this application by pressing on the sync icon (two curved arrows) in the action bar of the Tables and Survey applications. On this screen: + + 1. If you have not configured the application, from the menu, choose the :menuselection:`Settings --> Choose Server Settings` + 2. Choose :guilabel:`Server URL` and specify your server URL (if using :program:`Google App Engine`, be sure to specify :code:`https://...`). + 3. If your server is not configured to allow anonymous access: + + a. Change the :guilabel:`Server Sign-on Credential` to :menuselection:`Username` and enter the appropriate credentials in the other settings fields. + b. Exit out of the :menuselection:`Server Settings` page, and then the :menuselection:`Settings` page, by using the back button. + c. You will then be asked to :guilabel:`Authenticate Credentials`. Select the :guilabel:`Authenticate New User` option. + d. On the next screen :guilabel:`Select Verify User Permissions`. + e. After the verification succeeds, you will see a :guilabel:`Verification Successful` popup, select :guilabel:`OK`. + f. Hit the back button until you see the sync screen with the options to :guilabel:`Change User` and :guilabel:`Sync Now`. + + 4. The sync interaction has four options: + + - :menuselection:`Fully Sync Attachments` - *Default* - Synchronize all row-level data and file attachments with the server. + - :menuselection:`Upload Attachments Only` - Only upload attachments from the device to the server + - :menuselection:`Download Attachments Only` - Only download attachments from the server to the device + - :menuselection:`Do Not Sync Attachments` - Do not sync any attachments + + .. tip:: + When resetting the server, you typically want Fully Sync Attachments to be selected. + + 6. Click on :guilabel:`Reset App Server`. + 7. A confirmation dialog will popup asking you to confirm resetting the App Server. Again, this can delete all data on this Cloud Endpoint! If you are sure you want to continue, click :guilabel:`Reset`. + +Sync will contact the ODK Cloud Endpoint and attempt to push all configuration and data currently on the tablet up to the specified Cloud Endpoint. A progress dialog will be displayed and, alternatively, the status of resetting the app server can be obtained by looking at the notifications generated by Services in the notification area. + +.. note:: + + The sync will proceed whether or not you remain on this page and you can use the back button to back out of it and return to your work. + +.. warning:: + + Should you begin modifying data rows while syncing, the changes to those rows will not be synced until you save them as incomplete or finalize the row, and the act of editing will generally mark the sync as having ended with conflicts. This simply means that you must complete your edits and re-issue the sync to ensure that your changes are propagated up to the server. + +.. _services-using-resolve: + +Resolving Sync Conflicts +--------------------------------------- + +When you return from ODK Services and next access data from a data table, the ODK 2.0 tools (for example ODK Survey or ODK Tables) will scan all tables looking for conflicts arising from the synchronization process. If any conflicts are found, the user is required to resolve the conflict before proceeding to their activity. The options for resolving conflicts are as follows. + + - :guilabel:`Take Local Version` + - :guilabel:`Take Server Version` + - :guilabel:`Merge Changes` - Will be enabled once all conflicts in the row's data fields have been decided. + +Choose the desired option. Once the changes are reconciled, you can then proceed to the activity you were accessing and, when you next sync, the resolved conflicts and any new changes will be pushed up to the server. Then, other users will receive those changes when they sync to the server. diff --git a/odk2-src/suitcase-install.rst b/odk2-src/suitcase-install.rst new file mode 100644 index 000000000..b8bb4b5c4 --- /dev/null +++ b/odk2-src/suitcase-install.rst @@ -0,0 +1,36 @@ +Installing ODK Suitcase +================================= + +.. _suitcase-install: + +.. _suitcase-install-prereqs: + +Prerequisites +----------------------- + + 1. Set up an :doc:`cloud-endpoints-intro` + + .. note:: + + Ensure you are using a compatible Cloud Endpoint from the same revision. + + 2. Make sure :program:`Java` 7 or higher is installed on the computer you plan to use. If it is not, `download and install it `_. + +.. _suitcase-intstall-app: + +Installing ODK Suitcase +------------------------------ + + 1. Navigate to http://opendatakit-dev.cs.washington.edu/2_0_tools/download and download the latest :file:`ODK Suitcase.jar` file. + 2. Double click the file to start. If that fails, try running: + + .. code-block:: console + + $ java -jar path/to/jar + + 3. Alternatively you can use command line operation. For help on the command line interface type: + + .. code-block:: console + + $ java -jar path/to/jar --help + diff --git a/odk2-src/suitcase-intro.rst b/odk2-src/suitcase-intro.rst new file mode 100644 index 000000000..9e259eea1 --- /dev/null +++ b/odk2-src/suitcase-intro.rst @@ -0,0 +1,16 @@ +ODK Suitcase +=============== + +.. _suitcase-intro: + +:dfn:`ODK Suitcase` is a cross-platform tool that provides access to data on an ODK Cloud Endpoint from a personal computer. + +Data downloaded from an :doc:`cloud-endpoints-intro` is stored as spreadsheets in CSV format. This format is compatible with most spreadsheet software, for example :program:`Excel` or :program:`Numbers`. Once downloaded, the spreadsheets will be available for offline viewing. Similarly, data to be uploaded to an ODK Cloud Endpoint must be stored in a properly formatted csv file. + +Learn more about ODK Suitcase +------------------------------------- + +.. toctree:: + + suitcase-install + suitcase-using diff --git a/odk2-src/suitcase-using.rst b/odk2-src/suitcase-using.rst new file mode 100644 index 000000000..fc365e297 --- /dev/null +++ b/odk2-src/suitcase-using.rst @@ -0,0 +1,58 @@ +Using ODK Suitcase +===================== + +.. _suitcase-using: + +.. _suitcase-using-gui: + +Graphical Interface +----------------------- + +If your ODK Cloud Endpoint allows for anonymous access then you can leave the :guilabel:`username` and :guilabel:`password` fields blank. Otherwise, please specify an ODK Cloud Endpoint username and password with sufficient permissions. + +By default ODK Suitcase creates a :file:`Download` directory where the ODK Suitcase jar file is located and saves data in that directory. To specify a different directory for ODK Suitcase to store downloaded data in, click on the :guilabel:`...` button. + +ODK Suitcase provides three options to customize the CSV file. + + - Download attachments: + + - If this option is selected, ODK Suitcase will download all attachments from the given table and the CSV generated will contain hyperlinks to the local files. + - If this option is not selected, the CSV generated will contain hyperlink to the given ODK Cloud Endpoint. + + - Apply Scan formatting: + + - When this option is selected, ODK Suitcase will optimize the CSV by replacing certain columns added by ODK Scan. + + - Extra metadata columns + + - When this option is selected, two more columns will be included in the CSV, :th:`create_user` and :th:`last_update_user`. + +.. _suitcase-using-cli: + +Command Line Interface (CLI) +---------------------------------- + +ODK Suitcase also provides a command line interface that can be easily called by scripts and other programs. The CLI has the same features as the graphical user interface. CSV files produced by the two interfaces should also be identical. + +For a list of all available options, open command prompt/power shell or terminal. Type: + +.. code-block:: console + + $ java -jar path/to/jar.jar --help + +Combine the individual commands described in the help to perform the actions needed. Examples are as follows. + + - To download CSV of table *table_id* from app *default* with attachments as an anonymous user to the :file:`default` directory. + + .. code-block:: console + + $ java -jar suitcase.jar -download -a -cloudEndpointUrl "https://your-endpoint-server.com" -appId "default" -tableId "table_id" + + - To download CSV of table *table_id* from app *default* with attachments with username *user* and password *pass* to:file:` ~/Desktop`: + + .. code-block:: console + + $ java -jar suitcase.jar -download -a -cloudEndpointUrl "https://your-endpoint-server.com" -appId "default" -tableId "table_id" -username "user" -password "pass" -path "~/Desktop" + +To script the CLI, write the commands you would like to execute in a scripting language (for example, Bash, Batch, Python, Ruby) and use a scheduler (such as Cron or Windows Task Scheduler) to schedule the tasks. To skip over ODK Suitcase's prompts to overwrite, pass :code:`-f` as an argument to ODK Suitcase. + diff --git a/odk2-src/survey-install-sample.rst b/odk2-src/survey-install-sample.rst new file mode 100644 index 000000000..12ec43911 --- /dev/null +++ b/odk2-src/survey-install-sample.rst @@ -0,0 +1,66 @@ +Installing the ODK Survey Sample Application +============================================== + +.. _survey-sample-app: + +.. _survey-sample-app-install: + +Install the Sample Application +-------------------------------------- + +We have provided a sample application to help you acquaint yourself with the various features of ODK Survey. This sample app contains six sample forms within it. + +Unlike ODK Collect, the ODK 2.0 tools are application-focused. An application is identified by the name of the directory under the :file:`/sdcard/opendatakit/` folder. The sample application is named *default*, as are the sample applications provided for :doc:`tables-intro` :doc:`services-intro`. This means that you can only deploy one of these sample application at a time onto a device. Later, we will explain how to rename one of these so that two or more applications can co-exist and not interfere with each other on this same device (this will require setting up an :doc:`cloud-endpoints-intro` for that renamed application; each Endpoint can host only one application at a time and must be configured with the application name that it will host). + +.. _survey-sample-app-overview: + +Six Sample Forms +~~~~~~~~~~~~~~~~~~~~~~~ + +A reference set of sample forms to start with are: + + - **Example Form** -- a form with many examples of data entry widgets. + - **Grid Screen Form** -- a form used to demonstrate a new screen layout that allows fully-customized prompt placement. + - **Household Survey** -- a form used to gather information about a household. To operate correctly, this requires the *Household Member Survey* sub-form and the *Education* sub-form (you should not open those sub-forms directly -- they are launched from within Household Survey). + - **Select Examples** -- a form with several examples of select widgets, including widgets that access data on Yahoo servers, and others that access CSV files for their choice lists. It also demonstrates the use of custom CSS styles to change the look of the form. + - **Household Member Survey** -- a form used to gather information about household members. This is a sub-form of the Household Survey form (you should not open it directly -- it is launched from within Household Survey). ODK Survey eliminates the repeat group concept and replaces it with sub-forms. From within the Household Survey you navigate into this sub-form by entering information about individuals in a household. + - **Education** -- a form used to gather education information about household members. This is another sub-form of the Household Survey form (you should not open it directly -- it is launched from within Household Survey). This sub-form saves information to the same underlying data table (household_members) as the Household Member Survey form, but it asks different questions. This demonstrates the use of multiple forms to revise different sets of values within a data table. From within the Household Survey you navigate into this form when you enter education information about individuals in a household. + +.. note:: + + Since the *Education* and *Household Member Survey* operate on the same table, you will only see five tables in ODK Tables and in the Cloud Endpoint even though there are six forms. + +To access the sample application and its six sample forms: + + 1. Launch ODK Survey. + 2. Click on the sync icon (2 curved arrows) to launch ODK Services directly into the sync activity. + 3. From the menu, choose :menuselection:`Settings -> Server Settings`. You may be presented with a pop-up warning you that there are changes on your device that have not been synced to your server. Since you are setting up this demonstration application for the first time, you can choose :guilabel:`Ignore Unsynced Changes`. + 4. Choose Server URL and specify https://opendatakit-2.appspot.com as the server URL (note that this URL begins with https:// ). + 5. Because this server allows anonymous access, the :guilabel:`Server Sign-on Credential` should be set to: :menuselection:`None (anonymous access)`. Other options are :menuselection:`Username and Google Account`. When setting up your own server, the ODK Sync Endpoint only supports Username. ODK Aggregate supports both Username and Google Account. + 6. Exit out of the :menuselection:`Server Settings` page, and then the :menuselection:`Settings` page, by using the back button. During this process, if you had chosen a :guilabel:`Server Sign-on Credential` other than :menuselection:`None`, you will be prompted to authenticate that user. + + .. warning:: + + If you decline (by choosing to :guilabel:`Log Out`), or if your credential is rejected by the server, then your credential will be reset to the anonymous (unprivileged) user. + + 7. Confirm that the Server URL matches that set up above. From this point forward, whenever you initiate a sync, you do not need to visit the :menuselection:`Settings` page, but can perform the sync entirely from this screen. + 8. The sync interaction has four options: + + - :menuselection:`Fully Sync Attachments` - *Default* - Synchronize all row-level data and file attachments with the server. + - :menuselection:`Upload Attachments Only` - Only upload attachments from the device to the server + - :menuselection:`Download Attachments Only` - Only download attachments from the server to the device + - :menuselection:`Do Not Sync Attachments` - Do not sync any attachments + + 8. Click on :guilabel:`Sync Now`. + +The sync process will now begin. If you have selected to use a Google Account, you may be challenged to authorize access to your Google account information. Otherwise, the sync will begin and a progress dialog will appear. As stated above, this synchronization mechanism forces the configuration of the device to exactly match that of the server. Any local configuration files for data tables or forms that are not present on the server will be removed from your device (i.e., everything under the :file:`/sdcard/opendatakit/default/config` directory will be revised to exactly match the content on the server). + +.. note:: + + As a safeguard to prevent data loss, data tables that are only defined on the device will not be deleted. However, because their associated configuration files will have been removed, they are generally inaccessible until you restore their configuration files and their forms onto the device. + +Once the configuration of the device is an exact match to that of the server, the data within the data tables are synchronized. And, finally, the file attachments associated with those data are synchronized. If you have a slow connection, it may take two or three tries before the sync is successful; the system stops at the first timeout and does not attempt any retries. + +When complete, click :guilabel:`OK` on the :guilabel:`Sync Outcome` dialog and back out of the ODK Services application, returning to ODK Survey. + +If the sync was successful, ODK Survey will scan through the downloaded configuration, updating its list of available forms, and you should now be presented with the list of those six sample forms. diff --git a/odk2-src/survey-install.rst b/odk2-src/survey-install.rst new file mode 100644 index 000000000..6347540d5 --- /dev/null +++ b/odk2-src/survey-install.rst @@ -0,0 +1,43 @@ +Installing ODK Survey +=========================== + +.. _survey-install: + +.. _survey-install-prereqs: + +Prerequisites +-------------------------------------- + +ODK Survey requires: + + - :doc:`app-designer-intro` + - :doc:`services-intro` + - :program:`OI File Manager` -- please download this from the `Google Play Store `_ (this is required) + + +.. _survey-install-app: + +Installing the ODK Survey App +----------------------------------- + + + 1. From your device's :guilabel:`Settings`, choose :menuselection:`Security`. + + - Make sure *Unknown Sources* is checked. + - (On older versions of Android, this setting is in :menuselection:`Applications` rather than :menuselection:`Security`) + + 2. Open a web browser on your phone. + 3. Navigate to http://opendatakit-dev.cs.washington.edu/2_0_tools/download and download the latest ODK Survey APK. + 4. In the download window, you will see ODK_Survey.N.N.apk. - Select it to download the file. + + - On older devices, the APK will automatically install after you approve the security settings. + - On newer devices, you must go to the download list, rename the file to restore the .apk extension (the extension will have been renamed to .man during the download process), then click on it to install it. + +.. note:: + + You can also `download the ODK Survey APK `_ to your computer and load it on your device via `adb `_ or another tool like `AirDroid `_. + +.. tip:: + + You can also `install ODK Survey on an Android emulator `_. However, this can be slow and is only recommended for developers actively working on Survey. + diff --git a/odk2-src/survey-intro.rst b/odk2-src/survey-intro.rst new file mode 100644 index 000000000..ce9dce230 --- /dev/null +++ b/odk2-src/survey-intro.rst @@ -0,0 +1,27 @@ +ODK Survey +============== + +.. _survey-intro: + +:dfn:`ODK Survey` is an Android application for performing data collection in the ODK 2.0 framework. It operates similarly to ODK Collect, but is based on HTML, CSS, and Javascript rather than native Android, and is more flexible in its presentation and execution. + +.. note:: + + ODK Survey only works on Android 4.2 and newer devices. + +.. note:: + + ODK Survey cannot read or display the forms created for ODK Collect (that is, those created via ODK Build, XLSForm, or other form design tools). ODK Survey operates with ODK 2.0 *data management applications*. + +We have included a sample application built on top of Survey to showcase some of its features. + +.. _survey-intro-learn-more: + +Learn more about ODK Survey +--------------------------------- + +.. toctree:: + :maxdepth: 2 + + survey-setup + survey-using diff --git a/odk2-src/survey-setup.rst b/odk2-src/survey-setup.rst new file mode 100644 index 000000000..ac2b379f9 --- /dev/null +++ b/odk2-src/survey-setup.rst @@ -0,0 +1,8 @@ +Setting Up ODK Survey +========================== + +.. toctree:: + :maxdepth: 2 + + survey-install + survey-install-sample diff --git a/odk2-src/survey-using.rst b/odk2-src/survey-using.rst new file mode 100644 index 000000000..f1a1db7c0 --- /dev/null +++ b/odk2-src/survey-using.rst @@ -0,0 +1,95 @@ +.. spelling:: + myapp + +Using ODK Survey +======================= + +.. _survey-using: + +In this guide we will be demonstrating how to use ODK Survey via a guided tour of the sample application. If you have not installed it yet, follow the instructions for :doc:`survey-install-sample`. However, this guide can also be used as a reference. + +.. contents:: :local: + +.. _survey-using-open-form: + +Opening a Form +------------------------- + +Open ODK Survey. If you have successfully installed the sample application, you should be presented with a list of the six sample forms. Select the desired form. + +.. note:: + + The *Household Members* and *Education* forms are not intended to be called directly, but are launched from within the *Household* form. + +Forms in ODK Survey are HTML. You can control the look-and-feel of the forms using CSS and add new prompt widgets by writing JavaScript. To navigate forms using OpenDataKit's look-and-feel: + + - Tap on the name of the survey in the top left to access a pop-up menu of options. + - Tap the :guilabel:`back` or :guilabel:`next` buttons in the top right of the form to navigate through the form. + +Every change you make to the data in the form is written immediately to the database as a **checkpoint** save. You may also manually save the form as *incomplete* or *finalize* the form, as you did with ODK Collect. To do so anywhere in the form, open the pop-up menu and take the desired action. + +Otherwise, to exit the form without saving or canceling your changes (and return to the form chooser screen), tap the device's back button (on newer devices, tap the back arrow icon at the bottom of the touch screen). You will be asked whether to ignore any changes since your last explicit save, whether to save your changes as an incomplete change, or whether to cancel the back (exit) action and return to the form. + +.. _survey-using-syncing: + +Syncing Forms and Data +-------------------------- + +See the instructions in the :ref:`ODK Services user guide `. + +.. warning:: + + If a data table has any checkpoint saves (for example, caused by form crashes), the data table will not be synchronized. Checkpoints must be resolved before sync can proceed. The user must open a form on the problem table and either delete the checkpoint or edit the checkpoint. If editing, after that is complete they must save is as either incomplete or finalized. Once the checkpoints are eliminated, the user can initiate another synchronization, and the data in this table will then be synchronized with the information on the server. + +.. _survey-using-launching-appname: + +Launching with a different AppName +--------------------------------------------- + +The ODK 2.0 tools are designed to support multiple, independent, ODK 2.0 applications running on the Android device. Each of our tools has the ability to run in the context of either a default application name, or a specified application name. + +By default, ODK Survey runs under the *default* application name (as does ODK Tables and the other ODK 2.0 tools). Application names correspond to the name of the directory under :file:`/sdcard/opendatakit` where the configuration and data files for that application are stored. + +Here we describe how to launch the ODK 2.0 tools into an application name of your he use of widget shortcuts. + +First, you must create an alternative application. To do so, open :program:`OI File Manager`, navigate to the :file:`/sdcard/opendatakit` directory, and copy the *default* directory, renaming it *myapp*. You have now created the *myapp* application! It is isolated from and operates independently of the default application. + +To launch and use that application: + +.. _survey-using-launching-appname-android-4: + +Android 4.x devices +~~~~~~~~~~~~~~~~~~~~~~~~~ + + #. Choose to view the installed applications. + #. Select the :guilabel:`Widgets` tab at the top of that screen. + #. Navigate through the available widgets, and select and hold the :guilabel:`ODK Survey Form` widget. Drag and drop it onto one of your Android launcher (home) screens. + #. A list of available applications and forms will appear, in the form of application name for applications, and :menuselection:`application name --> form name` for each form within an application. Pick the :menuselection:`myapp` application that you created via :program:`OI File Manager`. + +.. _survey-using-launching-appname-android-5: + +Android 5.x and higher devices: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + #. Long press an open area of the device home screen + #. Select the :guilabel:`Widgets` tab at the bottom of resulting screen. + #. Navigate through the available widgets, and select and hold the :guilabel:`ODK Survey Form` widget. Drag and drop it onto one of your Android launcher (home) screens. + #. A list of available applications and forms will appear, in the form of application name for applications, and :menuselection:`application name --> form name` for each form within an application. Pick the :menuselection:`myapp` application that you created via :program:`OI File Manager`. + +.. _survey-using-launching-appname-try-it: + +Trying the new launcher +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now, play around with launching ODK Survey using this application shortcut and :guilabel:`Finalizing` a new filled-in form. Exit ODK Survey, and launch it from the applications list (so that it launches as the default application). Verify that you do not see that newly-filled-in form. You can also create a new filled-in form in this default application and confirm that it is not visible in the myapp application. + +This highlights the isolation of applications in the ODK 2.0 tools. This is even more powerful with applications that use ODK Tables because you can create entirely isolated applications, such as a forestry app and a health clinic app, and have the forms and data entirely independent of each other. + +This should eliminate much of the need for different groups to fork the ODK code base. + +.. _survey-using-dev-environment: + +Setting up a Form Development Environment +-------------------------------------------- + +To get started creating your own forms, go to the :doc:`app-designer-intro` documentation. diff --git a/odk2-src/sync-endpoint.rst b/odk2-src/sync-endpoint.rst new file mode 100644 index 000000000..f63000e9e --- /dev/null +++ b/odk2-src/sync-endpoint.rst @@ -0,0 +1,282 @@ +.. spelling:: + phpLDAPadmin + readonly + +ODK Sync Endpoint +===================== + +.. _sync-endpoint-intro: + +:dfn:`ODK Sync Endpoint` is an implementation of :doc:`cloud-endpoints-intro`. It runs a server inside a :program:`Docker` container that implements the `ODK 2.0 REST Protocol `_. + +It communicates with your ODK 2.0 Android applications to synchronize your data and application files. + +.. _sync-endpoint-auth: + +Authentication +---------------------- + +ODK Sync Endpoint does not store user information in its own database, instead it integrates with an *LDAP* directory or an *Active Directory*. That directory is then used to authenticate users and obtain user roles. + +.. note:: + + As a consequence of the integration, Basic Authentication is the only supported authentication method. + +.. _sync-endpoint-prereqs: + +ODK Sync Endpoint prerequisites +----------------------------------- + +You must have :program:`Docker 17.06.1` or newer, and be running in *Swarm Mode*. +Follow these links for detailed instructions on installing :program:`Docker` and enabling Swarm Mode. + + - `Docker `_ + - `Swarm Mode `_ + +.. _sync-endpoint-setup: + +ODK Sync Endpoint Setup +---------------------------- + +ODK Sync Endpoint requires a database and a *LDAP* directory, you could follow the instructions and deploy all three components together or supply your own database and/or *LDAP* directory. + +.. note:: + + All of the following command should be run on your server + +Setup instructions: + + 1. Choose a directory to store you endpoint in. In that directory, run: + + .. code-block:: console + + $ git clone https://github.com/opendatakit/sync-endpoint-default-setup + + 2. Then run: + + .. code-block:: console + + $ docker build --pull -t odk/sync_endpoint https://github.com/opendatakit/sync-endpoint-containers.git + + 3. Then run: + + .. code-block:: console + + $ docker build --pull -t odk/sync-web-ui https://github.com/opendatakit/sync-endpoint-web-ui.git + + 4. In the cloned repository, + + .. code-block:: console + + $ docker build --pull -t odk/db-bootstrap db-bootstrap + + 5. In the cloned repository, + + .. code-block:: console + + $ docker build --pull -t odk/openldap openldap + + 6. In the cloned repository, + + .. code-block:: console + + $ docker build --pull -t odk/phpldapadmin phpldapadmin + + 7. Enter your hostname in the :code:`security.server.hostname` field in the :file:`security.properties` file. + + 8. If you're not using the standard ports (80 for *HTTP* and 443 for *HTTPS*) enter the ports you're using in the :code:`security.server.port` and :code:`security.server.securePort` fields in the :file:`security.properties`. Then edit the **ports** section under the **sync** section in :file:`docker-compose.yml` to be :code:`YOUR_PORT:8080`. + + .. note:: + + It is important that the right side of the colon stays as 8080. This is the internal port that the web server is looking for. + + 9. If you're using your own *LDAP* directory or database, continue with the instructions: + + - :ref:`Custom database instructions ` + - :ref:`Custom LDAP instructions ` + + 10. In the cloned repository: + + .. code-block:: console + + $ docker stack deploy -c docker-compose.yml syncldap + + 11. The server takes about 30s to start, then it will be running at http://127.0.0.1. + 12. See the :ref:`LDAP section ` for instructions on configuring users and groups. + +.. _sync-endpoint-setup-database: + +Custom database +~~~~~~~~~~~~~~~~~~~~~~ + + 1. If you haven't followed the :ref:`common instructions `, start with those. + 2. Remove the *db* and *db-bootstrap* sections in :file:`docker-compose.yml`. + 3. Modify :file:`jdbc.properties` to match your database. Supported database systems are :program:`PostgreSQL`, :program:`MySQL` and :program:`Microsoft SQL Server`. Sample config for each type of database can be found `on Github `_. + 4. Modify :file:`sync.env` to match your database + 5. In the cloned repository, + + .. code-block:: console + + $ docker stack deploy -c docker-compose.yml syncldap + + 6. The server takes about 30s to start, then it will be running at http://127.0.0.1. + +.. _sync-endpoint-setup-ldap: + +Custom LDAP directory +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + 1. If you haven't followed the :ref:`common instructions `, start with those. + 2. OPTIONAL: If your LDAP directory uses a certificate that was signed by a self-signed CA, + + a. Make the public key of the CA available to ODK Sync Endpoint with this command. + + .. code-block:: console + + $ docker config create org.opendatakit.sync.ldapcert PATH_TO_CERT + + b. Uncomment the relevant lines in the *configs* section in :file:`docker-compose.yml` and the *configs* section under the *sync* section in :file:`docker-compose.yml`. + + 3. Remove the *ldap-service* and *phpldapadmin* sections in :file:`docker-compose.yml`. + 4. Modify the relevant sections in :file:`security.properties` to match your LDAP directory. Further instructions are in the file. + + .. note:: + + The default configuration does not use ldaps or StartTLS because the LDAP directory communicates with the ODK Sync Endpoint over a secure overlay network. You should use ldaps or StartTLS to communicate with your LDAP directory. + + 5. In the cloned repository: + + .. code-block:: console + + $ docker stack deploy -c docker-compose.yml syncldap + + 6. The server takes about 30s to start, then it will be running at http://127.0.0.1. + +.. _sync-endpoint-stopping: + +Stopping ODK Sync Endpoint +------------------------------- + + 1. Run: + + .. code-block:: console + + $ docker stack rm syncldap + + 2. OPTIONAL: If you want to remove the volumes as well, + + - Linux/macOS: + + .. code-block:: console + + $ docker volume rm $(docker volume ls -f "label=com.docker.stack.namespace=syncldap" -q) + + - Windows: + + .. code-block:: console + + $ docker volume rm (docker volume ls -f "label=com.docker.stack.namespace=syncldap" -q) + +.. _sync-endpoint-ldap: + +LDAP +----------- + + - The default admin account is *cn=admin,dc=example,dc=org*. + - The default password is *admin* - it can be changed with the *LDAP_ADMIN_PASSWORD* environment variable in :file:`ldap.env` + + - The default readonly account is *cn=readonly,dc=example,dc=org*. + - The default password is *readonly* - it can be changed with the *LDAP_READONLY_USER_PASSWORD* environment variable in :file:`ldap.env`. This account is used by the Sync Endpoint to retrieve user information. + +The LDAP directory that you deployed with the instructions above is an :program:`OpenLDAP` server. In addition to the directory, a :program:`phpLDAPadmin` server is also deployed to help you configure the directory. + +If you'd prefer to use the :program:`OpenLDAP` command line utilities, they're installed in the OpenLDAP container. These tools are accessible with this command: + + - Linux/macOS: + + .. code-block:: console + + $ docker exec $(docker ps -f "label=com.docker.swarm.service.name=syncldap_ldap-service" --format '{{.ID}}') LDAPTOOL ARGS + + - Windows: + + .. code-block:: console + + $ docker exec (docker ps -f "label=com.docker.swarm.service.name=syncldap_ldap-service" --format '{{.ID}}') LDAPTOOL ARGS + +.. note:: + + The phpLDAPadmin server listens on port 40000, it is important that you do not expose this port to the internet. + +The following guides assume that you're using :program:`phpLDAPadmin`. + +.. _sync-endpoint-ldap-users: + +Creating users +~~~~~~~~~~~~~~~~~~~~~~~~~ + + 1. Click: :guilabel:`login` on the right and login as *admin*. + 2. Expand the tree view on the right until you see :guilabel:`ou=people`. + 3. Click on :guilabel:`ou=people` and choose :guilabel:`Create a child entry`. + 4. Choose the :guilabel:`Generic: User Account` template. + 5. Fill out the form and click :guilabel:`Create Object`. + 6. Assign users to groups with `these instructions `. + +.. _sync-endpoint-ldap-groups: + +Creating groups +~~~~~~~~~~~~~~~~~~~~~~~~~ + + 1. Click: :guilabel:`login` on the right and login as *admin*. + 2. Expand the tree view on the right until you see :guilabel:`ou=groups`. + 3. Click on :guilabel:`ou=default_prefix` and choose :guilabel:`Create a child entry`. + 4. Choose the :guilabel:`Generic: Posix Group` template. + 5. Fill out the form and click :guilabel:`Create Object`. + + .. note:: + + The group name must start with the group prefix, in this case the group prefix is *default_prefix* so for example: *default_prefix my-new-group* + + 6. Assign users to groups with `these instructions `. + +.. _sync-endpoint-ldap-assign: + +Assigning users to groups +""""""""""""""""""""""""""""" + + 1. Click: :guilabel:`login` on the right and login as *admin*. + 2. Expand the tree view on the right until you see :guilabel:`ou=default_prefix`, then expand :guilabel:`ou=default_prefix`. + 3. This list is all the groups under *ou=default_prefix*. + 4. Click on the group that you want to assign users to. + 5. A few groups are created when the LDAP server is brought up, refer to :doc:`data-permission-filters` for descriptions of these groups. + 6. If the :guilabel:`memberUid` section is not present: + + a. Choose :guilabel:`Add new attribute`. + b. Choose :guilabel:`memberUid` from the dropdown, then enter :guilabel:`uid` of the user you want to assign. + c. Click :guilabel:`Update Object` at the bottom to update. + + 7. If the :guilabel:`memberUid` section is present, + + a. Navigate to the :guilabel:`memberUid` section. + b. Click modify group members to manage members. + +.. _sync-endpoint-https: + +HTTPS +----------------- + + 1. Store your certificate public key in a :program:`Docker` config with this command: + + .. code-block:: console + + $ docker config create example.com.fullchain.pem PATH_TO_PUBLIC_KEY + + 2. Store your certificate private key in a :program:`Docker` secret with this command: + + .. code-block:: console + + $ docker secret create examepl.com.privkey.pem PATH_TO_PRIVATE_KEY + + 3. Modify the *configs* section and *secrets* section in :guilabel:`docker-compose.yml` to include name of the :program:`Docker` config and :program:`Docker` secret created above. + 4. Uncomment the relevant lines in the *nginx* section in :guilabel:`docker-compose.yml`. + diff --git a/odk2-src/tables-install-sample.rst b/odk2-src/tables-install-sample.rst new file mode 100644 index 000000000..a09837588 --- /dev/null +++ b/odk2-src/tables-install-sample.rst @@ -0,0 +1,103 @@ +.. spelling:: + geotagging + +Installing the ODK Tables Sample Application +=============================================== + +.. _tables-sample-app: + +.. _tables-sample-app-install: + +Install the Sample Application +--------------------------------- + +We have provided a sample application to help you acquaint yourself with the various features. This sample app contains five demo apps within it. + +Unlike ODK Collect, the ODK 2.0 tools are application-focused. An application is identified by the name of the directory under the :file:`/sdcard/opendatakit/` folder. The sample application is named *default*, as are the sample applications provided for :doc:`survey-intro` :doc:`services-intro`. This means that you can only deploy one of these sample application at a time onto a device. Later, we will explain how to rename one of these so that two or more applications can co-exist and not interfere with each other on this same device (this will require setting up an :doc:`cloud-endpoints-intro` for that renamed application; each Endpoint can host only one application at a time and must be configured with the application name that it will host). + +.. _tables-sample-app-overview: + +Five Demo Apps +~~~~~~~~~~~~~~~~~~~~~ + +This sample application consists of 5 separate demo apps: + + - **Tea Houses** - a fictional Benin Teahouse directory. + - **Hope Study** - a simplified subset of a perinatal follow-up application that was piloted on ODK Tables and ODK Collect (now converted to use ODK Survey). + - **Plot** - a fictional agricultural field pest- and yield- assessment application. + - **Geotagger** - a simple geotagging application. + - **JGI** - an app used to track the daily behavior of chimpanzees. + +We will use the ODK 2.0 synchronization mechanism to install this app. It is about 26 MB in size and takes a few minutes to download from the web. + +.. warning:: + + Using the sync mechanism will delete all the configuration files for any data tables not present on the server. If you have experimented with creating forms and pushing them to your device, when you sync to the server, the synchronization process will delete these extraneous forms from your device and leave only the forms and files defined on the server. + +To access this sample application and its 5 demo apps, + + 1. Launch ODK Tables. + 2. Click on the sync icon (2 curved arrows) to launch ODK Services directly into the sync activity. + 3. From the menu, choose :menuselection:`Settings --> Server Settings`. You may be presented with a pop-up warning you that there are changes on your device that have not been synced to your server. Since you are setting up this demonstration application for the first time, you can choose :guilabel:`Ignore Unsynced Changes`. + 4. Choose :guilabel:`Server URL` and specify https://opendatakit-tablesdemo.appspot.com as the server URL (note that this URL begins with https:// ). + 5. Because this server allows anonymous access, the :guilabel:`Server Sign-on Credential` should be set to: :menuselection:`None (anonymous access)`. Other options are :menuselection:`Username and Google Account`. When setting up your own server, the ODK Sync Endpoint only supports Username. ODK Aggregate supports both Username and Google Account. + 6. Exit out of the :menuselection:`Server Settings` page, and then the :menuselection:`Settings` page, by using the back button. During this process, if you had chosen a :guilabel:`Server Sign-on Credential` other than :menuselection:`None`, you will be prompted to authenticate that user. + + .. warning:: + + If you decline (by choosing to :guilabel:`Log Out`), or if your credential is rejected by the server, then your credential will be reset to the anonymous (unprivileged) user. + + 7. Confirm that the Server URL matches that set up above. From this point forward, whenever you initiate a sync, you do not need to visit the :menuselection:`Settings` page, but can perform the sync entirely from this screen. + 8. The sync interaction has four options: + + - :menuselection:`Fully Sync Attachments` - *Default* - Synchronize all row-level data and file attachments with the server. + - :menuselection:`Upload Attachments Only` - Only upload attachments from the device to the server + - :menuselection:`Download Attachments Only` - Only download attachments from the server to the device + - :menuselection:`Do Not Sync Attachments` - Do not sync any attachments + + 8. Click on :guilabel:`Sync Now`. + +The sync process will now begin. If you have selected to use a Google Account, you may be challenged to authorize access to your Google account information. Otherwise, the sync will begin and a progress dialog will appear. As stated above, this synchronization mechanism forces the configuration of the device to exactly match that of the server. Any local configuration files for data tables or forms that are not present on the server will be removed from your device (i.e., everything under the :file:`/sdcard/opendatakit/default/config` directory will be revised to exactly match the content on the server). + +.. note:: + + As a safeguard to prevent data loss, data tables that are only defined on the device will not be deleted. However, because their associated configuration files will have been removed, they are generally inaccessible until you restore their configuration files and their forms onto the device. + +Once the configuration of the device is an exact match to that of the server, the data within the data tables are synchronized. And, finally, the file attachments associated with those data are synchronized. If you have a slow connection, it may take two or three tries before the sync is successful; the system stops at the first timeout and does not attempt any retries. + +When complete, click :guilabel:`OK` on the :guilabel:`Sync Outcome` dialog and back out of the ODK Services application, returning to ODK Tables. ODK Tables should now present a custom home screen with five tabs, one for each of the demos. If it does not, back out of ODK Tables and re-launch it. + +Select a tab (demo application), then click the :guilabel:`Launch Demo` button to enter that sample application. + +.. _tables-sample-app-layout: + +Layout of Application Files +------------------------------------- + +The layout of this sample app is as follows: + + - :file:`/sdcard/opendatakit`-- directory containing all ODK 2.0 applications. Each application is a sub-directory within this directory. + - :file:`/sdcard/opendatakit/default` -- default application name (directory) for the ODK 2.0 tools + +Within the application folder (:file:`/sdcard/opendatakit/default`), the following directories are present: + + - :file:`config` -- contains read-only configuration files that define the user's application (for example, the 5demos example application you just synced from https://opendatakit-tablesdemo.appspot.com). Within this folder are: + + - :file:`assets` -- contains files that initialize your data tables (in the csv sub-folder) and define the custom home screen and provides CSS files for overall appearance of your app, and JavaScript libraries and files for common behaviors in your app. + - :file:`tables` -- contains directories that are named with table ids. Within these sub-directories, the ODK Survey forms and table-specific HTML, JavaScript, and CSS files are found. For example, the HTML file describing the list view for the tea houses table is found in :file:`config/tables/Tea_houses/html/Tea_houses_list.html`. + + - :file:`data` -- contains the database and row-level attachments (files). + - :file:`output` -- contains files that are generated (such as detailed logging files) or exported (such as CSV files) by the ODK tools on the device. + - :file:`system` -- an area maintained by the tools themselves (ODK Survey, ODK Tables, ODK Scan, and so on). These files are extracted and placed here by the APKs. You should not modify files in this folder; when first started, the ODK tools sweep this directory to verify that these files match their internal copy. Any deviant file is replaced with a fresh internal copy. + +The automatic configuring and loading of data into ODK Tables is governed by the :file:`config/assets/tables.init` file. It provides a list of table ids and the CSV files (located in the :file:`config/assets/csv` folder) that should be imported to populate them. This is discussed in more detail in the :ref:`Tables User Guide `. + +.. note:: + + This file is the only configuration file that is not synced to the server. This is to optimize start-up of your application on other devices; once this initial data has been loaded into your data tables and synced to the server, the other devices will obtain the data through an ordinary sync action. + +.. note:: + + This file is scanned once. If the import(s) fail, it could leave some tables partially initialized. The file will be re-processed and data rows re-loaded by clicking on :menuselection:`Reset Configuration` on the :guilabel:`Settings` screen then exiting the ODK Tools and re-launching them. Upon being re-launched, the file will be scanned and processed. + +Most of the app-level settings that are configured through the :menuselection:`Settings` page are stored in the :file:`config/assets/app.properties` file. Excluded from this file are the :guilabel:`Server Sign-On Credential type`, and the values for that credential (such as username and password). This allows the application designer to specify and enforce most of the app-level settings (such as the server used when syncing) via the sync mechanism. diff --git a/odk2-src/tables-install.rst b/odk2-src/tables-install.rst new file mode 100644 index 000000000..4d2030bac --- /dev/null +++ b/odk2-src/tables-install.rst @@ -0,0 +1,44 @@ +Installing ODK Tables +=========================== + +.. _tables-install: + +.. _tables-install-prereqs: + +Prerequisites +-------------------------------------- + +ODK Tables requires: + + - :doc:`app-designer-intro` + - :doc:`services-intro` + - :doc:`survey-intro` + - :program:`OI File Manager` -- please download this from the `Google Play Store `_ (this is required) + + +.. _tables-install-app: + +Installing the ODK Tables App +----------------------------------- + + + 1. From your device's :guilabel:`Settings`, choose :menuselection:`Security`. + + - Make sure *Unknown Sources* is checked. + - (On older versions of Android, this setting is in :menuselection:`Applications` rather than :menuselection:`Security`) + + 2. Open a web browser on your phone. + 3. Navigate to http://opendatakit-dev.cs.washington.edu/2_0_tools/download and download the latest ODK Tables APK. + 4. In the download window, you will see ODK_Tables.N.N.apk. - Select it to download the file. + + - On older devices, the APK will automatically install after you approve the security settings. + - On newer devices, you must go to the download list, rename the file to restore the .apk extension (the extension will have been renamed to .man during the download process), then click on it to install it. + +.. note:: + + You can also `download the ODK Tables APK `_ to your computer and load it on your device via `adb `_ or another tool like `AirDroid `_. + +.. tip:: + + You can also `install ODK Tables on an Android emulator `_. However, this can be slow and is only recommended for developers actively working on Tables. + diff --git a/odk2-src/tables-intro.rst b/odk2-src/tables-intro.rst new file mode 100644 index 000000000..61d1e1f83 --- /dev/null +++ b/odk2-src/tables-intro.rst @@ -0,0 +1,26 @@ +ODK Tables +============= + +.. _tables-intro: + +:dfn:`ODK Tables` is a program that allows you to visualize and update existing data. Using Tables as your entry-point to data collection, you will be able to gather data using ODK Survey, sync it to a server using ODK Services, and have other users download and edit this same data on their own devices. + +Tables also enables web developers to build powerful *data management applications* to handle their complex workflows. While Survey follows a traditional data collection workflow, similar to that of Collect, Tables gives you the flexibility to implement your own arbitrary complex workflow. For example you might collect data via a customized mapping interface: Tables allows you to build an application using web technologies to achieve that. + +.. note:: + + ODK Tables only works on Android 4.2 and newer devices. + + +We have included a sample application built on top of Tables along with a handful of data tables that showcase some of its features. + +.. _tables-intro-learn-more: + +Learn more about ODK Tables +------------------------------- + +.. toctree:: + :maxdepth: 2 + + tables-setup + tables-using diff --git a/odk2-src/tables-setup.rst b/odk2-src/tables-setup.rst new file mode 100644 index 000000000..a07993bc1 --- /dev/null +++ b/odk2-src/tables-setup.rst @@ -0,0 +1,9 @@ +Setting Up ODK Tables +======================= + +.. toctree:: + :maxdepth: 2 + + tables-install + tables-install-sample + diff --git a/odk2-src/tables-using.rst b/odk2-src/tables-using.rst new file mode 100644 index 000000000..8d5885ecf --- /dev/null +++ b/odk2-src/tables-using.rst @@ -0,0 +1,328 @@ +.. spelling:: + Wi + Fi + Goodall + allfields + + +Using ODK Tables +=================== + +.. _tables-using: + +In this guide we will be demonstrating how to use ODK Tables via a guided tour of the sample application. If you have not installed it yet, follow the instructions for :doc:`tables-install-sample`. However, this guide can also be used as a reference. + +.. contents:: :local: + +.. _tables-using-custom-home: + +Custom Home Screen +----------------------- + +ODK Tables allows you to customize the app home screen. If you supply a custom home screen (:file:`config/assets/index.html`), you will have the option of using this as the home screen of the app. This is what is displayed after downloading the demo application. + +If there is no custom home screen configured, ODK Tables will display the *Table Manager* view, with the options to add new tables or data via CSVs (the :guilabel:`+` icon), export data to a CSV (the :guilabel:`->` icon), launch ODK Services (the two curved arrows icon), change app-level settings, or view the application version and license information. + +From the custom home screen, you can get to the *Table Manager* view by clicking on the icon with three lines at the top right of the menu bar. + +To enable or disable the use of the custom home screen, go to the *Table Manager* and click on the :guilabel:`Settings` icon. This will open a screen through-which you can access application-level settings. Click on :menuselection:`Tables-specific Settings` and then check or uncheck the :guilabel:`Use custom home screen` checkbox. This checkbox is only enabled if the :file:`config/assets/index.html` file exists. After making the selection, you will need to fully back out of the ODK Tools and re-launch ODK Tables to have it pick up the change and render the home screen. + +The sample application's custom home screen has five tabs. Select the :guilabel:`Tea` tab and click :guilabel:`Launch Demo`. This will display another screen with three buttons. This is a custom layout written in HTML, CSS, and Javascript. + +.. note:: + + All of these screens and web pages are served directly off of the device -- there is no network access. These are fully able to function in Airplane mode -- without a Wi-Fi or internet connection. + + When you design your applications, you can either have them operate without any network access, or you can write them to access data on the internet. This becomes your design choice. + +Click on the :guilabel:`View Tea Houses` button. + +.. _tables-using-view-data: + +Viewing Data +------------------------ + +Within ODK Tables, full data sets can be viewed in the following ways: + + - :ref:`List View ` -- A list of items, rendered using your own customized HTML and JavaScript. Each item can be selected to display details of that item. + - :ref:`Spreadsheet View ` -- A tabular view reminiscent of a Microsoft :program:`Excel` Spreadsheet. + + .. note:: + + *Spreadsheet View*, unlike all the other options here, is rendered with Android user interfaces rather than your own customized HTML and JavaScript. + + - :ref:`Map View ` -- A pap displaying data points which can be selected to display details of data associated with that data point. + +Additionally, the following view options provide alternative methods of viewing data: + + - :ref:`Detail View ` -- A page that shows the details of an individual record. Often used when a single record is selected in a *List View* or a *Map View*. + - :ref:`Detail with Sublist View ` -- A hybrid of a *Detail View* and a *List View*, using half the screen for each. + - :ref:`Graph View ` -- A graphical rendering of data (such as a bar graph or pie chart). + - :ref:`Navigate View ` -- A *Map View* that adds a compass and geographic data to help the user navigate to points on the map. + +Finally, if your workflow doesn't fit well into the above options, you can create your own :ref:`Custom View `. + +The following sections take you through an example of each type of view from the sample application. + +.. _tables-using-view-data-list: + +List View +~~~~~~~~~~~~~~~~~ + +After clicking on the :guilabel:`View Tea Houses` button in the :ref:`custom home screen section ` you are looking at a *List View* of the *Tea Houses* table. This view is designed entirely in JavaScript and HTML, and we have customized it for the *Tea Houses* table. Click on the lined paper icon at the top of the screen. Here you’ll see all the possible view types. Select :menuselection:`Spreadsheet`. + +.. _tables-using-view-data-spreadsheet: + +Spreadsheet View +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This takes you to a familiar view as if you were looking at it on a spreadsheet. Each row here represents a tea house, and each was a row in the *List View*. The thin column on the left is called the *status column*: it will show a different color based on the status of that row. + + - White (clear) -- The row is downloaded from the server and has not been modified. + - Yellow -- The row is modified. + - Green -- The row is entirely-new row + - Black -- The row is deleted. It will show as black until you sync with the server and publish those changes. + +Select the lined paper icon again, and select :menuselection:`Map`. + +.. _tables-using-view-data-map: + +Map View +~~~~~~~~~~~~~~~~~~~~~ + +All the fictional tea houses in Benin appear on the map. Pinch and squeeze or widen to zoom out and in, respectively. The tea house location is plotted based on what appeared in the *Location_latitude* and *Location_longitude* columns in the *Spreadsheet View*. When you click on a map marker, the *List View* will redraw with that marker's information at the top of the *List View*. + +Click on an entry and you will be taken to a *Detail with Sublist View*. + +.. _tables-using-view-data-detail-with-list: + +Detail with Sublist View +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Tea houses shows individual tea houses using the *Detail with Sublist View*. This renders the screen as a *Detail View* webpage and a subordinate *List View*. In this case, the *Detail View* displays information on the tea house, and the *List View* displays the teas that the tea house serves. Within the *Detail View*, you can scroll down to see the information we decided to display. Like the *List View*, we programmed this using very rudimentary HTML and JavaScript, but it could be customized to look fancier or display additional information. + +Scroll to the bottom and you’ll see a link as a number of teas. This is using the information in the table called *Tea Inventory* to tell you how many teas this tea house offers, and has also been defined in the JavaScript. + +The subordinate list webpage displays a list of all of these teas. Click on one, and you will now be in a *Detail View* for that tea inventory item. + +.. _tables-using-view-data-detail: + +Detail View +~~~~~~~~~~~~~~~~~~ + +The tea inventory *Detail View* displays information about the tea, including whether it is available hot, iced, in bags, or loose leaf. Note that the tea type is being pulled from the *Tea Types* table, but the JavaScript is getting the information from that table to construct our view. Like the other views, we programmed this using very rudimentary HTML and JavaScript, but it could be customized to look fancier or display additional information. + +Hit the device’s back button until you are back to the home screen. + +.. _tables-using-view-data-graph: + +Graph View +~~~~~~~~~~~~~~~~~~~~~~~~~ + +*List Views* can use JavaScript packages like D3 to render data graphically. Launch the *Plot Demo*, choose :guilabel:`View Plots`, and choose :guilabel:`Puerto Madero` to see a bar graph of corn crop heights across different visits to this farm. That graph was rendered using D3. That library can render scatter plots, line graphs, graphs with error bars and many other visualizations. + +.. _tables-using-view-data-navigate: + +Navigate View +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These views render similarly to the *Map View*, but will use show a compass, bearing, and heading towards a point selected from the map. Back out of the *Plot Demo* and launch the *Geotagger Demo*. From the view options in the upper right, change the selection from *Map View* to *Navigate View* to see the same map of points, but with the navigation user interface replacing the list on the top portion of the screen. Select different points and walk around to see the navigation information update in real time. + +.. _tables-using-view-data-custom: + +Custom Views +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The previous view examples cover common workflows. However, any arbitrary web based user interface can be constructed and rendered within Tables. Third party libraries, such as *Math.js* or *Snap.js*, can also be included. See more in the :ref:`App Designer user guide `. + +.. tip:: + + These views are not restricted to viewing data. Data can be edited by calling the provided Javascript APIs (see :ref:`Understanding the Web File `). + +.. _tables-using-view-data-custom-non-form-entry: + +Non-form-based data entry +""""""""""""""""""""""""""""""""" + +Finally, back out of the *Plot Demo* and choose *JGI*. + +The *JGI* (Jane Goodall Institute) demo app is a portion of a chimpanzee interaction tracking app that field researchers can use to record the activities of a designated ("followed") chimp and its interactions with nearby chimpanzees at 15-minute intervals. + +Choose :guilabel:`New Follow`. Fill in the form fields, and click :guilabel:`Begin`. And begin recording interactions of this chimp with other known chimps in that group. These fields are all filled-in using hand-written HTML -- not ODK Survey. ODK Survey would be too scripted and confining for this type of dynamic interaction record. + +.. note:: + + ODK Survey is not necessary for data collection. It is, however, more convenient in most cases. + +.. _tables-using-edit-row: + +Edit a row with Survey +-------------------------- + +.. _table-using-edit-row-tea-house: + +Simple Example: Tea Houses +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Launch the *Tea Houses* demo again. Follow the directions above to navigate to a *Detail with Sublist View* of a tea house. + +At the top of the screen you will see a pencil icon. Click on this to open ODK Survey to edit the row using the *Tea Houses* form. This is possible because that form has been specified as the form to use when editing rows in this table. + +.. _table-using-edit-row-hope-study: + +Complex Example: Hope Study +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A more complex example of this same flow is in the *Hope Demo*. Back out of the *Tea Houses* demo, back to the custom home screen and select the *Hope Demo* and launch it. Choose :guilabel:`Screen Female Client`. This launches an ODK Survey form for entering a new client into the *Hope Study*. Either complete an entry or back out of that form and choose to :guilabel:`Ignore Changes` to leave ODK Survey without adding a new client. + +Choose :guilabel:`Follow Up with Existing Client` to see a *List View* of clients who have already been entered into the study. Choosing one of these displays a *Detail View* that allows you to access client or partner forms for that individual. You can also click on the pencil icon at the top of that *Detail View* screen to launch an ODK Survey form within-which you can view or change any of the information for that client. Clicking on the top right button with the form name opens a drop-down menu from which you can choose :guilabel:`Contents` to see a summary of all the form's fields and their values. + +.. _tables-using-geo-tagger: + +Another Map example: Geo Tagger +--------------------------------- + +Another example demo app, *Geotagger*, is also included in the sample application. It contains information and pictures from various places around Seattle. The HTML and JavaScript files associated with this table are slightly more sophisticated, and will give you an idea of the customization you can achieve using Tables. + +From the custom home screen, click on the :guilabel:`Geo` tab, and click on the :guilabel:`Launch Demo` button. + +This directly opens the *Geotagger* dataset in the *Map View*. The data represent several places around Seattle. Click on :guilabel:`Phinney Ridge`, and the item will expand to give you more information. This more sophisticated behavior is all performed in the JavaScript and HTML file, which you can find in :file:`config/tables/geotagger/html/geo_list.html` as well as :file:`config/tables/geotagger/js/geo_list.js`. + +Click on the picture and you’ll be taken to a *Detail View* of the *Phinney Ridge* entry. This *Detail View* is also fancier than those in the :*Tea Time in Benin* example. This file is located in :guilabel:`config/tables/geotagger/html/geo_detail.html`. + +Press the device’s back button to go back to the *Map View*. We’re going to add an entry for your current location. Press the plus icon at the top of the screen and you’ll be taken to ODK Survey. + +Fill out the form, and at the last screen of the form and press :guilabel:`Finalizer`. You’ll now see your new entry in the list. Navigate to the *Detail View* and you’ll see it works there as well. If you go back to *List View* and change to *Spreadsheet View*, you’ll see it there as well. + +.. _tables-using-adding-tables: + +Adding Your Own Tables +------------------------------ + +The creation of data tables is handled within the App Designer. ODK Tables can display and present data, but cannot create Tables on the fly. This enables the ODK Services application to enforce that the configuration of the device (its tables, HTML files, etc.) are identical to those on the server. + +.. _tables-using-adding-tables-app-designer: + +Initialize from ODK Application Designer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +See the Application Designer documentation for :ref:`designing a form ` to describe a new data table. + +.. _tables-using-import-data: + +Import Data into a Table from a CSV +----------------------------------------- + +This section assumes you have already created the table into which you are importing data. + +Once you have created the table, you can load data from a CSV into it by choosing the plus :guilabel:`'+` icon on the *Table Manager* screen. + +A CSV is a comma-separated values file. It is a common way to transport tabular data between different programs. Microsoft :program:`Excel` can save and open CSV files, as can :program:`Open Office` and a variety of other programs. Tables expects a certain format of the data in order to import the data correctly: the first line must be the comma-separated list of column names. The remaining lines must be the data for each of the corresponding columns. + +For example, assume you wanted to load data into table of people's names, with column (field) names of :th:`Name` and :th:`Age`. In addition to those columns, your CSV file must also specify the unique row id (:th:`instance id`) for each data row (the :th:`_id` column); you can also specify the creator of the row, the time of creation, and other information. But, at a minimum, the file should look like: + +.. code-block:: none + + _id,Name,Age + myUniqueIdforSam,Sam,27 + +This can be achieved by creating a spreadsheet in a spreadsheet editor and saving it as a CSV, or by copying the above text into a text editor and saving it with a :file:`.csv` extension. + +The upload process is as follows: + + #. Place the CSV file onto the device and place it in the :file:`config/assets/csv/` directory with a filename of :file:`tableid.csv`. For example, :file:`/sdcard/opendatakit/default/config/assets/csv/people.csv` would be the CSV file for the *people* table. + #. Launch ODK Tables and navigate to the *Table Manager* screen. + #. Press the plus :guilabel:`+` button at the top of the *Table Manager* screen. + #. Press :guilabel:`Select CSV File to Import`. + + .. note:: + + You must have installed OI File Manager from the Play Store. + + #. Find your file, select it, and press :guilabel:`Pick file`. + #. Press :guilabel:`Append to an Existing Table`. + +The data will be read from the file and appended to your data table. + +.. warning:: + + Prior to any deployment, you should sync your device to your server and export the data table and copy the exported CSV file back on top of the simple CSV file that you created above. + + This ensures that the additional fields required by the ODK tools are properly populated and that a server-managed revision number is added to the data rows so that all devices will have the same internal ids for all of your data rows. This eliminates the possibility of the :file:`tables.init` mechanism introducing duplicate records and speeds the sync process and minimizes the occurrence of conflicts across the devices when these devices first sync to the server. + +.. warning:: + + Specifying the values for the :th:`_id` column is important. Otherwise, each device, when it loads the CSV file, would assign different unique ids for each of the rows, causing much duplication and confusion. + +.. _tables-using-export-data: + +Exporting Tables to CSV +---------------------------- + +You can export any of your tables to a CSV file and associated supporting files. These files will be written to the :file:`output/csv` directory. + +A Tables-exported CSV includes all the metadata needed to allow the table to be imported with exactly the same status settings, file associations and metadata settings on another device. Exporting produces the following files: + + - file:`tableid.definition.csv` -- this defines the data table's structure. It specifies the columns and their column types and is a copy of the file found under :file:`config/tables/tableId/` + - file:`tableid.properties.csv` -- this defines the column heading names, translations, and the HTML files associated with *List Views*, *Detail Views*, *Map Views*, and so on, and is a copy of the file found under :file:`config/tables/tableId/` + - file:`tableid.csv` -- this holds the data file that you can import to recreate the contents of your data table + - file:`tableId` -- this holds an instances folder that holds folders named after each row id (the row id is cleaned up to remove any invalid filename characters such as slashes and colons). Each of those folders contains the row-level attachments for that row id. + +To export a table: + + #. Launch ODK Tables and navigate to the *Table Manager* screen (if you have a custom home screen, click the icon at the top right with the three increasingly-wide lines). + #. Press the arrow :guilabel:`->` icon at the top of the *Table Manager* screen. + #. Select the table you want to export. + #. Optionally specify a qualifier that will be inserted into the filenames of the emitted files before the :file:`.csv` extension. + #. Press :guilabel:`Export`. + +For example, if you were to export the *geotagger* table and specified *demo* as a qualifier, the following files would be written: + + - :file:`output/csv/geotagger.demo.definition.csv` + - :file:`output/csv/geotagger.demo.properties.csv` + - :file:`output/csv/geotagger.demo.csv/geotagger.demo.csv` + - :file:`output/csv/geotagger/instances/1f9e.../137...jpg` + - :file:`output/csv/geotagger/instances/...` + +.. _tables-using-config-at-startup: + +Configuring an App at Startup +----------------------------------- + +If you are installing Tables on a new device and don’t have a server set up from which to pull the data (see the :ref:`advanced section about syncing `, you can alternatively configure Tables to import data at startup. This is useful during forms development, as you can simply push the form definitions, HTML and JavaScript for your application data down to the phone from your computer and launch ODK Tables, and it will load data from CSV files into your data tables. + +The configuration file must be titled :file:`tables.init` and placed in the :file:`/sdcard/odk/tables/config/assets` directory. Below is the complete contents of the :file:`tables.init` file distributed with the sample application: + +.. code-block:: none + + table_keys=teaHouses, teaTypes, teaInventory, teaHousesEditable, geotagger, plot, plotVisits, femaleClients, maleClients, geopoints, follow + teaHouses.filename=config/assets/csv/Tea_houses.updated.csv + teaTypes.filename=config/assets/csv/Tea_types.updated.csv + teaInventory.filename=config/assets/csv/Tea_inventory.updated.csv + teaHousesEditable.filename=config/assets/csv/Tea_houses_editable.updated.csv + geotagger.filename=config/assets/csv/geotagger.updated.csv + plotVisits.filename=config/assets/csv/visit.example.csv + plot.filename=config/assets/csv/plot.example.csv + femaleClients.filename=config/assets/csv/femaleClients.allfields.csv + maleClients.filename=config/assets/csv/maleClients.allfields.csv + geopoints.filename=config/assets/csv/geopoints.allfields.csv + follow.filename=config/assets/csv/follow.updated.csv + +The table_keys key contains a comma and space separated list of table keys. Each table key can then have a :file:`.filename` that indicate the filename of the CSV data that should be imported; this file should be under the :file:`config/assets/csv` directory and the name should begin with the **tableId**, followed by an optional qualifier (for example, allfields), and end with :file:`.csv`. If there are row-level file attachments for the table, they would be placed in a **tableId** file within the :file:`csv` directory. Each row-level file attachment filename is relative to the folder for that row's id. If the rows :th:`_id` column was *myUniqueIdForSam*, then the filenames in the data table for row-level attachments for that row would be relative to :file:`/sdcard/opendatakit/default/config/assets/csv/tableId/instances/myUniqueIdForSam/`. + +.. note:: + + Any table ids appearing in this file must already have their table definitions and metadata values defined in the definition.csv and properties.csv files within their corresponding :file:`config/tables/tableId` directory. + +.. tip:: + + Only one attempt is made to read and import data at start-up. If that attempt fails, some or all tables may not be initialized or may be partially initialized. You can trigger a re-processing of this file by going to :guilabel:`Settings` and clicking :guilabel:`Reset configuration` then exiting the ODK tool and re-opening it. + +As mentioned earlier, this file is never uploaded to the server. After you have created your user application and loaded data onto your device using this mechanism, resetting the app server will push all the configuration files and all of data (the data rows loaded by the :file:`tables.init` script) up to the server (except for this :file:`tables.init` file). Other devices that synchronize with the server will retrieve all of those data rows during the data-row synchronization phase. There is no need for the devices that synchronize with the server to have a copy of the :file:`tables.init` file and independently perform these actions. + +.. _tables-using-syncing: + +Syncing--Advanced +-------------------------- + +The final thing you might like to try is synchronizing data to the cloud. See the instructions in the :ref:`ODK Services user guide `. diff --git a/odk2-src/xlsx-converter-intro.rst b/odk2-src/xlsx-converter-intro.rst new file mode 100644 index 000000000..2d6322393 --- /dev/null +++ b/odk2-src/xlsx-converter-intro.rst @@ -0,0 +1,42 @@ +ODK XLSX Converter +==================== + +.. _xlsx-converter-intro: + +:dfn:`ODK XLSX Converter` is a tool, similar to XLSForm, that converts XLSX files (created with :program:`Excel`) into ODK Survey definition files that are used by ODK Survey. + +.. warning:: + + Forms created with XLSX Converter are not compatible with ODK Collect. They only work with ODK Survey. + +For example, a spreadsheet like this: + +.. csv-table:: Example Spreadsheet + :header: "type", "name", "display.prompt.text" + + "begin screen", + "text", "name", "Enter name:" + "integer", "age", "Enter age:" + "end screen", + +Will result in a survey like this: + +.. image:: /img/xlsxconverter/survey-screen.* + :alt: An example rendering of a basic XLSX file in ODK Survey + :class: device-screen-vertical + +.. tip:: + + See :file:`exampleForm.xlsx` (located at the path :file:`app/config/tables/exampleForm/forms/exampleForm/` in the Application Designer) for a more extensive list of examples. + +.. _xlsx-converter-intro-learn-more: + +Learn more about ODK XLSX Converter +------------------------------------ + +.. toctree:: + :maxdepth: 2 + + xlsx-converter-using + xlsx-converter-reference + diff --git a/odk2-src/xlsx-converter-reference.rst b/odk2-src/xlsx-converter-reference.rst new file mode 100644 index 000000000..a84698d4a --- /dev/null +++ b/odk2-src/xlsx-converter-reference.rst @@ -0,0 +1,1010 @@ +.. spelling:: + openRowInitialElementKeyToValueMap + newRowInitialElementKeyToValueMap + exampleForm + isSessionVariable + keyMapLongCol + keyMapLatCol + keyColorRuleType + mapListViewFileName + listViewFileName + defaultViewFileName + defaultViewType + hindi + templatePath + attr + inputAttributes + hideInContents + detailViewFileName + + +ODK XLSX Converter Reference +================================ + +.. contents:: :local: + +.. _xlsx-ref-worksheets: + +Excel Worksheets +------------------------------ + +A workbook is composed of one or more worksheets. XLSX Converter expects the worksheets within a workbook to use the following nomenclature. + +.. list-table:: Worksheet Reference Table + :header-rows: 1 + + * - Worksheet + - Required? + - | Description + * - :ref:`survey ` + - Required + - | Contains the content and control flow of the + | survey. It contains the full list of questions + | and determines the order in which they will be + | asked. This worksheet can be broken into + | different section worksheets for ease of use. + * - :ref:`settings ` + - Required + - | Includes such details as the form name and id, + | as well as the default language. + * - :ref:`properties ` + - Optional + - | Defines the key-value properties that can + | specify the detail, list view, and other + | properties to use with this table. This sheet + | should only be specified in forms whose + | :th:`form_id` matches their :th:`table_id`. + * - :ref:`calculates ` + - Optional + - | Contains the JavaScript formulas that can be + | used in other worksheets + * - :ref:`choices ` + - Optional + - | Contains the sets of choices for multiple + | choice questions. Each row represents a + | response. Choices with the same + | :th:`choice_list_name` are considered to be + | part of the same choice set. Choice sets can + | be used multiple times throughout a survey + | (such as a yes/no choice set). + * - :ref:`model ` + - Optional + - | Defines the table definition in cases where + | multiple forms edit the same data table + * - :ref:`queries ` + - Optional + - | Gets data from an external source that can + | be used as the choice set for multiple + | choice or :tc:`linked_table` questions much like + | the choices worksheet. + * - :ref:`user_defined_section ` + - Optional + - | Worksheets with custom section names can be + | used in conjunction with the survey worksheet + | to simplify control flow. + * - :ref:`prompt_types ` + - Optional + - | Defines custom prompt types that can be used + | within a survey. + * - :ref:`column_types ` + - Optional + - | Defines custom column types that are formulas, + | functions, or pathnames. + * - :ref:`framework_translations ` + - Required + - | ONLY :file:`framework.xlsx`. Translations for + | standard prompts. + * - :ref:`common_translations ` + - Optional + - | ONLY :file:`framework.xlsx`. Application-wide + | translations. + * - :ref:`table_specific_translations ` + - Optional + - | Only in :th:`form_id` matching :th:`table_id`. + | Translations specific to a given :th:`table_id`. + +.. note:: + + Each worksheet has a set of required and optional columns. For the XLSX workbook to be valid, all entries must have legal values in the required columns. Optional columns can be left blank at any point, and omitted entirely if not used. + +.. _xlsx-ref-survey: + +Survey +~~~~~~~~~~~~~~~~~~~~~~~~~ + +All XLSX Converter form definitions require a **survey** sheet. The **survey** worksheet contains the structure and most of the content of the form. It contains the full list of questions and information about how those questions should be presented. Most rows represent a question; the rest of the rows specify control structures such as screen groups. Blank rows are ignored. + +.. note:: + + In this document, questions and question types will also be referred to as prompts and prompt types. + +There are many prompts available for form development. Some ask the user a question and get a response, but other prompts are simply informational and referring to them as questions is not semantically correct. + +.. _xlsx-ref-survey-req-cols: + +Required Columns +""""""""""""""""""""""" + +A list of the required columns for a **survey** worksheet follows. + +.. list-table:: Survey Worksheet Required Columns + :header-rows: 1 + + * - Column + - | Description + * - type + - | The prompt type that will be used to display information to the user. Prompt + | types can also be used to get data from a user. + * - name + - | The name of the prompt type. This name will be used throughout the workbook + | to reference the prompt. + * - display.prompt + - | A string token identifying the translation entry that can define the text, + | audio, image and video to display for this prompt. + | + | Alternatively, this column can be omitted and the prompt text can be + | specified directly via the :th:`display.prompt.text` column. + +.. _xlsx-ref-survey-opt-cols: + +Optional Columns +""""""""""""""""""""""" + +A list of the optional columns that can be incorporated into a **survey** worksheet is below. + +.. list-table:: Survey Worksheet Optional Columns + :header-rows: 1 + + * - Column + - | Description + * - branch_label + - | Used to identify which part of the survey to branch to when + | used with a :tc:`goto` clause or :tc:`user_branch` prompt. + * - calculation + - | When used with the :tc:`assign` prompt type, assigns a value to a + | prompt type. + * - choice_filter + - | Used to filter the choices of a multiple choice or + | :tc:`linked_table` prompt. + * - clause + - | Used in conjunction with the :th:`condition` column to manage the + | control flow of the survey. :th:`clause` and :th:`condition` control which + | questions get asked in what order, if at all. The :th:`clause` column + | contains control flow options such as :tc:`if`, and the :th:`condition` + | column contains a predicate to determine if action will occur. + | :tc:`if` statements always require a :th:`condition` statement. For other + | :th:`clause` statements, a blank :th:`condition` column is assumed to + | be true. Other commands include :tc:`begin screen`, :tc:`end screen`, + | and :tc:`do section`. + * - comments + - | Never displayed to the user. Used for development purposes to + | leave comments about the form for future reference. It is good + | style to comment your work. + * - condition + - | Used with the :th:`clause` column to manage the control flow of the + | survey. :th:`clause` and :th:`condition` control which questions get + | asked in what order, if at all. The :th:`clause` column contains + | control flow options such as :tc:`if`, and the :th:`condition` column + | contains a predicate to determine if the following actions will + | occur. + * - constraint + - | Takes a JavaScript expression. User cannot navigate forward + | until the constraint evaluates to true. If left blank, its + | default value is true. + * - default + - | Used to set the default value. + * - display.constraint_message + - | A string token identifying the translation entry with the + | text shown to the user if the constraint is violated. + | + | Alternatively, this column can be omitted and this text + | can be specified directly via the + | :th:`display.constraint_message.text` column. + * - display.constraint_message.text + - | Message displayed to user if the constraint is violated. + | Tells the user what needs to change before they can + | continue. + * - display.hint + - | A string token identifying the translation entry with the text + | to display in italics and a smaller font than + | :th:`display.prompt.text`. + | + | Alternatively, this column can be omitted and this text can be + | specified directly via the :th:`display.hint.text` column. + * - display.hint.text + - | Used to display text in italics and a smaller font than + | :th:`display.prompt.text`. Can be used to provide extra instructions + | to the user. + * - display.prompt + - | A string token identifying the translation entry that can define + | the text, audio, image and video to display for this prompt. + | + | Alternatively, this column can be omitted and this information + | can be specified directly via the :th:`display.prompt.*` columns. + * - display.prompt.audio + - | Allows the user to play an audio recording. Requires a relative + | path to where the recording is saved. If saved in the same + | folder as the :file:`formDef.json`, then only the filename of the + | recording needs to be specified. + | + | Alternatively, this can be specified on the translations sheet + | under the :th:`display.prompt` string token (under the + | :th:`display.audio` column heading). + * - display.prompt.image + - | Used to display an image. Requires a relative path to where + | the image is saved. If saved in the same folder as the + | :file:`formDef.json`, then only the image file name and the + | extension (for example :file:`.jpg`, :file:`.gif`) are needed. + | + | Alternatively, this can be specified on the translations sheet + | under the :th:`display.prompt` string token (under the + | :th:`display.image` column heading). + * - display.prompt.text + - | The text that the user will see for this prompt type. + | + | Alternatively, this can be specified on the translations sheet + | under the :th:`display.prompt` string token (under the + | :th:`display.text` column heading). + * - display.prompt.video + - | Allows the user play a video. Requires a relative path to where + | the video is saved. If saved in the same folder as the + | :file:`formDef.json`, then only the filename of the video needs to be + | specified. + | + | Alternatively, this can be specified on the translations sheet + | under the :th:`display.prompt` string token (under the + | th:`display.video` column heading). + * - display.title + - | A string token identifying the translation entry that can + | define the text to display for this prompt in the contents + | screen and as the column name in ODK Tables. + | + | Alternatively, this column can be omitted and this information + | can be specified directly via the :th:`display.title.text` column. + * - display.title.text + - | The display value the user sees when the prompt is displayed + | in the contents screen. + | + | Alternatively, this can be specified on the translations sheet + | under the :th:`display.title` string token (under the + | th:`display.text` column heading). + * - hideInContents + - | Legal value is true. If true, then the prompt on the same row + | will not be displayed on the contents screen. + * - inputAttributes. + - | This column can be used in conjunction with the following + | prompt types: :tc:`string`, :tc:`text`, :tc:`integer`, :tc:`decimal`. The :code:`` can + | specify an HTML attribute to be added to the prompt types. + | For example, :th:`inputAttributes.min` with a value of 5 would add + | :code:`min=”5”` into the HTML element for the prompt type. + * - model.isSessionVariable + - | Legal value is true. If true, then the data value for the prompt + | will be treated as a session variable and won't be saved. + * - required + - | Takes a JavaScript expression. If true, the user will not be able + | to navigate to the next screen until the question is answered. + | If left blank, its default value is false. + * - templatePath + - | Must be specified if using a custom :command:`handlebars` template. + | Requires a relative path to where the template is saved. If + | saved in the same folder as the :file:`formDef.json`, then only the + | filename of the template needs to be specified. + * - value_list + - | Must be used with the **choices** worksheet. The :th:`value_list` + | column of the **survey** worksheet connects to the + | :th:`choice_list_name` column on the **choices** worksheet. + +.. _xlsx-ref-survey-prompt-types: + +Prompt Types +""""""""""""""""""""""""""""" + +The following prompt types are available in ODK Survey. + +.. list-table:: Survey Prompt Types + :header-rows: 1 + + * - Prompt Type + - | Description + * - acknowledge + - | Used to display a message to the user and have them click a checkbox + | to acknowledge that they have read the message. + * - assign + - | Used for internal assignment of a variable. + * - audio + - | Used to capture an audio recording. + * - barcode + - | Used to capture a barcode. + * - date + - | Uses a date picker widget to capture a date. + * - datetime + - | Uses a date time picker widget to capture a date and time. + * - decimal + - | Used to display a message to the user and have them enter a decimal. + * - geopoint + - | Used to capture a GPS location. + * - image + - | Used to capture an image. + * - integer + - | Used to display a message to the user and have them enter an integer + * - linked_table + - | Used to display the instances of table and allows the user to add + | another instance, edit an existing instance, or delete an instance. + * - note + - | Used to display a message to the user. + * - select_multiple + - | Used to ask the user a multiple choice question and allows the user + | to click multiple checkboxes. + * - select_multiple_grid + - | Used to ask the user a multiple choice question, displays the + | choices to the user in a grid, and allows the user to click + | multiple grid items. + * - select_multiple_inline + - | Used to ask the user a multiple choice question, displays the + | choices to the user inline, and allows the user to click multiple + | items. + * - select_one + - | Used to ask the user a multiple choice question and allows the user + | to click one item. + * - select_one_dropdown + - | Used to ask the user a multiple choice question and allows the user + | to select one item from a dropdown box. + * - select_one_grid + - | Used to ask the user a multiple choice question and allows the user + | to select one item from a grid. + * - select_one_inline + - | Used to ask the user a multiple choice question, displays the choices + | to the users inline, and allows the user to click one item. + * - select_one_with_other + - | Used to ask the user a multiple choice question, displays the choices + | to the user, and allows the user to click one item. One of the + | choices provided is an other option which if clicked provides a text + | box for the user to enter a value. + * - string + - | Used to ask the user a question and allows them to enter a string. + * - text + - | Used to ask the user a question and allows them to enter text. + * - time + - | Uses a time picker widget to capture a time. + * - user_branch + - | Used to allow the user to pick which section of the form they would + | like to enter. + * - video + - | Used to capture a video. + +.. _xlsx-ref-settings: + +Settings +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. list-table:: Settings Worksheet Columns + :header-rows: 1 + + * - Column + - | Description + * - setting_name + - | The name of the setting within the form + * - value + - | The value for the setting + * - display.title + - | A string token identifying the translation entry with the text shown to the user + | when the (survey) title is displayed. + | + | Alternatively, this column can be omitted and this text can be specified directly + | via the :th:`display.title.text` column. + * - display.locale + - | A string token identifying the translation entry with the text shown to the user + | when the translation locale is displayed. + | + | Alternatively, this column can be omitted and this text can be specified directly + | via the :th:`display.locale.text` column. + +Available :th:`setting_name` values that can be used: + +.. list-table:: :th:`setting_name` values + :header-rows: 1 + + * - | Value + - | Required? + - | Description + * - table_id + - | Required + - | The unique id of the table that the form data gets + | stored in. + * - survey + - | Required + - | Specify the title of the form via content of the + | :th:`display.title.text` column. That value will + | appear as the title to the user. + * - form_id + - | Optional + - | A unique identifier for the form. Default value is + | the unique id that ODK Survey uses to identify the + | form. + * - form_version + - | Optional + - | A value used for version control of the form. The + | recommended format is yearmonthday (for example: + | 20131212 to say the 12th of December 2013). + * - + - | Optional + - | Used with :th:`display.title.text` to set how the + | section name will appear to the user on the contents + | screen. + * - instance_name + - | Optional + - | Used to display the name of saved instances of the form. + | This must be the name of a prompt type from the **survey** + | worksheet. + * - default + - | Optional + - | Used with :th:`display.prompt.text` (no qualifier), or + | other fields to set the default translation of a UI + | element. Specify label under :th:`display.locale.text` + * - + - | Optional + - | Used with :th:`display.prompt.text.`, or + | other fiels to set other language options in the form. + +A sample **settings** worksheet might look like this: + +.. list-table:: Settings Worksheet Example + :header-rows: 1 + + * - setting_name + - Value + - :th:`display.title.text` + - display.locale.text + - display.locale.text.hindi + * - table_id + - sample_form + - + - + - + * - form_version + - 20130819 + - + - + - + * - survey + - + - Sample Form + - + - + * - default + - + - + - English + - English (as Hindi name) + * - hindi + - + - + - Hindi + - Hindi (as Hindi name) + +.. tip:: + + If the survey has been broken up into multiple worksheets, each worksheet can be assigned its own title by adding a row for it and filling in the :th:`display.title.text` column. + +.. tip:: + + In the case of multiple languages, the :th:`display.locale.text` column determines how the different language options are presented to the user. + +.. _xlsx-ref-properties: + +Properties +~~~~~~~~~~~~~~~~~~~~~~~~~ + +This holds the key-value settings for specifying detail and list views, and other parameters. +The columns in this sheet are: + +.. list-table:: Properties Worksheet Columns + :header-rows: 1 + + * - Column + - | Description + * - partition + - | The class of property to set + * - aspect + - | + * - key + - | The name of the property to set + * - type + - | Valid options: object, array, rowpath, configpath, string, integer, number, boolean + * - value + - | The value of the property to set + +For example, the following configuration specifies that the default view for the table is the list view (HTML). It also defines the detail view, list view, and map view HTML files. And, for the map view, it defines the color rule to apply to the pins in the map view and the latitude and longitude columns to use in displaying those pins. + +.. list-table:: Properties Worksheet Example Table + : header-rows: 1 + + * - partition + - aspect + - key + - type + - value + * - Table + - default + - defaultViewType + - string + - LIST + * - Table + - default + - detailViewFileName + - string + - config/tables/Tea_houses/html/Tea_houses_detail.html + * - Table + - default + - listViewFileName + - string + - config/tables/Tea_houses/html/Tea_houses_list.html + * - Table + - default + - mapListViewFileName + - string + - config/tables/Tea_houses/html/Tea_houses_list.html + * - TableMapFragment + - default + - keyColorRuleType + - string + - None + * - TableMapFragment + - default + - keyMapLatCol + - string + - Location_latitude + * - TableMapFragment + - default + - keyMapLongCol + - string + - Location_longitude + +.. _xlsx-ref-calculates: + +Calculates +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The **calculates** worksheet is an optional worksheet. + +.. list-table:: Calculates Worksheet Columns + :header-rows: 1 + + * - Column + - | Description + * - calculation_name + - | The name used to reference the calculation in other worksheets. + * - calculation + - | The JavaScriptf forumla to be evaluated. + + +Each row of the **calculates** page represents a function that can be used elsewhere in the workbook by referencing the individual :th:`calculation_name`. The :th:`calculation` column can store any valid JavaScript expression. In general, + +.. note:: + + Calculations are referenced in the :th:`condition` column of **survey** worksheets. + +.. tip:: + + There are built in functions for ODK Survey that can be used anywhere in the workbook. See the :ref:`Forumla Functions ` section for more details. + +If a complex calculation is required, you can access the full power of Javascript and the :program:`jquery.js` (that is: :code:`$.some_func(...)` ) and :program:`underscore.js` (that is: :code:`_.some_func(...)` ) libraries. Internally, the calculate column is wrapped and evaluated as a Javascript function: + +.. code-block:: javascript + + function() { + return (YOUR_CALCULATE_COLUMN_CONTENT_HERE); + } + +You can write your own code to perform a join via defining and invoking an anonymous function in your calculate. Here is an example: + +.. code-block:: javascript + + (function() { + var result = ""; + _.each(data('valueListField'), function(element) { + result = result + ", " + element; + }); + return result.substring(2); + }) () + +This defines a function and then invokes it. The available functions within a calculates expression are the following: + +.. list-table:: Available Calculates Functions + :header-rows: 1 + + * - Function + - | Description + - Usage + * - :code:`data(fieldName)` + - | Retrieve the value stored under this fieldName + - :code:`data('myField')` + * - :code:`metadata(instanceMetadataFieldName)` + - | Retrieve value stored under this name + - :code:`metadata('_group_modify')` + * - :code:`selected(promptValue, qValue)` + - | Test whether qValue occurs within a select-multiple + - :code:`selected(data('mySelectMultipleField'),'myChoiceDataValue')` + * - :code:`countSelected(promptValue)` + - | Count the number of selections in a select-multiple + - :code:`countSelected(data('mySelectMultipleField'))` + * - :code:`equivalent(promptValue1, promptValue2, ...)` + - | Test if values are equivalent + - :code:`equivalent(data('promptA'), data('promptB'))` + * - :code:`not(conditional)` + - | Negate a condition ( equivalent to !(conditional) ) + - :code:`not(data('fieldA') === data('fieldB'))` + * - :code:`now()` + - | Return the current time + - + * - :code:`isFinalized()` + - | Return whether or not the current row is finalized + - + * - :code:`assign(fieldName, value)` + - | Store value in fieldName and return value. + - :code:`(8 + assign('myField', 5))*10` + +Additionally, the following functions are also available, but are generally not useful in calculates. They are used within template helper functions (:file:`…/system/survey/js/handlebarsHelpers.js`). + +.. list-table:: Template Helper Functions + :header-rows: 1 + + * - Function + - | Description + - Usage + * - :code:`getCurrentLocale()` + - | Return the currently-active locale + - + * - :code:`localize(locale, displayProperty)` + - | Localize the given display.xxx text + - :code:`localize(getCurrentLocale(), display.hint)` + * - :code:`width(string)` + - | Determine the rendered width of a string + - + * - :code:`expandFormDirRelativeUrlPath(content)` + - | Return url for a file within the form directory. + - + +And, finally, you can also reference the *opendatakit* object (that is: :code:`opendatakit.some_func(...)` ) within these functions (:file:`system/survey/js/opendatakit.js`). + + +.. _xlsx-ref-choices: + +Choices +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The **choices** sheet allows you to specify the set of choices for multiple choice prompts. + +.. list-table:: Choices Worksheet Columns + :header-rows: 1 + + * - Column + - | Description + * - choice_list_name + - | The name used to reference the set of choices. This name must be the same + | as the :th:`values_list` in the **survey** worksheet. + * - data_value + - | The value that gets stored as the user’s response. + * - display.title + - | A string token identifying the translation entry with the text shown to + | the user for this choice value. + | + | Alternatively, this column can be omitted and this text can be + | specified directly via the :th:`display.title.text` column. + * - display.title.text + - | The text that the user sees for this choice. + * - display.title.image + - | An image that the user will see associated with a particular choice. + +.. _xlsx-ref-model: + +Model +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The **model** sheet is an optional sheet that allows you to specify the data model for the :th:`table_id` specified in the **settings** worksheet. + +.. list-table:: Model Worksheet Columns + :header-rows: 1 + + * - Column + - | Description + * - name + - | The name of the data field to be used in :th:`table_id` + * - type + - | The type of data that can be put into this :th:`data_field` of the table. + * - isSessionVariable + - | Whether or not this field is a session variable + | (not persisted -- defaults to false). + +Many more columns can be specified, including a :th:`default` column or, as shown in the exampleForm, a :th:`default[0]` column to initialize the first element (index zero) of a select multiple field. Default values cannot be calculates and must be simple literal values (integers, numbers and strings). + +.. _xlsx-ref-queries: + +Queries +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The **queries** worksheet is an optional sheet that allows you to request data from external sources for use in :tc:`select` prompts. These are some of the things you can do with queries: + + - Connect to website APIs. + - Get data from external Android Applications via file content providers. + - Get data from a linked table + - Open CSV files included in the survey's directory. + - Pass key-value maps to :th:`linked_table` forms when creating or opening that form. + +.. list-table:: Queries Worksheet Columns + :header-rows: 1 + + * - Column + - | Description + * - query_name + - | The name used to reference the information returned by + | the query. + * - query_type + - | Legal value are :tc:`ajax`, :tc:`csv`, and :tc:`linked_table`. + | Used to specify the provenance of the query data. + * - uri + - | Used by :tc:`ajax` and :tc:`csv` queries. The uri to use + | for an :tc:`ajax` query or the name of the CSV file to + | use relative to the location of the :file:`formDef.json` + | file. + * - callback + - | Used by :tc:`ajax` and :tc:`csv` queries. The function + | that will be used to map the query results to the set of + | choices for a multiple choice prompt. + * - linked_table_id + - | Used by :tc:`linked_table` queries. The :th:`table_id` + | used to identify the table that the data will come + | from. This should match the :th:`table_id` provided + | in the **settings** worksheet. + * - linked_form_id + - | Used by :tc:`linked_table` queries. The id of the form + | that will be used to get the results for the + | :tc:`linked_table`. This value should match the + | :th:`form_id` value in the **settings** worksheet. + * - selection + - | Used by :tc:`linked_table` queries to filter results + | when used with :tc:`selectionArgs`. Specifies the + | conditions that must be true for the results to be + | selected but must have :tc:`selectionArgs` to work. + * - selectionArgs + - | Used by :tc:`linked_table` queries to filter results + | when used with :th:`selection`. The arguments to be + | used in the :th:`selection` described above. + * - orderBy + - | Used by :tc:`linked_table` queries to specify the + | order in which results should be returned. + * - newRowInitialElementKeyToValueMap + - | Used by :tc:`linked_table` queries. A Javascript + | object containing key value pairs used to assign + | initial values when creating a new row in the + | linked table. The key is the element name in the + | linked form. The value is the initial value to + | assign to the element. + * - openRowInitialElementKeyToValueMap + - | Used by :tc:`linked_table` queries. A JavaScript + | object containing key value pairs used to assign + | initial values when opening an existing row in the + | linked table. The key is the element name in the + | linked form. The value is the initial value to + | assign to the element. + +The two columns :th:`newRowInitialElementKeyToValueMap` and :th:`openRowInitialElementKeyToValueMap` allow you to pass information from your originating form into the linked form. The element keys in these maps correspond to the element keys in the linked form (not the current form). These can refer to any of the form's fields; commonly, the values you would pass into the :th:`openRowInitialElementKeyToValueMap` would refer to session variables. You would typically pass the :th:`instanceID` of the originating form (that is: :code:`opendatakit.getInstanceID()` ) into the linked form when creating it so that you can store that id in a field in that linked table, thereby tying the newly-created row in that table back to the originating form's row. + +.. _xlsx-ref-user-defined: + +User Defined Section +~~~~~~~~~~~~~~~~~~~~~~~~~ + +A custom named section is essentially a subset of the **survey** worksheet. Thus, all of the columns that were described in the :ref:`survey ` section are applicable in a custom section worksheet. However, the following worksheet names are reserved and cannot be used to name a custom section worksheet: + + - settings + - properties + - choices + - queries + - calculates + - column_types + - prompt_types + - model + - framework_translations + - common_translations + - table_specific_translations + +.. _xlsx-ref-custom-prompt-types: + +Custom prompt_types +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Custom prompts can be created within the survey. The **prompt_types** worksheet can be used to specify the custom prompts so that they will be recognized by Survey. + +.. list-table:: prompt_types Worksheet Columns + :header-rows: 1 + + * - Column + - | Description + * - prompt_type_name + - | The name that will be used to reference the prompt_type + * - type + - | The type of object that will be used to store the data received by the user + | for this prompt type. + +.. _xlsx-ref-column-types: + +column_types +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Custom columns can be used within a workbook that are used to store functions, formulas, and path names. The **column_types** worksheet can be used to specify these custom columns. + +.. list-table:: prompt_types Worksheet Columns + :header-rows: 1 + + * - Column + - | Description + * - column_type_name + - | The name that will be used to reference the column. + * - type + - | The type of information that will be stored in the column (i.e. function, + | formula, app_path_localized). + +.. _xlsx-ref-framework-translations: + +framework_translations +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The **framework_translations** sheet is only present in the :file:`framework.xlsx file`. It defines the translations for all of the standard prompts provided by the ODK 2.0 framework. + +.. list-table:: framework_translations Worksheet Columns + :header-rows: 1 + + * - Column + - | Description + * - string_token + - | The name that will be used string to be translated. + * - text. + - | The value of the translated text string. There can be as many of these + | columns as you want translated languages (such as :th:`text.default`, :th:`text.gr`, + | :th:`text.es`). + * - image. + - | The value of the image url fragment relative to the appName directory + | for this locale. There can be as many of these columns as you want + | translated languages (i.e. :th:`image.default`, :th:`image.gr`, :th:`image.es`). + * - audio. + - | The value of the audio url fragment relative to the appName directory + | for this locale. There can be as many of these columns as you want + | translated languages (i.e. :th:`audio.default`, :th:`audio.gr`, :th:`audio.es`). + * - video. + - | The value of the videourl fragment relative to the appName directory + | for this locale. There can be as many of these columns as you want + | translated languages (i.e. :th:`video.default`, :th:`video.gr`, :th:`video.es`). + +The locale code should generally be the 2-letter language code, or, if necessary, the *language_COUNTRY* naming used by Android can be used to identify a specific language variant. For example: *en_US*, *en_UK* for US English and UK English, respectively. + +.. _xlsx-ref-common-translations: + +common_translations +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The **common_translations** sheet is optional. It should only be present in the :file:`framework.xlsx` file. It can be used by application designers to define translations used across multiple forms and web pages in an application. + +The format for this sheet is the same as that for the **framework_translations** sheet. + +.. list-table:: framework_translations Worksheet Columns + :header-rows: 1 + + * - Column + - | Description + * - string_token + - | The name that will be used string to be translated. + * - text. + - | The value of the translated text string. There can be as many of these + | columns as you want translated languages (such as :th:`text.default`, :th:`text.gr`, + | :th:`text.es`). + * - image. + - | The value of the image url fragment relative to the appName directory + | for this locale. There can be as many of these columns as you want + | translated languages (i.e. :th:`image.default`, :th:`image.gr`, :th:`image.es`). + * - audio. + - | The value of the audio url fragment relative to the appName directory + | for this locale. There can be as many of these columns as you want + | translated languages (i.e. :th:`audio.default`, :th:`audio.gr`, :th:`audio.es`). + * - video. + - | The value of the videourl fragment relative to the appName directory + | for this locale. There can be as many of these columns as you want + | translated languages (i.e. :th:`video.default`, :th:`video.gr`, :th:`video.es`). + +The locale code should generally be the 2-letter language code, or, if necessary, the *language_COUNTRY* naming used by Android can be used to identify a specific language variant. For example: *en_US*, *en_UK* for US English and UK English, respectively. + +.. _xlsx-ref-table-translations: + +table_specific_translations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The **table_specific_translations** sheet is optional. It should only be present in the XLSX file whose :th:`form_id` matches the :th:`table_id`. It defines translations that are available to all forms and web pages specific to that table id. + +.. list-table:: framework_translations Worksheet Columns + :header-rows: 1 + + * - Column + - | Description + * - string_token + - | The name that will be used string to be translated. + * - text. + - | The value of the translated text string. There can be as many of these + | columns as you want translated languages (such as :th:`text.default`, :th:`text.gr`, + | :th:`text.es`). + * - image. + - | The value of the image url fragment relative to the appName directory + | for this locale. There can be as many of these columns as you want + | translated languages (i.e. :th:`image.default`, :th:`image.gr`, :th:`image.es`). + * - audio. + - | The value of the audio url fragment relative to the appName directory + | for this locale. There can be as many of these columns as you want + | translated languages (i.e. :th:`audio.default`, :th:`audio.gr`, :th:`audio.es`). + * - video. + - | The value of the videourl fragment relative to the appName directory + | for this locale. There can be as many of these columns as you want + | translated languages (i.e. :th:`video.default`, :th:`video.gr`, :th:`video.es`). + +The locale code should generally be the 2-letter language code, or, if necessary, the *language_COUNTRY* naming used by Android can be used to identify a specific language variant. For example: *en_US*, *en_UK* for US English and UK English, respectively. + +.. _xlsx-ref-built-in: + +Built-in Functionality +-------------------------- + +The :program:`jquery` and :program:`underscore` libraries are available when defining calculates expressions. + +ODK Survey exposes built-in functionality through formula functions to decrease form development time. + +.. _xlsx-ref-formula: + +Formula Functions +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following formula functions can be used to simplify calculations or expressions. + +.. list-table:: Built in formula functions + :header-rows: 1 + + * - Name + - | Description + - Example + * - :code:`assign` + - | Assignment operator that will assign the value + | to the field and return the value + - :code:`assign('fieldname',value)` + * - :code:`countSelected` + - | Returns the number of items selected from a + | :tc:`select_multiple` prompt + - :code:`countSelected(data(‘options’))` + * - :code:`data` + - | Returns the value of a field or session variable. + - :code:`data(‘options’)` + * - :code:`equivalent` + - | Check to see if two values are equivalent + - :code:`equivalent(data(‘option1’), data(‘option2’))` + * - :code:`isFinalized` + - | Returns true if this submission is finalized + - :code:`isFinalized()` + * - :code:`localize` + - | Localizes the text passed in. + - :code:`localize(data('options'))` + * - :code:`metadata` + - | Returns a metadata field of this row + - :code:`metadata(‘_group_read_only’)` + * - :code:`not` + - | Negates the argument passed in. + - :code:`not(selected(data('examples'), 'label_features'))` + * - :code:`now` + - | Returns the current date + - :code:`now().getDay()` + * - :code:`selected` + - | Returns true if the value selected from a :tc:`select` + | prompt is equal to the second argument passed + | into the function. + - :code:`selected(data('visited_continents'), 'NorthAmerica')` + +And, additionally, the *opendatakit* object is also available for use in calculates expressions. + +.. warning:: + + The *opendatakit* object contains many useful functions but these should be considered internal methods subject to change. When upgrading, be sure to confirm that the methods you use have not disappeared! diff --git a/odk2-src/xlsx-converter-using.rst b/odk2-src/xlsx-converter-using.rst new file mode 100644 index 000000000..206da6f2c --- /dev/null +++ b/odk2-src/xlsx-converter-using.rst @@ -0,0 +1,573 @@ +.. spelling:: + getHashString + getCurrentInstanceId + tienes + Cuántos + años + Cuál + es + su + nombre + newRowInitialElementKeyToValueMap + happyBirthday + isBirthdayToday + getTime + daysOld + num + +Using ODK XLSX Converter +============================= + +.. _xlsx-using: + +ODK Survey offers a rich set of features that can be seamlessly integrated into a custom form. A lot of the functionality can be implemented solely within an Excel workbook. This guide is designed to help you take advantage of this via a guided tour of example tasks. + +.. contents:: :local: + +.. tip:: + + For a full reference to all the functionality available, see the :doc:`xlsx-converter-reference`. + +.. _xlsx-using-create-load-survey: + +Creating and Loading a Form into ODK Survey +--------------------------------------------- + +Below are the steps to create a new form from the *exampleForm*: + + 1. Within the Application Designer's folder, create the following directory structure :file:`app/config/tables/your_table_id/forms/your_table_id/` + 2. Copy the :file:`exampleForm.xlsx` from :file:`app/config/tables/exampleForm/forms/exampleForm/` into this new directory. + 3. Rename the XLSX file to :file:`your_table_id.xlsx` + 4. Edit the XLSX file and on the **settings** worksheet, change the value for *table_id* to *your_table_id*. Then update the display title for the survey and the form version. Save the changes. + 5. If you have not already, run :program:`grunt` to launch the :program:`Chrome` browser and open the Application Designer home page. + 6. Navigate to the :guilabel:`XLSX Converter` tab, choose this file to convert it. Once converted, choose :guilabel:`Save to File System` and click :guilabel:`OK` on the 3 pop-ups that alert you to the saving of 3 files to the file system. The three files that are saved are: + + - :file:`app/config/tables/your_table_id/definition.csv` -- defines the user-defined columns in your table + - :file:`app/config/tables/your_table_id/properties.csv` -- defines the appearance and available detail and list view HTML files for the table + - :file:`app/config/tables/your_table_id/forms/your_table_id/formDef.json` -- defines the ODK Survey form defined by the XLSX file + + 7. The first two files are written only if the form id matches the table id. That form and the XLSX file define the data table. + 8. Repeat the edit, conversion, and save steps to update the columns in your table and your survey form. + 9. Connect your device to your computer with a USB cable. + 10. In a separate :program:`command` window, navigate to the Application Designer directory and type: + + .. code-block:: console + + $ grunt adbpush + + to push the contents of the :file:`app/config` directory to your device. + + 11. Start ODK Survey. The form should now be available in ODK Survey. + +.. _xlsx-using-create-simple-survey: + +Creating a Simple Survey Form +----------------------------------- + +Typing the following in the **survey** worksheet of a workbook with an appropriate **settings** worksheet will result in a simple survey. + +.. csv-table:: Creating a Simple Survey Example Form + :header: "clause", "Condition", "type", "name", "display.prompt.text" + + ,,"integer", "person_age", "How old are you?" + "if ", "data('person_age') >= 18", + "begin screen", + ,,"text", "pizza_type", "What is your favorite kind of pizza?" + ,,"integer", "num_slices", "How many slices would you like?" + "end screen", + "else", + ,,"note",, "You are too young to be eating pizza" + "end if", + +The first row contains an empty clause and an empty condition column. Therefore, the :th:`display.prompt.text` will be shown on the screen, and the resulting :tc:`integer` answer will be stored in the variable :tc:`person_age`. + +On the next line there is an :tc:`if` in the :th:`clause` column and :tc:`data('person_age') >= 18` in the condition column. If the answer stored in the variable :tc:`person_age` is greater than or equal to 18, the following commands should be done until either an :tc:`else` or an :tc:`end if` tag is reached. Notice the other three columns are left blank. + +In the next row, there is a :tc:`begin screen` tag in the :th:`clause` column. The remaining four columns are left blank. Until an :tc:`end screen` tag is reached in the :th:`clause` column, all the following questions will be displayed on one screen. In this case, the user will be asked to input their favorite type of pizza and how many slices they would like on the same page, assuming they are 18 or older. + +In the next row, there is an :tc:`else` tag. Until :tc:`end if` is reached, anyone who did not satisfy the requirement for the :tc:`if` tag will be asked the following questions. In this case, a :tc:`note` to the user that they are too young to be eating pizza will be displayed. + +.. note:: + An important thing to remember when using the clause column is when to open and close new tags. The general rule is that the most recently opened grouping is the first to be closed. + +.. _xlsx-using-multi-choice: + +Adding Multiple Choice Questions +------------------------------------- + +There are three types of multiple choice questions supported by ODK Survey: + + - :tc:`select_one` + - :tc:`select_one_with_other` + - :tc:`select_multiple` + +Multiple choice questions use the :th:`values_list` column in the **survey** worksheet. The :th:`values_list` column is what links a multiple choice question to its answer set contained on the **choices** worksheet. + +The pizza survey example used earlier can be improved upon with multiple choice options.The resulting **survey** worksheet would look like this: + +.. csv-table:: Adding Multiple Choice Questions Example Survey Worksheet + :header: "clause", "Condition ", "type", "values_list ", "name", "display.prompt.text" + + ,,"select_one", "yes_no", "person_age", "Are you 18 or older?" + "if", "selected(data('person_age'), 'yes')", + "begin screen", + ,,"select_multiple", "topping_list", "pizza_type", "What are your favorite kind of pizza toppings (select up to 3)?" + ,,"integer", "num_slice", "How many slices would you like?" + "end screen", + "else", + ,,"note", "You are too young to be eating pizza" + "end if", + +and the corresponding **choices** worksheet would look like this: + +.. csv-table:: Adding Multiple Choice Questions Example Choices Worksheet + :header: "choice_list_name", "data_value", "display.title.text" + + "yes_no", "ye", "Yes" + "yes_no", "no", "No" + "topping_list", "pepperoni", "Pepperoni" + "topping_list", "olives", "Black Olives" + "topping_list", "onions", "Onions" + "topping_list", "mushroom", "Mushrooms" + "topping_list", "pepper", "Green Peppers" + "topping_list", "bacon", "Canadian Bacon" + "topping_list", "pineapple", "Pineapple" + +Now, instead of typing their age, the user simply selects whether they are older than 18 or not. Furthermore, instead of entering the type of pizza they like, they can select from a list of toppings. + +.. tip:: + + Because you determine whether a question is :tc:`select_one` or :tc:`select_multiple` from the **survey** worksheet, the same choice set on the **choices** worksheet can be used for both :tc:`select_one` and :tc:`select_multiple` questions. + +.. _xlsx-using-custom-section: + +Using Custom Section Worksheets +--------------------------------------- + +Custom section worksheets can be added to a workbook to make the control flow of a survey more readable. We could move all the previous questions about pizza to a new worksheet and name it **Pizza**. Our **survey** worksheet would then look like this: + +.. csv-table:: Custom Section Worksheets Example + :header: "clause", "condition ", "type", "values_list ", "name", "display.prompt.text" + + "do", "section Pizza", + +.. tip:: + When splitting a survey into different sections, it is wise to put a :tc:`note` before each section call with :th:`display.prompt.text` set to read *Section *. This is because a :tc:`do` :tc:`section ` call is transparent to the user. Unless the form designer explicitly adds a :tc:`note`, the user will not realize that they entered a section. + + Also, after leaving a section, if the user swipes back, the survey will go to the row before the :tc:`do` :tc:`section` call. If the user then swipes forward at this point, the survey will go to the beginning of the section they just completed. It is often beneficial to the user to put a :tc:`note` before entering a section and before leaving a section. + +.. _xlsx-using-calculations: + +Using Calculations +--------------------- + +The **calculates** worksheet is an optional worksheet. It consists of two columns: + + - :th:`calculation_name`: Each row of the **calculates** page represents a function that can be used elsewhere in the workbook by referencing the individual :th:`calculation_name`. + - :th:`calculation`: The calculation to be performed. + +.. note:: + + The :th:`calculation` column can store any valid JavaScript expression. + +.. tip:: + + There are also some built in functions for ODK Survey that can be used anywhere in the workbook. See the :ref:`Forumla Functions ` for more details. + +In general, calculations are referenced in the :th:`condition` column of **survey** worksheets. For example, suppose that on the **survey** page under the variable name *birthday* the user entered their birthday for a question of type :tc:`date`. The **calculates** worksheet might look like this: + +.. csv-table:: Calculates Worksheet Example + :header: "calculation_name", "calculation" + + "daysOld", "(now().getTime()-new Date(data('birthday')).getTime())/1000/60/60/24" + "isBirthdayToday", "calculates.daysOld()%365 == (now().getTime()/1000/60/60/24)%365" + +and one of the **survey** worksheets may look like this: + +.. csv-table:: Calculation Survey Worksheet Example + :header: "clause, "condition", "type", "name", "display.prompt.text" + + "if", "calculates.isBirthdayToday()", + ,,"note", "happyBirthday", "Happy Birthday!" + "end if", + +Notice that the <:th:`calculation_name`>s do not contain parentheses () at the end of them. However, when referencing them it is always in the format of :command:`calculates.()`. + +.. tip:: + + Variable names have scope for the entire workbook. + + +The **calculates** worksheet is handy because it adds readability to a workbook. Instead of having long, complicated JavaScript calculations in the **survey** worksheets, they can be consolidated in one, easy to reference location that allows for reusability. Also notice the consistent use of camelCase for variable naming across the different worksheets. + +.. _xlsx-using-queries: + +Using Queries +--------------------------------- + +The **queries** worksheet is an optional worksheet. + +For queries that get their data from external sources, the following columns should be used: + + - :th:`query_name` + - :th:`query_type` + - :th:`uri` + - :th:`callback` + +For :tc:`linked_table` queries, these columns should be used: + + - :th:`query_name` + - :th:`query_type` + - :th:`linked_table_id` + - :th:`linked_form_id` + - :th:`selection` + - :th:`selectionArgs` + - :th:`orderBy` + - :th:`auxillaryHash` + +Each row of the queries page represents a choice set that can be used by :tc:`select` prompt types in the workbook. In general, :th:`query_name` is referenced in the :th:`values_list` column of **survey** worksheets. For example, suppose that on the **survey** page under the variable name :tc:`region` the user is asked to select the region they are from. Then the user is asked to select which country they are from. The choices for the list of countries can be filtered based on the region the user selected. The **queries** worksheet might look like this: + +.. list-table:: Queries Worksheet Example + :header-rows: 1 + + * - query_name + - query_type + - uri + - callback + * - regions_csv + - csv + - "regions.csv" + - | _.chain(context).pluck('region').uniq().map(function(region){ + | return {data_value:region, display:{title: {text: region} } }; + | }).value() + * - countries.csv + - csv + - "regions.csv" + - | _.map(context, function(place){place.data_value = place.country; + | place.display = {title: {text:place.country} }; + | return place; + | }) + +The data for the queries is coming from the :file:`regions.csv` file that is located in the same directory as the :file:`formDef.json` and specified in the :th:`uri` column. Thus, the :th:`query_type` for both queries is :tc:`csv`. A snippet of the :file:`regions.csv` file looks like the following: + +.. csv-table:: regions.csv + :header: "region", "country" + + "Africa", "Algeria" + "Africa", "Angola" + "Africa", "Benin" + +Knowing the structure of the :file:`regions.csv` helps in understanding the callback function provided in the :th:`callback` column. The callback function maps the results from the :file:`regions.csv` file to the :th:`data_value` and the :th:`display.prompt.text` fields using JavaScript. The **survey** worksheets may look like this: + +.. csv-table:: Queries Survey Worksheet Example + :header: "clause", "condition ", "type", "values_list ", "name", "display.prompt.text ", "choice_filter" + + "begin screen", + ,,"select_one_dropdown", "regions_csv", "region", "Please select your region:", + ,,"select_one_dropdown", "countries_csv", "country", "Please select your country:", "choice_item.region === data('region')" + "end screen", + +The :th:`choice_filter` in this example ensures that the options for the :tc:`country` question will only be the countries from the previously selected region. Notice that :tc:`choice_item.region` specifies that any country with a corresponding region equal to the answer stored by the region question will be displayed. + +The **queries** worksheet is powerful because it allows more flexibility in terms of where data for the survey can reside. + +.. _xlsx-using-queries-linked-tables: + +Linked Tables +~~~~~~~~~~~~~~~~ + +:th:`linked_table` is the other use for the **queries** worksheet. :th:`linked_table` allows you to launch a subform that can edit a different data table. For example, if a survey is dealing with information about households, the user may want to ask questions about the general household but also questions about specific users. :th:`linked_table` can be used to launch subforms that ask questions about the specific household members. The **survey** worksheet may look like this: + +.. csv-table:: Linked Table Survey Worksheet Example + :header: "clause", "condition", "type", "values_list", "name", "display.prompt.text ", "choice_filter" + + ,,"text",, "house_id", "Input the unique household id:", + ,,"integer",, "num_members", "How many people live in this house?", + ,,"linked_table", "members",, "Add and enter information for the different household members", + ,,"select_one", "members", "household_head", "Who is the household head?", + +The **queries** worksheet would look like this: + +.. list-table:: Linked Table Query Worksheet Example + :header-rows: 1 + + * - query_name + - query_type + - linked_form_id + - linked_table_id + - selection + - selectionArgs + - newRowInitialElementKeyToValueMap + * - members + - linked_table + - members_info + - house_members + - house_id = ? + - [ opendatakit.getCurrentInstanceId() ] + - { house_id: opendatakit.getCurrentInstanceId() } + +First the user enters a :tc:`house id` for the house and answers an arbitrary question about its residents. This information is stored in the data table for general household information (specified on the **settings** worksheet under :th:`table_id`). Then the user reaches a :tc:`linked_table` prompt that uses the :th:`values_list` members. This is connected to the members query on the **queries** worksheet. It links to a different survey called :tc:`members_info` that edits a different data table. The selection criteria is that the :tc:`house_id` in the :tc:`house_members` data table matches the :tc:`instanceID` of this current household. + +Initially this list will be empty since no members have been added. The user can click on the :guilabel:`Create Instance` button to add new people for this household. The :tc:`house_id` will be set automatically for this new member via the :th:`newRowInitialElementKeyToValueMap` content, which specifies that the :tc:`house_id` field in the linked table should be initialized with the :tc:`instanceID` of the current household. + +.. note:: + + The selection criteria and its type (in this case, :tc:`house_id` and :tc:`text`) must be added to the model subset of the subform (members_info) in order for selection criteria to be persisted to the database and for the subform to be found by its parent form; the selection criteria cannot filter on session variables since those values are never persisted. + +When the user finishes the subform, the screen will return to the same linked_table prompt. At this point, the user can continue adding more users, edit an existing member's info, or go to a different screen. + +The :th:`values_list` for the :tc:`select_one` question prompt in the example above also uses the :tc:`members` query. Instead of being able to launch subforms to edit information about different members, the selection criteria is used to populate a multiple choice question. The answer to the multiple choice question is saved to the general :tc:`household` data table, not the :tc:`members` data table. + +.. _xlsx-using-internationalization: + +Internationalization +-------------------------- + +Survey offers the ability to display text in different languages. This requires usage of the **settings** worksheet to determine which language to use. However, for any language other than the default language, extra display columns need to be added. For example, if one of the non-default language options was Spanish (2-letter language code "es"), every worksheet with a :th:`display.prompt.text` column would also need a :th:`display.prompt.text.es` column. This is true for all columns that need an alternate language option. + +.. csv-table:: Internationalization framework_translations Worksheet Example + :header: "type", "name", "display.prompt.text", "display.prompt.text.es" + + "text", "user_name", "What is your name?", "¿Cuál es su nombre?" + "integer", "user_age", "How old are you?", "¿Cuántos años tienes?" + +The labels used in the buttons and prompts supplied by ODK Survey are defined in the **framework_translations** sheet of the :file:`framework.xlsx` file under :file:`config/assets/framework/forms/framework.xlsx` Simply add your language code and translations to this sheet of this XLSX file and run :guilabel:`XLSXConverter` on it to enable support of your language across all of the built-in buttons and prompts within ODK Survey. + +.. _xlsx-using-advanced-branching: + +More Advanced Branching +---------------------------- + +ODK Survey supports situations where the user needs to be in control of which survey or section of a survey they are working on. To do this, the :th:`branch_label` column is used, as well as the **choices** worksheet. It also utilizes a new question type: :tc:`user_branch`. The following example combines aforementioned surveys and allows the user to decide whether they want to fill out the survey about pizza, or the survey about birthdays. + +A choice set needs to be added to the **choices** worksheet with the applicable branching options. The resulting **choices** worksheet would look like this: + +.. csv-table:: Branching Choices Worksheet Example + :header: "choice_list_name", "data_value", "display.title.text" + + "which_form", "pizza_form", "Order pizza?" + "which_form", "birthday_form", "Is it your birthday?" + +And the **survey** page would look like this: + +.. csv-table:: Branching Survey Worksheet Example + :header: "branch_label", "clause", "condition ", "type", "values_list ", "display.prompt.text" + + ,,,"user_branch", "which_form", "Choose a survey to fill out" + "pizza_form", + ,"do section pizza", + "birthday_form", + ,"do section birthday", + +The XLSX file would then have corresponding **section** worksheets called *pizza* and *birthday* that contain the survey examples documented earlier. + +.. _xlsx-using-custom-initial: + +Creating a Custom Initial Worksheet +-------------------------------------- + +When ODK Survey opens, it displays a list of the different forms available on the device. After the user has selected which type of form to work on, Survey launches the initial worksheet for that particular survey. So far the initial worksheet has not been discussed and if one is not explicitly included in the XLSX file, survey uses this default initial worksheet: + +.. list-table:: Custom Initial Worksheet Example + :header-rows: 1 + + * - clause + - Condition + - type + - display.prompt.text + * - if // start + - (opendatakit.getCurrentInstanceId() != null) + - + - + * - + - + - opening + - Edit form + * - do section survey + - + - + - + * - + - + - finalize + - Save form + * - else // start + - + - + - + * - + - + - instances + - Saved instances + * - end if // start + - + - + - + +This checks to see if an instance of the current form has been selected :command:`(opendatakit.getCurrentInstanceId() != null)`. If it has, it opens that form. If not, it displays the instances that the user can edit. This utilizes three new types: + + - :tc:`opening` + - :tc:`finalize` + - :tc:`instances` + +.. warning:: + + When creating a custom initial worksheet, it is very important to include a finalize type. After completing a survey, it is the finalize prompt that lets the user formally finish the survey so that the results can be used. + +.. _xlsx-using-validate: + +Using Validate +------------------------ + +When users start having more control over which questions they are asked, it can lead to problems if they bypass required prompts. The validate feature allows for the form creator to require form validation in custom places. By default, the form performs a validation during the :tc:`finalize` section of the survey. However, this type of operation can be performed at multiple points throughout the survey on specific questions using the prompt type :tc:`validate` and the column :th:`validation_tags`. + +The following example will collect information from a user in *section1* and *section2* and will prevent completion of *section3* if certain questions have invalid answers. + +The **survey** page would look like this: + +.. csv-table:: Validate Survey Worksheet Example + :header: "branch_label", "Clause", "type", "values_list ", "display.prompt.text" + + "welcome_screen", + ,,"user_branch", "which_branch", "Choose the section to enter" + ,"goto welcome_screen", + "branch1", + ,,"note",, "Selected Section 1" + ,"do section section1", + ,,"note",, "Returning from Section 1" + ,"goto welcome_screen", + "branch2", + ,,"note",, "Selected Section 2" + ,"do section section2", + ,,"note",, "Returning from Section 2" + ,"goto welcome_screen", + "branch3", + ,,"note",, "Selected Section 3" + ,"validate user_info", + ,"do section section3", + ,,"note",, "Returning from Section 3" + ,"goto welcome_screen", + +The **choices** worksheet would look like this: + +.. csv-table:: Validate Choices Worksheet Example + :header: "choice_list_name", "data_value", "display.title.text" + + "which_branch", "branch1", "Do Section 1" + "which_branch", "branch2", "Do Section 2" + "which_branch", "branch3", "Do Section 3" + +The **section1** worksheet would look like this: + +.. csv-table:: Validate Section1 Worksheet Example + :header: "type", "name", "display.prompt.text", "required", "validation_tags" + + "text", "user_name", "What is your name?", "TRUE", "user_info, finalize" + "integer", "user_age", "What is your age?", "TRUE", "user_info, finalize" + "note",, "Thank you for answering", + +The **section2** worksheet would look like this: + +.. csv-table:: Validate Section2 Worksheet Example + :header: "type", "name", "display.prompt.text", "required", "validation_tags" + + "text", "occupation", "What is your current occupation?", "TRUE", "user_info, finalize" + "integer", "user_age", "How long have you worked at your current job (in years)?", "TRUE", "finalize" + "note",, "Thank you for answering", + +If the user selects to do *section 3* on the welcome page, survey will jump to the :tc:`branch3` :th:`branch_label`. The first row says to validate :tc:`user_info`. Survey then checks that every question with the :th:`validation_tags` :tc:`user_info` has been answered satisfactorily. If the questions have been answered correctly, it will go on to the next line (do section *section3*). If not, it will force the user to answer the missing, tagged questions. + +The use of many different :th:`validation_tags` can allow users to update information in the survey as it becomes available and to restrict questions that depend on other information. In general, the validation feature can be used to give users more control over their work while still maintaining a level of order and restriction. + +.. warning:: + + Like the use of :tc:`sections` and :tc:`gotos`, :tc:`validate` has no user interface. In other words, when a user runs into a :tc:`validate` call, they will have no idea unless Survey finds something wrong with the form. Whenever using :tc:`sections`, :tc:`gotos`, or :tc:`validates`, if the form designer wants the user to be aware of what is happening, a note explicitly informing the user must be added. + +.. _xlsx-using-custom-prompts: + +Customizing Prompts +-------------------------- + +There are 3 ways to customize prompts: + + - Add additional columns to your XLSX Converter form definitions like :th:`inputAttributes` to tweak existing prompts. + - If that's too limiting, you can make a custom HTML template by setting the :th:`templatePath` column. Templates can include :code:`