Files
2026-09-07 18:44:32 -03:00

163 lines
6.3 KiB
Python

from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.http import JsonResponse
from django.db.models import Q, Count, Avg
from decimal import Decimal
from datetime import date
import json
from .models import SpecialistCandidate, EvaluationRubric7D, SpecialistCategoryEnum, SeniorityEnum, CandidateStatusEnum
from .forms import SpecialistCandidateForm, EvaluationRubric7DForm
from .services.rubric import TalentScoringEngine
from expenses.chile_regions import REGIONS_COMMUNES
@login_required
def candidate_list(request):
candidates = SpecialistCandidate.objects.select_related().all().order_by('-created_at')
# Filter params
category = request.GET.get('category')
seniority = request.GET.get('seniority')
trl = request.GET.get('trl')
status = request.GET.get('status')
query = request.GET.get('q')
if category:
candidates = candidates.filter(category_code=category)
if seniority:
candidates = candidates.filter(seniority=seniority)
if trl:
candidates = candidates.filter(trl_level=int(trl))
if status:
candidates = candidates.filter(status=status)
if query:
candidates = candidates.filter(
Q(first_name__icontains=query) |
Q(last_name__icontains=query) |
Q(rut__icontains=query) |
Q(specialty_title__icontains=query) |
Q(summary__icontains=query)
)
# Statistics
stats = {
'total': SpecialistCandidate.objects.count(),
'approved': SpecialistCandidate.objects.filter(status=CandidateStatusEnum.APROBADO_CARTERA).count(),
'reserve': SpecialistCandidate.objects.filter(status=CandidateStatusEnum.EN_RESERVA).count(),
'evaluating': SpecialistCandidate.objects.filter(status=CandidateStatusEnum.EN_EVALUACION).count(),
}
return render(request, 'talent/candidate_list.html', {
'candidates': candidates,
'stats': stats,
'categories': SpecialistCategoryEnum.choices,
'seniorities': SeniorityEnum.choices,
'statuses': CandidateStatusEnum.choices,
'selected_category': category,
'selected_seniority': seniority,
'selected_trl': trl,
'selected_status': status,
'query': query,
})
@login_required
def candidate_create(request):
if request.method == 'POST':
form = SpecialistCandidateForm(request.POST)
if form.is_valid():
cand = form.save()
messages.success(request, f"Especialista {cand.full_name} registrado en cartera.")
return redirect('talent:candidate_detail', pk=cand.pk)
else:
form = SpecialistCandidateForm()
return render(request, 'talent/candidate_form.html', {
'form': form,
'title': 'Registrar Nuevo Especialista I+D+i',
'regions_json': json.dumps(REGIONS_COMMUNES)
})
@login_required
def candidate_edit(request, pk):
cand = get_object_or_404(SpecialistCandidate, pk=pk)
if request.method == 'POST':
form = SpecialistCandidateForm(request.POST, instance=cand)
if form.is_valid():
cand = form.save()
messages.success(request, f"Perfil de {cand.full_name} actualizado.")
return redirect('talent:candidate_detail', pk=cand.pk)
else:
form = SpecialistCandidateForm(instance=cand)
return render(request, 'talent/candidate_form.html', {
'form': form,
'title': f"Editar Especialista: {cand.full_name}",
'regions_json': json.dumps(REGIONS_COMMUNES)
})
@login_required
def candidate_detail(request, pk):
cand = get_object_or_404(SpecialistCandidate, pk=pk)
evaluations = cand.evaluations.select_related('evaluator').order_by('-evaluation_date', '-created_at')
latest_eval = evaluations.first()
return render(request, 'talent/candidate_detail.html', {
'candidate': cand,
'evaluations': evaluations,
'latest_eval': latest_eval,
})
@login_required
def evaluation_create(request, candidate_pk=None):
candidate = get_object_or_404(SpecialistCandidate, pk=candidate_pk) if candidate_pk else None
if request.method == 'POST':
form = EvaluationRubric7DForm(request.POST)
if form.is_valid():
ev = form.save(commit=False)
ev.evaluator = request.user
# Run Scoring Engine
result = TalentScoringEngine.evaluate(
dim_case_study=ev.dim_case_study,
dim_portfolio=ev.dim_portfolio,
dim_tech_specific=ev.dim_tech_specific,
dim_cardinal_skills=ev.dim_cardinal_skills,
dim_experience=ev.dim_experience,
dim_principles_safety=ev.dim_principles_safety,
dim_innovation_rndi=ev.dim_innovation_rndi,
evaluation_date=ev.evaluation_date
)
ev.total_weighted_score = result['weighted_score']
ev.is_knockout = result['is_knockout']
ev.result_status = result['result_status']
ev.valid_until = result['valid_until']
ev.save()
# Sync candidate status
cand_obj = ev.candidate
cand_obj.status = ev.result_status
cand_obj.save(update_fields=['status'])
if ev.is_knockout:
messages.error(request, f"Evaluación finalizada: {result['verdict_message']}")
else:
messages.success(request, f"Evaluación 7D guardada exitosamente. Score: {ev.total_weighted_score}/100 - {result['verdict_message']}")
return redirect('talent:candidate_detail', pk=cand_obj.pk)
else:
initial = {'evaluation_date': date.today()}
if candidate:
initial['candidate'] = candidate
form = EvaluationRubric7DForm(initial=initial)
return render(request, 'talent/evaluation_form.html', {
'form': form,
'candidate': candidate,
})
@login_required
def evaluation_detail(request, pk):
eval_obj = get_object_or_404(EvaluationRubric7D.objects.select_related('candidate', 'evaluator'), pk=pk)
return render(request, 'talent/evaluation_detail.html', {'eval': eval_obj})