From db8d666aa1d3150943f30f4158af16f711e10f54 Mon Sep 17 00:00:00 2001
From: serversdown
Date: Fri, 29 May 2026 00:56:41 +0000
Subject: [PATCH] settings: add mic_unit_pref for event-report chart
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
New UserPreferences field controls the mic channel's unit on the
SFM event-detail modal's waveform chart only. "dBL" default,
"psi" alternate. Peaks everywhere else (tables, KPI tiles, modal
summary) stay in dBL regardless — this is strictly a chart-axis
preference.
Surfaced as a single dropdown on Settings → General, below the
auto-refresh interval.
Setting up the storage half ahead of the chart port in the next
commit, so the chart can read the value from /api/settings/preferences
on first render instead of needing a follow-up wiring pass.
Includes idempotent backend/migrate_add_mic_unit_pref.py for fleets
already on an older schema.
Co-Authored-By: Claude Opus 4.7 (1M context)
---
backend/migrate_add_mic_unit_pref.py | 56 ++++++++++++++++++++++++++++
backend/models.py | 3 ++
backend/routers/settings.py | 3 ++
templates/settings.html | 22 ++++++++++-
4 files changed, 83 insertions(+), 1 deletion(-)
create mode 100644 backend/migrate_add_mic_unit_pref.py
diff --git a/backend/migrate_add_mic_unit_pref.py b/backend/migrate_add_mic_unit_pref.py
new file mode 100644
index 0000000..b471949
--- /dev/null
+++ b/backend/migrate_add_mic_unit_pref.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+"""
+Database migration: Add mic_unit_pref column to user_preferences.
+
+Adds a single field controlling the mic channel's unit on the event-
+report waveform chart in the SFM event detail modal. "dBL" (default)
+or "psi". Peaks and KPI tiles elsewhere are always dBL regardless.
+
+Idempotent — safe to re-run.
+"""
+
+import sqlite3
+from pathlib import Path
+
+
+def migrate():
+ possible_paths = [
+ Path("data/seismo_fleet.db"),
+ Path("data/sfm.db"),
+ Path("data/seismo.db"),
+ ]
+ db_path = next((p for p in possible_paths if p.exists()), None)
+ if db_path is None:
+ print(f"Database not found in any of: {[str(p) for p in possible_paths]}")
+ print("Will be created with the new column when models.py initialises.")
+ return
+
+ print(f"Using database: {db_path}")
+ conn = sqlite3.connect(db_path)
+ cur = conn.cursor()
+
+ cur.execute("PRAGMA table_info(user_preferences)")
+ existing = {row[1] for row in cur.fetchall()}
+
+ if "mic_unit_pref" in existing:
+ print("mic_unit_pref already exists — nothing to do.")
+ conn.close()
+ return
+
+ cur.execute(
+ "ALTER TABLE user_preferences "
+ "ADD COLUMN mic_unit_pref TEXT DEFAULT 'dBL'"
+ )
+ # Backfill the single row that should exist (id=1) to the default,
+ # in case the column ends up NULL on existing rows.
+ cur.execute(
+ "UPDATE user_preferences SET mic_unit_pref = 'dBL' "
+ "WHERE mic_unit_pref IS NULL"
+ )
+ conn.commit()
+ conn.close()
+ print("Added mic_unit_pref to user_preferences (default 'dBL').")
+
+
+if __name__ == "__main__":
+ migrate()
diff --git a/backend/models.py b/backend/models.py
index 3ba1e11..5be50ea 100644
--- a/backend/models.py
+++ b/backend/models.py
@@ -135,6 +135,9 @@ class UserPreferences(Base):
calibration_warning_days = Column(Integer, default=30)
status_ok_threshold_hours = Column(Integer, default=12)
status_pending_threshold_hours = Column(Integer, default=24)
+ # Mic display units on the event-report waveform chart only — peaks
+ # and KPI tiles elsewhere are always dBL. "dBL" (default) or "psi".
+ mic_unit_pref = Column(String, default="dBL")
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
diff --git a/backend/routers/settings.py b/backend/routers/settings.py
index e32f4d6..3b01e70 100644
--- a/backend/routers/settings.py
+++ b/backend/routers/settings.py
@@ -267,6 +267,7 @@ class PreferencesUpdate(BaseModel):
calibration_warning_days: Optional[int] = None
status_ok_threshold_hours: Optional[int] = None
status_pending_threshold_hours: Optional[int] = None
+ mic_unit_pref: Optional[str] = None
@router.get("/preferences")
@@ -293,6 +294,7 @@ def get_preferences(db: Session = Depends(get_db)):
"calibration_warning_days": prefs.calibration_warning_days,
"status_ok_threshold_hours": prefs.status_ok_threshold_hours,
"status_pending_threshold_hours": prefs.status_pending_threshold_hours,
+ "mic_unit_pref": prefs.mic_unit_pref or "dBL",
"updated_at": prefs.updated_at.isoformat() if prefs.updated_at else None
}
@@ -334,6 +336,7 @@ def update_preferences(
"calibration_warning_days": prefs.calibration_warning_days,
"status_ok_threshold_hours": prefs.status_ok_threshold_hours,
"status_pending_threshold_hours": prefs.status_pending_threshold_hours,
+ "mic_unit_pref": prefs.mic_unit_pref or "dBL",
"updated_at": prefs.updated_at.isoformat() if prefs.updated_at else None
}
diff --git a/templates/settings.html b/templates/settings.html
index ba5532e..662a70e 100644
--- a/templates/settings.html
+++ b/templates/settings.html
@@ -122,6 +122,21 @@
How often the dashboard should refresh automatically
+
+
+
+
+
+
+ Applies only to the waveform chart inside the event detail modal. Peak values everywhere else (tables, KPIs, modal summary) stay in dB(L) regardless.
+