- Implemented a new API router for managing report templates, including endpoints for listing, creating, retrieving, updating, and deleting templates. - Added a new HTML partial for a unified SLM settings modal, allowing users to configure SLM settings with dynamic modem selection and FTP credentials. - Created a report preview page with an editable data table using jspreadsheet, enabling users to modify report details and download the report as an Excel file.
303 lines
13 KiB
Python
303 lines
13 KiB
Python
from sqlalchemy import Column, String, DateTime, Boolean, Text, Date, Integer
|
|
from datetime import datetime
|
|
from backend.database import Base
|
|
|
|
|
|
class Emitter(Base):
|
|
__tablename__ = "emitters"
|
|
|
|
id = Column(String, primary_key=True, index=True)
|
|
unit_type = Column(String, nullable=False)
|
|
last_seen = Column(DateTime, default=datetime.utcnow)
|
|
last_file = Column(String, nullable=False)
|
|
status = Column(String, nullable=False)
|
|
notes = Column(String, nullable=True)
|
|
|
|
|
|
class RosterUnit(Base):
|
|
"""
|
|
Roster table: represents our *intended assignment* of a unit.
|
|
This is editable from the GUI.
|
|
|
|
Supports multiple device types with type-specific fields:
|
|
- "seismograph" - Seismic monitoring devices (default)
|
|
- "modem" - Field modems and network equipment
|
|
- "slm" - Sound level meters (NL-43/NL-53)
|
|
"""
|
|
__tablename__ = "roster"
|
|
|
|
# Core fields (all device types)
|
|
id = Column(String, primary_key=True, index=True)
|
|
unit_type = Column(String, default="series3") # Backward compatibility
|
|
device_type = Column(String, default="seismograph") # "seismograph" | "modem" | "slm"
|
|
deployed = Column(Boolean, default=True)
|
|
retired = Column(Boolean, default=False)
|
|
note = Column(String, nullable=True)
|
|
project_id = Column(String, nullable=True)
|
|
location = Column(String, nullable=True) # Legacy field - use address/coordinates instead
|
|
address = Column(String, nullable=True) # Human-readable address
|
|
coordinates = Column(String, nullable=True) # Lat,Lon format: "34.0522,-118.2437"
|
|
last_updated = Column(DateTime, default=datetime.utcnow)
|
|
|
|
# Seismograph-specific fields (nullable for modems and SLMs)
|
|
last_calibrated = Column(Date, nullable=True)
|
|
next_calibration_due = Column(Date, nullable=True)
|
|
|
|
# Modem assignment (shared by seismographs and SLMs)
|
|
deployed_with_modem_id = Column(String, nullable=True) # FK to another RosterUnit (device_type=modem)
|
|
|
|
# Modem-specific fields (nullable for seismographs and SLMs)
|
|
ip_address = Column(String, nullable=True)
|
|
phone_number = Column(String, nullable=True)
|
|
hardware_model = Column(String, nullable=True)
|
|
|
|
# Sound Level Meter-specific fields (nullable for seismographs and modems)
|
|
slm_host = Column(String, nullable=True) # Device IP or hostname
|
|
slm_tcp_port = Column(Integer, nullable=True) # TCP control port (default 2255)
|
|
slm_ftp_port = Column(Integer, nullable=True) # FTP data retrieval port (default 21)
|
|
slm_model = Column(String, nullable=True) # NL-43, NL-53, etc.
|
|
slm_serial_number = Column(String, nullable=True) # Device serial number
|
|
slm_frequency_weighting = Column(String, nullable=True) # A, C, Z
|
|
slm_time_weighting = Column(String, nullable=True) # F (Fast), S (Slow), I (Impulse)
|
|
slm_measurement_range = Column(String, nullable=True) # e.g., "30-130 dB"
|
|
slm_last_check = Column(DateTime, nullable=True) # Last communication check
|
|
|
|
|
|
class IgnoredUnit(Base):
|
|
"""
|
|
Ignored units: units that report but should be filtered out from unknown emitters.
|
|
Used to suppress noise from old projects.
|
|
"""
|
|
__tablename__ = "ignored_units"
|
|
|
|
id = Column(String, primary_key=True, index=True)
|
|
reason = Column(String, nullable=True)
|
|
ignored_at = Column(DateTime, default=datetime.utcnow)
|
|
|
|
|
|
class UnitHistory(Base):
|
|
"""
|
|
Unit history: complete timeline of changes to each unit.
|
|
Tracks note changes, status changes, deployment/benched events, and more.
|
|
"""
|
|
__tablename__ = "unit_history"
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
unit_id = Column(String, nullable=False, index=True) # FK to RosterUnit.id
|
|
change_type = Column(String, nullable=False) # note_change, deployed_change, retired_change, etc.
|
|
field_name = Column(String, nullable=True) # Which field changed
|
|
old_value = Column(Text, nullable=True) # Previous value
|
|
new_value = Column(Text, nullable=True) # New value
|
|
changed_at = Column(DateTime, default=datetime.utcnow, nullable=False, index=True)
|
|
source = Column(String, default="manual") # manual, csv_import, telemetry, offline_sync
|
|
notes = Column(Text, nullable=True) # Optional reason/context for the change
|
|
|
|
|
|
class UserPreferences(Base):
|
|
"""
|
|
User preferences: persistent storage for application settings.
|
|
Single-row table (id=1) to store global user preferences.
|
|
"""
|
|
__tablename__ = "user_preferences"
|
|
|
|
id = Column(Integer, primary_key=True, default=1)
|
|
timezone = Column(String, default="America/New_York")
|
|
theme = Column(String, default="auto") # auto, light, dark
|
|
auto_refresh_interval = Column(Integer, default=10) # seconds
|
|
date_format = Column(String, default="MM/DD/YYYY")
|
|
table_rows_per_page = Column(Integer, default=25)
|
|
calibration_interval_days = Column(Integer, default=365)
|
|
calibration_warning_days = Column(Integer, default=30)
|
|
status_ok_threshold_hours = Column(Integer, default=12)
|
|
status_pending_threshold_hours = Column(Integer, default=24)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
|
|
# ============================================================================
|
|
# Project Management System
|
|
# ============================================================================
|
|
|
|
class ProjectType(Base):
|
|
"""
|
|
Project type templates: defines available project types and their capabilities.
|
|
Pre-populated with: sound_monitoring, vibration_monitoring, combined.
|
|
"""
|
|
__tablename__ = "project_types"
|
|
|
|
id = Column(String, primary_key=True) # sound_monitoring, vibration_monitoring, combined
|
|
name = Column(String, nullable=False, unique=True) # "Sound Monitoring", "Vibration Monitoring"
|
|
description = Column(Text, nullable=True)
|
|
icon = Column(String, nullable=True) # Icon identifier for UI
|
|
supports_sound = Column(Boolean, default=False) # Enables SLM features
|
|
supports_vibration = Column(Boolean, default=False) # Enables seismograph features
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
|
|
|
|
class Project(Base):
|
|
"""
|
|
Projects: top-level organization for monitoring work.
|
|
Type-aware to enable/disable features based on project_type_id.
|
|
"""
|
|
__tablename__ = "projects"
|
|
|
|
id = Column(String, primary_key=True, index=True) # UUID
|
|
name = Column(String, nullable=False, unique=True)
|
|
description = Column(Text, nullable=True)
|
|
project_type_id = Column(String, nullable=False) # FK to ProjectType.id
|
|
status = Column(String, default="active") # active, completed, archived
|
|
|
|
# Project metadata
|
|
client_name = Column(String, nullable=True)
|
|
site_address = Column(String, nullable=True)
|
|
site_coordinates = Column(String, nullable=True) # "lat,lon"
|
|
start_date = Column(Date, nullable=True)
|
|
end_date = Column(Date, nullable=True)
|
|
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
|
|
class MonitoringLocation(Base):
|
|
"""
|
|
Monitoring locations: generic location for monitoring activities.
|
|
Can be NRL (Noise Recording Location) for sound projects,
|
|
or monitoring point for vibration projects.
|
|
"""
|
|
__tablename__ = "monitoring_locations"
|
|
|
|
id = Column(String, primary_key=True, index=True) # UUID
|
|
project_id = Column(String, nullable=False, index=True) # FK to Project.id
|
|
location_type = Column(String, nullable=False) # "sound" | "vibration"
|
|
|
|
name = Column(String, nullable=False) # NRL-001, VP-North, etc.
|
|
description = Column(Text, nullable=True)
|
|
coordinates = Column(String, nullable=True) # "lat,lon"
|
|
address = Column(String, nullable=True)
|
|
|
|
# Type-specific metadata stored as JSON
|
|
# For sound: {"ambient_conditions": "urban", "expected_sources": ["traffic"]}
|
|
# For vibration: {"ground_type": "bedrock", "depth": "10m"}
|
|
location_metadata = Column(Text, nullable=True)
|
|
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
|
|
class UnitAssignment(Base):
|
|
"""
|
|
Unit assignments: links devices (SLMs or seismographs) to monitoring locations.
|
|
Supports temporary assignments with assigned_until.
|
|
"""
|
|
__tablename__ = "unit_assignments"
|
|
|
|
id = Column(String, primary_key=True, index=True) # UUID
|
|
unit_id = Column(String, nullable=False, index=True) # FK to RosterUnit.id
|
|
location_id = Column(String, nullable=False, index=True) # FK to MonitoringLocation.id
|
|
|
|
assigned_at = Column(DateTime, default=datetime.utcnow)
|
|
assigned_until = Column(DateTime, nullable=True) # Null = indefinite
|
|
status = Column(String, default="active") # active, completed, cancelled
|
|
notes = Column(Text, nullable=True)
|
|
|
|
# Denormalized for efficient queries
|
|
device_type = Column(String, nullable=False) # "slm" | "seismograph"
|
|
project_id = Column(String, nullable=False, index=True) # FK to Project.id
|
|
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
|
|
|
|
class ScheduledAction(Base):
|
|
"""
|
|
Scheduled actions: automation for recording start/stop/download.
|
|
Terra-View executes these by calling SLMM or SFM endpoints.
|
|
"""
|
|
__tablename__ = "scheduled_actions"
|
|
|
|
id = Column(String, primary_key=True, index=True) # UUID
|
|
project_id = Column(String, nullable=False, index=True) # FK to Project.id
|
|
location_id = Column(String, nullable=False, index=True) # FK to MonitoringLocation.id
|
|
unit_id = Column(String, nullable=True, index=True) # FK to RosterUnit.id (nullable if location-based)
|
|
|
|
action_type = Column(String, nullable=False) # start, stop, download, calibrate
|
|
device_type = Column(String, nullable=False) # "slm" | "seismograph"
|
|
|
|
scheduled_time = Column(DateTime, nullable=False, index=True)
|
|
executed_at = Column(DateTime, nullable=True)
|
|
execution_status = Column(String, default="pending") # pending, completed, failed, cancelled
|
|
|
|
# Response from device module (SLMM or SFM)
|
|
module_response = Column(Text, nullable=True) # JSON
|
|
error_message = Column(Text, nullable=True)
|
|
|
|
notes = Column(Text, nullable=True)
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
|
|
|
|
class RecordingSession(Base):
|
|
"""
|
|
Recording sessions: tracks actual monitoring sessions.
|
|
Created when recording starts, updated when it stops.
|
|
"""
|
|
__tablename__ = "recording_sessions"
|
|
|
|
id = Column(String, primary_key=True, index=True) # UUID
|
|
project_id = Column(String, nullable=False, index=True) # FK to Project.id
|
|
location_id = Column(String, nullable=False, index=True) # FK to MonitoringLocation.id
|
|
unit_id = Column(String, nullable=False, index=True) # FK to RosterUnit.id
|
|
|
|
session_type = Column(String, nullable=False) # sound | vibration
|
|
started_at = Column(DateTime, nullable=False)
|
|
stopped_at = Column(DateTime, nullable=True)
|
|
duration_seconds = Column(Integer, nullable=True)
|
|
status = Column(String, default="recording") # recording, completed, failed
|
|
|
|
# Snapshot of device configuration at recording time
|
|
session_metadata = Column(Text, nullable=True) # JSON
|
|
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
|
|
class DataFile(Base):
|
|
"""
|
|
Data files: references to recorded data files.
|
|
Terra-View tracks file metadata; actual files stored in data/Projects/ directory.
|
|
"""
|
|
__tablename__ = "data_files"
|
|
|
|
id = Column(String, primary_key=True, index=True) # UUID
|
|
session_id = Column(String, nullable=False, index=True) # FK to RecordingSession.id
|
|
|
|
file_path = Column(String, nullable=False) # Relative to data/Projects/
|
|
file_type = Column(String, nullable=False) # wav, csv, mseed, json
|
|
file_size_bytes = Column(Integer, nullable=True)
|
|
downloaded_at = Column(DateTime, nullable=True)
|
|
checksum = Column(String, nullable=True) # SHA256 or MD5
|
|
|
|
# Additional file metadata
|
|
file_metadata = Column(Text, nullable=True) # JSON
|
|
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
|
|
|
|
class ReportTemplate(Base):
|
|
"""
|
|
Report templates: saved configurations for generating Excel reports.
|
|
Allows users to save time filter presets, titles, etc. for reuse.
|
|
"""
|
|
__tablename__ = "report_templates"
|
|
|
|
id = Column(String, primary_key=True, index=True) # UUID
|
|
name = Column(String, nullable=False) # "Nighttime Report", "Full Day Report"
|
|
project_id = Column(String, nullable=True) # Optional: project-specific template
|
|
|
|
# Template settings
|
|
report_title = Column(String, default="Background Noise Study")
|
|
start_time = Column(String, nullable=True) # "19:00" format
|
|
end_time = Column(String, nullable=True) # "07:00" format
|
|
start_date = Column(String, nullable=True) # "2025-01-15" format (optional)
|
|
end_date = Column(String, nullable=True) # "2025-01-20" format (optional)
|
|
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|