owenkaplinsky's picture
update from github stable code (#3)
3370983 verified
"""
CV Upload Router.
Handles CV submission and candidate registration.
"""
from pathlib import Path
from fastapi import APIRouter, HTTPException, UploadFile, File, Form
from src.backend.api.schemas.cv_upload import SubmitResponse
from src.backend.configs import get_cv_settings
from src.backend.database.candidates import register_candidate, update_parsed_cv_path
from src.backend.database.cvs import save_cv
from src.backend.doc_parser import pdf_to_markdown
router = APIRouter()
# Load settings and ensure directories exist
settings = get_cv_settings()
settings.ensure_dirs()
# ==================================================================================
# ENDPOINTS
# ==================================================================================
@router.post("/submit", response_model=SubmitResponse)
async def submit_application(
full_name: str = Form(..., description="Candidate's full name"),
email: str = Form(..., description="Candidate's email address"),
phone: str = Form(default="", description="Candidate's phone number"),
cv_file: UploadFile = File(..., description="CV file (PDF or DOCX)")
) -> SubmitResponse:
"""
Submit a job application with CV.
This endpoint:
1. Saves the uploaded CV file
2. Registers the candidate in the database
3. Parses the CV to markdown for AI processing
4. Updates the parsed CV path in the database
Returns success status and details about the submission.
"""
# Validate file type
allowed_extensions = {".pdf", ".docx"}
file_ext = Path(cv_file.filename or "").suffix.lower()
if file_ext not in allowed_extensions:
raise HTTPException(
status_code=400,
detail=f"Invalid file type. Allowed: {', '.join(allowed_extensions)}"
)
try:
# 1. Save CV locally
file_path = save_cv(cv_file.file, cv_file.filename or "cv.pdf", candidate_name=full_name)
file_path = Path(file_path)
# 2. Register candidate in DB
success = register_candidate(full_name, email, phone, str(file_path))
if not success:
return SubmitResponse(
success=False,
message=f"An application with email '{email}' already exists. You can only apply once.",
candidate_name=full_name,
email=email,
already_exists=True,
)
# 3. Parse CV to markdown
pdf_to_markdown(
input_path=file_path,
output_path=settings.parsed_path,
model="gpt-4.1-mini",
)
# 4. Update parsed CV path in DB
parsed_path = settings.parsed_path / (file_path.stem + ".txt")
update_parsed_cv_path(email, str(parsed_path))
return SubmitResponse(
success=True,
message=f"Application submitted successfully for {full_name}!",
candidate_name=full_name,
email=email,
cv_file_path=str(file_path),
)
except Exception as e:
raise HTTPException(
status_code=500,
detail=f"Failed to process application: {str(e)}"
)
@router.get("/health")
async def cv_upload_health():
"""Health check for CV upload router."""
return {"status": "healthy", "service": "cv_upload"}