|
|
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) |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
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") |