0.4.2 - Early implementation of SLMs. WIP.

This commit is contained in:
serversdwn
2026-01-06 07:50:58 +00:00
parent 96cb27ef83
commit 4d74eda65f
36 changed files with 4211 additions and 12 deletions

120
scripts/README.md Normal file
View File

@@ -0,0 +1,120 @@
# Helper Scripts
This directory contains helper scripts for database management and testing.
## Database Migration Scripts
### migrate_dev_db.py
Migrates the DEV database schema to add SLM-specific columns to the `roster` table.
**Usage:**
```bash
cd /home/serversdown/sfm/seismo-fleet-manager
python3 scripts/migrate_dev_db.py
```
**What it does:**
- Adds 8 SLM-specific columns to the DEV database (data-dev/seismo_fleet.db)
- Columns: slm_host, slm_tcp_port, slm_model, slm_serial_number, slm_frequency_weighting, slm_time_weighting, slm_measurement_range, slm_last_check
- Safe to run multiple times (skips existing columns)
### update_dev_db_schema.py
Inspects and displays the DEV database schema.
**Usage:**
```bash
python3 scripts/update_dev_db_schema.py
```
**What it does:**
- Shows all tables in the DEV database
- Lists all columns in the roster table
- Useful for verifying schema after migrations
## Test Data Scripts
### add_test_slms.py
Adds test Sound Level Meter units to the DEV database.
**Usage:**
```bash
python3 scripts/add_test_slms.py
```
**What it creates:**
- nl43-001: NL-43 SLM at Construction Site A
- nl43-002: NL-43 SLM at Construction Site B
- nl53-001: NL-53 SLM at Residential Area
- nl43-003: NL-43 SLM (not deployed, spare unit)
### add_test_modems.py
Adds test modem units to the DEV database and assigns them to SLMs.
**Usage:**
```bash
python3 scripts/add_test_modems.py
```
**What it creates:**
- modem-001, modem-002, modem-003: Deployed modems (Raven XTV and Sierra Wireless)
- modem-004: Spare modem (not deployed)
**Modem assignments:**
- nl43-001 → modem-001
- nl43-002 → modem-002
- nl53-001 → modem-003
## Cleanup Scripts
### remove_test_data_from_prod.py
**⚠️ PRODUCTION DATABASE CLEANUP**
Removes test data from the production database (data/seismo_fleet.db).
**Status:** Already executed successfully. Production database is clean.
**What it removed:**
- All test SLM units (nl43-001, nl43-002, nl53-001, nl43-003)
- All test modem units (modem-001, modem-002, modem-003, modem-004)
## Database Cloning
### clone_db_to_dev.py
Clones the production database to create/update the DEV database.
**Usage:**
```bash
python3 scripts/clone_db_to_dev.py
```
**What it does:**
- Copies data/seismo_fleet.db → data-dev/seismo_fleet.db
- Useful for syncing DEV database with production schema/data
## Setup Sequence
To set up a fresh DEV database with test data:
```bash
cd /home/serversdown/sfm/seismo-fleet-manager
# 1. Fix permissions (if needed)
sudo chown -R serversdown:serversdown data-dev/
# 2. Migrate schema
python3 scripts/migrate_dev_db.py
# 3. Add test data
python3 scripts/add_test_slms.py
python3 scripts/add_test_modems.py
# 4. Verify
sqlite3 data-dev/seismo_fleet.db "SELECT id, device_type FROM roster WHERE device_type IN ('sound_level_meter', 'modem');"
```
## Important Notes
- **DEV Database**: `data-dev/seismo_fleet.db` - Used for development and testing
- **Production Database**: `data/seismo_fleet.db` - Used by the running application
- All test scripts are configured to use the DEV database only
- Never run test data scripts against production

View File

@@ -0,0 +1,39 @@
#!/usr/bin/env python3
"""
Add slm_ftp_port column to roster table for FTP data retrieval port
"""
from sqlalchemy import create_engine, text
import os
# Determine database based on environment
ENVIRONMENT = os.getenv("ENVIRONMENT", "production")
if ENVIRONMENT == "development":
DB_URL = "sqlite:///./data-dev/seismo_fleet.db"
else:
DB_URL = "sqlite:///./data/seismo_fleet.db"
def add_ftp_port_column():
print(f"Adding slm_ftp_port column to {DB_URL}...")
print("=" * 60)
engine = create_engine(DB_URL, connect_args={"check_same_thread": False})
with engine.connect() as conn:
try:
# Try to add the column
conn.execute(text("ALTER TABLE roster ADD COLUMN slm_ftp_port INTEGER"))
conn.commit()
print("✓ Added column: slm_ftp_port (INTEGER)")
except Exception as e:
if "duplicate column name" in str(e).lower():
print(" Column slm_ftp_port already exists, skipping")
else:
print(f"✗ Error adding slm_ftp_port: {e}")
raise
print("=" * 60)
print("Migration completed!")
if __name__ == "__main__":
add_ftp_port_column()

105
scripts/add_test_modems.py Executable file
View File

@@ -0,0 +1,105 @@
#!/usr/bin/env python3
"""
Add test modem units and assign them to SLMs in DEV database
"""
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from backend.models import RosterUnit
from datetime import datetime
# DEV database
DEV_DB_URL = "sqlite:///./data-dev/seismo_fleet.db"
def add_test_modems():
engine = create_engine(DEV_DB_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
db = SessionLocal()
# Test modems
test_modems = [
{
"id": "modem-001",
"device_type": "modem",
"deployed": True,
"retired": False,
"note": "Raven XTV modem for Construction Site A",
"address": "123 Main St, Construction Site A",
"coordinates": "34.0522,-118.2437",
"ip_address": "192.168.1.100",
"phone_number": "+1-555-0100",
"hardware_model": "Raven XTV"
},
{
"id": "modem-002",
"device_type": "modem",
"deployed": True,
"retired": False,
"note": "Raven XTV modem for Construction Site B",
"address": "456 Oak Ave, Construction Site B",
"coordinates": "34.0622,-118.2537",
"ip_address": "192.168.1.101",
"phone_number": "+1-555-0101",
"hardware_model": "Raven XTV"
},
{
"id": "modem-003",
"device_type": "modem",
"deployed": True,
"retired": False,
"note": "Sierra Wireless modem for Residential Area",
"address": "789 Elm St, Residential Area",
"coordinates": "34.0722,-118.2637",
"ip_address": "192.168.1.102",
"phone_number": "+1-555-0102",
"hardware_model": "Sierra Wireless AirLink"
},
{
"id": "modem-004",
"device_type": "modem",
"deployed": False,
"retired": False,
"note": "Spare modem - not deployed",
"ip_address": None,
"phone_number": "+1-555-0103",
"hardware_model": "Raven XTV"
}
]
for modem_data in test_modems:
# Check if modem already exists
existing = db.query(RosterUnit).filter_by(id=modem_data["id"]).first()
if existing:
print(f"Modem {modem_data['id']} already exists, skipping...")
continue
modem = RosterUnit(**modem_data)
db.add(modem)
print(f"Added {modem_data['id']}")
# Assign modems to existing SLMs
slm_modem_assignments = {
"nl43-001": "modem-001",
"nl43-002": "modem-002",
"nl53-001": "modem-003"
}
for slm_id, modem_id in slm_modem_assignments.items():
slm = db.query(RosterUnit).filter_by(id=slm_id).first()
if slm:
slm.deployed_with_modem_id = modem_id
# Remove legacy slm_host since we're using modems now
slm.slm_host = None
print(f"Assigned {slm_id} to {modem_id}")
else:
print(f"SLM {slm_id} not found, skipping assignment...")
db.commit()
db.close()
print("\nTest modems added and assigned to SLMs successfully in DEV database (data-dev/seismo_fleet.db)!")
print("\nModem assignments:")
for slm_id, modem_id in slm_modem_assignments.items():
print(f" {slm_id}{modem_id}")
if __name__ == "__main__":
add_test_modems()

106
scripts/add_test_slms.py Executable file
View File

@@ -0,0 +1,106 @@
#!/usr/bin/env python3
"""
Add test Sound Level Meter units to the DEV database
"""
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from backend.models import RosterUnit
from datetime import datetime
# DEV database
DEV_DB_URL = "sqlite:///./data-dev/seismo_fleet.db"
def add_test_slms():
engine = create_engine(DEV_DB_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
db = SessionLocal()
test_slms = [
{
"id": "nl43-001",
"device_type": "sound_level_meter",
"deployed": True,
"retired": False,
"note": "Test unit at construction site A",
"address": "123 Main St, Construction Site A",
"coordinates": "34.0522,-118.2437",
"slm_host": "192.168.1.100",
"slm_tcp_port": 2255,
"slm_model": "NL-43",
"slm_serial_number": "123456",
"slm_frequency_weighting": "A",
"slm_time_weighting": "F",
"slm_measurement_range": "30-130 dB",
"slm_last_check": datetime.utcnow()
},
{
"id": "nl43-002",
"device_type": "sound_level_meter",
"deployed": True,
"retired": False,
"note": "Test unit at construction site B",
"address": "456 Oak Ave, Construction Site B",
"coordinates": "34.0622,-118.2537",
"slm_host": "192.168.1.101",
"slm_tcp_port": 2255,
"slm_model": "NL-43",
"slm_serial_number": "123457",
"slm_frequency_weighting": "A",
"slm_time_weighting": "S",
"slm_measurement_range": "30-130 dB",
"slm_last_check": datetime.utcnow()
},
{
"id": "nl53-001",
"device_type": "sound_level_meter",
"deployed": True,
"retired": False,
"note": "Test unit at residential monitoring",
"address": "789 Elm St, Residential Area",
"coordinates": "34.0722,-118.2637",
"slm_host": "192.168.1.102",
"slm_tcp_port": 2255,
"slm_model": "NL-53",
"slm_serial_number": "234567",
"slm_frequency_weighting": "C",
"slm_time_weighting": "F",
"slm_measurement_range": "25-140 dB",
"slm_last_check": datetime.utcnow()
},
{
"id": "nl43-003",
"device_type": "sound_level_meter",
"deployed": False,
"retired": False,
"note": "Benched for calibration",
"address": None,
"coordinates": None,
"slm_host": None,
"slm_tcp_port": None,
"slm_model": "NL-43",
"slm_serial_number": "123458",
"slm_frequency_weighting": "A",
"slm_time_weighting": "F",
"slm_measurement_range": "30-130 dB",
"slm_last_check": None
}
]
for slm_data in test_slms:
# Check if unit already exists
existing = db.query(RosterUnit).filter_by(id=slm_data["id"]).first()
if existing:
print(f"Unit {slm_data['id']} already exists, skipping...")
continue
unit = RosterUnit(**slm_data)
db.add(unit)
print(f"Added {slm_data['id']}")
db.commit()
db.close()
print("\nTest SLM units added successfully to DEV database (data-dev/seismo_fleet.db)!")
if __name__ == "__main__":
add_test_slms()

46
scripts/migrate_dev_db.py Normal file
View File

@@ -0,0 +1,46 @@
#!/usr/bin/env python3
"""
Migrate DEV database to add SLM-specific columns
"""
from sqlalchemy import create_engine, text
# DEV database
DEV_DB_URL = "sqlite:///./data-dev/seismo_fleet.db"
def migrate_dev_database():
print("Migrating DEV database to add SLM columns...")
print("=" * 60)
engine = create_engine(DEV_DB_URL, connect_args={"check_same_thread": False})
# SLM columns to add
slm_columns = [
("slm_host", "VARCHAR"),
("slm_tcp_port", "INTEGER"),
("slm_model", "VARCHAR"),
("slm_serial_number", "VARCHAR"),
("slm_frequency_weighting", "VARCHAR"),
("slm_time_weighting", "VARCHAR"),
("slm_measurement_range", "VARCHAR"),
("slm_last_check", "DATETIME"),
]
with engine.connect() as conn:
for column_name, column_type in slm_columns:
try:
# Try to add the column
conn.execute(text(f"ALTER TABLE roster ADD COLUMN {column_name} {column_type}"))
conn.commit()
print(f"✓ Added column: {column_name}")
except Exception as e:
if "duplicate column name" in str(e).lower():
print(f" Column {column_name} already exists, skipping")
else:
print(f"✗ Error adding {column_name}: {e}")
print("=" * 60)
print("DEV database migration completed!")
if __name__ == "__main__":
migrate_dev_database()

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env python3
"""
Remove test SLMs and modems from PRODUCTION database
"""
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from backend.models import RosterUnit
# PRODUCTION database
PROD_DB_URL = "sqlite:///./data/seismo_fleet.db"
def remove_test_data():
engine = create_engine(PROD_DB_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
db = SessionLocal()
# IDs to remove
test_slm_ids = ["nl43-001", "nl43-002", "nl53-001", "nl43-003"]
test_modem_ids = ["modem-001", "modem-002", "modem-003", "modem-004"]
all_test_ids = test_slm_ids + test_modem_ids
removed = []
for unit_id in all_test_ids:
unit = db.query(RosterUnit).filter_by(id=unit_id).first()
if unit:
db.delete(unit)
removed.append(unit_id)
print(f"Removed {unit_id}")
if removed:
db.commit()
print(f"\nRemoved {len(removed)} test units from PRODUCTION database")
else:
print("No test units found in production database")
db.close()
if __name__ == "__main__":
print("Removing test data from PRODUCTION database...")
print("=" * 60)
remove_test_data()
print("=" * 60)
print("Done! Production database is clean.")

View File

@@ -0,0 +1,39 @@
#!/usr/bin/env python3
"""
Update DEV database schema to match current models
"""
from sqlalchemy import create_engine, inspect
from backend.models import Base
# DEV database
DEV_DB_URL = "sqlite:///./data-dev/seismo_fleet.db"
def update_schema():
print("Updating DEV database schema...")
print("=" * 60)
engine = create_engine(DEV_DB_URL, connect_args={"check_same_thread": False})
# Create all tables (will update existing tables with new columns)
Base.metadata.create_all(bind=engine)
# Inspect to see what we have
inspector = inspect(engine)
tables = inspector.get_table_names()
print(f"Tables in DEV database: {tables}")
if 'roster' in tables:
columns = [col['name'] for col in inspector.get_columns('roster')]
print(f"\nColumns in roster table:")
for col in sorted(columns):
print(f" - {col}")
print("=" * 60)
print("DEV database schema updated successfully!")
print("\nNote: SQLite doesn't support ALTER COLUMN, so existing")
print("columns won't be modified, but new columns will be added.")
if __name__ == "__main__":
update_schema()