# File: src/database.py # Purpose: SQLAlchemy ORM models and session factory for PostgreSQL storage from datetime import datetime from sqlalchemy import ( create_engine, Column, Integer, String, Float, Text, DateTime, ForeignKey, JSON ) from sqlalchemy.orm import declarative_base, relationship, sessionmaker from config import DATABASE_URL engine = create_engine(DATABASE_URL, pool_pre_ping=True) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base() class Candidate(Base): __tablename__ = "candidates" id = Column(Integer, primary_key=True, index=True) name = Column(String(200), nullable=False) phone = Column(String(30), nullable=False) email = Column(String(200)) resume_text = Column(Text) created_at = Column(DateTime, default=datetime.utcnow) sessions = relationship("InterviewSession", back_populates="candidate") class InterviewSession(Base): __tablename__ = "interview_sessions" id = Column(Integer, primary_key=True, index=True) candidate_id = Column(Integer, ForeignKey("candidates.id"), nullable=False) call_sid = Column(String(60), unique=True) # Twilio CallSid status = Column(String(30), default="initiated") # initiated|active|completed|failed started_at = Column(DateTime, default=datetime.utcnow) ended_at = Column(DateTime) communication_score = Column(Float) technical_score = Column(Float) confidence_score = Column(Float) overall_score = Column(Float) recommendation = Column(String(100)) report_text = Column(Text) extracted_skills = Column(JSON) candidate = relationship("Candidate", back_populates="sessions") turns = relationship("ConversationTurn", back_populates="session") class ConversationTurn(Base): __tablename__ = "conversation_turns" id = Column(Integer, primary_key=True, index=True) session_id = Column(Integer, ForeignKey("interview_sessions.id"), nullable=False) turn_index = Column(Integer, nullable=False) speaker = Column(String(10), nullable=False) # "ai" | "candidate" text = Column(Text, nullable=False) audio_url = Column(String(500)) # S3/GCS path if stored timestamp = Column(DateTime, default=datetime.utcnow) session = relationship("InterviewSession", back_populates="turns") def init_db(): Base.metadata.create_all(bind=engine) def get_db(): db = SessionLocal() try: yield db finally: db.close()