monitor.export¶
The format:1 producer (spec 2026-07-12).
Pure reshaping; validation is the pydantic models’ job — the schema drift guards police this module for free.
-
otto.monitor.export.session_meta(collector: MetricCollector, *, interval: float | None =
None) SessionMeta¶ Reshape
get_meta_model()into an archival session meta.The result is a lenient
SessionMeta.The single source of this reshape — never hand-roll it. The two models do NOT share a field name for the chart list:
MonitorMetacalls itmetrics,SessionMetacalls itcharts. BecauseSessionMetais a lenientRowModel(extra='ignore'), feeding it a rawMonitorMetadump validates silently and drops every chart spec —tabssurvives (same name), so the damage looks partial and is easy to miss. A session persisted that way renders with no chart grouping and no units, exactly as an emptychart_mapdoes.charts/tabscome from the parser catalog rather than from collected data, so they are fully known even before the first tick — which is what lets the--dbpaths persistmeta_jsonat construction time.- Parameters:¶
collector – The collector whose parser catalog describes this session.
interval – The run’s collection interval in seconds. Pass this whenever the meta is built before :meth:`MetricCollector.run` has started — i.e. from the construction-time
--dbcall sites inotto.cli.monitor(its--intervaloption) andotto.suite.plugin(its--monitor-interval). The collector only records its ownintervaloncerun()begins, so reading it off the model at construction yieldsNonepermanently for a DB archive: nothing repairs it later (unlikechart_map, which the collector rewrites incrementally). A null interval is not cosmetic — the frontend resolves cadence aschart.interval ?? session.meta.interval(web/src/data/health.ts), so with both nullcadenceMs()returns null and derived health/staleness is unresolvable for the whole session. Omit it only when the collector has already run and its own recorded value is authoritative (build_live_export()).
- otto.monitor.export.build_session_metric_db(path: str, frame: SessionFrame, lab: LabSnapshot, meta_collector: MetricCollector, *, interval: float) MetricDB¶
Construct an unopened, archive-real v2
MetricDB.The shared construction for every ``–db``-backed session-monitor call site — never hand-roll ``MetricDB(…, lab_json=”{}”, meta_json=”{}”)``. Used by
otto.suite.suite.OttoSuite.start_monitor(),otto.suite.plugin.OttoPlugin’s--monitor --monitor-output *.dbsession fixture, andotto.cli.monitor’s--live --dbpath — closing a long-standing triplication: all three call sites used to build their ownMetricDB. One of them (OttoSuite.start_monitor) had drifted and kept persistinglab_json="{}"/meta_json="{}"— the degraded-archive shape this producer phase spent three fix waves eliminating elsewhere (no chart specs, no units, null interval, no lab topology on replay); the CLI’s copy was already correct, just duplicated.- Parameters:¶
path – Filesystem path for the SQLite archive.
frame – This run’s session identity (see
otto.monitor.session.new_frame()).lab – The frozen lab config for this session (see
otto.monitor.session.snapshot_lab()).meta_collector – A throwaway collector, built the same way (same hosts/targets/parsers) as the real collector that will go on to own this
MetricDB— never run, never handed this database. Needed becauseMetricDB’s constructor needsmeta_jsonup front, but the real collector can’t be constructed until theMetricDBobject it will own already exists.interval – The run’s collection interval in seconds. Must be passed explicitly — see
session_meta()’s docstring for why a null interval here would persist forever and leave the replayed session’s derived health unresolvable.
- Returns:¶
An unopened
MetricDB— call sites open it themselves, directly or by handing it to aMetricCollectorasdb=.
- otto.monitor.export.build_live_export(frame: SessionFrame, collector: MetricCollector, lab: LabSnapshot) MonitorExport¶
Wrap one live collector’s in-memory state into a format:1 document.
Always exactly one session, taking its identity straight from frame:
endmirrorsframe.endverbatim, so a still-open frame (end is None) produces a still-open session — a live export is never “crashed”, so no last-sample fallback applies here (that’sbuild_db_export()’s job, for archives a process actually walked away from). Nothing here reads the wall clock: frame fully determines the session’s bounds.- Parameters:¶
frame – This run’s session identity/lifetime (see
otto.monitor.session).collector – The live (or scripted) collector to snapshot.
lab – The frozen lab config for this session (see
otto.monitor.session.snapshot_lab()).
- Returns:¶
The single-session
MonitorExport.
- otto.monitor.export.build_db_export(path: str) MonitorExport¶
Read every session from a v2 archive into one format:1 document.
- Parameters:¶
path – Filesystem path to the v2 SQLite archive (see
otto.monitor.db.read_sessions(); fails loud on anything that is not schema v2).- Returns:¶
A
MonitorExportwith oneSessionRecordper archived session, in the archive’s own (start-ordered) order.
- otto.monitor.export.document_json(export: MonitorExport) str¶
Serialize export exactly as
scripts/gen_monitor_fixtures.pydoes.The generator’s
dumps()callsdoc.model_dump(mode="json", exclude_none=True)thenjson.dumps(...);model_dump_jsonis pydantic’s direct equivalent of that samemode="json"shape, so passing the identicalexclude_none=Trueargument here guarantees the frontend’s normalizer (web/src/data/exportDoc.ts) sees identical field presence/absence for a live/DB export as it does for the committed fixtures, byte-for-byte whitespace aside.