Skip to content

Commit

Permalink
Freeze results fix
Browse files Browse the repository at this point in the history
  • Loading branch information
mmihalik committed Nov 23, 2024
1 parent 8425318 commit 8503f9c
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 179 deletions.
3 changes: 0 additions & 3 deletions competition/exceptions.py

This file was deleted.

26 changes: 6 additions & 20 deletions competition/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import datetime
import json
from typing import Optional

from django.conf import settings
Expand All @@ -18,7 +17,6 @@
from base.managers import UnspecifiedValueManager
from base.models import RestrictedFileField
from base.validators import school_year_validator
from competition.exceptions import FreezingNotClosedResults
from competition.querysets import ActiveQuerySet
from competition.utils.school_year_manipulation import \
get_school_year_end_by_date
Expand Down Expand Up @@ -249,11 +247,6 @@ def get_first_series(self) -> 'Series':
def get_second_series(self) -> 'Series':
return self.series_set.get(order=2)

def freeze_results(self, results):
if any(not series.complete for series in self.series_set.all()):
raise FreezingNotClosedResults()
self.frozen_results = json.dumps(results)

@property
def complete(self) -> bool:
return self.frozen_results is not None
Expand Down Expand Up @@ -363,14 +356,6 @@ def get_actual_late_flag(self) -> Optional[LateTag]:
.order_by('upper_bound')\
.first()

def freeze_results(self, results):
if any(
problem.num_solutions != problem.num_corrected_solutions
for problem in self.problems.all()
):
raise FreezingNotClosedResults()
self.frozen_results = json.dumps(results)

@property
def num_problems(self) -> int:
return self.problems.count()
Expand Down Expand Up @@ -418,7 +403,8 @@ class Meta:

def __str__(self):
return f'{self.series.semester.competition.name}-{self.series.semester.year}' \
f'-{self.series.semester.season[0]}S-S{self.series.order} - {self.order}. úloha'
f'-{self.series.semester.season[0]
}S-S{self.series.order} - {self.order}. úloha'

def get_stats(self):
stats = {}
Expand Down Expand Up @@ -607,7 +593,7 @@ def get_registration_by_profile_and_event(profile, event):
return registration

def __str__(self):
return f'{ self.profile.user.get_full_name() } @ { self.event }'
return f'{self.profile.user.get_full_name()} @ {self.event}'

def can_user_modify(self, user):
return self.event.can_user_modify(user)
Expand Down Expand Up @@ -674,18 +660,18 @@ class Meta:
verbose_name='internetové riešenie', default=False)

def __str__(self):
return f'Riešiteľ: { self.semester_registration } - úloha { self.problem }'
return f'Riešiteľ: {self.semester_registration} - úloha {self.problem}'

def get_solution_file_name(self):
return f'{self.semester_registration.profile.user.get_full_name_camel_case()}'\
f'-{self.problem.id}-{self.semester_registration.id}.pdf'
f'-{self.problem.id}-{self.semester_registration.id}.pdf'

def get_solution_file_path(self):
return f'solutions/user_solutions/{self.get_solution_file_name()}'

def get_corrected_solution_file_name(self):
return f'{self.semester_registration.profile.user.get_full_name_camel_case()}'\
f'-{self.problem.id}-{self.semester_registration.id}_corrected.pdf'
f'-{self.problem.id}-{self.semester_registration.id}_corrected.pdf'

def get_corrected_solution_file_path(self):
return f'solutions/corrected/{self.get_corrected_solution_file_name()}'
Expand Down
156 changes: 156 additions & 0 deletions competition/results.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
from json import loads as json_loads, dumps as json_dumps
from competition.utils import sum_methods
from competition.serializers import EventRegistrationReadSerializer
from competition.models import Series, Semester, EventRegistration
from operator import itemgetter


class FreezingNotClosedResults(Exception):
"""Snažíš sa zamraziť výsledky série, ktorá nemá opravené všetky riešenia"""


def semester_results(self: Semester) -> dict:
"""Vyrobí výsledky semestra"""
if self.frozen_results is not None:
return json_loads(self.frozen_results)
results = []
for registration in self.eventregistration_set.all():
results.append(_generate_result_row(registration, self))

results.sort(key=itemgetter('total'), reverse=True)
results = _rank_results(results)
return results


def freeze_semester_results(semester: Semester):
if any(not series.complete for series in semester.series_set.all()):
raise FreezingNotClosedResults()

semester.frozen_results = json_dumps(semester_results(semester))


def series_results(series: Series):
results = [
_generate_result_row(registration, only_series=series)
for registration in series.semester.eventregistration_set.all()
]

results.sort(key=itemgetter('total'), reverse=True)

return _rank_results(results)


def freeze_series_results(series: Series):
if any(
problem.num_solutions != problem.num_corrected_solutions
for problem in series.problems.all()
):
raise FreezingNotClosedResults()

series.frozen_results = json_dumps(series.results)


def generate_praticipant_invitations(
results_with_ranking: list[dict],
number_of_participants: int,
number_of_substitues: int,
) -> list[dict]:
invited_users = []
for i, result_row in enumerate(results_with_ranking):
if i < number_of_participants:
invited_users.append({
'first_name': result_row['registration']['profile']['first_name'],
'last_name': result_row['registration']['profile']['last_name'],
'school': result_row['registration']['school'],
'is_participant': True
})
elif i < number_of_participants+number_of_substitues:
invited_users.append({
'first_name': result_row['registration']['profile']['first_name'],
'last_name': result_row['registration']['profile']['last_name'],
'school': result_row['registration']['school'],
'is_participant': False
})
return invited_users


def _generate_result_row(
semester_registration: EventRegistration,
semester: Semester | None = None,
only_series: Series | None = None,
):
"""
Vygeneruje riadok výsledku pre používateľa.
Ak je uvedený only_semester vygenerujú sa výsledky iba sa daný semester
"""
user_solutions = semester_registration.solution_set
series_set = semester.series_set.order_by(
'order') if semester is not None else [only_series]
solutions = []
subtotal = []
for series in series_set:
series_solutions = []
solution_points = []
for problem in series.problems.order_by('order'):
sol = user_solutions.filter(problem=problem).first()

solution_points.append(sol.score or 0 if sol is not None else 0)
series_solutions.append(
{
'points': (str(sol.score if sol.score is not None else '?')
if sol is not None else '-'),
'solution_pk': sol.pk if sol is not None else None,
'problem_pk': problem.pk,
'votes': 0 # TODO: Implement votes sol.vote
}
)
series_sum_func = getattr(sum_methods, series.sum_method or '',
sum_methods.series_simple_sum)
solutions.append(series_solutions)
subtotal.append(
series_sum_func(solution_points, semester_registration)
)
return {
# Poradie - horná hranica, v prípade deleného miesto(napr. 1.-3.) ide o nižšie miesto(1)
'rank_start': 0,
# Poradie - dolná hranica, v prípade deleného miesto(napr. 1.-3.) ide o vyššie miesto(3)
'rank_end': 0,
# Indikuje či sa zmenilo poradie od minulej priečky, slúži na delené miesta
'rank_changed': True,
# primary key riešiteľovej registrácie do semestra
'registration': EventRegistrationReadSerializer(semester_registration).data,
# Súčty bodov po sériách
'subtotal': subtotal,
# Celkový súčet za danú entitu
'total': sum(subtotal),
# Zoznam riešení,
'solutions': solutions
}


def _rank_results(results: list[dict]) -> list[dict]:
# Spodná hranica
current_rank = 1
n_teams = 1
last_points = None
for res in results:
if last_points != res['total']:
current_rank = n_teams
last_points = res['total']
res['rank_changed'] = True
else:
res['rank_changed'] = False
res['rank_start'] = current_rank
n_teams += 1

# Horná hranica
current_rank = len(results)
n_teams = len(results)
last_points = None
for res in reversed(results):
if last_points != res['total']:
current_rank = n_teams
last_points = res['total']
res['rank_end'] = current_rank
n_teams -= 1
return results
9 changes: 4 additions & 5 deletions competition/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from rest_framework import serializers

from competition import models
from competition.models import Event, Problem, RegistrationLink
from personal.serializers import ProfileShortSerializer, SchoolShortSerializer


Expand Down Expand Up @@ -71,11 +70,11 @@ def create(self, validated_data):
registration_link = validated_data.pop('registration_link', None)

if registration_link is not None:
registration_link = RegistrationLink.objects.create(
registration_link = models.RegistrationLink.objects.create(
**registration_link,
)

return Event.objects.create(
return models.Event.objects.create(
registration_link=registration_link,
**validated_data,
)
Expand Down Expand Up @@ -299,7 +298,7 @@ def format_list_of_names(self, names: list[str]) -> str:
def format_histogram(self, histogram: list[dict[str, int]]) -> str:
return ''.join([f'({item["score"]},{item["count"]})' for item in histogram])

def get_tex_header(self, obj: Problem) -> str:
def get_tex_header(self, obj: models.Problem) -> str:
"""Generuje tex hlavicku vzoraku do casaku"""
try:
corrected_by = [user.get_full_name()
Expand All @@ -309,7 +308,7 @@ def get_tex_header(self, obj: Problem) -> str:
best_solutions = [user.get_full_name()
for user in obj.correction.corrected_by.all()]
best_solution_suffix = 'e' if len(best_solutions) > 1 else 'a'
except Problem.correction.RelatedObjectDoesNotExist: # pylint: disable=no-member
except models.Problem.correction.RelatedObjectDoesNotExist: # pylint: disable=no-member
corrected_by = None
corrected_suffix = ''
best_solutions = None
Expand Down
49 changes: 0 additions & 49 deletions competition/utils/results.py

This file was deleted.

Loading

0 comments on commit 8503f9c

Please sign in to comment.