from fastapi import HTTPException, UploadFile from schemas.report import ( AnalyzeResponseSchema, CVAnalysisSchema, JobMatchSchema, RecommendationSchema, SenioritySchema, ) from use_cases.analyze_cv import AnalyzeCVUseCase from domain.entities import AnalysisReport MAX_PDF_SIZE_MB = 5 MIN_CONFIDENCE = 0.4 class AnalyzeController: def __init__(self, use_case: AnalyzeCVUseCase): self.use_case = use_case async def handle( self, file: UploadFile, job_description: str | None, ) -> AnalyzeResponseSchema: pdf_bytes = await self._validate_and_read(file) report = self.use_case.execute(pdf_bytes=pdf_bytes, job_description=job_description) return self._to_response(report) @staticmethod def _resolve_area(label: str | None, confidence: float | None) -> str: if not label or label == "Unknown": return "No detectado" if (confidence or 0.0) < MIN_CONFIDENCE: return "No detectado" return label async def _validate_and_read(self, file: UploadFile) -> bytes: if file.content_type != "application/pdf": raise HTTPException(status_code=400, detail="El archivo debe ser un PDF.") pdf_bytes = await file.read() if len(pdf_bytes) > MAX_PDF_SIZE_MB * 1024 * 1024: raise HTTPException( status_code=400, detail=f"El PDF no puede superar {MAX_PDF_SIZE_MB}MB.", ) return pdf_bytes def _to_response(self, report: AnalysisReport) -> AnalyzeResponseSchema: return AnalyzeResponseSchema( cv_analysis=CVAnalysisSchema( extracted_skills=report.profile.skills, experience_years=report.profile.experience_years, roles=report.profile.roles, education=report.profile.education, seniority=SenioritySchema( label=report.profile.seniority or "Unknown", confidence=report.profile.seniority_confidence or 0.0, ), area=SenioritySchema( label=self._resolve_area(report.profile.area, report.profile.area_confidence), confidence=report.profile.area_confidence or 0.0, ), has_projects=report.profile.has_projects, ), job_match=JobMatchSchema( available=report.job_match.available, score=report.job_match.score, matched_skills=report.job_match.matched_skills, missing_skills=report.job_match.missing_skills, ), recommendations=[ RecommendationSchema( category=r.category, priority=r.priority, message=r.message, ) for r in report.recommendations ], narrative=report.narrative, )