diff --git a/galerie/models.py b/galerie/models.py index 2d9b8e56..c48d3a95 100644 --- a/galerie/models.py +++ b/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ří cesta = ( ['Galerie'] + - (["soustredeni_" + gal.soustredeni.pk] if gal.soustredeni else []) + - ["galerie_" + cislo_gal, self.nazev] + (["soustredeni_{}".format(gal.soustredeni.pk)] if gal.soustredeni else []) + + ["galerie_{}".format(cislo_gal), self.nazev] ) return os.path.join(*cesta) diff --git a/mamweb/static/css/mamweb.css b/mamweb/static/css/mamweb.css index 09b870f4..2a719264 100644 --- a/mamweb/static/css/mamweb.css +++ b/mamweb/static/css/mamweb.css @@ -242,6 +242,10 @@ nav.nav-button { display: none; } +div.dropdown-backdrop { /* tohle způsobuje, že funguje mobilní menu */ + z-index: -1; +} + h1 a:hover { text-decoration: none; } @@ -387,11 +391,16 @@ p.license-mobile { } div.graf{ + width: 70%; float: none; margin-left: auto; margin-right: auto; - width: 70%; + margin-top: 10px; } + #svg-graf{ + width: 100%; + height: auto;; + } ul.menu { font-size: 90%; @@ -575,12 +584,9 @@ ul.submenu { float: none; } - div.graf{ - float: none; - width: 70%; - margin-left: auto; - margin-right: auto; - } + div.graf { + width: 100%; + } } diff --git a/mamweb/templates/graph.svg b/mamweb/templates/graph.svg index 7f25678c..ed730edf 100644 --- a/mamweb/templates/graph.svg +++ b/mamweb/templates/graph.svg @@ -10,7 +10,7 @@ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.1" - id="svg2" + id="svg-graf" width="482.57019" height="599.45636" viewBox="0 0 482.57019 599.45636" diff --git a/seminar/testutils.py b/seminar/testutils.py index 65ffbfcd..b562bf74 100644 --- a/seminar/testutils.py +++ b/seminar/testutils.py @@ -173,13 +173,12 @@ def gen_organizatori(rnd, osoby, last_rocnik, users): if do.year > datetime.datetime.now().year: do = None 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 -def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size): - logger.info('Generuji úlohy do čísla (size={})...'.format(size)) - - # ulohy resene v cisle +def gen_zadani_ulohy(rnd, cisla, organizatori, pocet_oboru, poradi_cisla, poradi_problemu): + + # Proměnné pro náhodné generování názvů a zadání. jaka = ["Šachová", "Černá", "Větrná", "Dlouhá", "Křehká", "Rychlá", "Zákeřná", "Fyzikální"] 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"] jmeno = ["řešení", "tahů", "čísel", "kalhot", "koulí", "hadů"] 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," "otevřený problém", "neřešitelné", "triviálně triviální", "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ě" "sporné", "nepopsatelně jednoduché", "pokud jste na to nepřišli," "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 for rocnik in rocniky: - k+=1 + k += 1 print("Generuji {}. číslo.".format(k)) - cisla = rocnik_cisla[k-1] - for ci in range(3, len(cisla)+1): # pro všechna čísla + cisla = rocnik_cisla[k - 1] + 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ů - 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ů # (náhodná hausnumera, možno změnit) # úč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 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_oboru = rnd.randint(1, 2) - p = Uloha.objects.create( - # atributy třídy Problem - nazev=" ".join([rnd.choice(jaka), rnd.choice(co)]), - stav=Problem.STAV_ZADANY, - zamereni=rnd.sample(["M", "F", "I", "O", "B"], poc_oboru), - autor=rnd.choice(organizatori), - garant=rnd.choice(organizatori), - kod=str(pi), - # atributy třídy Uloha - 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 - ) + + # Generování zadání úlohy a UlohaZadaniNode, + # přivěšení pod dané číslo + p = gen_zadani_ulohy(rnd, cisla, organizatori, poc_oboru, ci, pi) + # Generování vzorového řešení + gen_vzoroveho_reseni_ulohy(rnd, cisla, organizatori, p, poc_op, ci) + # Generování řešení a hodnocení k úloze + gen_reseni_ulohy(rnd, cisla, p, poc_res, ci, + resitele_cisla, resitele) return @@ -482,8 +506,10 @@ def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori) rocnik=rocnik, 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í...) - # Tohle sice umožňuje vygenerovat vzorák do čísla dávno po konci témátka, ale to nám pro jednoduchost nevadí. + # 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í...) + # 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: cislo_se_vzorakem = cisla[-1] else: @@ -505,7 +531,8 @@ def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori) tema = tema_node.tema # 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): 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"] for i in range(5): - text_novinky = " ".join([rnd.choice(kdy),rnd.choice(kde),rnd.choice(jake),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])) + text_novinky = " ".join([rnd.choice(kdy), rnd.choice(kde), rnd.choice(jake), + 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() return diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index ee21791a..86eec7f9 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -387,10 +387,16 @@ class ArchivView(generic.ListView): ### 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): + """ 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 @@ -421,20 +427,28 @@ def sloupec_s_poradim(setrizene_body): aktualni_poradi = aktualni_poradi + velikost_skupiny return sloupec_s_poradim -# vrátí všechna čísla daného ročníku 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() -# pro daný problém vrátí jeho nejvyšší nadproblém 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): @@ -445,8 +459,8 @@ def hlavni_problemy_rocniku(rocnik, jen_verejne=True): 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): + """ 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() # hodnocení, která se vážou k danému číslu @@ -468,36 +482,71 @@ def hlavni_problemy_cisla(cislo): 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_odjakziva(rocnik, resitele): +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=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ů - resitele_s_body = Resitel.objects.annotate(body=Sum('reseni__hodnoceni__body')) - # Teď jen z QuerySetu řešitelů anotovaných body vygenerujeme slovník indexovaný řešitelským id obsahující body - # ... ale jen ro řešitele, které dostaneme jako parametr. - # TODO: Zjistit, co ten parametr říká a proč je potřeba - body_odjakziva = {int(res.id) : res.body for res in resitele_s_body if res in resitele} - return body_odjakziva - -# 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 + 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. + """ 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): @@ -510,7 +559,7 @@ class RadekVysledkovkyRocniku(object): self.titul = resitel.get_titul(body_odjakziva) 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" """ @@ -523,7 +572,6 @@ def vysledkovka_rocniku(rocnik, jen_verejne=True): #.filter(hodnoceni_set__rocnik__eq=cislo_rocnik) cisla = cisla_rocniku(rocnik, jen_verejne) body_cisla_slov = {} - print("Jen veřejná: {}, čísla: {}".format(jen_verejne, cisla)) for cislo in cisla: # získáme body za číslo _, 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) # 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 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) resitel_odjakzivabody_slov[ar_id], # body odjakživa 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) - print("Přikládám {}-tý řádek.".format(i)) i += 1 return radky_vysledkovky @@ -618,8 +662,8 @@ class RadekVysledkovkyCisla(object): self.titul = resitel.get_titul(body_odjakziva) -# přiřazuje danému řešiteli body do slovníku 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ů), @@ -630,15 +674,16 @@ def pricti_body(slovnik, resitel, body): slovnik[resitel.id] += body def secti_body_za_rocnik(rocnik, aktivni_resitele): - # spočítáme všem řešitelům jejich body za ročník - resitel_rocnikbody_slov = body_resitelu_za_rocnik(rocnik, aktivni_resitele) + """ Spočítá body za ročník, setřídí je sestupně a vrátí jako seznam.""" + # 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ě resitel_rocnikbody_sezn = sorted(resitel_rocnikbody_slov.items(), key = lambda x: x[1], reverse = True) 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): + """ 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ů) @@ -701,8 +746,7 @@ def vysledkovka_cisla(cislo, context=None): resitel_rocnikbody_sezn = secti_body_za_rocnik(cislo.rocnik, aktivni_resitele) # získáme body odjakživa - resitel_odjakzivabody_slov = body_resitelu_odjakziva(cislo.rocnik, - aktivni_resitele) + 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] @@ -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) resitel_odjakzivabody_slov[ar_id], # body odjakživa 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) - print("Přikládám {}-tý řádek.".format(i)) i += 1 # vytahané informace předáváme do kontextu @@ -741,7 +782,6 @@ def vysledkovka_cisla(cislo, context=None): context['problemy'] = hlavni_problemy #context['v_cisle_zadane'] = TODO #context['resene_problemy'] = resene_problemy - print("Předávám kontext.") return context class CisloView(generic.DetailView): @@ -779,7 +819,7 @@ class ArchivTemataView(generic.ListView): ### Generovani vysledkovky 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 template_name = 'seminar/archiv/cislo_vysledkovka.tex' @@ -789,7 +829,7 @@ class CisloVysledkovkaView(CisloView): #vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani 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 template_name = 'seminar/archiv/rocnik_vysledkovka.tex' #content_type = 'application/x-tex; charset=UTF8' @@ -798,6 +838,7 @@ class RocnikVysledkovkaView(RocnikView): #vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani ### Generovani obalek + class CisloObalkyStruct: rocnik = None cisla = None @@ -822,13 +863,15 @@ def aktivniResitele(rocnik,cislo): letos.rocnik = Rocnik.objects.get(rocnik = rocnik) 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) if int(cislo) > 3: problemy = Problem.objects.filter(cislo_zadani__in = letos.cisla) else: - problemy = Problem.objects.filter(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() + problemy = Problem.objects.filter( + 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 @@ -875,13 +918,15 @@ def oldObalkovaniView(request, rocnik, cislo): ### Tituly def TitulyView(request, rocnik, cislo): + """ View pro stažení makra titulů v TeXu.""" rocnik_obj = Rocnik.objects.get(rocnik = rocnik) resitele = Resitel.objects.filter(rok_maturity__gte = rocnik_obj.prvni_rok) cislo_obj = Cislo.objects.get(rocnik = rocnik_obj, poradi = cislo) asciijmena = [] - jmenovci = False # detekuje, zda jsou dva řešitelé jmenovci (modulo nabodeníčka), pokud ano, vrátí se jako true - slovnik_s_body = body_resitelu_odjakziva(rocnik_obj, resitele) + jmenovci = False # detekuje, zda jsou dva řešitelé jmenovci (modulo nabodeníčka), + # pokud ano, vrátí se jako true + slovnik_s_body = body_resitelu(resitele, rocnik_obj) for resitel in resitele: 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', {'resitele': resitele,'jmenovci':jmenovci},content_type="text/plain") - - ### Soustredeni class SoustredeniListView(generic.ListView): @@ -929,13 +972,13 @@ class SoustredeniUcastniciBaseView(generic.ListView): class SoustredeniMailyUcastnikuView(SoustredeniUcastniciBaseView): - """Seznam e-mailů řešitelů oddělených čárkami""" + """ Seznam e-mailů řešitelů oddělených čárkami. """ model = Soustredeni_Ucastnici template_name = 'seminar/soustredeni/maily_ucastniku.txt' class SoustredeniUcastniciView(SoustredeniUcastniciBaseView): - """HTML tabulka účastníků pro tisk""" + """ HTML tabulka účastníků pro tisk. """ model = Soustredeni_Ucastnici template_name = 'seminar/soustredeni/seznam_ucastniku.html' @@ -1057,11 +1100,11 @@ def texUploadView(request): } problem_typ = typy[meta["typ"]] - # Pokud už problém existuje, vytáhneme jej z db a upravíme - # Pokud neexistuje, vytvoříme jej jedině pokud je to vynucené + # Pokud už problém existuje, vytáhneme jej z db a upravíme. + # Pokud neexistuje, vytvoříme jej jedině pokud je to vynucené. # 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"]) cislo = Cislo.objects.get(rocnik=rocnik, cislo=meta["cislo"]) @@ -1195,7 +1238,8 @@ class ResitelView(LoginRequiredMixin,generic.DetailView): print(self.request.user) return Resitel.objects.get(osoba__user=self.request.user) -## Formulare +### Formulare + class AddSolutionView(LoginRequiredMixin, FormView): template_name = 'seminar/org/vloz_reseni.html' form_class = f.VlozReseniForm @@ -1234,9 +1278,6 @@ class SubmitSolutionView(LoginRequiredMixin, CreateView): return HttpResponseRedirect(self.get_success_url()) - - - def resetPasswordView(request): pass @@ -1398,10 +1439,6 @@ def prihlaskaView(request): return render(request, 'seminar/prihlaska.html', {'form': form}) - - - - # FIXME: Tohle asi vlastně vůbec nepatří do aplikace 'seminar' class LoginView(auth_views.LoginView): # 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é. next_page = reverse_lazy('titulni_strana') -# "Chci resetovat heslo" class PasswordResetView(auth_views.PasswordResetView): + """ Chci resetovat heslo. """ #template_name = 'seminar/password_reset.html' # TODO: vlastní email_template_name a subject_template_name a html_email_template_name success_url = reverse_lazy('reset_password_done') from_email = 'login@mam.mff.cuni.cz' -# "Poslali jsme e-mail (pokud bylo kam))" class PasswordResetDoneView(auth_views.PasswordResetDoneView): + """ Poslali jsme e-mail (pokud bylo kam)). """ #template_name = 'seminar/password_reset_done.html' pass -# "Vymysli si heslo" class PasswordResetConfirmView(auth_views.PasswordResetConfirmView): + """ Vymysli si heslo. """ #template_name = 'seminar/password_confirm_done.html' success_url = reverse_lazy('reset_password_complete') -# "Heslo se asi změnilo." class PasswordResetCompleteView(auth_views.PasswordResetCompleteView): + """ Heslo se asi změnilo.""" #template_name = 'seminar/password_complete_done.html' pass