Browse Source

Přepsání výsledkovek v1

zadavatko_problemu
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. 655
      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 %}

655
vysledkovky/utils.py

@ -1,298 +1,277 @@
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: class FixedIterator:
sloupec_s_poradim (seznam stringů) def next(self):
""" return self.niter.__next__()
def __init__(self, niter):
self.niter = niter
def body_resitelu(
za=None,
do: m.Deadline = None,
od: m.Deadline = None,
jen_verejne: bool = True,
resitele=None,
null=0
) -> 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__lte=do)
if od:
filtr &= Q(reseni__hodnoceni__deadline_body__gte=od)
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))
@cached_property
def setrizeni_resitele_id(self) -> list[int]:
return self.body_za_rocnik_seznamy[0]
@cached_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é
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ů @cached_property
resitele_s_body = m.Resitel.objects.filter(id__in=resitele_id).annotate( def cisla_rocniku(self) -> list[m.Cislo]:
body=body_k_zapocteni) return cisla_rocniku(self.rocnik, self.jen_verejne)
# 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): @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. """ 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]
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 = [] radky_vysledkovky = []
i = 0 i = 0
setrizeni_resitele_dict = {} # Tento slovnik se vyrab setrizeni_resitele_dict = dict()
for r in m.Resitel.objects.filter(id__in=setrizeni_resitele_id).select_related('osoba'): for r in m.Resitel.objects.filter(
id__in=self.setrizeni_resitele_id
).select_related('osoba'):
setrizeni_resitele_dict[r.id] = r setrizeni_resitele_dict[r.id] = r
for ar_id in setrizeni_resitele_id: 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 # seznam počtu bodů daného řešitele pro jednotlivá čísla
body_cisla_sezn = [] body_cisla_seznam = []
for cislo in cisla: for cislo in self.cisla_rocniku:
body_cisla_sezn.append(body_cisla_slov[cislo.id][ar_id]) 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 # vytáhneme informace pro daného řešitele
radek = RadekVysledkovkyRocniku( radek = self.RadekVysledkovkyRocniku(
poradi[i], # pořadí poradi=self.poradi[i],
setrizeni_resitele_dict[ar_id], # řešitel (z id) resitel=setrizeni_resitele_dict[ar_id],
body_cisla_sezn, # seznam bodů za čísla body_cisla_seznam=body_cisla_seznam,
setrizene_body[i], # body za ročník (spočítané výše s pořadím) body_rocnik=self.setrizene_body[i],
resitel_odjakzivabody_slov[ar_id], # body odjakživa body_odjakziva=self.resitel_body_odjakziva_slovnik[ar_id],
rocnik) # ročník semináře pro získání ročníku řešitele rok=self.rocnik) # ročník semináře pro získání ročníku řešitele
radky_vysledkovky.append(radek) radky_vysledkovky.append(radek)
i += 1
end = time.time() i += 1
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 = {} return radky_vysledkovky
for hp in temata_a_spol:
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[hp.id] = {}
hlavni_problemy_slovnik[-1] = {} hlavni_problemy_slovnik[-1] = {}
# zakládání prázdných záznamů pro řešitele # zakládání prázdných záznamů pro řešitele
cislobody = {} cislobody = {}
for ar in aktivni_resitele: for ar in self.aktivni_resitele:
# řešitele převedeme na řetězec pomocí unikátního id # řešitele převedeme na řetězec pomocí unikátního id
cislobody[ar.id] = "" cislobody[ar.id] = ""
for hp in temata_a_spol: for hp in self.temata_a_spol:
slovnik = hlavni_problemy_slovnik[hp.id] slovnik = hlavni_problemy_slovnik[hp.id]
slovnik[ar.id] = "" slovnik[ar.id] = ""
hlavni_problemy_slovnik[-1][ar.id] = "" hlavni_problemy_slovnik[-1][ar.id] = ""
hodnoceni_do_cisla = m.Hodnoceni.objects.prefetch_related('problem', 'reseni', 'reseni__resitele').filter(cislo_body=cislo) hodnoceni_do_cisla = m.Hodnoceni.objects.prefetch_related(
'problem', 'reseni', 'reseni__resitele').filter(deadline_body__cislo=self.cislo)
start = time.time()
for hodnoceni in hodnoceni_do_cisla: for hodnoceni in hodnoceni_do_cisla:
prob = hodnoceni.problem prob = hodnoceni.problem
nadproblem = hlavni_problem(prob) nadproblem = hlavni_problem(prob)
if ne_clanek_ne_konfera(nadproblem): if self.ne_clanek_ne_konfera(nadproblem):
nadproblem_slovnik = hlavni_problemy_slovnik[nadproblem.id] nadproblem_slovnik = hlavni_problemy_slovnik[nadproblem.id]
else: else:
nadproblem_slovnik = hlavni_problemy_slovnik[-1] nadproblem_slovnik = hlavni_problemy_slovnik[-1]
@ -301,45 +280,74 @@ def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None):
# a mít více řešitelů # a mít více řešitelů
for resitel in hodnoceni.reseni.resitele.all(): for resitel in hodnoceni.reseni.resitele.all():
if resitel not in aktivni_resitele: if resitel not in self.aktivni_resitele:
print("Skipping {}".format(resitel.id))
continue continue
pricti_body(cislobody, resitel, body) self.pricti_body(cislobody, resitel, body)
pricti_body(nadproblem_slovnik, resitel, body) self.pricti_body(nadproblem_slovnik, resitel, body)
end = time.time()
print("for cykly:", end-start)
return hlavni_problemy_slovnik, cislobody 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
def secti_body_za_cislo_podle_temat(cislo, aktivni_resitele, podproblemy=None, temata=None): @cached_property
""" Spočítá u řešitelů body za číslo za úlohy v jednotlivých hlavních problémech (témata).""" def podproblemy(self) -> list[list[m.Problem]]:
if temata is None: return podproblemy_v_cislu(self.cislo, self.problemy, self.temata_a_spol)
temata = hlavni_problemy_f(problemy_cisla(cislo))
if podproblemy is None: @cached_property
podproblemy_v_cislu(cislo, hlavni_problemy=temata) 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 = {} body_slovnik = {}
for tema in temata: for tema in self.temata_a_spol:
body_slovnik[tema.id] = {} body_slovnik[tema.id] = {}
for problem in podproblemy[tema.id]: for problem in self.podproblemy[tema.id]:
body_slovnik[tema.id][problem.id] = {} body_slovnik[tema.id][problem.id] = {}
body_slovnik[-1] = {} body_slovnik[-1] = {}
for problem in podproblemy[-1]: for problem in self.podproblemy[-1]:
body_slovnik[-1][problem.id] = {} body_slovnik[-1][problem.id] = {}
# zakládání prázdných záznamů pro řešitele # zakládání prázdných záznamů pro řešitele
for ar in aktivni_resitele: for ar in self.aktivni_resitele:
for tema in temata: for tema in self.temata_a_spol:
for problem in podproblemy[tema.id]: for problem in self.podproblemy[tema.id]:
body_slovnik[tema.id][problem.id][ar.id] = "" body_slovnik[tema.id][problem.id][ar.id] = ""
for problem in podproblemy[-1]: for problem in self.podproblemy[-1]:
body_slovnik[-1][problem.id][ar.id] = "" body_slovnik[-1][problem.id][ar.id] = ""
temata = set(t.id for t in temata) temata = set(t.id for t in self.temata_a_spol)
hodnoceni_do_cisla = m.Hodnoceni.objects.prefetch_related('problem', 'reseni', 'reseni__resitele').filter(cislo_body=cislo) 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: for hodnoceni in hodnoceni_do_cisla:
prob = hodnoceni.problem prob = hodnoceni.problem
@ -355,108 +363,89 @@ def secti_body_za_cislo_podle_temat(cislo, aktivni_resitele, podproblemy=None, t
# a mít více řešitelů # a mít více řešitelů
for resitel in hodnoceni.reseni.resitele.all(): for resitel in hodnoceni.reseni.resitele.all():
if resitel not in aktivni_resitele: if resitel not in self.aktivni_resitele:
print("Skipping {}".format(resitel.id))
continue continue
pricti_body(problem_slovnik, resitel, body) self.pricti_body(problem_slovnik, resitel, body)
return body_slovnik 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)."""
# TODELETE def __init__(self, poradi, resitel, body_hlavni_problemy_seznam, body_cislo, body_rocnik, body_odjakziva, rok, body_podproblemy, body_podproblemy_iter):
class FixedIterator: self.resitel = resitel
def next(self): self.rocnik_resitele = resitel.rocnik(rok)
return self.niter.__next__() self.body_cislo = body_cislo
self.body_rocnik = body_rocnik
def __init__(self, niter): self.body_celkem_odjakziva = body_odjakziva
self.niter = niter self.poradi = poradi
# TODELETE self.body_hlavni_problemy_seznam = body_hlavni_problemy_seznam
self.titul = resitel.get_titul(body_odjakziva)
self.body_podproblemy = body_podproblemy
def data_vysledkovky_cisla(cislo): self.body_podproblemy_iter = body_podproblemy_iter
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)
@cached_property
def radky_vysledkovky(self) -> list[RadekVysledkovkyCisla]:
# vytvoříme jednotlivé sloupce výsledkovky # vytvoříme jednotlivé sloupce výsledkovky
radky_vysledkovky = [] radky_vysledkovky = []
i = 0 i = 0
def ne_clanek_ne_konfera(problem):
return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera))
if cislo.rocnik.rocnik < ROCNIK_ZRUSENI_TEMAT:
temata_a_spol = hlavni_problemy
else:
temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy))
# získáme body u jednotlivých témat
podproblemy = podproblemy_v_cislu(cislo, problemy, temata_a_spol)
problemy_slovnik = secti_body_za_cislo_podle_temat(cislo, aktivni_resitele, podproblemy, temata_a_spol)
# def not_empty(value):
# return value != ''
#
# je_nejake_ostatni = any(filter(not_empty, hlavni_problemy_slovnik[-1].values())) > 0
je_nejake_ostatni = len(hlavni_problemy) - len(temata_a_spol) > 0
setrizeni_resitele_slovnik = {} setrizeni_resitele_slovnik = {}
setrizeni_resitele = m.Resitel.objects.filter(id__in=setrizeni_resitele_id).select_related('osoba') setrizeni_resitele = m.Resitel.objects.filter(id__in=self.setrizeni_resitele_id).select_related('osoba')
for r in setrizeni_resitele: for r in setrizeni_resitele:
setrizeni_resitele_slovnik[r.id] = r setrizeni_resitele_slovnik[r.id] = r
for ar_id in setrizeni_resitele_id: 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 # získáme seznam bodů za problémy pro daného řešitele
body_problemy = [] body_problemy = []
body_podproblemy = [] body_podproblemy = []
for hp in temata_a_spol: for hp in self.temata_a_spol:
body_problemy.append(hlavni_problemy_slovnik[hp.id][ar_id]) body_problemy.append(self.hlavni_problemy_slovnik[hp.id][ar_id])
body_podproblemy.append([problemy_slovnik[hp.id][it.id][ar_id] for it in podproblemy[hp.id]]) body_podproblemy.append([
if je_nejake_ostatni: self.problemy_slovnik[hp.id][it.id][ar_id]
body_problemy.append(hlavni_problemy_slovnik[-1][ar_id]) for it in self.podproblemy[hp.id]
body_podproblemy.append([problemy_slovnik[-1][it.id][ar_id] for it in podproblemy[-1]]) ])
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 # vytáhneme informace pro daného řešitele
radek = RadekVysledkovkyCisla( radek = self.RadekVysledkovkyCisla(
poradi[i], # pořadí poradi=self.poradi[i],
setrizeni_resitele_slovnik[ar_id], # řešitel (z id) resitel=setrizeni_resitele_slovnik[ar_id],
body_problemy, # seznam bodů za hlavní problémy čísla body_hlavni_problemy_seznam=body_problemy,
cislobody[ar_id], # body za číslo body_cislo=self.body_za_cislo[ar_id],
setrizeni_resitele_body[i], # body za ročník (spočítané výše s pořadím) body_rocnik=self.setrizene_body[i],
resitel_odjakzivabody_slov[ar_id], # body odjakživa body_odjakziva=self.resitel_body_odjakziva_slovnik[ar_id],
cislo.rocnik, rok=self.rocnik,
body_podproblemy, # body všech podproblémů body_podproblemy=body_podproblemy, # body všech podproblémů
FixedIterator(body_podproblemy.__iter__()) # TODELETE body_podproblemy_iter=FixedIterator(body_podproblemy.__iter__())
) # ročník semináře pro zjištění ročníku řešitele ) # ročník semináře pro zjištění ročníku řešitele
radky_vysledkovky.append(radek) radky_vysledkovky.append(radek)
i += 1 i += 1
return radky_vysledkovky
# vytahané informace předáváme do kontextu @staticmethod
pt = [podproblemy[it.id] for it in temata_a_spol]+[podproblemy[-1]] def pricti_body(slovnik, resitel, body):
radky_vysledkovky = [radek for radek in radky_vysledkovky if radek.body_rocnik > 0] """ Přiřazuje danému řešiteli body do slovníku. """
return ( # testujeme na None (""), pokud je to první řešení
radky_vysledkovky, # daného řešitele, předěláme na 0
temata_a_spol, # (v dalším kroku přičteme reálný počet bodů),
je_nejake_ostatni, # rozlišujeme tím mezi 0 a neodevzdaným řešením
pt,
FixedIterator(pt.__iter__()) # 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