This vignette is for someone with a running EpiSODIC instance whose detection behaviour does not quite match their catchment: too many false alarms, missing outbreaks, clusters closing too early, the wrong things at the top of the board. It maps each of those complaints to the parameters that control it, the trade-off each adjustment creates, and how to verify the change helped. It does not re-explain how the detectors work (Detection and reconciliation) or how the configuration overlay system works (Deployment).
Every detection run records the exact configuration it used: a SHA-1
hash of the resolved settings (config_hash) and a full JSON
snapshot (config_snapshot), both on the run’s own row. A
change you make today affects only future runs, never the meaning of a
past one. You can tune with confidence, since nothing becomes
retroactively wrong.
Point the EPISODIC_CONFIG environment variable at a YAML
file with only the keys you want to change; everything else keeps its
shipped default. See Deployment
for the mechanics.
Three guidelines before you start:
episodic_validate_detection() for a controlled experiment.
See “Verifying that a change helped” below.If Farrington fires on modest rises that are within normal variation for your catchment, widen the statistical bound or raise the effect-size floor that gates new clusters.
farrington.alpha is the significance level for the upper
bound. Lowering it from 0.05 to 0.01 widens the bound, so a run needs a
larger exceedance before it reports one. This is a
sensitivity-specificity trade-off: fewer false alarms, but genuine small
outbreaks take longer to appear or are missed entirely.
effect_size_floor.min_excess_over_upperbound (default:
3) requires at least this many cases above the bound before a
new cluster is opened. Raising it filters out borderline
exceedances. A match against an already-open cluster is always merged,
regardless of this floor.
effect_size_floor.min_ratio_observed_expected (default:
1.5) requires the observed/expected ratio to reach this level. Together
with the excess floor, it means a signal must be both statistically and
practically notable. Either can be set to ~ (null) to
disable it.
farrington:
alpha: 0.01
effect_size_floor:
min_excess_over_upperbound: 5
min_ratio_observed_expected: 2.0To measure the effect before deploying it:
result_before <- episodic_validate_detection(history_years = 4)
result_after <- episodic_validate_detection(
history_years = 4,
config = list(
farrington = list(alpha = 0.01),
effect_size_floor = list(min_excess_over_upperbound = 5)
)
)
result_before$summary[result_before$summary$metric == "sensitivity", ]
result_after$summary[result_after$summary$metric == "sensitivity", ]The rule-based detector opens clusters whenever N cases of the same pathogen appear at the same institution within K days. For a common organism at a large hospital, three cases in 14 days may be entirely unremarkable.
The global defaults (same_place.default_n_cases and
same_place.default_k_days) affect every pathogen that does
not have its own override. Raising default_n_cases globally
also raises it for rare organisms where it should stay low, so
per-pathogen overrides under same_place.overrides are
usually the better lever:
same_place:
overrides:
Escherichia coli: {n_cases: 5, k_days: 14}
Staphylococcus aureus: {n_cases: 4, k_days: 10}Lowering default_k_days requires cases to be more
tightly clustered in time, which reduces noise from background endemic
rates.
If the same real outbreak appears as separate clusters at multiple lattice levels (a ward cluster and its parent institution cluster), suppression is not aggressive enough.
suppression.child_dominance_threshold (default: 0.70) is
the share of a parent’s cases that a single child must account for to
suppress the parent. Lowering it makes it easier for a child to absorb
its parent. suppression.parent_diffuse_threshold (default:
0.50) and suppression.parent_min_flagged_children (default:
1) control the reverse: a parent absorbs its children when each holds
less than that share of its cases and at least that many children
overlap it. The default of 1 lets a region absorb a single flagged
province whose rise is mostly outside it; raise it to require the rise
to be seen in several children first. A cluster someone has already
assessed is never suppressed, regardless of these thresholds.
Too aggressive suppression hides a genuinely independent signal behind a coincidentally overlapping one. If you find suppressed clusters that should not have been, raise the thresholds.
If Farrington reports nothing and the run log says a stream does not have enough baseline history, the model cannot fit because the stream’s history is shorter than the baseline requirement.
farrington.b (default: 2) is the number of reference
years. The model needs (b + 1) * 52 weeks before it fits at
all, so at b: 2 a stream needs three years of history.
Lowering it to 1 reduces the requirement to two years, at the cost of a
less stable model. Raise it as history accrues: 5 is the conventional
choice for a mature instance.
The three eligibility gates
(eligibility.min_baseline_weeks,
eligibility.min_median_weekly_count,
eligibility.min_nonzero_week_share) determine whether a
stream reaches the statistical detectors at all. Streams that fail still
run through same_place and rare_trigger.
Loosening these lets the model fit on sparser streams, where it may not
be meaningful.
A ward-level cluster of 2 cases of a serious organism may go
unreported because the global default_n_cases is 3. A
per-pathogen override is the targeted fix:
A stream you expect to be watched may fail the eligibility check.
eligibility.min_nonzero_week_share (default: 0.2) requires
at least 20% of baseline weeks to have at least one case.
eligibility.min_median_weekly_count (default: 1) requires a
median weekly count of at least 1. For genuinely sparse organisms,
lowering these lets the stream through, though the statistical model is
not meaningful on a stream with very few cases.
If a cluster auto-closes and a new one opens for the same ongoing outbreak, the case-free interval or the run-count limit is too short.
reconciliation.case_free_days_default (default: 14) is
the fallback interval: if no case arrives within this many days, the
cluster’s episode ends. Per-pathogen case_free_days in the
pathogen configuration CSV overrides this for pathogens with longer
incubation periods. reconciliation.close_after_runs
(default: 14) closes an unconfirmed cluster after this many consecutive
runs with no new case.
A cluster that has been confirmed by an epidemiologist is never
auto-closed by close_after_runs, only by case-free
days.
If the active queue has stale clusters nobody has looked at,
reconciliation.stale_open_days (default: 60) force-closes
any still-unassessed cluster this many days after its last case.
reconciliation.autoclose_unassessed must be
true (the default) for either close_after_runs
or stale_open_days to act on an unassessed cluster.
Aggressive auto-closure can close a genuine slow-burn outbreak before anyone has assessed it.
After a cluster closes, a cooldown window (per-pathogen
cooldown_days in the pathogen configuration CSV) absorbs
nearby cases into the recently-closed cluster rather than opening a new
one. reconciliation.cooldown_reopen_ratio (default: 1.5) is
an escape hatch: if the absorbed cases are at least 1.5 times the
cluster’s own size, the absorption is flagged rather than silent. Set to
~ to disable this escape hatch entirely, or shorten
cooldown_days per pathogen to narrow the absorption
window.
The priority score is a 0-100 weighted mean of seven components. A
component that cannot be computed for a given cluster (for example,
density_component requires a patient-day denominator) drops
out and the remaining weights renormalise.
priority_score.weights controls how much each component
contributes:
priority_score:
weights:
excess_component: 0.25
ratio_component: 0.15
severity_component: 0.20
growth_component: 0.15
agreement_component: 0.10
density_component: 0.10
spatial_component: 0.05Common adjustments:
severity_component. It reads from
severity_weight in the pathogen configuration CSV.severity_component, lower
excess_component.spatial_component.agreement_component, so signals
corroborated by multiple detectors are ranked above those seen by one
alone.All seven weights must sum to 1.0.
If no epidemics appear even for a pathogen you expect to be seasonal, MEM may lack enough history to fit.
mem.min_seasons (default: 2) requires two fully-observed
prior seasons before fitting. mem.min_climatology_years
(default: 3) requires three complete ISO years of case history to derive
a season anchor. Both are “need real history first” dials with the same
consequence as farrington.b: set beyond what an instance
carries, MEM says nothing. Lower them for a newer instance, and raise
them as history accrues.
Per-pathogen mem_mode in the pathogen configuration CSV
overrides the automatic seasonality test. Set to yes to
force MEM for a pathogen that the automatic test (auto)
does not flag as seasonal, or to no to suppress it for one
that it does.
If the pre-epidemic threshold crossing does not align with clinical reality, adjust the climatology or the seasonality test.
mem.climatology_smooth_weeks (default: 5) is the
smoothing window for the 52-week climatology used to find the season
trough. mem.trough_percentile (default: 0.25) is the
quantile at or below which a week counts as part of the trough.
mem.seasonality_peak_weeks (default: 8) and
mem.seasonality_min_peak_share (default: 0.40) control the
automatic seasonality test: a pathogen is seasonal under
mem_mode = 'auto' if at least 40% of its detrended annual
cases fall within an 8-week peak window.
Declaring the season started or ended is always a human act, never automatic. MEM raises the signal; a person declares. See Detection and reconciliation.
Per-pathogen biology lives in a CSV, separate from the YAML
configuration. The default file is inst/config/episodic_default_pathogen_config.csv.
To override it, point EPISODIC_PATHOGEN_CONFIG at your
own CSV containing only the pathogens and columns you want to change.
For each pathogen you list, non-empty values override the default; empty
cells keep it. Pathogens not in your file keep their row entirely. New
pathogens are added.
A minimal overlay that changes only the severity weight for MRSA and adds a local pathogen:
pathogen,severity_weight,case_free_days
MRSA,0.95,
My Local Pathogen,0.60,21
The columns and what they control:
| Column | Effect |
|---|---|
pathogen |
Match key, raw lab-provided string |
episode_days |
Deduplication window for repeated isolates from the same patient
(via AMR::get_episode()) |
incub_min_days, incub_max_days |
Incubation period bounds, shown on the dossier timeline |
case_free_days |
Days without a new case before a cluster’s episode boundary,
overrides
reconciliation: |
cooldown_days |
Cool-down window after closure during which new cases are absorbed rather than opening a new cluster |
rt_applicable |
Whether Rt estimation applies (0 or 1) |
si_mean_days, si_sd_days |
Serial interval parameters for Rt estimation |
si_dist |
Serial interval distribution: gamma,
lognormal, or weibull |
end_r, end_k |
Mean and dispersion of the negative binomial number of cases each
outbreak case goes on to cause, for the probability that an outbreak is
over; both or neither, end_r at least 0 and
end_k above 0. Shipped empty for every pathogen |
mem_mode |
MEM seasonal detection: auto (derive from data),
yes (force on), no (force off) |
severity_weight |
Clinical severity weight (0 to 1) feeding the priority score’s severity component |
A common confusion point: the per-pathogen detection
thresholds for same_place are in the YAML (under
same_place.overrides), not in this CSV. The CSV holds
per-pathogen biology, the YAML holds per-pathogen detection
rules.
The validation framework accepts per-pathogen overrides through the
pathogen_config argument:
episodic_validate_detection() replays generated history
through the full detection pipeline week by week and matches what it
found against seeded outbreaks on case sets. Pass a config
list (for YAML keys) or a pathogen_config data frame (for
CSV columns) with the settings you want to test:
baseline <- episodic_validate_detection(
seeds = 1:10,
history_years = 4,
evaluation_weeks = 52
)
adjusted <- episodic_validate_detection(
seeds = 1:10,
history_years = 4,
evaluation_weeks = 52,
config = list(farrington = list(alpha = 0.01))
)
# Compare
baseline$summary[baseline$summary$metric %in% c("sensitivity", "ppv"), ]
adjusted$summary[adjusted$summary$metric %in% c("sensitivity", "ppv"), ]This measures the machinery against known truth: does the change improve sensitivity, reduce false alarms, or affect time-to-detection? The defaults are deliberately small (one seed, four weeks) so it returns in seconds; for a proper comparison use more seeds and a longer window. See Measuring detection against known truth for the full framework.
The Performance screen measures the instance against its own epidemiologists’ verdicts: what share of clusters the board classified as noise (positive predictive value), and how quickly real signals were detected (time-to-detection). This is not circular for the question “did this change reduce false alarms”, which is exactly what PPV answers. It cannot measure sensitivity, because an outbreak the detectors miss never becomes a cluster for anyone to judge.
config_hash lets you compare runs before and after a
change: filter the Performance screen or the
episodic_detection_run table by hash to see the metrics
under the old settings beside the new ones.
Every tunable parameter across both the YAML configuration and the per-pathogen CSV, with its default and one-line effect.
| Key | Default | Effect |
|---|---|---|
reconciliation: |
14 | Fallback days without a case before a cluster episode boundary |
reconciliation: |
14 | Consecutive runs with no new case before an unconfirmed cluster auto-closes |
reconciliation: |
1.5 | Absorption escape-hatch ratio; ~ disables |
reconciliation: |
60 | Force-closes unassessed clusters this many days after last case;
~ disables |
reconciliation: |
TRUE |
Whether auto-closure rules apply to clusters nobody assessed |
| Key | Default | Effect |
|---|---|---|
eligibility: |
52 | Weeks of history required before statistical detection runs |
eligibility: |
1 | Median weekly count over baseline; streams below are excluded from Farrington/MEM |
eligibility: |
0.2 | Share of baseline weeks with at least one case |
| Key | Default | Effect |
|---|---|---|
effect_size_floor: |
3 | Cases above Farrington’s upper bound required to open a new cluster;
~ disables |
effect_size_floor: |
1.5 | Observed/expected ratio required to open a new cluster;
~ disables |
| Key | Default | Effect |
|---|---|---|
same_place: |
TRUE |
Master switch |
same_place: |
3 | Cases at one institution within K days to fire |
same_place: |
14 | Time window in days |
same_place: |
90 | How far back a run reports hits; ~ unbounds (not
recommended) |
same_place: |
(per pathogen) | {n_cases, k_days} per raw pathogen string |
| Key | Default | Effect |
|---|---|---|
farrington: |
TRUE |
Master switch; switching off stops the detector, not the Pathogen screen’s baseline |
farrington: |
week | Aggregation unit |
farrington: |
2 | Reference years; model needs (b+1)*52 weeks |
farrington: |
3 | Half-window (weeks) around the same calendar week in each reference year |
farrington: |
TRUE |
Down-weight past outbreak weeks by Anscombe residual |
farrington: |
2.58 | Residual cutoff for reweighting |
farrington: |
TRUE |
Allow a linear trend term |
farrington: |
26 | Trailing weeks excluded from the noise estimate |
farrington: |
[5, 4] | Low-count safeguard |
farrington: |
0.05 | Significance level; lower = wider bound = fewer alarms |
farrington: |
8 | Cap on trailing weeks tested per run |
| Key | Default | Effect |
|---|---|---|
mem: |
TRUE |
Master switch |
mem: |
2 | Fully-observed prior seasons required before fitting |
mem: |
[pathogen_province, pathogen_region] | Lattice levels MEM runs at |
mem: |
3 | Complete ISO years required to derive a season anchor |
mem: |
5 | Smoothing window for the 52-week climatology |
mem: |
0.25 | Quantile at or below which a week is part of the trough |
mem: |
8 | Peak-concentration window width for the seasonality test |
mem: |
0.40 | Minimum share of detrended cases in the peak window for
mem_mode = 'auto' |
mem: |
median |
Statistic the typical past season is summarised by:
median, geometric_mean or
arithmetic_mean; see “Typical past season” below |
| Key | Default | Effect |
|---|---|---|
rare_trigger: |
TRUE |
Master switch |
rare_trigger: |
(4 shipped) | Curated list of raw pathogen strings; a single case fires |
rare_trigger: |
1 | Cases in the window to fire |
rare_trigger: |
90 | How far back a run reports hits; ~ unbounds (not
recommended) |
Every run nowcasts each stream that carries an open cluster: the
expected final count of its most recent days and weeks, once reporting
is complete, from the stream’s own reporting delays
(surveillance::nowcast(), method bayes.trunc).
A case’s reporting date is the date of the run that first loaded it, so
the delay’s resolution is the cron schedule. Only cases sampled on or
after the first run on the database carry an observed delay; a
backfilled archive does not. Each run’s nowcast is stored, drawn on the
dossier curves, and scored on the Performance screen once its targets
are past max_delay_days.
| Key | Default | Effect |
|---|---|---|
forecast: |
TRUE |
Master switch |
forecast: |
21 | Reporting horizon in days: the longest delay allowed for, and the days nowcast |
forecast: |
0.25 | Expected share already reported below which a day is not nowcast |
forecast: |
20 | Cases with a fully observed delay a stream needs before it is nowcast |
forecast: |
28 | Fully reported days whose mean daily count centres the prior |
forecast: |
~ |
Days of reports the delay is estimated from; ~ uses the
whole history, a value must be at least max_delay_days |
forecast: |
500 | Delay-distribution samples per nowcast (at least 100) |
Every run gives each open outbreak of a transmissible pathogen
(rt_applicable 1, with a complete serial interval) the
probability that it is over: that none of its cases goes on to cause a
case not yet reported (Nishiura et al. 2016, with the negative binomial
offspring distribution of Linton et al. 2022). Its inputs are the
pathogen’s serial interval and its offspring mean and dispersion
(end_r, end_k in the pathogen CSV), and the
stream’s reporting completeness, so quiet days whose cases may still be
on their way count for less. It is not computed for an outbreak whose
cases all fall within one maximum incubation period, which points to a
common source rather than transmission. The dossier shows it beside the
case-free days, with the date it reaches
probability_threshold if no further case is reported; it
never closes an outbreak. Each run’s probability is stored and scored on
the Performance screen once the case-free window after the last case has
passed and been reported.
end_r and end_k ship empty for every
pathogen: set them in your pathogen CSV from a source you have checked
for your setting. Until they are set, the dossier says so instead of
showing a probability.
| Key | Default | Effect |
|---|---|---|
forecast: |
TRUE |
Master switch |
forecast: |
0.95 | Probability whose date the dossier reports (above 0, below 1) |
Every run estimates, for each open epidemic, its stream’s weekly
growth rate over the last window_weeks weeks: a log-linear
Poisson regression on the weekly counts, its standard error scaled by
the Pearson dispersion (never below Poisson). The weeks still inside the
nowcast’s reporting horizon
(forecast.nowcast.max_delay_days) are taken from the
nowcast, their uncertainty carried through by sampling; a week neither
fully reported nor nowcast ends the window before it rather than being
read as final, so without a nowcast the window ends some weeks back, and
the dossier names the weeks it covers. Weeks that begin before the
earliest sample date in the case data are left out, never counted as
weeks without cases. The dossier shows the change in weekly cases the
trend implies, the doubling or halving time when the 90% interval is on
one side of zero, the chance the epidemic is growing, and the chance it
is past its peak so far: declining, with its last week below its highest
since its first week. That last says nothing about whether it will rise
again. Each run’s estimate is stored and scored on the Performance
screen once its weeks have passed the reporting horizon, against the
same trend fitted to their counts as then reported.
| Key | Default | Effect |
|---|---|---|
forecast: |
TRUE |
Master switch |
forecast: |
4 | Weeks the growth rate is estimated over (at least 3) |
forecast: |
20 | Reported cases those weeks need before a rate is estimated |
forecast: |
1000 | Draws the interval and probabilities are computed from (at least 100) |
Every run forecasts, for each open epidemic, its stream’s weekly
count in each of the horizon_weeks weeks after the last
complete week. The forecast is an equally weighted ensemble of two
models, combined by averaging their quantiles as the European and US
COVID-19 forecast hubs combine theirs: a model of the stream’s whole
history, which knows its seasons and usual levels, and the trend of its
last weeks extended ahead, which follows a rise or fall the history has
not seen. Averaging quantiles keeps the ensemble about as sharp as its
members, where pooling their draws would widen it to cover both.
The history model is surveillance::hhh4() with a
negative binomial count: each week’s expected count is an endemic level,
seasonal (one sine-cosine pair of period 52 weeks) for a seasonal
epidemic and constant otherwise, plus an autoregressive share of the
week before. It is fitted on the stream’s fully reported weeks, of which
it needs min_history_weeks. When the autoregressive share
is not identified (its log-scale standard error above 1, as when the
counts show no week-to-week dependence), the endemic level alone is used
rather than a share whose uncertainty would produce runaway paths.
Paths are simulated from the last fully reported week with parameters drawn from the fit’s approximate sampling distribution, so the intervals carry the uncertainty of the estimates as well as the counts’ noise. Weeks still being reported are taken from the nowcast, or, without one, drawn from the model but never below what has already been reported; the week in progress likewise.
The trend is the log-linear trend the direction tile reports, fitted
along each path over the last
forecast.epidemic_direction.window_weeks weeks and extended
to the target weeks with its parameter uncertainty and the counts’
dispersion. It takes part only when every week of that window, up to the
last complete week, is fully reported or nowcast: a trend is a
short-range forecast, and one fitted to the history model’s own draws,
or extended from a window that ended weeks back, carries a growth rate
far beyond where it was measured. Without it, which is the case until
the nowcast has a reporting history, the outlook is the history model’s
alone, and the dossier says so.
For a seasonal epidemic, each week ahead also gets the chance of reaching each MEM band it was opened against, read from the averaged quantiles, and each band the chance of being reached in any of the weeks, read from the averaged distribution of each path’s highest week.
The Epidemics dossier shows the outlook, as a table and as marks
after the weekly curve’s bars, only once that pathogen’s outlooks have
been scored on min_scored weeks and, over those, beat
expecting no change (the last complete week’s count carried forward):
their interval score relative to it is below 1. It states that record,
with the share inside the 90% interval. Until then it says how far the
record has come, or that it does not beat expecting no change: a model
that fits past seasons can still miss an unusual one, which is what the
record is there to show. Each run’s outlook is stored and scored on the
Performance screen as its weeks are reported.
| Key | Default | Effect |
|---|---|---|
forecast: |
TRUE |
Master switch |
forecast: |
4 | Weeks ahead forecast (1 to 8) |
forecast: |
104 | Fully reported weeks the model is fitted on at least (at least 26) |
forecast: |
1000 | Simulated paths per outlook (at least 100) |
forecast: |
20 | Scored weeks a pathogen’s outlooks need, and must beat expecting no change over, before the dossier shows them |
When MEM opens a seasonal epidemic, the fit that set its thresholds
also gives the typical course of the past seasons’ epidemics
(mem::memmodel()’s typical curve, with its 95% interval),
the week they typically started, and their typical length. These are
stored with the epidemic and fixed, like its thresholds. The Epidemics
dossier draws the typical curve on the weekly curve with its start at
this epidemic’s onset, and gives the typical length and how many weeks
earlier or later this epidemic started. It describes past seasons and is
labelled as such; it is not a forecast.
mem.typical_curve sets the statistic the curve
summarises the aligned past seasons by, week by week. It changes only
the curve: the thresholds, the intensity bands, and the typical start
and length are the same whichever it is. The start and length are always
medians.
| Value | Curve | Interval |
|---|---|---|
median |
The middle past season in each week: one extreme season does not move it. Equal to the arithmetic mean with two seasons | Hettmansperger-Sheather/Nyblom, normal approximation with few seasons |
geometric_mean |
exp(mean(log(x + 1))) - 1: less pulled up by one large
season than the arithmetic mean, pulled down by one quiet one, and
always below it |
Normal on the log scale |
arithmetic_mean |
The average: a single severe season raises the whole curve | Normal, symmetric, so its lower limit can fall below zero in quiet weeks |
mem’s median with a bootstrap interval
(i.type.curve = 4) is deliberately not offered: it samples
at random, so the same seasons give a different curve on each fit and
the fit could not be reused from run to run; it takes some thirty
seconds per fit; and it fails on two seasons. The dossier names the
statistic under the curve, and a limit below zero is drawn at zero.
Changing the value refits every stream’s MEM model on the next run, but
an epidemic already open keeps the curve it opened with.
| Key | Default | Effect |
|---|---|---|
priority_score: |
0.25 | Weight for cases above the model’s upper bound |
priority_score: |
0.15 | Weight for observed/expected ratio |
priority_score: |
0.20 | Weight for the pathogen’s severity_weight |
priority_score: |
0.15 | Weight for cluster acceleration |
priority_score: |
0.10 | Weight for share of detectors that fired |
priority_score: |
0.10 | Weight for incidence per 1,000 patient-days versus baseline |
priority_score: |
0.05 | Weight for case concentration in one location |
| Key | Default | Effect |
|---|---|---|
scale: |
[pathogen_province, pathogen_region] | Lattice levels that produce epidemics rather than outbreaks |
| Key | Default | Effect |
|---|---|---|
geography: |
REGION | L5 whole-catchment code; name your service area |
geography: |
AREA- | L3 prefix before the leading postcode characters |
geography: |
2 | Number of leading postcode characters for L3 grouping |
| Key | Default | Effect |
|---|---|---|
suppression: |
0.70 | Case-overlap share for a child to suppress its parent |
suppression: |
0.50 | Below this per child, the rise counts as diffuse |
suppression: |
1 | Minimum overlapping children before the diffuse rule fires |
notifications ships empty (~): nothing is
sent until it is configured. Its keys are about delivery, not detection,
and are left out of config_hash.
| Key | Default | Effect |
|---|---|---|
notifications: |
(unset) | Master switch for every channel |
notifications: |
(unset) | Address of the dashboard, linked from each message |
notifications: |
(unset) | Send when a run opens at least one new cluster; must be set to
true explicitly |
notifications: |
(unset) | Send when a run fails, including one that stops before it begins;
must be set to true explicitly |
notifications: |
(unset) | One block per channel (ntfy, smtp,
sendmail, microsoft365, teams,
slack), each with its own enabled and
settings |
Every channel’s settings, and how to test them, are in Notifications.
| Key | Default | Effect |
|---|---|---|
report: |
5 | Counts below this in geography/institution tables are suppressed to prevent re-identification |
report: |
~ | Base directory for rendered reports; mandatory when
EPISODIC_DB is a DSN |
| Key | Default | Effect |
|---|---|---|
access: |
FALSE |
Whether the dashboard requires a sign-in to read anything |
| Key | Default | Effect |
|---|---|---|
database: |
TRUE |
Whether a scheduled run brings the database up to the installed package’s schema itself; see Deployment’s “Upgrading EpiSODIC” |
| Column | Typical default | Effect |
|---|---|---|
episode_days |
30 | Deduplication window for repeated isolates |
incub_min_days, incub_max_days |
(varies) | Incubation period bounds for the dossier timeline |
case_free_days |
14 | Overrides
reconciliation: |
cooldown_days |
(varies) | Cool-down absorption window after closure |
rt_applicable |
0 | Whether Rt estimation applies (0 or 1) |
si_mean_days, si_sd_days |
(varies) | Serial interval for Rt |
si_dist |
(varies) | Serial interval distribution |
end_r, end_k |
(empty) | Offspring mean and dispersion for the outbreak-end probability |
mem_mode |
auto | MEM: auto, yes, or no |
severity_weight |
1.00 | Clinical severity for priority scoring (0 to 1) |
EPISODIC_* variables including
EPISODIC_PATHOGEN_CONFIG