diff --git a/seminar/models.py b/seminar/models.py index fd34773f..b85711e5 100644 --- a/seminar/models.py +++ b/seminar/models.py @@ -18,6 +18,7 @@ from django.contrib.contenttypes.models import ContentType from django.utils.text import get_valid_filename from imagekit.models import ImageSpecField, ProcessedImageField from imagekit.processors import ResizeToFit, Transpose +from django.utils.functional import cached_property from django_countries.fields import CountryField from solo.models import SingletonModel @@ -855,10 +856,11 @@ class Problem(SeminarModelBase,PolymorphicModel): return self.nazev # Implicitini implementace, jednotlivé dědící třídy si přepíšou + @cached_property def kod_v_rocniku(self): if self.stav == 'zadany': if self.nadproblem: - return self.nadproblem.kod_v_rocniku()+".{}".format(self.kod) + return self.nadproblem.kod_v_rocniku+".{}".format(self.kod) return str(self.kod) return '' @@ -927,10 +929,11 @@ class Tema(Problem): abstrakt = models.TextField('Abstrakt na rozcestník', blank=True) obrazek = models.ImageField('Obrázek na rozcestník', null=True) + @cached_property def kod_v_rocniku(self): if self.stav == 'zadany': if self.nadproblem: - return self.nadproblem.kod_v_rocniku()+".t{}".format(self.kod) + return self.nadproblem.kod_v_rocniku+".t{}".format(self.kod) return "t{}".format(self.kod) return '' @@ -963,7 +966,7 @@ class Clanek(Problem): if self.stav == 'zadany': # Nemělo by být potřeba # if self.nadproblem: -# return self.nadproblem.kod_v_rocniku()+".c{}".format(self.kod) +# return self.nadproblem.kod_v_rocniku+".c{}".format(self.kod) return "c{}".format(self.kod) return '' @@ -1020,11 +1023,12 @@ class Uloha(Problem): # UlohaZadaniNode # UlohaVzorakNode + @cached_property def kod_v_rocniku(self): if self.stav == 'zadany': name="{}.u{}".format(self.cislo_zadani.poradi,self.kod) if self.nadproblem: - return self.nadproblem.kod_v_rocniku()+name + return self.nadproblem.kod_v_rocniku+name return name return '' diff --git a/seminar/utils.py b/seminar/utils.py index 47d5027c..a02c317c 100644 --- a/seminar/utils.py +++ b/seminar/utils.py @@ -17,10 +17,11 @@ import seminar.treelib as t org_required = permission_required('auth.org', raise_exception=True) resitel_required = permission_required('auth.resitel', raise_exception=True) User = get_user_model() -User.je_org = lambda self: self.has_perm('auth.org') -User.je_resitel = lambda self: self.has_perm('auth.resitel') -AnonymousUser.je_org = lambda self: False -AnonymousUser.je_resitel = lambda self: False +# Není to úplně hezké, ale budeme doufat, že to je funkční... +User.je_org = property(lambda self: self.has_perm('auth.org')) +User.je_resitel = property(lambda self: self.has_perm('auth.resitel')) +AnonymousUser.je_org = False +AnonymousUser.je_resitel = False class FirstTagParser(HTMLParser): @@ -239,47 +240,25 @@ def hlavni_problem(problem): problem = problem.nadproblem return problem -def hlavni_problemy_rocniku(rocnik, jen_verejne=True): - """ Pro zadaný ročník vrátí hlavní problémy ročníku, - tj. ty, které už nemají nadproblém.""" - hlavni_problemy = [] - for cislo in cisla_rocniku(rocnik, jen_verejne): - for problem in hlavni_problemy_cisla(cislo): - hlavni_problemy.append(problem) - hlavni_problemy_set = set(hlavni_problemy) - hlavni_problemy = list(hlavni_problemy_set) - hlavni_problemy.sort(key=lambda k:k.kod_v_rocniku()) # setřídit podle pořadí - - return hlavni_problemy +def problemy_rocniku(rocnik, jen_verejne=True): + return m.Problem.objects.filter(hodnoceni__in = m.Hodnoceni.objects.filter(cislo_body__in = cisla_rocniku(rocnik, jen_verejne))).distinct().select_related('nadproblem').select_related('nadproblem__nadproblem') def problemy_cisla(cislo): """ Vrátí seznam všech problémů s body v daném čísle. """ - hodnoceni = cislo.hodnoceni.select_related('problem', 'reseni').all() - # hodnocení, která se vážou k danému číslu - - reseni = [h.reseni for h in hodnoceni] - problemy = [h.problem for h in hodnoceni] - problemy_set = set(problemy) # chceme každý problém unikátně, - problemy = (list(problemy_set)) # převedení na množinu a zpět to zaručí - - return problemy - + return m.Problem.objects.filter(hodnoceni__in = m.Hodnoceni.objects.filter(cislo_body = cislo)).distinct().select_related('nadproblem').select_related('nadproblem__nadproblem') -def hlavni_problemy_cisla(cislo, problemy=None): - """ Vrátí seznam všech problémů s body v daném čísle, které již nemají nadproblém. """ - if problemy is None: - problemy = problemy_cisla(cislo) +def hlavni_problemy_f(problemy=None): + """ Vrátí seznam všech problémů, které již nemají nadproblém. """ # hlavní problémy čísla # (mají vlastní sloupeček ve výsledkovce, nemají nadproblém) - hlavni_problemy = [] + hlavni_problemy = set() for p in problemy: - hlavni_problemy.append(hlavni_problem(p)) + hlavni_problemy.add(hlavni_problem(p)) # zunikátnění - hlavni_problemy_set = set(hlavni_problemy) - hlavni_problemy = list(hlavni_problemy_set) - hlavni_problemy.sort(key=lambda k: k.kod_v_rocniku()) # setřídit podle t1, t2, c3, ... + hlavni_problemy = list(hlavni_problemy) + hlavni_problemy.sort(key=lambda k: k.kod_v_rocniku) # setřídit podle t1, t2, c3, ... return hlavni_problemy @@ -289,7 +268,7 @@ def podproblemy_v_cislu(cislo, problemy=None, hlavni_problemy=None): if problemy is None: problemy = problemy_cisla(cislo) if hlavni_problemy is None: - hlavni_problemy = hlavni_problemy_cisla(cislo, problemy) + hlavni_problemy = hlavni_problemy_f(problemy) podproblemy = dict((hp.id, []) for hp in hlavni_problemy) hlavni_problemy = set(hlavni_problemy) diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index d4a875fd..22b6a743 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -49,7 +49,7 @@ import csv import logging import time -from seminar.utils import aktivniResitele, resi_v_rocniku, hlavni_problemy_rocniku, cisla_rocniku, hlavni_problemy_cisla +from seminar.utils import aktivniResitele, resi_v_rocniku, problemy_rocniku, cisla_rocniku, hlavni_problemy_f # ze starého modelu #def verejna_temata(rocnik): @@ -644,29 +644,25 @@ class RocnikView(generic.DetailView): def get_object(self, queryset=None): if queryset is None: queryset = self.get_queryset() - rocnik_arg = self.kwargs.get('rocnik') - queryset = queryset.filter(rocnik=rocnik_arg) - try: - obj = queryset.get() - except queryset.model.DoesNotExist: - raise Http404(_("No %(verbose_name)s found matching the query") % - {'verbose_name': queryset.model._meta.verbose_name}) - return obj + return get_object_or_404(queryset,rocnik=self.kwargs.get('rocnik')) def get_context_data(self, **kwargs): + start = time.time() context = super(RocnikView, self).get_context_data(**kwargs) # vysledkovka = True zajistí vykreslení, # zkontrolovat, kdy se má a nemá vykreslovat context['vysledkovka'] = True - context['cisla_s_neverejnymi'] = cisla_rocniku(context["rocnik"], jen_verejne=False) + if self.request.user.je_org: + context['cisla_s_neverejnymi'] = cisla_rocniku(context["rocnik"], jen_verejne=False) + context['radky_vysledkovky_s_neverejnymi'] = vysledkovka_rocniku(context["rocnik"], jen_verejne=False) + context['hlavni_problemy_v_rocniku_s_neverejnymi'] = hlavni_problemy_f(problemy_rocniku(context["rocnik"], jen_verejne=False)) context['cisla'] = cisla_rocniku(context["rocnik"]) context['radky_vysledkovky'] = vysledkovka_rocniku(context["rocnik"]) - context['radky_vysledkovky_s_neverejnymi'] = vysledkovka_rocniku( - context["rocnik"], jen_verejne=False) - context['hlavni_problemy_v_rocniku'] = hlavni_problemy_rocniku(context["rocnik"]) - context['hlavni_problemy_v_rocniku_s_neverejnymi'] = hlavni_problemy_rocniku(context["rocnik"], jen_verejne=False) + context['hlavni_problemy_v_rocniku'] = hlavni_problemy_f(problemy_rocniku(context["rocnik"])) + end = time.time() + print("Kontext:", end-start) return context @@ -973,7 +969,7 @@ class ClankyResitelView(generic.ListView): queryset = [] skupiny_clanku = group_by_rocnik(clanky) for skupina in skupiny_clanku: - skupina.sort(key=lambda clanek: clanek.kod_v_rocniku()) + skupina.sort(key=lambda clanek: clanek.kod_v_rocniku) for clanek in skupina: queryset.append(clanek) return queryset diff --git a/seminar/views/vysledkovka.py b/seminar/views/vysledkovka.py index dd8956b9..f4fa5edb 100644 --- a/seminar/views/vysledkovka.py +++ b/seminar/views/vysledkovka.py @@ -1,6 +1,7 @@ import seminar.models as m from django.db.models import Q, Sum, Count -from seminar.utils import aktivniResitele, resi_v_rocniku, cisla_rocniku, hlavni_problemy_rocniku, hlavni_problem, hlavni_problemy_cisla, problemy_cisla, podproblemy_v_cislu +from seminar.utils import aktivniResitele, resi_v_rocniku, cisla_rocniku, hlavni_problem, hlavni_problemy_f, problemy_cisla, podproblemy_v_cislu +import time ### Výsledky def sloupec_s_poradim(setrizene_body): @@ -126,15 +127,16 @@ class RadekVysledkovkyRocniku(object): def setrid_resitele_a_body(slov_resitel_body): setrizeni_resitele_id = [dvojice[0] for dvojice in slov_resitel_body] - setrizeni_resitele = [m.Resitel.objects.get(id=i) for i in setrizeni_resitele_id] setrizene_body = [dvojice[1] for dvojice in slov_resitel_body] - return setrizeni_resitele_id, setrizeni_resitele, setrizene_body + return setrizeni_resitele_id, setrizene_body def vysledkovka_rocniku(rocnik, jen_verejne=True): """ Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html" """ + start = time.time() + ## TODO možná chytřeji vybírat aktivní řešitele # aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají # u alespoň jedné hodnoty něco jiného než NULL @@ -150,7 +152,7 @@ def vysledkovka_rocniku(rocnik, jen_verejne=True): resitel_rocnikbody_sezn = secti_body_za_rocnik(rocnik, aktivni_resitele) # setřídíme řešitele podle počtu bodů a získáme seznam s body od nejvyšších po nenižší - setrizeni_resitele_id, setrizeni_resitele, setrizene_body = setrid_resitele_a_body(resitel_rocnikbody_sezn) + setrizeni_resitele_id, setrizene_body = setrid_resitele_a_body(resitel_rocnikbody_sezn) poradi = sloupec_s_poradim(setrizene_body) # získáme body odjakživa @@ -159,6 +161,10 @@ def vysledkovka_rocniku(rocnik, jen_verejne=True): # vytvoříme jednotlivé sloupce výsledkovky radky_vysledkovky = [] i = 0 + setrizeni_resitele_dict = {} # Tento slovnik se vyrab + for r in m.Resitel.objects.filter(id__in=setrizeni_resitele_id).select_related('osoba'): + setrizeni_resitele_dict[r.id] = r + for ar_id in setrizeni_resitele_id: # seznam počtu bodů daného řešitele pro jednotlivá čísla body_cisla_sezn = [] @@ -168,7 +174,7 @@ def vysledkovka_rocniku(rocnik, jen_verejne=True): # vytáhneme informace pro daného řešitele radek = RadekVysledkovkyRocniku( poradi[i], # pořadí - m.Resitel.objects.get(id=ar_id), # řešitel (z id) + setrizeni_resitele_dict[ar_id], # řešitel (z id) body_cisla_sezn, # seznam bodů za čísla setrizene_body[i], # body za ročník (spočítané výše s pořadím) resitel_odjakzivabody_slov[ar_id], # body odjakživa @@ -176,7 +182,11 @@ def vysledkovka_rocniku(rocnik, jen_verejne=True): radky_vysledkovky.append(radek) i += 1 + end = time.time() + print("Vysledkovka rocniku",end-start) + return radky_vysledkovky + class RadekVysledkovkyCisla(object): """Obsahuje věci, které se hodí vědět při konstruování výsledkovky. Umožňuje snazší práci v templatu (lepší, než seznam).""" @@ -225,17 +235,18 @@ def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None): # TODO setřídit hlavní problémy čísla podle id, ať jsou ve stejném pořadí pokaždé # pro každý hlavní problém zavedeme slovník s body za daný hlavní problém # pro jednotlivé řešitele (slovník slovníků hlavních problémů) + + print("Scitam cislo",cislo) + if hlavni_problemy is None: - hlavni_problemy = hlavni_problemy_cisla(cislo) + hlavni_problemy = hlavni_problemy_f(problemy_cisla(cislo)) def ne_clanek_ne_konfera(problem): - return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera)) + inst = problem.get_real_instance() + return not(isinstance(inst, m.Clanek) or isinstance(inst, m.Konfera)) temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy)) - def cosi(problem): - return problem.id - hlavni_problemy_slovnik = {} for hp in temata_a_spol: hlavni_problemy_slovnik[hp.id] = {} @@ -257,6 +268,7 @@ def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None): reseni_do_cisla = m.Reseni.objects.prefetch_related('problem', 'resitele', 'hodnoceni_set').filter(hodnoceni__cislo_body=cislo) + start = time.time() # projdeme všechna řešení do čísla a přičteme body každému řešiteli do celkových # bodů i do bodů za problém for reseni in reseni_do_cisla: @@ -280,13 +292,15 @@ def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None): continue pricti_body(cislobody, resitel, body) pricti_body(nadproblem_slovnik, resitel, body) + end = time.time() + print("for cykly:", end-start) return hlavni_problemy_slovnik, cislobody def secti_body_za_cislo_podle_temat(cislo, aktivni_resitele, podproblemy=None, temata=None): """ Spočítá u řešitelů body za číslo za úlohy v jednotlivých hlavních problémech (témata).""" if temata is None: - temata = hlavni_problemy_cisla(cislo) + temata = hlavni_problemy_f(problemy_cisla(cislo)) if podproblemy is None: podproblemy_v_cislu(cislo, hlavni_problemy=temata) @@ -356,7 +370,7 @@ def vysledkovka_cisla(cislo, context=None): if context is None: context = {} problemy = problemy_cisla(cislo) - hlavni_problemy = hlavni_problemy_cisla(cislo, problemy) + hlavni_problemy = hlavni_problemy_f(problemy) ## TODO možná chytřeji vybírat aktivní řešitele # aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají # u alespoň jedné hodnoty něco jiného než NULL @@ -373,7 +387,6 @@ def vysledkovka_cisla(cislo, context=None): # řešitelé setřídění podle bodů za číslo sestupně setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn] - setrizeni_resitele = [m.Resitel.objects.get(id=i) for i in setrizeni_resitele_id] # spočítáme pořadí řešitelů setrizeni_resitele_body = [dvojice[1] for dvojice in resitel_rocnikbody_sezn] @@ -384,6 +397,7 @@ def vysledkovka_cisla(cislo, context=None): i = 0 def ne_clanek_ne_konfera(problem): + return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera)) temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy)) @@ -399,6 +413,11 @@ def vysledkovka_cisla(cislo, context=None): je_nejake_ostatni = len(hlavni_problemy) - len(temata_a_spol) > 0 + setrizeni_resitele_slovnik = {} + setrizeni_resitele = m.Resitel.objects.filter(id__in=setrizeni_resitele_id).select_related('osoba') + for r in setrizeni_resitele: + setrizeni_resitele_slovnik[r.id] = r + for ar_id in setrizeni_resitele_id: # získáme seznam bodů za problémy pro daného řešitele body_problemy = [] @@ -412,7 +431,7 @@ def vysledkovka_cisla(cislo, context=None): # vytáhneme informace pro daného řešitele radek = RadekVysledkovkyCisla( poradi[i], # pořadí - m.Resitel.objects.get(id=ar_id), # řešitel (z id) + setrizeni_resitele_slovnik[ar_id], # řešitel (z id) body_problemy, # seznam bodů za hlavní problémy čísla cislobody[ar_id], # body za číslo setrizeni_resitele_body[i], # body za ročník (spočítané výše s pořadím)