Skip to content

Advanced rules

Beyond per-field settings, a survey type can carry three kinds of record-level rule. They are edited in the Advanced rules panel at the bottom of the survey builder and validated when you save, so a malformed rule is never stored. Each section has a point-and-click editor by default, and an Edit as JSON toggle for the exact shape or for the rare edge case the visual editor does not cover.

A check is a rule across the whole record: a comparison between two values, where each value is a field, a number, or an aggregate over a repeat group (a sum of a sub-field, or a count of entries). The check holds when the comparison is true; otherwise the record gets your message, as an error or a warning.

In the visual editor each check reads left to right: pick a value, a comparison (=, not equal, at most, less than, at least, greater than), and another value, then type a plain-language message and choose Error (blocks save) or Warning (flags only). The field dropdowns come from the survey’s own fields, and “compare to another field” is a peer of “compare to a number.”

Use checks for things a single field cannot express:

  • A repeat group must total a fixed number (for example, a set of counts must sum to 20).
  • One field must be at or below another (a start at or before an end).
  • A count must be within bounds.

The same rule, shown by the Edit as JSON toggle:

[
{
"key": "counts_total",
"message": { "en": "The counts must total 20." },
"left": { "sum": { "repeat": "rows", "field": "count" } },
"op": "eq",
"right": { "const": 20 }
}
]

A check whose values cannot be resolved yet (a field is still empty) is skipped, so incomplete data never fails. The JSON view also exposes a when condition (evaluate a check only in certain cases) and a field (to attach the message to a field).

Besides comparing two values, a check can fail when a condition matches, which is the way to express a “these two things must not both be true” rule that a comparison cannot. For example, a specimen may keep an aging structure sample or carry a tag, but not both.

This “prohibition” form is written with the Edit as JSON toggle (the visual editor covers comparisons). It looks like this:

[
{
"key": "structure_or_tag",
"message": { "en": "A specimen keeps its structure sample or carries a tag, never both." },
"failWhen": {
"all": [
{ "field": "structure_no", "op": "not_empty" },
{ "field": "tag_no", "op": "not_empty" }
]
}
}
]

The check fails when every part of failWhen holds. Set severity to error or warning like any check.

When a check sums or counts over a repeat group, the sub-field it names is checked when you save, so a typo in the summed field is caught then rather than becoming a rule that silently never fires.

A computed field is filled by the engine from other fields instead of being entered. It is read-only on the form and stored with the record, so reports, export, and checks all see the value.

In the visual editor you pick a number field to make computed, then build its value by choosing a function: a field, a number, a total or count over a list, a duration between two time fields, or a formula (a +−×÷ b, where each side is itself an expression). The dropdowns are filtered to compatible fields, so a duration only offers date or time fields and a total only offers numeric sub-fields.

The same expression as JSON (the key is an existing field’s key; that field becomes computed):

{ "soak_hours": { "duration": { "from": "set_at", "to": "haul_at", "unit": "hours" } } }

A computed value updates live as you fill the fields it depends on.

A sub-sampling plan decides which records to select for extra sampling, at a rate that can vary by group (area, season, species, size class). This is something most field tools cannot do, because it needs a running count across records, which CensusIO keeps even offline.

In the visual editor you add a plan, give it a name, and pick a Method: every Nth record (systematic), a random probability per record (random sample), a quota per group (fixed count), all records (every unit), or take-what-you-can (opportunistic). The rate field changes to match the method (a percent, a 1-in-N interval, or a per-group quota). A Seed makes the selection identical across devices. Vary the rate by adds group dimensions from the survey’s fields (numeric ones can be split into bins, such as 5 cm length classes), and a rate overrides table sets a different rate for specific groups. You can also turn on specimen numbering (prefix, start, end, padding) and restart counting at each parent record.

The same plan as JSON:

[
{
"key": "subsample",
"label": { "en": "Sub-sample" },
"strategy": "systematic",
"rate": 3,
"seed": "subsample-2025",
"stratum": { "keys": [{ "field": "species" }] }
}
]

During entry the form shows the live decision (selected, a selection coming up, or not selected) and a specimen number when one is configured. The decision is stored with the record so sampling rates can be applied in analysis.