File size: 3,000 Bytes
031a84a
 
 
 
 
 
 
 
 
 
 
 
 
5b624c3
031a84a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5b624c3
 
 
 
 
 
 
 
031a84a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5b624c3
031a84a
 
5b624c3
031a84a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9b0650f
031a84a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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,
        )