84 lines
2.9 KiB
Python
84 lines
2.9 KiB
Python
# apps/participants/services.py
|
|
|
|
from django.db import transaction
|
|
from .models import Participant
|
|
from apps.settingsapp.models import GradingSettings
|
|
|
|
def _factor(diff: float) -> float:
|
|
if diff <= 10:
|
|
return 1.0
|
|
elif diff <= 20:
|
|
return 0.8
|
|
return 0.6
|
|
|
|
def calculate_scores():
|
|
"""Recalcula y persiste sólo el campo 'score' (base + similitud + bonus objetivo)."""
|
|
settings = GradingSettings.get()
|
|
participants = list(Participant.objects.all())
|
|
|
|
# 1) Base + similitud
|
|
for p in participants:
|
|
base = settings.report_weight + settings.presentation_weight
|
|
sim_total = sum(
|
|
_factor(getattr(p, f"{fld}_diff")) * getattr(settings, f"{fld}_weight")
|
|
for fld in ("R", "L", "Ph", "Pcu")
|
|
)
|
|
p.score = base + sim_total
|
|
|
|
# 2) Calculamos bonus de objetivo según ranking de s_vol_over_eta (mayor es mejor)
|
|
sorted_obj = sorted(participants, key=lambda x: x.s_vol_over_eta, reverse=True)
|
|
obj_bonus_map = {}
|
|
for idx, p in enumerate(sorted_obj, start=1):
|
|
bonus = max(settings.objective_weight - (idx - 1), 0)
|
|
obj_bonus_map[p.pk] = bonus
|
|
|
|
# 3) Persistimos sumando el bonus a score
|
|
with transaction.atomic():
|
|
for p in participants:
|
|
p.score += obj_bonus_map[p.pk]
|
|
p.save(update_fields=["score"])
|
|
|
|
|
|
def calculate_scores_details():
|
|
"""
|
|
Devuelve una lista de dicts con desgloses:
|
|
[{
|
|
participant: <Participant>,
|
|
base: float,
|
|
sim_total: float,
|
|
obj_bonus: float,
|
|
total: float,
|
|
objective_metric: float
|
|
}, ...]
|
|
ordenada exclusivamente por 'objective_metric' (s_vol_over_eta) descendente.
|
|
"""
|
|
settings = GradingSettings.get()
|
|
participants = list(Participant.objects.all())
|
|
|
|
# 1) Calculamos base y similitud
|
|
details = []
|
|
for p in participants:
|
|
base = settings.report_weight + settings.presentation_weight
|
|
sim_total = sum(
|
|
_factor(getattr(p, f"{fld}_diff")) * getattr(settings, f"{fld}_weight")
|
|
for fld in ("R", "L", "Ph", "Pcu")
|
|
)
|
|
details.append({
|
|
"participant": p,
|
|
"base": base,
|
|
"sim_total": sim_total,
|
|
"obj_bonus": 0, # se asignará más abajo
|
|
"total": base + sim_total, # se incrementará con obj_bonus
|
|
"objective_metric": p.s_vol_over_eta,
|
|
})
|
|
|
|
# 2) Asignamos bonus de objetivo según posición en objective_metric
|
|
sorted_by_obj = sorted(details, key=lambda d: d["objective_metric"], reverse=True)
|
|
for idx, entry in enumerate(sorted_by_obj, start=1):
|
|
bonus = max(settings.objective_weight - (idx - 1), 0)
|
|
entry["obj_bonus"] = bonus
|
|
entry["total"] += bonus
|
|
|
|
# 3) Orden final sólo por objective_metric descendente
|
|
return sorted(details, key=lambda d: d["objective_metric"], reverse=True)
|