from sqlalchemy import Column, String, Integer, Float, DateTime, Text, ForeignKey, JSON from sqlalchemy.dialects.postgresql import UUID, ARRAY from sqlalchemy.sql import func from pgvector.sqlalchemy import Vector import uuid from app.core.database import Base class PersonIdentity(Base): """ Unique person identity — grouped by face embedding similarity. All face events linked to this identity let us trace where this person has been. """ __tablename__ = "person_identities" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) name = Column(String(255), nullable=True) # e.g. "Juan Dela Cruz" if registered label = Column(String(100), nullable=True) # e.g. "Unit 3A Resident", "Delivery Rider" notes = Column(Text, nullable=True) is_registered = Column(Integer, default=0) # 1 = manually registered with name is_watchlisted = Column(Integer, default=0) # 1 = flagged for alerts thumbnail_path = Column(Text, nullable=True) # Best face shot # Face embedding (512-dim for InsightFace buffalo_l) embedding = Column(Vector(512), nullable=True) # Stats first_seen_at = Column(DateTime(timezone=True), nullable=True) last_seen_at = Column(DateTime(timezone=True), nullable=True) total_sightings = Column(Integer, default=0) created_at = Column(DateTime(timezone=True), server_default=func.now()) def __repr__(self): return f"" class FaceEvent(Base): """ Every face detection event — one row per detected face per frame. """ __tablename__ = "face_events" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) camera_id = Column(UUID(as_uuid=True), ForeignKey("cameras.id"), nullable=False, index=True) identity_id = Column(UUID(as_uuid=True), ForeignKey("person_identities.id"), nullable=True, index=True) # Detection detection_confidence = Column(Float, nullable=True) face_bbox = Column(JSON, nullable=True) # {"x","y","w","h"} face_embedding = Column(Vector(512), nullable=True) # Per-event embedding for re-clustering similarity_score = Column(Float, nullable=True) # Match score to identity # Context — if detected alongside a vehicle event vehicle_event_id = Column(UUID(as_uuid=True), ForeignKey("vehicle_events.id"), nullable=True) # Storage snapshot_path = Column(Text, nullable=True) # Full frame face_snapshot_path = Column(Text, nullable=True) # Cropped face chip # Meta captured_at = Column(DateTime(timezone=True), server_default=func.now(), index=True) def __repr__(self): return f""