|
|
""" |
|
|
Database Query Schemas. |
|
|
|
|
|
Flexible schemas for querying any table in the recruitment database. |
|
|
""" |
|
|
|
|
|
from datetime import datetime |
|
|
from typing import Any, Optional |
|
|
from uuid import UUID |
|
|
from enum import Enum |
|
|
|
|
|
from pydantic import BaseModel, Field |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TableName(str, Enum): |
|
|
"""Available tables for querying.""" |
|
|
candidates = "candidates" |
|
|
cv_screening_results = "cv_screening_results" |
|
|
voice_screening_results = "voice_screening_results" |
|
|
interview_scheduling = "interview_scheduling" |
|
|
final_decision = "final_decision" |
|
|
|
|
|
|
|
|
class SortOrder(str, Enum): |
|
|
"""Sort order options.""" |
|
|
asc = "asc" |
|
|
desc = "desc" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class QueryRequest(BaseModel): |
|
|
"""Flexible query request for any table.""" |
|
|
|
|
|
table: TableName = Field(..., description="Table to query") |
|
|
|
|
|
|
|
|
filters: Optional[dict[str, Any]] = Field( |
|
|
default=None, |
|
|
description="Key-value filters (e.g., {'email': 'john@example.com', 'status': 'applied'})" |
|
|
) |
|
|
|
|
|
|
|
|
fields: Optional[list[str]] = Field( |
|
|
default=None, |
|
|
description="Specific fields to return. If None, returns all fields." |
|
|
) |
|
|
|
|
|
|
|
|
include_relations: Optional[bool] = Field( |
|
|
default=False, |
|
|
description="Include related tables (only for candidates table)" |
|
|
) |
|
|
|
|
|
|
|
|
limit: Optional[int] = Field(default=100, ge=1, le=1000, description="Max records to return") |
|
|
offset: Optional[int] = Field(default=0, ge=0, description="Number of records to skip") |
|
|
|
|
|
|
|
|
sort_by: Optional[str] = Field(default=None, description="Field to sort by") |
|
|
sort_order: SortOrder = Field(default=SortOrder.desc, description="Sort order") |
|
|
|
|
|
model_config = { |
|
|
"json_schema_extra": { |
|
|
"examples": [ |
|
|
{ |
|
|
"table": "candidates", |
|
|
"filters": {"status": "applied"}, |
|
|
"fields": ["id", "full_name", "email", "status"], |
|
|
"limit": 10 |
|
|
}, |
|
|
{ |
|
|
"table": "cv_screening_results", |
|
|
"filters": {"overall_fit_score": {"$gte": 0.8}}, |
|
|
"sort_by": "overall_fit_score", |
|
|
"sort_order": "desc" |
|
|
} |
|
|
] |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CandidateResponse(BaseModel): |
|
|
"""Candidate data response.""" |
|
|
id: UUID |
|
|
full_name: str |
|
|
email: str |
|
|
phone_number: Optional[str] = None |
|
|
cv_file_path: Optional[str] = None |
|
|
parsed_cv_file_path: Optional[str] = None |
|
|
status: str |
|
|
created_at: datetime |
|
|
updated_at: datetime |
|
|
|
|
|
|
|
|
cv_screening_results: Optional[list[dict[str, Any]]] = None |
|
|
voice_screening_results: Optional[list[dict[str, Any]]] = None |
|
|
interview_scheduling: Optional[list[dict[str, Any]]] = None |
|
|
final_decision: Optional[dict[str, Any]] = None |
|
|
|
|
|
model_config = {"from_attributes": True} |
|
|
|
|
|
|
|
|
class CVScreeningResponse(BaseModel): |
|
|
"""CV Screening result response.""" |
|
|
id: UUID |
|
|
candidate_id: UUID |
|
|
job_title: Optional[str] = None |
|
|
skills_match_score: Optional[float] = None |
|
|
experience_match_score: Optional[float] = None |
|
|
education_match_score: Optional[float] = None |
|
|
overall_fit_score: Optional[float] = None |
|
|
llm_feedback: Optional[str] = None |
|
|
reasoning_trace: Optional[dict[str, Any]] = None |
|
|
timestamp: datetime |
|
|
|
|
|
model_config = {"from_attributes": True} |
|
|
|
|
|
|
|
|
class VoiceScreeningResponse(BaseModel): |
|
|
"""Voice Screening result response.""" |
|
|
id: UUID |
|
|
candidate_id: UUID |
|
|
call_sid: Optional[str] = None |
|
|
transcript_text: Optional[str] = None |
|
|
sentiment_score: Optional[float] = None |
|
|
confidence_score: Optional[float] = None |
|
|
communication_score: Optional[float] = None |
|
|
llm_summary: Optional[str] = None |
|
|
llm_judgment_json: Optional[dict[str, Any]] = None |
|
|
audio_url: Optional[str] = None |
|
|
timestamp: datetime |
|
|
|
|
|
model_config = {"from_attributes": True} |
|
|
|
|
|
|
|
|
class InterviewSchedulingResponse(BaseModel): |
|
|
"""Interview scheduling response.""" |
|
|
id: UUID |
|
|
candidate_id: UUID |
|
|
calendar_event_id: Optional[str] = None |
|
|
event_summary: Optional[str] = None |
|
|
start_time: Optional[datetime] = None |
|
|
end_time: Optional[datetime] = None |
|
|
status: Optional[str] = None |
|
|
timestamp: datetime |
|
|
|
|
|
model_config = {"from_attributes": True} |
|
|
|
|
|
|
|
|
class FinalDecisionResponse(BaseModel): |
|
|
"""Final decision response.""" |
|
|
id: UUID |
|
|
candidate_id: UUID |
|
|
overall_score: Optional[float] = None |
|
|
decision: Optional[str] = None |
|
|
llm_rationale: Optional[str] = None |
|
|
human_notes: Optional[str] = None |
|
|
timestamp: datetime |
|
|
|
|
|
model_config = {"from_attributes": True} |
|
|
|
|
|
|
|
|
class QueryResponse(BaseModel): |
|
|
"""Generic query response wrapper.""" |
|
|
success: bool |
|
|
table: str |
|
|
total_count: int |
|
|
returned_count: int |
|
|
offset: int |
|
|
data: list[dict[str, Any]] |
|
|
message: Optional[str] = None |
|
|
|
|
|
|
|
|
class SingleRecordResponse(BaseModel): |
|
|
"""Single record response.""" |
|
|
success: bool |
|
|
table: str |
|
|
data: Optional[dict[str, Any]] = None |
|
|
message: Optional[str] = None |
|
|
|
|
|
|