Skip to content

Form Instrument

Structure

An Open Data Capture form instrument (i.e., the object that is the default export from the index source file) consists of several properties that together define the data and display model. By convention, these properties should be sorted in the order in which they are documented below (e.g., kind should be the first property, followed by language).

  • kind
    • The discriminator key for the type of instrument (e.g., form). This is primarily used to determine the structure of the content.
  • language
    • The language(s) in which the instrument is written. If the instrument is available in only one language, this should be a string (e.g., 'en'). On the other hand, if the instrument is available in multiple languages, this should be an array of languages (e.g., ['en', 'fr']).
  • internal
    • Information about the instrument that is used internally to generate a unique identifier. As such, the combination of name and edition should not conflict with any existing instruments in the database.
  • internal.name
    • The full English name of the instrument in screaming snake case (e.g., 'HAPPINESS_QUESTIONNAIRE').
  • internal.edition
    • A positive integer indicating the internal version of the implementation of this instrument. For a given instrument implementation, this should initially be set to 1 and incremented on each subsequent iteration. For example, suppose the “Happiness Questionnaire Version 1” is initially implemented (as edition 1) in the platform. However, edition 1 has a poor validation schema, so edition 2 is an internal revision to fix the validation schema. If “Happiness Questionnaire Version 2” is published, it can then be implemented in the platform as edition 1.
  • tags
    • A list of strings that users can use to filter and/or find instruments.
  • details
    • User-facing metadata about the instrument (i.e., the information shown for the initial overview of an instrument).
  • details.title
    • The title of the instrument should be the name of the instrument in title case (if applicable in the language). In most cases, this can be taken verbatim from a paper version of the instrument (e.g., 'Happiness Questionnaire').
    • If a common short form of the name exists, it should be displayed in brackets after the full name (e.g., Happiness Questionnaire (HQ)). This short name should be included for all languages. If a widely-known translated common short form is available it can be used (e.g., 'Questionnaire sur le bonheur (QB)'), otherwise default to English (e.g., 'Questionnaire sur le bonheur (HQ)').
  • details.description
    • A brief description of the instrument, such as the purpose and history of the instrument
  • content
    • The form items (the content object) defines the content shown on the “content” page when completing an instrument (i.e., the fields used to uptake data).
  • content.kind
    • Discriminator property for the type of field. The possible values depends on the type of data for this field.
  • content.variant
    • The display structure for the field. The possible values depends on the field kind.
  • measures
    • These are the outcomes of the instrument that are of relevance to users. In some cases, this may simply be a reference to fields in the instrument, or a computed value.
    • Defines how raw data is displayed on the ODC frontend, such as in tables and graphs.
    • Determines the summary page.
    • Also included in data export.
  • validationSchema
    • This defines the data model for the form, and corresponds one-to-one with the data structure that is ultimately stored in the database. As such, this is the most important part of the form definition, and should be defined before any fields.

Always consider using `group`ing to structure sections

Best Practices

  1. Name the directory and files and name in a long-form way to indicate what it is, prefix it with the PROJECT NAME only if the form is unique to the project.
  2. Name the output variables for the forms appropriately given the question and answer being provided.
    • Remember that they will be prefixed with the form name when eventually dumped
  3. Consider the appropriate encoding for the data
    • Is there a clear order of the values? If so, consider doing an ordered enum.
  4. Test your inputs against the validation schema
    • Make sure your rules are correct given the paper form
    • Please familiarize yourself with the possible validation rules here
    • Be careful to write the most restrictive validation schema possible
      • Example: likert-scale data should be .int()
      • Remember the API data-entry is independent of the form, so the validation schema must properly restrict API entry as well
  5. Please make sure to format your code whenever possible, as this can prevent annoying issues with missing brackets. In the playground, you can do this with (Alt + F)
  6. If you find yourself repeating code frequently, consider extracting the logic into shared variables. The guiding principle of this should be to maximize readability.
    • For example, if your data schema is z.number().int().min(0).max(7) for every field, consider moving it into a variable, as this can reduce the cognitive load of programmers reading your form
    • On the other hand, if this is the schema for only two or three variables, creating a variable would probably increase cognitive load, rather than reducing it.
    • The same applies for computed measures (i.e., define a function at the top of your file instead of copy-pasting it to every field).
  7. Review common styling conventions
  8. Use the playground to create instruments
    • It is updated regularly to use the latest runtime
    • It is setup with formatting, linting, and type checking by default

Example

import { defineInstrument } from '/runtime/v1/@opendatacapture/runtime-core';
import { z } from '/runtime/v1/zod@3.23.x';
export default defineInstrument({
kind: 'FORM',
language: ['en', 'fr'],
internal: {
name: 'HAPPINESS_QUESTIONNAIRE',
edition: 1
},
tags: {
en: ['Well-Being'],
fr: ['Bien-être']
},
details: {
description: {
en: 'The Happiness Questionnaire is a questionnaire about happiness.',
fr: 'Le questionnaire sur le bonheur est un questionnaire sur le bonheur.'
},
estimatedDuration: 1,
instructions: {
en: ['Please answer the questions based on your current feelings.'],
fr: ['Veuillez répondre àux questions en fonction de vos sentiments actuels.']
},
license: 'Apache-2.0',
title: {
en: 'Happiness Questionnaire',
fr: 'Questionnaire sur le bonheur'
}
},
content: {
overallHappiness: {
description: {
en: 'Please select a number from 1 to 10 (inclusive), where 1 is very dissatisfied and 10 is very satisfied.',
fr: 'Veuillez choisir un chiffre de 1 à 10 (inclus), où 1 correspond à très insatisfait et 10 à très satisfait.'
},
kind: 'number',
label: {
en: 'How happy are you?',
fr: 'Quel est votre niveau de bonheur ?'
},
max: 10,
min: 1,
variant: 'slider'
}
},
measures: {
overallHappiness: {
kind: 'const',
ref: 'overallHappiness'
}
},
validationSchema: z.object({
overallHappiness: z.number().int().min(1).max(10)
})
});