Browse Source

Merge branch 'vysledkovky2' into develop

pull/3/head
Jonas Havelka 2 years ago
parent
commit
4aa67d6151
  1. 15
      deploy_v2/admin_org_prava.json
  2. 33
      odevzdavatko/forms.py
  3. 12
      odevzdavatko/templates/odevzdavatko/detail.html
  4. 6
      odevzdavatko/templates/odevzdavatko/detail_resitele.html
  5. 4
      odevzdavatko/templates/odevzdavatko/prehled_reseni.html
  6. 2
      odevzdavatko/templates/odevzdavatko/seznam.html
  7. 18
      odevzdavatko/views.py
  8. 2
      personalni/views.py
  9. 14
      seminar/admin.py
  10. 11
      seminar/management/commands/pregeneruj_zmrazene_vysledkovky.py
  11. 81
      seminar/migrations/0103_deadline.py
  12. 86
      seminar/migrations/0104_hodnoceni_deadline_body.py
  13. 50
      seminar/migrations/0105_odstraneni_deadlinu_cisla.py
  14. 27
      seminar/migrations/0106_remove_cislo_verejna_vysledkovka.py
  15. 26
      seminar/migrations/0107_zmrazenavysledkovka.py
  16. 7
      seminar/models/odevzdavatko.py
  17. 4
      seminar/models/personalni.py
  18. 132
      seminar/models/tvorba.py
  19. 22
      seminar/templates/seminar/archiv/cislo.html
  20. 6
      seminar/templates/seminar/archiv/cislo_vysledkovka.tex
  21. 20
      seminar/templates/seminar/archiv/odmeny.html
  22. 6
      seminar/templates/seminar/archiv/rocnik.html
  23. 8
      seminar/templates/seminar/archiv/rocnik_vysledkovka.tex
  24. 6
      seminar/templates/seminar/titulnistrana/titulnistrana.html
  25. 4
      seminar/templates/seminar/zadani/AktualniVysledkovka.html
  26. 14
      seminar/templates/seminar/zadani/AktualniZadani.html
  27. 44
      seminar/templatetags/deadliny.py
  28. 149
      seminar/test_deadlines.py
  29. 2
      seminar/testutils.py
  30. 10
      seminar/urls.py
  31. 142
      seminar/utils.py
  32. 170
      seminar/views/views_all.py
  33. 58
      vysledkovky/templates/vysledkovky/vysledkovka_cisla.html
  34. 6
      vysledkovky/templates/vysledkovky/vysledkovka_rocnik.html
  35. 1
      vysledkovky/templates/vysledkovky/vysledkovka_rocnik_neverejna.html
  36. 784
      vysledkovky/utils.py
  37. 37
      vysledkovky/views.py

15
deploy_v2/admin_org_prava.json

@ -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"
} }
] ]

33
odevzdavatko/forms.py

@ -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((
strftime(DATE_FORMAT, cislo.datum_vydani.timetuple()),
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(( result.append((
strftime(DATE_FORMAT, cislo.datum_deadline.timetuple()), strftime(DATE_FORMAT, deadline.deadline.timetuple()),
f"Finální deadline {cislo.poradi}. čísla")) str(deadline)))
result.append(( result.append((
strftime(DATE_FORMAT, datetime.date.today().timetuple()), f"Dnes")) strftime(DATE_FORMAT, datetime.date.today().timetuple()), f"Dnes"))

12
odevzdavatko/templates/odevzdavatko/detail.html

@ -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>

6
odevzdavatko/templates/odevzdavatko/detail_resitele.html

@ -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>

4
odevzdavatko/templates/odevzdavatko/prehled_reseni.html

@ -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>

2
odevzdavatko/templates/odevzdavatko/seznam.html

@ -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 }})

18
odevzdavatko/views.py

@ -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()

2
personalni/views.py

@ -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()

14
seminar/admin.py

@ -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()

11
seminar/management/commands/pregeneruj_zmrazene_vysledkovky.py

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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',
},
),
]

7
seminar/models/odevzdavatko.py

@ -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',

4
seminar/models/personalni.py

@ -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)

132
seminar/models/tvorba.py

@ -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
@ -157,26 +159,9 @@ 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):

22
seminar/templates/seminar/archiv/cislo.html

@ -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>
{% endif %} {% else %}
<h2>{{ nadpis }}</h2>
{% 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 }}
{% else %}
{% include "vysledkovky/vysledkovka_cisla.html" with oznaceni_vysledkovky=forloop.counter %}
{% endif %} {% 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 %}

6
seminar/templates/seminar/archiv/cislo_vysledkovka.tex

@ -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}

20
seminar/templates/seminar/archiv/odmeny.html

@ -3,11 +3,27 @@
{% 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>
{% for z in zmeny_prvni_prvni %}
<li> {{z.jmeno}}: {{z.ftitul}} &rarr; {{z.ttitul}}</li>
{% endfor %}
</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}} &rarr; {{z.ttitul}}</li>
{% endfor %}
</ul>
<h2>Od prvního deadlinu {{ from_cislo }} do {{ to_cislo }} (pro poslední číslo)</h2>
<ul> <ul>
{% for z in zmeny %} {% for z in zmeny_prvni_posledni %}
<li> {{z.jmeno}}: {{z.ftitul}} &rarr; {{z.ttitul}}</li> <li> {{z.jmeno}}: {{z.ftitul}} &rarr; {{z.ttitul}}</li>
{% endfor %} {% endfor %}
</ul> </ul>

6
seminar/templates/seminar/archiv/rocnik.html

@ -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 %}

8
seminar/templates/seminar/archiv/rocnik_vysledkovka.tex

@ -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 %}

6
seminar/templates/seminar/titulnistrana/titulnistrana.html

@ -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 %}

4
seminar/templates/seminar/zadani/AktualniVysledkovka.html

@ -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 %}

14
seminar/templates/seminar/zadani/AktualniZadani.html

@ -17,17 +17,19 @@
<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 %} {% for deadline in ac.deadline_v_cisle.all %}
<span class="datum">{{ac.datum_deadline_soustredeni}}</span> pro účast na soustředění<br> {% if deadline.typ == deadline.TYP_SOUS or deadline.typ == deadline.TYP_PRVNI_A_SOUS %}
<span class="datum">{{deadline.deadline.date}}</span> pro účast na soustředění<br>
{% endif %} {% endif %}
{% if ac.datum_preddeadline %} {% if deadline.typ == deadline.TYP_PRVNI or deadline.typ == deadline.TYP_PRVNI_A_SOUS %}
<span class="datum">{{ac.datum_preddeadline}}</span> pro otištění v dalším čísle<br> <span class="datum">{{deadline.deadline.date}}</span> pro otištění v dalším čísle<br>
{% endif %} {% endif %}
{% if ac.datum_deadline %} {% if deadline.typ == deadline.TYP_CISLA %}
<span class="datum">{{ac.datum_deadline}}</span> definitivní deadline<br> <span class="datum">{{deadline.deadline.date}}</span> definitivní deadline<br>
{% endif %} {% endif %}
{% endfor %}
</div> </div>
<hr> <hr>

44
seminar/templatetags/deadliny.py

@ -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):

149
seminar/test_deadlines.py

@ -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))

2
seminar/testutils.py

@ -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

10
seminar/urls.py

@ -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

@ -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"""

170
seminar/views/views_all.py

@ -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,8 +462,10 @@ 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):
frombody = body_resitelu(resitele=resitele, jen_verejne=False, do=from_deadline)
tobody = body_resitelu(resitele=resitele, jen_verejne=False, do=to_deadline)
outlist = [] outlist = []
for (aid, tbody) in tobody.items(): for (aid, tbody) in tobody.items():
fbody = frombody.get(aid,0) fbody = frombody.get(aid,0)
@ -486,7 +474,29 @@ class OdmenyView(generic.TemplateView):
ttitul = resitel.get_titul(tbody) ttitul = resitel.get_titul(tbody)
if ftitul != ttitul: if ftitul != ttitul:
outlist.append({'jmeno': resitel.osoba.plne_jmeno(), 'ftitul': ftitul, 'ttitul': ttitul}) outlist.append({'jmeno': resitel.osoba.plne_jmeno(), 'ftitul': ftitul, 'ttitul': ttitul})
context['zmeny'] = outlist 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])

58
vysledkovky/templates/vysledkovky/vysledkovka_cisla.html

@ -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 %}

6
vysledkovky/templates/vysledkovky/vysledkovka_rocnik.html

@ -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
vysledkovky/templates/vysledkovky/vysledkovka_rocnik_neverejna.html

@ -1 +0,0 @@
{% include "vysledkovky/vysledkovka_rocnik.html" with radky_vysledkovky=radky_vysledkovky_s_neverejnymi cisla=cisla_s_neverejnymi %}

784
vysledkovky/utils.py

@ -1,222 +1,449 @@
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ů)
"""
class FixedIterator:
def next(self):
return self.niter.__next__()
def __init__(self, niter):
self.niter = niter
def body_resitelu(
za: Union[m.Cislo, m.Rocnik, None] = None,
do: m.Deadline = None,
od: m.Deadline = None,
jen_verejne: bool = True,
resitele=None,
null=0 # Výchozí hodnota, pokud pro daného řešitele nejsou body
) -> dict[int, int]:
filtr = Q()
if jen_verejne:
filtr &= Q(reseni__hodnoceni__deadline_body__verejna_vysledkovka=True)
# Zjistíme, typ objektu v parametru "za"
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)
if do:
filtr &= Q(reseni__hodnoceni__deadline_body__deadline__lte=do.deadline)
if od:
filtr &= Q(reseni__hodnoceni__deadline_body__deadline__gte=od.deadline)
resiteleQuery = m.Resitel.objects.all()
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 # ze seznamu obsahujícího setřízené body spočítáme sloupec s pořadím
aktualni_poradi = 1 aktualni_poradi = 1
sloupec_s_poradim = [] sloupec_s_poradim = []
# seskupíme seznam všech bodů podle hodnot # seskupíme seznam všech bodů podle hodnot
for index in range(0, len(setrizene_body)): 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 # pokud je pořadí větší než číslo řádku, tak jsme vypsali větší rozsah
# vypsat už jen prázdné místo, než dojdeme na správný řádek # a chceme vypsat už jen prázdné místo, než dojdeme na správný řádek
if (index + 1) < aktualni_poradi: if (index + 1) < aktualni_poradi:
sloupec_s_poradim.append("") sloupec_s_poradim.append("")
continue continue
velikost_skupiny = 0 velikost_skupiny = 0
# zjistíme počet po sobě jdoucích stejných hodnot # zjistíme počet po sobě jdoucích stejných hodnot
while setrizene_body[index] == setrizene_body[index + velikost_skupiny]: while self.setrizene_body[index] == self.setrizene_body[
velikost_skupiny = velikost_skupiny + 1 index + velikost_skupiny]:
velikost_skupiny += 1
# na konci musíme ošetřit přetečení seznamu # na konci musíme ošetřit přetečení seznamu
if (index + velikost_skupiny) > len(setrizene_body) - 1: if (index + velikost_skupiny) > len(self.setrizene_body) - 1:
break break
# pokud je velikost skupiny 1, vypíšu pořadí # pokud je velikost skupiny 1, vypíšu pořadí
if velikost_skupiny == 1: if velikost_skupiny == 1:
sloupec_s_poradim.append("{}.".format(aktualni_poradi)) sloupec_s_poradim.append(f"{aktualni_poradi}.")
# pokud je skupina větší, vypíšu rozsah # pokud je skupina větší, vypíšu rozsah
else: else:
sloupec_s_poradim.append("{}.–{}.".format(aktualni_poradi, sloupec_s_poradim.append(
aktualni_poradi+velikost_skupiny-1)) f"{aktualni_poradi}.–{aktualni_poradi + velikost_skupiny - 1}."
)
# zvětšíme aktuální pořadí o tolik, kolik pozic bylo přeskočeno # zvětšíme aktuální pořadí o tolik, kolik pozic bylo přeskočeno
aktualni_poradi = aktualni_poradi + velikost_skupiny aktualni_poradi += velikost_skupiny
return sloupec_s_poradim 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()
def body_resitelu(resitele, za, odjakziva=True, jen_verejne=False): @cached_property
""" Funkce počítající počty bodů pro zadané řešitele, def aktivni_resitele(self) -> list[m.Resitel]:
buď odjakživa do daného ročníku/čísla anebo za daný ročník/číslo. return list(resi_v_rocniku(self.rocnik))
Parametry:
resitele (seznam obsahující položky typu Resitel): aktivní řešitelé @cached_property
za (Rocnik/Cislo): za co se mají počítat body def cisla_rocniku(self) -> list[m.Cislo]:
(generování starších výsledkovek) """ Vrátí všechna čísla daného ročníku. """
odjakziva (bool): zda se mají počítat body odjakživa, nebo jen za číslo/ročník if self.jen_verejne:
zadané v "za" return self.rocnik.verejne_vysledkovky_cisla()
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: else:
assert True, "body_resitelu: Neplatná kombinace za a odjakživa." return self.rocnik.cisla.all().order_by('poradi')
# Následující řádek přidá ke každému řešiteli údaj ".body" se součtem jejich bodů @cached_property
resitele_s_body = m.Resitel.objects.filter(id__in=resitele_id).annotate( def body_za_cisla_slovnik(self) -> dict[int, dict[int, int]]: # Výstup: m.Cislo.id → ( m.Resitel.id → body )
body=body_k_zapocteni) # TODO: Body jsou decimal!
# Teď jen z QuerySetu řešitelů anotovaných body vygenerujeme slovník body_cisla_slovnik = dict()
# indexovaný řešitelským id obsahující body. for cislo in self.cisla_rocniku:
# Pokud jsou body None, nahradíme za 0. # získáme body za číslo
slovnik = {int(res.id) : (res.body if res.body else 0) for res in resitele_s_body} body_za_cislo = body_resitelu(
return slovnik 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(object): class RadekVysledkovkyRocniku:
# TODO: přepsat na dataclass
""" Obsahuje věci, které se hodí vědět při konstruování výsledkovky. """ 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).""" Umožňuje snazší práci v templatu (lepší, než seznam)."""
def __init__(self, poradi, resitel, body_cisla_sezn, body_rocnik, body_odjakziva, rok): def __init__(self, poradi, resitel, body_cisla_seznam, body_rocnik, body_odjakziva, rok):
self.poradi = poradi self.poradi = poradi
self.resitel = resitel self.resitel = resitel
self.rocnik_resitele = resitel.rocnik(rok) self.rocnik_resitele = resitel.rocnik(rok)
self.body_rocnik = body_rocnik self.body_rocnik = body_rocnik
self.body_celkem_odjakziva = body_odjakziva self.body_celkem_odjakziva = body_odjakziva
self.body_cisla_sezn = body_cisla_sezn self.body_cisla_seznam = body_cisla_seznam
self.titul = resitel.get_titul(body_odjakziva) self.titul = resitel.get_titul(body_odjakziva)
def setrid_resitele_a_body(slov_resitel_body): @cached_property
setrizeni_resitele_id = [dvojice[0] for dvojice in slov_resitel_body] def radky_vysledkovky(self) -> list[RadekVysledkovkyRocniku]:
setrizene_body = [dvojice[1] for dvojice in slov_resitel_body] radky_vysledkovky = []
return setrizeni_resitele_id, setrizene_body
def data_vysledkovky_rocniku(rocnik, jen_verejne=True): setrizeni_resitele_dict = dict()
""" Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve for r in m.Resitel.objects.filter(
formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html" 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.
start = time.time() Problém s ID '-1' znamená problémy bez nadproblémů, jež nejsou témata, tj. články, úlohy, konfery,
"""
## TODO možná chytřeji vybírat aktivní řešitele # Body za číslo
# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají body_za_cislo = {ar.id: "" for ar in self.aktivni_resitele}
# u alespoň jedné hodnoty něco jiného než NULL
aktivni_resitele = list(resi_v_rocniku(rocnik)) # Body za hlavní problémy
cisla = cisla_rocniku(rocnik, jen_verejne) body_za_temata = {
body_cisla_slov = {} hp.id: {ar.id: "" for ar in self.aktivni_resitele}
for cislo in cisla: for hp in self.temata_a_spol
# získáme body za číslo }
_, cislobody = secti_body_za_cislo(cislo, aktivni_resitele) # Ostatní body
body_cisla_slov[cislo.id] = cislobody 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
# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně # Když nadproblém není "téma", pak je "Ostatní"
resitel_rocnikbody_sezn = secti_body_za_rocnik(rocnik, aktivni_resitele, jen_verejne=jen_verejne) if nadproblem not in body_za_temata:
nadproblem = -1
# setřídíme řešitele podle počtu bodů a získáme seznam s body od nejvyšších po nenižší problem_slovnik = body_za_problemy[nadproblem][prob.id]
setrizeni_resitele_id, setrizene_body = setrid_resitele_a_body(resitel_rocnikbody_sezn) nadproblem_slovnik = body_za_temata[nadproblem]
poradi = sloupec_s_poradim(setrizene_body)
# získáme body odjakživa body = hodnoceni.body
resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, rocnik, jen_verejne=jen_verejne)
# vytvoříme jednotlivé sloupce výsledkovky # Může mít více řešitelů
radky_vysledkovky = [] for resitel in hodnoceni.reseni.resitele.all():
i = 0 if resitel not in self.aktivni_resitele:
setrizeni_resitele_dict = {} # Tento slovnik se vyrab continue
for r in m.Resitel.objects.filter(id__in=setrizeni_resitele_id).select_related('osoba'): self.pricti_body(body_za_cislo, resitel, body)
setrizeni_resitele_dict[r.id] = r 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 ar_id in setrizeni_resitele_id: for podproblem in podproblemy.keys():
# seznam počtu bodů daného řešitele pro jednotlivá čísla def int_or_zero(p):
body_cisla_sezn = [] try:
for cislo in cisla: return int(p.kod)
body_cisla_sezn.append(body_cisla_slov[cislo.id][ar_id]) except ValueError:
return 0
# vytáhneme informace pro daného řešitele podproblemy[podproblem] = sorted(podproblemy[podproblem], key=int_or_zero)
radek = RadekVysledkovkyRocniku( return podproblemy
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() @cached_property
print("Vysledkovka rocniku",end-start) def podproblemy_seznam(self) -> list[list[m.Problem]]:
return [self.podproblemy[it.id] for it in self.temata_a_spol] + [self.podproblemy[-1]]
radky_vysledkovky = [radek for radek in radky_vysledkovky if radek.body_rocnik > 0] @cached_property
return radky_vysledkovky, cisla def podproblemy_iter(self) -> FixedIterator:
return FixedIterator(self.podproblemy_seznam.__iter__())
class RadekVysledkovkyCisla(object): class RadekVysledkovkyCisla(object):
# TODO: Přepsat na dataclass
"""Obsahuje věci, které se hodí vědět při konstruování výsledkovky. """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).""" Umožňuje snazší práci v templatu (lepší, než seznam)."""
def __init__(self, poradi, resitel, body_problemy_sezn, def __init__(self, poradi, resitel, temata_seznamk, body_cislo, body_rocnik, body_odjakziva, rok, body_podproblemy, body_podproblemy_iter):
body_cislo, body_rocnik, body_odjakziva, rok, body_podproblemy, body_podproblemy_iter):
self.resitel = resitel self.resitel = resitel
self.rocnik_resitele = resitel.rocnik(rok) self.rocnik_resitele = resitel.rocnik(rok)
self.body_cislo = body_cislo self.body_cislo = body_cislo
self.body_rocnik = body_rocnik self.body_rocnik = body_rocnik
self.body_celkem_odjakziva = body_odjakziva self.body_celkem_odjakziva = body_odjakziva
self.poradi = poradi self.poradi = poradi
self.body_problemy_sezn = body_problemy_sezn self.body_za_temata_seznam = temata_seznamk
self.titul = resitel.get_titul(body_odjakziva) self.titul = resitel.get_titul(body_odjakziva)
self.body_podproblemy = body_podproblemy self.body_podproblemy = body_podproblemy
self.body_podproblemy_iter = body_podproblemy_iter # TODELETE 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): def pricti_body(slovnik, resitel, body):
""" Přiřazuje danému řešiteli body do slovníku. """ """ Přiřazuje danému řešiteli body do slovníku. """
# testujeme na None (""), pokud je to první řešení # testujeme na None (""), pokud je to první řešení
@ -224,239 +451,42 @@ def pricti_body(slovnik, resitel, body):
# (v dalším kroku přičteme reálný počet bodů), # (v dalším kroku přičteme reálný počet bodů),
# rozlišujeme tím mezi 0 a neodevzdaným řešením # 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] == "": if slovnik[resitel.id] == "":
slovnik[resitel.id] = 0 slovnik[resitel.id] = 0
slovnik[resitel.id] += body slovnik[resitel.id] += body
def secti_body_za_rocnik(za, aktivni_resitele, jen_verejne): @staticmethod
""" 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 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): def ne_clanek_ne_konfera(problem):
inst = problem.get_real_instance() inst = problem.get_real_instance()
return not (isinstance(inst, m.Clanek) or isinstance(inst, 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))
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:
def next(self):
return self.niter.__next__()
def __init__(self, niter):
self.niter = niter
# TODELETE
def data_vysledkovky_cisla(cislo):
problemy = problemy_cisla(cislo)
hlavni_problemy = hlavni_problemy_f(problemy)
## 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(cislo.rocnik))
# získáme body za číslo
hlavni_problemy_slovnik, cislobody = secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy)
# 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(cislo, aktivni_resitele, jen_verejne=True)
# získáme body odjakživa
resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, cislo, jen_verejne=True)
# řešitelé setřídění podle bodů za číslo sestupně
setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn]
# 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 class VysledkovkaDoTeXu(VysledkovkaCisla):
radky_vysledkovky = [] def __init__(
i = 0 self,
nejake_cislo: m.Cislo,
def ne_clanek_ne_konfera(problem): od_vyjma: m.Deadline,
do_vcetne: m.Deadline
return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera)) ):
super().__init__(nejake_cislo, False, do_vcetne)
if cislo.rocnik.rocnik < ROCNIK_ZRUSENI_TEMAT: self.od_deadlinu = od_vyjma
temata_a_spol = hlavni_problemy
else: @cached_property
temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy)) def problemy(self) -> list[m.Problem]:
return m.Problem.objects.filter(hodnoceni__in=m.Hodnoceni.objects.filter(
# získáme body u jednotlivých témat deadline_body__deadline__gt=self.od_deadlinu.deadline,
podproblemy = podproblemy_v_cislu(cislo, problemy, temata_a_spol) deadline_body__deadline__lte=self.do_deadlinu.deadline,
problemy_slovnik = secti_body_za_cislo_podle_temat(cislo, aktivni_resitele, podproblemy, temata_a_spol) )).distinct().non_polymorphic().select_related('nadproblem').select_related('nadproblem__nadproblem')
# def not_empty(value): @property
# return value != '' def hodnoceni_do_cisla(self):
# hodnoceni = m.Hodnoceni.objects.prefetch_related(
# je_nejake_ostatni = any(filter(not_empty, hlavni_problemy_slovnik[-1].values())) > 0 'problem', 'reseni', 'reseni__resitele')
if self.jen_verejne:
je_nejake_ostatni = len(hlavni_problemy) - len(temata_a_spol) > 0 hodnoceni = hodnoceni.filter(deadline_body__verejna_vysledkovka=True)
return hodnoceni.filter(
setrizeni_resitele_slovnik = {} deadline_body__deadline__gt=self.od_deadlinu.deadline,
setrizeni_resitele = m.Resitel.objects.filter(id__in=setrizeni_resitele_id).select_related('osoba') deadline_body__deadline__lte=self.do_deadlinu.deadline,
for r in setrizeni_resitele: body__isnull=False,
setrizeni_resitele_slovnik[r.id] = r
for ar_id in setrizeni_resitele_id:
# získáme seznam bodů za problémy pro daného řešitele
body_problemy = []
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__())
) )

37
vysledkovky/views.py

@ -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…
Cancel
Save