description: The chiptime object model: ParseResult, Activity, Session, Records, Streams — the object tree and the ideas that organize it.¶
Python API — model¶
What parse returns and what you drill into. Everything on this page is plain
data — frozen or simple dataclasses with no hidden state, no lazy IO, no
side effects. You make one call, then navigate attributes.
The object tree¶
ParseResult one parse() call returns exactly this
├─ ok · mode · file_type · source the verdict + input identity
├─ errors · warnings · provenance the paper trail (coded diagnostics)
├─ recovery what salvage did (None if unneeded)
├─ messages → [Message] lossless middle layer, file order
└─ activity → Activity the workout, made sense of
├─ sessions → [Session] one per sport bout
│ ├─ declared / derived → Totals device's claim vs recomputed truth
│ ├─ discrepancies where those two disagree
│ ├─ laps · lengths declared structure
│ └─ records → Records the per-second timeline
│ └─ streams → {Stream} columnar; null ≠ 0, always
├─ gaps recording holes, classified
└─ events · device · athlete · hrv_intervals_s
Three ideas organize it:
- Two truths, kept side by side. Devices declare totals; chiptime recomputes
them from the records.
Session.declaredandSession.derivedare bothTotals, anddiscrepancieslists every material disagreement — the disagreement is signal, not noise to reconcile away. - Columns, not rows.
Recordsstores one shared time axis plus oneStreamper field. That makes analytics natural (a stream is already a series) and keeps unknown fields lossless — every field any record carried becomes a stream, known or not. - The paper trail is part of the result.
provenanceonParseResultis the complete list of decisions chiptime made about your data. An empty list means the file was exactly what it claimed to be.
The result envelope¶
chiptime.result.ParseResult ¶
Everything chiptime.parse learned about one source.
The navigation model: one call, then drill into plain data —
result.activity.sessions[0].records.stream("power"). Nothing here is
lazy or stateful; what you see is the complete, final read.
The paper trail is the other half: errors (what was wrong),
warnings (what was suspicious), and provenance (every drop,
repair, and reinterpretation chiptime performed). An empty paper trail
means the file was exactly what it claimed to be.
Attributes:
| Name | Type | Description |
|---|---|---|
ok |
True when usable content was produced. |
|
mode |
Mode
|
The policy used ( |
source |
Input identity ( |
|
parts |
One |
|
errors |
Structural problems, as coded diagnostics. |
|
warnings |
Suspicious-but-recoverable findings. |
|
provenance |
The complete record of decisions taken on your data. |
|
recovery |
|
chiptime.result.RecoveryReport
dataclass
¶
What salvage did, when it had to. Present on the result only if truncation recovery or resynchronization engaged — its absence means the file needed none.
Attributes:
| Name | Type | Description |
|---|---|---|
recovered_records |
int
|
Data messages decoded despite the damage. |
estimated_total_records |
int | None
|
Best estimate of what a healthy file held. |
bytes_read |
int
|
Bytes successfully consumed. |
bytes_skipped |
int
|
Bytes stepped over as unreadable. |
resync_count |
int
|
Times the reader re-anchored past corruption. |
chiptime.result.SourceInfo
dataclass
¶
Identity of the parsed input. The local path is kept for humans
but never serialized (privacy + determinism, ADR-0002); sha256 is
the stable identity — cache and dedupe on it.
Attributes:
| Name | Type | Description |
|---|---|---|
path |
str | None
|
Where the bytes came from locally, or None for in-memory input. |
size_bytes |
int
|
Input size after any unwrapping. |
sha256 |
str
|
Hash of the parsed bytes. |
unwrapped |
tuple[str, ...]
|
Containers removed on the way in ( |
The workout model¶
Activity is the semantic view of one activity part; Session is the unit
almost everything operates on — analytics functions take a Session, splits and
intervals are computed per session, and multisport files simply have several.
chiptime.model.Activity
dataclass
¶
The whole workout: every session plus file-level context.
Attributes:
| Name | Type | Description |
|---|---|---|
sessions |
list[Session]
|
One per sport bout, in order (multisport gives several). |
events |
list[Event]
|
Timer and device events (start/stop/battery/...). |
gaps |
list[Gap]
|
Recording holes across the timeline, each classified. |
device |
DeviceInfo | None
|
Recording device identity, when the file says. |
athlete |
AthleteProfile | None
|
Athlete profile fields, when present. |
local_timestamp |
str | None
|
Raw local-time string from the activity message. |
utc_offset_s |
int | None
|
Validated local-UTC offset (ADR-0005), or None. |
hrv_intervals_s |
list[float]
|
Beat-to-beat RR intervals when the file logged HRV. |
chiptime.model.Session
dataclass
¶
One continuous bout of one sport — the center of the model.
A workout has one session per sport segment (a triathlon has five:
swim, transition, bike, transition, run). Everything hangs off it:
per-second data (records), declared structure (laps,
lengths), and the declared-vs-derived totals pair.
Attributes:
| Name | Type | Description |
|---|---|---|
sport |
str
|
FIT sport name ( |
sub_sport |
str | None
|
Refinement ( |
start_time |
datetime | None
|
Session start. |
end_time |
datetime | None
|
Start + declared elapsed when known. |
laps |
list[Lap]
|
Declared laps in order. |
lengths |
list[Length]
|
Pool lengths (swims only). |
records |
Records
|
The per-second timeline. |
declared |
Totals | None
|
The device's totals, if its session message survived. |
derived |
Totals
|
Totals recomputed from the records — always present. |
discrepancies |
list[Discrepancy]
|
Where declared and derived disagree materially. |
rebuilt |
bool
|
True when no session message survived and this one was synthesized from the records (recorded in provenance). |
chiptime.model.Totals
dataclass
¶
One set of summary numbers for a session or lap.
Appears twice on a Session — declared (the device's claim, absent
if the message never arrived) and derived (recomputed from the
records). Keeping both is the point: devices lie, and the disagreement
is signal (see Discrepancy).
Attributes:
| Name | Type | Description |
|---|---|---|
elapsed_time_s |
float | None
|
Wall-clock span, pauses included. |
timer_time_s |
float | None
|
Time with the timer running. |
moving_time_s |
float | None
|
Time actually moving (derived only). |
distance_m |
float | None
|
Distance in meters. |
ascent_m |
float | None
|
Total climb in meters. |
descent_m |
float | None
|
Total descent in meters. |
calories_kcal |
float | None
|
Energy as reported. |
avg |
dict[str, float]
|
Mean per stream name ( |
max |
dict[str, float]
|
Maximum per stream name. |
chiptime.model.Records
dataclass
¶
The per-second timeline, stored as columns rather than rows.
One shared time axis plus one Stream per field that ever appeared
in a record — lossless (unknown fields become streams too) and
analytics-friendly. Row-oriented access is a view (rows), not the
storage.
Attributes:
| Name | Type | Description |
|---|---|---|
time |
list[datetime | None]
|
Record timestamps ( |
streams |
dict[str, Stream]
|
Stream name → |
to_pandas ¶
DataFrame view (requires the chiptime[pandas] extra). None stays
NaN/NA — never silently zero (taxonomy #64).
chiptime.model.Stream
dataclass
¶
One column of record data — every FIT record field becomes a stream.
The honesty rule lives here: in values, None means the sensor said
nothing (dropout, sentinel on the wire) and 0 means it said zero
(coasting). They are never conflated, so a wire sentinel can never leak
into an average.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
Stream name — the FIT field name, or a promoted developer-field
name like |
units |
str | None
|
Unit string from the profile ( |
values |
list[Any]
|
One entry per record, index-aligned with |
source |
str
|
|
chiptime.model.Lap
dataclass
¶
One declared lap. end_time is always start + elapsed — never the
message's write timestamp, which devices emit late (taxonomy #50).
Analytics note: whether a lap was a button press or an auto-lap lives in
the raw lap message (lap_trigger), read by
chiptime.metrics.intervals.detect_structure — pass result.messages.
chiptime.model.Length
dataclass
¶
One pool length — the atom of swim structure. length_type is
"active" for swum lengths and "idle" for wall rest; zero-length
wall artifacts are flagged during reconciliation, not silently dropped.
chiptime.model.Gap
dataclass
¶
A hole in the recording, classified with evidence — an auto-pause is
not corruption, and the kind says which is which.
Attributes:
| Name | Type | Description |
|---|---|---|
start |
datetime
|
Last good timestamp before the hole. |
end |
datetime
|
First timestamp after it. |
duration_s |
float
|
Length of the hole in seconds. |
kind |
str
|
|
evidence |
str
|
Human-readable reason this classification was chosen. |
chiptime.model.Discrepancy
dataclass
¶
A disagreement between what the device declared and what the records prove — surfaced, never silently reconciled.
Attributes:
| Name | Type | Description |
|---|---|---|
field |
str
|
Totals field name ( |
declared |
float
|
The device's number. |
derived |
float
|
The recomputed number. |
delta |
float
|
|
Messages — the lossless middle layer¶
Below the workout model sits the decoded message list: every message in file
order, unknown-tolerant, with both decoded values and raw wire values. The
semantic model is derived from these; nothing is lost in between. Analytics
functions accept result.messages to read fields the semantic model doesn't
surface (lap triggers, workout steps, pool length).
chiptime.message.Message
dataclass
¶
A decoded FIT data message, unknown-tolerant (contract #6).
chiptime.message.FieldValue
dataclass
¶
One decoded field. value is scaled/normalized with sentinels → None;
raw is the wire value (kept for round-trips and include_raw output).
Repair and validation results¶
chiptime.repair.RepairResult
dataclass
¶
A repaired file plus the proof of what repair did.
Attributes:
| Name | Type | Description |
|---|---|---|
data |
bytes
|
The complete, valid |
provenance |
list[ProvenanceEntry]
|
Every salvaged, synthesized, and dropped element. |
output_strict_ok |
bool
|
Self-check — the output re-parsed in strict mode. |
parse_result |
ParseResult | None
|
The salvage parse of the input, for inspection. |
chiptime.validate.Finding
dataclass
¶
One platform-acceptance issue: a severity level, a stable code, and the human reason — encoding the checks that actually make uploads fail.