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/galerie/static/galerie/prvky/dalsi.png b/galerie/static/galerie/prvky/dalsi.png
deleted file mode 100644
index b5c68dcc..00000000
Binary files a/galerie/static/galerie/prvky/dalsi.png and /dev/null differ
diff --git a/galerie/static/galerie/prvky/dalsi.svg b/galerie/static/galerie/prvky/dalsi.svg
new file mode 100644
index 00000000..bd3274b6
--- /dev/null
+++ b/galerie/static/galerie/prvky/dalsi.svg
@@ -0,0 +1,35 @@
+
+
diff --git a/galerie/static/galerie/prvky/nahoru.png b/galerie/static/galerie/prvky/nahoru.png
deleted file mode 100644
index d297af60..00000000
Binary files a/galerie/static/galerie/prvky/nahoru.png and /dev/null differ
diff --git a/galerie/static/galerie/prvky/predchozi.png b/galerie/static/galerie/prvky/predchozi.png
deleted file mode 100644
index dc657411..00000000
Binary files a/galerie/static/galerie/prvky/predchozi.png and /dev/null differ
diff --git a/galerie/static/galerie/prvky/predchozi.svg b/galerie/static/galerie/prvky/predchozi.svg
new file mode 100644
index 00000000..af139f7e
--- /dev/null
+++ b/galerie/static/galerie/prvky/predchozi.svg
@@ -0,0 +1,35 @@
+
+
diff --git a/mamweb/static/css/mamweb.css b/mamweb/static/css/mamweb.css
index 09b870f4..dc942cc2 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%;
+ }
}
@@ -706,19 +712,6 @@ div.cislo_odkazy ul {
padding: 0px;
}
-/* archiv ročník
-div.cisla-v-rocniku {
- font-weight: bold;
- color: #6f2509;
-}
-
-div.cislo-v-rocniku-blok {
- display: inline-block;
- width: 150px;
- height: 220px;
- text-align: center;
-}*/
-
/* galerie */
@@ -739,7 +732,8 @@ div.cislo-v-rocniku-blok {
top: 0;
}
.predchozi_obrazek:hover{
- background-image: url("/static/galerie/prvky/predchozi.png");
+ background-image: url("/static/galerie/prvky/predchozi.svg");
+ filter: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.4));
background-position: left center;
background-repeat: no-repeat;
}
@@ -752,7 +746,8 @@ div.cislo-v-rocniku-blok {
top: 0;
}
.dalsi_obrazek:hover{
- background-image: url("/static/galerie/prvky/dalsi.png");
+ background-image: url("/static/galerie/prvky/dalsi.svg");
+ filter: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.4));
background-position: right center;
background-repeat: no-repeat;
}
@@ -782,31 +777,25 @@ div.cislo-v-rocniku-blok {
/* titulní obrázek hlavní galerie soustředění */
.titulni_obrazek {
- border: 1px solid black;
}
.galerie_nahledy{
/*margin: 1em 0;*/
- margin: 0 auto 30px auto;
+ margin: auto;
+ padding: 10px;
text-align: center;
overflow: auto;
}
+.galerie_nahledy img {
+ margin: 10px;
+}
+
.galerie_nahledy div.navigace {
display: inline-block;
- width: 150px;
}
-/*.galerie_nahledy img{*/
-/*margin: 0 10px 0 10px;*/
-/*}*/
-
-/*.galerie_nahledy a{*/
-/*height: 100%;*/
-/*width: 100%;*/
-/*}*/
-
-.galerie_nahled { /* frame */
+.galerie_nahled, .podgalerie_nahled { /* frame */
display: block;
position: relative;
float: left;
@@ -814,19 +803,19 @@ div.cislo-v-rocniku-blok {
height: 200px;
text-align: center;
border: solid;
- border-width: 2px;
- border-radius: 5px;
- /*border-color: #ffa500;*/
- border-color: #ffd546;
- /*background-color: #ffb52d;*/
- background-color: white;
+ border-width: 1px;
+ border-radius: 4px;
+ border-color: #f9d59e;
+ background-color: #fffbf6;
white-space: nowrap;
- margin: 10px 20px 10px 0px;
+ margin: 10px;
+ font-weight: bold;
}
-.galerie_nahled:hover {
- background-color: #ffd546;
- border-color: #ffa500;
+.galerie_nahled:hover, .podgalerie_nahled:hover {
+ background-color: #f9d59e;
+ filter: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.4));
+ color: #6f2509;
}
.vystredeno{ /* helper */
@@ -839,12 +828,6 @@ div.cislo-v-rocniku-blok {
vertical-align: middle;
max-height: 180px;
max-width: 180px;
- /*border: 1px solid white;*/
-
-}
-
-.galerie_nahled img, .podgalerie_nahled img {
- border-radius: 2px;
}
.galerie_nahled div {
@@ -854,30 +837,6 @@ div.cislo-v-rocniku-blok {
text-align: center;
}
-
-.podgalerie_nahled {
- display: block;
- position: relative;
- float: left;
- width: 200px;
- height: 200px;
- text-align: center;
- border: solid;
- border-width: 2px;
- border-radius: 5px;
- border-color: #ffa500;
- /*border-color: #ffd546;*/
- background-color: #ffd546;
- /*background-color: white;*/
- white-space: nowrap;
- margin: 10px 20px 10px 0px;
- font-weight: bold;
-}
-
-.podgalerie_nahled:hover {
- background-color: #ffa500;
-}
-
.podgalerie_nahled img {
margin-top: 20px;
margin-bottom: 15px;
@@ -893,10 +852,10 @@ div.cislo-v-rocniku-blok {
/* plus a minus tlacitka */
.mam-org-only-galerie {
- background: #fff0d7;
+ background: #eee4ec;
padding: 10px;
margin: 10px 10px 10px -20px;
- border: orange 2px dashed;
+ border: #333 2px dashed;
float: left;
}
@@ -904,8 +863,8 @@ div.cislo-v-rocniku-blok {
padding: 3px 5px;
margin: 5px;
border-radius: 20px;
- background-color: lightblue;
- color: black;
+ background-color: #6f2509;;
+ color: #fffbf6;
float: left;
}
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 5259c025..95eb07d1 100644
--- a/seminar/views/views_all.py
+++ b/seminar/views/views_all.py
@@ -324,10 +324,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
@@ -358,20 +364,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):
@@ -382,8 +396,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
@@ -405,36 +419,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):
@@ -447,7 +496,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"
"""
@@ -460,7 +509,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)
@@ -476,7 +524,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 = []
@@ -495,11 +543,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
@@ -581,8 +625,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ů),
@@ -593,15 +637,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ů)
@@ -664,8 +709,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]
@@ -692,10 +736,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
@@ -704,7 +745,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):
@@ -742,7 +782,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'
@@ -752,7 +792,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'
@@ -761,6 +801,7 @@ class RocnikVysledkovkaView(RocnikView):
#vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani
### Generovani obalek
+
class CisloObalkyStruct:
rocnik = None
cisla = None
@@ -785,13 +826,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
@@ -838,13 +881,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])
@@ -862,8 +907,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):
@@ -892,13 +935,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'
@@ -1020,11 +1063,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"])
@@ -1158,7 +1201,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
@@ -1197,9 +1241,6 @@ class SubmitSolutionView(LoginRequiredMixin, CreateView):
return HttpResponseRedirect(self.get_success_url())
-
-
-
def resetPasswordView(request):
pass
@@ -1361,10 +1402,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
@@ -1382,25 +1419,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