owenkaplinsky's picture
Update src/database/candidates/models.py
1b2ba1c verified
from sqlalchemy import (
Column,
String,
Float,
Text,
DateTime,
Enum,
ForeignKey,
JSON,
)
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import declarative_base, relationship
from datetime import datetime
import uuid
import secrets
import string
from src.state.candidate import CandidateStatus, InterviewStatus, DecisionStatus
Base = declarative_base()
def generate_auth_code() -> str:
"""Generate a 6-digit random authentication code.
"""
return "".join(
secrets.choice(string.digits) for _ in range(6)
)
# --- TABLES ---
class Candidate(Base):
__tablename__ = "candidates"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
full_name = Column(String, nullable=False)
email = Column(String, unique=True, nullable=False)
phone_number = Column(String)
cv_file_path = Column(String)
parsed_cv_file_path = Column(String)
status = Column(Enum(CandidateStatus), default=CandidateStatus.applied, nullable=False)
created_at = Column(DateTime, default=datetime.utcnow)
auth_code = Column(String, default=generate_auth_code, nullable=True)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
cv_screening_results = relationship(
"CVScreeningResult",
back_populates="candidate",
cascade="all, delete-orphan",
)
voice_screening_results = relationship(
"VoiceScreeningResult",
back_populates="candidate",
cascade="all, delete-orphan",
)
interview_scheduling = relationship(
"InterviewScheduling",
back_populates="candidate",
cascade="all, delete-orphan",
)
final_decision = relationship(
"FinalDecision",
back_populates="candidate",
uselist=False,
cascade="all, delete-orphan",
)
class CVScreeningResult(Base):
__tablename__ = "cv_screening_results"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
candidate_id = Column(UUID(as_uuid=True), ForeignKey("candidates.id", ondelete="CASCADE"), nullable=False)
job_title = Column(String)
skills_match_score = Column(Float)
experience_match_score = Column(Float)
education_match_score = Column(Float)
overall_fit_score = Column(Float)
llm_feedback = Column(Text)
reasoning_trace = Column(JSON)
timestamp = Column(DateTime, default=datetime.utcnow)
candidate = relationship("Candidate", back_populates="cv_screening_results")
class VoiceScreeningResult(Base):
__tablename__ = "voice_screening_results"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
candidate_id = Column(UUID(as_uuid=True), ForeignKey("candidates.id", ondelete="CASCADE"), nullable=False)
call_sid = Column(String)
transcript_text = Column(Text)
sentiment_score = Column(Float)
confidence_score = Column(Float)
communication_score = Column(Float)
llm_summary = Column(Text)
llm_judgment_json = Column(JSON)
audio_url = Column(String)
timestamp = Column(DateTime, default=datetime.utcnow)
candidate = relationship("Candidate", back_populates="voice_screening_results")
class InterviewScheduling(Base):
__tablename__ = "interview_scheduling"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
candidate_id = Column(UUID(as_uuid=True), ForeignKey("candidates.id", ondelete="CASCADE"), nullable=False)
calendar_event_id = Column(String)
event_summary = Column(String)
start_time = Column(DateTime)
end_time = Column(DateTime)
status = Column(Enum(InterviewStatus))
timestamp = Column(DateTime, default=datetime.utcnow)
candidate = relationship("Candidate", back_populates="interview_scheduling")
class FinalDecision(Base):
__tablename__ = "final_decision"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
candidate_id = Column(UUID(as_uuid=True), ForeignKey("candidates.id", ondelete="CASCADE"), nullable=False)
overall_score = Column(Float)
decision = Column(Enum(DecisionStatus))
llm_rationale = Column(Text)
human_notes = Column(Text)
timestamp = Column(DateTime, default=datetime.utcnow)
candidate = relationship("Candidate", back_populates="final_decision")