Chap blocks¶
Every block the Chap pack contributes, with the config it takes and the output it
produces. The tables are the JSON Schemas each block publishes into the catalog -- the same ones
GET /blocks serves and the UI builds its step form from. Every block declares the chap
group and takes a connection naming a chap connection by its code.
Chap changes fast, so these blocks lean on the parts of its API that do not: a job-backed block
submits through its own endpoint and then follows /v1/jobs/{id}, deciding from the job's
status alone before it asks a result helper for anything. Cancelling a step sends
POST /v1/jobs/{id}/cancel. A step's config carries the request body's fields in snake_case; an
undeclared key is sent to Chap as written, in Chap's camelCase, and the output hands on Chap's
JSON as it comes rather than pinning its shape.
Following a job¶
GET /v1/jobs/{id} answers in one of two shapes, and both are read:
| Chap | Answer |
|---|---|
| Releases up to v2.5.1 | The bare status, a JSON string such as "SUCCESS". |
master since ef17a258 |
The whole job: status, and the reason a job failed under error. |
The status is a Celery state, read in any case:
| Status | The step |
|---|---|
PENDING, RECEIVED, STARTED, RETRY |
waits. |
SUCCESS |
reads the result helper and ends. |
FAILURE, REVOKED, REJECTED |
fails, with the end of the job's log from /v1/jobs/{id}/logs written into the run, and the job's error in the message when Chap gave one. |
| any other word | waits, as for a status a newer Chap adds, and the run logs it once per job as an unknown status. |
An answer with no status at all fails the step as chap.job.unreadable, a refusal that is not
retried.
A job is followed for follow_for, 6h unless the step says otherwise, counted from its
submission. A job still waiting after that fails the step as chap.job.not_settled, naming the job
and the last status it reported; the failure is transient, so the step's retry policy decides
whether the work is submitted again. A job that settles is read even when it settles late. A step
deadline shorter than follow_for ends the wait first, settled by the step's on_timeout.
The reading was checked against chap-core v2.5.1, the newest release, and master at d78b4c23; the
pack's page keeps that record.
Idempotent says whether re-running the block after an unclear failure is safe. No block in this pack runs code on the worker.
| Block | Kind | Summary |
|---|---|---|
chap.dataset.import |
operator | Import observations as a Chap dataset. |
chap.backtest.run |
operator | Run a Chap backtest on a stored dataset. |
chap.prediction.run |
operator | Run a Chap prediction setup. |
chap.prediction.read |
operator | Read a Chap prediction's quantiles. |
chap.models.list |
operator | List the models a Chap server has configured. |
chap.thresholds |
operator | Compute the endemic channel for a Chap dataset. |
chap.alerts.record |
operator | Record alerts in Chap's review queue. |
chap.alerts.reviewed |
sensor | Wait for a Chap prediction's alerts to be reviewed. |
chap.alerts.list |
operator | List Chap alerts by their review state. |
chap.dataset.import¶
Import observations as a Chap dataset.
Not idempotent. Polls every 30s unless the step says otherwise.
Sends the observations to POST /v1/analytics/make-dataset. Chap validates them synchronously
and answers with how many locations it took and every observation it rejected; the import
itself runs as a job, which the step follows at /v1/jobs/{id} and ends with the stored
dataset's id, read from /v1/jobs/{id}/database_result.
dry_run: true sends dryRun=true and ends at the validation, with no job. Under
on_rejected: fail the step validates with a dry run first and fails, as a refusal, when Chap
would reject anything, so nothing is imported; warn imports what Chap accepts and logs each
rejection. A validation answered with no list under rejected fails the step with
chap.dataset.not_validated, under dry_run and on_rejected: fail alike.
Observations arrive as rows Chap spells for itself in provided_data, as a wide Chap CSV in
csv, or both. The CSV is unfolded into one observation per feature cell; an empty or nan
cell is sent as a missing value. Any undeclared key -- dataSources, maskedRanges -- is sent to
Chap as written. Note that maskedRanges deletes the target values inside the ranges for good.
Config
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
connection |
string |
yes | The code of the chap connection naming the server. | |
provided_data |
object[] |
Observations as Chap spells them: {period, orgUnit, value, featureName}, one per row. |
||
csv |
string or null |
null |
A wide Chap CSV, the shape aggregate_to_chap_csv writes: time_period, location, and one column per feature. Each feature cell becomes one observation, sent beside provided_data. |
|
max_observations |
integer |
500000 |
The most observations csv may unfold to, columns times rows; a larger one fails the step and sends nothing. |
|
follow_for |
string (humane-duration) |
"6h" |
How long the step follows the Chap job before failing it; a shorter step deadline ends it sooner. |
|
name |
string |
yes | The name the dataset is stored under. | |
geojson |
object |
yes | The org units as a GeoJSON FeatureCollection, each feature's id the location its observations name. |
|
data_to_be_fetched |
object[] |
Features Chap fetches itself, as Chap spells them: {featureName, dataSourceName}. |
||
dry_run |
boolean |
false |
Whether Chap only validates the observations and reports the rejections, importing nothing. | |
on_rejected |
"fail" or "warn" |
"warn" |
What a rejected observation does: warn imports the rest and logs the rejections, fail imports nothing and fails the step. |
Output
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
dataset_id |
integer or null |
null |
The stored dataset's id, which a backtest names; null for a dry run. | |
job_id |
string or null |
null |
The import job Chap ran; null for a dry run. | |
imported_count |
integer |
yes | How many locations Chap accepted. | |
rejected_count |
integer |
yes | How many observations Chap rejected. | |
rejected |
any[] |
The first hundred rejections, up to 64KB of them, in Chap's own words. | ||
dry_run |
boolean |
false |
Whether this was a validation only. |
chap.backtest.run¶
Run a Chap backtest on a stored dataset.
Not idempotent. Polls every 30s unless the step says otherwise.
Submits POST /v1/analytics/create-backtest and follows the evaluation job. The output is the
backtest exactly as /v1/jobs/{id}/evaluation_result answers with it, with its
aggregateMetrics lifted out. A parameter left unset is not sent, so Chap applies the
defaults the server has saved for the dataset's period type -- the numbers in the table are
Chap's own defaults when nothing was saved. An unknown dataset (404) and a target column the
dataset lacks (422) are refusals.
Config
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
connection |
string |
yes | The code of the chap connection naming the server. | |
follow_for |
string (humane-duration) |
"6h" |
How long the step follows the Chap job before failing it; a shorter step deadline ends it sooner. |
|
name |
string |
yes | The name the backtest is stored under. | |
model_id |
integer or string |
yes | The configured model to evaluate, by its id or its name. | |
dataset_id |
integer |
yes | The stored dataset to evaluate it on, as chap.dataset.import outputs it. |
|
n_periods |
integer or null |
null |
How many periods ahead each split forecasts; Chap's default is 3. | |
n_splits |
integer or null |
null |
How many train/test splits are evaluated; Chap's default is 7. | |
stride |
integer or null |
null |
How many periods one split moves on from the last; Chap's default is 1. | |
n_retrain |
integer or null |
null |
How many times the model is retrained across the splits; Chap's default is 1. | |
future_weather_provider |
string or null |
null |
Where the forecast window's weather comes from; Chap's default is climatology. |
|
target_column |
string or null |
null |
The dataset column the model forecasts; Chap's default is disease_cases. |
Output
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
backtest_id |
integer or null |
null |
The stored backtest's id. | |
job_id |
string |
yes | The evaluation job Chap ran. | |
aggregate_metrics |
any |
null |
The backtest's aggregateMetrics, lifted out of it. |
|
backtest |
any |
null |
The backtest exactly as Chap's evaluation result answers with it. |
chap.prediction.run¶
Run a Chap prediction setup.
Not idempotent. Polls every 30s unless the step says otherwise.
Submits POST /v1/crud/prediction-setups/{predictionSetupId}/run with the observations and
follows the forecast job; the output is the prediction as /v1/jobs/{id}/prediction_result
answers with it. An unknown setup (404), an archived model (409) and observations Chap cannot
use (422) are refusals and are never retried. The setup fixes the model, the target and the
future-weather provider. A finished job whose result has no integer id fails the step with
chap.prediction.not_stored, so a later step never reads every prediction's alerts for want of one.
Config
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
connection |
string |
yes | The code of the chap connection naming the server. | |
provided_data |
object[] |
Observations as Chap spells them: {period, orgUnit, value, featureName}, one per row. |
||
csv |
string or null |
null |
A wide Chap CSV, the shape aggregate_to_chap_csv writes: time_period, location, and one column per feature. Each feature cell becomes one observation, sent beside provided_data. |
|
max_observations |
integer |
500000 |
The most observations csv may unfold to, columns times rows; a larger one fails the step and sends nothing. |
|
follow_for |
string (humane-duration) |
"6h" |
How long the step follows the Chap job before failing it; a shorter step deadline ends it sooner. |
|
prediction_setup_id |
integer |
yes | The saved prediction setup to forecast with, which fixes the model and its target. | |
name |
string |
yes | The name the prediction is stored under. | |
geojson |
object |
yes | The org units as a GeoJSON FeatureCollection, each feature's id the location its observations name. |
|
n_periods |
integer or null |
null |
How many periods ahead to forecast; Chap's default is 3. |
Output
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
prediction_id |
integer |
yes | The stored prediction's id, which chap.prediction.read names. |
|
job_id |
string |
yes | The forecast job Chap ran. | |
prediction |
any |
null |
The prediction exactly as Chap's prediction result answers with it. |
chap.prediction.read¶
Read a Chap prediction's quantiles.
Not idempotent.
Reads GET /v1/analytics/prediction-entry/{predictionId} with one quantiles parameter per
quantile, and hands on the entries as Chap answers with them. An answer that is not a list fails
the step with chap.not_a_list, so a forecast that could not be read is not taken for an empty one.
Config
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
connection |
string |
yes | The code of the chap connection naming the server. | |
prediction_id |
integer |
yes | The stored prediction to read, as chap.prediction.run outputs it. |
|
quantiles |
number[] |
The quantiles to read each forecast at, each between 0 and 1. |
Output
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
entries |
any[] |
yes | The entries as Chap answers with them: {period, orgUnit, quantile, value}. |
|
count |
integer |
yes | How many entries there are. |
chap.models.list¶
List the models a Chap server has configured.
Not idempotent.
Reads GET /v1/crud/configured-models and hands on each model as Chap describes it. An answer that
is not a list fails the step with chap.not_a_list.
Config
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
connection |
string |
yes | The code of the chap connection naming the server. |
Output
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
models |
any[] |
yes | Each configured model as Chap answers with it: its id, name, covariates, target and the rest. |
|
count |
integer |
yes | How many models there are. |
chap.thresholds¶
Compute the endemic channel for a Chap dataset.
Not idempotent.
Posts POST /v1/analytics/thresholds and hands on the answer. params.type selects the
strategy; GET /v1/analytics/thresholds/strategies lists every strategy a server has, and the
two Chap ships are seasonal (stdMultiplier) and percentile (quantile,
baselineYears). A line parameter given as a list produces one threshold line per entry, so
quantile: [0.25, 0.75] is the endemic channel's band. Any undeclared key is sent as written. An
answer with no list under entries fails the step with chap.thresholds.no_entries.
Config
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
connection |
string |
yes | The code of the chap connection naming the server. | |
dataset_id |
integer |
yes | The stored dataset whose historical disease_cases the thresholds are computed from. |
|
period_ids |
string[] |
yes | The periods to compute thresholds for, at the dataset's own frequency, such as 2024-01. |
|
locations |
string[] or null |
null |
The locations to restrict the answer to; every location in the dataset when unset. | |
params |
object |
yes | The strategy and its parameters as Chap spells them: type selects the strategy (seasonal, percentile, or any GET /v1/analytics/thresholds/strategies lists), and a line parameter such as stdMultiplier or quantile takes one value or a list, one threshold line each. |
Output
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
params |
any |
null |
The parameters the thresholds were computed with, Chap's defaults applied. | |
lines |
any[] |
The line parameter of each threshold line, in the order of every entry's values. |
||
entries |
any[] |
One entry per requested period and location: {period, location, values}, a value null where no threshold could be computed. |
chap.alerts.record¶
Record alerts in Chap's review queue.
Not idempotent: a second alert for the same prediction, org unit and period is refused.
Posts POST /v1/crud/alerts with the alerts as written and hands on the ids Chap gave them.
Chap validates the whole batch before it writes any of it, so an unknown policy, level or
prediction (422) records nothing and is never retried. Every alert starts pending. A
prediction setup that names an alert policy raises its own alerts when its forecast finishes,
so a pipeline forecasting from one has nothing to record. Any undeclared key is sent as
written. See Alert verification.
Config
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
connection |
string |
yes | The code of the chap connection naming the server. | |
alerts |
object[] |
yes | The alerts as Chap spells them: {timePeriod, orgUnit, alertPolicyId, level, predictionId}, each level one the policy defines. |
Output
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
alert_ids |
integer[] |
yes | The recorded alerts' ids, in the order they were sent; each starts pending. |
|
count |
integer |
yes | How many alerts were recorded. |
chap.alerts.reviewed¶
Wait for a Chap prediction's alerts to be reviewed.
A sensor. Pokes every 5m and gives up after 3 days unless the step says otherwise.
Each poke is one GET /v1/crud/alerts?predictionId=..., narrowed by alertPolicyId when the
step names a policy and to org_units when it names any. An alert whose approved is
approved or declined is decided; pending, and any state Chap adds later, is still
waiting. Under release: all the wait ends once nothing is pending, under release: any as
soon as one alert is decided, and under either a prediction that raised no alerts ends it at
once with empty lists. While it waits, the attempt says how far the review has come -- 3 of 7
reviewed -- with the same fraction as its progress. The output splits the alerts by where they
stand, so a for_each over approved is one step per alert cleared for release.
The sensor never decides on an alert, and it is not a source: it waits inside the run that
forecast the prediction it names. poll, deadline and on_timeout are the step's own, as
for every sensor. See Alert verification.
The rows Chap answers with are held to what the step named: a row whose predictionId or, when a
policy is named, alertPolicyId differs fails the poke with chap.alerts.outside, here and in
chap.alerts.list. Rows are refused rather than skipped, since skipping them could release the gate
on an empty list. An org unit is filtered on the worker, and Chap's word for a row's org unit is taken.
Config
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
connection |
string |
yes | The code of the chap connection naming the server. | |
prediction_id |
integer |
yes | The prediction whose alerts are waited on, as chap.prediction.run outputs it. |
|
alert_policy_id |
integer or null |
null |
Only the alerts raised against this policy; every policy when unset. | |
org_units |
string[] or null |
null |
Only the alerts for these org units; every org unit when unset. | |
release |
"all" or "any" |
"all" |
When the wait ends: all once no alert is pending, any as soon as one is decided. A prediction that raised no alerts ends it at once either way. |
Output
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
approved |
any[] |
The alerts a reviewer cleared for release, as Chap answers with them: {id, timePeriod, orgUnit, alertPolicyId, level, predictionId, approvedBy, approvedAt, ...}. |
||
declined |
any[] |
The alerts a reviewer decided not to release, in the same shape. | ||
pending |
any[] |
The alerts nobody has decided on yet, in the same shape. | ||
counts |
object |
How many alerts there are, and how many of them are approved, declined and pending. |
The counts object carries total, approved, declined and pending.
chap.alerts.list¶
List Chap alerts by their review state.
Idempotent.
The same listing chap.alerts.reviewed pokes with, read once and never waited on: every alert
the filters select, split into approved, declined and pending. With no prediction_id
it reads every prediction's alerts, and its pending is the whole review queue. It is the
step that releases what was approved when a review's deadline passed.
Config
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
connection |
string |
yes | The code of the chap connection naming the server. | |
prediction_id |
integer or null |
null |
Only the alerts this prediction raised; every prediction's when unset. | |
alert_policy_id |
integer or null |
null |
Only the alerts raised against this policy; every policy when unset. | |
org_units |
string[] or null |
null |
Only the alerts for these org units; every org unit when unset. |
Output
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
approved |
any[] |
The alerts a reviewer cleared for release, as Chap answers with them: {id, timePeriod, orgUnit, alertPolicyId, level, predictionId, approvedBy, approvedAt, ...}. |
||
declined |
any[] |
The alerts a reviewer decided not to release, in the same shape. | ||
pending |
any[] |
The alerts nobody has decided on yet, in the same shape. | ||
counts |
object |
How many alerts there are, and how many of them are approved, declined and pending. |
What a path may carry¶
A job id Chap answers with goes into the path of every poll, read and cancel, so it must be one id:
a letter or a digit, then letters, digits, ., _ and -, at most 256 characters. Any other fails
the step with chap.job.not_submitted. Every other value in a path, such as prediction_id, is an
integer.
How a failure is classified¶
A refusal the server answers with is classified by its status: a 5xx and a 429 are
transient and retried within the step's budget, any other 4xx is a refusal and never retried. A
transport failure -- a refused connection, a timeout -- is transient. A refusal carries the
server's own words, its FastAPI detail where it gave one.
Every refusal carries a code¶
Each refusal is minted in the pack's catalogue under the chap prefix, so a reader selects it by its code and a translation can replace its words.
| Code | Template |
|---|---|
chap.answered |
{where} answered {status}: {remote} |
chap.not_json |
{where} answered with a body that is not JSON |
chap.connection.bad_api_token |
an api_token must be visible ASCII with no whitespace, or it cannot be sent as a header value |
chap.response_too_large |
{where} answered with more than max_response ({limit} bytes) and the rest was not read; raise max_response if the answer is expected |
chap.past_deadline |
{where} took longer than the {seconds} seconds it is allowed |
chap.not_a_list |
{where} answered with something other than a list |
chap.connection.unreachable |
Chap did not answer: {detail} |
chap.connection.auth_required |
Chap requires an API token on every call but /system/info, and this connection carries none |
chap.connection.token_refused |
Chap refused the API token: {detail} |
chap.job.running |
job {job} is {status} |
chap.job.failed |
job {job} ended {status}: {error} |
chap.job.unknown |
Chap no longer knows job {job} |
chap.job.unreadable |
{where} answered {answer}, which is neither a job status nor a job carrying one |
chap.job.not_settled |
job {job} did not settle within follow_for ({follow_for}); its last status was {status} |
chap.job.unsettled |
job {job} is {status} rather than SUCCESS, so it has no result to collect |
chap.observations.missing |
a step that sends observations to Chap needs provided_data, csv, or both |
chap.csv.missing_column |
the csv has no {column} column, which every row needs |
chap.csv.bad_value |
the csv's {column} on line {line} is '{value}', which is not a number |
chap.csv.too_large |
the csv unfolds to {count} observations, more than max_observations allows ({limit}) |
chap.dataset.rejected |
Chap rejected {count} of the observations and on_rejected is fail, so nothing was imported: {detail} |
chap.dataset.not_validated |
{where} answered a validation without a list under rejected, so nothing is known about the observations |
chap.thresholds.no_entries |
{where} answered without a list of entries |
chap.job.not_submitted |
{where} answered without a job id to follow |
chap.prediction.not_stored |
job {job} finished without an integer prediction id |
chap.alerts.reviewing |
{reviewed} of {total} reviewed |
chap.alerts.not_a_list |
{where} answered with something other than a list of alerts |
chap.alerts.outside |
{where} answered with an alert whose {field} is not the {asked} asked for |
chap.alerts.not_recorded |
{where} answered without the ids of the alerts it recorded |