Browse Source

Merge branch 'data_migrations' into treenode_editor

export_seznamu_prednasek
parent
commit
b977a44344
  1. 4
      galerie/models.py
  2. 20
      mamweb/static/css/mamweb.css
  3. 2
      mamweb/templates/graph.svg
  4. 207
      seminar/testutils.py
  5. 189
      seminar/views/views_all.py

4
galerie/models.py

@ -39,8 +39,8 @@ def obrazek_filename(self, filename):
# soustředění je v cestě jen pokud galerie pod nějaké patří # soustředění je v cestě jen pokud galerie pod nějaké patří
cesta = ( cesta = (
['Galerie'] + ['Galerie'] +
(["soustredeni_" + gal.soustredeni.pk] if gal.soustredeni else []) + (["soustredeni_{}".format(gal.soustredeni.pk)] if gal.soustredeni else []) +
["galerie_" + cislo_gal, self.nazev] ["galerie_{}".format(cislo_gal), self.nazev]
) )
return os.path.join(*cesta) return os.path.join(*cesta)

20
mamweb/static/css/mamweb.css

@ -242,6 +242,10 @@ nav.nav-button {
display: none; display: none;
} }
div.dropdown-backdrop { /* tohle způsobuje, že funguje mobilní menu */
z-index: -1;
}
h1 a:hover { h1 a:hover {
text-decoration: none; text-decoration: none;
} }
@ -387,11 +391,16 @@ p.license-mobile {
} }
div.graf{ div.graf{
width: 70%;
float: none; float: none;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
width: 70%; margin-top: 10px;
} }
#svg-graf{
width: 100%;
height: auto;;
}
ul.menu { ul.menu {
font-size: 90%; font-size: 90%;
@ -575,12 +584,9 @@ ul.submenu {
float: none; float: none;
} }
div.graf{ div.graf {
float: none; width: 100%;
width: 70%; }
margin-left: auto;
margin-right: auto;
}
} }

2
mamweb/templates/graph.svg

@ -10,7 +10,7 @@
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1" version="1.1"
id="svg2" id="svg-graf"
width="482.57019" width="482.57019"
height="599.45636" height="599.45636"
viewBox="0 0 482.57019 599.45636" viewBox="0 0 482.57019 599.45636"

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

207
seminar/testutils.py

@ -173,13 +173,12 @@ def gen_organizatori(rnd, osoby, last_rocnik, users):
if do.year > datetime.datetime.now().year: if do.year > datetime.datetime.now().year:
do = None do = None
organizatori.append(Organizator.objects.create(osoba=os, organizatori.append(Organizator.objects.create(osoba=os,
organizuje_od=od, organizuje_do=do, strucny_popis_organizatora = popis_orga)) organizuje_od=od, organizuje_do=do, strucny_popis_organizatora = popis_orga))
return organizatori return organizatori
def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size): def gen_zadani_ulohy(rnd, cisla, organizatori, pocet_oboru, poradi_cisla, poradi_problemu):
logger.info('Generuji úlohy do čísla (size={})...'.format(size))
# Proměnné pro náhodné generování názvů a zadání.
# ulohy resene v cisle
jaka = ["Šachová", "Černá", "Větrná", "Dlouhá", "Křehká", "Rychlá", jaka = ["Šachová", "Černá", "Větrná", "Dlouhá", "Křehká", "Rychlá",
"Zákeřná", "Fyzikální"] "Zákeřná", "Fyzikální"]
co = ["kostka", "smršť", "díra", "zrada", "toulka", "tyč", co = ["kostka", "smršť", "díra", "zrada", "toulka", "tyč",
@ -189,7 +188,47 @@ def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size)
ceho = ["všech", "správných", "konstatních", "zelených"] ceho = ["všech", "správných", "konstatních", "zelených"]
jmeno = ["řešení", "tahů", "čísel", "kalhot", "koulí", "hadů"] jmeno = ["řešení", "tahů", "čísel", "kalhot", "koulí", "hadů"]
kde = ["na zemi", "ve vesmíru", "ve vzduchu", "na šňůře", "v letadle"] kde = ["na zemi", "ve vesmíru", "ve vzduchu", "na šňůře", "v letadle"]
obor = ["M", "F", "I", "O", "B"] obory = ["M", "F", "I", "O", "B"]
p = Uloha.objects.create(
# atributy třídy Problem
nazev=" ".join([rnd.choice(jaka), rnd.choice(co)]),
stav=Problem.STAV_ZADANY,
zamereni=rnd.sample(obory, pocet_oboru),
autor=rnd.choice(organizatori),
garant=rnd.choice(organizatori),
kod=str(poradi_problemu),
# atributy třídy Uloha
cislo_zadani=cisla[poradi_cisla-2-1],
cislo_reseni=cisla[poradi_cisla-1],
cislo_deadline=cisla[poradi_cisla-1],
max_body = rnd.randint(1, 8)
)
text_zadani = Text.objects.create(
na_web = " ".join(
[rnd.choice(sloveso),
rnd.choice(koho),
rnd.choice(ceho),
rnd.choice(jmeno),
rnd.choice(kde)]
),
do_cisla = " ".join(
[rnd.choice(sloveso),
rnd.choice(koho),
rnd.choice(ceho),
rnd.choice(jmeno),
rnd.choice(kde)]
)
)
zad = TextNode.objects.create(text = text_zadani)
uloha_zadani = UlohaZadaniNode.objects.create(uloha = p, first_child = zad)
p.ulohazadaninode = uloha_zadani
otec_syn(cisla[poradi_cisla-2-1].cislonode, uloha_zadani)
return p
def gen_vzoroveho_reseni_ulohy(rnd, cisla, organizatori, uloha, pocet_opravovatelu, poradi_cisla):
reseni = ["to je přece jasné", "triviální", "omlouváme se," reseni = ["to je přece jasné", "triviální", "omlouváme se,"
"otevřený problém", "neřešitelné", "triviálně triviální", "otevřený problém", "neřešitelné", "triviálně triviální",
"použitím věty z prvního semestru na matfyzu", "použitím věty z prvního semestru na matfyzu",
@ -197,96 +236,81 @@ def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size)
"netriviální aplikace diferenciálních rovnic", "zadání je vnitřně" "netriviální aplikace diferenciálních rovnic", "zadání je vnitřně"
"sporné", "nepopsatelně jednoduché", "pokud jste na to nepřišli," "sporné", "nepopsatelně jednoduché", "pokud jste na to nepřišli,"
"tak jste fakt hloupí"] "tak jste fakt hloupí"]
# Generování vzorového řešení.
text_vzoraku = Text.objects.create(
na_web = rnd.choice(reseni),
do_cisla = rnd.choice(reseni)
)
vzorak = TextNode.objects.create(text = text_vzoraku)
uloha_vzorak = UlohaVzorakNode.objects.create(uloha = uloha, first_child = vzorak)
uloha.ulohavzoraknode = uloha_vzorak
otec_syn(cisla[poradi_cisla-1].cislonode, uloha_vzorak)
uloha.opravovatele.set(rnd.sample(organizatori, pocet_opravovatelu))
uloha.save()
return
def gen_reseni_ulohy(rnd, cisla, uloha, pocet_resitelu, poradi_cisla, resitele_cisla, resitele):
pocet_reseni = rnd.randint(pocet_resitelu//4, pocet_resitelu * 4)
# generujeme náhodný počet řešení vzhledem k počtu řešitelů čísla
for _ in range(pocet_reseni):
#print("Generuji {}-té řešení".format(reseni))
if rnd.randint(1, 10) == 1:
# cca desetina řešení od více řešitelů
res_vyber = rnd.sample(resitele_cisla,
rnd.randint(2, 5))
else:
res_vyber = rnd.sample(resitele_cisla, 1)
if resitele[0] in res_vyber: # speciální řešitel, který nemá žádné body
res_vyber.remove(resitele[0])
# Vytvoření řešení.
res = Reseni.objects.create(forma=rnd.choice(Reseni.FORMA_CHOICES)[0])
# Problím a řešitele přiřadíme později, ManyToManyField
# se nedá vyplnit v create().
res.resitele.set(res_vyber)
res.save()
# Vytvoření hodnocení.
hod = Hodnoceni.objects.create(
body=rnd.randint(0, uloha.max_body),
cislo_body=cisla[poradi_cisla - 1],
reseni=res,
problem=uloha
)
return
def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size):
logger.info('Generuji úlohy do čísla (size={})...'.format(size))
k = 0 k = 0
for rocnik in rocniky: for rocnik in rocniky:
k+=1 k += 1
print("Generuji {}. číslo.".format(k)) print("Generuji {}. číslo.".format(k))
cisla = rocnik_cisla[k-1] cisla = rocnik_cisla[k - 1]
for ci in range(3, len(cisla)+1): # pro všechna čísla for ci in range(3, len(cisla) + 1): # pro všechna čísla
resitele_size = round(7/8 * 30 * size) # očekáváný celkový počet řešitelů resitele_size = round(7/8 * 30 * size) # očekáváný celkový počet řešitelů
poc_res = rnd.randint(round(resitele_size/8), round(resitele_size/4)) poc_res = rnd.randint(resitele_size//8, resitele_size//4)
# dané číslo řeší něco mezi osminou a čtvrtinou všech řešitelů # dané číslo řeší něco mezi osminou a čtvrtinou všech řešitelů
# (náhodná hausnumera, možno změnit) # (náhodná hausnumera, možno změnit)
# účelem je, aby se řešení generovala z menší množiny řešitelů a tedy # účelem je, aby se řešení generovala z menší množiny řešitelů a tedy
# bylo více řešení od jednoho řešitele daného čísla # bylo více řešení od jednoho řešitele daného čísla
resitele_cisla = rnd.sample(resitele, poc_res) resitele_cisla = rnd.sample(resitele, poc_res)
for pi in range(1, ((size + 1) // 2) + 1): for pi in range(1, ((size + 1) // 2) + 1): # počet problémů
poc_op = rnd.randint(1, 4) # počet opravovatelů poc_op = rnd.randint(1, 4) # počet opravovatelů
poc_oboru = rnd.randint(1, 2) poc_oboru = rnd.randint(1, 2)
p = Uloha.objects.create(
# atributy třídy Problem # Generování zadání úlohy a UlohaZadaniNode,
nazev=" ".join([rnd.choice(jaka), rnd.choice(co)]), # přivěšení pod dané číslo
stav=Problem.STAV_ZADANY, p = gen_zadani_ulohy(rnd, cisla, organizatori, poc_oboru, ci, pi)
zamereni=rnd.sample(["M", "F", "I", "O", "B"], poc_oboru), # Generování vzorového řešení
autor=rnd.choice(organizatori), gen_vzoroveho_reseni_ulohy(rnd, cisla, organizatori, p, poc_op, ci)
garant=rnd.choice(organizatori), # Generování řešení a hodnocení k úloze
kod=str(pi), gen_reseni_ulohy(rnd, cisla, p, poc_res, ci,
# atributy třídy Uloha resitele_cisla, resitele)
cislo_zadani=cisla[ci-2-1],
cislo_reseni=cisla[ci-1],
cislo_deadline=cisla[ci-1],
max_body = rnd.randint(1, 8)
)
text_zadani = Text.objects.create(
na_web = " ".join(
[rnd.choice(sloveso),
rnd.choice(koho),
rnd.choice(ceho),
rnd.choice(jmeno),
rnd.choice(kde)]
),
do_cisla = " ".join(
[rnd.choice(sloveso),
rnd.choice(koho),
rnd.choice(ceho),
rnd.choice(jmeno),
rnd.choice(kde)]
)
)
zad = TextNode.objects.create(text = text_zadani)
uloha_zadani = UlohaZadaniNode.objects.create(uloha=p, first_child = zad)
p.ulohazadaninode = uloha_zadani
otec_syn(cisla[ci-2-1].cislonode, uloha_zadani)
# generování vzorového textu
text_vzoraku = Text.objects.create(
na_web = rnd.choice(reseni),
do_cisla = rnd.choice(reseni)
)
vzorak = TextNode.objects.create(text = text_vzoraku)
uloha_vzorak = UlohaVzorakNode.objects.create(uloha=p, first_child = vzorak)
p.ulohavzoraknode = uloha_vzorak
otec_syn(cisla[ci-1].cislonode, uloha_vzorak)
p.opravovatele.set(rnd.sample(organizatori,poc_op))
p.save()
# generování řešení
poc_reseni = rnd.randint(poc_res, poc_res * 4)
# generujeme náhodný počet řešení vzhledem k počtu řešitelů čísla
for ri in range(poc_reseni):
#print("Generuji {}-té řešení".format(ri))
if rnd.randint(1, 10) == 6:
# cca desetina řešení od více řešitelů
res_vyber = rnd.sample(resitele_cisla,
rnd.randint(2, 5))
else:
res_vyber = rnd.sample(resitele_cisla, 1)
if resitele[0] in res_vyber:
res_vyber.remove(resitele[0])
res = Reseni.objects.create(forma=rnd.choice(Reseni.FORMA_CHOICES)[0])
# problem a resitele přiřadíme později, ManyToManyField
# se nedá vyplnit v create()
res.resitele.set(res_vyber)
res.save()
hod = Hodnoceni.objects.create(
body=rnd.randint(0, p.max_body),
cislo_body=cisla[ci-1],
reseni=res,
problem=p
)
return return
@ -482,8 +506,10 @@ def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori)
rocnik=rocnik, rocnik=rocnik,
poradi=str(int(cislo.poradi) + 2), poradi=str(int(cislo.poradi) + 2),
) )
# Pokud není číslo pro vzorák, tak se dá do posledního čísla (i kdyby tam mělo být zadání i řešení...) # Pokud není číslo pro vzorák, tak se dá do posledního čísla
# Tohle sice umožňuje vygenerovat vzorák do čísla dávno po konci témátka, ale to nám pro jednoduchost nevadí. # (i kdyby tam mělo být zadání i řešení...)
# Tohle sice umožňuje vygenerovat vzorák do čísla dávno po konci témátka,
# ale to nám pro jednoduchost nevadí.
if len(cislo_se_vzorakem) == 0: if len(cislo_se_vzorakem) == 0:
cislo_se_vzorakem = cisla[-1] cislo_se_vzorakem = cisla[-1]
else: else:
@ -505,7 +531,8 @@ def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori)
tema = tema_node.tema tema = tema_node.tema
# Pokud už témátko skončilo, žádné úložky negenerujeme # Pokud už témátko skončilo, žádné úložky negenerujeme
# FIXME: Bylo by hezčí, kdyby se čísla předávala jako Cislo a ne jako int v té trojici (start, konec, tema) # FIXME: Bylo by hezčí, kdyby se čísla předávala jako Cislo a ne
# jako int v té trojici (start, konec, tema)
if not temata[int(tema.kod)-1][1] >= int(cislo_se_vzorakem.poradi): if not temata[int(tema.kod)-1][1] >= int(cislo_se_vzorakem.poradi):
continue continue
@ -581,8 +608,10 @@ def gen_novinky(rnd, organizatori):
kdy = ["Zítra bude", "10. 10. 2020 bude", "V prosinci bude", "V létě bude"] kdy = ["Zítra bude", "10. 10. 2020 bude", "V prosinci bude", "V létě bude"]
for i in range(5): for i in range(5):
text_novinky = " ".join([rnd.choice(kdy),rnd.choice(kde),rnd.choice(jake),rnd.choice(co)]) text_novinky = " ".join([rnd.choice(kdy), rnd.choice(kde), rnd.choice(jake),
novinka = Novinky.objects.create(id=i,autor=rnd.choice(organizatori),text=(text_novinky+", těšíme se na vás!"),zverejneno=rnd.choice([True,False])) rnd.choice(co)])
novinka = Novinky.objects.create(id=i,autor=rnd.choice(organizatori),
text=(text_novinky+", těšíme se na vás!"),zverejneno=rnd.choice([True,False]))
novinka.save() novinka.save()
return return

189
seminar/views/views_all.py

@ -387,10 +387,16 @@ class ArchivView(generic.ListView):
### Výsledky ### Výsledky
# 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
def sloupec_s_poradim(setrizene_body): 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 # ze seznamu obsahujícího setřízené body spočítáme sloupec s pořadím
aktualni_poradi = 1 aktualni_poradi = 1
@ -421,20 +427,28 @@ def sloupec_s_poradim(setrizene_body):
aktualni_poradi = aktualni_poradi + velikost_skupiny aktualni_poradi = aktualni_poradi + velikost_skupiny
return sloupec_s_poradim return sloupec_s_poradim
# vrátí všechna čísla daného ročníku
def cisla_rocniku(rocnik, jen_verejne=True): 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: if jen_verejne:
return rocnik.verejna_cisla() return rocnik.verejna_cisla()
else: else:
return rocnik.cisla.all() return rocnik.cisla.all()
# pro daný problém vrátí jeho nejvyšší nadproblém
def hlavni_problem(problem): def hlavni_problem(problem):
""" Pro daný problém vrátí jeho nejvyšší nadproblém."""
while not(problem.nadproblem == None): while not(problem.nadproblem == None):
problem = problem.nadproblem problem = problem.nadproblem
return problem return problem
def hlavni_problemy_rocniku(rocnik, jen_verejne=True): def hlavni_problemy_rocniku(rocnik, jen_verejne=True):
""" Pro zadaný ročník vrátí hlavní problémy ročníku,
tj. ty, které nemají nadproblém."""
hlavni_problemy = [] hlavni_problemy = []
for cislo in cisla_rocniku(rocnik, jen_verejne): for cislo in cisla_rocniku(rocnik, jen_verejne):
for problem in hlavni_problemy_cisla(cislo): for problem in hlavni_problemy_cisla(cislo):
@ -445,8 +459,8 @@ def hlavni_problemy_rocniku(rocnik, jen_verejne=True):
return hlavni_problemy return hlavni_problemy
# vrátí list všech problémů s body v daném čísle, které již nemají nadproblém
def hlavni_problemy_cisla(cislo): def hlavni_problemy_cisla(cislo):
""" Vrátí seznam všech problémů s body v daném čísle, které již nemají nadproblém. """
hodnoceni = cislo.hodnoceni.select_related('problem', 'reseni').all() hodnoceni = cislo.hodnoceni.select_related('problem', 'reseni').all()
# hodnocení, která se vážou k danému číslu # hodnocení, která se vážou k danému číslu
@ -468,36 +482,71 @@ def hlavni_problemy_cisla(cislo):
return hlavni_problemy return hlavni_problemy
# vrátí slovník řešitel:body obsahující počty bodů zadaných řešitelů za daný ročník def body_resitelu(resitele, za, odjakziva=True):
def body_resitelu_odjakziva(rocnik, resitele): """ 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=cislo) ))
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ů # Následující řádek přidá ke každému řešiteli údaj ".body" se součtem jejich bodů
resitele_s_body = Resitel.objects.annotate(body=Sum('reseni__hodnoceni__body')) resitele_s_body = Resitel.objects.filter(id__in=resitele_id).annotate(
# Teď jen z QuerySetu řešitelů anotovaných body vygenerujeme slovník indexovaný řešitelským id obsahující body body=body_k_zapocteni)
# ... ale jen ro řešitele, které dostaneme jako parametr. # Teď jen z QuerySetu řešitelů anotovaných body vygenerujeme slovník
# TODO: Zjistit, co ten parametr říká a proč je potřeba # indexovaný řešitelským id obsahující body.
body_odjakziva = {int(res.id) : res.body for res in resitele_s_body if res in resitele} # Pokud jsou body None, nahradíme za 0.
return body_odjakziva slovnik = {int(res.id) : (res.body if res.body else 0) for res in resitele_s_body}
return slovnik
# vrátí slovník řešitel:body obsahující počty bodů zadaných řešitelů za daný ročník
def body_resitelu_za_rocnik(rocnik, aktivni_resitele):
body_za_rocnik = {}
# inicializujeme na 0 pro všechny aktivní řešitele
for ar in aktivni_resitele:
body_za_rocnik[ar.id] = 0
# spočítáme body řešitelům přes všechna řešení s hodnocením v daném ročníku
print("Před dotazem:{}".format(time.time()))
reseni = Reseni.objects.prefetch_related('resitele', 'hodnoceni_set').filter(hodnoceni__cislo_body__rocnik=rocnik)
print("Po dotazu:{}".format(time.time()))
for res in reseni:
for resitel in res.resitele.all():
for hodn in res.hodnoceni_set.all():
pricti_body(body_za_rocnik, resitel, hodn.body)
print("Po for-cyklu:{}".format(time.time()))
return body_za_rocnik
class RadekVysledkovkyRocniku(object): class RadekVysledkovkyRocniku(object):
"""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_sezn, body_rocnik, body_odjakziva, rok):
@ -510,7 +559,7 @@ class RadekVysledkovkyRocniku(object):
self.titul = resitel.get_titul(body_odjakziva) self.titul = resitel.get_titul(body_odjakziva)
def vysledkovka_rocniku(rocnik, jen_verejne=True): def vysledkovka_rocniku(rocnik, jen_verejne=True):
"""Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve """ Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve
formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html" formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html"
""" """
@ -523,7 +572,6 @@ def vysledkovka_rocniku(rocnik, jen_verejne=True):
#.filter(hodnoceni_set__rocnik__eq=cislo_rocnik) #.filter(hodnoceni_set__rocnik__eq=cislo_rocnik)
cisla = cisla_rocniku(rocnik, jen_verejne) cisla = cisla_rocniku(rocnik, jen_verejne)
body_cisla_slov = {} body_cisla_slov = {}
print("Jen veřejná: {}, čísla: {}".format(jen_verejne, cisla))
for cislo in cisla: for cislo in cisla:
# získáme body za číslo # získáme body za číslo
_, cislobody = secti_body_za_cislo(cislo, aktivni_resitele) _, cislobody = secti_body_za_cislo(cislo, aktivni_resitele)
@ -539,7 +587,7 @@ def vysledkovka_rocniku(rocnik, jen_verejne=True):
poradi = sloupec_s_poradim(setrizene_body) poradi = sloupec_s_poradim(setrizene_body)
# získáme body odjakživa # získáme body odjakživa
resitel_odjakzivabody_slov = body_resitelu_odjakziva(rocnik, aktivni_resitele) resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, rocnik)
# vytvoříme jednotlivé sloupce výsledkovky # vytvoříme jednotlivé sloupce výsledkovky
radky_vysledkovky = [] radky_vysledkovky = []
@ -558,11 +606,7 @@ def vysledkovka_rocniku(rocnik, jen_verejne=True):
setrizene_body[i], # body za ročník (spočítané výše s pořadím) setrizene_body[i], # body za ročník (spočítané výše s pořadím)
resitel_odjakzivabody_slov[ar_id], # body odjakživa resitel_odjakzivabody_slov[ar_id], # body odjakživa
rocnik) # ročník semináře pro získání ročníku řešitele rocnik) # ročník semináře pro získání ročníku řešitele
print("{}: číslobody - {}, ročníkbody - {},"
"odjakživabody - {}".format(radek.resitel, radek.body_cisla_sezn,
radek.body_rocnik, radek.body_celkem_odjakziva))
radky_vysledkovky.append(radek) radky_vysledkovky.append(radek)
print("Přikládám {}-tý řádek.".format(i))
i += 1 i += 1
return radky_vysledkovky return radky_vysledkovky
@ -618,8 +662,8 @@ class RadekVysledkovkyCisla(object):
self.titul = resitel.get_titul(body_odjakziva) self.titul = resitel.get_titul(body_odjakziva)
# přiřazuje danému řešiteli body do slovníku
def pricti_body(slovnik, resitel, body): 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í # testujeme na None (""), pokud je to první řešení
# daného řešitele, předěláme na 0 # daného řešitele, předěláme na 0
# (v dalším kroku přičteme reálný počet bodů), # (v dalším kroku přičteme reálný počet bodů),
@ -630,15 +674,16 @@ def pricti_body(slovnik, resitel, body):
slovnik[resitel.id] += body slovnik[resitel.id] += body
def secti_body_za_rocnik(rocnik, aktivni_resitele): def secti_body_za_rocnik(rocnik, aktivni_resitele):
# spočítáme všem řešitelům jejich body za ročník """ Spočítá body za ročník, setřídí je sestupně a vrátí jako seznam."""
resitel_rocnikbody_slov = body_resitelu_za_rocnik(rocnik, aktivni_resitele) # 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, rocnik, False)
# zeptáme se na dvojice (řešitel, body) za ročník a setřídíme sestupně # 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(), resitel_rocnikbody_sezn = sorted(resitel_rocnikbody_slov.items(),
key = lambda x: x[1], reverse = True) key = lambda x: x[1], reverse = True)
return resitel_rocnikbody_sezn return resitel_rocnikbody_sezn
# spočítá u řešitelů body za číslo a za jednotlivé hlavní problémy (témata)
def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None): 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é # 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 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ů) # pro jednotlivé řešitele (slovník slovníků hlavních problémů)
@ -701,8 +746,7 @@ def vysledkovka_cisla(cislo, context=None):
resitel_rocnikbody_sezn = secti_body_za_rocnik(cislo.rocnik, aktivni_resitele) resitel_rocnikbody_sezn = secti_body_za_rocnik(cislo.rocnik, aktivni_resitele)
# získáme body odjakživa # získáme body odjakživa
resitel_odjakzivabody_slov = body_resitelu_odjakziva(cislo.rocnik, resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, cislo)
aktivni_resitele)
# řešitelé setřídění podle bodů za číslo sestupně # řešitelé setřídění podle bodů za číslo sestupně
setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn] setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn]
@ -729,10 +773,7 @@ def vysledkovka_cisla(cislo, context=None):
setrizeni_resitele_body[i], # body za ročník (spočítané výše s pořadím) 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 resitel_odjakzivabody_slov[ar_id], # body odjakživa
cislo.rocnik) # ročník semináře pro zjištění ročníku řešitele cislo.rocnik) # ročník semináře pro zjištění ročníku řešitele
print("{}: body za problémy - {}, číslobody - {}, ročníkbody - {}, odjakživabody - {}".format(radek.resitel,
radek.body_problemy_sezn, radek.body_cislo, radek.body_rocnik, radek.body_celkem_odjakziva))
radky_vysledkovky.append(radek) radky_vysledkovky.append(radek)
print("Přikládám {}-tý řádek.".format(i))
i += 1 i += 1
# vytahané informace předáváme do kontextu # vytahané informace předáváme do kontextu
@ -741,7 +782,6 @@ def vysledkovka_cisla(cislo, context=None):
context['problemy'] = hlavni_problemy context['problemy'] = hlavni_problemy
#context['v_cisle_zadane'] = TODO #context['v_cisle_zadane'] = TODO
#context['resene_problemy'] = resene_problemy #context['resene_problemy'] = resene_problemy
print("Předávám kontext.")
return context return context
class CisloView(generic.DetailView): class CisloView(generic.DetailView):
@ -779,7 +819,7 @@ class ArchivTemataView(generic.ListView):
### Generovani vysledkovky ### Generovani vysledkovky
class CisloVysledkovkaView(CisloView): class CisloVysledkovkaView(CisloView):
"View vytvořené pro stránku zobrazující výsledkovku čísla v TeXu." """View vytvořené pro stránku zobrazující výsledkovku čísla v TeXu."""
model = Cislo model = Cislo
template_name = 'seminar/archiv/cislo_vysledkovka.tex' template_name = 'seminar/archiv/cislo_vysledkovka.tex'
@ -789,7 +829,7 @@ class CisloVysledkovkaView(CisloView):
#vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani #vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani
class RocnikVysledkovkaView(RocnikView): class RocnikVysledkovkaView(RocnikView):
"View vytvořené pro stránku zobrazující výsledkovku ročníku v TeXu." """ View vytvořené pro stránku zobrazující výsledkovku ročníku v TeXu."""
model = Rocnik model = Rocnik
template_name = 'seminar/archiv/rocnik_vysledkovka.tex' template_name = 'seminar/archiv/rocnik_vysledkovka.tex'
#content_type = 'application/x-tex; charset=UTF8' #content_type = 'application/x-tex; charset=UTF8'
@ -798,6 +838,7 @@ class RocnikVysledkovkaView(RocnikView):
#vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani #vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani
### Generovani obalek ### Generovani obalek
class CisloObalkyStruct: class CisloObalkyStruct:
rocnik = None rocnik = None
cisla = None cisla = None
@ -822,13 +863,15 @@ def aktivniResitele(rocnik,cislo):
letos.rocnik = Rocnik.objects.get(rocnik = rocnik) letos.rocnik = Rocnik.objects.get(rocnik = rocnik)
loni.rocnik = Rocnik.objects.get(rocnik = int(rocnik)-1) loni.rocnik = Rocnik.objects.get(rocnik = int(rocnik)-1)
letos.cisla = Cislo.objects.filter(rocnik=letos.rocnik,cislo__lte = cislo) letos.cisla = Cislo.objects.filter(rocnik=letos.rocnik, cislo__lte = cislo)
loni.cisla = Cislo.objects.filter(rocnik=loni.rocnik) loni.cisla = Cislo.objects.filter(rocnik=loni.rocnik)
if int(cislo) > 3: if int(cislo) > 3:
problemy = Problem.objects.filter(cislo_zadani__in = letos.cisla) problemy = Problem.objects.filter(cislo_zadani__in = letos.cisla)
else: else:
problemy = Problem.objects.filter(Q(cislo_zadani__in = letos.cisla)|Q(cislo_zadani__in = loni.cisla)) problemy = Problem.objects.filter(
resitele = aktualni_resitele.filter(reseni__in = Reseni.objects.filter(problem__in=problemy)).distinct() Q(cislo_zadani__in = letos.cisla) | Q(cislo_zadani__in = loni.cisla) )
resitele = aktualni_resitele.filter(reseni__in = Reseni.objects.filter(
problem__in=problemy)).distinct()
return resitele return resitele
@ -875,13 +918,15 @@ def oldObalkovaniView(request, rocnik, cislo):
### Tituly ### Tituly
def TitulyView(request, rocnik, cislo): def TitulyView(request, rocnik, cislo):
""" View pro stažení makra titulů v TeXu."""
rocnik_obj = Rocnik.objects.get(rocnik = rocnik) rocnik_obj = Rocnik.objects.get(rocnik = rocnik)
resitele = Resitel.objects.filter(rok_maturity__gte = rocnik_obj.prvni_rok) resitele = Resitel.objects.filter(rok_maturity__gte = rocnik_obj.prvni_rok)
cislo_obj = Cislo.objects.get(rocnik = rocnik_obj, poradi = cislo) cislo_obj = Cislo.objects.get(rocnik = rocnik_obj, poradi = cislo)
asciijmena = [] asciijmena = []
jmenovci = False # detekuje, zda jsou dva řešitelé jmenovci (modulo nabodeníčka), pokud ano, vrátí se jako true jmenovci = False # detekuje, zda jsou dva řešitelé jmenovci (modulo nabodeníčka),
slovnik_s_body = body_resitelu_odjakziva(rocnik_obj, resitele) # pokud ano, vrátí se jako true
slovnik_s_body = body_resitelu(resitele, rocnik_obj)
for resitel in resitele: for resitel in resitele:
resitel.titul = resitel.get_titul(slovnik_s_body[resitel.id]) resitel.titul = resitel.get_titul(slovnik_s_body[resitel.id])
@ -899,8 +944,6 @@ def TitulyView(request, rocnik, cislo):
return render(request, 'seminar/archiv/tituly.tex', return render(request, 'seminar/archiv/tituly.tex',
{'resitele': resitele,'jmenovci':jmenovci},content_type="text/plain") {'resitele': resitele,'jmenovci':jmenovci},content_type="text/plain")
### Soustredeni ### Soustredeni
class SoustredeniListView(generic.ListView): class SoustredeniListView(generic.ListView):
@ -929,13 +972,13 @@ class SoustredeniUcastniciBaseView(generic.ListView):
class SoustredeniMailyUcastnikuView(SoustredeniUcastniciBaseView): class SoustredeniMailyUcastnikuView(SoustredeniUcastniciBaseView):
"""Seznam e-mailů řešitelů oddělených čárkami""" """ Seznam e-mailů řešitelů oddělených čárkami. """
model = Soustredeni_Ucastnici model = Soustredeni_Ucastnici
template_name = 'seminar/soustredeni/maily_ucastniku.txt' template_name = 'seminar/soustredeni/maily_ucastniku.txt'
class SoustredeniUcastniciView(SoustredeniUcastniciBaseView): class SoustredeniUcastniciView(SoustredeniUcastniciBaseView):
"""HTML tabulka účastníků pro tisk""" """ HTML tabulka účastníků pro tisk. """
model = Soustredeni_Ucastnici model = Soustredeni_Ucastnici
template_name = 'seminar/soustredeni/seznam_ucastniku.html' template_name = 'seminar/soustredeni/seznam_ucastniku.html'
@ -1057,11 +1100,11 @@ def texUploadView(request):
} }
problem_typ = typy[meta["typ"]] problem_typ = typy[meta["typ"]]
# Pokud už problém existuje, vytáhneme jej z db a upravíme # Pokud už problém existuje, vytáhneme jej z db a upravíme.
# Pokud neexistuje, vytvoříme jej jedině pokud je to vynucené # Pokud neexistuje, vytvoříme jej jedině pokud je to vynucené.
# Pokud ročník/číslo ještě neexistuje, vyhodí to výjimku -> # Pokud ročník/číslo ještě neexistuje, vyhodí to výjimku ->
# číslo/ročník se musí založit ručně v adminu # číslo/ročník se musí založit ručně v adminu.
rocnik = Rocnik.objects.get(rocnik=meta["rocnik"]) rocnik = Rocnik.objects.get(rocnik=meta["rocnik"])
cislo = Cislo.objects.get(rocnik=rocnik, cislo=meta["cislo"]) cislo = Cislo.objects.get(rocnik=rocnik, cislo=meta["cislo"])
@ -1195,7 +1238,8 @@ class ResitelView(LoginRequiredMixin,generic.DetailView):
print(self.request.user) print(self.request.user)
return Resitel.objects.get(osoba__user=self.request.user) return Resitel.objects.get(osoba__user=self.request.user)
## Formulare ### Formulare
class AddSolutionView(LoginRequiredMixin, FormView): class AddSolutionView(LoginRequiredMixin, FormView):
template_name = 'seminar/org/vloz_reseni.html' template_name = 'seminar/org/vloz_reseni.html'
form_class = f.VlozReseniForm form_class = f.VlozReseniForm
@ -1234,9 +1278,6 @@ class SubmitSolutionView(LoginRequiredMixin, CreateView):
return HttpResponseRedirect(self.get_success_url()) return HttpResponseRedirect(self.get_success_url())
def resetPasswordView(request): def resetPasswordView(request):
pass pass
@ -1398,10 +1439,6 @@ def prihlaskaView(request):
return render(request, 'seminar/prihlaska.html', {'form': form}) return render(request, 'seminar/prihlaska.html', {'form': form})
# FIXME: Tohle asi vlastně vůbec nepatří do aplikace 'seminar' # FIXME: Tohle asi vlastně vůbec nepatří do aplikace 'seminar'
class LoginView(auth_views.LoginView): class LoginView(auth_views.LoginView):
# Jen vezmeme vestavěný a dáme mu vhodný template a přesměrovací URL # Jen vezmeme vestavěný a dáme mu vhodný template a přesměrovací URL
@ -1419,25 +1456,25 @@ class LogoutView(auth_views.LogoutView):
# Pavel: Vůbec nevím, proč to s _lazy funguje, ale bez toho to bylo rozbité. # Pavel: Vůbec nevím, proč to s _lazy funguje, ale bez toho to bylo rozbité.
next_page = reverse_lazy('titulni_strana') next_page = reverse_lazy('titulni_strana')
# "Chci resetovat heslo"
class PasswordResetView(auth_views.PasswordResetView): class PasswordResetView(auth_views.PasswordResetView):
""" Chci resetovat heslo. """
#template_name = 'seminar/password_reset.html' #template_name = 'seminar/password_reset.html'
# TODO: vlastní email_template_name a subject_template_name a html_email_template_name # TODO: vlastní email_template_name a subject_template_name a html_email_template_name
success_url = reverse_lazy('reset_password_done') success_url = reverse_lazy('reset_password_done')
from_email = 'login@mam.mff.cuni.cz' from_email = 'login@mam.mff.cuni.cz'
# "Poslali jsme e-mail (pokud bylo kam))"
class PasswordResetDoneView(auth_views.PasswordResetDoneView): class PasswordResetDoneView(auth_views.PasswordResetDoneView):
""" Poslali jsme e-mail (pokud bylo kam)). """
#template_name = 'seminar/password_reset_done.html' #template_name = 'seminar/password_reset_done.html'
pass pass
# "Vymysli si heslo"
class PasswordResetConfirmView(auth_views.PasswordResetConfirmView): class PasswordResetConfirmView(auth_views.PasswordResetConfirmView):
""" Vymysli si heslo. """
#template_name = 'seminar/password_confirm_done.html' #template_name = 'seminar/password_confirm_done.html'
success_url = reverse_lazy('reset_password_complete') success_url = reverse_lazy('reset_password_complete')
# "Heslo se asi změnilo."
class PasswordResetCompleteView(auth_views.PasswordResetCompleteView): class PasswordResetCompleteView(auth_views.PasswordResetCompleteView):
""" Heslo se asi změnilo."""
#template_name = 'seminar/password_complete_done.html' #template_name = 'seminar/password_complete_done.html'
pass pass

Loading…
Cancel
Save