Browse Source

Přepsání výsledkovek v1

pull/3/head
Jonas Havelka 2 years ago
parent
commit
ba7e3409a2
  1. 2
      personalni/views.py
  2. 4
      seminar/models/personalni.py
  3. 10
      seminar/models/tvorba.py
  4. 6
      seminar/templates/seminar/archiv/cislo_vysledkovka.tex
  5. 4
      seminar/templates/seminar/archiv/rocnik.html
  6. 8
      seminar/templates/seminar/archiv/rocnik_vysledkovka.tex
  7. 4
      seminar/templates/seminar/zadani/AktualniVysledkovka.html
  8. 10
      seminar/utils.py
  9. 37
      seminar/views/views_all.py
  10. 34
      vysledkovky/templates/vysledkovky/vysledkovka_cisla.html
  11. 6
      vysledkovky/templates/vysledkovky/vysledkovka_rocnik.html
  12. 1
      vysledkovky/templates/vysledkovky/vysledkovka_rocnik_neverejna.html
  13. 871
      vysledkovky/utils.py
  14. 37
      vysledkovky/views.py

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

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)

10
seminar/models/tvorba.py

@ -8,6 +8,7 @@ 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.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
@ -328,6 +329,9 @@ class Cislo(SeminarModelBase):
if self.datum_deadline_soustredeni is not None and self.datum_deadline_soustredeni > self.datum_deadline: if self.datum_deadline_soustredeni is not None and self.datum_deadline_soustredeni > self.datum_deadline:
raise ValidationError({'datum_deadline_soustredeni': "Soustřeďkový deadline musí předcházet finálnímu deadlinu"}) raise ValidationError({'datum_deadline_soustredeni': "Soustřeďkový deadline musí předcházet finálnímu deadlinu"})
def zlomovy_deadline_pro_papirove_cislo(self):
return Deadline.objects.filter(Q(typ=Deadline.TYP_PRVNI) | Q(typ=Deadline.TYP_PRVNI_A_SOUS), cislo=self).first()
class Deadline(SeminarModelBase): class Deadline(SeminarModelBase):
class Meta: class Meta:
@ -365,6 +369,12 @@ class Deadline(SeminarModelBase):
def __str__(self): def __str__(self):
return self.CHOICES_MAP[self.typ] + " " + str(self.cislo) return self.CHOICES_MAP[self.typ] + " " + str(self.cislo)
def next(self):
return Deadline.objects.filter(gt=self).first()
def previous(self):
return Deadline.objects.filter(lt=self).last()
@reversion.register(ignore_duplicates=True) @reversion.register(ignore_duplicates=True)

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 %}|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}} & & \\\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 %}\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_problemy_sezn %}{{b}}&{% endfor %}{{rv.body_cislo}}&{{rv.body_rocnik|default:0}}\\
{% endfor %} {% endfor %}
\end{longtable} \end{longtable}

4
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 %}
@ -123,7 +123,7 @@
{# 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_sezn %} & {{ b }}{% endfor %} & {{ rv.body_rocnik }} \\
{% endfor %}\end{longtable} {% endfor %}\end{longtable}
{% endwith %} {% endwith %}
{% endwith %} {% endwith %}

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

10
seminar/utils.py

@ -179,11 +179,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):
@ -269,11 +269,11 @@ def hlavni_problem(problem):
return problem return problem
def problemy_rocniku(rocnik, jen_verejne=True): 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') return m.Problem.objects.filter(hodnoceni__in = m.Hodnoceni.objects.filter(deadline_body__cislo__in = cisla_rocniku(rocnik, jen_verejne))).distinct().select_related('nadproblem').select_related('nadproblem__nadproblem')
def problemy_cisla(cislo): def problemy_cisla(cislo):
""" Vrátí seznam všech problémů s body v daném čísle. """ """ 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') return m.Problem.objects.filter(hodnoceni__in = m.Hodnoceni.objects.filter(deadline_body__cislo = cislo)).distinct().non_polymorphic().select_related('nadproblem').select_related('nadproblem__nadproblem')
def hlavni_problemy_f(problemy=None): def hlavni_problemy_f(problemy=None):

37
seminar/views/views_all.py

@ -17,8 +17,8 @@ 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
from datetime import date, datetime from datetime import date, datetime
from django.utils import timezone from django.utils import timezone
@ -206,26 +206,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
@ -377,17 +368,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):
@ -453,7 +437,8 @@ 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 # vrátíme context (aktuálně obsahuje jen věci ohledně výsledkovky
return vysledkovka_cisla(cislo, context) context['vysledkovka'] = VysledkovkaCisla(cislo, not self.request.user.je_org)
return context
class ArchivTemataView(generic.ListView): class ArchivTemataView(generic.ListView):

34
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{{ 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{{ 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.ostatni %}<th class='border-r' id='problem{{ vysledkovka.temata_a_spol| length }}'>Ostatní {% endif %}
{# TODELETE #} {# TODELETE #}
{% for podproblemy in podproblemy_iter.next %} {% for podproblemy in 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{{ 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_hlavni_problemy_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{{ 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,7 +53,7 @@
{# TODELETE #} {# TODELETE #}
<script> <script>
{% for p in problemy %} {% for p in vysledkovka.temata_a_spol%}
diplayed{{ forloop.counter0 }} = false; diplayed{{ forloop.counter0 }} = false;
$(".podproblem{{ forloop.counter0 }}").css("display", "none") $(".podproblem{{ forloop.counter0 }}").css("display", "none")
$("#problem{{ forloop.counter0 }}")[0].addEventListener('click', podproblem{{ forloop.counter0 }}); $("#problem{{ forloop.counter0 }}")[0].addEventListener('click', podproblem{{ forloop.counter0 }});
@ -68,16 +66,16 @@
} }
} }
{% endfor %} {% endfor %}
{% if ostatni %} {% if vysledkovka.ostatni %}
diplayed{{ problemy | length }} = false; diplayed{{ vysledkovka.temata_a_spol| length }} = false;
$(".podproblem{{ problemy | length }}").css("display", "none") $(".podproblem{{ vysledkovka.temata_a_spol| length }}").css("display", "none")
$("#problem{{ problemy | length }}")[0].addEventListener('click', podproblem{{ problemy | length }}); $("#problem{{ vysledkovka.temata_a_spol| length }}")[0].addEventListener('click', podproblem{{ vysledkovka.temata_a_spol| length }});
function podproblem{{ problemy | length }}(event) { function podproblem{{ vysledkovka.temata_a_spol| length }}(event) {
diplayed{{ problemy | length }} = !diplayed{{ problemy | length }}; diplayed{{ vysledkovka.temata_a_spol| length }} = !diplayed{{ vysledkovka.temata_a_spol| length }};
if (diplayed{{ problemy | length }}) { if (diplayed{{ vysledkovka.temata_a_spol| length }}) {
$(".podproblem{{ problemy | length }}").css("display", ""); $(".podproblem{{ vysledkovka.temata_a_spol| length }}").css("display", "");
} else { } else {
$(".podproblem{{ problemy | length }}").css("display", "none"); $(".podproblem{{ 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 %}

871
vysledkovky/utils.py

@ -1,462 +1,451 @@
import abc
from functools import cached_property
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, cisla_rocniku, hlavni_problem,\
import time hlavni_problemy_f, problemy_cisla, podproblemy_v_cislu
ROCNIK_ZRUSENI_TEMAT = 25 ROCNIK_ZRUSENI_TEMAT = 25
def sloupec_s_poradim(setrizene_body):
"""
Ze seznamu obsahujícího sestupně setřízené body řešitelů za daný ročník
vytvoří seznam s pořadími (včetně 3.-5. a pak 2 volná místa atp.),
podle toho, jak jdou za sebou ve výsledkovce.
Parametr:
setrizene_body (seznam integerů): sestupně setřízená čísla
Výstup:
sloupec_s_poradim (seznam stringů)
"""
# ze seznamu obsahujícího setřízené body spočítáme sloupec s pořadím
aktualni_poradi = 1
sloupec_s_poradim = []
# seskupíme seznam všech bodů podle hodnot
for index in range(0, len(setrizene_body)):
# pokud je pořadí větší než číslo řádku, tak jsme vypsali větší rozsah a chceme
# vypsat už jen prázdné místo, než dojdeme na správný řádek
if (index + 1) < aktualni_poradi:
sloupec_s_poradim.append("")
continue
velikost_skupiny = 0
# zjistíme počet po sobě jdoucích stejných hodnot
while setrizene_body[index] == setrizene_body[index + velikost_skupiny]:
velikost_skupiny = velikost_skupiny + 1
# na konci musíme ošetřit přetečení seznamu
if (index + velikost_skupiny) > len(setrizene_body) - 1:
break
# pokud je velikost skupiny 1, vypíšu pořadí
if velikost_skupiny == 1:
sloupec_s_poradim.append("{}.".format(aktualni_poradi))
# pokud je skupina větší, vypíšu rozsah
else:
sloupec_s_poradim.append("{}.–{}.".format(aktualni_poradi,
aktualni_poradi+velikost_skupiny-1))
# zvětšíme aktuální pořadí o tolik, kolik pozic bylo přeskočeno
aktualni_poradi = aktualni_poradi + velikost_skupiny
return sloupec_s_poradim
def body_resitelu(resitele, za, odjakziva=True, jen_verejne=False):
""" Funkce počítající počty bodů pro zadané řešitele,
buď odjakživa do daného ročníku/čísla anebo za daný ročník/číslo.
Parametry:
resitele (seznam obsahující položky typu Resitel): aktivní řešitelé
za (Rocnik/Cislo): za co se mají počítat body
(generování starších výsledkovek)
odjakziva (bool): zda se mají počítat body odjakživa, nebo jen za číslo/ročník
zadané v "za"
Výstup:
slovník (Resitel.id):body
"""
resitele_id = [r.id for r in resitele]
# Zjistíme, typ objektu v parametru "za"
if isinstance(za, m.Rocnik):
cislo = None
rocnik = za
rok = rocnik.prvni_rok
elif isinstance(za, m.Cislo):
cislo = za
rocnik = None
rok = cislo.rocnik.prvni_rok
else:
assert True, "body_resitelu: za není ani číslo ani ročník."
# Kvůli rychlosti používáme sčítáme body už v databázi, viz
# https://docs.djangoproject.com/en/3.0/topics/db/aggregation/,
# sekce Filtering on annotations (protože potřebujeme filtrovat výsledky
# jen do nějakého data, abychom uměli správně nagenerovat výsledkovky i
# za historická čísla.
# Níže se vytváří dotaz na součet bodů za správně vyfiltrovaná hodnocení,
# který se použije ve výsledném dotazu.
if cislo and odjakziva: # Body se sčítají odjakživa do zadaného čísla.
# Vyfiltrujeme všechna hodnocení, která jsou buď ze starších ročníků,
# anebo ze stejného ročníku, jak je zadané číslo, tam ale sčítáme jen
# pro čísla s pořadím nejvýše stejným, jako má zadané číslo.
body_k_zapocteni = Sum('reseni__hodnoceni__body',
filter=( Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lt=rok) |
Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok=rok,
reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi) ))
elif cislo and not odjakziva: # Body se sčítají za dané číslo.
body_k_zapocteni = Sum('reseni__hodnoceni__body',
filter=( Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok=rok,
reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi) ))
elif rocnik and odjakziva: # Spočítáme body za starší ročníky až do zadaného včetně.
if jen_verejne:
body_k_zapocteni = Sum('reseni__hodnoceni__body',
filter= Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lte=rok,
reseni__hodnoceni__cislo_body__verejna_vysledkovka=True))
else:
body_k_zapocteni = Sum('reseni__hodnoceni__body',
filter= Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lte=rok))
elif rocnik and not odjakziva: # Spočítáme body za daný ročník.
if jen_verejne:
body_k_zapocteni = Sum('reseni__hodnoceni__body',
filter=Q(reseni__hodnoceni__cislo_body__rocnik=rocnik,
reseni__hodnoceni__cislo_body__verejna_vysledkovka=True))
else:
body_k_zapocteni = Sum('reseni__hodnoceni__body',
filter=Q(reseni__hodnoceni__cislo_body__rocnik=rocnik))
else:
assert True, "body_resitelu: Neplatná kombinace za a odjakživa."
# Následující řádek přidá ke každému řešiteli údaj ".body" se součtem jejich bodů
resitele_s_body = m.Resitel.objects.filter(id__in=resitele_id).annotate(
body=body_k_zapocteni)
# Teď jen z QuerySetu řešitelů anotovaných body vygenerujeme slovník
# indexovaný řešitelským id obsahující body.
# Pokud jsou body None, nahradíme za 0.
slovnik = {int(res.id) : (res.body if res.body else 0) for res in resitele_s_body}
return slovnik
class RadekVysledkovkyRocniku(object):
""" Obsahuje věci, které se hodí vědět při konstruování výsledkovky.
Umožňuje snazší práci v templatu (lepší, než seznam)."""
def __init__(self, poradi, resitel, body_cisla_sezn, body_rocnik, body_odjakziva, rok):
self.poradi = poradi
self.resitel = resitel
self.rocnik_resitele = resitel.rocnik(rok)
self.body_rocnik = body_rocnik
self.body_celkem_odjakziva = body_odjakziva
self.body_cisla_sezn = body_cisla_sezn
self.titul = resitel.get_titul(body_odjakziva)
def setrid_resitele_a_body(slov_resitel_body):
setrizeni_resitele_id = [dvojice[0] for dvojice in slov_resitel_body]
setrizene_body = [dvojice[1] for dvojice in slov_resitel_body]
return setrizeni_resitele_id, setrizene_body
def data_vysledkovky_rocniku(rocnik, jen_verejne=True):
""" Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve
formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html"
"""
start = time.time()
## TODO možná chytřeji vybírat aktivní řešitele
# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají
# u alespoň jedné hodnoty něco jiného než NULL
aktivni_resitele = list(resi_v_rocniku(rocnik))
cisla = cisla_rocniku(rocnik, jen_verejne)
body_cisla_slov = {}
for cislo in cisla:
# získáme body za číslo
_, cislobody = secti_body_za_cislo(cislo, aktivni_resitele)
body_cisla_slov[cislo.id] = cislobody
# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně
resitel_rocnikbody_sezn = secti_body_za_rocnik(rocnik, aktivni_resitele, jen_verejne=jen_verejne)
# setřídíme řešitele podle počtu bodů a získáme seznam s body od nejvyšších po nenižší
setrizeni_resitele_id, setrizene_body = setrid_resitele_a_body(resitel_rocnikbody_sezn)
poradi = sloupec_s_poradim(setrizene_body)
# získáme body odjakživa
resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, rocnik, jen_verejne=jen_verejne)
# vytvoříme jednotlivé sloupce výsledkovky
radky_vysledkovky = []
i = 0
setrizeni_resitele_dict = {} # Tento slovnik se vyrab
for r in m.Resitel.objects.filter(id__in=setrizeni_resitele_id).select_related('osoba'):
setrizeni_resitele_dict[r.id] = r
for ar_id in setrizeni_resitele_id:
# seznam počtu bodů daného řešitele pro jednotlivá čísla
body_cisla_sezn = []
for cislo in cisla:
body_cisla_sezn.append(body_cisla_slov[cislo.id][ar_id])
# vytáhneme informace pro daného řešitele
radek = RadekVysledkovkyRocniku(
poradi[i], # pořadí
setrizeni_resitele_dict[ar_id], # řešitel (z id)
body_cisla_sezn, # seznam bodů za čísla
setrizene_body[i], # body za ročník (spočítané výše s pořadím)
resitel_odjakzivabody_slov[ar_id], # body odjakživa
rocnik) # ročník semináře pro získání ročníku řešitele
radky_vysledkovky.append(radek)
i += 1
end = time.time()
print("Vysledkovka rocniku",end-start)
radky_vysledkovky = [radek for radek in radky_vysledkovky if radek.body_rocnik > 0]
return radky_vysledkovky, cisla
class RadekVysledkovkyCisla(object):
"""Obsahuje věci, které se hodí vědět při konstruování výsledkovky.
Umožňuje snazší práci v templatu (lepší, než seznam)."""
def __init__(self, poradi, resitel, body_problemy_sezn,
body_cislo, body_rocnik, body_odjakziva, rok, body_podproblemy, body_podproblemy_iter):
self.resitel = resitel
self.rocnik_resitele = resitel.rocnik(rok)
self.body_cislo = body_cislo
self.body_rocnik = body_rocnik
self.body_celkem_odjakziva = body_odjakziva
self.poradi = poradi
self.body_problemy_sezn = body_problemy_sezn
self.titul = resitel.get_titul(body_odjakziva)
self.body_podproblemy = body_podproblemy
self.body_podproblemy_iter = body_podproblemy_iter # TODELETE
def pricti_body(slovnik, resitel, body):
""" Přiřazuje danému řešiteli body do slovníku. """
# testujeme na None (""), pokud je to první řešení
# daného řešitele, předěláme na 0
# (v dalším kroku přičteme reálný počet bodů),
# rozlišujeme tím mezi 0 a neodevzdaným řešením
# Speciálně pokud jsou body None (hodnocení není obodované), vraťse
# TODO nejde to udělat lépe?
if body is None:
return
if slovnik[resitel.id] == "":
slovnik[resitel.id] = 0
slovnik[resitel.id] += body
def secti_body_za_rocnik(za, aktivni_resitele, jen_verejne):
""" Spočítá body za ročník (celý nebo do daného čísla),
setřídí je sestupně a vrátí jako seznam.
Parametry:
za (typu Rocnik nebo Cislo) spočítá za ročník, nebo za ročník do
daného čísla
"""
# spočítáme všem řešitelům jejich body za ročník (False => ne odjakživa)
resitel_rocnikbody_slov = body_resitelu(aktivni_resitele, za, False, jen_verejne=jen_verejne)
# zeptáme se na dvojice (řešitel, body) za ročník a setřídíme sestupně
resitel_rocnikbody_sezn = sorted(resitel_rocnikbody_slov.items(),
key = lambda x: x[1], reverse = True)
return resitel_rocnikbody_sezn
def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None):
""" Spočítá u řešitelů body za číslo a za jednotlivé hlavní problémy (témata)."""
# TODO setřídit hlavní problémy čísla podle id, ať jsou ve stejném pořadí pokaždé
# pro každý hlavní problém zavedeme slovník s body za daný hlavní problém
# pro jednotlivé řešitele (slovník slovníků hlavních problémů)
print("Scitam cislo",cislo)
if hlavni_problemy is None:
hlavni_problemy = hlavni_problemy_f(problemy_cisla(cislo))
def ne_clanek_ne_konfera(problem):
inst = problem.get_real_instance()
return not(isinstance(inst, m.Clanek) or isinstance(inst, m.Konfera))
if cislo.rocnik.rocnik < ROCNIK_ZRUSENI_TEMAT:
temata_a_spol = hlavni_problemy
else:
temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy))
hlavni_problemy_slovnik = {}
for hp in temata_a_spol:
hlavni_problemy_slovnik[hp.id] = {}
hlavni_problemy_slovnik[-1] = {}
# zakládání prázdných záznamů pro řešitele
cislobody = {}
for ar in aktivni_resitele:
# řešitele převedeme na řetězec pomocí unikátního id
cislobody[ar.id] = ""
for hp in temata_a_spol:
slovnik = hlavni_problemy_slovnik[hp.id]
slovnik[ar.id] = ""
hlavni_problemy_slovnik[-1][ar.id] = ""
hodnoceni_do_cisla = m.Hodnoceni.objects.prefetch_related('problem', 'reseni', 'reseni__resitele').filter(cislo_body=cislo)
start = time.time()
for hodnoceni in hodnoceni_do_cisla:
prob = hodnoceni.problem
nadproblem = hlavni_problem(prob)
if ne_clanek_ne_konfera(nadproblem):
nadproblem_slovnik = hlavni_problemy_slovnik[nadproblem.id]
else:
nadproblem_slovnik = hlavni_problemy_slovnik[-1]
body = hodnoceni.body
# a mít více řešitelů
for resitel in hodnoceni.reseni.resitele.all():
if resitel not in aktivni_resitele:
print("Skipping {}".format(resitel.id))
continue
pricti_body(cislobody, resitel, body)
pricti_body(nadproblem_slovnik, resitel, body)
end = time.time()
print("for cykly:", end-start)
return hlavni_problemy_slovnik, cislobody
def secti_body_za_cislo_podle_temat(cislo, aktivni_resitele, podproblemy=None, temata=None):
""" Spočítá u řešitelů body za číslo za úlohy v jednotlivých hlavních problémech (témata)."""
if temata is None:
temata = hlavni_problemy_f(problemy_cisla(cislo))
if podproblemy is None:
podproblemy_v_cislu(cislo, hlavni_problemy=temata)
body_slovnik = {}
for tema in temata:
body_slovnik[tema.id] = {}
for problem in podproblemy[tema.id]:
body_slovnik[tema.id][problem.id] = {}
body_slovnik[-1] = {}
for problem in podproblemy[-1]:
body_slovnik[-1][problem.id] = {}
# zakládání prázdných záznamů pro řešitele
for ar in aktivni_resitele:
for tema in temata:
for problem in podproblemy[tema.id]:
body_slovnik[tema.id][problem.id][ar.id] = ""
for problem in podproblemy[-1]:
body_slovnik[-1][problem.id][ar.id] = ""
temata = set(t.id for t in temata)
hodnoceni_do_cisla = m.Hodnoceni.objects.prefetch_related('problem', 'reseni', 'reseni__resitele').filter(cislo_body=cislo)
for hodnoceni in hodnoceni_do_cisla:
prob = hodnoceni.problem
nadproblem = hlavni_problem(prob)
if nadproblem.id in temata:
nadproblem_slovnik = body_slovnik[nadproblem.id]
else:
nadproblem_slovnik = body_slovnik[-1]
problem_slovnik = nadproblem_slovnik[prob.id]
body = hodnoceni.body
# a mít více řešitelů
for resitel in hodnoceni.reseni.resitele.all():
if resitel not in aktivni_resitele:
print("Skipping {}".format(resitel.id))
continue
pricti_body(problem_slovnik, resitel, body)
return body_slovnik
# TODELETE
class FixedIterator: class FixedIterator:
def next(self): def next(self):
return self.niter.__next__() return self.niter.__next__()
def __init__(self, niter): def __init__(self, niter):
self.niter = niter self.niter = niter
# TODELETE
def data_vysledkovky_cisla(cislo): def body_resitelu(
problemy = problemy_cisla(cislo) za=None,
hlavni_problemy = hlavni_problemy_f(problemy) do: m.Deadline = None,
## TODO možná chytřeji vybírat aktivní řešitele od: m.Deadline = None,
# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají jen_verejne: bool = True,
# u alespoň jedné hodnoty něco jiného než NULL resitele=None,
aktivni_resitele = list(resi_v_rocniku(cislo.rocnik)) null=0
) -> dict[int, int]:
filtr = Q()
# získáme body za číslo if jen_verejne:
hlavni_problemy_slovnik, cislobody = secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy) filtr &= Q(reseni__hodnoceni__deadline_body__verejna_vysledkovka=True)
# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně # Zjistíme, typ objektu v parametru "za"
resitel_rocnikbody_sezn = secti_body_za_rocnik(cislo, aktivni_resitele, jen_verejne=True) if isinstance(za, m.Rocnik):
filtr &= Q(reseni__hodnoceni__deadline_body__cislo__rocnik=za)
elif isinstance(za, m.Cislo):
filtr &= Q(reseni__hodnoceni__deadline_body__cislo=za)
# získáme body odjakživa if do:
resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, cislo, jen_verejne=True) filtr &= Q(reseni__hodnoceni__deadline_body__lte=do)
# řešitelé setřídění podle bodů za číslo sestupně if od:
setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn] filtr &= Q(reseni__hodnoceni__deadline_body__gte=od)
# spočítáme pořadí řešitelů resiteleQuery = m.Resitel.objects.all()
setrizeni_resitele_body = [dvojice[1] for dvojice in resitel_rocnikbody_sezn]
poradi = sloupec_s_poradim(setrizeni_resitele_body)
# vytvoříme jednotlivé sloupce výsledkovky if resitele is not None:
radky_vysledkovky = [] resitele_id = [r.id for r in resitele]
i = 0 resiteleQuery = resiteleQuery.filter(id__in=resitele_id)
def ne_clanek_ne_konfera(problem): # 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
return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera))
class Vysledkovka(abc.ABC):
if cislo.rocnik.rocnik < ROCNIK_ZRUSENI_TEMAT: jen_verejne: bool
temata_a_spol = hlavni_problemy rocnik: m.Rocnik
else: do_deadlinu: m.Deadline
temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy))
@property
# získáme body u jednotlivých témat @abc.abstractmethod
podproblemy = podproblemy_v_cislu(cislo, problemy, temata_a_spol) def aktivni_resitele(self) -> list[m.Resitel]:
problemy_slovnik = secti_body_za_cislo_podle_temat(cislo, aktivni_resitele, podproblemy, temata_a_spol) ...
# def not_empty(value): @cached_property
# return value != '' 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
# je_nejake_ostatni = any(filter(not_empty, hlavni_problemy_slovnik[-1].values())) > 0 resitel_body_za_rocnik_slovnik = body_resitelu(
resitele=self.aktivni_resitele,
je_nejake_ostatni = len(hlavni_problemy) - len(temata_a_spol) > 0 za=self.rocnik,
jen_verejne=self.jen_verejne,
setrizeni_resitele_slovnik = {} do=self.do_deadlinu
setrizeni_resitele = m.Resitel.objects.filter(id__in=setrizeni_resitele_id).select_related('osoba') )
for r in setrizeni_resitele:
setrizeni_resitele_slovnik[r.id] = r # 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(
for ar_id in setrizeni_resitele_id: resitel_body_za_rocnik_slovnik.items(),
# získáme seznam bodů za problémy pro daného řešitele key=lambda x: x[1], reverse=True
body_problemy = [] )
body_podproblemy = []
for hp in temata_a_spol: return resitele_s_body_za_rocnik_setrizeny_seznam
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]]) @cached_property
if je_nejake_ostatni: def body_za_rocnik_seznamy(self) -> tuple[list[int], list[int]]:
body_problemy.append(hlavni_problemy_slovnik[-1][ar_id]) if len(self.resitele_s_body_za_rocnik_setrizeny_seznam) == 0:
body_podproblemy.append([problemy_slovnik[-1][it.id][ar_id] for it in podproblemy[-1]]) return [], []
# vytáhneme informace pro daného řešitele return tuple(zip(*self.resitele_s_body_za_rocnik_setrizeny_seznam))
radek = RadekVysledkovkyCisla(
poradi[i], # pořadí @cached_property
setrizeni_resitele_slovnik[ar_id], # řešitel (z id) def setrizeni_resitele_id(self) -> list[int]:
body_problemy, # seznam bodů za hlavní problémy čísla return self.body_za_rocnik_seznamy[0]
cislobody[ar_id], # body za číslo
setrizeni_resitele_body[i], # body za ročník (spočítané výše s pořadím) @cached_property
resitel_odjakzivabody_slov[ar_id], # body odjakživa def setrizene_body(self) -> list[int]:
cislo.rocnik, return self.body_za_rocnik_seznamy[1]
body_podproblemy, # body všech podproblémů
FixedIterator(body_podproblemy.__iter__()) # TODELETE @cached_property
) # ročník semináře pro zjištění ročníku řešitele def resitel_body_odjakziva_slovnik(self) -> dict[int, int]:
radky_vysledkovky.append(radek) return body_resitelu(jen_verejne=self.jen_verejne, do=self.do_deadlinu)
i += 1
@cached_property
# vytahané informace předáváme do kontextu def poradi(self):
pt = [podproblemy[it.id] for it in temata_a_spol]+[podproblemy[-1]] # ze seznamu obsahujícího setřízené body spočítáme sloupec s pořadím
radky_vysledkovky = [radek for radek in radky_vysledkovky if radek.body_rocnik > 0] aktualni_poradi = 1
return ( sloupec_s_poradim = []
radky_vysledkovky,
temata_a_spol, # seskupíme seznam všech bodů podle hodnot
je_nejake_ostatni, for index in range(0, len(self.setrizene_body)):
pt, # pokud je pořadí větší než číslo řádku, tak jsme vypsali větší rozsah
FixedIterator(pt.__iter__()) # a chceme vypsat už jen prázdné místo, než dojdeme na správný řádek
) if (index + 1) < aktualni_poradi:
sloupec_s_poradim.append("")
continue
velikost_skupiny = 0
# zjistíme počet po sobě jdoucích stejných hodnot
while self.setrizene_body[index] == self.setrizene_body[
index + velikost_skupiny]:
velikost_skupiny += 1
# na konci musíme ošetřit přetečení seznamu
if (index + velikost_skupiny) > len(self.setrizene_body) - 1:
break
# pokud je velikost skupiny 1, vypíšu pořadí
if velikost_skupiny == 1:
sloupec_s_poradim.append(f"{aktualni_poradi}.")
# pokud je skupina větší, vypíšu rozsah
else:
sloupec_s_poradim.append(
f"{aktualni_poradi}.–{aktualni_poradi + velikost_skupiny - 1}."
)
# zvětšíme aktuální pořadí o tolik, kolik pozic bylo přeskočeno
aktualni_poradi += velikost_skupiny
return sloupec_s_poradim
class VysledkovkaRocniku(Vysledkovka):
def __init__(self, rocnik: m.Rocnik, jen_verejne: bool = True):
self.rocnik = rocnik
self.jen_verejne = jen_verejne
self.do_deadlinu = m.Deadline.objects.filter(cislo__rocnik=rocnik).last()
@cached_property
def aktivni_resitele(self) -> list[m.Resitel]:
return list(resi_v_rocniku(self.rocnik))
@cached_property
def cisla_rocniku(self) -> list[m.Cislo]:
return cisla_rocniku(self.rocnik, self.jen_verejne)
@cached_property
def body_za_cisla_slovnik(self) -> dict[int, dict[int, int]]:
body_cisla_slovnik = dict()
for cislo in self.cisla_rocniku:
# získáme body za číslo
body_za_cislo = body_resitelu(
za=cislo,
resitele=self.aktivni_resitele,
jen_verejne=self.jen_verejne,
null=""
)
body_cisla_slovnik[cislo.id] = body_za_cislo
return body_cisla_slovnik
class RadekVysledkovkyRocniku:
""" Obsahuje věci, které se hodí vědět při konstruování výsledkovky.
Umožňuje snazší práci v templatu (lepší, než seznam)."""
def __init__(self, poradi, resitel, body_cisla_seznam, body_rocnik, body_odjakziva, rok):
self.poradi = poradi
self.resitel = resitel
self.rocnik_resitele = resitel.rocnik(rok)
self.body_rocnik = body_rocnik
self.body_celkem_odjakziva = body_odjakziva
self.body_cisla_seznam = body_cisla_seznam
self.titul = resitel.get_titul(body_odjakziva)
@cached_property
def radky_vysledkovky(self) -> list[RadekVysledkovkyRocniku]:
radky_vysledkovky = []
i = 0
setrizeni_resitele_dict = dict()
for r in m.Resitel.objects.filter(
id__in=self.setrizeni_resitele_id
).select_related('osoba'):
setrizeni_resitele_dict[r.id] = r
for ar_id in 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)
i += 1
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]:
return problemy_cisla(self.cislo)
@cached_property
def hlavni_problemy(self) -> list[m.Problem]:
return hlavni_problemy_f(self.problemy)
@cached_property
def problemy_s_body_za_cislo(self):
hlavni_problemy_slovnik = dict()
for hp in self.hlavni_problemy:
hlavni_problemy_slovnik[hp.id] = {}
hlavni_problemy_slovnik[-1] = {}
# zakládání prázdných záznamů pro řešitele
cislobody = {}
for ar in self.aktivni_resitele:
# řešitele převedeme na řetězec pomocí unikátního id
cislobody[ar.id] = ""
for hp in self.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(deadline_body__cislo=self.cislo)
for hodnoceni in hodnoceni_do_cisla:
prob = hodnoceni.problem
nadproblem = hlavni_problem(prob)
if self.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 self.aktivni_resitele:
continue
self.pricti_body(cislobody, resitel, body)
self.pricti_body(nadproblem_slovnik, resitel, body)
return hlavni_problemy_slovnik, cislobody
@cached_property
def hlavni_problemy_slovnik(self) -> dict[int, dict[int, str]]:
return self.problemy_s_body_za_cislo[0]
@cached_property
def body_za_cislo(self) -> dict[int, str]:
return self.problemy_s_body_za_cislo[1]
@cached_property
def temata_a_spol(self) -> list[m.Problem]:
if self.cislo.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) -> list[list[m.Problem]]:
return podproblemy_v_cislu(self.cislo, self.problemy, self.temata_a_spol)
@cached_property
def podproblemy_seznam(self) -> list[list[m.Problem]]:
return [self.podproblemy[it.id] for it in self.temata_a_spol] + [self.podproblemy[-1]]
@cached_property
def podproblemy_iter(self) -> FixedIterator:
return FixedIterator(self.podproblemy_seznam.__iter__())
@cached_property
def problemy_slovnik(self):
# získáme body u jednotlivých témat
""" Spočítá u řešitelů body za číslo za úlohy v jednotlivých hlavních
problémech (témata). """
body_slovnik = {}
for tema in self.temata_a_spol:
body_slovnik[tema.id] = {}
for problem in self.podproblemy[tema.id]:
body_slovnik[tema.id][problem.id] = {}
body_slovnik[-1] = {}
for problem in self.podproblemy[-1]:
body_slovnik[-1][problem.id] = {}
# zakládání prázdných záznamů pro řešitele
for ar in self.aktivni_resitele:
for tema in self.temata_a_spol:
for problem in self.podproblemy[tema.id]:
body_slovnik[tema.id][problem.id][ar.id] = ""
for problem in self.podproblemy[-1]:
body_slovnik[-1][problem.id][ar.id] = ""
temata = set(t.id for t in self.temata_a_spol)
hodnoceni = m.Hodnoceni.objects.prefetch_related(
'problem', 'reseni', 'reseni__resitele')
if self.jen_verejne:
hodnoceni = hodnoceni.filter(deadline_body__verejna_vysledkovka=True)
hodnoceni_do_cisla = hodnoceni.filter(deadline_body__cislo=self.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 self.aktivni_resitele:
continue
self.pricti_body(problem_slovnik, resitel, body)
return body_slovnik
class RadekVysledkovkyCisla(object):
"""Obsahuje věci, které se hodí vědět při konstruování výsledkovky.
Umožňuje snazší práci v templatu (lepší, než seznam)."""
def __init__(self, poradi, resitel, body_hlavni_problemy_seznam, body_cislo, body_rocnik, body_odjakziva, rok, body_podproblemy, body_podproblemy_iter):
self.resitel = resitel
self.rocnik_resitele = resitel.rocnik(rok)
self.body_cislo = body_cislo
self.body_rocnik = body_rocnik
self.body_celkem_odjakziva = body_odjakziva
self.poradi = poradi
self.body_hlavni_problemy_seznam = body_hlavni_problemy_seznam
self.titul = resitel.get_titul(body_odjakziva)
self.body_podproblemy = body_podproblemy
self.body_podproblemy_iter = body_podproblemy_iter
@cached_property
def radky_vysledkovky(self) -> list[RadekVysledkovkyCisla]:
# vytvoříme jednotlivé sloupce výsledkovky
radky_vysledkovky = []
i = 0
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 ar_id in 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.hlavni_problemy_slovnik[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.hlavni_problemy_slovnik[-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],
body_hlavni_problemy_seznam=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)
i += 1
return radky_vysledkovky
@staticmethod
def pricti_body(slovnik, resitel, body):
""" Přiřazuje danému řešiteli body do slovníku. """
# testujeme na None (""), pokud je to první řešení
# daného řešitele, předěláme na 0
# (v dalším kroku přičteme reálný počet bodů),
# rozlišujeme tím mezi 0 a neodevzdaným řešením
# Speciálně pokud jsou body None (hodnocení není obodované), vraťse
# TODO nejde to udělat lépe?
if body is None:
return
if slovnik[resitel.id] == "":
slovnik[resitel.id] = 0
slovnik[resitel.id] += body
@staticmethod
def ne_clanek_ne_konfera(problem):
inst = problem.get_real_instance()
return not (isinstance(inst, m.Clanek) or isinstance(inst, m.Konfera))

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