import tempfile import subprocess import shutil import os import os.path as op import unicodedata from itertools import groupby from collections import OrderedDict from django.shortcuts import get_object_or_404, render from django.http import HttpResponse from django.views import generic from django.utils.translation import ugettext as _ from django.http import Http404 from django.db.models import Q from django.conf import settings from personalni.models import Resitel from .models import * from .utils import * from personalni.utils import aktivniResitele, resi_v_rocniku from vysledkovky.utils import body_resitelu, VysledkovkaCisla, \ VysledkovkaRocniku, VysledkovkaDoTeXu ### Archiv class ArchivView(generic.ListView): model = Rocnik template_name = 'tvorba/archiv/cisla.html' def get_context_data(self, **kwargs): context = super(ArchivView, self).get_context_data(**kwargs) cisla = Cislo.objects.filter(poradi=1) if not self.request.user.je_org: cisla = cisla.filter(verejne_db=True) urls = {} for i, c in enumerate(cisla): # Výchozí nastavení if c.rocnik not in urls: urls[c.rocnik] = op.join(settings.STATIC_URL, "images", "no-picture.png") # NOTE: tohle možná nastavuje poslední titulku if c.titulka_nahled: urls[c.rocnik] = c.titulka_nahled.url context["object_list"] = urls return context class RocnikView(generic.DetailView): model = Rocnik template_name = 'tvorba/archiv/rocnik.html' # Vlastni ziskavani objektu z databaze podle (Rocnik.rocnik) def get_object(self, queryset=None): if queryset is None: queryset = self.get_queryset() return get_object_or_404(queryset, rocnik=self.kwargs.get('rocnik')) def get_context_data(self, **kwargs): context = super(RocnikView, self).get_context_data(**kwargs) context["vysledkovka"] = VysledkovkaRocniku(context["rocnik"], True) context["neprazdna_vysledkovka"] = len(context['vysledkovka'].cisla_rocniku) != 0 context["vysledkovka_neverejna"] = VysledkovkaRocniku(context["rocnik"], False) return context def resiteleRocnikuCsvExportView(request, rocnik): from personalni.views import dataResiteluCsvResponse assert request.method in ('GET', 'HEAD') return dataResiteluCsvResponse( resi_v_rocniku( get_object_or_404(Rocnik, rocnik=rocnik) ) ) class CisloView(generic.DetailView): # FIXME zobrazování témátek a vůbec, teď je tam jen odkaz na číslo v pdf model = Cislo template_name = 'tvorba/archiv/cislo.html' # Vlastni ziskavani objektu z databaze podle (Rocnik.rocnik) def get_object(self, queryset=None): if queryset is None: queryset = self.get_queryset() rocnik_arg = self.kwargs.get('rocnik') poradi_arg = self.kwargs.get('cislo') queryset = queryset.filter(rocnik__rocnik=rocnik_arg, poradi=poradi_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 def get_context_data(self, **kwargs): context = super(CisloView, self).get_context_data(**kwargs) cislo = context['cislo'] context['prevcislo'] = Cislo.objects.filter((Q(rocnik__lt=self.object.rocnik) | Q(poradi__lt=self.object.poradi)) & Q(rocnik__lte=self.object.rocnik)).first() deadliny = Deadline.objects.filter(cislo=cislo).reverse() deadliny_s_vysledkovkami = [] nadpisy = { Deadline.TYP_CISLA: "Výsledkovka", Deadline.TYP_PRVNI: "Výsledkovka do prvního deadlinu", Deadline.TYP_PRVNI_A_SOUS: "Výsledkovka do prvního deadlinu a deadlinu pro účast na soustředění", Deadline.TYP_SOUS: "Výsledkovka do deadlinu pro účast na soustředění", } for deadline in deadliny: if self.request.user.je_org | deadline.verejna_vysledkovka: deadliny_s_vysledkovkami.append((deadline, nadpisy[deadline.typ], VysledkovkaCisla(cislo, not self.request.user.je_org, deadline))) context['deadliny_s_vysledkovkami'] = deadliny_s_vysledkovkami return context class ArchivTemataView(generic.ListView): model = Problem template_name = 'tvorba/archiv/temata.html' queryset = Tema.objects.filter(stav=Problem.STAV_ZADANY).select_related('rocnik').order_by('rocnik', 'kod') def get_context_data(self, *args, **kwargs): ctx = super().get_context_data(*args, **kwargs) ctx['rocniky'] = OrderedDict() for rocnik, temata in groupby(ctx['object_list'], lambda tema: tema.rocnik): ctx['rocniky'][rocnik] = list(temata) return ctx class OdmenyView(generic.TemplateView): template_name = 'tvorba/archiv/odmeny.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) fromcislo = get_object_or_404(Cislo, rocnik=self.kwargs.get('frocnik'), poradi=self.kwargs.get('fcislo')) tocislo = get_object_or_404(Cislo, rocnik=self.kwargs.get('trocnik'), poradi=self.kwargs.get('tcislo')) resitele = aktivniResitele(tocislo) def get_diff(from_deadline: Deadline, to_deadline: Deadline): frombody = body_resitelu(resitele=resitele, jen_verejne=False, do=from_deadline) tobody = body_resitelu(resitele=resitele, jen_verejne=False, do=to_deadline) outlist = [] for resitel in resitele: fbody = frombody.get(resitel.id, 0) tbody = tobody.get(resitel.id, 0) ftitul = resitel.get_titul(fbody) ttitul = resitel.get_titul(tbody) if ftitul != ttitul: outlist.append({'jmeno': resitel.osoba.plne_jmeno(), 'ftitul': ftitul, 'ttitul': ttitul}) return outlist def posledni_deadline_oprava(cislo: Cislo) -> Deadline: posledni_deadline = cislo.posledni_deadline if posledni_deadline is None: return Deadline.objects.filter(Q(cislo__poradi__lt=cislo.poradi, cislo__rocnik=cislo.rocnik) | Q(cislo__rocnik__rocnik__lt=cislo.rocnik.rocnik)).order_by("deadline").last() return posledni_deadline context["from_cislo"] = fromcislo context["to_cislo"] = tocislo from_deadline = posledni_deadline_oprava(fromcislo) to_deadline = posledni_deadline_oprava(tocislo) context["from_deadline"] = from_deadline context["to_deadline"] = to_deadline context["zmeny"] = get_diff(from_deadline, to_deadline) return context ### Generovani vysledkovky class CisloVysledkovkaView(CisloView): """View vytvořené pro stránku zobrazující výsledkovku čísla v TeXu.""" model = Cislo template_name = 'tvorba/archiv/cislo_vysledkovka.tex' # content_type = 'application/x-tex; charset=UTF8' # umozni rovnou stahnout TeXovsky dokument content_type = 'text/plain; charset=UTF8' # vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani def get_context_data(self, **kwargs): context = super(CisloVysledkovkaView, self).get_context_data() cislo = context['cislo'] cislopred = cislo.predchozi() if cislopred is not None: context['vysledkovka'] = VysledkovkaDoTeXu( cislo, od_vyjma=cislopred.zlomovy_deadline_pro_papirove_cislo(), do_vcetne=cislo.zlomovy_deadline_pro_papirove_cislo(), ) else: context['vysledkovka'] = VysledkovkaCisla( cislo, jen_verejne=False, do_deadlinu=cislo.zlomovy_deadline_pro_papirove_cislo(), ) return context # Podle předchozího class PosledniCisloVysledkovkaView(generic.DetailView): """View vytvořené pro zobrazení výsledkovky posledního čísla v TeXu.""" model = Rocnik template_name = 'tvorba/archiv/cislo_vysledkovka.tex' content_type = 'text/plain; charset=UTF8' 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 def get_context_data(self, **kwargs): context = super(PosledniCisloVysledkovkaView, self).get_context_data() rocnik = context['rocnik'] cislo = rocnik.cisla.order_by("poradi").filter(deadline_v_cisle__isnull=False).last() if cislo is None: raise Http404(f"Ročník {rocnik.rocnik} nemá číslo s deadlinem.") cislopred = cislo.predchozi() context['vysledkovka'] = VysledkovkaDoTeXu( cislo, od_vyjma=cislopred.zlomovy_deadline_pro_papirove_cislo(), do_vcetne=cislo.deadline_v_cisle.order_by("deadline").last(), ) return context class RocnikVysledkovkaView(RocnikView): """ View vytvořené pro stránku zobrazující výsledkovku ročníku v TeXu.""" model = Rocnik template_name = 'tvorba/archiv/rocnik_vysledkovka.tex' # content_type = 'application/x-tex; charset=UTF8' # umozni rovnou stahnout TeXovsky dokument content_type = 'text/plain; charset=UTF8' # vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani def cisloObalkyView(request, rocnik, cislo): realne_cislo = get_object_or_404(Cislo, poradi=cislo, rocnik__rocnik=rocnik) return obalkyView(request, aktivniResitele(realne_cislo)) def obalkyView(request, resitele): tex = render(request, 'tvorba/archiv/obalky.tex', {'resitele': resitele}).content tempdir = tempfile.mkdtemp() with open(tempdir+"/obalky.tex", "w") as texfile: texfile.write(tex.decode()) shutil.copy(os.path.join(settings.STATIC_ROOT, 'seminar/lisak.pdf'), tempdir) subprocess.call(["pdflatex", "obalky.tex"], cwd=tempdir) with open(tempdir+"/obalky.pdf", "rb") as pdffile: response = HttpResponse(pdffile.read(), content_type='application/pdf') shutil.rmtree(tempdir) return response ### Tituly def TitulyViewRocnik(request, rocnik): return TitulyView(request, rocnik, None) def TitulyView(request, rocnik, cislo): """ View pro stažení makra titulů v TeXu.""" rocnik_obj = get_object_or_404(Rocnik, rocnik=rocnik) resitele = Resitel.objects.filter(rok_maturity__gte=rocnik_obj.prvni_rok) asciijmena = [] jmenovci = False # detekuje, zda jsou dva řešitelé jmenovci (modulo nabodeníčka), # pokud ano, vrátí se jako true if cislo is not None: cislo_obj = get_object_or_404(Cislo, rocnik=rocnik_obj, poradi=cislo) slovnik_s_body = body_resitelu(do=cislo_obj.zlomovy_deadline_pro_papirove_cislo(), jen_verejne=False) else: slovnik_s_body = body_resitelu(do=Deadline.objects.filter(cislo__rocnik=rocnik_obj).last(), jen_verejne=False) for resitel in resitele: resitel.titul = resitel.get_titul(slovnik_s_body[resitel.id]) jmeno = resitel.osoba.jmeno+resitel.osoba.prijmeni # převedeme jména a příjmení řešitelů do ASCII ascii_jmeno_bytes = unicodedata.normalize('NFKD', jmeno).encode("ascii", "ignore") # vrátí se byte string, převedeme na standardní string ascii_jmeno_divnoznaky = str(ascii_jmeno_bytes, "utf-8", "ignore").replace(" ", "") resitel.ascii = ''.join(a for a in ascii_jmeno_divnoznaky if a.isalnum()) if resitel.ascii not in asciijmena: asciijmena.append(resitel.ascii) else: jmenovci = True return render( request, 'tvorba/archiv/tituly.tex', {'resitele': resitele, 'jmenovci': jmenovci}, content_type="text/plain", ) ### Články # FIXME: clanky jsou vsechny, pokud budou i neresitelske, tak se take zobrazi # FIXME: Původně tu byl kód přímo v těle třídy, což rozbíjelo migrace. Opravil jsem, ale vůbec nevím, jestli to funguje. class ClankyResitelView(generic.ListView): model = Problem template_name = 'tvorba/clanky/resitelske_clanky.html' # FIXME: QuerySet není pole! def get_queryset(self): clanky = Clanek.objects.filter(stav=Problem.STAV_VYRESENY).select_related('cislo__rocnik').order_by('-cislo__rocnik__rocnik') queryset = [] skupiny_clanku = group_by_rocnik(clanky) for skupina in skupiny_clanku: skupina.sort(key=lambda clanek: clanek.kod_v_rocniku) for clanek in skupina: queryset.append(clanek) return queryset # FIXME: pokud chceme orgoclanky, tak nejak zavest do modelu a podle toho odkomentovat a upravit # class ClankyOrganizatorView(generic.ListView): # model = Problem # template_name = 'tvorba/clanky/organizatorske_clanky.html' # queryset = Problem.objects.filter(stav=Problem.STAV_ZADANY).select_related('cislo_zadani__rocnik').order_by('-cislo_zadani__rocnik__rocnik', 'kod') # def TematkoView(request, rocnik, tematko): # nastaveni = Nastaveni.objects.first() # rocnik_object = Rocnik.objects.filter(rocnik=rocnik) # tematko_object = Tema.objects.filter(rocnik=rocnik_object[0], kod=tematko) # seznam = vytahniZLesaSeznam(tematko_object[0], nastaveni.aktualni_rocnik().rocniknode) # for node, depth in seznam: # if node.isinstance(node, KonferaNode): # raise Exception("Not implemented yet") # if node.isinstance(node, PohadkaNode): # Mohu ignorovat, má pod sebou # pass # # return render(request, 'seminar/tematka/toaletak.html', {}) from seminar.views.docasne import problemView # FIXME: Pozor, níž je ještě jeden ProblemView! # class ProblemView(generic.DetailView): # model = Problem # # Zkopírujeme template_name od TreeNodeView, protože jsme prakticky jen trošku upravený TreeNodeView # template_name = TreeNodeView.template_name # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # user = self.request.user # # Teď potřebujeme doplnit tnldata do kontextu. # # Ošklivý type switch, hezčí by bylo udělat to polymorfni. FIXME. # if False: # # Hezčí formátování zbytku :-P # pass # elif isinstance(self.object, Clanek) or isinstance(self.object, Konfera): # # Tyhle Problémy mají ŘešeníNode # context['tnldata'] = TNLData.from_treenode(self.object.reseninode,user) # elif isinstance(self.object, Uloha): # # FIXME: Teď vždycky zobrazujeme i vzorák! Možná by bylo hezčí/lepší mít to stejně jako pro Téma: procházet jen dosažitelné z Ročníku / čísla / whatever # tnl_zadani = TNLData.from_treenode(self.object.ulohazadaninode,user) # tnl_vzorak = TNLData.from_treenode(self.object.ulohavzoraknode,user) # context['tnldata'] = TNLData.from_tnldata_list([tnl_zadani, tnl_vzorak]) # elif isinstance(self.object, Tema): # rocniknode = self.object.rocnik.rocniknode # context['tnldata'] = TNLData.filter_treenode(rocniknode, lambda x: isinstance(x, TemaVCisleNode)) # else: # raise ValueError("Obecný problém nejde zobrazit.") # return context # FIXME: Pozor, výš je ještě jeden ProblemView! # class ProblemView(generic.DetailView): # model = Problem # # # Používáme funkci, protože přímo template_name neumí mít v přiřazení dost logiky. Ledaže by se to udělalo polymorfně... # def get_template_names(self, **kwargs): # # FIXME: Switch podle typu není hezký, ale nechtělo se mi to přepisovat celé. Správně by se tohle mělo řešit polymorfismem. # spravne_templaty = { # Uloha: "uloha", # Tema: "tema", # Konfera: "konfera", # Clanek: "clanek", # } # context = super().get_context_data(**kwargs) # return ['seminar/archiv/problem_' + spravne_templaty[context['object'].__class__] + '.html'] # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # # Musí se používat context['object'], protože nevíme, jestli dostaneme úložku, téma, článek, .... a tyhle věci vyrábějí různé klíče. # if not context['object'].verejne() and not self.request.user.je_org: # raise PermissionDenied() # if isinstance(context['object'], Clanek): # context['reseni'] = Reseni.objects.filter(problem=context['object']).select_related('resitel').order_by('resitel__prijmeni') # return context