charts
Chart models: server-computed chart GEOMETRY.
The API computes all layout math (domains, tick positions, normalized
fractions) once; every renderer — the frontend's thin components, the export
pipeline's SVG emitters — draws from the same model. Raw values ride along
for tooltips and data-* attributes; colors are brand token NAMES resolved
by the consumer (CSS custom properties live, tokens.yaml literals in PDFs).
AxisTick
pydantic-model
¶
Bases: BaseModel
One axis tick: value, normalized position, display label.
Fields:
-
value(float) -
fraction(float) -
label(str) -
clock_label(str | None)
clock_label = None
pydantic-field
¶
Wall-clock display label (e.g. '23:00') for time axes whose producer knows the recording start. None on non-time axes or producers predating the field; consumers offer an Elapsed/Clock toggle when present.
fraction
pydantic-field
¶
Position, 0 (left) … 1 (right).
label
pydantic-field
¶
Display label for the tick.
value
pydantic-field
¶
Tick value in the domain's unit.
BoxplotBoxValues
pydantic-model
¶
Bases: BaseModel
Five-number summary — raw values or normalized fractions.
Fields:
-
min(float) -
q1(float) -
median(float) -
q3(float) -
max(float)
BoxplotModel
pydantic-model
¶
Bases: BaseModel
A horizontal box-and-whisker chart: shared axis + rows.
Fields:
-
domain_min(float) -
domain_max(float) -
unit_label(str) -
value_format(Literal['duration-hours', 'decimal']) -
decimals(int) -
ticks(list[AxisTick]) -
rows(list[BoxplotRow]) -
svg(str | None)
decimals = 1
pydantic-field
¶
Decimal places for 'decimal' formatting; ignored otherwise. Matches the tick precision the producer chose.
domain_max
pydantic-field
¶
Axis maximum, in the axis unit.
domain_min
pydantic-field
¶
Axis minimum, in the axis unit.
rows
pydantic-field
¶
The rows, in display order.
svg = None
pydantic-field
¶
Server-rendered inline SVG of this chart: the one renderer behind live pages and document exports. Colors are CSS custom-property references the page resolves; elements carry stable classes, native
ticks
pydantic-field
¶
Axis ticks at nice intervals.
unit_label
pydantic-field
¶
Axis caption including the unit, e.g. 'Total duration (h)'.
value_format = 'decimal'
pydantic-field
¶
How a consumer renders one of this chart's VALUES as text (tooltips, data labels). The axis ticks are already formatted in ticks[].label; this is the same rule for the numbers between them, so a tooltip cannot contradict the axis above it. 'duration-hours' means values are hours to be shown as a duration ('1h 30m'); 'decimal' means a plain number at decimals places. Defaults to 'decimal' so a producer predating the field is read literally rather than as hours.
BoxplotPoint
pydantic-model
¶
Bases: BaseModel
One raw observation drawn as a dot, in the axis unit and on the 0…1 domain.
Fields:
fraction
pydantic-field
¶
The same value on the shared domain.
label = None
pydantic-field
¶
Which observation the dot is, for its tooltip — the study, and the subject and visit it belongs to when those are declared. None when the source draws anonymous observations.
value
pydantic-field
¶
The observation, in the axis unit.
BoxplotRow
pydantic-model
¶
Bases: BaseModel
One labelled box-and-whisker row on a shared axis.
Fields:
-
label(str) -
values(BoxplotBoxValues) -
fractions(BoxplotBoxValues) -
n(int | None) -
subjects(int | None) -
color_token(str | None) -
series_key(str | None) -
points(list[BoxplotPoint] | None)
Validators:
color_token = None
pydantic-field
¶
Brand color token NAME (e.g. 'primary-600'); consumers resolve it (CSS variable live, tokens.yaml literal in PDFs).
fractions
pydantic-field
¶
The same box mapped to 0…1 on the shared domain.
label
pydantic-field
¶
Row label (e.g. an event type).
n = None
pydantic-field
¶
Sample count behind the box.
points = None
pydantic-field
¶
Set when the row has too few observations for a box to mean anything (below the producer's configured minimum): the raw observations, drawn as dots on the shared axis instead of the box. values/fractions still carry the five-number summary of the same observations, so a consumer that ignores this field draws a (degenerate) box rather than nothing.
series_key = None
pydantic-field
¶
Stable series identifier stamped as a CSS class hook (e.g. group show/hide).
subjects = None
pydantic-field
¶
Distinct subjects behind the same observations, when the producer knows them and they can differ from n — one person recorded twice in a calendar bin is two observations and one subject. None when subjects are unknown or not meaningful for the row.
values
pydantic-field
¶
Raw five-number summary, in the axis unit (tooltips/data-*).
validate_fractions_bounded()
pydantic-validator
¶
Fractions must lie on the shared 0…1 domain.
values and fractions share a shape but not an invariant — the
bound lives here so a producer whose domain math drifts fails at the
contract, not in a renderer.
HypnogramModel
pydantic-model
¶
Bases: BaseModel
A staging hypnogram: stage lanes + step segments over the recording.
Fields:
-
duration_ms(int) -
lanes(list[str]) -
segments(list[HypnogramSegment]) -
ticks(list[AxisTick]) -
lights_off_fraction(float | None) -
lights_on_fraction(float | None) -
svg(str | None)
duration_ms
pydantic-field
¶
Recording length the fractions map over.
lanes
pydantic-field
¶
Stage labels, top lane first.
segments
pydantic-field
¶
Runs in time order.
svg = None
pydantic-field
¶
Server-rendered inline SVG of this hypnogram — same contract as BoxplotModel.svg: CSS-variable colors, stable classes,
ticks
pydantic-field
¶
Time-axis ticks (value in ms).
HypnogramSegment
pydantic-model
¶
Bases: BaseModel
One constant-stage run of the hypnogram step curve.
Fields:
LineChartModel
pydantic-model
¶
Bases: BaseModel
Lines over a shared value axis and a shared, ordered time axis.
Fields:
-
domain_min(float) -
domain_max(float) -
unit_label(str) -
value_format(Literal['duration-hours', 'decimal']) -
decimals(int) -
ticks(list[AxisTick]) -
x_ticks(list[AxisTick]) -
series(list[LineSeries]) -
svg(str | None)
decimals = 1
pydantic-field
¶
Decimal places for 'decimal' formatting.
domain_max
pydantic-field
¶
Value-axis maximum, in the axis unit.
domain_min
pydantic-field
¶
Value-axis minimum, in the axis unit.
series
pydantic-field
¶
The lines, in legend order.
svg = None
pydantic-field
¶
Server-rendered inline SVG of this chart, the same renderer behind live pages and document exports. None for producers predating the field.
ticks
pydantic-field
¶
Value-axis ticks at nice intervals.
unit_label
pydantic-field
¶
Value-axis caption including the unit, e.g. 'TST (min)'.
value_format = 'decimal'
pydantic-field
¶
How a consumer renders one of this chart's VALUES as text, the same rule the axis ticks already follow.
x_ticks
pydantic-field
¶
Time-axis ticks: one per bin, in order, labelled as the bins are. value is the bin's index.
LinePoint
pydantic-model
¶
Bases: BaseModel
One observation on a line: where it sits on both axes.
Fields:
-
value(float) -
fraction(float) -
x_fraction(float) -
label(str)
LineSeries
pydantic-model
¶
Bases: BaseModel
One line: a subject's observations in time order.
Fields:
color_token = None
pydantic-field
¶
Brand color token NAME the line uses; consumers resolve it (CSS variable live, tokens.yaml literal in PDFs).
dash = None
pydantic-field
¶
SVG dash pattern, e.g. '5 3'. Set when lines share a colour because colour carries something else — a group, say — so the stroke still tells them apart on paper, where no tooltip can. None draws a solid line.
key
pydantic-field
¶
Stable series identity within the chart (a CSS class hook and a client selection key), not display text.
label
pydantic-field
¶
Legend text, e.g. the subject's key.
points
pydantic-field
¶
The observations in time order. A bin the series has no study in carries no point, so the line breaks rather than interpolating a night that was never recorded.