"""Vessel enrichment cache tables (v5). Profile and media enrichment are stored separately so cache TTLs can differ and so the conflict-resolution + display layers can read either independently. """ from sqlalchemy import BigInteger, Column, DateTime, Float, JSON, String from sqlalchemy.sql import func from app.core.time import to_iso8601_utc from app.db.session import Base class VesselProfileEnrichment(Base): """Cached static vessel profile (type, flag, dimensions, operator, etc.).""" __tablename__ = "vessel_profile_enrichment" mmsi = Column(BigInteger, primary_key=True) source = Column(String(100), nullable=False, default="system") payload = Column(JSON, nullable=False, default=dict) fetched_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now()) expires_at = Column(DateTime(timezone=True), nullable=True) confidence = Column(Float, nullable=True) reference_url = Column(String(500), nullable=True) updated_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now()) def to_dict(self) -> dict: return { "mmsi": self.mmsi, "source": self.source, "payload": self.payload or {}, "fetched_at": to_iso8601_utc(self.fetched_at), "expires_at": to_iso8601_utc(self.expires_at), "confidence": self.confidence, "reference_url": self.reference_url, } class VesselMediaEnrichment(Base): """Cached vessel imagery / external detail references.""" __tablename__ = "vessel_media_enrichment" mmsi = Column(BigInteger, primary_key=True) source = Column(String(100), nullable=False, default="system") payload = Column(JSON, nullable=False, default=dict) fetched_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now()) expires_at = Column(DateTime(timezone=True), nullable=True) confidence = Column(Float, nullable=True) reference_url = Column(String(500), nullable=True) updated_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now()) def to_dict(self) -> dict: return { "mmsi": self.mmsi, "source": self.source, "payload": self.payload or {}, "fetched_at": to_iso8601_utc(self.fetched_at), "expires_at": to_iso8601_utc(self.expires_at), "confidence": self.confidence, "reference_url": self.reference_url, }