"""Vessel AIS models for live maritime tracking.""" from sqlalchemy import BigInteger, Column, DateTime, Float, Index, Integer, SmallInteger, String from sqlalchemy.sql import func from app.core.time import to_iso8601_utc from app.db.session import Base class VesselStatic(Base): """Slow-changing vessel identity and dimensions.""" __tablename__ = "vessel_static" mmsi = Column(BigInteger, primary_key=True) name = Column(String(128), nullable=True) callsign = Column(String(16), nullable=True) vessel_type = Column(SmallInteger, nullable=True, index=True) vessel_type_name = Column(String(64), nullable=True, index=True) flag = Column(String(4), nullable=True, index=True) length = Column(Float, nullable=True) width = Column(Float, nullable=True) draught = Column(Float, nullable=True) imo = Column(BigInteger, nullable=True) updated_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now()) def to_dict(self) -> dict: return { "mmsi": self.mmsi, "name": self.name, "callsign": self.callsign, "vessel_type": self.vessel_type, "vessel_type_name": self.vessel_type_name, "flag": self.flag, "length": self.length, "width": self.width, "draught": self.draught, "imo": self.imo, "updated_at": to_iso8601_utc(self.updated_at), } class VesselPosition(Base): """Append-only AIS positions retained for short history windows.""" __tablename__ = "vessel_position" id = Column(Integer, primary_key=True, autoincrement=True) mmsi = Column(BigInteger, nullable=False, index=True) lat = Column(Float, nullable=False) lon = Column(Float, nullable=False) sog = Column(Float, nullable=True) cog = Column(Float, nullable=True) heading = Column(SmallInteger, nullable=True) nav_status = Column(SmallInteger, nullable=True, index=True) received_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now(), index=True) __table_args__ = ( Index("idx_vessel_pos_mmsi_time", "mmsi", "received_at"), Index("idx_vessel_pos_time", "received_at"), Index("idx_vessel_pos_lat_lon", "lat", "lon"), ) def to_dict(self) -> dict: return { "id": self.id, "mmsi": self.mmsi, "lat": self.lat, "lon": self.lon, "sog": self.sog, "cog": self.cog, "heading": self.heading, "nav_status": self.nav_status, "received_at": to_iso8601_utc(self.received_at), }