diff --git a/sfm/database.py b/sfm/database.py index 1792f20..37d0bbb 100644 --- a/sfm/database.py +++ b/sfm/database.py @@ -86,6 +86,14 @@ CREATE TABLE IF NOT EXISTS events ( a5_pickle_filename TEXT, -- ".a5.pkl" sidecar sidecar_filename TEXT, -- ".sfm.json" review/metadata sidecar device_family TEXT, -- "series3" (MiniMate Plus / BW) | "series4" (Micromate / Thor) — drives per-family UI rendering (units, labels) + tran_zc_freq REAL, -- Transverse ZC frequency (Hz) + vert_zc_freq REAL, -- Vertical ZC frequency (Hz) + long_zc_freq REAL, -- Longitudinal ZC frequency (Hz) + mic_zc_freq REAL, -- Microphone ZC frequency (Hz) + tran_zc_above_range INTEGER, -- 1 = value is device ceiling, render ">N Hz" + vert_zc_above_range INTEGER, + long_zc_above_range INTEGER, + mic_zc_above_range INTEGER, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), UNIQUE(serial, timestamp) ); @@ -200,6 +208,14 @@ class SeismoDb: ("a5_pickle_filename", "TEXT"), ("sidecar_filename", "TEXT"), ("device_family", "TEXT"), + ("tran_zc_freq", "REAL"), + ("vert_zc_freq", "REAL"), + ("long_zc_freq", "REAL"), + ("mic_zc_freq", "REAL"), + ("tran_zc_above_range", "INTEGER"), + ("vert_zc_above_range", "INTEGER"), + ("long_zc_above_range", "INTEGER"), + ("mic_zc_above_range", "INTEGER"), ): if col not in existing_cols: log.info("_migrate: events ADD COLUMN %s %s", col, ddl) diff --git a/tests/test_zc_freq_columns.py b/tests/test_zc_freq_columns.py new file mode 100644 index 0000000..51b079f --- /dev/null +++ b/tests/test_zc_freq_columns.py @@ -0,0 +1,45 @@ +from __future__ import annotations +import os, sys, sqlite3 +from pathlib import Path +import pytest + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from sfm.database import SeismoDb + +ZC_COLS = ["tran_zc_freq", "vert_zc_freq", "long_zc_freq", "mic_zc_freq", + "tran_zc_above_range", "vert_zc_above_range", + "long_zc_above_range", "mic_zc_above_range"] + + +def test_fresh_db_has_zc_freq_columns(tmp_path: Path): + db = SeismoDb(tmp_path / "seismo_relay.db") + with sqlite3.connect(str(tmp_path / "seismo_relay.db")) as conn: + cols = {r[1] for r in conn.execute("PRAGMA table_info(events)").fetchall()} + for c in ZC_COLS: + assert c in cols, f"missing column {c}" + + +def test_migration_adds_zc_freq_columns_to_legacy_db(tmp_path: Path): + db_path = tmp_path / "old.db" + # A "modern-minus-zc" events table (has blastware_* but not the zc columns). + with sqlite3.connect(str(db_path)) as conn: + conn.executescript(""" + CREATE TABLE events ( + id TEXT PRIMARY KEY, serial TEXT NOT NULL, waveform_key TEXT NOT NULL, + session_id TEXT, timestamp TEXT, + tran_ppv REAL, vert_ppv REAL, long_ppv REAL, peak_vector_sum REAL, mic_ppv REAL, + project TEXT, client TEXT, operator TEXT, sensor_location TEXT, + sample_rate INTEGER, record_type TEXT, + false_trigger INTEGER NOT NULL DEFAULT 0, + created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), + UNIQUE(serial, timestamp) + ); + INSERT INTO events (id, serial, waveform_key, timestamp) + VALUES ('legacy-id', 'BE11529', '01110000', '2026-04-01T12:00:00'); + """) + db = SeismoDb(db_path) # triggers _migrate + rows = db.query_events(serial="BE11529") + assert len(rows) == 1 + for c in ZC_COLS: + assert c in rows[0] # present + assert rows[0][c] is None # NULL for the legacy row