feat(db): zip recent events' waveform files
This commit is contained in:
@@ -6,6 +6,7 @@ unit-testable without HTTP.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
import zipfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
@@ -22,3 +23,28 @@ def snapshot_db(src_path: Path, dest_path: Path) -> Path:
|
|||||||
src.close()
|
src.close()
|
||||||
dest.close()
|
dest.close()
|
||||||
return Path(dest_path)
|
return Path(dest_path)
|
||||||
|
|
||||||
|
|
||||||
|
def build_recent_waveforms_zip(db, store, n: int, dest_path: Path) -> dict:
|
||||||
|
"""Zip every on-disk file (event file + .h5/.a5.pkl/.sfm.json siblings) for
|
||||||
|
the n most-recent events, arcname '<serial>/<filename>'. Events with no
|
||||||
|
resolvable files on disk are skipped. Returns {'events': int, 'files': int}."""
|
||||||
|
rows = db.query_events(limit=n, offset=0)
|
||||||
|
events = 0
|
||||||
|
files = 0
|
||||||
|
with zipfile.ZipFile(dest_path, "w", compression=zipfile.ZIP_DEFLATED) as zf:
|
||||||
|
for row in rows:
|
||||||
|
serial = row.get("serial")
|
||||||
|
fn = row.get("blastware_filename")
|
||||||
|
if not serial or not fn:
|
||||||
|
continue
|
||||||
|
serial_dir = store.root / serial
|
||||||
|
matched = sorted(serial_dir.glob(f"{fn}*")) if serial_dir.is_dir() else []
|
||||||
|
matched = [p for p in matched if p.is_file()]
|
||||||
|
if not matched:
|
||||||
|
continue
|
||||||
|
events += 1
|
||||||
|
for p in matched:
|
||||||
|
zf.write(p, arcname=f"{serial}/{p.name}")
|
||||||
|
files += 1
|
||||||
|
return {"events": events, "files": files}
|
||||||
|
|||||||
@@ -45,3 +45,37 @@ def test_snapshot_db_produces_queryable_copy(tmp_path):
|
|||||||
n = conn.execute("SELECT COUNT(*) FROM t").fetchone()[0]
|
n = conn.execute("SELECT COUNT(*) FROM t").fetchone()[0]
|
||||||
conn.close()
|
conn.close()
|
||||||
assert n == 5
|
assert n == 5
|
||||||
|
|
||||||
|
|
||||||
|
def test_recent_waveforms_zip_bundles_all_per_event_files(tmp_path):
|
||||||
|
import zipfile
|
||||||
|
from sfm.database import SeismoDb
|
||||||
|
from sfm.waveform_store import WaveformStore
|
||||||
|
from sfm.db_snapshot import build_recent_waveforms_zip
|
||||||
|
|
||||||
|
db = SeismoDb(tmp_path / "seismo_relay.db") # creates schema on init
|
||||||
|
store = WaveformStore(tmp_path / "waveforms")
|
||||||
|
|
||||||
|
def add(serial, fn, ts):
|
||||||
|
d = store.root / serial
|
||||||
|
d.mkdir(parents=True, exist_ok=True)
|
||||||
|
for suffix in ("", ".h5", ".a5.pkl", ".sfm.json"):
|
||||||
|
(d / f"{fn}{suffix}").write_bytes(b"data")
|
||||||
|
_seed_event(db.db_path, serial, fn, ts)
|
||||||
|
|
||||||
|
add("BE111", "AAAA1111.OE0H", "2026-06-01T00:00:00")
|
||||||
|
add("BE111", "BBBB2222.OE0H", "2026-06-02T00:00:00") # newest
|
||||||
|
|
||||||
|
dest = tmp_path / "wf.zip"
|
||||||
|
stats = build_recent_waveforms_zip(db, store, n=1, dest_path=dest)
|
||||||
|
|
||||||
|
assert stats["events"] == 1
|
||||||
|
assert stats["files"] == 4
|
||||||
|
with zipfile.ZipFile(dest) as zf:
|
||||||
|
names = set(zf.namelist())
|
||||||
|
assert names == {
|
||||||
|
"BE111/BBBB2222.OE0H",
|
||||||
|
"BE111/BBBB2222.OE0H.h5",
|
||||||
|
"BE111/BBBB2222.OE0H.a5.pkl",
|
||||||
|
"BE111/BBBB2222.OE0H.sfm.json",
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user