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 (seismograph, modem) with type-specific fields. """ __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" 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) last_calibrated = Column(Date, nullable=True) next_calibration_due = Column(Date, nullable=True) deployed_with_modem_id = Column(String, nullable=True) # FK to another RosterUnit # Modem-specific fields (nullable for seismographs) ip_address = Column(String, nullable=True) phone_number = Column(String, nullable=True) hardware_model = Column(String, nullable=True) 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)