getodk / web-forms

ODK Web Forms enables form filling and submission editing of ODK forms in a web browser. It's coming soon! ✨
https://getodk.org
Apache License 2.0
12 stars 9 forks source link
data-collection odk odk-xforms

ODK Web Forms

With ODK Web Forms, you can define forms with powerful logic using the spreadsheet-based XLSForm standard. Use our Vue-based frontend or build your own user experience around the engine!

[!IMPORTANT] ODK Web Forms is currently pre-release. We don't yet guarantee that its interfaces are stable and it is missing many features that are available in XLSForm form definitions.

https://github.com/getodk/web-forms/assets/447837/9b25e1bc-d209-462c-8e9e-3259bd8c5aa6

Packages

[!NOTE] Comprehensive usage and development instructions are coming soon! For now, you can see each package's README. Please be sure to run yarn commands from the project root.

Project status

ODK Web Forms is developed by the ODK team.

The ODK Web Forms frontend is designed to provide a similar user experience to the ODK Collect mobile data collection app. Our short-term goal is to use it to replace Enketo in the ODK Central form server for web-based form filling and editing existing submissions.

Longer-term, we hope to use the engine to replace JavaRosa to power ODK Collect, so that we can maintain a single correct and extensible form engine.

Here are some of our high-level priorities to get to a production-ready state:

Feature matrix

This section is auto generated. Please update feature-matrix.json and then run yarn feature-matrix from the repository's root to update it.

##### $\texttt{Question types (basic functionality)\hspace{5mm}\color{green}██\color{LightGray}█████████████ \color{initial} 18\\%}$
| Feature | Progress | | -------------------------- | :------: | | text | ✅ | | integer | | | decimal | | | note | ✅ | | select_one | ✅ | | select_multiple | ✅ | | repeat | ✅ | | group | ✅ | | geopoint | | | geotrace | | | geoshape | | | start-geopoint | | | range | | | image | | | barcode | | | audio | | | background-audio | | | video | | | file | | | date | | | time | | | datetime | | | rank | | | csv-external | | | acknowledge | 🚧 | | start | | | end | | | today | | | deviceid | | | username | | | phonenumber | | | email | | | audit | |
##### $\texttt{Appearances\hspace{41mm}\color{green}███\color{LightGray}████████████ \color{initial} 24\\%}$
| Feature | Progress | | -------------------------- | :------: | | numbers | | | multiline | | | url | | | ex: | | | thousands-sep | | | bearing | | | vertical | | | no-ticks | | | picker | | | rating | | | new | | | new-front | | | draw | | | annotate | | | signature | | | no-calendar | | | month-year | | | year | | | ethiopian | | | coptic | | | islamic | | | bikram-sambat | | | myanmar | | | persian | | | placement-map | | | maps | | | hide-input | | | minimal | ✅ | | search / autocomplete | ✅ | | quick | | | columns-pack | | | columns | ✅ | | columns-n | ✅ | | no-buttons | ✅ | | image-map | | | likert | ✅ | | map | | | field-list | ✅ | | label | ✅ | | list-nolabel | ✅ | | list | ✅ |
##### $\texttt{Parameters\hspace{43mm}\color{green}███\color{LightGray}████████████ \color{initial} 22\\%}$
| Feature | Progress | | -------------------------------------------------------------------------------------------------------------------------------- | :------: | | randomize | ✅ | | seed | ✅ | | value | | | label | | | geopoint capture-accuracy, warning-accur
acy, allow-mock-accuracy | | | range start, end, step | | | image max-pixels | | | audio quality | | | Audit: location-priority, location-min-i
nterval, location-max-age, track-changes
, track-changes-reasons, identify-user | |
##### $\texttt{Form Logic\hspace{43mm}\color{green}████████████\color{LightGray}███ \color{initial} 83\\%}$
| Feature | Progress | | -------------------------- | :------: | | calculate | ✅ | | relevant | ✅ | | required | ✅ | | required message | ✅ | | custom constraint | ✅ | | constraint message | ✅ | | read only | ✅ | | trigger | | | choice filter | ✅ | | default | ✅ | | query parameter | | | repeat_count | ✅ |
##### $\texttt{Descriptions and Annotations\hspace{14mm}\color{green}███\color{LightGray}████████████ \color{initial} 20\\%}$
| Feature | Progress | | ---------------------------------------------- | :------: | | label | ✅ | | hint | ✅ | | guidance hint | | | Translations | ✅ | | Translations with field/question value | | | Markdown | | | Inline HTML | | | Form attachments | | | image | | | big-image | | | audio | | | video | | | secondary instance (external choice file
) | | | secondary instance (last saved) | | | autoplay | |
##### $\texttt{Theme and Layouts\hspace{30mm}\color{green}█\color{LightGray}██████████████ \color{initial} 9\\%}$
| Feature | Progress | | -------------------------- | :------: | | grid | | | pages | | | print | | | logo | | | theme color | | | Submissions | | | preview | ✅ | | send | | | view | | | edit | | | attachments | |
##### $\texttt{Offline capabilities\hspace{31mm}\color{green}█\color{LightGray}██████████████ \color{initial} 0\\%}$
| Feature | Progress | | ---------------------------- | :------: | | List of projects & forms | | | local persistence (single) | | | save as draft | | | offline entities | | | MBtiles / offline map layers | |
##### $\texttt{XPath\hspace{51mm}\color{green}██████████████\color{LightGray}█ \color{initial} 95\\%}$
| Feature | Progress | | --------------------------------------------------------------------------------------------------------------- | :------: | | operators | ✅ | | predicates | ✅ | | axes | ✅ | | string(\* arg) | ✅ | | concat(string arg*\|node-set arg*) | ✅ | | join(string separator, node-set nodes\*) | ✅ | | substr(string value, number start, numbe
r end?) | ✅ | | substring-before(string, string) | ✅ | | substring-after(string, string) | ✅ | | translate(string, string, string) | ✅ | | string-length(string arg) | ✅ | | normalize-space(string arg?) | ✅ | | contains(string haystack, string needle) | ✅ | | starts-with(string haystack, string need
le) | ✅ | | ends-with(string haystack, string needle
) | ✅ | | uuid(number?) | ✅ | | digest(string src, string algorithm, str
ing encoding?) | ✅ | | pulldata(string instance_id, string desi
red_element, string query_element, strin
g query) | | | if(boolean condition, _ then, _ else) | ✅ | | coalesce(string arg1, string arg2) | ✅ | | once(string calc) | ✅ | | true() | ✅ | | false() | ✅ | | boolean(\* arg) | ✅ | | boolean-from-string(string arg) | ✅ | | not(boolean arg) | ✅ | | regex(string value, string expression) | ✅ | | checklist(number min, number max, string
v\*) | ✅ | | weighted-checklist(number min, number ma
x, [string v, string w]\*) | ✅ | | number(\* arg) | ✅ | | random() | ✅ | | int(number arg) | ✅ | | sum(node-set arg) | ✅ | | max(node-set arg\*) | ✅ | | min(node-set arg\*) | ✅ | | round(number arg, number decimals?) | ✅ | | pow(number value, number power) | ✅ | | log(number arg) | ✅ | | log10(number arg) | ✅ | | abs(number arg) | ✅ | | sin(number arg) | ✅ | | cos(number arg) | ✅ | | tan(number arg) | ✅ | | asin(number arg) | ✅ | | acos(number arg) | ✅ | | atan(number arg) | ✅ | | atan2(number arg, number arg) | ✅ | | sqrt(number arg) | ✅ | | exp(number arg) | ✅ | | exp10(number arg) | ✅ | | pi() | ✅ | | count(node-set arg) | ✅ | | count-non-empty(node-set arg) | ✅ | | position(node arg?) | ✅ | | instance(string id) | ✅ | | current() | ✅ | | randomize(node-set arg, number seed) | ✅ | | today() | ✅ | | now() | ✅ | | format-date(date value, string format) | ✅ | | format-date-time(dateTime value, string
format) | ✅ | | date(\* value) | ✅ | | decimal-date-time(dateTime value) | ✅ | | decimal-time(time value) | ✅ | | selected(string list, string value) | ✅ | | selected-at(string list, number index) | ✅ | | count-selected(node node) | ✅ | | jr:choice-name(node node, string value) | | | jr:itext(string id) | ✅ | | indexed-repeat(node-set arg, node-set re
peat1, number index1, [node-set repeatN,
number indexN]{0,2}) | ✅ | | area(node-set ns\|geoshape gs) | ✅ | | distance(node-set ns\|geoshape gs\|geotr
ace gt\|(geopoint\|string) arg\*) | ✅ | | base64-decode(base64Binary input) | |

We welcome discussion about the project on the ODK forum! The forum is generally the preferred place for questions, issue reports, and feature requests unless you have information to add to an existing issue.

Q&A

Why not evolve Enketo?

Enketo is critical infrastructure for a number of organizations and used in many different ways. As its maintainer, we found deeper changes to be challenging because they often led to regressions, many times in functionality that we don't use ourselves. We hope that the narrower scope of ODK Web Forms (in particular, no transformation step and no standalone service) will allow us to iterate quickly and align more closely with Collect while allowing organizations that have built infrastructure around Enketo to continue using it as they prefer.

What will happen to Enketo?

We are committed to continuing its maintenance through the end of 2024. We are actively seeking new maintainers and will offer some transitional support.

Why not build a web frontend around JavaRosa?

After many years of maintaining JavaRosa and a few maintaining Enketo, we have learned a lot about how we'd like to structure an ODK XForms engine to isolate concerns and reduce the risk of regressions. We believe a fresh start will give us an opportunity to build strong patterns that will allow for a faster development pace with fewer bugs and performance issues.

Why use web technologies?

There exist more and more ways to run code written with web technologies in different environments and web technologies continue to increase in popularity. We believe this choice will give us a lot of flexibility in how these packages can be used.

Why have a strong separation between the form engine and its frontend?

We aspire to use the engine to drive other kinds of frontends such as test runners and eventually mobile applications. Additionally, our experience maintaining JavaRosa and Enketo suggests that blurring the engine/frontend line can be the cause of many surprising bugs that are hard to troubleshoot.

Why Vue and PrimeVue?

Vue powers Central frontend where it has served us well. For Web Forms, we've selected to use a component library to help us build a consistent, accessible, and user-friendly experience in minimal time. We chose PrimeVue for its development pace, approach to extensibility, and dedication to backwards compatibility.

Why not use browsers' XPath parser and evaluator (e.g. Enketo's wrapper around them)?

We want to be able to use this code in browsers but also in backends and eventually wrapped by mobile applications. Taking control of XPath evaluation gives us more portability and also has the advantage of giving us the opportunity to make targeted performance improvements.

Why not build an engine that operates directly on XLSForms?

While XLSForm is a powerful form authoring format, it doesn't have clearly defined engine semantics or a formal specification. An XLSForm engine would have to refer to the underlying ODK XForms specification for much of its behavior and represent the form in a way that's appropriate for XPath querying.

When are you going to add XYZ?

If there's specific functionality you're eager to see, please let us know on the ODK forum. You can see what we're currently prioritizing on the project board and the ODK roadmap.

The default theme looks very... gray. Will I be able to customize it?

We will be adding color and more styling soon. We intend to expose a way to do basic theming as well as a no-styles option to let advanced users define their own styling.

Related projects

In the ODK ecosystem

Outside the ODK ecosystem