Merge branch 'vysledkovky2' into develop
This commit is contained in:
commit
4aa67d6151
37 changed files with 1170 additions and 1001 deletions
|
@ -633,5 +633,20 @@
|
||||||
"codename": "view_fotkaurlvazba",
|
"codename": "view_fotkaurlvazba",
|
||||||
"ct_app_label": "header_fotky",
|
"ct_app_label": "header_fotky",
|
||||||
"ct_model": "fotkaurlvazba"
|
"ct_model": "fotkaurlvazba"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"codename": "add_deadline",
|
||||||
|
"ct_app_label": "seminar",
|
||||||
|
"ct_model": "deadline"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"codename": "change_deadline",
|
||||||
|
"ct_app_label": "seminar",
|
||||||
|
"ct_model": "deadline"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"codename": "view_deadline",
|
||||||
|
"ct_app_label": "seminar",
|
||||||
|
"ct_model": "deadline"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django import forms
|
||||||
from dal import autocomplete
|
from dal import autocomplete
|
||||||
from django.forms import formset_factory
|
from django.forms import formset_factory
|
||||||
from django.forms.models import inlineformset_factory
|
from django.forms.models import inlineformset_factory
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
from seminar.models import Resitel
|
from seminar.models import Resitel
|
||||||
import seminar.models as m
|
import seminar.models as m
|
||||||
|
@ -87,7 +88,7 @@ ReseniSPrilohamiFormSet = inlineformset_factory(m.Reseni,m.PrilohaReseni,
|
||||||
class JednoHodnoceniForm(forms.ModelForm):
|
class JednoHodnoceniForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = m.Hodnoceni
|
model = m.Hodnoceni
|
||||||
fields = ('problem', 'body', 'cislo_body')
|
fields = ('problem', 'body', 'deadline_body')
|
||||||
widgets = {
|
widgets = {
|
||||||
'problem': autocomplete.ModelSelect2(
|
'problem': autocomplete.ModelSelect2(
|
||||||
url='autocomplete_problem_odevzdatelny', # FIXME: Dovolit i starší?
|
url='autocomplete_problem_odevzdatelny', # FIXME: Dovolit i starší?
|
||||||
|
@ -141,7 +142,6 @@ class OdevzdavatkoTabulkaFiltrForm(forms.Form):
|
||||||
from django.db.utils import OperationalError
|
from django.db.utils import OperationalError
|
||||||
try:
|
try:
|
||||||
aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik
|
aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik
|
||||||
aktualni_cislo = m.Nastaveni.get_solo().aktualni_cislo
|
|
||||||
except OperationalError:
|
except OperationalError:
|
||||||
# django.db.utils.OperationalError: no such table: seminar_nastaveni
|
# django.db.utils.OperationalError: no such table: seminar_nastaveni
|
||||||
# Nemáme databázi, takže to selhalo. Pro jistotu vrátíme aspoň dvě možnosti, ať to nepadá dál
|
# Nemáme databázi, takže to selhalo. Pro jistotu vrátíme aspoň dvě možnosti, ať to nepadá dál
|
||||||
|
@ -152,31 +152,18 @@ class OdevzdavatkoTabulkaFiltrForm(forms.Form):
|
||||||
# FIXME: Tohle je hnusný monkey patch, mělo by to být nějak zahrnuto výš.
|
# FIXME: Tohle je hnusný monkey patch, mělo by to být nějak zahrnuto výš.
|
||||||
if rocnik is not None:
|
if rocnik is not None:
|
||||||
aktualni_rocnik = rocnik
|
aktualni_rocnik = rocnik
|
||||||
aktualni_cislo = m.Cislo.objects.filter(rocnik=rocnik).order_by('poradi').last()
|
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
|
|
||||||
for cislo in m.Cislo.objects.filter(
|
for deadline in m.Deadline.objects.filter(
|
||||||
rocnik=aktualni_rocnik,
|
deadline__lte=timezone.now(),
|
||||||
poradi__lte=aktualni_cislo.poradi,
|
cislo__rocnik=aktualni_rocnik
|
||||||
).reverse(): # Standardně se řadí od nejnovějšího čísla
|
).order_by("deadline"):
|
||||||
# Předem je mi líto kohokoliv, kdo tyhle řádky bude číst...
|
|
||||||
if cislo.datum_vydani is not None and cislo.datum_vydani <= datetime.date.today():
|
result.append((
|
||||||
result.append((
|
strftime(DATE_FORMAT, deadline.deadline.timetuple()),
|
||||||
strftime(DATE_FORMAT, cislo.datum_vydani.timetuple()),
|
str(deadline)))
|
||||||
f"Vydání {cislo.poradi}. čísla"))
|
|
||||||
if cislo.datum_preddeadline is not None and cislo.datum_preddeadline <= datetime.date.today():
|
|
||||||
result.append((
|
|
||||||
strftime(DATE_FORMAT, cislo.datum_preddeadline.timetuple()),
|
|
||||||
f"Předdeadline {cislo.poradi}. čísla"))
|
|
||||||
if cislo.datum_deadline_soustredeni is not None and cislo.datum_deadline_soustredeni <= datetime.date.today():
|
|
||||||
result.append((
|
|
||||||
strftime(DATE_FORMAT, cislo.datum_deadline_soustredeni.timetuple()),
|
|
||||||
f"Sous. deadline {cislo.poradi}. čísla"))
|
|
||||||
if cislo.datum_deadline is not None and cislo.datum_deadline <= datetime.date.today():
|
|
||||||
result.append((
|
|
||||||
strftime(DATE_FORMAT, cislo.datum_deadline.timetuple()),
|
|
||||||
f"Finální deadline {cislo.poradi}. čísla"))
|
|
||||||
result.append((
|
result.append((
|
||||||
strftime(DATE_FORMAT, datetime.date.today().timetuple()), f"Dnes"))
|
strftime(DATE_FORMAT, datetime.date.today().timetuple()), f"Dnes"))
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,10 @@ $(document).ready(function(){
|
||||||
$('.smazat_hodnoceni').click(function(){
|
$('.smazat_hodnoceni').click(function(){
|
||||||
deleteForm("form",this);
|
deleteForm("form",this);
|
||||||
});
|
});
|
||||||
|
// Copy deadline
|
||||||
|
if (form_idx !== "0") {
|
||||||
|
$('#id_form-' + form_idx + '-deadline_body')[0].value = $('#id_form-' + (form_idx - 1) + '-deadline_body')[0].value
|
||||||
|
}
|
||||||
$('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1);
|
$('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1);
|
||||||
});
|
});
|
||||||
$('.smazat_hodnoceni').click(function(){
|
$('.smazat_hodnoceni').click(function(){
|
||||||
|
@ -66,7 +70,7 @@ $(document).ready(function(){
|
||||||
{# https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.get_FOO_display #}
|
{# https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.get_FOO_display #}
|
||||||
<p>Forma: {{ object.get_forma_display }}</p>
|
<p>Forma: {{ object.get_forma_display }}</p>
|
||||||
|
|
||||||
<p>Doručeno {{ object.cas_doruceni }}, deadline: {{object.cas_doruceni | deadline_html }}</p>
|
<p>Doručeno {{ object.cas_doruceni }}, deadline: {{object.deadline_reseni | deadline_html }}</p>
|
||||||
|
|
||||||
{# Soubory: #}
|
{# Soubory: #}
|
||||||
<h3>Přílohy:</h3>
|
<h3>Přílohy:</h3>
|
||||||
|
@ -97,13 +101,13 @@ $(document).ready(function(){
|
||||||
{{ form.management_form }}
|
{{ form.management_form }}
|
||||||
</table>
|
</table>
|
||||||
<table id="form_set">
|
<table id="form_set">
|
||||||
<tr><th>Problém</th><th>Body</th><th>Číslo pro body</th></tr>
|
<tr><th>Problém</th><th>Body</th><th>Deadline pro body</th></tr>
|
||||||
{% for subform in form %}
|
{% for subform in form %}
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr class="hodnoceni">
|
<tr class="hodnoceni">
|
||||||
<td>{{ subform.problem }}</td>
|
<td>{{ subform.problem }}</td>
|
||||||
<td>{{ subform.body }}</td>
|
<td>{{ subform.body }}</td>
|
||||||
<td>{{ subform.cislo_body }}</td>
|
<td>{{ subform.deadline_body }}</td>
|
||||||
<td><a href="#" class="smazat_hodnoceni" id="id_{{subform.prefix}}-jsremove"><img src="{% static "odevzdavatko/cross.png" %}" alt="Smazat"></a></td>
|
<td><a href="#" class="smazat_hodnoceni" id="id_{{subform.prefix}}-jsremove"><img src="{% static "odevzdavatko/cross.png" %}" alt="Smazat"></a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -118,7 +122,7 @@ $(document).ready(function(){
|
||||||
<tr class="hodnoceni">
|
<tr class="hodnoceni">
|
||||||
<td>{{ form.empty_form.problem }}</td>
|
<td>{{ form.empty_form.problem }}</td>
|
||||||
<td>{{ form.empty_form.body }}</td>
|
<td>{{ form.empty_form.body }}</td>
|
||||||
<td>{{ form.empty_form.cislo_body }}</td>
|
<td>{{ form.empty_form.deadline_body }}</td>
|
||||||
<td><a href="#" class="smazat_hodnoceni" id="id_{{form.empty_form.prefix}}-jsremove"><img src="{% static "odevzdavatko/cross.png" %}" alt="Smazat"></a></td>
|
<td><a href="#" class="smazat_hodnoceni" id="id_{{form.empty_form.prefix}}-jsremove"><img src="{% static "odevzdavatko/cross.png" %}" alt="Smazat"></a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
{# https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.get_FOO_display #}
|
{# https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.get_FOO_display #}
|
||||||
<p>Forma: {{ object.get_forma_display }}</p>
|
<p>Forma: {{ object.get_forma_display }}</p>
|
||||||
|
|
||||||
<p>Doručeno {{ object.cas_doruceni }}, deadline: {{object.cas_doruceni | deadline_html }}</p>
|
<p>Doručeno {{ object.cas_doruceni }}, deadline: {{object.deadline_reseni | deadline_html }}</p>
|
||||||
|
|
||||||
{# Soubory: #}
|
{# Soubory: #}
|
||||||
<h3>Přílohy:</h3>
|
<h3>Přílohy:</h3>
|
||||||
|
@ -37,12 +37,12 @@
|
||||||
{# Hodnocení: #}
|
{# Hodnocení: #}
|
||||||
<h3>Hodnocení:</h3>
|
<h3>Hodnocení:</h3>
|
||||||
<table id="form_set" class="dosla_reseni">
|
<table id="form_set" class="dosla_reseni">
|
||||||
<tr><th>Problém</th><th>Body</th>{# <th>Číslo pro body</th> #}</tr>
|
<tr><th>Problém</th><th>Body</th>{# <th>Deadline pro body</th> #}</tr>
|
||||||
{% for h in hodnoceni %}
|
{% for h in hodnoceni %}
|
||||||
<tr class="hodnoceni">
|
<tr class="hodnoceni">
|
||||||
<td>{{ h.problem }}</td>
|
<td>{{ h.problem }}</td>
|
||||||
<td>{{ h.body }}</td>
|
<td>{{ h.body }}</td>
|
||||||
{# <td>{{ h.cislo_body }}</td>#}
|
{# <td>{{ h.deadline_body }}</td>#}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<h3>Označení deadlinů</h3>
|
<h3>Označení deadlinů</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Ⓢ deadline pro účast na soustředění</li>
|
<li>Ⓢ deadline pro účast na soustředění</li>
|
||||||
<li>♲ 1. deadline</li>
|
<li>⭯ 1. deadline</li>
|
||||||
<li>✓ 2. deadline</li>
|
<li>✓ 2. deadline</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
<td class="problem odevzdanareseni_small"><span title="{{ hodn.problem.nazev }}">{{ hodn.problem.nazev | zkrat_nazev_problemu:27 }}</span></td>
|
<td class="problem odevzdanareseni_small"><span title="{{ hodn.problem.nazev }}">{{ hodn.problem.nazev | zkrat_nazev_problemu:27 }}</span></td>
|
||||||
<td class="problem odevzdanareseni_mini"><span title="{{ hodn.problem.nazev }}">{{ hodn.problem.nazev | zkrat_nazev_problemu:10 }}</span></td>
|
<td class="problem odevzdanareseni_mini"><span title="{{ hodn.problem.nazev }}">{{ hodn.problem.nazev | zkrat_nazev_problemu:10 }}</span></td>
|
||||||
<td>{{ hodn.body|default_if_none:"---" }}</td>
|
<td>{{ hodn.body|default_if_none:"---" }}</td>
|
||||||
<td>{{ hodn.reseni.cas_doruceni | deadline_html }}</td>
|
<td>{{ hodn.deadline_body | deadline_html }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% for dl, mnozina_reseni in reseni_podle_deadlinu.items %}
|
{% for dl, mnozina_reseni in reseni_podle_deadlinu.items %}
|
||||||
<h1>{{ dl.2 | deadline_html }}</h1>
|
<h1>{{ dl | deadline_html }}</h1>
|
||||||
<ul>
|
<ul>
|
||||||
{% for obj in mnozina_reseni %}
|
{% for obj in mnozina_reseni %}
|
||||||
<li>{{ obj.sum_body }} b za <a href="{% url 'odevzdavatko_detail_reseni' pk=obj.id %}">{{ obj }}</a> ({{ obj.get_forma_display }} {{ obj.cas_doruceni }})
|
<li>{{ obj.sum_body }} b za <a href="{% url 'odevzdavatko_detail_reseni' pk=obj.id %}">{{ obj }}</a> ({{ obj.get_forma_display }} {{ obj.cas_doruceni }})
|
||||||
|
|
|
@ -19,7 +19,7 @@ import logging
|
||||||
import seminar.models as m
|
import seminar.models as m
|
||||||
from . import forms as f
|
from . import forms as f
|
||||||
from .forms import OdevzdavatkoTabulkaFiltrForm as FiltrForm
|
from .forms import OdevzdavatkoTabulkaFiltrForm as FiltrForm
|
||||||
from seminar.utils import resi_v_rocniku, deadline
|
from seminar.utils import resi_v_rocniku
|
||||||
from seminar.views import formularOKView
|
from seminar.views import formularOKView
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -202,7 +202,7 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi
|
||||||
# XXX: Předat groupby do template nejde: https://stackoverflow.com/questions/6906593/itertools-groupby-in-a-django-template
|
# XXX: Předat groupby do template nejde: https://stackoverflow.com/questions/6906593/itertools-groupby-in-a-django-template
|
||||||
# Django má {% regroup %}, ale ten potřebuje, aby klíč byl atribut položky: https://docs.djangoproject.com/en/3.2/ref/templates/builtins/#regroup
|
# Django má {% regroup %}, ale ten potřebuje, aby klíč byl atribut položky: https://docs.djangoproject.com/en/3.2/ref/templates/builtins/#regroup
|
||||||
# Takže rozbalíme groupby do slovníku klíč → seznam sami (dictionary comphrehension)
|
# Takže rozbalíme groupby do slovníku klíč → seznam sami (dictionary comphrehension)
|
||||||
ctx['reseni_podle_deadlinu'] = {k: list(v) for k,v in groupby(ctx['object_list'], lambda r: deadline(r.cas_doruceni))}
|
ctx['reseni_podle_deadlinu'] = {k: list(v) for k,v in groupby(ctx['object_list'], lambda r: r.deadline_reseni)}
|
||||||
|
|
||||||
# Pro sitetree:
|
# Pro sitetree:
|
||||||
ctx["resitel_id"] = self.kwargs['resitel']
|
ctx["resitel_id"] = self.kwargs['resitel']
|
||||||
|
@ -216,12 +216,12 @@ class DetailReseniView(DetailView):
|
||||||
|
|
||||||
def aktualni_hodnoceni(self):
|
def aktualni_hodnoceni(self):
|
||||||
self.reseni = get_object_or_404(m.Reseni, id=self.kwargs['pk'])
|
self.reseni = get_object_or_404(m.Reseni, id=self.kwargs['pk'])
|
||||||
result = [] # Slovníky s klíči problem, body, cislo_body -- initial data pro f.OhodnoceniReseniFormSet
|
result = [] # Slovníky s klíči problem, body, deadline_body -- initial data pro f.OhodnoceniReseniFormSet
|
||||||
for hodn in m.Hodnoceni.objects.filter(reseni=self.reseni):
|
for hodn in m.Hodnoceni.objects.filter(reseni=self.reseni):
|
||||||
result.append(
|
result.append(
|
||||||
{"problem": hodn.problem,
|
{"problem": hodn.problem,
|
||||||
"body": hodn.body,
|
"body": hodn.body,
|
||||||
"cislo_body": hodn.cislo_body,
|
"deadline_body": hodn.deadline_body,
|
||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -260,11 +260,11 @@ def hodnoceniReseniView(request, pk, *args, **kwargs):
|
||||||
for form in formset:
|
for form in formset:
|
||||||
problem = form.cleaned_data['problem']
|
problem = form.cleaned_data['problem']
|
||||||
body = form.cleaned_data['body']
|
body = form.cleaned_data['body']
|
||||||
cislo_body = form.cleaned_data['cislo_body']
|
deadline_body = form.cleaned_data['deadline_body']
|
||||||
hodnoceni = m.Hodnoceni(
|
hodnoceni = m.Hodnoceni(
|
||||||
problem=problem,
|
problem=problem,
|
||||||
body=body,
|
body=body,
|
||||||
cislo_body=cislo_body,
|
deadline_body=deadline_body,
|
||||||
reseni=reseni,
|
reseni=reseni,
|
||||||
)
|
)
|
||||||
logger.info(f"Creating Hodnoceni: {hodnoceni}")
|
logger.info(f"Creating Hodnoceni: {hodnoceni}")
|
||||||
|
@ -285,7 +285,7 @@ class ResitelReseniView(DetailView):
|
||||||
{
|
{
|
||||||
"problem": hodn.problem,
|
"problem": hodn.problem,
|
||||||
"body": hodn.body,
|
"body": hodn.body,
|
||||||
# "cislo_body": hodn.cislo_body,
|
# "deadline_body": hodn.deadline_body,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
@ -320,9 +320,8 @@ class PrehledOdevzdanychReseni(ListView):
|
||||||
ctx = super().get_context_data(*args, **kwargs)
|
ctx = super().get_context_data(*args, **kwargs)
|
||||||
# Ročník určujeme podle čísla, do jehož deadlinu došlo řešení.
|
# Ročník určujeme podle čísla, do jehož deadlinu došlo řešení.
|
||||||
# Chceme to mít seřazené, takže místo comphrerehsion ručně postavíme pole polí. Django templates neumí použít OrderedDict :-/
|
# Chceme to mít seřazené, takže místo comphrerehsion ručně postavíme pole polí. Django templates neumí použít OrderedDict :-/
|
||||||
# TODO: Funkce deadline vrací deadliny v jiném ročníku, zvlášť pokud se vyrobí řešení až po deadlinu (třeba při poslání mailem)
|
|
||||||
podle_rocniku = []
|
podle_rocniku = []
|
||||||
for rocnik, hodnoceni in groupby(ctx['object_list'], lambda ho: deadline(ho.reseni.cas_doruceni)[1].rocnik if deadline(ho.reseni.cas_doruceni) is not None else None):
|
for rocnik, hodnoceni in groupby(ctx['object_list'], lambda ho: ho.deadline_body.cislo.rocnik if ho.deadline_body is not None else None):
|
||||||
podle_rocniku.append((rocnik, list(hodnoceni)))
|
podle_rocniku.append((rocnik, list(hodnoceni)))
|
||||||
ctx['podle_rocniku'] = reversed(podle_rocniku) # Od nejnovějšího ročníku
|
ctx['podle_rocniku'] = reversed(podle_rocniku) # Od nejnovějšího ročníku
|
||||||
# TODO: Umožnit stažení / zobrazení řešení
|
# TODO: Umožnit stažení / zobrazení řešení
|
||||||
|
@ -412,6 +411,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
|
||||||
self.object = form.save()
|
self.object = form.save()
|
||||||
self.object.resitele.add(m.Resitel.objects.get(osoba__user = self.request.user))
|
self.object.resitele.add(m.Resitel.objects.get(osoba__user = self.request.user))
|
||||||
self.object.cas_doruceni = timezone.now()
|
self.object.cas_doruceni = timezone.now()
|
||||||
|
self.object.deadline = m.Deadline.objects.filter(deadline__gte=self.object.cas_doruceni).first()
|
||||||
self.object.forma = m.Reseni.FORMA_UPLOAD
|
self.object.forma = m.Reseni.FORMA_UPLOAD
|
||||||
self.object.save()
|
self.object.save()
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ class OrgoRozcestnikView(TemplateView):
|
||||||
# přes treenody (a dát si přitom pozor na MezicisloNode)
|
# přes treenody (a dát si přitom pozor na MezicisloNode)
|
||||||
|
|
||||||
neobodovana_reseni = s.Hodnoceni.objects.filter(body__isnull=True)
|
neobodovana_reseni = s.Hodnoceni.objects.filter(body__isnull=True)
|
||||||
reseni_mimo_cislo = s.Hodnoceni.objects.filter(cislo_body__isnull=True)
|
reseni_mimo_cislo = s.Hodnoceni.objects.filter(deadline_body__isnull=True)
|
||||||
context['pocet_neobodovanych_reseni'] = neobodovana_reseni.count()
|
context['pocet_neobodovanych_reseni'] = neobodovana_reseni.count()
|
||||||
context['pocet_reseni_mimo_cislo'] = reseni_mimo_cislo.count()
|
context['pocet_reseni_mimo_cislo'] = reseni_mimo_cislo.count()
|
||||||
|
|
||||||
|
|
|
@ -7,14 +7,21 @@ from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModel
|
||||||
from solo.admin import SingletonModelAdmin
|
from solo.admin import SingletonModelAdmin
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from seminar.utils import hlavni_problem
|
|
||||||
|
|
||||||
# Todo: reversion
|
# Todo: reversion
|
||||||
|
|
||||||
import seminar.models as m
|
import seminar.models as m
|
||||||
|
|
||||||
admin.site.register(m.Rocnik)
|
admin.site.register(m.Rocnik)
|
||||||
|
|
||||||
|
admin.site.register(m.Deadline)
|
||||||
|
admin.site.register(m.ZmrazenaVysledkovka)
|
||||||
|
|
||||||
|
|
||||||
|
class DeadlineAdminInline(admin.TabularInline):
|
||||||
|
model = m.Deadline
|
||||||
|
extra = 0
|
||||||
|
|
||||||
|
|
||||||
class CisloForm(ModelForm):
|
class CisloForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = m.Cislo
|
model = m.Cislo
|
||||||
|
@ -65,6 +72,7 @@ class CisloForm(ModelForm):
|
||||||
class CisloAdmin(admin.ModelAdmin):
|
class CisloAdmin(admin.ModelAdmin):
|
||||||
form = CisloForm
|
form = CisloForm
|
||||||
actions = ['force_publish']
|
actions = ['force_publish']
|
||||||
|
inlines = (DeadlineAdminInline,)
|
||||||
|
|
||||||
def force_publish(self,request,queryset):
|
def force_publish(self,request,queryset):
|
||||||
for cislo in queryset:
|
for cislo in queryset:
|
||||||
|
@ -90,7 +98,7 @@ class CisloAdmin(admin.ModelAdmin):
|
||||||
ch.stav = m.Problem.STAV_ZADANY
|
ch.stav = m.Problem.STAV_ZADANY
|
||||||
ch.save()
|
ch.save()
|
||||||
|
|
||||||
hp = hlavni_problem(ch)
|
hp = ch.hlavni_problem
|
||||||
if hp.stav not in (m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY):
|
if hp.stav not in (m.Problem.STAV_ZADANY, m.Problem.STAV_VYRESENY):
|
||||||
hp.stav = m.Problem.STAV_ZADANY
|
hp.stav = m.Problem.STAV_ZADANY
|
||||||
hp.save()
|
hp.save()
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
import seminar.models as m
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = "Všem deadlinům se zveřejněnou výsledkovkou vygeneruj výsledkovku"
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
for deadline in m.Deadline.objects.filter(verejna_vysledkovka=True):
|
||||||
|
deadline.vygeneruj_vysledkovku()
|
||||||
|
|
81
seminar/migrations/0103_deadline.py
Normal file
81
seminar/migrations/0103_deadline.py
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
# Generated by Django 3.2.15 on 2022-10-01 08:44
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
import seminar.models as m
|
||||||
|
|
||||||
|
|
||||||
|
def vytvor_deadliny(apps, schema_editor):
|
||||||
|
Cislo = apps.get_model('seminar', 'Cislo')
|
||||||
|
Deadline = apps.get_model('seminar', 'Deadline')
|
||||||
|
|
||||||
|
for cislo in Cislo.objects.all():
|
||||||
|
if cislo.rocnik.rocnik < 26:
|
||||||
|
Deadline.objects.create(
|
||||||
|
cislo=cislo,
|
||||||
|
typ=m.Deadline.TYP_CISLA,
|
||||||
|
deadline=timezone.make_aware(datetime.datetime.combine(datetime.date(1994 + cislo.rocnik.rocnik, 6, int(cislo.poradi[0])), datetime.time.min)),
|
||||||
|
verejna_vysledkovka=cislo.verejna_vysledkovka,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
def vytvor_deadline(date: datetime.date, typ):
|
||||||
|
Deadline.objects.create(
|
||||||
|
cislo=cislo,
|
||||||
|
typ=typ,
|
||||||
|
deadline=timezone.make_aware(datetime.datetime.combine(date, datetime.time.min)) + datetime.timedelta(days=1),
|
||||||
|
verejna_vysledkovka=True
|
||||||
|
)
|
||||||
|
|
||||||
|
if cislo.datum_deadline_soustredeni and cislo.datum_deadline_soustredeni == cislo.datum_preddeadline:
|
||||||
|
vytvor_deadline(
|
||||||
|
date=cislo.datum_deadline_soustredeni,
|
||||||
|
typ=m.Deadline.TYP_PRVNI_A_SOUS
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if cislo.datum_deadline_soustredeni:
|
||||||
|
vytvor_deadline(
|
||||||
|
date=cislo.datum_deadline_soustredeni,
|
||||||
|
typ=m.Deadline.TYP_SOUS
|
||||||
|
)
|
||||||
|
if cislo.datum_preddeadline:
|
||||||
|
vytvor_deadline(
|
||||||
|
date=cislo.datum_preddeadline,
|
||||||
|
typ=m.Deadline.TYP_PRVNI
|
||||||
|
)
|
||||||
|
|
||||||
|
if cislo.datum_deadline:
|
||||||
|
vytvor_deadline(
|
||||||
|
date=cislo.datum_deadline,
|
||||||
|
typ=m.Deadline.TYP_CISLA
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('seminar', '0102_osoba_jak_se_dozvedeli'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Deadline',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||||
|
('deadline', models.DateTimeField(default=timezone.make_aware(datetime.datetime.combine(timezone.now(), datetime.time.max)))),
|
||||||
|
('typ', models.CharField(choices=[('cisla', 'Deadline celého čísla'), ('prvni', 'První deadline'), ('prvniasous', 'Sousový a první deadline'), ('sous', 'Sousový deadline')], max_length=32, verbose_name='typ deadlinu')),
|
||||||
|
('verejna_vysledkovka', models.BooleanField(db_column='verejna_vysledkovka', default=False, verbose_name='veřejná výsledkovka')),
|
||||||
|
('cislo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='deadline_v_cisle', to='seminar.cislo', verbose_name='deadline v čísle')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Deadline',
|
||||||
|
'verbose_name_plural': 'Deadliny',
|
||||||
|
'db_table': 'seminar_deadliny',
|
||||||
|
'ordering': ['deadline'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.RunPython(vytvor_deadliny, migrations.RunPython.noop),
|
||||||
|
]
|
86
seminar/migrations/0104_hodnoceni_deadline_body.py
Normal file
86
seminar/migrations/0104_hodnoceni_deadline_body.py
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
# Generated by Django 3.2.15 on 2022-10-01 09:28
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
from logging import getLogger
|
||||||
|
|
||||||
|
log = getLogger(__name__)
|
||||||
|
|
||||||
|
def prirad_deadliny(apps, schema_editor):
|
||||||
|
Hodnoceni = apps.get_model('seminar', 'Hodnoceni')
|
||||||
|
Deadline = apps.get_model('seminar', 'Deadline')
|
||||||
|
|
||||||
|
for h in Hodnoceni.objects.all():
|
||||||
|
if h.cislo_body is not None and h.cislo_body.rocnik.rocnik < 26:
|
||||||
|
# Deadline připravený v minulé migraci
|
||||||
|
h.deadline_body = h.cislo_body.deadline_v_cisle.get()
|
||||||
|
h.save()
|
||||||
|
continue
|
||||||
|
|
||||||
|
p = h.problem
|
||||||
|
|
||||||
|
if p.polymorphic_ctype.model == 'tema':
|
||||||
|
t = p.tema
|
||||||
|
d = Deadline.objects.filter(cislo__rocnik=t.rocnik, deadline__gte=h.reseni.cas_doruceni).first()
|
||||||
|
|
||||||
|
if d is None:
|
||||||
|
d = Deadline.objects.filter(cislo__rocnik=t.rocnik).last()
|
||||||
|
|
||||||
|
if d is not None:
|
||||||
|
h.deadline_body = d
|
||||||
|
h.save()
|
||||||
|
continue
|
||||||
|
|
||||||
|
cislo = None
|
||||||
|
|
||||||
|
if p.polymorphic_ctype.model == 'uloha':
|
||||||
|
u = p.uloha
|
||||||
|
cislo = u.cislo_zadani
|
||||||
|
|
||||||
|
if p.polymorphic_ctype.model == 'clanek':
|
||||||
|
c = p.clanek
|
||||||
|
if c.cislo is not None:
|
||||||
|
cislo = c.cislo
|
||||||
|
|
||||||
|
if cislo is None:
|
||||||
|
log.warning(f"Číslo hodnocení {h.id} se nepodařilo určit exaktním způsobem. Dané hodnocení házím do jeho cislo_body: {h.cislo_body}.")
|
||||||
|
cislo = h.cislo_body
|
||||||
|
|
||||||
|
if cislo is not None:
|
||||||
|
d = Deadline.objects.filter(cislo=cislo, deadline__gte=h.reseni.cas_doruceni).first()
|
||||||
|
if d is None:
|
||||||
|
d = Deadline.objects.filter(cislo=cislo).last()
|
||||||
|
if d is not None:
|
||||||
|
h.deadline_body = d
|
||||||
|
h.save()
|
||||||
|
continue
|
||||||
|
|
||||||
|
d = Deadline.objects.filter(deadline__gte=h.reseni.cas_doruceni).first()
|
||||||
|
h.deadline_body = d
|
||||||
|
h.save()
|
||||||
|
|
||||||
|
log.warning(f"Deadline hodnocení {h.id} se nepodařil určit exaktnějším způsobem. Zkouším další. Přiřazen {h.deadline_body}. Původní cislo_body: {h.cislo_body}.")
|
||||||
|
|
||||||
|
# Zběžná kontrola. Předpokládá, že M&M má méně než 10 čísel v ročníku
|
||||||
|
# a že první znak pořadí je int určující dané pořadí (schroustání 7-8).
|
||||||
|
if h.cislo_body and h.deadline_body and (
|
||||||
|
int(h.deadline_body.cislo.poradi[0]) + 2 < int(h.cislo_body.poradi[0])
|
||||||
|
or int(h.deadline_body.cislo.poradi[0]) > int(h.cislo_body.poradi[0])
|
||||||
|
):
|
||||||
|
log.error(f"Hodnocení {h.id} se špatně změnilo číslo z {h.cislo_body} na {h.deadline_body.cislo}")
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('seminar', '0103_deadline'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='hodnoceni',
|
||||||
|
name='deadline_body',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.deadline', verbose_name='deadline pro body'),
|
||||||
|
),
|
||||||
|
migrations.RunPython(prirad_deadliny, migrations.RunPython.noop),
|
||||||
|
]
|
50
seminar/migrations/0105_odstraneni_deadlinu_cisla.py
Normal file
50
seminar/migrations/0105_odstraneni_deadlinu_cisla.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
# Generated by Django 3.2.15 on 2022-10-09 10:14
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
from seminar.models import Deadline
|
||||||
|
|
||||||
|
|
||||||
|
def vrat_deadliny(apps, schema_editor):
|
||||||
|
Cislo = apps.get_model('seminar', 'Cislo')
|
||||||
|
|
||||||
|
for cislo in Cislo.objects.all():
|
||||||
|
prvni_deadline = cislo.deadline_v_cisle.filter(typ=Deadline.TYP_PRVNI).last()
|
||||||
|
sous_deadline = cislo.deadline_v_cisle.filter(typ=Deadline.TYP_SOUS).last()
|
||||||
|
prvni_a_sous_deadline = cislo.deadline_v_cisle.filter(typ=Deadline.TYP_PRVNI_A_SOUS).last()
|
||||||
|
posledni_deadline = cislo.deadline_v_cisle.filter(typ=Deadline.TYP_CISLA).last()
|
||||||
|
|
||||||
|
if prvni_a_sous_deadline is not None:
|
||||||
|
cislo.datum_deadline_soustredeni = prvni_a_sous_deadline.deadline.date()
|
||||||
|
cislo.datum_preddeadline = prvni_a_sous_deadline.deadline.date()
|
||||||
|
else:
|
||||||
|
if sous_deadline is not None:
|
||||||
|
cislo.datum_deadline_soustredeni = sous_deadline.deadline.date()
|
||||||
|
if prvni_deadline is not None:
|
||||||
|
cislo.datum_preddeadline = prvni_deadline.deadline.date()
|
||||||
|
|
||||||
|
if posledni_deadline:
|
||||||
|
cislo.datum_deadline = posledni_deadline.deadline.date()
|
||||||
|
|
||||||
|
cislo.save()
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('seminar', '0104_hodnoceni_deadline_body'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(migrations.RunPython.noop, vrat_deadliny),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='cislo',
|
||||||
|
name='datum_deadline',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='cislo',
|
||||||
|
name='datum_deadline_soustredeni',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='cislo',
|
||||||
|
name='datum_preddeadline',
|
||||||
|
),
|
||||||
|
]
|
27
seminar/migrations/0106_remove_cislo_verejna_vysledkovka.py
Normal file
27
seminar/migrations/0106_remove_cislo_verejna_vysledkovka.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# Generated by Django 3.2.15 on 2022-10-09 11:04
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
from seminar.models import Deadline
|
||||||
|
|
||||||
|
|
||||||
|
def vrat_verejnost(apps, schema_editor):
|
||||||
|
Cislo = apps.get_model('seminar', 'Cislo')
|
||||||
|
|
||||||
|
for cislo in Cislo.objects.all():
|
||||||
|
cislo.verejna_vysledkovka = cislo.deadline_v_cisle.filter(typ=Deadline.TYP_CISLA, verejna_vysledkovka=True).exists()
|
||||||
|
cislo.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('seminar', '0105_odstraneni_deadlinu_cisla'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(migrations.RunPython.noop, vrat_verejnost),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='cislo',
|
||||||
|
name='verejna_vysledkovka',
|
||||||
|
),
|
||||||
|
]
|
26
seminar/migrations/0107_zmrazenavysledkovka.py
Normal file
26
seminar/migrations/0107_zmrazenavysledkovka.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# Generated by Django 3.2.15 on 2022-10-10 07:23
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('seminar', '0106_remove_cislo_verejna_vysledkovka'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ZmrazenaVysledkovka',
|
||||||
|
fields=[
|
||||||
|
('deadline', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='vysledkovka_v_deadlinu', serialize=False, to='seminar.deadline')),
|
||||||
|
('html', models.TextField()),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Zmražená výsledkovka',
|
||||||
|
'verbose_name_plural': 'Zmražené výsledkovky',
|
||||||
|
'db_table': 'seminar_vysledkovky',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -78,6 +78,9 @@ class Reseni(bm.SeminarModelBase):
|
||||||
return "{}({}): {}({})".format(self.resitele.first(),len(self.resitele.all()), self.problem.first() ,len(self.problem.all()))
|
return "{}({}): {}({})".format(self.resitele.first(),len(self.resitele.all()), self.problem.first() ,len(self.problem.all()))
|
||||||
# NOTE: Potenciální DB HOG (bez select_related)
|
# NOTE: Potenciální DB HOG (bez select_related)
|
||||||
|
|
||||||
|
def deadline_reseni(self):
|
||||||
|
return am.Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first()
|
||||||
|
|
||||||
## Pravdepodobne uz nebude potreba:
|
## Pravdepodobne uz nebude potreba:
|
||||||
# def save(self, *args, **kwargs):
|
# def save(self, *args, **kwargs):
|
||||||
# if ((self.cislo_body is None) and (self.problem.cislo_reseni) and
|
# if ((self.cislo_body is None) and (self.problem.cislo_reseni) and
|
||||||
|
@ -101,6 +104,10 @@ class Hodnoceni(bm.SeminarModelBase):
|
||||||
cislo_body = models.ForeignKey(am.Cislo, verbose_name='číslo pro body',
|
cislo_body = models.ForeignKey(am.Cislo, verbose_name='číslo pro body',
|
||||||
related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT)
|
related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT)
|
||||||
|
|
||||||
|
# V ročníku < 26 nastaveno na deadline vygenerovaný pro původní cislo_body
|
||||||
|
deadline_body = models.ForeignKey(am.Deadline, verbose_name='deadline pro body',
|
||||||
|
related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT)
|
||||||
|
|
||||||
reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE)
|
reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE)
|
||||||
|
|
||||||
problem = models.ForeignKey(am.Problem, verbose_name='problém',
|
problem = models.ForeignKey(am.Problem, verbose_name='problém',
|
||||||
|
|
|
@ -323,7 +323,7 @@ class Resitel(SeminarModelBase):
|
||||||
# - proto se započítávají dvojnásobně a byly posunuté hranice titulů
|
# - proto se započítávají dvojnásobně a byly posunuté hranice titulů
|
||||||
# - staré tituly se ale nemají odebrat, pokud řešitel v t.č. minulém (26.) ročníku měl titul, má ho mít pořád.
|
# - staré tituly se ale nemají odebrat, pokud řešitel v t.č. minulém (26.) ročníku měl titul, má ho mít pořád.
|
||||||
from .odevzdavatko import Hodnoceni
|
from .odevzdavatko import Hodnoceni
|
||||||
hodnoceni_do_25_rocniku = Hodnoceni.objects.filter(cislo_body__rocnik__rocnik__lte=25,reseni__in=self.reseni_set.all())
|
hodnoceni_do_25_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=25,reseni__in=self.reseni_set.all())
|
||||||
novejsi_hodnoceni = Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()).difference(hodnoceni_do_25_rocniku)
|
novejsi_hodnoceni = Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()).difference(hodnoceni_do_25_rocniku)
|
||||||
|
|
||||||
def body_z_hodnoceni(hh : list):
|
def body_z_hodnoceni(hh : list):
|
||||||
|
@ -361,7 +361,7 @@ class Resitel(SeminarModelBase):
|
||||||
return Titul.akad
|
return Titul.akad
|
||||||
|
|
||||||
from .odevzdavatko import Hodnoceni
|
from .odevzdavatko import Hodnoceni
|
||||||
hodnoceni_do_26_rocniku = Hodnoceni.objects.filter(cislo_body__rocnik__rocnik__lte=26,reseni__in=self.reseni_set.all())
|
hodnoceni_do_26_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=26,reseni__in=self.reseni_set.all())
|
||||||
novejsi_body = body_z_hodnoceni(
|
novejsi_body = body_z_hodnoceni(
|
||||||
Hodnoceni.objects.filter(reseni__in=self.reseni_set.all())
|
Hodnoceni.objects.filter(reseni__in=self.reseni_set.all())
|
||||||
.difference(hodnoceni_do_26_rocniku)
|
.difference(hodnoceni_do_26_rocniku)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import datetime
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import pathlib
|
import pathlib
|
||||||
|
@ -7,6 +8,8 @@ import logging
|
||||||
|
|
||||||
from django.contrib.sites.shortcuts import get_current_site
|
from django.contrib.sites.shortcuts import get_current_site
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models import Q
|
||||||
|
from django.template.loader import render_to_string
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
@ -22,7 +25,6 @@ from taggit.managers import TaggableManager
|
||||||
from reversion import revisions as reversion
|
from reversion import revisions as reversion
|
||||||
|
|
||||||
from seminar.utils import roman
|
from seminar.utils import roman
|
||||||
from seminar.utils import hlavni_problem
|
|
||||||
from treenode import treelib
|
from treenode import treelib
|
||||||
|
|
||||||
from unidecode import unidecode # Používám pro získání ID odkazu (ještě je to někde po někom zakomentované)
|
from unidecode import unidecode # Používám pro získání ID odkazu (ještě je to někde po někom zakomentované)
|
||||||
|
@ -96,7 +98,7 @@ class Rocnik(SeminarModelBase):
|
||||||
return vc[-1] if vc else None
|
return vc[-1] if vc else None
|
||||||
|
|
||||||
def verejne_vysledkovky_cisla(self):
|
def verejne_vysledkovky_cisla(self):
|
||||||
vc = list(self.cisla.filter(verejna_vysledkovka=True))
|
vc = list(self.cisla.filter(deadline_v_cisle__verejna_vysledkovka=True).distinct())
|
||||||
vc.sort(key=lambda c: c.poradi)
|
vc.sort(key=lambda c: c.poradi)
|
||||||
return vc
|
return vc
|
||||||
|
|
||||||
|
@ -156,27 +158,10 @@ class Cislo(SeminarModelBase):
|
||||||
|
|
||||||
datum_vydani = models.DateField('datum vydání', blank=True, null=True,
|
datum_vydani = models.DateField('datum vydání', blank=True, null=True,
|
||||||
help_text='Datum vydání finální verze')
|
help_text='Datum vydání finální verze')
|
||||||
|
|
||||||
datum_deadline_soustredeni = models.DateField(
|
|
||||||
'datum deadline soustředění',
|
|
||||||
blank=True, null=True,
|
|
||||||
help_text='Datum pro příjem řešení pro účast na soustředění')
|
|
||||||
|
|
||||||
datum_preddeadline = models.DateField('datum předdeadline', blank=True, null=True,
|
|
||||||
help_text='Datum pro příjem řešení, která se otisknou v dalším čísle')
|
|
||||||
|
|
||||||
datum_deadline = models.DateField('datum deadline', blank=True, null=True,
|
|
||||||
help_text='Datum pro příjem řešení úloh zadaných v tomto čísle')
|
|
||||||
|
|
||||||
verejne_db = models.BooleanField('číslo zveřejněno',
|
verejne_db = models.BooleanField('číslo zveřejněno',
|
||||||
db_column='verejne', default=False)
|
db_column='verejne', default=False)
|
||||||
|
|
||||||
verejna_vysledkovka = models.BooleanField(
|
|
||||||
'zveřejněna výsledkovka',
|
|
||||||
default=False,
|
|
||||||
help_text='Je-li false u veřejného čísla, '
|
|
||||||
'není výsledkovka zatím veřejná.')
|
|
||||||
|
|
||||||
poznamka = models.TextField('neveřejná poznámka', blank=True,
|
poznamka = models.TextField('neveřejná poznámka', blank=True,
|
||||||
help_text='Neveřejná poznámka k číslu (plain text)')
|
help_text='Neveřejná poznámka k číslu (plain text)')
|
||||||
|
|
||||||
|
@ -218,7 +203,7 @@ class Cislo(SeminarModelBase):
|
||||||
|
|
||||||
def relativni_v_rocniku(self, rel_index):
|
def relativni_v_rocniku(self, rel_index):
|
||||||
"Číslo o `index` dále v ročníku. None pokud neexistuje."
|
"Číslo o `index` dále v ročníku. None pokud neexistuje."
|
||||||
cs = self.rocnik.cisla.order_by('cislo').all()
|
cs = self.rocnik.cisla.order_by('poradi').all()
|
||||||
i = list(cs).index(self) + rel_index
|
i = list(cs).index(self) + rel_index
|
||||||
if (i < 0) or (i >= len(cs)):
|
if (i < 0) or (i >= len(cs)):
|
||||||
return None
|
return None
|
||||||
|
@ -316,16 +301,97 @@ class Cislo(SeminarModelBase):
|
||||||
from seminar.models.treenode import CisloNode
|
from seminar.models.treenode import CisloNode
|
||||||
CisloNode.objects.create(cislo=self)
|
CisloNode.objects.create(cislo=self)
|
||||||
|
|
||||||
def clean(self):
|
def zlomovy_deadline_pro_papirove_cislo(self):
|
||||||
# Finální deadline má být až poslední a je povinný, pokud nějaký deadline existuje.
|
prvni_deadline = Deadline.objects.filter(Q(typ=Deadline.TYP_PRVNI) | Q(typ=Deadline.TYP_PRVNI_A_SOUS), cislo=self).first()
|
||||||
# Existence:
|
if prvni_deadline is None:
|
||||||
if self.datum_deadline is None and (self.datum_preddeadline is not None or self.datum_deadline_soustredeni is not None):
|
posledni_deadline = self.posledni_deadline
|
||||||
raise ValidationError({'datum_deadline': "Číslo musí mít finální deadline, pokud má nějaké deadliny"})
|
if posledni_deadline is None:
|
||||||
if self.datum_deadline is not None:
|
# TODO promyslet, co se má stát tady
|
||||||
if self.datum_preddeadline is not None and self.datum_preddeadline > self.datum_deadline:
|
return Deadline.objects.filter(Q(cislo__poradi__lt=self.poradi, cislo__rocnik=self.rocnik) | Q(cislo__rocnik__rocnik__lt=self.rocnik.rocnik)).order_by("deadline").last()
|
||||||
raise ValidationError({'datum_preddeadline': "Předdeadline musí předcházet finálnímu deadlinu"})
|
return posledni_deadline
|
||||||
if self.datum_deadline_soustredeni is not None and self.datum_deadline_soustredeni > self.datum_deadline:
|
return prvni_deadline
|
||||||
raise ValidationError({'datum_deadline_soustredeni': "Soustřeďkový deadline musí předcházet finálnímu deadlinu"})
|
|
||||||
|
@property
|
||||||
|
def posledni_deadline(self):
|
||||||
|
return self.deadline_v_cisle.all().order_by("deadline").last()
|
||||||
|
|
||||||
|
class Deadline(SeminarModelBase):
|
||||||
|
class Meta:
|
||||||
|
db_table = 'seminar_deadliny'
|
||||||
|
verbose_name = 'Deadline'
|
||||||
|
verbose_name_plural = 'Deadliny'
|
||||||
|
ordering = ['deadline']
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.__original_verejna_vysledkovka = self.verejna_vysledkovka
|
||||||
|
|
||||||
|
id = models.AutoField(primary_key=True)
|
||||||
|
|
||||||
|
# V ročníku < 26 nastaveno na datetime.datetime.combine(datetime.date(1994 + cislo.rocnik.rocnik, 6, int(cislo.poradi[0])), datetime.time.min)
|
||||||
|
deadline = models.DateTimeField(blank=False, default=timezone.make_aware(datetime.datetime.combine(timezone.now(), datetime.time.max)))
|
||||||
|
|
||||||
|
cislo = models.ForeignKey(Cislo, verbose_name='deadline v čísle',
|
||||||
|
related_name='deadline_v_cisle', blank=False,
|
||||||
|
on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
TYP_CISLA = 'cisla'
|
||||||
|
TYP_PRVNI_A_SOUS = 'prvniasous'
|
||||||
|
TYP_PRVNI = 'prvni'
|
||||||
|
TYP_SOUS = 'sous'
|
||||||
|
TYP_CHOICES = [
|
||||||
|
(TYP_CISLA, 'Deadline celého čísla'),
|
||||||
|
(TYP_PRVNI, 'První deadline'),
|
||||||
|
(TYP_PRVNI_A_SOUS, 'Sousový a první deadline'),
|
||||||
|
(TYP_SOUS, 'Sousový deadline'),
|
||||||
|
]
|
||||||
|
CHOICES_MAP = dict(TYP_CHOICES)
|
||||||
|
typ = models.CharField('typ deadlinu', max_length=32,
|
||||||
|
choices=TYP_CHOICES, blank=False)
|
||||||
|
|
||||||
|
verejna_vysledkovka = models.BooleanField('veřejná výsledkovka',
|
||||||
|
db_column='verejna_vysledkovka',
|
||||||
|
default=False)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.CHOICES_MAP[self.typ] + " " + str(self.cislo)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
if self.verejna_vysledkovka and not self.__original_verejna_vysledkovka:
|
||||||
|
self.vygeneruj_vysledkovku()
|
||||||
|
if not self.verejna_vysledkovka and hasattr(self, "vysledkovka_v_deadlinu"):
|
||||||
|
self.vysledkovka_v_deadlinu.delete()
|
||||||
|
|
||||||
|
def vygeneruj_vysledkovku(self):
|
||||||
|
from vysledkovky.utils import VysledkovkaCisla
|
||||||
|
if hasattr(self, "vysledkovka_v_deadlinu"):
|
||||||
|
self.vysledkovka_v_deadlinu.delete()
|
||||||
|
vysledkovka = VysledkovkaCisla(self.cislo, jen_verejne=True, do_deadlinu=self)
|
||||||
|
if len(vysledkovka.radky_vysledkovky) != 0:
|
||||||
|
ZmrazenaVysledkovka.objects.create(
|
||||||
|
deadline=self,
|
||||||
|
html=render_to_string(
|
||||||
|
"vysledkovky/vysledkovka_cisla.html",
|
||||||
|
context={"vysledkovka": vysledkovka, "oznaceni_vysledkovky": self.id}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ZmrazenaVysledkovka(SeminarModelBase):
|
||||||
|
class Meta:
|
||||||
|
db_table = 'seminar_vysledkovky'
|
||||||
|
verbose_name = 'Zmražená výsledkovka'
|
||||||
|
verbose_name_plural = 'Zmražené výsledkovky'
|
||||||
|
|
||||||
|
deadline = models.OneToOneField(
|
||||||
|
Deadline,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
primary_key=True,
|
||||||
|
related_name="vysledkovka_v_deadlinu"
|
||||||
|
)
|
||||||
|
|
||||||
|
html = models.TextField(null=False, blank=False)
|
||||||
|
|
||||||
|
|
||||||
@reversion.register(ignore_duplicates=True)
|
@reversion.register(ignore_duplicates=True)
|
||||||
|
@ -437,9 +503,13 @@ class Problem(SeminarModelBase,PolymorphicModel):
|
||||||
def admin_url(self):
|
def admin_url(self):
|
||||||
return reverse('admin:seminar_problem_change', args=(self.id, ))
|
return reverse('admin:seminar_problem_change', args=(self.id, ))
|
||||||
|
|
||||||
|
@cached_property
|
||||||
def hlavni_problem(self):
|
def hlavni_problem(self):
|
||||||
""" Pro daný problém vrátí jeho nejvyšší nadproblém."""
|
""" Pro daný problém vrátí jeho nejvyšší nadproblém."""
|
||||||
return hlavni_problem(self)
|
problem = self
|
||||||
|
while not (problem.nadproblem is None):
|
||||||
|
problem = problem.nadproblem
|
||||||
|
return problem
|
||||||
|
|
||||||
# FIXME - k úloze
|
# FIXME - k úloze
|
||||||
def body_v_zavorce(self):
|
def body_v_zavorce(self):
|
||||||
|
|
|
@ -68,23 +68,27 @@
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
|
|
||||||
{% if cislo.verejna_vysledkovka %}
|
{% for deadline, nadpis, vysledkovka in deadliny_s_vysledkovkami %}
|
||||||
<h2>Výsledkovka</h2>
|
{% if deadline.vysledkovka_v_deadlinu or vysledkovka.radky_vysledkovky %}
|
||||||
|
|
||||||
{% else %}
|
{% if not deadline.verejna_vysledkovka %}
|
||||||
{% if user.je_org %}
|
|
||||||
<div class='mam-org-only'>
|
<div class='mam-org-only'>
|
||||||
<h2>Výsledkovka (neveřejná)</h2>
|
<h2>{{ nadpis }} (neveřejná)</h2>
|
||||||
|
{% else %}
|
||||||
|
<h2>{{ nadpis }}</h2>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if cislo.verejna_vysledkovka or user.je_org %}
|
{% if deadline.vysledkovka_v_deadlinu %}
|
||||||
{% include "vysledkovky/vysledkovka_cisla.html" %}
|
{{ deadline.vysledkovka_v_deadlinu.html | safe }}
|
||||||
{% endif %}
|
{% else %}
|
||||||
|
{% include "vysledkovky/vysledkovka_cisla.html" with oznaceni_vysledkovky=forloop.counter %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if not cislo.verejna_vysledkovka and user.je_org %}
|
{% if not deadline.verejna_vysledkovka %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
\setlength{\tabcolsep}{3pt}
|
\setlength{\tabcolsep}{3pt}
|
||||||
\begin{longtable}{|r|l|c|r|{% for p in problemy %}c@{\hskip.5em}{% endfor %}|r|r|}\hline
|
\begin{longtable}{|r|l|c|r|{% for p in vysledkovka.temata_a_spol %}c@{\hskip.5em}{% endfor %}{% if vysledkovka.je_nejake_ostatni %}|c@{\hskip.5em}{% endif %}|r|r|}\hline
|
||||||
& & & & \multicolumn{ {{ problemy|length}} }{c|}{\textbf{Témata}} & & \\\textbf{Poř.}& \textbf{Jméno}& \textbf{R.}& \raisebox{0.7mm}{$\sum_{-1}$}& {% for p in problemy %}\textbf{ {{ p.kod_v_rocniku }} }&{% endfor %}\raisebox{0.7mm}{$\sum_0$}&\raisebox{0.7mm}{$\sum_1$}\\\hline
|
& & & & \multicolumn{ {{ vysledkovka.temata_a_spol|length}} }{c|}{\textbf{Témata}} & & {% if vysledkovka.je_nejake_ostatni %}&{\hskip.5em}{% endif %} \\\textbf{Poř.}& \textbf{Jméno}& \textbf{R.}& \raisebox{0.7mm}{$\sum_{-1}$}& {% for p in vysledkovka.temata_a_spol %}\textbf{ {{ p.kod_v_rocniku }} }&{% endfor %}{% if vysledkovka.je_nejake_ostatni %}\textbf{Ostatní}&{% endif %}\raisebox{0.7mm}{$\sum_0$}&\raisebox{0.7mm}{$\sum_1$}\\\hline
|
||||||
\endhead
|
\endhead
|
||||||
\hline
|
\hline
|
||||||
\endfoot
|
\endfoot
|
||||||
{% for rv in radky_vysledkovky %}{{rv.poradi}}&{% if rv.titul %}\titul{ {{ rv.titul}}}{% endif %}{{rv.resitel.osoba.jmeno|slice:":1"}}. {{rv.resitel.osoba.prijmeni}}&{{rv.rocnik_resitele|default:""}}&{{rv.body_celkem_odjakziva}}&{% for b in rv.body_problemy_sezn %}{{b}}&{% endfor %}{{rv.body_cislo}}&{{rv.body_rocnik|default:0}}\\
|
{% for rv in vysledkovka.radky_vysledkovky %}{{rv.poradi}}&{% if rv.titul %}\titul{ {{ rv.titul}}}{% endif %}{{rv.resitel.osoba.jmeno|slice:":1"}}. {{rv.resitel.osoba.prijmeni}}&{{rv.rocnik_resitele|default:""}}&{{rv.body_celkem_odjakziva}}&{% for b in rv.body_za_temata_seznam %}{{b}}&{% endfor %}{{rv.body_cislo}}&{{rv.body_rocnik|default:0}}\\
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
\end{longtable}
|
\end{longtable}
|
||||||
|
|
|
@ -3,13 +3,29 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>
|
<h1>
|
||||||
{% block nadpis1a %}
|
{% block nadpis1a %}
|
||||||
Odměny {{ cislo }}
|
Odměny
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
|
<h2>Od prvního deadlinu {{ from_cislo }} do prvního deadlinu {{ to_cislo }}</h2>
|
||||||
<ul>
|
<ul>
|
||||||
{% for z in zmeny %}
|
{% for z in zmeny_prvni_prvni %}
|
||||||
<li> {{z.jmeno}}: {{z.ftitul}} → {{z.ttitul}}</li>
|
<li> {{z.jmeno}}: {{z.ftitul}} → {{z.ttitul}}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<h2>Od {{ from_cislo }} do prvního deadlinu {{ to_cislo }} (pro první číslo)</h2>
|
||||||
|
<ul>
|
||||||
|
{% for z in zmeny_posledni_prvni %}
|
||||||
|
<li> {{z.jmeno}}: {{z.ftitul}} → {{z.ttitul}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Od prvního deadlinu {{ from_cislo }} do {{ to_cislo }} (pro poslední číslo)</h2>
|
||||||
|
<ul>
|
||||||
|
{% for z in zmeny_prvni_posledni %}
|
||||||
|
<li> {{z.jmeno}}: {{z.ftitul}} → {{z.ttitul}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
|
@ -112,7 +112,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% if vysledkovka %}
|
{% if vysledkovka.radky_vysledkovky %}
|
||||||
<h2>Výsledková listina</h2>
|
<h2>Výsledková listina</h2>
|
||||||
{% include "vysledkovky/vysledkovka_rocnik.html" %}
|
{% include "vysledkovky/vysledkovka_rocnik.html" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -120,10 +120,12 @@
|
||||||
{% if user.je_org %}
|
{% if user.je_org %}
|
||||||
<div class='mam-org-only'>
|
<div class='mam-org-only'>
|
||||||
<p><a href='vysledkovka.tex' download>Výsledkovka ročníku (LaTeX, včetně neveřejných)</a></p>
|
<p><a href='vysledkovka.tex' download>Výsledkovka ročníku (LaTeX, včetně neveřejných)</a></p>
|
||||||
|
<p><a href="tituly.tex" download>Tituly (TeX, do konce ročníku = pro poslední číslo)</a></p>
|
||||||
|
<p><a href="posledni_vysledkovka.tex" download>Výsledkovka posledního čísla</a></p>
|
||||||
{# FIXME: Sice to sem asi nepatří sémanticky, ale bylo to nejjednodušší… #}
|
{# FIXME: Sice to sem asi nepatří sémanticky, ale bylo to nejjednodušší… #}
|
||||||
<p><a href='{% url 'seminar_rocnik_resitele_csv' rocnik=rocnik.rocnik %}' download>CSV export řešitelů</a></p>
|
<p><a href='{% url 'seminar_rocnik_resitele_csv' rocnik=rocnik.rocnik %}' download>CSV export řešitelů</a></p>
|
||||||
<h2>Výsledková listina včetně neveřejných bodů</h2>
|
<h2>Výsledková listina včetně neveřejných bodů</h2>
|
||||||
{% include "vysledkovky/vysledkovka_rocnik_neverejna.html" %}
|
{% include "vysledkovky/vysledkovka_rocnik.html" with vysledkovka=vysledkovka_neverejna %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
{% with lb="{" %}
|
{% with lb="{" %}
|
||||||
{% with rb="}" %}
|
{% with rb="}" %}
|
||||||
{% with radky_vysledkovky=radky_vysledkovky_s_neverejnymi cisla=cisla_s_neverejnymi %}
|
{% with vysledkovka=vysledkovka_neverejna %}
|
||||||
\setlength{\tabcolsep}{3pt}
|
\setlength{\tabcolsep}{3pt}
|
||||||
\begin{longtable}{|r|l|c|r|{% for cislo in cisla %}c{% if not forloop.last %}@{\hskip.5em}{% endif %}{% endfor %}|r|}\hline
|
\begin{longtable}{|r|l|c|r|{% for cislo in vysledkovka.cisla_rocniku %}c{% if not forloop.last %}@{\hskip.5em}{% endif %}{% endfor %}|r|}\hline
|
||||||
& & & & \multicolumn{{ lb }}{{ cisla|length }}}{c|}{\textbf{Číslo}} & \\\textbf{Poř.} & \textbf{Jméno} & \textbf{R.} & \raisebox{0.7mm}{$\sum_{-1}$} & {% for cislo in cisla %}\textbf{{ lb }}{{ cislo.poradi }}{{ rb }} & {% endfor %}\raisebox{0.7mm}{$\sum_1$} \\\hline
|
& & & & \multicolumn{{ lb }}{{ vysledkovka.cisla_rocniku|length }}}{c|}{\textbf{Číslo}} & \\\textbf{Poř.} & \textbf{Jméno} & \textbf{R.} & \raisebox{0.7mm}{$\sum_{-1}$} & {% for cislo in vysledkovka.cisla_rocniku %}\textbf{{ lb }}{{ cislo.poradi }}{{ rb }} & {% endfor %}\raisebox{0.7mm}{$\sum_1$} \\\hline
|
||||||
\endhead
|
\endhead
|
||||||
\hline
|
\hline
|
||||||
\endfoot
|
\endfoot
|
||||||
{% for rv in radky_vysledkovky %}{{ rv.poradi }} & {% if rv.titul %}\titul{{ lb }}{{ rv.titul }}}~{% endif %}{{ rv.resitel.osoba.jmeno|slice:":1" }}.~{{ rv.resitel.osoba.prijmeni }} & {% if rv.rocnik_resitele %}{{ rv.rocnik_resitele }}{% endif %} & {{ rv.body_celkem_odjakziva }} {% for b in rv.body_cisla_sezn %} & {{ b }}{% endfor %} & {{ rv.body_rocnik }} \\
|
{% for rv in vysledkovka.radky_vysledkovky %}{{ rv.poradi }} & {% if rv.titul %}\titul{{ lb }}{{ rv.titul }}}~{% endif %}{{ rv.resitel.osoba.jmeno|slice:":1" }}.~{{ rv.resitel.osoba.prijmeni }} & {% if rv.rocnik_resitele %}{{ rv.rocnik_resitele }}{% endif %} & {{ rv.body_celkem_odjakziva }} {% for b in rv.body_cisla_seznam %} & {{ b }}{% endfor %} & {{ rv.body_rocnik }} \\
|
||||||
{% endfor %}\end{longtable}
|
{% endfor %}\end{longtable}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
|
@ -19,17 +19,17 @@ function sousdeadline() {
|
||||||
<hr>
|
<hr>
|
||||||
<div class="odpocet">
|
<div class="odpocet">
|
||||||
<b><big>Do
|
<b><big>Do
|
||||||
{% if typ_deadline == 'soustredeni' %}
|
{% if nejblizsi_deadline.typ == nejblizsi_deadline.TYP_SOUS or nejblizsi_deadline.typ == nejblizsi_deadline.TYP_PRVNI_A_SOUS %}
|
||||||
<a href="" onClick="sousdeadline()"
|
<a href="" onClick="sousdeadline()"
|
||||||
title="Body za řešení, která nám přijdou do tohoto deadlinu, se ještě započítají pro účast na připravovaném soustředění.">
|
title="Body za řešení, která nám přijdou do tohoto deadlinu, se ještě započítají pro účast na připravovaném soustředění.">
|
||||||
deadlinu</a> odeslání <a href="/aktualni/zadani/">řešení
|
deadlinu</a> odeslání <a href="/aktualni/zadani/">řešení
|
||||||
</a> pro účast na soustředění
|
</a> pro účast na soustředění
|
||||||
|
|
||||||
{% elif typ_deadline == 'preddeadline' %} <a href="" onClick="preddeadline()"
|
{% elif nejblizsi_deadline.typ == 'preddeadline' %} <a href="" onClick="preddeadline()"
|
||||||
title="Řešení, která nám přijdou do tohoto deadlinu, se pokusíme opravit co nejdříve, abyste měli ještě šanci si je ještě opravit před definitivním deadlinem čísla.">1. deadlinu</a> aktuálního <a href="/aktualni/zadani/">čísla</a>
|
title="Řešení, která nám přijdou do tohoto deadlinu, se pokusíme opravit co nejdříve, abyste měli ještě šanci si je ještě opravit před definitivním deadlinem čísla.">1. deadlinu</a> aktuálního <a href="/aktualni/zadani/">čísla</a>
|
||||||
{% else %} deadlinu aktuálního <a href="/aktualni/zadani/">čísla</a>
|
{% else %} deadlinu aktuálního <a href="/aktualni/zadani/">čísla</a>
|
||||||
{% endif %}zbývá:
|
{% endif %}zbývá:
|
||||||
{{nejblizsi_deadline|timeuntil}}</big></b>
|
{{nejblizsi_deadline.deadline|timeuntil}}</big></b>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
{% if radky_vysledkovky %}
|
{% if vysledkovka.radky_vysledkovky %}
|
||||||
{% include "vysledkovky/vysledkovka_rocnik.html" %}
|
{% include "vysledkovky/vysledkovka_rocnik.html" %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>V tomto ročníku zatím žádné výsledky nejsou.</p>
|
<p>V tomto ročníku zatím žádné výsledky nejsou.</p>
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
{% if user.je_org and vysledkovka_s_neverejnymi %}
|
{% if user.je_org and vysledkovka_s_neverejnymi %}
|
||||||
<div class='mam-org-only'>
|
<div class='mam-org-only'>
|
||||||
<h1>Výsledky včetně neveřejných</h1>
|
<h1>Výsledky včetně neveřejných</h1>
|
||||||
{% include "vysledkovky/vysledkovka_rocnik_neverejna.html" %}
|
{% include "vysledkovky/vysledkovka_rocnik.html" with vysledkovka=vysledkovka_neverejna %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
|
@ -16,18 +16,20 @@
|
||||||
<hr>
|
<hr>
|
||||||
<div class="zadani_termin">
|
<div class="zadani_termin">
|
||||||
Termíny pro odeslání řešení {{ac.poradi}}. série:<br>
|
Termíny pro odeslání řešení {{ac.poradi}}. série:<br>
|
||||||
|
|
||||||
{% if ac.datum_deadline_soustredeni %}
|
|
||||||
<span class="datum">{{ac.datum_deadline_soustredeni}}</span> pro účast na soustředění<br>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if ac.datum_preddeadline %}
|
{% for deadline in ac.deadline_v_cisle.all %}
|
||||||
<span class="datum">{{ac.datum_preddeadline}}</span> pro otištění v dalším čísle<br>
|
{% if deadline.typ == deadline.TYP_SOUS or deadline.typ == deadline.TYP_PRVNI_A_SOUS %}
|
||||||
{% endif %}
|
<span class="datum">{{deadline.deadline.date}}</span> pro účast na soustředění<br>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if ac.datum_deadline %}
|
{% if deadline.typ == deadline.TYP_PRVNI or deadline.typ == deadline.TYP_PRVNI_A_SOUS %}
|
||||||
<span class="datum">{{ac.datum_deadline}}</span> definitivní deadline<br>
|
<span class="datum">{{deadline.deadline.date}}</span> pro otištění v dalším čísle<br>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if deadline.typ == deadline.TYP_CISLA %}
|
||||||
|
<span class="datum">{{deadline.deadline.date}}</span> definitivní deadline<br>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
|
@ -1,44 +1,32 @@
|
||||||
from django import template
|
from django import template
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from seminar.utils import TypDeadline, deadline
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
import seminar.models as m
|
||||||
@register.filter(name='deadline')
|
|
||||||
def deadline_text(datum):
|
|
||||||
if deadline(datum) is None:
|
|
||||||
return 'Neznámý deadline'
|
|
||||||
typ, cislo, dl = deadline(datum)
|
|
||||||
strings = {
|
|
||||||
TypDeadline.PredDeadline: f"1. deadline čísla {cislo} ({dl})",
|
|
||||||
TypDeadline.SousDeadline: f"Soustřeďkový deadline čísla {cislo} ({dl})",
|
|
||||||
TypDeadline.FinalDeadline: f"Finální deadline čísla {cislo} ({dl})",
|
|
||||||
}
|
|
||||||
return strings[typ]
|
|
||||||
|
|
||||||
@register.filter(name='deadline_kratseji')
|
@register.filter(name='deadline_kratseji')
|
||||||
def deadline_kratsi_text(datum):
|
def deadline_kratsi_text(deadline: m.Deadline):
|
||||||
if deadline(datum) is None:
|
if deadline is None:
|
||||||
return 'NONE'
|
return 'NONE'
|
||||||
typ, cislo, dl = deadline(datum)
|
|
||||||
strings = {
|
strings = {
|
||||||
TypDeadline.PredDeadline: f"{cislo} ♲",
|
m.Deadline.TYP_PRVNI: f"{deadline.cislo} ⭯",
|
||||||
TypDeadline.SousDeadline: f"{cislo} Ⓢ",
|
m.Deadline.TYP_SOUS: f"{deadline.cislo} Ⓢ",
|
||||||
TypDeadline.FinalDeadline: f"{cislo} ✓",
|
m.Deadline.TYP_PRVNI_A_SOUS: f"{deadline.cislo} ⭯Ⓢ",
|
||||||
|
m.Deadline.TYP_CISLA: f"{deadline.cislo} ✓",
|
||||||
}
|
}
|
||||||
return strings[typ]
|
return strings[deadline.typ]
|
||||||
|
|
||||||
@register.filter(name='deadline_html')
|
@register.filter(name='deadline_html')
|
||||||
def deadline_html(datum):
|
def deadline_html(deadline: m.Deadline):
|
||||||
if deadline(datum) is None:
|
if deadline is None:
|
||||||
return 'Neznámý deadline'
|
return 'Neznámý deadline'
|
||||||
typ, _, _ = deadline(datum)
|
text = deadline_kratsi_text(deadline)
|
||||||
text = deadline_kratsi_text(datum)
|
|
||||||
classes = {
|
classes = {
|
||||||
TypDeadline.PredDeadline: 'preddeadline',
|
m.Deadline.TYP_PRVNI: 'preddeadline',
|
||||||
TypDeadline.SousDeadline: 'sous_deadline',
|
m.Deadline.TYP_SOUS: 'sous_deadline',
|
||||||
TypDeadline.FinalDeadline: 'final_deadline',
|
m.Deadline.TYP_PRVNI_A_SOUS: 'sous_deadline',
|
||||||
|
m.Deadline.TYP_CISLA: 'final_deadline',
|
||||||
}
|
}
|
||||||
return mark_safe(f'<span class="{classes[typ]}">{text}</span>')
|
return mark_safe(f'<span class="{classes[deadline.typ]}">{text}</span>')
|
||||||
|
|
||||||
@register.filter(name='zkrat_nazev_problemu')
|
@register.filter(name='zkrat_nazev_problemu')
|
||||||
def zkrat_nazev_problemu(nazev,width):
|
def zkrat_nazev_problemu(nazev,width):
|
||||||
|
|
|
@ -1,149 +0,0 @@
|
||||||
from django.test import TestCase
|
|
||||||
from datetime import date
|
|
||||||
|
|
||||||
import seminar.models as m
|
|
||||||
from seminar.utils import deadline, TypDeadline
|
|
||||||
|
|
||||||
class DeadlineTestCase(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
# Chceme pár ročníků a v nich pár čísel
|
|
||||||
r1 = m.Rocnik.objects.create(rocnik=1, prvni_rok=2000, exportovat=False)
|
|
||||||
r2 = m.Rocnik.objects.create(rocnik=2, prvni_rok=2001, exportovat=False)
|
|
||||||
r3 = m.Rocnik.objects.create(rocnik=3, prvni_rok=2002, exportovat=False)
|
|
||||||
|
|
||||||
# První číslo mívá soustřeďkový deadline…
|
|
||||||
c1_1 = m.Cislo.objects.create(rocnik=r1, poradi='1',
|
|
||||||
datum_vydani=date.fromisoformat('2000-05-22'),
|
|
||||||
datum_preddeadline=date.fromisoformat('2000-09-11'),
|
|
||||||
datum_deadline_soustredeni=date.fromisoformat('2000-09-11'),
|
|
||||||
datum_deadline=date.fromisoformat('2000-10-01'),
|
|
||||||
)
|
|
||||||
c1_2 = m.Cislo.objects.create(rocnik=r1, poradi='2',
|
|
||||||
datum_vydani=date.fromisoformat('2000-10-19'),
|
|
||||||
datum_preddeadline=date.fromisoformat('2000-12-05'),
|
|
||||||
datum_deadline=date.fromisoformat('2001-01-02'),
|
|
||||||
)
|
|
||||||
# Některá čísla nemají předdeadline…
|
|
||||||
c1_3 = m.Cislo.objects.create(rocnik=r1, poradi='3',
|
|
||||||
datum_vydani=date.fromisoformat('2001-01-28'),
|
|
||||||
datum_deadline=date.fromisoformat('2001-03-12'),
|
|
||||||
)
|
|
||||||
# Poslední číslo nemá ani normální deadline…
|
|
||||||
c1_4 = m.Cislo.objects.create(rocnik=r1, poradi='4-5',
|
|
||||||
datum_vydani=date.fromisoformat('2001-04-24'),
|
|
||||||
)
|
|
||||||
# První číslo dalšího ročníku se někdy vydá dřív, než poslední minulého…
|
|
||||||
c2_1 = m.Cislo.objects.create(rocnik=r2, poradi='1',
|
|
||||||
datum_vydani=date.fromisoformat('2001-04-19'),
|
|
||||||
datum_deadline_soustredeni=date.fromisoformat('2001-09-26'),
|
|
||||||
datum_deadline=date.fromisoformat('2001-10-07'),
|
|
||||||
)
|
|
||||||
# Tohle číslo má finální deadline až po vydání prvního čísla dalšího ročníku
|
|
||||||
# To samé se skoro stalo na přelomu (reálných) ročníků 27 a 28.
|
|
||||||
c2_2 = m.Cislo.objects.create(rocnik=r2, poradi='2',
|
|
||||||
datum_vydani=date.fromisoformat('2002-03-14'),
|
|
||||||
datum_preddeadline=date.fromisoformat('2002-05-26'),
|
|
||||||
datum_deadline=date.fromisoformat('2002-06-30'),
|
|
||||||
)
|
|
||||||
# Závěrečné číslo druhého ročníku až na podzim
|
|
||||||
c2_3 = m.Cislo.objects.create(rocnik=r2, poradi='3',
|
|
||||||
datum_vydani=date.fromisoformat('2002-09-05'),
|
|
||||||
)
|
|
||||||
# Divný případ: sous deadline stejný jako finální
|
|
||||||
c3_1 = m.Cislo.objects.create(rocnik=r3, poradi='1',
|
|
||||||
datum_vydani=date.fromisoformat('2002-06-02'),
|
|
||||||
datum_preddeadline=date.fromisoformat('2002-08-31'),
|
|
||||||
datum_deadline=date.fromisoformat('2002-09-30'),
|
|
||||||
datum_deadline_soustredeni=date.fromisoformat('2002-09-30'),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Celkový harmonogram:
|
|
||||||
# 2000-05-22 začátek 1. ročníku, číslo 1.1
|
|
||||||
|
|
||||||
# 2000-09-11 sous a 1. deadline 1.1
|
|
||||||
# 2000-10-01 finální deadline 1.1
|
|
||||||
# 2000-10-19 Vydání 1.2
|
|
||||||
# 2000-12-05 předdeadline 1.2
|
|
||||||
# 2001-01-02 finální deadline 1.2
|
|
||||||
# 2001-01-28 vyd 1.3
|
|
||||||
# 2001-03-12 deadline 1.3
|
|
||||||
# 2001-04-19 Začátek 2. ročníku, číslo 2.1
|
|
||||||
# 2001-04-24 Vydání 1.4-5 -- závěrečné číslo 1. roč.
|
|
||||||
|
|
||||||
# 2001-09-26 Sous-deadline 2.1
|
|
||||||
# 2001-10-07 Deadline 2.1
|
|
||||||
# 2002-03-14 Pí den, vydání 2.2
|
|
||||||
# 2002-05-26 Předdeadline 2.2
|
|
||||||
# 2002-06-02 Třetí ročník, vydání 3.1
|
|
||||||
# 2002-06-30 Deadline 2.2
|
|
||||||
|
|
||||||
# 2002-08-31 Předdeadline 3.1
|
|
||||||
# 2002-09-04 Vydání 2.3, konec 2. roč.
|
|
||||||
# 2002-09-30 Sous a finální deadline 3.1
|
|
||||||
|
|
||||||
def test_deadline_spravne_vysledky(self):
|
|
||||||
"""V každém intervalu mezi deadliny dostáváme ten správný deadline"""
|
|
||||||
# První ročník
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2000-05-30')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='1'), date.fromisoformat('2000-09-11')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2000-09-15')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='1'), date.fromisoformat('2000-10-01')))
|
|
||||||
|
|
||||||
# Trochu divný případ, kdy někdo něco pošle před vydáním čísla. Ale článkům se to asi stát může…
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2000-10-10')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='2'), date.fromisoformat('2000-12-05')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2000-10-22')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='2'), date.fromisoformat('2000-12-05')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2000-12-15')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='2'), date.fromisoformat('2001-01-02')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2001-01-08')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='3'), date.fromisoformat('2001-03-12')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2001-01-30')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='3'), date.fromisoformat('2001-03-12')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2001-03-15')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='1'), date.fromisoformat('2001-09-26')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2001-04-22')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='1'), date.fromisoformat('2001-09-26')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2001-04-30')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='1'), date.fromisoformat('2001-09-26')))
|
|
||||||
|
|
||||||
# Druhý ročník
|
|
||||||
# Pro jistotu ještě prázdniny
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2001-07-30')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='1'), date.fromisoformat('2001-09-26')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2001-09-27')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='1'), date.fromisoformat('2001-10-07')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2001-12-27')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='2'), date.fromisoformat('2002-05-26')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2002-03-15')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='2'), date.fromisoformat('2002-05-26')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2002-03-15')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='2'), date.fromisoformat('2002-05-26')))
|
|
||||||
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2002-05-27')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='2'), date.fromisoformat('2002-06-30')))
|
|
||||||
# Tohle je trochu podezřelý případ, protože relevantní deadliny existují dvě… Ale ten pro minulý ročník je těsnější a realističtější
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2002-06-03')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='2'), date.fromisoformat('2002-06-30')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2002-07-01')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=3, poradi='1'), date.fromisoformat('2002-08-31')))
|
|
||||||
|
|
||||||
# Třetí ročník
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2002-09-01')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=3, poradi='1'), date.fromisoformat('2002-09-30')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2002-09-05')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=3, poradi='1'), date.fromisoformat('2002-09-30')))
|
|
||||||
|
|
||||||
def test_deadline_ve_zlomove_dny(self):
|
|
||||||
"""Pro dny, kdy je deadline nebo vydání čísla, pořád dostáváme správné deadliny.
|
|
||||||
|
|
||||||
Testuje hlavně přítomnost někde nějakých off-by-one"""
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2000-05-22')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='1'), date.fromisoformat('2000-09-11')))
|
|
||||||
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2000-09-11')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='1'), date.fromisoformat('2000-09-11')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2000-10-01')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='1'), date.fromisoformat('2000-10-01')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2000-10-19')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='2'), date.fromisoformat('2000-12-05')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2000-12-05')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='2'), date.fromisoformat('2000-12-05')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2001-01-02')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='2'), date.fromisoformat('2001-01-02')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2001-01-28')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='3'), date.fromisoformat('2001-03-12')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2001-03-12')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='3'), date.fromisoformat('2001-03-12')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2001-04-19')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='1'), date.fromisoformat('2001-09-26')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2001-04-24')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='1'), date.fromisoformat('2001-09-26')))
|
|
||||||
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2001-09-26')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='1'), date.fromisoformat('2001-09-26')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2001-10-07')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='1'), date.fromisoformat('2001-10-07')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2002-03-14')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='2'), date.fromisoformat('2002-05-26')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2002-05-26')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='2'), date.fromisoformat('2002-05-26')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2002-06-02')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='2'), date.fromisoformat('2002-06-30')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2002-06-30')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='2'), date.fromisoformat('2002-06-30')))
|
|
||||||
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2002-08-31')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=3, poradi='1'), date.fromisoformat('2002-08-31')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2002-09-04')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=3, poradi='1'), date.fromisoformat('2002-09-30')))
|
|
||||||
self.assertEqual(deadline(date.fromisoformat('2002-09-30')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=3, poradi='1'), date.fromisoformat('2002-09-30')))
|
|
||||||
|
|
||||||
def test_deadline_pro_datetime(self):
|
|
||||||
"""Testuje, že i pro datetime dostáváme správné deadliny"""
|
|
||||||
self.skipTest('Chybí implementace testu')
|
|
||||||
|
|
||||||
def test_moc_pozdni_deadline(self):
|
|
||||||
self.assertIsNone(deadline(date.max))
|
|
|
@ -297,7 +297,7 @@ def gen_reseni_ulohy(rnd, cisla, uloha, pocet_resitelu, poradi_cisla, resitele_c
|
||||||
res_vyber.remove(resitele[0])
|
res_vyber.remove(resitele[0])
|
||||||
|
|
||||||
# Vytvoření řešení.
|
# Vytvoření řešení.
|
||||||
if uloha.cislo_zadani.datum_deadline is not None:
|
if uloha.cislo_zadani.zlomovy_deadline_pro_papirove_cislo() is not None:
|
||||||
# combine, abychom dostali plný čas a ne jen datum
|
# combine, abychom dostali plný čas a ne jen datum
|
||||||
cas_doruceni = datetime.datetime.combine(uloha.cislo_zadani.datum_deadline, datetime.datetime.min.time()) - datetime.timedelta(days=random.randint(0, 40)) - datetime.timedelta(minutes=random.randint(0, 60*24))
|
cas_doruceni = datetime.datetime.combine(uloha.cislo_zadani.datum_deadline, datetime.datetime.min.time()) - datetime.timedelta(days=random.randint(0, 40)) - datetime.timedelta(minutes=random.randint(0, 60*24))
|
||||||
# astimezone, protože jinak vyhazuje warning o nenastavené TZ
|
# astimezone, protože jinak vyhazuje warning o nenastavené TZ
|
||||||
|
|
|
@ -72,6 +72,16 @@ urlpatterns = [
|
||||||
org_required(views.resiteleRocnikuCsvExportView),
|
org_required(views.resiteleRocnikuCsvExportView),
|
||||||
name='seminar_rocnik_resitele_csv'
|
name='seminar_rocnik_resitele_csv'
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
'rocnik/<int:rocnik>/tituly.tex',
|
||||||
|
org_required(views.TitulyViewRocnik),
|
||||||
|
name='seminar_rocnik_titul'
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
'rocnik/<int:rocnik>/posledni_vysledkovka.tex',
|
||||||
|
org_required(views.PosledniCisloVysledkovkaView.as_view()),
|
||||||
|
name='seminar_rocnik_posledni_vysledkovka'
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
'cislo/<int:rocnik>.<str:cislo>/vysledkovka.tex',
|
'cislo/<int:rocnik>.<str:cislo>/vysledkovka.tex',
|
||||||
org_required(views.CisloVysledkovkaView.as_view()),
|
org_required(views.CisloVysledkovkaView.as_view()),
|
||||||
|
|
142
seminar/utils.py
142
seminar/utils.py
|
@ -14,9 +14,6 @@ from django.contrib.auth.models import AnonymousUser
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
|
||||||
from enum import Enum
|
|
||||||
from enum import auto
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import seminar.models as m
|
import seminar.models as m
|
||||||
|
@ -179,11 +176,11 @@ def resi_v_rocniku(rocnik, cislo=None):
|
||||||
if cislo is None:
|
if cislo is None:
|
||||||
# filtrujeme pouze podle ročníku
|
# filtrujeme pouze podle ročníku
|
||||||
return m.Resitel.objects.filter(rok_maturity__gte=rocnik.druhy_rok(),
|
return m.Resitel.objects.filter(rok_maturity__gte=rocnik.druhy_rok(),
|
||||||
reseni__hodnoceni__cislo_body__rocnik=rocnik).distinct()
|
reseni__hodnoceni__deadline_body__cislo__rocnik=rocnik).distinct()
|
||||||
else: # filtrujeme podle ročníku i čísla
|
else: # filtrujeme podle ročníku i čísla
|
||||||
return m.Resitel.objects.filter(rok_maturity__gte=rocnik.druhy_rok(),
|
return m.Resitel.objects.filter(rok_maturity__gte=rocnik.druhy_rok(),
|
||||||
reseni__hodnoceni__cislo_body__rocnik=rocnik,
|
reseni__hodnoceni__deadline_body__cislo__rocnik=rocnik,
|
||||||
reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi).distinct()
|
reseni__hodnoceni__deadline_body__cislo__poradi__lte=cislo.poradi).distinct()
|
||||||
|
|
||||||
|
|
||||||
def aktivniResitele(cislo, pouze_letosni=False):
|
def aktivniResitele(cislo, pouze_letosni=False):
|
||||||
|
@ -248,139 +245,6 @@ def viewMethodSwitch(get, post):
|
||||||
|
|
||||||
return NewView.as_view()
|
return NewView.as_view()
|
||||||
|
|
||||||
def cisla_rocniku(rocnik, jen_verejne=True):
|
|
||||||
"""
|
|
||||||
Vrátí všechna čísla daného ročníku.
|
|
||||||
Parametry:
|
|
||||||
rocnik (Rocnik): ročník semináře
|
|
||||||
jen_verejne (bool): zda se mají vrátit jen veřejná, nebo všechna čísla
|
|
||||||
Vrátí:
|
|
||||||
seznam objektů typu Cislo
|
|
||||||
"""
|
|
||||||
if jen_verejne:
|
|
||||||
return rocnik.verejne_vysledkovky_cisla()
|
|
||||||
else:
|
|
||||||
return rocnik.cisla.all().order_by('poradi')
|
|
||||||
|
|
||||||
def hlavni_problem(problem):
|
|
||||||
""" Pro daný problém vrátí jeho nejvyšší nadproblém."""
|
|
||||||
while not(problem.nadproblem == None):
|
|
||||||
problem = problem.nadproblem
|
|
||||||
return problem
|
|
||||||
|
|
||||||
def problemy_rocniku(rocnik, jen_verejne=True):
|
|
||||||
return m.Problem.objects.filter(hodnoceni__in = m.Hodnoceni.objects.filter(cislo_body__in = cisla_rocniku(rocnik, jen_verejne))).distinct().select_related('nadproblem').select_related('nadproblem__nadproblem')
|
|
||||||
|
|
||||||
def problemy_cisla(cislo):
|
|
||||||
""" Vrátí seznam všech problémů s body v daném čísle. """
|
|
||||||
return m.Problem.objects.filter(hodnoceni__in = m.Hodnoceni.objects.filter(cislo_body = cislo)).distinct().non_polymorphic().select_related('nadproblem').select_related('nadproblem__nadproblem')
|
|
||||||
|
|
||||||
|
|
||||||
def hlavni_problemy_f(problemy=None):
|
|
||||||
""" Vrátí seznam všech problémů, které již nemají nadproblém. """
|
|
||||||
# hlavní problémy čísla
|
|
||||||
# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém)
|
|
||||||
hlavni_problemy = set()
|
|
||||||
for p in problemy:
|
|
||||||
hlavni_problemy.add(hlavni_problem(p))
|
|
||||||
|
|
||||||
# zunikátnění
|
|
||||||
hlavni_problemy = list(hlavni_problemy)
|
|
||||||
hlavni_problemy.sort(key=lambda k: k.kod_v_rocniku) # setřídit podle t1, t2, c3, ...
|
|
||||||
|
|
||||||
return hlavni_problemy
|
|
||||||
|
|
||||||
|
|
||||||
def podproblemy_v_cislu(cislo, problemy=None, hlavni_problemy=None):
|
|
||||||
""" Vrátí seznam všech problémů s body v daném čísle v poli 'indexovaném' tématy. """
|
|
||||||
if problemy is None:
|
|
||||||
problemy = problemy_cisla(cislo)
|
|
||||||
if hlavni_problemy is None:
|
|
||||||
hlavni_problemy = hlavni_problemy_f(problemy)
|
|
||||||
|
|
||||||
podproblemy = dict((hp.id, []) for hp in hlavni_problemy)
|
|
||||||
hlavni_problemy = set(hlavni_problemy)
|
|
||||||
podproblemy[-1] = []
|
|
||||||
|
|
||||||
for problem in problemy:
|
|
||||||
h_problem = hlavni_problem(problem)
|
|
||||||
if h_problem in hlavni_problemy:
|
|
||||||
podproblemy[h_problem.id].append(problem)
|
|
||||||
else:
|
|
||||||
podproblemy[-1].append(problem)
|
|
||||||
|
|
||||||
for podproblem in podproblemy.keys():
|
|
||||||
def int_or_zero(p):
|
|
||||||
try:
|
|
||||||
return int(p.kod)
|
|
||||||
except ValueError:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
podproblemy[podproblem] = sorted(podproblemy[podproblem], key=int_or_zero)
|
|
||||||
|
|
||||||
return podproblemy
|
|
||||||
|
|
||||||
class TypDeadline(Enum):
|
|
||||||
PredDeadline = auto()
|
|
||||||
SousDeadline = auto()
|
|
||||||
FinalDeadline = auto()
|
|
||||||
|
|
||||||
def deadline_v_rocniku(datum, rocnik):
|
|
||||||
"""Funkce pro dohledání, ke kterému deadlinu daného ročníku se datum váže.
|
|
||||||
|
|
||||||
Vrací trojici (TypDeadline, Cislo, datumDeadline: date).
|
|
||||||
|
|
||||||
V případě nevalidního volání není aktuálně chování definováno(!)
|
|
||||||
"""
|
|
||||||
cisla = m.Cislo.objects.filter(rocnik=rocnik)
|
|
||||||
deadliny = []
|
|
||||||
for c in cisla:
|
|
||||||
if c.datum_preddeadline is not None:
|
|
||||||
deadliny.append((TypDeadline.PredDeadline, c, c.datum_preddeadline))
|
|
||||||
if c.datum_deadline_soustredeni is not None:
|
|
||||||
deadliny.append((TypDeadline.SousDeadline, c, c.datum_deadline_soustredeni))
|
|
||||||
if c.datum_deadline is not None:
|
|
||||||
deadliny.append((TypDeadline.FinalDeadline, c, c.datum_deadline))
|
|
||||||
deadliny = sorted(deadliny, key=lambda x: x[2]) # podle data
|
|
||||||
for dl in deadliny:
|
|
||||||
if datum <= dl[2]:
|
|
||||||
# První takový deadline je ten nejtěsnější
|
|
||||||
return dl
|
|
||||||
logger.error(f'Pro datum {datum} v ročníku {rocnik} neexistuje deadline.')
|
|
||||||
|
|
||||||
def deadline(datum):
|
|
||||||
"""Funkce pro dohledání, ke kterému deadlinu se datum váže.
|
|
||||||
|
|
||||||
Vrací trojici (TypDeadline, Cislo, datumDeadline: date). Pokud se deadline nenajde, vrátí None
|
|
||||||
"""
|
|
||||||
|
|
||||||
if isinstance(datum, datetime.datetime):
|
|
||||||
datum = datum.date()
|
|
||||||
rok = datum.year
|
|
||||||
# Dva ročníky podezřelé z obsahování dat
|
|
||||||
try:
|
|
||||||
pozdejsi_rocnik = m.Rocnik.objects.get(prvni_rok=rok)
|
|
||||||
except m.Rocnik.DoesNotExist:
|
|
||||||
pozdejsi_rocnik = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
drivejsi_rocnik = m.Rocnik.objects.get(prvni_rok=rok-1)
|
|
||||||
except m.Rocnik.DoesNotExist:
|
|
||||||
drivejsi_rocnik = None
|
|
||||||
|
|
||||||
if drivejsi_rocnik is not None:
|
|
||||||
# Předpokládáme, že neexistuje číslo, které má deadline ale nemá finální deadline.
|
|
||||||
# Seznam čísel je potřeba ručně setřídit chronologicky, protože Model říká, že se řadí od nejnovějšího
|
|
||||||
posledni_deadline_drivejsiho_rocniku = m.Cislo.objects.filter(rocnik=drivejsi_rocnik, datum_deadline__isnull=False).order_by('poradi').last().datum_deadline
|
|
||||||
|
|
||||||
logger.debug(f'Nalezené ročníky: {drivejsi_rocnik}, {pozdejsi_rocnik}')
|
|
||||||
if drivejsi_rocnik is not None and datum <= posledni_deadline_drivejsiho_rocniku:
|
|
||||||
logger.debug(f'Hledám v dřívějším ročníku: {drivejsi_rocnik}')
|
|
||||||
return deadline_v_rocniku(datum, drivejsi_rocnik)
|
|
||||||
else:
|
|
||||||
logger.debug(f'Hledám v pozdějším ročníku: {pozdejsi_rocnik}')
|
|
||||||
return deadline_v_rocniku(datum, pozdejsi_rocnik)
|
|
||||||
|
|
||||||
|
|
||||||
def sync_skoly(base_url):
|
def sync_skoly(base_url):
|
||||||
"""Stáhne všechny školy z mamwebu na adrese <base_url> a uloží je do databáze"""
|
"""Stáhne všechny školy z mamwebu na adrese <base_url> a uloží je do databáze"""
|
||||||
|
|
|
@ -11,14 +11,16 @@ from django.core.exceptions import PermissionDenied
|
||||||
|
|
||||||
import seminar.models as s
|
import seminar.models as s
|
||||||
import seminar.models as m
|
import seminar.models as m
|
||||||
from seminar.models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Organizator, Resitel, Novinky, Tema, Clanek # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci
|
from seminar.models import Problem, Cislo, Reseni, Nastaveni, Rocnik, \
|
||||||
|
Organizator, Resitel, Novinky, Tema, Clanek, \
|
||||||
|
Deadline # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci
|
||||||
#from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva
|
#from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva
|
||||||
from seminar import utils
|
from seminar import utils
|
||||||
from treenode import treelib
|
from treenode import treelib
|
||||||
import treenode.templatetags as tnltt
|
import treenode.templatetags as tnltt
|
||||||
import treenode.serializers as vr
|
import treenode.serializers as vr
|
||||||
from vysledkovky.utils import body_resitelu
|
from vysledkovky.utils import body_resitelu, VysledkovkaCisla, \
|
||||||
from vysledkovky.views import vysledkovka_rocniku, vysledkovka_cisla
|
VysledkovkaRocniku, VysledkovkaDoTeXu
|
||||||
|
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
@ -206,26 +208,17 @@ def ZadaniAktualniVysledkovkaView(request):
|
||||||
nastaveni = get_object_or_404(Nastaveni)
|
nastaveni = get_object_or_404(Nastaveni)
|
||||||
# Aktualni verejna vysledkovka
|
# Aktualni verejna vysledkovka
|
||||||
rocnik = nastaveni.aktualni_rocnik
|
rocnik = nastaveni.aktualni_rocnik
|
||||||
context = vysledkovka_rocniku(
|
context = {'vysledkovka': VysledkovkaRocniku(rocnik, True)}
|
||||||
rocnik=rocnik,
|
|
||||||
request=request,
|
|
||||||
sneverejnou=True
|
|
||||||
)
|
|
||||||
|
|
||||||
# kdyz neni verejna vysledkovka, tak zobraz starou
|
# kdyz neni verejna vysledkovka, tak zobraz starou
|
||||||
if len(context['cisla']) == 0:
|
if len(context['vysledkovka'].cisla_rocniku) == 0:
|
||||||
try:
|
try:
|
||||||
minuly_rocnik = Rocnik.objects.get(
|
minuly_rocnik = Rocnik.objects.get(
|
||||||
prvni_rok=(rocnik.prvni_rok-1))
|
rocnik=(rocnik.rocnik-1))
|
||||||
rocnik = minuly_rocnik
|
rocnik = minuly_rocnik
|
||||||
|
|
||||||
# Přepíšeme prázdnou výsledkovku výsledkovkou z minulého ročníku
|
# Přepíšeme prázdnou výsledkovku výsledkovkou z minulého ročníku
|
||||||
context = vysledkovka_rocniku(
|
context['vysledkovka'] = VysledkovkaRocniku(rocnik, True)
|
||||||
rocnik=rocnik,
|
|
||||||
context=context,
|
|
||||||
request=request,
|
|
||||||
sneverejnou=True
|
|
||||||
)
|
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -268,23 +261,8 @@ class TitulniStranaView(generic.ListView):
|
||||||
context = super(TitulniStranaView, self).get_context_data(**kwargs)
|
context = super(TitulniStranaView, self).get_context_data(**kwargs)
|
||||||
nastaveni = get_object_or_404(Nastaveni)
|
nastaveni = get_object_or_404(Nastaveni)
|
||||||
|
|
||||||
deadline_soustredeni = (nastaveni.aktualni_cislo.datum_deadline_soustredeni, "soustredeni")
|
deadline = m.Deadline.objects.filter(deadline__gte=timezone.now()).order_by("deadline").first()
|
||||||
preddeadline = (nastaveni.aktualni_cislo.datum_preddeadline, "preddeadline")
|
context['nejblizsi_deadline'] = deadline
|
||||||
deadline = (nastaveni.aktualni_cislo.datum_deadline, "deadline")
|
|
||||||
|
|
||||||
try:
|
|
||||||
nejblizsi_deadline = sorted(filter(lambda dl: dl[0] is not None and dl[0] >= date.today(), [deadline_soustredeni, preddeadline, deadline]))[0]
|
|
||||||
if nejblizsi_deadline[0] == deadline_soustredeni[0]:
|
|
||||||
nejblizsi_deadline = deadline_soustredeni
|
|
||||||
except IndexError:
|
|
||||||
nejblizsi_deadline = (None, None) # neni zadna aktualni deadline
|
|
||||||
|
|
||||||
if nejblizsi_deadline[0] is not None:
|
|
||||||
context['nejblizsi_deadline'] = datetime.combine(nejblizsi_deadline[0], datetime.max.time())
|
|
||||||
else:
|
|
||||||
context['nejblizsi_deadline'] = None
|
|
||||||
|
|
||||||
context['typ_deadline'] = nejblizsi_deadline[1]
|
|
||||||
|
|
||||||
# Aktuální témata
|
# Aktuální témata
|
||||||
nazvy_a_odkazy_na_aktualni_temata = []
|
nazvy_a_odkazy_na_aktualni_temata = []
|
||||||
|
@ -377,17 +355,10 @@ class RocnikView(generic.DetailView):
|
||||||
return get_object_or_404(queryset,rocnik=self.kwargs.get('rocnik'))
|
return get_object_or_404(queryset,rocnik=self.kwargs.get('rocnik'))
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
start = time.time()
|
|
||||||
context = super(RocnikView, self).get_context_data(**kwargs)
|
context = super(RocnikView, self).get_context_data(**kwargs)
|
||||||
context = vysledkovka_rocniku(
|
context["vysledkovka"] = VysledkovkaRocniku(context["rocnik"], True)
|
||||||
rocnik=context["rocnik"],
|
context["neprazdna_vysledkovka"] = len(context['vysledkovka'].cisla_rocniku) != 0
|
||||||
context=context,
|
context["vysledkovka_neverejna"] = VysledkovkaRocniku(context["rocnik"], False)
|
||||||
request=self.request,
|
|
||||||
sneverejnou=True
|
|
||||||
)
|
|
||||||
end = time.time()
|
|
||||||
print("Kontext:", end-start)
|
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def resiteleRocnikuCsvExportView(request, rocnik):
|
def resiteleRocnikuCsvExportView(request, rocnik):
|
||||||
|
@ -452,8 +423,23 @@ class CisloView(generic.DetailView):
|
||||||
|
|
||||||
cislo = context['cislo']
|
cislo = context['cislo']
|
||||||
context['prevcislo'] = Cislo.objects.filter((Q(rocnik__lt=self.object.rocnik) | Q(poradi__lt=self.object.poradi))&Q(rocnik__lte=self.object.rocnik)).first()
|
context['prevcislo'] = Cislo.objects.filter((Q(rocnik__lt=self.object.rocnik) | Q(poradi__lt=self.object.poradi))&Q(rocnik__lte=self.object.rocnik)).first()
|
||||||
# vrátíme context (aktuálně obsahuje jen věci ohledně výsledkovky
|
|
||||||
return vysledkovka_cisla(cislo, context)
|
deadliny = Deadline.objects.filter(cislo=cislo).reverse()
|
||||||
|
deadliny_s_vysledkovkami = []
|
||||||
|
|
||||||
|
nadpisy = {
|
||||||
|
m.Deadline.TYP_CISLA: "Výsledkovka",
|
||||||
|
m.Deadline.TYP_PRVNI: "Výsledkovka do prvního deadlinu",
|
||||||
|
m.Deadline.TYP_PRVNI_A_SOUS: "Výsledkovka do prvního deadlinu a deadlinu pro účast na soustředění",
|
||||||
|
m.Deadline.TYP_SOUS: "Výsledkovka do deadlinu pro účast na soustředění",
|
||||||
|
}
|
||||||
|
|
||||||
|
for deadline in deadliny:
|
||||||
|
if self.request.user.je_org | deadline.verejna_vysledkovka:
|
||||||
|
deadliny_s_vysledkovkami.append((deadline, nadpisy[deadline.typ], VysledkovkaCisla(cislo, not self.request.user.je_org, deadline)))
|
||||||
|
|
||||||
|
context['deadliny_s_vysledkovkami'] = deadliny_s_vysledkovkami
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
class ArchivTemataView(generic.ListView):
|
class ArchivTemataView(generic.ListView):
|
||||||
|
@ -476,17 +462,41 @@ class OdmenyView(generic.TemplateView):
|
||||||
fromcislo = Cislo.objects.get(rocnik=self.kwargs.get('frocnik'), poradi=self.kwargs.get('fcislo'))
|
fromcislo = Cislo.objects.get(rocnik=self.kwargs.get('frocnik'), poradi=self.kwargs.get('fcislo'))
|
||||||
tocislo = Cislo.objects.get(rocnik=self.kwargs.get('trocnik'), poradi=self.kwargs.get('tcislo'))
|
tocislo = Cislo.objects.get(rocnik=self.kwargs.get('trocnik'), poradi=self.kwargs.get('tcislo'))
|
||||||
resitele = aktivniResitele(tocislo)
|
resitele = aktivniResitele(tocislo)
|
||||||
frombody = body_resitelu(resitele, fromcislo)
|
|
||||||
tobody = body_resitelu(resitele, tocislo)
|
def get_diff(from_deadline: Deadline, to_deadline: Deadline):
|
||||||
outlist = []
|
frombody = body_resitelu(resitele=resitele, jen_verejne=False, do=from_deadline)
|
||||||
for (aid, tbody) in tobody.items():
|
tobody = body_resitelu(resitele=resitele, jen_verejne=False, do=to_deadline)
|
||||||
fbody = frombody.get(aid,0)
|
outlist = []
|
||||||
resitel = Resitel.objects.get(pk=aid)
|
for (aid, tbody) in tobody.items():
|
||||||
ftitul = resitel.get_titul(fbody)
|
fbody = frombody.get(aid,0)
|
||||||
ttitul = resitel.get_titul(tbody)
|
resitel = Resitel.objects.get(pk=aid)
|
||||||
if ftitul != ttitul:
|
ftitul = resitel.get_titul(fbody)
|
||||||
outlist.append({'jmeno': resitel.osoba.plne_jmeno(), 'ftitul': ftitul, 'ttitul': ttitul})
|
ttitul = resitel.get_titul(tbody)
|
||||||
context['zmeny'] = outlist
|
if ftitul != ttitul:
|
||||||
|
outlist.append({'jmeno': resitel.osoba.plne_jmeno(), 'ftitul': ftitul, 'ttitul': ttitul})
|
||||||
|
return outlist
|
||||||
|
|
||||||
|
def posledni_deadline_oprava(cislo: Cislo) -> Deadline:
|
||||||
|
posledni_deadline = cislo.posledni_deadline
|
||||||
|
if posledni_deadline is None:
|
||||||
|
return Deadline.objects.filter(Q(cislo__poradi__lt=cislo.poradi, cislo__rocnik=cislo.rocnik) | Q(cislo__rocnik__rocnik__lt=cislo.rocnik.rocnik)).order_by("deadline").last()
|
||||||
|
return posledni_deadline
|
||||||
|
|
||||||
|
context["from_cislo"] = fromcislo
|
||||||
|
context["to_cislo"] = tocislo
|
||||||
|
context["zmeny_prvni_prvni"] = get_diff(
|
||||||
|
fromcislo.zlomovy_deadline_pro_papirove_cislo(),
|
||||||
|
tocislo.zlomovy_deadline_pro_papirove_cislo()
|
||||||
|
)
|
||||||
|
context["zmeny_prvni_posledni"] = get_diff(
|
||||||
|
fromcislo.zlomovy_deadline_pro_papirove_cislo(),
|
||||||
|
posledni_deadline_oprava(tocislo)
|
||||||
|
)
|
||||||
|
context["zmeny_posledni_prvni"] = get_diff(
|
||||||
|
posledni_deadline_oprava(fromcislo),
|
||||||
|
tocislo.zlomovy_deadline_pro_papirove_cislo()
|
||||||
|
)
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@ -504,6 +514,60 @@ class CisloVysledkovkaView(CisloView):
|
||||||
content_type = 'text/plain; charset=UTF8'
|
content_type = 'text/plain; charset=UTF8'
|
||||||
#vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani
|
#vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(CisloVysledkovkaView, self).get_context_data()
|
||||||
|
cislo = context['cislo']
|
||||||
|
|
||||||
|
cislopred = cislo.predchozi()
|
||||||
|
if cislopred is not None:
|
||||||
|
context['vysledkovka'] = VysledkovkaDoTeXu(
|
||||||
|
cislo,
|
||||||
|
od_vyjma=cislopred.zlomovy_deadline_pro_papirove_cislo(),
|
||||||
|
do_vcetne=cislo.zlomovy_deadline_pro_papirove_cislo(),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
context['vysledkovka'] = VysledkovkaCisla(
|
||||||
|
cislo,
|
||||||
|
jen_verejne=False,
|
||||||
|
do_deadlinu=cislo.zlomovy_deadline_pro_papirove_cislo(),
|
||||||
|
)
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
# Podle předchozího
|
||||||
|
class PosledniCisloVysledkovkaView(generic.DetailView):
|
||||||
|
"""View vytvořené pro zobrazení výsledkovky posledního čísla v TeXu."""
|
||||||
|
|
||||||
|
model = Rocnik
|
||||||
|
template_name = 'seminar/archiv/cislo_vysledkovka.tex'
|
||||||
|
content_type = 'text/plain; charset=UTF8'
|
||||||
|
|
||||||
|
def get_object(self, queryset=None):
|
||||||
|
if queryset is None:
|
||||||
|
queryset = self.get_queryset()
|
||||||
|
rocnik_arg = self.kwargs.get('rocnik')
|
||||||
|
queryset = queryset.filter(rocnik=rocnik_arg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
obj = queryset.get()
|
||||||
|
except queryset.model.DoesNotExist:
|
||||||
|
raise Http404(_("No %(verbose_name)s found matching the query") %
|
||||||
|
{'verbose_name': queryset.model._meta.verbose_name})
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(PosledniCisloVysledkovkaView, self).get_context_data()
|
||||||
|
rocnik = context['rocnik']
|
||||||
|
cislo = rocnik.cisla.order_by("poradi").last()
|
||||||
|
cislopred = cislo.predchozi()
|
||||||
|
context['vysledkovka'] = VysledkovkaDoTeXu(
|
||||||
|
cislo,
|
||||||
|
od_vyjma=cislopred.zlomovy_deadline_pro_papirove_cislo(),
|
||||||
|
do_vcetne=cislo.deadline_v_cisle.order_by("deadline").last(),
|
||||||
|
)
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
class RocnikVysledkovkaView(RocnikView):
|
class RocnikVysledkovkaView(RocnikView):
|
||||||
""" View vytvořené pro stránku zobrazující výsledkovku ročníku v TeXu."""
|
""" View vytvořené pro stránku zobrazující výsledkovku ročníku v TeXu."""
|
||||||
model = Rocnik
|
model = Rocnik
|
||||||
|
@ -556,17 +620,23 @@ def oldObalkovaniView(request, rocnik, cislo):
|
||||||
|
|
||||||
|
|
||||||
### Tituly
|
### Tituly
|
||||||
|
def TitulyViewRocnik(request, rocnik):
|
||||||
|
return TitulyView(request, rocnik, None)
|
||||||
|
|
||||||
|
|
||||||
def TitulyView(request, rocnik, cislo):
|
def TitulyView(request, rocnik, cislo):
|
||||||
""" View pro stažení makra titulů v TeXu."""
|
""" View pro stažení makra titulů v TeXu."""
|
||||||
rocnik_obj = Rocnik.objects.get(rocnik = rocnik)
|
rocnik_obj = Rocnik.objects.get(rocnik = rocnik)
|
||||||
resitele = Resitel.objects.filter(rok_maturity__gte = rocnik_obj.prvni_rok)
|
resitele = Resitel.objects.filter(rok_maturity__gte = rocnik_obj.prvni_rok)
|
||||||
cislo_obj = Cislo.objects.get(rocnik = rocnik_obj, poradi = cislo)
|
|
||||||
|
|
||||||
asciijmena = []
|
asciijmena = []
|
||||||
jmenovci = False # detekuje, zda jsou dva řešitelé jmenovci (modulo nabodeníčka),
|
jmenovci = False # detekuje, zda jsou dva řešitelé jmenovci (modulo nabodeníčka),
|
||||||
# pokud ano, vrátí se jako true
|
# pokud ano, vrátí se jako true
|
||||||
slovnik_s_body = body_resitelu(resitele, cislo_obj)
|
if cislo is not None:
|
||||||
|
cislo_obj = Cislo.objects.get(rocnik=rocnik_obj, poradi=cislo)
|
||||||
|
slovnik_s_body = body_resitelu(do=cislo_obj.zlomovy_deadline_pro_papirove_cislo(), jen_verejne=False)
|
||||||
|
else:
|
||||||
|
slovnik_s_body = body_resitelu(do=Deadline.objects.filter(cislo__rocnik=rocnik_obj).last(), jen_verejne=False)
|
||||||
|
|
||||||
for resitel in resitele:
|
for resitel in resitele:
|
||||||
resitel.titul = resitel.get_titul(slovnik_s_body[resitel.id])
|
resitel.titul = resitel.get_titul(slovnik_s_body[resitel.id])
|
||||||
|
|
|
@ -3,21 +3,21 @@
|
||||||
<tr class='border-b'>
|
<tr class='border-b'>
|
||||||
<th class='border-r'>#
|
<th class='border-r'>#
|
||||||
<th class='border-r'>Jméno
|
<th class='border-r'>Jméno
|
||||||
{% for p in problemy %}
|
{% for p in vysledkovka.temata_a_spol%}
|
||||||
<th class='border-r' id="problem{{ forloop.counter0 }}">{# <a href="{{ p.verejne_url }}"> #}{{ p.kod_v_rocniku }}{# </a> #}
|
<th class='border-r' id="problem{{ oznaceni_vysledkovky }}_{{ forloop.counter0 }}">{# <a href="{{ p.verejne_url }}"> #}{{ p.kod_v_rocniku }}{# </a> #}
|
||||||
|
|
||||||
{# TODELETE #}
|
{# TODELETE #}
|
||||||
{% for podproblemy in podproblemy_iter.next %}
|
{% for podproblemy in vysledkovka.podproblemy_iter.next %}
|
||||||
<th class='border-r podproblem{{ forloop.parentloop.counter0 }} podproblem'>{# <a href="{{ podproblemy.verejne_url }}"> #}{{ podproblemy.kod_v_rocniku }}{# </a> #}
|
<th class='border-r podproblem{{ oznaceni_vysledkovky }}_{{ forloop.parentloop.counter0 }} podproblem'>{# <a href="{{ podproblemy.verejne_url }}"> #}{{ podproblemy.kod_v_rocniku }}{# </a> #}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{# TODELETE #}
|
{# TODELETE #}
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if ostatni %}<th class='border-r' id='problem{{ problemy | length }}'>Ostatní {% endif %}
|
{% if vysledkovka.je_nejake_ostatni %}<th class='border-r' id='problem{{ oznaceni_vysledkovky }}_{{ vysledkovka.temata_a_spol| length }}'>Ostatní {% endif %}
|
||||||
|
|
||||||
{# TODELETE #}
|
{# TODELETE #}
|
||||||
{% for podproblemy in podproblemy_iter.next %}
|
{% for podproblemy in vysledkovka.podproblemy_iter.next %}
|
||||||
<th class='border-r podproblem{{ problemy | length }} podproblem'>{# <a href="{{ podproblemy.verejne_url }}"> #}{{ podproblemy.kod_v_rocniku }}{# </a> #}
|
<th class='border-r podproblem{{ oznaceni_vysledkovky }}_{{ vysledkovka.temata_a_spol| length }} podproblem'>{# <a href="{{ podproblemy.verejne_url }}"> #}{{ podproblemy.kod_v_rocniku }}{# </a> #}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{# TODELETE #}
|
{# TODELETE #}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
<th class='border-r'>Za číslo
|
<th class='border-r'>Za číslo
|
||||||
<th class='border-r'>Za ročník
|
<th class='border-r'>Za ročník
|
||||||
<th class='border-r'>Odjakživa
|
<th class='border-r'>Odjakživa
|
||||||
{% for rv in radky_vysledkovky %}
|
{% for rv in vysledkovka.radky_vysledkovky %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %}
|
<td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %}
|
||||||
<th class='border-r'>
|
<th class='border-r'>
|
||||||
|
@ -33,14 +33,12 @@
|
||||||
{{ rv.titul }}<sup>MM</sup>
|
{{ rv.titul }}<sup>MM</sup>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ rv.resitel.osoba.plne_jmeno }}
|
{{ rv.resitel.osoba.plne_jmeno }}
|
||||||
{% for b in rv.body_problemy_sezn %}
|
{% for b in rv.body_za_temata_seznam %}
|
||||||
<td class='border-r'>{{ b }}
|
<td class='border-r'>{{ b }}
|
||||||
|
|
||||||
{# TODELETE #}
|
|
||||||
{% for body_podproblemu in rv.body_podproblemy_iter.next %}
|
{% for body_podproblemu in rv.body_podproblemy_iter.next %}
|
||||||
<td class='border-r podproblem{{ forloop.parentloop.counter0 }} podproblem'>{{ body_podproblemu }}
|
<td class='border-r podproblem{{ oznaceni_vysledkovky }}_{{ forloop.parentloop.counter0 }} podproblem'>{{ body_podproblemu }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{# TODELETE #}
|
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<td class='border-r'>{{ rv.body_cislo }}
|
<td class='border-r'>{{ rv.body_cislo }}
|
||||||
|
@ -55,29 +53,29 @@
|
||||||
|
|
||||||
{# TODELETE #}
|
{# TODELETE #}
|
||||||
<script>
|
<script>
|
||||||
{% for p in problemy %}
|
{% for p in vysledkovka.temata_a_spol%}
|
||||||
diplayed{{ forloop.counter0 }} = false;
|
displayed{{ oznaceni_vysledkovky }}_{{ forloop.counter0 }} = false;
|
||||||
$(".podproblem{{ forloop.counter0 }}").css("display", "none")
|
$(".podproblem{{ oznaceni_vysledkovky }}_{{ forloop.counter0 }}").css("display", "none")
|
||||||
$("#problem{{ forloop.counter0 }}")[0].addEventListener('click', podproblem{{ forloop.counter0 }});
|
$("#problem{{ oznaceni_vysledkovky }}_{{ forloop.counter0 }}")[0].addEventListener('click', podproblem{{ oznaceni_vysledkovky }}_{{ forloop.counter0 }});
|
||||||
function podproblem{{ forloop.counter0 }}(event) {
|
function podproblem{{ oznaceni_vysledkovky }}_{{ forloop.counter0 }}(event) {
|
||||||
diplayed{{ forloop.counter0 }} = !diplayed{{ forloop.counter0 }};
|
displayed{{ oznaceni_vysledkovky }}_{{ forloop.counter0 }} = !displayed{{ oznaceni_vysledkovky }}_{{ forloop.counter0 }};
|
||||||
if (diplayed{{ forloop.counter0 }}) {
|
if (displayed{{ oznaceni_vysledkovky }}_{{ forloop.counter0 }}) {
|
||||||
$(".podproblem{{ forloop.counter0 }}").css("display", "");
|
$(".podproblem{{ oznaceni_vysledkovky }}_{{ forloop.counter0 }}").css("display", "");
|
||||||
} else {
|
} else {
|
||||||
$(".podproblem{{ forloop.counter0 }}").css("display", "none");
|
$(".podproblem{{ oznaceni_vysledkovky }}_{{ forloop.counter0 }}").css("display", "none");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if ostatni %}
|
{% if vysledkovka.je_nejake_ostatni %}
|
||||||
diplayed{{ problemy | length }} = false;
|
displayed{{ oznaceni_vysledkovky }}_{{ vysledkovka.temata_a_spol| length }} = false;
|
||||||
$(".podproblem{{ problemy | length }}").css("display", "none")
|
$(".podproblem{{ oznaceni_vysledkovky }}_{{ vysledkovka.temata_a_spol| length }}").css("display", "none")
|
||||||
$("#problem{{ problemy | length }}")[0].addEventListener('click', podproblem{{ problemy | length }});
|
$("#problem{{ oznaceni_vysledkovky }}_{{ vysledkovka.temata_a_spol| length }}")[0].addEventListener('click', podproblem{{ oznaceni_vysledkovky }}_{{ vysledkovka.temata_a_spol| length }});
|
||||||
function podproblem{{ problemy | length }}(event) {
|
function podproblem{{ oznaceni_vysledkovky }}_{{ vysledkovka.temata_a_spol| length }}(event) {
|
||||||
diplayed{{ problemy | length }} = !diplayed{{ problemy | length }};
|
displayed{{ oznaceni_vysledkovky }}_{{ vysledkovka.temata_a_spol| length }} = !displayed{{ oznaceni_vysledkovky }}_{{ vysledkovka.temata_a_spol| length }};
|
||||||
if (diplayed{{ problemy | length }}) {
|
if (displayed{{ oznaceni_vysledkovky }}_{{ vysledkovka.temata_a_spol| length }}) {
|
||||||
$(".podproblem{{ problemy | length }}").css("display", "");
|
$(".podproblem{{ oznaceni_vysledkovky }}_{{ vysledkovka.temata_a_spol| length }}").css("display", "");
|
||||||
} else {
|
} else {
|
||||||
$(".podproblem{{ problemy | length }}").css("display", "none");
|
$(".podproblem{{ oznaceni_vysledkovky }}_{{ vysledkovka.temata_a_spol| length }}").css("display", "none");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
<th class='border-r'>Jméno
|
<th class='border-r'>Jméno
|
||||||
<th class='border-r'>R.
|
<th class='border-r'>R.
|
||||||
<th class='border-r'>Odjakživa
|
<th class='border-r'>Odjakživa
|
||||||
{% for c in cisla %}
|
{% for c in vysledkovka.cisla_rocniku %}
|
||||||
<th class='border-r'><a href="{{ c.verejne_url }}">
|
<th class='border-r'><a href="{{ c.verejne_url }}">
|
||||||
{{c.rocnik.rocnik}}.{{ c.poradi }}</a>
|
{{c.rocnik.rocnik}}.{{ c.poradi }}</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<th class='border-r'>Celkem
|
<th class='border-r'>Celkem
|
||||||
|
|
||||||
{% for rv in radky_vysledkovky %}
|
{% for rv in vysledkovka.radky_vysledkovky %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %}
|
<td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %}
|
||||||
<th class='border-r'>
|
<th class='border-r'>
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
{{ rv.resitel.osoba.plne_jmeno }}
|
{{ rv.resitel.osoba.plne_jmeno }}
|
||||||
<td class='border-r'>{{ rv.rocnik_resitele }}
|
<td class='border-r'>{{ rv.rocnik_resitele }}
|
||||||
<td class='border-r'>{{ rv.body_celkem_odjakziva }}
|
<td class='border-r'>{{ rv.body_celkem_odjakziva }}
|
||||||
{% for b in rv.body_cisla_sezn %}
|
{% for b in rv.body_cisla_seznam %}
|
||||||
<td class='border-r'>{{ b }}
|
<td class='border-r'>{{ b }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<td class='border-r'><b>{{ rv.body_rocnik }}</b>
|
<td class='border-r'><b>{{ rv.body_rocnik }}</b>
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
{% include "vysledkovky/vysledkovka_rocnik.html" with radky_vysledkovky=radky_vysledkovky_s_neverejnymi cisla=cisla_s_neverejnymi %}
|
|
|
@ -1,462 +1,492 @@
|
||||||
|
import abc
|
||||||
|
from functools import cached_property
|
||||||
|
from typing import Union # TODO: s pythonem 3.10 přepsat na '|'
|
||||||
|
|
||||||
import seminar.models as m
|
import seminar.models as m
|
||||||
from django.db.models import Q, Sum, Count
|
from django.db.models import Q, Sum
|
||||||
from seminar.utils import aktivniResitele, resi_v_rocniku, cisla_rocniku, hlavni_problem, hlavni_problemy_f, problemy_cisla, podproblemy_v_cislu
|
from seminar.utils import resi_v_rocniku
|
||||||
import time
|
|
||||||
|
|
||||||
ROCNIK_ZRUSENI_TEMAT = 25
|
ROCNIK_ZRUSENI_TEMAT = 25
|
||||||
|
|
||||||
def sloupec_s_poradim(setrizene_body):
|
|
||||||
"""
|
|
||||||
Ze seznamu obsahujícího sestupně setřízené body řešitelů za daný ročník
|
|
||||||
vytvoří seznam s pořadími (včetně 3.-5. a pak 2 volná místa atp.),
|
|
||||||
podle toho, jak jdou za sebou ve výsledkovce.
|
|
||||||
Parametr:
|
|
||||||
setrizene_body (seznam integerů): sestupně setřízená čísla
|
|
||||||
|
|
||||||
Výstup:
|
|
||||||
sloupec_s_poradim (seznam stringů)
|
|
||||||
"""
|
|
||||||
|
|
||||||
# ze seznamu obsahujícího setřízené body spočítáme sloupec s pořadím
|
|
||||||
aktualni_poradi = 1
|
|
||||||
sloupec_s_poradim = []
|
|
||||||
|
|
||||||
# seskupíme seznam všech bodů podle hodnot
|
|
||||||
for index in range(0, len(setrizene_body)):
|
|
||||||
# pokud je pořadí větší než číslo řádku, tak jsme vypsali větší rozsah a chceme
|
|
||||||
# vypsat už jen prázdné místo, než dojdeme na správný řádek
|
|
||||||
if (index + 1) < aktualni_poradi:
|
|
||||||
sloupec_s_poradim.append("")
|
|
||||||
continue
|
|
||||||
velikost_skupiny = 0
|
|
||||||
# zjistíme počet po sobě jdoucích stejných hodnot
|
|
||||||
while setrizene_body[index] == setrizene_body[index + velikost_skupiny]:
|
|
||||||
velikost_skupiny = velikost_skupiny + 1
|
|
||||||
# na konci musíme ošetřit přetečení seznamu
|
|
||||||
if (index + velikost_skupiny) > len(setrizene_body) - 1:
|
|
||||||
break
|
|
||||||
# pokud je velikost skupiny 1, vypíšu pořadí
|
|
||||||
if velikost_skupiny == 1:
|
|
||||||
sloupec_s_poradim.append("{}.".format(aktualni_poradi))
|
|
||||||
# pokud je skupina větší, vypíšu rozsah
|
|
||||||
else:
|
|
||||||
sloupec_s_poradim.append("{}.–{}.".format(aktualni_poradi,
|
|
||||||
aktualni_poradi+velikost_skupiny-1))
|
|
||||||
# zvětšíme aktuální pořadí o tolik, kolik pozic bylo přeskočeno
|
|
||||||
aktualni_poradi = aktualni_poradi + velikost_skupiny
|
|
||||||
return sloupec_s_poradim
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def body_resitelu(resitele, za, odjakziva=True, jen_verejne=False):
|
|
||||||
""" Funkce počítající počty bodů pro zadané řešitele,
|
|
||||||
buď odjakživa do daného ročníku/čísla anebo za daný ročník/číslo.
|
|
||||||
Parametry:
|
|
||||||
resitele (seznam obsahující položky typu Resitel): aktivní řešitelé
|
|
||||||
za (Rocnik/Cislo): za co se mají počítat body
|
|
||||||
(generování starších výsledkovek)
|
|
||||||
odjakziva (bool): zda se mají počítat body odjakživa, nebo jen za číslo/ročník
|
|
||||||
zadané v "za"
|
|
||||||
Výstup:
|
|
||||||
slovník (Resitel.id):body
|
|
||||||
"""
|
|
||||||
resitele_id = [r.id for r in resitele]
|
|
||||||
# Zjistíme, typ objektu v parametru "za"
|
|
||||||
if isinstance(za, m.Rocnik):
|
|
||||||
cislo = None
|
|
||||||
rocnik = za
|
|
||||||
rok = rocnik.prvni_rok
|
|
||||||
elif isinstance(za, m.Cislo):
|
|
||||||
cislo = za
|
|
||||||
rocnik = None
|
|
||||||
rok = cislo.rocnik.prvni_rok
|
|
||||||
else:
|
|
||||||
assert True, "body_resitelu: za není ani číslo ani ročník."
|
|
||||||
|
|
||||||
|
|
||||||
# Kvůli rychlosti používáme sčítáme body už v databázi, viz
|
|
||||||
# https://docs.djangoproject.com/en/3.0/topics/db/aggregation/,
|
|
||||||
# sekce Filtering on annotations (protože potřebujeme filtrovat výsledky
|
|
||||||
# jen do nějakého data, abychom uměli správně nagenerovat výsledkovky i
|
|
||||||
# za historická čísla.
|
|
||||||
|
|
||||||
# Níže se vytváří dotaz na součet bodů za správně vyfiltrovaná hodnocení,
|
|
||||||
# který se použije ve výsledném dotazu.
|
|
||||||
if cislo and odjakziva: # Body se sčítají odjakživa do zadaného čísla.
|
|
||||||
# Vyfiltrujeme všechna hodnocení, která jsou buď ze starších ročníků,
|
|
||||||
# anebo ze stejného ročníku, jak je zadané číslo, tam ale sčítáme jen
|
|
||||||
# pro čísla s pořadím nejvýše stejným, jako má zadané číslo.
|
|
||||||
body_k_zapocteni = Sum('reseni__hodnoceni__body',
|
|
||||||
filter=( Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lt=rok) |
|
|
||||||
Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok=rok,
|
|
||||||
reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi) ))
|
|
||||||
elif cislo and not odjakziva: # Body se sčítají za dané číslo.
|
|
||||||
body_k_zapocteni = Sum('reseni__hodnoceni__body',
|
|
||||||
filter=( Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok=rok,
|
|
||||||
reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi) ))
|
|
||||||
elif rocnik and odjakziva: # Spočítáme body za starší ročníky až do zadaného včetně.
|
|
||||||
if jen_verejne:
|
|
||||||
body_k_zapocteni = Sum('reseni__hodnoceni__body',
|
|
||||||
filter= Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lte=rok,
|
|
||||||
reseni__hodnoceni__cislo_body__verejna_vysledkovka=True))
|
|
||||||
else:
|
|
||||||
body_k_zapocteni = Sum('reseni__hodnoceni__body',
|
|
||||||
filter= Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lte=rok))
|
|
||||||
elif rocnik and not odjakziva: # Spočítáme body za daný ročník.
|
|
||||||
if jen_verejne:
|
|
||||||
body_k_zapocteni = Sum('reseni__hodnoceni__body',
|
|
||||||
filter=Q(reseni__hodnoceni__cislo_body__rocnik=rocnik,
|
|
||||||
reseni__hodnoceni__cislo_body__verejna_vysledkovka=True))
|
|
||||||
else:
|
|
||||||
body_k_zapocteni = Sum('reseni__hodnoceni__body',
|
|
||||||
filter=Q(reseni__hodnoceni__cislo_body__rocnik=rocnik))
|
|
||||||
else:
|
|
||||||
assert True, "body_resitelu: Neplatná kombinace za a odjakživa."
|
|
||||||
|
|
||||||
# Následující řádek přidá ke každému řešiteli údaj ".body" se součtem jejich bodů
|
|
||||||
resitele_s_body = m.Resitel.objects.filter(id__in=resitele_id).annotate(
|
|
||||||
body=body_k_zapocteni)
|
|
||||||
# Teď jen z QuerySetu řešitelů anotovaných body vygenerujeme slovník
|
|
||||||
# indexovaný řešitelským id obsahující body.
|
|
||||||
# Pokud jsou body None, nahradíme za 0.
|
|
||||||
slovnik = {int(res.id) : (res.body if res.body else 0) for res in resitele_s_body}
|
|
||||||
return slovnik
|
|
||||||
|
|
||||||
class RadekVysledkovkyRocniku(object):
|
|
||||||
""" Obsahuje věci, které se hodí vědět při konstruování výsledkovky.
|
|
||||||
Umožňuje snazší práci v templatu (lepší, než seznam)."""
|
|
||||||
|
|
||||||
def __init__(self, poradi, resitel, body_cisla_sezn, body_rocnik, body_odjakziva, rok):
|
|
||||||
self.poradi = poradi
|
|
||||||
self.resitel = resitel
|
|
||||||
self.rocnik_resitele = resitel.rocnik(rok)
|
|
||||||
self.body_rocnik = body_rocnik
|
|
||||||
self.body_celkem_odjakziva = body_odjakziva
|
|
||||||
self.body_cisla_sezn = body_cisla_sezn
|
|
||||||
self.titul = resitel.get_titul(body_odjakziva)
|
|
||||||
|
|
||||||
def setrid_resitele_a_body(slov_resitel_body):
|
|
||||||
setrizeni_resitele_id = [dvojice[0] for dvojice in slov_resitel_body]
|
|
||||||
setrizene_body = [dvojice[1] for dvojice in slov_resitel_body]
|
|
||||||
return setrizeni_resitele_id, setrizene_body
|
|
||||||
|
|
||||||
def data_vysledkovky_rocniku(rocnik, jen_verejne=True):
|
|
||||||
""" Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve
|
|
||||||
formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html"
|
|
||||||
"""
|
|
||||||
|
|
||||||
start = time.time()
|
|
||||||
|
|
||||||
## TODO možná chytřeji vybírat aktivní řešitele
|
|
||||||
# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají
|
|
||||||
# u alespoň jedné hodnoty něco jiného než NULL
|
|
||||||
aktivni_resitele = list(resi_v_rocniku(rocnik))
|
|
||||||
cisla = cisla_rocniku(rocnik, jen_verejne)
|
|
||||||
body_cisla_slov = {}
|
|
||||||
for cislo in cisla:
|
|
||||||
# získáme body za číslo
|
|
||||||
_, cislobody = secti_body_za_cislo(cislo, aktivni_resitele)
|
|
||||||
body_cisla_slov[cislo.id] = cislobody
|
|
||||||
|
|
||||||
# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně
|
|
||||||
resitel_rocnikbody_sezn = secti_body_za_rocnik(rocnik, aktivni_resitele, jen_verejne=jen_verejne)
|
|
||||||
|
|
||||||
# setřídíme řešitele podle počtu bodů a získáme seznam s body od nejvyšších po nenižší
|
|
||||||
setrizeni_resitele_id, setrizene_body = setrid_resitele_a_body(resitel_rocnikbody_sezn)
|
|
||||||
poradi = sloupec_s_poradim(setrizene_body)
|
|
||||||
|
|
||||||
# získáme body odjakživa
|
|
||||||
resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, rocnik, jen_verejne=jen_verejne)
|
|
||||||
|
|
||||||
# vytvoříme jednotlivé sloupce výsledkovky
|
|
||||||
radky_vysledkovky = []
|
|
||||||
i = 0
|
|
||||||
setrizeni_resitele_dict = {} # Tento slovnik se vyrab
|
|
||||||
for r in m.Resitel.objects.filter(id__in=setrizeni_resitele_id).select_related('osoba'):
|
|
||||||
setrizeni_resitele_dict[r.id] = r
|
|
||||||
|
|
||||||
for ar_id in setrizeni_resitele_id:
|
|
||||||
# seznam počtu bodů daného řešitele pro jednotlivá čísla
|
|
||||||
body_cisla_sezn = []
|
|
||||||
for cislo in cisla:
|
|
||||||
body_cisla_sezn.append(body_cisla_slov[cislo.id][ar_id])
|
|
||||||
|
|
||||||
# vytáhneme informace pro daného řešitele
|
|
||||||
radek = RadekVysledkovkyRocniku(
|
|
||||||
poradi[i], # pořadí
|
|
||||||
setrizeni_resitele_dict[ar_id], # řešitel (z id)
|
|
||||||
body_cisla_sezn, # seznam bodů za čísla
|
|
||||||
setrizene_body[i], # body za ročník (spočítané výše s pořadím)
|
|
||||||
resitel_odjakzivabody_slov[ar_id], # body odjakživa
|
|
||||||
rocnik) # ročník semináře pro získání ročníku řešitele
|
|
||||||
radky_vysledkovky.append(radek)
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
end = time.time()
|
|
||||||
print("Vysledkovka rocniku",end-start)
|
|
||||||
|
|
||||||
radky_vysledkovky = [radek for radek in radky_vysledkovky if radek.body_rocnik > 0]
|
|
||||||
return radky_vysledkovky, cisla
|
|
||||||
|
|
||||||
class RadekVysledkovkyCisla(object):
|
|
||||||
"""Obsahuje věci, které se hodí vědět při konstruování výsledkovky.
|
|
||||||
Umožňuje snazší práci v templatu (lepší, než seznam)."""
|
|
||||||
|
|
||||||
def __init__(self, poradi, resitel, body_problemy_sezn,
|
|
||||||
body_cislo, body_rocnik, body_odjakziva, rok, body_podproblemy, body_podproblemy_iter):
|
|
||||||
self.resitel = resitel
|
|
||||||
self.rocnik_resitele = resitel.rocnik(rok)
|
|
||||||
self.body_cislo = body_cislo
|
|
||||||
self.body_rocnik = body_rocnik
|
|
||||||
self.body_celkem_odjakziva = body_odjakziva
|
|
||||||
self.poradi = poradi
|
|
||||||
self.body_problemy_sezn = body_problemy_sezn
|
|
||||||
self.titul = resitel.get_titul(body_odjakziva)
|
|
||||||
self.body_podproblemy = body_podproblemy
|
|
||||||
self.body_podproblemy_iter = body_podproblemy_iter # TODELETE
|
|
||||||
|
|
||||||
|
|
||||||
def pricti_body(slovnik, resitel, body):
|
|
||||||
""" Přiřazuje danému řešiteli body do slovníku. """
|
|
||||||
# testujeme na None (""), pokud je to první řešení
|
|
||||||
# daného řešitele, předěláme na 0
|
|
||||||
# (v dalším kroku přičteme reálný počet bodů),
|
|
||||||
# rozlišujeme tím mezi 0 a neodevzdaným řešením
|
|
||||||
|
|
||||||
# Speciálně pokud jsou body None (hodnocení není obodované), vraťse
|
|
||||||
# TODO nejde to udělat lépe?
|
|
||||||
if body is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
if slovnik[resitel.id] == "":
|
|
||||||
slovnik[resitel.id] = 0
|
|
||||||
|
|
||||||
slovnik[resitel.id] += body
|
|
||||||
|
|
||||||
def secti_body_za_rocnik(za, aktivni_resitele, jen_verejne):
|
|
||||||
""" Spočítá body za ročník (celý nebo do daného čísla),
|
|
||||||
setřídí je sestupně a vrátí jako seznam.
|
|
||||||
Parametry:
|
|
||||||
za (typu Rocnik nebo Cislo) spočítá za ročník, nebo za ročník až do
|
|
||||||
daného čísla
|
|
||||||
"""
|
|
||||||
# spočítáme všem řešitelům jejich body za ročník (False => ne odjakživa)
|
|
||||||
resitel_rocnikbody_slov = body_resitelu(aktivni_resitele, za, False, jen_verejne=jen_verejne)
|
|
||||||
# zeptáme se na dvojice (řešitel, body) za ročník a setřídíme sestupně
|
|
||||||
resitel_rocnikbody_sezn = sorted(resitel_rocnikbody_slov.items(),
|
|
||||||
key = lambda x: x[1], reverse = True)
|
|
||||||
return resitel_rocnikbody_sezn
|
|
||||||
|
|
||||||
def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None):
|
|
||||||
""" Spočítá u řešitelů body za číslo a za jednotlivé hlavní problémy (témata)."""
|
|
||||||
# TODO setřídit hlavní problémy čísla podle id, ať jsou ve stejném pořadí pokaždé
|
|
||||||
# pro každý hlavní problém zavedeme slovník s body za daný hlavní problém
|
|
||||||
# pro jednotlivé řešitele (slovník slovníků hlavních problémů)
|
|
||||||
|
|
||||||
print("Scitam cislo",cislo)
|
|
||||||
|
|
||||||
if hlavni_problemy is None:
|
|
||||||
hlavni_problemy = hlavni_problemy_f(problemy_cisla(cislo))
|
|
||||||
|
|
||||||
def ne_clanek_ne_konfera(problem):
|
|
||||||
inst = problem.get_real_instance()
|
|
||||||
return not(isinstance(inst, m.Clanek) or isinstance(inst, m.Konfera))
|
|
||||||
|
|
||||||
if cislo.rocnik.rocnik < ROCNIK_ZRUSENI_TEMAT:
|
|
||||||
temata_a_spol = hlavni_problemy
|
|
||||||
else:
|
|
||||||
temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy))
|
|
||||||
|
|
||||||
hlavni_problemy_slovnik = {}
|
|
||||||
for hp in temata_a_spol:
|
|
||||||
hlavni_problemy_slovnik[hp.id] = {}
|
|
||||||
|
|
||||||
hlavni_problemy_slovnik[-1] = {}
|
|
||||||
|
|
||||||
# zakládání prázdných záznamů pro řešitele
|
|
||||||
cislobody = {}
|
|
||||||
for ar in aktivni_resitele:
|
|
||||||
# řešitele převedeme na řetězec pomocí unikátního id
|
|
||||||
cislobody[ar.id] = ""
|
|
||||||
for hp in temata_a_spol:
|
|
||||||
slovnik = hlavni_problemy_slovnik[hp.id]
|
|
||||||
slovnik[ar.id] = ""
|
|
||||||
|
|
||||||
hlavni_problemy_slovnik[-1][ar.id] = ""
|
|
||||||
|
|
||||||
hodnoceni_do_cisla = m.Hodnoceni.objects.prefetch_related('problem', 'reseni', 'reseni__resitele').filter(cislo_body=cislo)
|
|
||||||
|
|
||||||
start = time.time()
|
|
||||||
|
|
||||||
for hodnoceni in hodnoceni_do_cisla:
|
|
||||||
prob = hodnoceni.problem
|
|
||||||
nadproblem = hlavni_problem(prob)
|
|
||||||
if ne_clanek_ne_konfera(nadproblem):
|
|
||||||
nadproblem_slovnik = hlavni_problemy_slovnik[nadproblem.id]
|
|
||||||
else:
|
|
||||||
nadproblem_slovnik = hlavni_problemy_slovnik[-1]
|
|
||||||
|
|
||||||
body = hodnoceni.body
|
|
||||||
|
|
||||||
# a mít více řešitelů
|
|
||||||
for resitel in hodnoceni.reseni.resitele.all():
|
|
||||||
if resitel not in aktivni_resitele:
|
|
||||||
print("Skipping {}".format(resitel.id))
|
|
||||||
continue
|
|
||||||
pricti_body(cislobody, resitel, body)
|
|
||||||
pricti_body(nadproblem_slovnik, resitel, body)
|
|
||||||
end = time.time()
|
|
||||||
print("for cykly:", end-start)
|
|
||||||
return hlavni_problemy_slovnik, cislobody
|
|
||||||
|
|
||||||
|
|
||||||
def secti_body_za_cislo_podle_temat(cislo, aktivni_resitele, podproblemy=None, temata=None):
|
|
||||||
""" Spočítá u řešitelů body za číslo za úlohy v jednotlivých hlavních problémech (témata)."""
|
|
||||||
if temata is None:
|
|
||||||
temata = hlavni_problemy_f(problemy_cisla(cislo))
|
|
||||||
|
|
||||||
if podproblemy is None:
|
|
||||||
podproblemy_v_cislu(cislo, hlavni_problemy=temata)
|
|
||||||
|
|
||||||
body_slovnik = {}
|
|
||||||
for tema in temata:
|
|
||||||
body_slovnik[tema.id] = {}
|
|
||||||
for problem in podproblemy[tema.id]:
|
|
||||||
body_slovnik[tema.id][problem.id] = {}
|
|
||||||
body_slovnik[-1] = {}
|
|
||||||
for problem in podproblemy[-1]:
|
|
||||||
body_slovnik[-1][problem.id] = {}
|
|
||||||
|
|
||||||
# zakládání prázdných záznamů pro řešitele
|
|
||||||
for ar in aktivni_resitele:
|
|
||||||
for tema in temata:
|
|
||||||
for problem in podproblemy[tema.id]:
|
|
||||||
body_slovnik[tema.id][problem.id][ar.id] = ""
|
|
||||||
|
|
||||||
for problem in podproblemy[-1]:
|
|
||||||
body_slovnik[-1][problem.id][ar.id] = ""
|
|
||||||
|
|
||||||
temata = set(t.id for t in temata)
|
|
||||||
|
|
||||||
hodnoceni_do_cisla = m.Hodnoceni.objects.prefetch_related('problem', 'reseni', 'reseni__resitele').filter(cislo_body=cislo)
|
|
||||||
|
|
||||||
for hodnoceni in hodnoceni_do_cisla:
|
|
||||||
prob = hodnoceni.problem
|
|
||||||
nadproblem = hlavni_problem(prob)
|
|
||||||
if nadproblem.id in temata:
|
|
||||||
nadproblem_slovnik = body_slovnik[nadproblem.id]
|
|
||||||
else:
|
|
||||||
nadproblem_slovnik = body_slovnik[-1]
|
|
||||||
|
|
||||||
problem_slovnik = nadproblem_slovnik[prob.id]
|
|
||||||
|
|
||||||
body = hodnoceni.body
|
|
||||||
|
|
||||||
# a mít více řešitelů
|
|
||||||
for resitel in hodnoceni.reseni.resitele.all():
|
|
||||||
if resitel not in aktivni_resitele:
|
|
||||||
print("Skipping {}".format(resitel.id))
|
|
||||||
continue
|
|
||||||
pricti_body(problem_slovnik, resitel, body)
|
|
||||||
return body_slovnik
|
|
||||||
|
|
||||||
|
|
||||||
# TODELETE
|
|
||||||
class FixedIterator:
|
class FixedIterator:
|
||||||
def next(self):
|
def next(self):
|
||||||
return self.niter.__next__()
|
return self.niter.__next__()
|
||||||
|
|
||||||
def __init__(self, niter):
|
def __init__(self, niter):
|
||||||
self.niter = niter
|
self.niter = niter
|
||||||
# TODELETE
|
|
||||||
|
|
||||||
|
|
||||||
def data_vysledkovky_cisla(cislo):
|
def body_resitelu(
|
||||||
problemy = problemy_cisla(cislo)
|
za: Union[m.Cislo, m.Rocnik, None] = None,
|
||||||
hlavni_problemy = hlavni_problemy_f(problemy)
|
do: m.Deadline = None,
|
||||||
## TODO možná chytřeji vybírat aktivní řešitele
|
od: m.Deadline = None,
|
||||||
# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají
|
jen_verejne: bool = True,
|
||||||
# u alespoň jedné hodnoty něco jiného než NULL
|
resitele=None,
|
||||||
aktivni_resitele = list(resi_v_rocniku(cislo.rocnik))
|
null=0 # Výchozí hodnota, pokud pro daného řešitele nejsou body
|
||||||
|
) -> dict[int, int]:
|
||||||
|
filtr = Q()
|
||||||
|
|
||||||
# získáme body za číslo
|
if jen_verejne:
|
||||||
hlavni_problemy_slovnik, cislobody = secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy)
|
filtr &= Q(reseni__hodnoceni__deadline_body__verejna_vysledkovka=True)
|
||||||
|
|
||||||
# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně
|
# Zjistíme, typ objektu v parametru "za"
|
||||||
resitel_rocnikbody_sezn = secti_body_za_rocnik(cislo, aktivni_resitele, jen_verejne=True)
|
if isinstance(za, m.Rocnik):
|
||||||
|
filtr &= Q(reseni__hodnoceni__deadline_body__cislo__rocnik=za)
|
||||||
|
elif isinstance(za, m.Cislo):
|
||||||
|
filtr &= Q(reseni__hodnoceni__deadline_body__cislo=za)
|
||||||
|
|
||||||
# získáme body odjakživa
|
if do:
|
||||||
resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, cislo, jen_verejne=True)
|
filtr &= Q(reseni__hodnoceni__deadline_body__deadline__lte=do.deadline)
|
||||||
|
|
||||||
# řešitelé setřídění podle bodů za číslo sestupně
|
if od:
|
||||||
setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn]
|
filtr &= Q(reseni__hodnoceni__deadline_body__deadline__gte=od.deadline)
|
||||||
|
|
||||||
# spočítáme pořadí řešitelů
|
|
||||||
setrizeni_resitele_body = [dvojice[1] for dvojice in resitel_rocnikbody_sezn]
|
|
||||||
poradi = sloupec_s_poradim(setrizeni_resitele_body)
|
|
||||||
|
|
||||||
# vytvoříme jednotlivé sloupce výsledkovky
|
resiteleQuery = m.Resitel.objects.all()
|
||||||
radky_vysledkovky = []
|
|
||||||
i = 0
|
|
||||||
|
|
||||||
|
if resitele is not None:
|
||||||
|
resitele_id = [r.id for r in resitele]
|
||||||
|
resiteleQuery = resiteleQuery.filter(id__in=resitele_id)
|
||||||
|
|
||||||
|
# Přidáme ke každému řešiteli údaj ".body" se součtem jejich bodů
|
||||||
|
resitele_s_body = resiteleQuery.annotate(
|
||||||
|
body=Sum('reseni__hodnoceni__body', filter=filtr))
|
||||||
|
|
||||||
|
# Teď jen z QuerySetu řešitelů anotovaných body vygenerujeme slovník
|
||||||
|
# indexovaný řešitelským id obsahující body.
|
||||||
|
# Pokud jsou body None, nahradíme za 0.
|
||||||
|
slovnik = {
|
||||||
|
int(res.id): (res.body if res.body else null) for res in resitele_s_body
|
||||||
|
}
|
||||||
|
return slovnik
|
||||||
|
|
||||||
|
|
||||||
|
class Vysledkovka(abc.ABC):
|
||||||
|
jen_verejne: bool
|
||||||
|
rocnik: m.Rocnik
|
||||||
|
do_deadlinu: m.Deadline
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
|
def aktivni_resitele(self) -> list[m.Resitel]:
|
||||||
|
...
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def resitele_s_body_za_rocnik_setrizeny_seznam(self) -> list[tuple[int, int]]:
|
||||||
|
# spočítáme všem řešitelům jejich body za ročník
|
||||||
|
resitel_body_za_rocnik_slovnik = body_resitelu(
|
||||||
|
resitele=self.aktivni_resitele,
|
||||||
|
za=self.rocnik,
|
||||||
|
jen_verejne=self.jen_verejne,
|
||||||
|
do=self.do_deadlinu
|
||||||
|
)
|
||||||
|
|
||||||
|
# zeptáme se na dvojice (řešitel, body) za ročník a setřídíme sestupně
|
||||||
|
resitele_s_body_za_rocnik_setrizeny_seznam = sorted(
|
||||||
|
resitel_body_za_rocnik_slovnik.items(),
|
||||||
|
key=lambda x: x[1], reverse=True
|
||||||
|
)
|
||||||
|
|
||||||
|
return resitele_s_body_za_rocnik_setrizeny_seznam
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def body_za_rocnik_seznamy(self) -> tuple[list[int], list[int]]:
|
||||||
|
if len(self.resitele_s_body_za_rocnik_setrizeny_seznam) == 0:
|
||||||
|
return [], []
|
||||||
|
return tuple(zip(*self.resitele_s_body_za_rocnik_setrizeny_seznam))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def setrizeni_resitele_id(self) -> list[int]:
|
||||||
|
return self.body_za_rocnik_seznamy[0]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def setrizene_body(self) -> list[int]:
|
||||||
|
return self.body_za_rocnik_seznamy[1]
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def resitel_body_odjakziva_slovnik(self) -> dict[int, int]:
|
||||||
|
return body_resitelu(jen_verejne=self.jen_verejne, do=self.do_deadlinu)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def poradi(self):
|
||||||
|
# ze seznamu obsahujícího setřízené body spočítáme sloupec s pořadím
|
||||||
|
aktualni_poradi = 1
|
||||||
|
sloupec_s_poradim = []
|
||||||
|
|
||||||
|
# seskupíme seznam všech bodů podle hodnot
|
||||||
|
for index in range(0, len(self.setrizene_body)):
|
||||||
|
# pokud je pořadí větší než číslo řádku, tak jsme vypsali větší rozsah
|
||||||
|
# a chceme vypsat už jen prázdné místo, než dojdeme na správný řádek
|
||||||
|
if (index + 1) < aktualni_poradi:
|
||||||
|
sloupec_s_poradim.append("")
|
||||||
|
continue
|
||||||
|
velikost_skupiny = 0
|
||||||
|
# zjistíme počet po sobě jdoucích stejných hodnot
|
||||||
|
while self.setrizene_body[index] == self.setrizene_body[
|
||||||
|
index + velikost_skupiny]:
|
||||||
|
velikost_skupiny += 1
|
||||||
|
# na konci musíme ošetřit přetečení seznamu
|
||||||
|
if (index + velikost_skupiny) > len(self.setrizene_body) - 1:
|
||||||
|
break
|
||||||
|
# pokud je velikost skupiny 1, vypíšu pořadí
|
||||||
|
if velikost_skupiny == 1:
|
||||||
|
sloupec_s_poradim.append(f"{aktualni_poradi}.")
|
||||||
|
# pokud je skupina větší, vypíšu rozsah
|
||||||
|
else:
|
||||||
|
sloupec_s_poradim.append(
|
||||||
|
f"{aktualni_poradi}.–{aktualni_poradi + velikost_skupiny - 1}."
|
||||||
|
)
|
||||||
|
# zvětšíme aktuální pořadí o tolik, kolik pozic bylo přeskočeno
|
||||||
|
aktualni_poradi += velikost_skupiny
|
||||||
|
return sloupec_s_poradim
|
||||||
|
|
||||||
|
|
||||||
|
class VysledkovkaRocniku(Vysledkovka):
|
||||||
|
|
||||||
|
def __init__(self, rocnik: m.Rocnik, jen_verejne: bool = True):
|
||||||
|
self.rocnik = rocnik
|
||||||
|
self.jen_verejne = jen_verejne
|
||||||
|
self.do_deadlinu = m.Deadline.objects.filter(cislo__rocnik=rocnik).last()
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def aktivni_resitele(self) -> list[m.Resitel]:
|
||||||
|
return list(resi_v_rocniku(self.rocnik))
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def cisla_rocniku(self) -> list[m.Cislo]:
|
||||||
|
""" Vrátí všechna čísla daného ročníku. """
|
||||||
|
if self.jen_verejne:
|
||||||
|
return self.rocnik.verejne_vysledkovky_cisla()
|
||||||
|
else:
|
||||||
|
return self.rocnik.cisla.all().order_by('poradi')
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def body_za_cisla_slovnik(self) -> dict[int, dict[int, int]]: # Výstup: m.Cislo.id → ( m.Resitel.id → body )
|
||||||
|
# TODO: Body jsou decimal!
|
||||||
|
body_cisla_slovnik = dict()
|
||||||
|
for cislo in self.cisla_rocniku:
|
||||||
|
# získáme body za číslo
|
||||||
|
body_za_cislo = body_resitelu(
|
||||||
|
za=cislo,
|
||||||
|
resitele=self.aktivni_resitele,
|
||||||
|
jen_verejne=self.jen_verejne,
|
||||||
|
null=""
|
||||||
|
)
|
||||||
|
body_cisla_slovnik[cislo.id] = body_za_cislo
|
||||||
|
return body_cisla_slovnik
|
||||||
|
|
||||||
|
class RadekVysledkovkyRocniku:
|
||||||
|
# TODO: přepsat na dataclass
|
||||||
|
""" Obsahuje věci, které se hodí vědět při konstruování výsledkovky.
|
||||||
|
Umožňuje snazší práci v templatu (lepší, než seznam)."""
|
||||||
|
|
||||||
|
def __init__(self, poradi, resitel, body_cisla_seznam, body_rocnik, body_odjakziva, rok):
|
||||||
|
self.poradi = poradi
|
||||||
|
self.resitel = resitel
|
||||||
|
self.rocnik_resitele = resitel.rocnik(rok)
|
||||||
|
self.body_rocnik = body_rocnik
|
||||||
|
self.body_celkem_odjakziva = body_odjakziva
|
||||||
|
self.body_cisla_seznam = body_cisla_seznam
|
||||||
|
self.titul = resitel.get_titul(body_odjakziva)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def radky_vysledkovky(self) -> list[RadekVysledkovkyRocniku]:
|
||||||
|
radky_vysledkovky = []
|
||||||
|
|
||||||
|
setrizeni_resitele_dict = dict()
|
||||||
|
for r in m.Resitel.objects.filter(
|
||||||
|
id__in=self.setrizeni_resitele_id
|
||||||
|
).select_related('osoba'):
|
||||||
|
setrizeni_resitele_dict[r.id] = r
|
||||||
|
|
||||||
|
for i, ar_id in enumerate(self.setrizeni_resitele_id):
|
||||||
|
if self.setrizene_body[i] > 0:
|
||||||
|
# seznam počtu bodů daného řešitele pro jednotlivá čísla
|
||||||
|
body_cisla_seznam = []
|
||||||
|
for cislo in self.cisla_rocniku:
|
||||||
|
body_cisla_seznam.append(self.body_za_cisla_slovnik[cislo.id][ar_id])
|
||||||
|
|
||||||
|
# Pokud řešitel dostal nějaké body
|
||||||
|
if self.resitele_s_body_za_rocnik_setrizeny_seznam[i] != 0:
|
||||||
|
# vytáhneme informace pro daného řešitele
|
||||||
|
radek = self.RadekVysledkovkyRocniku(
|
||||||
|
poradi=self.poradi[i],
|
||||||
|
resitel=setrizeni_resitele_dict[ar_id],
|
||||||
|
body_cisla_seznam=body_cisla_seznam,
|
||||||
|
body_rocnik=self.setrizene_body[i],
|
||||||
|
body_odjakziva=self.resitel_body_odjakziva_slovnik[ar_id],
|
||||||
|
rok=self.rocnik) # ročník semináře pro získání ročníku řešitele
|
||||||
|
radky_vysledkovky.append(radek)
|
||||||
|
|
||||||
|
return radky_vysledkovky
|
||||||
|
|
||||||
|
|
||||||
|
class VysledkovkaCisla(Vysledkovka):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
cislo: m.Cislo,
|
||||||
|
jen_verejne: bool = True,
|
||||||
|
do_deadlinu: m.Deadline = None
|
||||||
|
):
|
||||||
|
self.cislo = cislo
|
||||||
|
self.rocnik = cislo.rocnik
|
||||||
|
self.jen_verejne = jen_verejne
|
||||||
|
if do_deadlinu is None:
|
||||||
|
do_deadlinu = m.Deadline.objects.filter(cislo=cislo).last()
|
||||||
|
self.do_deadlinu = do_deadlinu
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def aktivni_resitele(self) -> list[m.Resitel]:
|
||||||
|
# TODO možná chytřeji vybírat aktivní řešitele
|
||||||
|
return list(resi_v_rocniku(self.rocnik))
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def problemy(self) -> list[m.Problem]:
|
||||||
|
""" Vrátí seznam všech problémů s body v daném čísle. """
|
||||||
|
return m.Problem.objects.filter(
|
||||||
|
hodnoceni__in=m.Hodnoceni.objects.filter(deadline_body__cislo=self.cislo)
|
||||||
|
).distinct().non_polymorphic().select_related('nadproblem').select_related('nadproblem__nadproblem')
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def hlavni_problemy(self) -> list[m.Problem]:
|
||||||
|
""" Vrátí seznam všech problémů, které již nemají nadproblém. """
|
||||||
|
# hlavní problémy čísla
|
||||||
|
# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém)
|
||||||
|
hlavni_problemy = set()
|
||||||
|
for p in self.problemy:
|
||||||
|
hlavni_problemy.add(p.hlavni_problem)
|
||||||
|
|
||||||
|
# zunikátnění
|
||||||
|
hlavni_problemy = list(hlavni_problemy)
|
||||||
|
hlavni_problemy.sort(
|
||||||
|
key=lambda k: k.kod_v_rocniku) # setřídit podle t1, t2, c3, ...
|
||||||
|
|
||||||
|
return hlavni_problemy
|
||||||
|
|
||||||
|
# Není cached, protože si myslím, že queryset lze použít ve for jen jednou.
|
||||||
|
@property
|
||||||
|
def hodnoceni_do_cisla(self):
|
||||||
|
hodnoceni = m.Hodnoceni.objects.prefetch_related('reseni__resitele').select_related('problem', 'reseni')
|
||||||
|
if self.jen_verejne:
|
||||||
|
hodnoceni = hodnoceni.filter(deadline_body__verejna_vysledkovka=True)
|
||||||
|
return hodnoceni.filter(
|
||||||
|
deadline_body__cislo=self.cislo,
|
||||||
|
deadline_body__deadline__lte=self.do_deadlinu.deadline,
|
||||||
|
body__isnull=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def sectene_body(self):
|
||||||
|
"""
|
||||||
|
Sečte body za číslo, hlavní problémy a podproblémy.
|
||||||
|
|
||||||
|
Problém s ID '-1' znamená problémy bez nadproblémů, jež nejsou témata, tj. články, úlohy, konfery, …
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Body za číslo
|
||||||
|
body_za_cislo = {ar.id: "" for ar in self.aktivni_resitele}
|
||||||
|
|
||||||
|
# Body za hlavní problémy
|
||||||
|
body_za_temata = {
|
||||||
|
hp.id: {ar.id: "" for ar in self.aktivni_resitele}
|
||||||
|
for hp in self.temata_a_spol
|
||||||
|
}
|
||||||
|
# Ostatní body
|
||||||
|
body_za_temata[-1] = {ar.id: "" for ar in self.aktivni_resitele}
|
||||||
|
|
||||||
|
# Body za podproblémy
|
||||||
|
body_za_problemy = {
|
||||||
|
tema.id: {
|
||||||
|
problem.id: {ar.id: "" for ar in self.aktivni_resitele}
|
||||||
|
for problem in self.podproblemy[tema.id]
|
||||||
|
}
|
||||||
|
for tema in self.temata_a_spol
|
||||||
|
}
|
||||||
|
# Ostatní body
|
||||||
|
body_za_problemy[-1] = {
|
||||||
|
problem.id: {ar.id: "" for ar in self.aktivni_resitele}
|
||||||
|
for problem in self.podproblemy[-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Sečteme hodnocení
|
||||||
|
for hodnoceni in self.hodnoceni_do_cisla:
|
||||||
|
prob = hodnoceni.problem
|
||||||
|
nadproblem = prob.hlavni_problem.id
|
||||||
|
|
||||||
|
# Když nadproblém není "téma", pak je "Ostatní"
|
||||||
|
if nadproblem not in body_za_temata:
|
||||||
|
nadproblem = -1
|
||||||
|
|
||||||
|
problem_slovnik = body_za_problemy[nadproblem][prob.id]
|
||||||
|
nadproblem_slovnik = body_za_temata[nadproblem]
|
||||||
|
|
||||||
|
body = hodnoceni.body
|
||||||
|
|
||||||
|
# Může mít více řešitelů
|
||||||
|
for resitel in hodnoceni.reseni.resitele.all():
|
||||||
|
if resitel not in self.aktivni_resitele:
|
||||||
|
continue
|
||||||
|
self.pricti_body(body_za_cislo, resitel, body)
|
||||||
|
self.pricti_body(nadproblem_slovnik, resitel, body)
|
||||||
|
self.pricti_body(problem_slovnik, resitel, body)
|
||||||
|
return body_za_cislo, body_za_temata, body_za_problemy
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def body_za_temata(self) -> dict[int, dict[int, str]]:
|
||||||
|
return self.sectene_body[1]
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def body_za_cislo(self) -> dict[int, str]:
|
||||||
|
return self.sectene_body[0]
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def problemy_slovnik(self):
|
||||||
|
return self.sectene_body[2]
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def temata_a_spol(self) -> list[m.Problem]:
|
||||||
|
if self.rocnik.rocnik < ROCNIK_ZRUSENI_TEMAT:
|
||||||
|
return self.hlavni_problemy
|
||||||
|
else:
|
||||||
|
return list(filter(self.ne_clanek_ne_konfera, self.hlavni_problemy))
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def je_nejake_ostatni(self):
|
||||||
|
return len(self.hlavni_problemy) - len(self.temata_a_spol) > 0
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def podproblemy(self) -> dict[int, list[m.Problem]]:
|
||||||
|
podproblemy = {hp.id: [] for hp in self.temata_a_spol}
|
||||||
|
temata_a_spol = set(self.temata_a_spol)
|
||||||
|
podproblemy[-1] = []
|
||||||
|
|
||||||
|
for problem in self.problemy:
|
||||||
|
h_problem = problem.hlavni_problem
|
||||||
|
if h_problem in temata_a_spol:
|
||||||
|
podproblemy[h_problem.id].append(problem)
|
||||||
|
else:
|
||||||
|
podproblemy[-1].append(problem)
|
||||||
|
|
||||||
|
for podproblem in podproblemy.keys():
|
||||||
|
def int_or_zero(p):
|
||||||
|
try:
|
||||||
|
return int(p.kod)
|
||||||
|
except ValueError:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
podproblemy[podproblem] = sorted(podproblemy[podproblem], key=int_or_zero)
|
||||||
|
return podproblemy
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def podproblemy_seznam(self) -> list[list[m.Problem]]:
|
||||||
|
return [self.podproblemy[it.id] for it in self.temata_a_spol] + [self.podproblemy[-1]]
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def podproblemy_iter(self) -> FixedIterator:
|
||||||
|
return FixedIterator(self.podproblemy_seznam.__iter__())
|
||||||
|
|
||||||
|
class RadekVysledkovkyCisla(object):
|
||||||
|
# TODO: Přepsat na dataclass
|
||||||
|
"""Obsahuje věci, které se hodí vědět při konstruování výsledkovky.
|
||||||
|
Umožňuje snazší práci v templatu (lepší, než seznam)."""
|
||||||
|
|
||||||
|
def __init__(self, poradi, resitel, temata_seznamk, body_cislo, body_rocnik, body_odjakziva, rok, body_podproblemy, body_podproblemy_iter):
|
||||||
|
self.resitel = resitel
|
||||||
|
self.rocnik_resitele = resitel.rocnik(rok)
|
||||||
|
self.body_cislo = body_cislo
|
||||||
|
self.body_rocnik = body_rocnik
|
||||||
|
self.body_celkem_odjakziva = body_odjakziva
|
||||||
|
self.poradi = poradi
|
||||||
|
self.body_za_temata_seznam = temata_seznamk
|
||||||
|
self.titul = resitel.get_titul(body_odjakziva)
|
||||||
|
self.body_podproblemy = body_podproblemy
|
||||||
|
self.body_podproblemy_iter = body_podproblemy_iter
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def radky_vysledkovky(self) -> list[RadekVysledkovkyCisla]:
|
||||||
|
# vytvoříme jednotlivé sloupce výsledkovky
|
||||||
|
radky_vysledkovky = []
|
||||||
|
|
||||||
|
setrizeni_resitele_slovnik = {}
|
||||||
|
setrizeni_resitele = m.Resitel.objects.filter(id__in=self.setrizeni_resitele_id).select_related('osoba')
|
||||||
|
|
||||||
|
for r in setrizeni_resitele:
|
||||||
|
setrizeni_resitele_slovnik[r.id] = r
|
||||||
|
|
||||||
|
for i, ar_id in enumerate(self.setrizeni_resitele_id):
|
||||||
|
if self.setrizene_body[i] > 0:
|
||||||
|
# získáme seznam bodů za problémy pro daného řešitele
|
||||||
|
body_problemy = []
|
||||||
|
body_podproblemy = []
|
||||||
|
for hp in self.temata_a_spol:
|
||||||
|
body_problemy.append(self.body_za_temata[hp.id][ar_id])
|
||||||
|
body_podproblemy.append([
|
||||||
|
self.problemy_slovnik[hp.id][it.id][ar_id]
|
||||||
|
for it in self.podproblemy[hp.id]
|
||||||
|
])
|
||||||
|
if self.je_nejake_ostatni:
|
||||||
|
body_problemy.append(self.body_za_temata[-1][ar_id])
|
||||||
|
body_podproblemy.append(
|
||||||
|
[self.problemy_slovnik[-1][it.id][ar_id] for it in self.podproblemy[-1]])
|
||||||
|
# vytáhneme informace pro daného řešitele
|
||||||
|
radek = self.RadekVysledkovkyCisla(
|
||||||
|
poradi=self.poradi[i],
|
||||||
|
resitel=setrizeni_resitele_slovnik[ar_id],
|
||||||
|
temata_seznamk=body_problemy,
|
||||||
|
body_cislo=self.body_za_cislo[ar_id],
|
||||||
|
body_rocnik=self.setrizene_body[i],
|
||||||
|
body_odjakziva=self.resitel_body_odjakziva_slovnik[ar_id],
|
||||||
|
rok=self.rocnik,
|
||||||
|
body_podproblemy=body_podproblemy, # body všech podproblémů
|
||||||
|
body_podproblemy_iter=FixedIterator(body_podproblemy.__iter__())
|
||||||
|
) # ročník semináře pro zjištění ročníku řešitele
|
||||||
|
radky_vysledkovky.append(radek)
|
||||||
|
return radky_vysledkovky
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def pricti_body(slovnik, resitel, body):
|
||||||
|
""" Přiřazuje danému řešiteli body do slovníku. """
|
||||||
|
# testujeme na None (""), pokud je to první řešení
|
||||||
|
# daného řešitele, předěláme na 0
|
||||||
|
# (v dalším kroku přičteme reálný počet bodů),
|
||||||
|
# rozlišujeme tím mezi 0 a neodevzdaným řešením
|
||||||
|
|
||||||
|
if slovnik[resitel.id] == "":
|
||||||
|
slovnik[resitel.id] = 0
|
||||||
|
|
||||||
|
slovnik[resitel.id] += body
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def ne_clanek_ne_konfera(problem):
|
def ne_clanek_ne_konfera(problem):
|
||||||
|
inst = problem.get_real_instance()
|
||||||
return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera))
|
return not (isinstance(inst, m.Clanek) or isinstance(inst, m.Konfera))
|
||||||
|
|
||||||
if cislo.rocnik.rocnik < ROCNIK_ZRUSENI_TEMAT:
|
|
||||||
temata_a_spol = hlavni_problemy
|
|
||||||
else:
|
|
||||||
temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy))
|
|
||||||
|
|
||||||
# získáme body u jednotlivých témat
|
class VysledkovkaDoTeXu(VysledkovkaCisla):
|
||||||
podproblemy = podproblemy_v_cislu(cislo, problemy, temata_a_spol)
|
def __init__(
|
||||||
problemy_slovnik = secti_body_za_cislo_podle_temat(cislo, aktivni_resitele, podproblemy, temata_a_spol)
|
self,
|
||||||
|
nejake_cislo: m.Cislo,
|
||||||
|
od_vyjma: m.Deadline,
|
||||||
|
do_vcetne: m.Deadline
|
||||||
|
):
|
||||||
|
super().__init__(nejake_cislo, False, do_vcetne)
|
||||||
|
self.od_deadlinu = od_vyjma
|
||||||
|
|
||||||
# def not_empty(value):
|
@cached_property
|
||||||
# return value != ''
|
def problemy(self) -> list[m.Problem]:
|
||||||
#
|
return m.Problem.objects.filter(hodnoceni__in=m.Hodnoceni.objects.filter(
|
||||||
# je_nejake_ostatni = any(filter(not_empty, hlavni_problemy_slovnik[-1].values())) > 0
|
deadline_body__deadline__gt=self.od_deadlinu.deadline,
|
||||||
|
deadline_body__deadline__lte=self.do_deadlinu.deadline,
|
||||||
|
)).distinct().non_polymorphic().select_related('nadproblem').select_related('nadproblem__nadproblem')
|
||||||
|
|
||||||
je_nejake_ostatni = len(hlavni_problemy) - len(temata_a_spol) > 0
|
@property
|
||||||
|
def hodnoceni_do_cisla(self):
|
||||||
setrizeni_resitele_slovnik = {}
|
hodnoceni = m.Hodnoceni.objects.prefetch_related(
|
||||||
setrizeni_resitele = m.Resitel.objects.filter(id__in=setrizeni_resitele_id).select_related('osoba')
|
'problem', 'reseni', 'reseni__resitele')
|
||||||
for r in setrizeni_resitele:
|
if self.jen_verejne:
|
||||||
setrizeni_resitele_slovnik[r.id] = r
|
hodnoceni = hodnoceni.filter(deadline_body__verejna_vysledkovka=True)
|
||||||
|
return hodnoceni.filter(
|
||||||
for ar_id in setrizeni_resitele_id:
|
deadline_body__deadline__gt=self.od_deadlinu.deadline,
|
||||||
# získáme seznam bodů za problémy pro daného řešitele
|
deadline_body__deadline__lte=self.do_deadlinu.deadline,
|
||||||
body_problemy = []
|
body__isnull=False,
|
||||||
body_podproblemy = []
|
)
|
||||||
for hp in temata_a_spol:
|
|
||||||
body_problemy.append(hlavni_problemy_slovnik[hp.id][ar_id])
|
|
||||||
body_podproblemy.append([problemy_slovnik[hp.id][it.id][ar_id] for it in podproblemy[hp.id]])
|
|
||||||
if je_nejake_ostatni:
|
|
||||||
body_problemy.append(hlavni_problemy_slovnik[-1][ar_id])
|
|
||||||
body_podproblemy.append([problemy_slovnik[-1][it.id][ar_id] for it in podproblemy[-1]])
|
|
||||||
# vytáhneme informace pro daného řešitele
|
|
||||||
radek = RadekVysledkovkyCisla(
|
|
||||||
poradi[i], # pořadí
|
|
||||||
setrizeni_resitele_slovnik[ar_id], # řešitel (z id)
|
|
||||||
body_problemy, # seznam bodů za hlavní problémy čísla
|
|
||||||
cislobody[ar_id], # body za číslo
|
|
||||||
setrizeni_resitele_body[i], # body za ročník (spočítané výše s pořadím)
|
|
||||||
resitel_odjakzivabody_slov[ar_id], # body odjakživa
|
|
||||||
cislo.rocnik,
|
|
||||||
body_podproblemy, # body všech podproblémů
|
|
||||||
FixedIterator(body_podproblemy.__iter__()) # TODELETE
|
|
||||||
) # ročník semináře pro zjištění ročníku řešitele
|
|
||||||
radky_vysledkovky.append(radek)
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
# vytahané informace předáváme do kontextu
|
|
||||||
pt = [podproblemy[it.id] for it in temata_a_spol]+[podproblemy[-1]]
|
|
||||||
radky_vysledkovky = [radek for radek in radky_vysledkovky if radek.body_rocnik > 0]
|
|
||||||
return (
|
|
||||||
radky_vysledkovky,
|
|
||||||
temata_a_spol,
|
|
||||||
je_nejake_ostatni,
|
|
||||||
pt,
|
|
||||||
FixedIterator(pt.__iter__())
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
from .utils import data_vysledkovky_cisla, \
|
|
||||||
data_vysledkovky_rocniku
|
|
||||||
|
|
||||||
|
|
||||||
def vysledkovka_cisla(cislo, context=None):
|
|
||||||
if context is None:
|
|
||||||
context = {}
|
|
||||||
context['cislo'] = cislo
|
|
||||||
|
|
||||||
(
|
|
||||||
context['radky_vysledkovky'],
|
|
||||||
context['problemy'],
|
|
||||||
context['ostatni'],
|
|
||||||
context['podproblemy'],
|
|
||||||
context['podproblemy_iter']
|
|
||||||
) = data_vysledkovky_cisla(cislo)
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
def vysledkovka_rocniku(rocnik, context=None, request=None, sneverejnou=False):
|
|
||||||
if context is None:
|
|
||||||
context = {}
|
|
||||||
|
|
||||||
(
|
|
||||||
context['radky_vysledkovky'],
|
|
||||||
context['cisla']
|
|
||||||
) = data_vysledkovky_rocniku(rocnik)
|
|
||||||
|
|
||||||
context['vysledkovka'] = len(context['cisla']) != 0
|
|
||||||
|
|
||||||
if sneverejnou and request and request.user.je_org:
|
|
||||||
(
|
|
||||||
context['radky_vysledkovky_s_neverejnymi'],
|
|
||||||
context['cisla_s_neverejnymi']
|
|
||||||
) = data_vysledkovky_rocniku(rocnik, jen_verejne=False)
|
|
||||||
|
|
||||||
return context
|
|
Loading…
Reference in a new issue