""" Modelo de Grabación para almacenar videos de cámaras. """ from datetime import datetime from sqlalchemy import ( DateTime, Float, ForeignKey, Index, Integer, String, Text, ) from sqlalchemy.orm import Mapped, mapped_column, relationship from app.core.database import Base from app.models.base import TimestampMixin class Grabacion(Base, TimestampMixin): """Modelo para almacenar grabaciones de video.""" __tablename__ = "grabaciones" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) # Relaciones camara_id: Mapped[int] = mapped_column( ForeignKey("camaras.id", ondelete="CASCADE"), nullable=False, index=True, ) vehiculo_id: Mapped[int] = mapped_column( ForeignKey("vehiculos.id", ondelete="CASCADE"), nullable=False, index=True, ) # Tiempo inicio_tiempo: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) fin_tiempo: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) duracion_segundos: Mapped[int | None] = mapped_column(Integer, nullable=True) # Archivo archivo_url: Mapped[str] = mapped_column(String(500), nullable=False) archivo_nombre: Mapped[str] = mapped_column(String(255), nullable=False) tamaño_mb: Mapped[float | None] = mapped_column(Float, nullable=True) formato: Mapped[str] = mapped_column(String(10), default="mp4", nullable=False) # mp4, webm, mkv resolucion: Mapped[str | None] = mapped_column(String(20), nullable=True) # Tipo de grabación tipo: Mapped[str] = mapped_column( String(50), default="continua", nullable=False, ) # continua, evento, manual, snapshot # Evento asociado (si es grabación por evento) evento_video_id: Mapped[int | None] = mapped_column( ForeignKey("eventos_video.id", ondelete="SET NULL"), nullable=True, ) # Ubicación al inicio de la grabación lat: Mapped[float | None] = mapped_column(Float, nullable=True) lng: Mapped[float | None] = mapped_column(Float, nullable=True) # Estado estado: Mapped[str] = mapped_column( String(20), default="disponible", nullable=False, ) # grabando, procesando, disponible, error, eliminado # Thumbnail thumbnail_url: Mapped[str | None] = mapped_column(String(500), nullable=True) # Notas notas: Mapped[str | None] = mapped_column(Text, nullable=True) # Relaciones ORM camara: Mapped["Camara"] = relationship( "Camara", back_populates="grabaciones", lazy="selectin", ) # Índices __table_args__ = ( Index("idx_grabaciones_camara_inicio", "camara_id", "inicio_tiempo"), Index("idx_grabaciones_vehiculo_inicio", "vehiculo_id", "inicio_tiempo"), Index("idx_grabaciones_tipo", "tipo"), ) @property def duracion_formateada(self) -> str: """Retorna la duración en formato legible.""" if not self.duracion_segundos: return "N/A" minutos = self.duracion_segundos // 60 segundos = self.duracion_segundos % 60 if minutos > 0: return f"{minutos}m {segundos}s" return f"{segundos}s" def __repr__(self) -> str: return f""