from datetime import datetime from typing import Optional from sqlalchemy import String, Float, Integer, Boolean, DateTime, JSON, ForeignKey from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship class Base(DeclarativeBase): pass class FlightModel(Base): __tablename__ = "flights" id: Mapped[str] = mapped_column(String(36), primary_key=True) name: Mapped[str] = mapped_column(String(255)) description: Mapped[str] = mapped_column(String(1000)) start_lat: Mapped[float] = mapped_column(Float) start_lon: Mapped[float] = mapped_column(Float) altitude: Mapped[float] = mapped_column(Float) camera_params: Mapped[dict] = mapped_column(JSON) geofences: Mapped[dict] = mapped_column(JSON) status: Mapped[str] = mapped_column(String(50), default="created") frames_processed: Mapped[int] = mapped_column(Integer, default=0) frames_total: Mapped[int] = mapped_column(Integer, default=0) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) waypoints: Mapped[list["WaypointModel"]] = relationship(back_populates="flight", cascade="all, delete-orphan") frame_results: Mapped[list["FrameResultModel"]] = relationship(back_populates="flight", cascade="all, delete-orphan") chunks: Mapped[list["ChunkModel"]] = relationship(back_populates="flight", cascade="all, delete-orphan") class WaypointModel(Base): __tablename__ = "waypoints" id: Mapped[str] = mapped_column(String(36), primary_key=True) flight_id: Mapped[str] = mapped_column(String(36), ForeignKey("flights.id")) lat: Mapped[float] = mapped_column(Float) lon: Mapped[float] = mapped_column(Float) altitude: Mapped[Optional[float]] = mapped_column(Float, nullable=True) confidence: Mapped[float] = mapped_column(Float, default=0.0) refined: Mapped[bool] = mapped_column(Boolean, default=False) timestamp: Mapped[datetime] = mapped_column(DateTime) flight: Mapped["FlightModel"] = relationship(back_populates="waypoints") class FrameResultModel(Base): __tablename__ = "frame_results" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) flight_id: Mapped[str] = mapped_column(String(36), ForeignKey("flights.id")) frame_id: Mapped[int] = mapped_column(Integer) gps_lat: Mapped[float] = mapped_column(Float) gps_lon: Mapped[float] = mapped_column(Float) altitude: Mapped[float] = mapped_column(Float) heading: Mapped[float] = mapped_column(Float) confidence: Mapped[float] = mapped_column(Float) refined: Mapped[bool] = mapped_column(Boolean, default=False) objects: Mapped[dict] = mapped_column(JSON, default=list) timestamp: Mapped[datetime] = mapped_column(DateTime) updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) flight: Mapped["FlightModel"] = relationship(back_populates="frame_results") class ChunkModel(Base): __tablename__ = "chunks" id: Mapped[str] = mapped_column(String(36), primary_key=True) flight_id: Mapped[str] = mapped_column(String(36), ForeignKey("flights.id")) start_frame_id: Mapped[int] = mapped_column(Integer) end_frame_id: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) frames: Mapped[list] = mapped_column(JSON, default=list) is_active: Mapped[bool] = mapped_column(Boolean, default=True) has_anchor: Mapped[bool] = mapped_column(Boolean, default=False) anchor_frame_id: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) anchor_lat: Mapped[Optional[float]] = mapped_column(Float, nullable=True) anchor_lon: Mapped[Optional[float]] = mapped_column(Float, nullable=True) matching_status: Mapped[str] = mapped_column(String(50), default="unanchored") created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) flight: Mapped["FlightModel"] = relationship(back_populates="chunks")