vysledkovka | funkce pro vysledkovku presunuty do samostatneho souboru.
This commit is contained in:
parent
fe1cc4ac39
commit
1f0460e3c9
3 changed files with 523 additions and 516 deletions
|
@ -219,3 +219,87 @@ def viewMethodSwitch(get, post):
|
||||||
|
|
||||||
return NewView.as_view()
|
return NewView.as_view()
|
||||||
|
|
||||||
|
def cisla_rocniku(rocnik, jen_verejne=True):
|
||||||
|
"""
|
||||||
|
Vrátí všechna čísla daného ročníku.
|
||||||
|
Parametry:
|
||||||
|
rocnik (Rocnik): ročník semináře
|
||||||
|
jen_verejne (bool): zda se mají vrátit jen veřejná, nebo všechna čísla
|
||||||
|
Vrátí:
|
||||||
|
seznam objektů typu Cislo
|
||||||
|
"""
|
||||||
|
if jen_verejne:
|
||||||
|
return rocnik.verejna_cisla()
|
||||||
|
else:
|
||||||
|
return rocnik.cisla.all()
|
||||||
|
|
||||||
|
def hlavni_problem(problem):
|
||||||
|
""" Pro daný problém vrátí jeho nejvyšší nadproblém."""
|
||||||
|
while not(problem.nadproblem == None):
|
||||||
|
problem = problem.nadproblem
|
||||||
|
return problem
|
||||||
|
|
||||||
|
def hlavni_problemy_rocniku(rocnik, jen_verejne=True):
|
||||||
|
""" Pro zadaný ročník vrátí hlavní problémy ročníku,
|
||||||
|
tj. ty, které už nemají nadproblém."""
|
||||||
|
hlavni_problemy = []
|
||||||
|
for cislo in cisla_rocniku(rocnik, jen_verejne):
|
||||||
|
for problem in hlavni_problemy_cisla(cislo):
|
||||||
|
hlavni_problemy.append(problem)
|
||||||
|
hlavni_problemy_set = set(hlavni_problemy)
|
||||||
|
hlavni_problemy = list(hlavni_problemy_set)
|
||||||
|
hlavni_problemy.sort(key=lambda k:k.kod_v_rocniku()) # setřídit podle pořadí
|
||||||
|
|
||||||
|
return hlavni_problemy
|
||||||
|
|
||||||
|
def problemy_cisla(cislo):
|
||||||
|
""" Vrátí seznam všech problémů s body v daném čísle. """
|
||||||
|
hodnoceni = cislo.hodnoceni.select_related('problem', 'reseni').all()
|
||||||
|
# hodnocení, která se vážou k danému číslu
|
||||||
|
|
||||||
|
reseni = [h.reseni for h in hodnoceni]
|
||||||
|
problemy = [h.problem for h in hodnoceni]
|
||||||
|
problemy_set = set(problemy) # chceme každý problém unikátně,
|
||||||
|
problemy = (list(problemy_set)) # převedení na množinu a zpět to zaručí
|
||||||
|
|
||||||
|
return problemy
|
||||||
|
|
||||||
|
|
||||||
|
def hlavni_problemy_cisla(cislo, problemy=None):
|
||||||
|
""" Vrátí seznam všech problémů s body v daném čísle, které již nemají nadproblém. """
|
||||||
|
if problemy is None:
|
||||||
|
problemy = problemy_cisla(cislo)
|
||||||
|
|
||||||
|
# hlavní problémy čísla
|
||||||
|
# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém)
|
||||||
|
hlavni_problemy = []
|
||||||
|
for p in problemy:
|
||||||
|
hlavni_problemy.append(hlavni_problem(p))
|
||||||
|
|
||||||
|
# zunikátnění
|
||||||
|
hlavni_problemy_set = set(hlavni_problemy)
|
||||||
|
hlavni_problemy = list(hlavni_problemy_set)
|
||||||
|
hlavni_problemy.sort(key=lambda k: k.kod_v_rocniku()) # setřídit podle t1, t2, c3, ...
|
||||||
|
|
||||||
|
return hlavni_problemy
|
||||||
|
|
||||||
|
|
||||||
|
def podproblemy_v_cislu(cislo, problemy=None, hlavni_problemy=None):
|
||||||
|
""" Vrátí seznam všech problémů s body v daném čísle v poli 'indexovaném' tématy. """
|
||||||
|
if problemy is None:
|
||||||
|
problemy = problemy_cisla(cislo)
|
||||||
|
if hlavni_problemy is None:
|
||||||
|
hlavni_problemy = hlavni_problemy_cisla(cislo, problemy)
|
||||||
|
|
||||||
|
podproblemy = dict((hp.id, []) for hp in hlavni_problemy)
|
||||||
|
hlavni_problemy = set(hlavni_problemy)
|
||||||
|
podproblemy[-1] = []
|
||||||
|
|
||||||
|
for problem in problemy:
|
||||||
|
h_problem = hlavni_problem(problem)
|
||||||
|
if h_problem in hlavni_problemy:
|
||||||
|
podproblemy[h_problem.id].append(problem)
|
||||||
|
else:
|
||||||
|
podproblemy[-1].append(problem)
|
||||||
|
|
||||||
|
return podproblemy
|
||||||
|
|
|
@ -29,6 +29,7 @@ from seminar.forms import PrihlaskaForm, LoginForm, ProfileEditForm
|
||||||
import seminar.forms as f
|
import seminar.forms as f
|
||||||
import seminar.templatetags.treenodes as tnltt
|
import seminar.templatetags.treenodes as tnltt
|
||||||
import seminar.views.views_rest as vr
|
import seminar.views.views_rest as vr
|
||||||
|
from seminar.views.vysledkovka import vysledkovka_rocniku, vysledkovka_cisla
|
||||||
|
|
||||||
from datetime import timedelta, date, datetime, MAXYEAR
|
from datetime import timedelta, date, datetime, MAXYEAR
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
@ -48,7 +49,7 @@ import csv
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from seminar.utils import aktivniResitele, resi_v_rocniku
|
from seminar.utils import aktivniResitele, resi_v_rocniku, hlavni_problemy_rocniku, cisla_rocniku, hlavni_problemy_cisla
|
||||||
|
|
||||||
# ze starého modelu
|
# ze starého modelu
|
||||||
#def verejna_temata(rocnik):
|
#def verejna_temata(rocnik):
|
||||||
|
@ -631,266 +632,9 @@ class ArchivView(generic.ListView):
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
### Výsledky
|
|
||||||
|
|
||||||
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 cisla_rocniku(rocnik, jen_verejne=True):
|
|
||||||
"""
|
|
||||||
Vrátí všechna čísla daného ročníku.
|
|
||||||
Parametry:
|
|
||||||
rocnik (Rocnik): ročník semináře
|
|
||||||
jen_verejne (bool): zda se mají vrátit jen veřejná, nebo všechna čísla
|
|
||||||
Vrátí:
|
|
||||||
seznam objektů typu Cislo
|
|
||||||
"""
|
|
||||||
if jen_verejne:
|
|
||||||
return rocnik.verejna_cisla()
|
|
||||||
else:
|
|
||||||
return rocnik.cisla.all()
|
|
||||||
|
|
||||||
def hlavni_problem(problem):
|
|
||||||
""" Pro daný problém vrátí jeho nejvyšší nadproblém."""
|
|
||||||
while not(problem.nadproblem == None):
|
|
||||||
problem = problem.nadproblem
|
|
||||||
return problem
|
|
||||||
|
|
||||||
def hlavni_problemy_rocniku(rocnik, jen_verejne=True):
|
|
||||||
""" Pro zadaný ročník vrátí hlavní problémy ročníku,
|
|
||||||
tj. ty, které už nemají nadproblém."""
|
|
||||||
hlavni_problemy = []
|
|
||||||
for cislo in cisla_rocniku(rocnik, jen_verejne):
|
|
||||||
for problem in hlavni_problemy_cisla(cislo):
|
|
||||||
hlavni_problemy.append(problem)
|
|
||||||
hlavni_problemy_set = set(hlavni_problemy)
|
|
||||||
hlavni_problemy = list(hlavni_problemy_set)
|
|
||||||
hlavni_problemy.sort(key=lambda k:k.kod_v_rocniku()) # setřídit podle pořadí
|
|
||||||
|
|
||||||
return hlavni_problemy
|
|
||||||
|
|
||||||
|
|
||||||
def problemy_cisla(cislo):
|
|
||||||
""" Vrátí seznam všech problémů s body v daném čísle. """
|
|
||||||
hodnoceni = cislo.hodnoceni.select_related('problem', 'reseni').all()
|
|
||||||
# hodnocení, která se vážou k danému číslu
|
|
||||||
|
|
||||||
reseni = [h.reseni for h in hodnoceni]
|
|
||||||
problemy = [h.problem for h in hodnoceni]
|
|
||||||
problemy_set = set(problemy) # chceme každý problém unikátně,
|
|
||||||
problemy = (list(problemy_set)) # převedení na množinu a zpět to zaručí
|
|
||||||
|
|
||||||
return problemy
|
|
||||||
|
|
||||||
|
|
||||||
def hlavni_problemy_cisla(cislo, problemy=None):
|
|
||||||
""" Vrátí seznam všech problémů s body v daném čísle, které již nemají nadproblém. """
|
|
||||||
if problemy is None:
|
|
||||||
problemy = problemy_cisla(cislo)
|
|
||||||
|
|
||||||
# hlavní problémy čísla
|
|
||||||
# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém)
|
|
||||||
hlavni_problemy = []
|
|
||||||
for p in problemy:
|
|
||||||
hlavni_problemy.append(hlavni_problem(p))
|
|
||||||
|
|
||||||
# zunikátnění
|
|
||||||
hlavni_problemy_set = set(hlavni_problemy)
|
|
||||||
hlavni_problemy = list(hlavni_problemy_set)
|
|
||||||
hlavni_problemy.sort(key=lambda k: k.kod_v_rocniku()) # setřídit podle t1, t2, c3, ...
|
|
||||||
|
|
||||||
return hlavni_problemy
|
|
||||||
|
|
||||||
|
|
||||||
def podproblemy_v_cislu(cislo, problemy=None, hlavni_problemy=None):
|
|
||||||
""" Vrátí seznam všech problémů s body v daném čísle v poli 'indexovaném' tématy. """
|
|
||||||
if problemy is None:
|
|
||||||
problemy = problemy_cisla(cislo)
|
|
||||||
if hlavni_problemy is None:
|
|
||||||
hlavni_problemy = hlavni_problemy_cisla(cislo, problemy)
|
|
||||||
|
|
||||||
podproblemy = dict((hp.id, []) for hp in hlavni_problemy)
|
|
||||||
hlavni_problemy = set(hlavni_problemy)
|
|
||||||
podproblemy[-1] = []
|
|
||||||
|
|
||||||
for problem in problemy:
|
|
||||||
h_problem = hlavni_problem(problem)
|
|
||||||
if h_problem in hlavni_problemy:
|
|
||||||
podproblemy[h_problem.id].append(problem)
|
|
||||||
else:
|
|
||||||
podproblemy[-1].append(problem)
|
|
||||||
|
|
||||||
return podproblemy
|
|
||||||
|
|
||||||
def body_resitelu(resitele, za, odjakziva=True):
|
|
||||||
""" 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, Rocnik):
|
|
||||||
cislo = None
|
|
||||||
rocnik = za
|
|
||||||
rok = rocnik.prvni_rok
|
|
||||||
elif isinstance(za, 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ě.
|
|
||||||
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.
|
|
||||||
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 = 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]
|
|
||||||
setrizeni_resitele = [Resitel.objects.get(id=i) for i in setrizeni_resitele_id]
|
|
||||||
setrizene_body = [dvojice[1] for dvojice in slov_resitel_body]
|
|
||||||
return setrizeni_resitele_id, setrizeni_resitele, setrizene_body
|
|
||||||
|
|
||||||
def vysledkovka_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"
|
|
||||||
"""
|
|
||||||
|
|
||||||
## 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)
|
|
||||||
|
|
||||||
# setřídíme řešitele podle počtu bodů a získáme seznam s body od nejvyšších po nenižší
|
|
||||||
setrizeni_resitele_id, setrizeni_resitele, 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)
|
|
||||||
|
|
||||||
# vytvoříme jednotlivé sloupce výsledkovky
|
|
||||||
radky_vysledkovky = []
|
|
||||||
i = 0
|
|
||||||
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í
|
|
||||||
Resitel.objects.get(id=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
|
|
||||||
|
|
||||||
return radky_vysledkovky
|
|
||||||
|
|
||||||
|
|
||||||
class RocnikView(generic.DetailView):
|
class RocnikView(generic.DetailView):
|
||||||
model = Rocnik
|
model = Rocnik
|
||||||
|
@ -952,264 +696,6 @@ class ProblemView(generic.DetailView):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
if slovnik[resitel.id] == "":
|
|
||||||
slovnik[resitel.id] = 0
|
|
||||||
|
|
||||||
slovnik[resitel.id] += body
|
|
||||||
|
|
||||||
def secti_body_za_rocnik(za, aktivni_resitele):
|
|
||||||
""" Spočítá body za ročník (celý nebo do daného čísla),
|
|
||||||
setřídí je sestupně a vrátí jako seznam.
|
|
||||||
Parametry:
|
|
||||||
za (typu Rocnik nebo Cislo) spočítá za ročník, nebo za ročník až do
|
|
||||||
daného čísla
|
|
||||||
"""
|
|
||||||
# spočítáme všem řešitelům jejich body za ročník (False => ne odjakživa)
|
|
||||||
resitel_rocnikbody_slov = body_resitelu(aktivni_resitele, za, False)
|
|
||||||
# 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ů)
|
|
||||||
if hlavni_problemy is None:
|
|
||||||
hlavni_problemy = hlavni_problemy_cisla(cislo)
|
|
||||||
|
|
||||||
def ne_clanek_ne_konfera(problem):
|
|
||||||
return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera))
|
|
||||||
|
|
||||||
temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy))
|
|
||||||
|
|
||||||
def cosi(problem):
|
|
||||||
return problem.id
|
|
||||||
|
|
||||||
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] = ""
|
|
||||||
|
|
||||||
# vezmeme všechna řešení s body do daného čísla
|
|
||||||
reseni_do_cisla = Reseni.objects.prefetch_related('problem', 'resitele',
|
|
||||||
'hodnoceni_set').filter(hodnoceni__cislo_body=cislo)
|
|
||||||
|
|
||||||
# projdeme všechna řešení do čísla a přičteme body každému řešiteli do celkových
|
|
||||||
# bodů i do bodů za problém
|
|
||||||
for reseni in reseni_do_cisla:
|
|
||||||
|
|
||||||
# řešení může řešit více problémů
|
|
||||||
for prob in list(reseni.problem.all()):
|
|
||||||
nadproblem = hlavni_problem(prob)
|
|
||||||
if ne_clanek_ne_konfera(nadproblem):
|
|
||||||
nadproblem_slovnik = hlavni_problemy_slovnik[nadproblem.id]
|
|
||||||
else:
|
|
||||||
nadproblem_slovnik = hlavni_problemy_slovnik[-1]
|
|
||||||
|
|
||||||
# a mít více hodnocení
|
|
||||||
for hodn in list(reseni.hodnoceni_set.all()):
|
|
||||||
body = hodn.body
|
|
||||||
|
|
||||||
# a mít více řešitelů
|
|
||||||
for resitel in list(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)
|
|
||||||
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_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)
|
|
||||||
|
|
||||||
# vezmeme všechna řešení s body do daného čísla
|
|
||||||
reseni_do_cisla = Reseni.objects.prefetch_related('problem', 'resitele',
|
|
||||||
'hodnoceni_set').filter(hodnoceni__cislo_body=cislo)
|
|
||||||
|
|
||||||
# projdeme všechna řešení do čísla a přičteme body každému řešiteli do celkových
|
|
||||||
# bodů i do bodů za problém
|
|
||||||
for reseni in reseni_do_cisla:
|
|
||||||
|
|
||||||
# řešení může řešit více problémů
|
|
||||||
for prob in list(reseni.problem.all()):
|
|
||||||
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]
|
|
||||||
|
|
||||||
# a mít více hodnocení
|
|
||||||
for hodn in list(reseni.hodnoceni_set.all()):
|
|
||||||
body = hodn.body
|
|
||||||
|
|
||||||
# a mít více řešitelů
|
|
||||||
for resitel in list(reseni.resitele.all()):
|
|
||||||
if resitel not in aktivni_resitele:
|
|
||||||
print("Skipping {}".format(resitel.id))
|
|
||||||
continue
|
|
||||||
pricti_body(problem_slovnik, resitel, body)
|
|
||||||
return body_slovnik
|
|
||||||
|
|
||||||
|
|
||||||
# TODELETE
|
|
||||||
class FixedIterator:
|
|
||||||
def next(self):
|
|
||||||
return self.niter.__next__()
|
|
||||||
|
|
||||||
def __init__(self, niter):
|
|
||||||
self.niter = niter
|
|
||||||
# TODELETE
|
|
||||||
|
|
||||||
|
|
||||||
def vysledkovka_cisla(cislo, context=None):
|
|
||||||
if context is None:
|
|
||||||
context = {}
|
|
||||||
problemy = problemy_cisla(cislo)
|
|
||||||
hlavni_problemy = hlavni_problemy_cisla(cislo, 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(aktivniResitele(cislo))
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# získáme body odjakživa
|
|
||||||
resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, cislo)
|
|
||||||
|
|
||||||
# řešitelé setřídění podle bodů za číslo sestupně
|
|
||||||
setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn]
|
|
||||||
setrizeni_resitele = [Resitel.objects.get(id=i) for i in setrizeni_resitele_id]
|
|
||||||
|
|
||||||
# spočítáme pořadí řešitelů
|
|
||||||
setrizeni_resitele_body = [dvojice[1] for dvojice in resitel_rocnikbody_sezn]
|
|
||||||
poradi = sloupec_s_poradim(setrizeni_resitele_body)
|
|
||||||
|
|
||||||
# vytvoříme jednotlivé sloupce výsledkovky
|
|
||||||
radky_vysledkovky = []
|
|
||||||
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))
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
for ar_id in setrizeni_resitele_id:
|
|
||||||
# získáme seznam bodů za problémy pro daného řešitele
|
|
||||||
body_problemy = []
|
|
||||||
body_podproblemy = []
|
|
||||||
for hp in temata_a_spol:
|
|
||||||
body_problemy.append(hlavni_problemy_slovnik[hp.id][ar_id])
|
|
||||||
body_podproblemy.append([problemy_slovnik[hp.id][it.id][ar_id] for it in podproblemy[hp.id]])
|
|
||||||
if je_nejake_ostatni:
|
|
||||||
body_problemy.append(hlavni_problemy_slovnik[-1][ar_id])
|
|
||||||
body_podproblemy.append([problemy_slovnik[-1][it.id][ar_id] for it in podproblemy[-1]])
|
|
||||||
# vytáhneme informace pro daného řešitele
|
|
||||||
radek = RadekVysledkovkyCisla(
|
|
||||||
poradi[i], # pořadí
|
|
||||||
Resitel.objects.get(id=ar_id), # řešitel (z id)
|
|
||||||
body_problemy, # seznam bodů za hlavní problémy čísla
|
|
||||||
cislobody[ar_id], # body za číslo
|
|
||||||
setrizeni_resitele_body[i], # body za ročník (spočítané výše s pořadím)
|
|
||||||
resitel_odjakzivabody_slov[ar_id], # body odjakživa
|
|
||||||
cislo.rocnik,
|
|
||||||
body_podproblemy, # body všech podproblémů
|
|
||||||
FixedIterator(body_podproblemy.__iter__()) # TODELETE
|
|
||||||
) # ročník semináře pro zjištění ročníku řešitele
|
|
||||||
radky_vysledkovky.append(radek)
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
# vytahané informace předáváme do kontextu
|
|
||||||
context['cislo'] = cislo
|
|
||||||
context['radky_vysledkovky'] = radky_vysledkovky
|
|
||||||
context['problemy'] = temata_a_spol
|
|
||||||
context['ostatni'] = je_nejake_ostatni
|
|
||||||
pt = [podproblemy[it.id] for it in temata_a_spol]+[podproblemy[-1]]
|
|
||||||
context['podproblemy'] = pt
|
|
||||||
context['podproblemy_iter'] = FixedIterator(pt.__iter__()) # TODELETE
|
|
||||||
#context['v_cisle_zadane'] = TODO
|
|
||||||
#context['resene_problemy'] = resene_problemy
|
|
||||||
return context
|
|
||||||
|
|
||||||
class CisloView(generic.DetailView):
|
class CisloView(generic.DetailView):
|
||||||
# FIXME zobrazování témátek a vůbec, teď je tam jen odkaz na číslo v pdf
|
# FIXME zobrazování témátek a vůbec, teď je tam jen odkaz na číslo v pdf
|
||||||
|
|
437
seminar/views/vysledkovka.py
Normal file
437
seminar/views/vysledkovka.py
Normal file
|
@ -0,0 +1,437 @@
|
||||||
|
import seminar.models as m
|
||||||
|
from django.db.models import Q, Sum, Count
|
||||||
|
from seminar.utils import aktivniResitele, resi_v_rocniku, cisla_rocniku, hlavni_problemy_rocniku, hlavni_problem, hlavni_problemy_cisla, problemy_cisla, podproblemy_v_cislu
|
||||||
|
### Výsledky
|
||||||
|
|
||||||
|
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):
|
||||||
|
""" 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ě.
|
||||||
|
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.
|
||||||
|
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]
|
||||||
|
setrizeni_resitele = [m.Resitel.objects.get(id=i) for i in setrizeni_resitele_id]
|
||||||
|
setrizene_body = [dvojice[1] for dvojice in slov_resitel_body]
|
||||||
|
return setrizeni_resitele_id, setrizeni_resitele, setrizene_body
|
||||||
|
|
||||||
|
def vysledkovka_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"
|
||||||
|
"""
|
||||||
|
|
||||||
|
## 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)
|
||||||
|
|
||||||
|
# setřídíme řešitele podle počtu bodů a získáme seznam s body od nejvyšších po nenižší
|
||||||
|
setrizeni_resitele_id, setrizeni_resitele, 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)
|
||||||
|
|
||||||
|
# vytvoříme jednotlivé sloupce výsledkovky
|
||||||
|
radky_vysledkovky = []
|
||||||
|
i = 0
|
||||||
|
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í
|
||||||
|
m.Resitel.objects.get(id=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
|
||||||
|
|
||||||
|
return radky_vysledkovky
|
||||||
|
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
|
||||||
|
if slovnik[resitel.id] == "":
|
||||||
|
slovnik[resitel.id] = 0
|
||||||
|
|
||||||
|
slovnik[resitel.id] += body
|
||||||
|
|
||||||
|
def secti_body_za_rocnik(za, aktivni_resitele):
|
||||||
|
""" Spočítá body za ročník (celý nebo do daného čísla),
|
||||||
|
setřídí je sestupně a vrátí jako seznam.
|
||||||
|
Parametry:
|
||||||
|
za (typu Rocnik nebo Cislo) spočítá za ročník, nebo za ročník až do
|
||||||
|
daného čísla
|
||||||
|
"""
|
||||||
|
# spočítáme všem řešitelům jejich body za ročník (False => ne odjakživa)
|
||||||
|
resitel_rocnikbody_slov = body_resitelu(aktivni_resitele, za, False)
|
||||||
|
# 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ů)
|
||||||
|
if hlavni_problemy is None:
|
||||||
|
hlavni_problemy = hlavni_problemy_cisla(cislo)
|
||||||
|
|
||||||
|
def ne_clanek_ne_konfera(problem):
|
||||||
|
return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera))
|
||||||
|
|
||||||
|
temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy))
|
||||||
|
|
||||||
|
def cosi(problem):
|
||||||
|
return problem.id
|
||||||
|
|
||||||
|
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] = ""
|
||||||
|
|
||||||
|
# vezmeme všechna řešení s body do daného čísla
|
||||||
|
reseni_do_cisla = m.Reseni.objects.prefetch_related('problem', 'resitele',
|
||||||
|
'hodnoceni_set').filter(hodnoceni__cislo_body=cislo)
|
||||||
|
|
||||||
|
# projdeme všechna řešení do čísla a přičteme body každému řešiteli do celkových
|
||||||
|
# bodů i do bodů za problém
|
||||||
|
for reseni in reseni_do_cisla:
|
||||||
|
|
||||||
|
# řešení může řešit více problémů
|
||||||
|
for prob in list(reseni.problem.all()):
|
||||||
|
nadproblem = hlavni_problem(prob)
|
||||||
|
if ne_clanek_ne_konfera(nadproblem):
|
||||||
|
nadproblem_slovnik = hlavni_problemy_slovnik[nadproblem.id]
|
||||||
|
else:
|
||||||
|
nadproblem_slovnik = hlavni_problemy_slovnik[-1]
|
||||||
|
|
||||||
|
# a mít více hodnocení
|
||||||
|
for hodn in list(reseni.hodnoceni_set.all()):
|
||||||
|
body = hodn.body
|
||||||
|
|
||||||
|
# a mít více řešitelů
|
||||||
|
for resitel in list(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)
|
||||||
|
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_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)
|
||||||
|
|
||||||
|
# vezmeme všechna řešení s body do daného čísla
|
||||||
|
reseni_do_cisla = m.Reseni.objects.prefetch_related('problem', 'resitele',
|
||||||
|
'hodnoceni_set').filter(hodnoceni__cislo_body=cislo)
|
||||||
|
|
||||||
|
# projdeme všechna řešení do čísla a přičteme body každému řešiteli do celkových
|
||||||
|
# bodů i do bodů za problém
|
||||||
|
for reseni in reseni_do_cisla:
|
||||||
|
|
||||||
|
# řešení může řešit více problémů
|
||||||
|
for prob in reseni.problem.all():
|
||||||
|
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]
|
||||||
|
|
||||||
|
# a mít více hodnocení
|
||||||
|
for hodn in reseni.hodnoceni_set.all():
|
||||||
|
body = hodn.body
|
||||||
|
|
||||||
|
# a mít více řešitelů
|
||||||
|
for resitel in reseni.resitele.all():
|
||||||
|
if resitel not in aktivni_resitele:
|
||||||
|
print("Skipping {}".format(resitel.id))
|
||||||
|
continue
|
||||||
|
pricti_body(problem_slovnik, resitel, body)
|
||||||
|
return body_slovnik
|
||||||
|
|
||||||
|
|
||||||
|
# TODELETE
|
||||||
|
class FixedIterator:
|
||||||
|
def next(self):
|
||||||
|
return self.niter.__next__()
|
||||||
|
|
||||||
|
def __init__(self, niter):
|
||||||
|
self.niter = niter
|
||||||
|
# TODELETE
|
||||||
|
|
||||||
|
|
||||||
|
def vysledkovka_cisla(cislo, context=None):
|
||||||
|
if context is None:
|
||||||
|
context = {}
|
||||||
|
problemy = problemy_cisla(cislo)
|
||||||
|
hlavni_problemy = hlavni_problemy_cisla(cislo, 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(aktivniResitele(cislo))
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# získáme body odjakživa
|
||||||
|
resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, cislo)
|
||||||
|
|
||||||
|
# řešitelé setřídění podle bodů za číslo sestupně
|
||||||
|
setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn]
|
||||||
|
setrizeni_resitele = [m.Resitel.objects.get(id=i) for i in setrizeni_resitele_id]
|
||||||
|
|
||||||
|
# spočítáme pořadí řešitelů
|
||||||
|
setrizeni_resitele_body = [dvojice[1] for dvojice in resitel_rocnikbody_sezn]
|
||||||
|
poradi = sloupec_s_poradim(setrizeni_resitele_body)
|
||||||
|
|
||||||
|
# vytvoříme jednotlivé sloupce výsledkovky
|
||||||
|
radky_vysledkovky = []
|
||||||
|
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))
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
for ar_id in setrizeni_resitele_id:
|
||||||
|
# získáme seznam bodů za problémy pro daného řešitele
|
||||||
|
body_problemy = []
|
||||||
|
body_podproblemy = []
|
||||||
|
for hp in temata_a_spol:
|
||||||
|
body_problemy.append(hlavni_problemy_slovnik[hp.id][ar_id])
|
||||||
|
body_podproblemy.append([problemy_slovnik[hp.id][it.id][ar_id] for it in podproblemy[hp.id]])
|
||||||
|
if je_nejake_ostatni:
|
||||||
|
body_problemy.append(hlavni_problemy_slovnik[-1][ar_id])
|
||||||
|
body_podproblemy.append([problemy_slovnik[-1][it.id][ar_id] for it in podproblemy[-1]])
|
||||||
|
# vytáhneme informace pro daného řešitele
|
||||||
|
radek = RadekVysledkovkyCisla(
|
||||||
|
poradi[i], # pořadí
|
||||||
|
m.Resitel.objects.get(id=ar_id), # řešitel (z id)
|
||||||
|
body_problemy, # seznam bodů za hlavní problémy čísla
|
||||||
|
cislobody[ar_id], # body za číslo
|
||||||
|
setrizeni_resitele_body[i], # body za ročník (spočítané výše s pořadím)
|
||||||
|
resitel_odjakzivabody_slov[ar_id], # body odjakživa
|
||||||
|
cislo.rocnik,
|
||||||
|
body_podproblemy, # body všech podproblémů
|
||||||
|
FixedIterator(body_podproblemy.__iter__()) # TODELETE
|
||||||
|
) # ročník semináře pro zjištění ročníku řešitele
|
||||||
|
radky_vysledkovky.append(radek)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
# vytahané informace předáváme do kontextu
|
||||||
|
context['cislo'] = cislo
|
||||||
|
context['radky_vysledkovky'] = radky_vysledkovky
|
||||||
|
context['problemy'] = temata_a_spol
|
||||||
|
context['ostatni'] = je_nejake_ostatni
|
||||||
|
pt = [podproblemy[it.id] for it in temata_a_spol]+[podproblemy[-1]]
|
||||||
|
context['podproblemy'] = pt
|
||||||
|
context['podproblemy_iter'] = FixedIterator(pt.__iter__()) # TODELETE
|
||||||
|
#context['v_cisle_zadane'] = TODO
|
||||||
|
#context['resene_problemy'] = resene_problemy
|
||||||
|
return context
|
Loading…
Reference in a new issue