Model specification
See Authoring model definitions to
create your first model.
Field types and constraints
Every field declares a type:
string: text.
email: text matching
^[^\s@]+@[^\s@]+\.[^\s@]+$; mailbox existence is not
checked.
integer: a finite number without a fractional
part.
number: a finite number.
boolean: true or false.
latitude: a finite number in decimal degrees, from -90
to 90 inclusive.
longitude: a finite number in decimal degrees, from
-180 to 180 inclusive.
date: a valid date string in YYYY-MM-DD
format.
datetime: a valid UTC datetime string in
YYYY-MM-DDTHH:MM:SSZ format.
Coordinate types already include their geographic bounds. Missing
values are governed by requiredness.
Optional constraints are:
mandatory: requires a filled value; omitted means
false.
required_field: requires a value when the named field
on the same sheet is filled.
enum: a nonempty list of distinct, non-null values
matching the type, bounds and pattern.
minimum, maximum: inclusive numeric
bounds; integer bounds must be whole numbers.
maximum_field: the value must not exceed the named
numeric field on the same sheet; compare only valid, present
values.
pattern: a PCRE regular expression for string, email,
date or datetime fields.
sequence: consecutive integer numbering in worksheet
order, grouped by same-sheet fields. Declare
sequence: {start: 1, group_by: [trip_id, catch_id]} on an
integer or string table field.
unique: requires distinct values within a repeated
table field.
reference: a catalogue, table and named lookup.
Enums are constraints, not a separate type. Quote numeric-looking
string codes: enum: ["01", "02"].
For conditional fields, required_field: length makes the
unit required when length is filled, including zero.
In tables, this applies within the same row. A field cannot depend on
itself; mutual dependencies are allowed.
The condition still permits a unit when length is empty.
Reference bindings default to error severity. Use
severity: warning for a warning.
Declare tables under freely named catalogues:
references:
people: [contacts]
Bind the field to a table and lookup:
contact_email: {location: C4, type: email, reference: {catalogue: people, table: contacts, lookup: email, severity: warning}}
Catalogue names have no built-in meaning. Matching settings and
reference data belong outside the model.
See ?form_fields for the effective field properties
returned by accessors.
Workbook layouts
sheets is an ordered mapping of worksheet names. Every
declared sheet is required; names are unique ignoring case.
Each sheet has a layout, an optional
primary_key, and fields, keyed by local field
name.
kind: cells: requires column_count. Each
field needs a unique uppercase A1 location, such as
E3.
kind: table: requires start_row. Field
order defines columns; omit field location and layout
column_count.
Table column counts are derived from the fields. An explicit
column_count must match that count.
Rows before start_row are preamble.
An optional row_count declares the exact physical row
count. Fixed locations must fit declared dimensions.
Coordinates are one-based. An empty sheet can declare
fields: {}; an empty table derives a column count of
zero.
Primary keys
A table can declare one field or an ordered combination of fields as
its primary key:
primary_key: [station, observation_id]
Every component must be filled; the combination identifies one row
within that sheet of a submitted form.
Individual components may repeat in a composite key. Field-level
unique still means individual uniqueness.
Use local field names. Keys must be nonempty, contain no repeated
fields, and reference declared table fields.
Omit primary_key when no key is defined. Fixed-cell
sheets cannot declare a primary key.
form_sheets() and form_definition() retain
the declaration without changing field constraints.
This package checks key definitions. Checking submitted key values
belongs to the validation package.
Optional composite keys and row completeness
Table sheets can declare these independent constraints:
unique_key: [station, category]
at_least_one: {fields: [category, count], severity: warning}
unique_key checks uniqueness only when every component
is supplied and correctly typed. It does not make its fields mandatory.
Every occurrence of a duplicate complete key is reported as an error. A
sheet may declare both a primary key and a unique key; they are checked
independently. Parent relationships continue to reference a parent’s
primary key.
at_least_one requires a supplied value in at least one
selected field on each populated row. A row is populated when any
declared field has a supplied value after preparation. Entirely empty
rows are skipped. Zero and false count as supplied values. Both
constraints use the validation engine’s configured missing-value
policy.
Both field lists must be nonempty, contain no duplicates, and name
declared fields in the same table. at_least_one accepts
error, warning, or info; omitted
severity defaults to error.
YAML conventions
Use one UTF-8 document. Tags, anchors, aliases, merges, directives
and document markers are unsupported.
Mapping keys must be unique strings. Unknown properties are
errors.
Quote versions and codes, especially leading-zero codes such as
"01".
Use true and false for booleans. Quote text
codes such as "yes", "no", "on"
and "off".
Empty YAML values, null and ~ mean null;
"" means an empty string. Omit unused optional
properties.
Identifiers start with an ASCII letter, followed by letters, digits,
dots, underscores or hyphens.
Full field IDs combine the sheet and field names:
<sheet>.<field>.
Write field properties in this order: mandatory when
true, location for cells, type,
required_field, then constraints.
schema_version: "1.0.0" selects the package contract.
form.version independently identifies your model
version.
Change form.version when the model meaning changes. New
schema properties require a supported contract version.
Definition checks
Load a file with read_form_model() to check its
structure and consistency.
Use check_form_model() on a definition list to get
diagnostics with source, property path and message.
Consistency checks run only after structure passes. Submitted-data
validation belongs to form.validation.