94 lines
4.2 KiB
Python
94 lines
4.2 KiB
Python
"""
|
|
7-Dimension Scoring Engine with Knockout Criterion for EFINTEL Talent Pool.
|
|
"""
|
|
|
|
from decimal import Decimal, ROUND_HALF_UP
|
|
from datetime import date, timedelta
|
|
from typing import Dict, Any
|
|
|
|
class TalentScoringEngine:
|
|
# 7-Dimension Official Weighting
|
|
WEIGHT_CASE_STUDY = Decimal('0.25') # 25% (Knockout if < 70)
|
|
WEIGHT_PORTFOLIO = Decimal('0.15') # 15%
|
|
WEIGHT_TECH_SPECIFIC = Decimal('0.15') # 15%
|
|
WEIGHT_CARDINAL_SKILLS = Decimal('0.15') # 15%
|
|
WEIGHT_EXPERIENCE = Decimal('0.10') # 10%
|
|
WEIGHT_PRINCIPLES = Decimal('0.10') # 10%
|
|
WEIGHT_INNOVATION = Decimal('0.10') # 10%
|
|
|
|
KNOCKOUT_THRESHOLD = Decimal('70.0')
|
|
APPROVAL_THRESHOLD = Decimal('80.0')
|
|
RESERVE_THRESHOLD = Decimal('65.0')
|
|
VALIDITY_DAYS = 365
|
|
|
|
@classmethod
|
|
def evaluate(
|
|
cls,
|
|
dim_case_study: Decimal,
|
|
dim_portfolio: Decimal,
|
|
dim_tech_specific: Decimal,
|
|
dim_cardinal_skills: Decimal,
|
|
dim_experience: Decimal,
|
|
dim_principles_safety: Decimal,
|
|
dim_innovation_rndi: Decimal,
|
|
evaluation_date: date = None
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Calculates weighted 7D score, evaluates Knockout condition, determines status and validity.
|
|
"""
|
|
d1 = Decimal(str(dim_case_study or 0))
|
|
d2 = Decimal(str(dim_portfolio or 0))
|
|
d3 = Decimal(str(dim_tech_specific or 0))
|
|
d4 = Decimal(str(dim_cardinal_skills or 0))
|
|
d5 = Decimal(str(dim_experience or 0))
|
|
d6 = Decimal(str(dim_principles_safety or 0))
|
|
d7 = Decimal(str(dim_innovation_rndi or 0))
|
|
|
|
# Check Knockout condition on Dimension 1 (Caso Práctico / Prueba Técnica)
|
|
is_knockout = d1 < cls.KNOCKOUT_THRESHOLD
|
|
|
|
# Calculate weighted total
|
|
weighted_score = (
|
|
(d1 * cls.WEIGHT_CASE_STUDY) +
|
|
(d2 * cls.WEIGHT_PORTFOLIO) +
|
|
(d3 * cls.WEIGHT_TECH_SPECIFIC) +
|
|
(d4 * cls.WEIGHT_CARDINAL_SKILLS) +
|
|
(d5 * cls.WEIGHT_EXPERIENCE) +
|
|
(d6 * cls.WEIGHT_PRINCIPLES) +
|
|
(d7 * cls.WEIGHT_INNOVATION)
|
|
).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
|
|
|
|
# Determine Final Result Status
|
|
if is_knockout:
|
|
result_status = 'DESCARTADO'
|
|
verdict_message = f"Descartado por Criterio Knockout: Caso Práctico ({d1}/100) inferior al mínimo aprobatorio de {cls.KNOCKOUT_THRESHOLD} puntos."
|
|
elif weighted_score >= cls.APPROVAL_THRESHOLD:
|
|
result_status = 'APROBADO_CARTERA'
|
|
verdict_message = f"¡Aprobado en Cartera de Especialistas! Puntaje de {weighted_score}/100 superior al umbral de excelencia ({cls.APPROVAL_THRESHOLD})."
|
|
elif weighted_score >= cls.RESERVE_THRESHOLD:
|
|
result_status = 'EN_RESERVA'
|
|
verdict_message = f"Calificado en Banco de Reserva ({weighted_score}/100). Cumple estándares operacionales para proyectos secundarios o convocatorias de contingencia."
|
|
else:
|
|
result_status = 'DESCARTADO'
|
|
verdict_message = f"Descartado: Puntaje ponderado ({weighted_score}/100) no alcanza el umbral mínimo de corte ({cls.RESERVE_THRESHOLD})."
|
|
|
|
eval_date = evaluation_date or date.today()
|
|
valid_until = eval_date + timedelta(days=cls.VALIDITY_DAYS)
|
|
|
|
return {
|
|
'weighted_score': weighted_score,
|
|
'is_knockout': is_knockout,
|
|
'result_status': result_status,
|
|
'verdict_message': verdict_message,
|
|
'valid_until': valid_until,
|
|
'breakdown': {
|
|
'case_study_weighted': (d1 * cls.WEIGHT_CASE_STUDY).quantize(Decimal('0.01')),
|
|
'portfolio_weighted': (d2 * cls.WEIGHT_PORTFOLIO).quantize(Decimal('0.01')),
|
|
'tech_specific_weighted': (d3 * cls.WEIGHT_TECH_SPECIFIC).quantize(Decimal('0.01')),
|
|
'cardinal_skills_weighted': (d4 * cls.WEIGHT_CARDINAL_SKILLS).quantize(Decimal('0.01')),
|
|
'experience_weighted': (d5 * cls.WEIGHT_EXPERIENCE).quantize(Decimal('0.01')),
|
|
'principles_weighted': (d6 * cls.WEIGHT_PRINCIPLES).quantize(Decimal('0.01')),
|
|
'innovation_weighted': (d7 * cls.WEIGHT_INNOVATION).quantize(Decimal('0.01')),
|
|
}
|
|
}
|