ursa.recovery

Recovery utilities — migration-grade code, not on the production read path.

This sub-package houses derivation helpers used by the one-off rebuild / maintenance scripts under scripts/ and any future recovery tooling that needs to compute catalog field values from raw R2 segment bytes rather than catalog rows.

Production reads should not import from this module; the contract is “best-effort derivation,” not “correct on every input.” Error handling differs per submodule: :mod:ursa.recovery.timing parsers catch exceptions internally (collected in ModalitySignals.errors), while

mod:

ursa.recovery.first_epoch probes let read/parse failures raise — callers must catch them per row.

Package Contents

Classes

ModalitySignals

End-time candidates per signal source for one recording.

Functions

derive_recording_end_time

Probe every modality worker under recordings/<rec_id>/ and return per-modality end-time candidates.

API

class ursa.recovery.ModalitySignals[source]

End-time candidates per signal source for one recording.

Populated by :func:derive_recording_end_time. Use

Meth:

max_end to collapse the modality candidates into a single ended_at value; None indicates no signal source produced a parseable result (the row is indeterminate).

eeg: datetime.datetime | None

None

camera: datetime.datetime | None

None

mic: datetime.datetime | None

None

screen: datetime.datetime | None

None

samsungwatch: datetime.datetime | None

None

environment: datetime.datetime | None

None

worker_report: datetime.datetime | None

None

errors: list[str]

‘field(…)’

max_end() datetime.datetime | None[source]

Best ended_at estimate, or None if no signal produced a candidate (the row is indeterminate).

Prefers the latest data-stream end (eeg / camera / mic / screen / samsungwatch / environment), each read from actual recorded sample timestamps. worker_report (stopped_at_utc) is wall-clock at worker process teardown, not the last sample — workers routinely linger hours (even into the next day) past the last data, so including it in the max inflated durations (e.g. a 5h session reported as 30h, or short single-modality sessions reported as a uniform ~8h once the recorder’s idle timeout fires). It is therefore used only as a fallback when no data-stream signal exists — i.e. for worker types that still lack a sample-timestamp probe (notes / pupillabs / keyboard / mouse / location / battery).

ursa.recovery.derive_recording_end_time(store: ursa.store.ObjectStore, rec_id: str) ursa.recovery.timing.ModalitySignals[source]

Probe every modality worker under recordings/<rec_id>/ and return per-modality end-time candidates.

Func:

ModalitySignals.max_end collapses them into a single ended_at value. A single broken segment file degrades only its own worker — the error is recorded in ModalitySignals.errors and the walk continues. Callers should log errors regardless of the max_end() outcome so future parser regressions don’t hide behind a successful sibling modality.