feat: add waveform download and storage.
This commit is contained in:
+42
-4
@@ -81,6 +81,7 @@ CREATE TABLE IF NOT EXISTS events (
|
||||
sample_rate INTEGER,
|
||||
record_type TEXT, -- "single_shot" | "continuous"
|
||||
false_trigger INTEGER NOT NULL DEFAULT 0, -- 0=no, 1=yes (manual flag)
|
||||
waveform_blob TEXT, -- JSON waveform response (channels + metadata)
|
||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
|
||||
UNIQUE(serial, timestamp)
|
||||
);
|
||||
@@ -216,6 +217,17 @@ class SeismoDb:
|
||||
""")
|
||||
log.info("_migrate: monitor_log table rebuilt OK")
|
||||
|
||||
# Migration 3: add waveform_blob column to events (nullable TEXT).
|
||||
# ALter TABLE ADD COLUMN is safe in SQLite for nullable columns — no rebuild needed.
|
||||
col_names = {
|
||||
row[1]
|
||||
for row in conn.execute("PRAGMA table_info(events)").fetchall()
|
||||
}
|
||||
if "waveform_blob" not in col_names:
|
||||
log.info("_migrate: adding waveform_blob column to events")
|
||||
conn.execute("ALTER TABLE events ADD COLUMN waveform_blob TEXT")
|
||||
log.info("_migrate: waveform_blob column added OK")
|
||||
|
||||
@staticmethod
|
||||
def _iso(dt: Optional[datetime.datetime]) -> Optional[str]:
|
||||
return dt.isoformat() if dt is not None else None
|
||||
@@ -282,12 +294,19 @@ class SeismoDb:
|
||||
*,
|
||||
serial: str,
|
||||
session_id: Optional[str] = None,
|
||||
waveform_blobs: Optional[dict[str, str]] = None,
|
||||
) -> tuple[int, int]:
|
||||
"""
|
||||
Insert triggered events. Silently skips duplicates (serial+timestamp).
|
||||
Returns (inserted, skipped).
|
||||
|
||||
waveform_blobs: optional mapping of waveform_key (hex str) → JSON string
|
||||
containing the full waveform response (channels + metadata). When provided,
|
||||
the blob is stored alongside the event row and is retrievable via
|
||||
GET /db/events/{id}/waveform.
|
||||
"""
|
||||
inserted = skipped = 0
|
||||
blobs = waveform_blobs or {}
|
||||
with self._connect() as conn:
|
||||
for ev in events:
|
||||
key = ev._waveform_key.hex() if ev._waveform_key else None
|
||||
@@ -305,8 +324,9 @@ class SeismoDb:
|
||||
except Exception:
|
||||
ts = str(ev.timestamp)
|
||||
|
||||
pv = ev.peak_values
|
||||
pi = ev.project_info
|
||||
pv = ev.peak_values
|
||||
pi = ev.project_info
|
||||
blob = blobs.get(key)
|
||||
|
||||
try:
|
||||
conn.execute(
|
||||
@@ -315,8 +335,8 @@ class SeismoDb:
|
||||
(id, serial, waveform_key, session_id, timestamp,
|
||||
tran_ppv, vert_ppv, long_ppv, peak_vector_sum, mic_ppv,
|
||||
project, client, operator, sensor_location,
|
||||
sample_rate, record_type)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
sample_rate, record_type, waveform_blob)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
self._new_id(), serial, key, session_id, ts,
|
||||
@@ -331,6 +351,7 @@ class SeismoDb:
|
||||
pi.sensor_location if pi else None,
|
||||
ev.sample_rate,
|
||||
ev.record_type,
|
||||
blob,
|
||||
),
|
||||
)
|
||||
inserted += 1
|
||||
@@ -387,6 +408,23 @@ class SeismoDb:
|
||||
)
|
||||
return cur.rowcount > 0
|
||||
|
||||
def get_event_waveform(self, event_id: str) -> tuple[bool, Optional[str]]:
|
||||
"""
|
||||
Return (found, waveform_blob) for a given event UUID.
|
||||
|
||||
found=False means the event row doesn't exist.
|
||||
found=True, blob=None means the event exists but has no stored waveform
|
||||
(e.g. downloaded before waveform storage was implemented).
|
||||
found=True, blob=<str> means the full waveform JSON is available.
|
||||
"""
|
||||
with self._connect() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT waveform_blob FROM events WHERE id = ?", (event_id,)
|
||||
).fetchone()
|
||||
if row is None:
|
||||
return False, None
|
||||
return True, row["waveform_blob"]
|
||||
|
||||
# ── Monitor log ───────────────────────────────────────────────────────────
|
||||
|
||||
def insert_monitor_log(
|
||||
|
||||
Reference in New Issue
Block a user