From c96ada8b37fee3ac90279af19aa8294df680e97f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Thu, 10 Aug 2023 15:21:22 +0200 Subject: [PATCH] =?UTF-8?q?Vypreparov=C3=A1n=C3=AD=20views,=20templates=20?= =?UTF-8?q?a=20urls=20tvorby=20ze=20semin=C3=A1=C5=99e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mamweb/urls.py | 3 + seminar/urls.py | 57 +-- seminar/views/views_all.py | 443 +----------------- soustredeni/views.py | 2 +- .../templates/tvorba}/archiv/cisla.html | 0 .../templates/tvorba}/archiv/cislo.html | 0 .../tvorba}/archiv/cislo_vysledkovka.tex | 0 .../templates/tvorba}/archiv/obalky.tex | 0 .../templates/tvorba}/archiv/odmeny.html | 0 .../templates/tvorba}/archiv/prispevek.html | 0 .../templates/tvorba}/archiv/problem.html | 0 .../tvorba}/archiv/problem_clanek.html | 0 .../tvorba}/archiv/problem_tema.html | 0 .../tvorba}/archiv/problem_uloha.html | 0 .../tvorba}/archiv/problem_uloha_tema.html | 0 .../templates/tvorba}/archiv/rocnik.html | 0 .../tvorba}/archiv/rocnik_vysledkovka.tex | 0 .../templates/tvorba}/archiv/temata.html | 0 .../templates/tvorba}/archiv/tituly.tex | 0 .../tvorba}/clanky/organizatorske_clanky.html | 0 .../tvorba}/clanky/resitelske_clanky.html | 0 tvorba/urls.py | 92 ++++ tvorba/utils.py | 21 + tvorba/views.py | 407 ++++++++++++++++ 24 files changed, 533 insertions(+), 492 deletions(-) rename {seminar/templates/seminar => tvorba/templates/tvorba}/archiv/cisla.html (100%) rename {seminar/templates/seminar => tvorba/templates/tvorba}/archiv/cislo.html (100%) rename {seminar/templates/seminar => tvorba/templates/tvorba}/archiv/cislo_vysledkovka.tex (100%) rename {seminar/templates/seminar => tvorba/templates/tvorba}/archiv/obalky.tex (100%) rename {seminar/templates/seminar => tvorba/templates/tvorba}/archiv/odmeny.html (100%) rename {seminar/templates/seminar => tvorba/templates/tvorba}/archiv/prispevek.html (100%) rename {seminar/templates/seminar => tvorba/templates/tvorba}/archiv/problem.html (100%) rename {seminar/templates/seminar => tvorba/templates/tvorba}/archiv/problem_clanek.html (100%) rename {seminar/templates/seminar => tvorba/templates/tvorba}/archiv/problem_tema.html (100%) rename {seminar/templates/seminar => tvorba/templates/tvorba}/archiv/problem_uloha.html (100%) rename {seminar/templates/seminar => tvorba/templates/tvorba}/archiv/problem_uloha_tema.html (100%) rename {seminar/templates/seminar => tvorba/templates/tvorba}/archiv/rocnik.html (100%) rename {seminar/templates/seminar => tvorba/templates/tvorba}/archiv/rocnik_vysledkovka.tex (100%) rename {seminar/templates/seminar => tvorba/templates/tvorba}/archiv/temata.html (100%) rename {seminar/templates/seminar => tvorba/templates/tvorba}/archiv/tituly.tex (100%) rename {seminar/templates/seminar => tvorba/templates/tvorba}/clanky/organizatorske_clanky.html (100%) rename {seminar/templates/seminar => tvorba/templates/tvorba}/clanky/resitelske_clanky.html (100%) create mode 100644 tvorba/urls.py create mode 100644 tvorba/utils.py create mode 100644 tvorba/views.py diff --git a/mamweb/urls.py b/mamweb/urls.py index 016bc673..b2a0a0f0 100644 --- a/mamweb/urls.py +++ b/mamweb/urls.py @@ -17,6 +17,9 @@ urlpatterns = [ # Seminarova aplikace (ma vlastni podadresare) path('', include('seminar.urls')), + # Tvorba, Čísla, Ročníky, atd. (ma vlastni podadresare) + path('', include('tvorba.urls')), + # Odevzdavatko (ma vlastni podadresare) path('', include('odevzdavatko.urls')), diff --git a/seminar/urls.py b/seminar/urls.py index 125b1082..c95f802a 100644 --- a/seminar/urls.py +++ b/seminar/urls.py @@ -1,80 +1,27 @@ -from django.urls import path, include, re_path +from django.urls import path from . import views from personalni.utils import org_required import various.views urlpatterns = [ -# path('aktualni/temata/', views.TemataRozcestnikView), -# path('/t/', views.TematkoView), - # Organizatori path('o-nas/organizatori/', views.CojemamOrganizatoriView.as_view(), name='organizatori'), path('o-nas/organizatori/organizovali/', views.CojemamOrganizatoriStariView.as_view(), name='stari_organizatori'), - # Archiv - path('archiv/rocniky/', views.ArchivView.as_view(), name="seminar_archiv_rocniky"), - path('archiv/temata/', views.ArchivTemataView.as_view(), name="seminar_archiv_temata"), - - path('rocnik//', views.RocnikView.as_view(), name='seminar_rocnik'), - path('cislo/./', views.CisloView.as_view(), name='seminar_cislo'), - path('problem//', views.problemView, name='seminar_problem'), # Zadani # path('aktualni/zadani/', views.AktualniZadaniView.as_view(), name='seminar_aktualni_zadani'), # Dočasně ad-hoc jednoduchá věc. path('aktualni/zadani/', views.AktualniZadaniView, name='seminar_aktualni_zadani'), #path('aktualni/temata/', views.ZadaniTemataView, name='seminar_temata'), + # path('aktualni/temata/', views.TemataRozcestnikView), path('aktualni/vysledkova-listina/', views.ZadaniAktualniVysledkovkaView, name='seminar_aktualni_vysledky'), path('aktualni/rocnik/', views.AktualniRocnikRedirectView.as_view(), name='seminar_aktualni_rocnik'), - # Clanky - path('archiv/clanky/', views.ClankyResitelView.as_view(), name='clanky_resitel'), - #path('clanky/org/', views.ClankyOrganizatorView.as_view(), name='clanky_organizator'), - - # Stranky viditelne pouze pro orgy: - path( - 'rocnik//vysledkovka.tex', - org_required(views.RocnikVysledkovkaView.as_view()), - name='seminar_rocnik_vysledkovka' - ), - path( - 'rocnik//resitele.csv', - org_required(views.resiteleRocnikuCsvExportView), - name='seminar_rocnik_resitele_csv' - ), - path( - 'rocnik//tituly.tex', - org_required(views.TitulyViewRocnik), - name='seminar_rocnik_titul' - ), - path( - 'rocnik//posledni_vysledkovka.tex', - org_required(views.PosledniCisloVysledkovkaView.as_view()), - name='seminar_rocnik_posledni_vysledkovka' - ), - path( - 'cislo/./vysledkovka.tex', - org_required(views.CisloVysledkovkaView.as_view()), - name='seminar_cislo_vysledkovka' - ), - path( - 'cislo/./obalky.pdf', - org_required(views.cisloObalkyView), - name='seminar_cislo_obalky' - ), - path( - 'cislo/./tituly.tex', - org_required(views.TitulyView), - name='seminar_cislo_titul' - ), path( 'stav', org_required(various.views.StavDatabazeView), name='stav_databaze' ), - path( - 'cislo/./odmeny/./', - org_required(views.OdmenyView.as_view()), - name="seminar_archiv_odmeny"), path('', views.TitulniStranaView.as_view(), name='titulni_strana'), path('jak-resit/', views.JakResitView.as_view(), name='jak_resit'), diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index b188a900..94d9af08 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -1,40 +1,18 @@ from django.shortcuts import get_object_or_404, render -from django.http import HttpResponse from django.urls import reverse from django.core.exceptions import ObjectDoesNotExist from django.views import generic -from django.utils.translation import ugettext as _ -from django.http import Http404 -from django.db.models import Q, Sum, Count from django.views.generic.base import RedirectView -from django.core.exceptions import PermissionDenied - -from tvorba.models import Rocnik, Cislo, Deadline, Problem, Tema, Clanek -from seminar.models.nastaveni import Nastaveni -from personalni.models import Resitel, Organizator -from seminar.models.novinky import Novinky -#from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva -from treenode import treelib -import treenode.templatetags as tnltt -import treenode.serializers as vr -from vysledkovky.utils import body_resitelu, VysledkovkaCisla, \ - VysledkovkaRocniku, VysledkovkaDoTeXu - -from datetime import date, datetime + +from tvorba.models import Rocnik, Deadline, Tema +from seminar.models import * +from personalni.models import Organizator + +from vysledkovky.utils import VysledkovkaRocniku + from django.utils import timezone -from itertools import groupby -from collections import OrderedDict -import tempfile -import subprocess -import shutil -import os -import os.path as op -from django.conf import settings -import unicodedata -import time from collections.abc import Sequence -from personalni.utils import aktivniResitele # ze starého modelu #def verejna_temata(rocnik): @@ -46,38 +24,6 @@ from personalni.utils import aktivniResitele #def temata_v_rocniku(rocnik): # return Problem.objects.filter(typ=Problem.TYP_TEMA, rocnik=rocnik) -def get_problemy_k_tematu(tema): - return Problem.objects.filter(nadproblem = tema) - - -# 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 #class AktualniZadaniView(generic.TemplateView): @@ -132,20 +78,6 @@ def ZadaniTemataView(request): # ) # # -# -#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', {}) -# # #def TemataRozcestnikView(request): # print("=============================================") @@ -291,367 +223,6 @@ class CojemamOrganizatoriStariView(generic.ListView): queryset = Organizator.objects.exclude( id__in=aktivniOrganizatori()).order_by('-organizuje_do') -### Archiv - - -class ArchivView(generic.ListView): - model = Rocnik - template_name='seminar/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 = 'seminar/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( - utils.resi_v_rocniku( - get_object_or_404(Rocnik, rocnik=rocnik) - ) - ) - - -# 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 - - - -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 = 'seminar/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 = 'seminar/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 = 'seminar/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 = 'seminar/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 = 'seminar/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 = 'seminar/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,'seminar/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, 'seminar/archiv/tituly.tex', - {'resitele': resitele,'jmenovci':jmenovci},content_type="text/plain") - - -### Články -def group_by_rocnik(clanky): - ''' Vezme zadaný seznam článků a seskupí je podle ročníku. - Vrátí seznam seznamů článků ze stejného ročníku.''' - if len(clanky) == 0: - return clanky - clanky.order_by('cislo__rocnik__rocnik') - skupiny_clanku = [] - skupina = [] - - rocnik = clanky.first().cislo.rocnik.rocnik # první ročník - for clanek in clanky: - if clanek.cislo.rocnik.rocnik == rocnik: - skupina.append(clanek) - else: - skupiny_clanku.append(skupina) - skupina = [] - skupina.append(clanek) - rocnik = clanek.cislo.rocnik.rocnik - skupiny_clanku.append(skupina) - return skupiny_clanku - - -# 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 = 'seminar/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 = 'seminar/clanky/organizatorske_clanky.html' -# queryset = Problem.objects.filter(stav=Problem.STAV_ZADANY).select_related('cislo_zadani__rocnik').order_by('-cislo_zadani__rocnik__rocnik', 'kod') # Interní, nemá se nikdy objevit v urls (jinak to účastníci vytrolí) diff --git a/soustredeni/views.py b/soustredeni/views.py index fa267e94..ba442011 100644 --- a/soustredeni/views.py +++ b/soustredeni/views.py @@ -12,7 +12,7 @@ import shutil import subprocess from pathlib import Path -from seminar.views import obalkyView +from tvorba.views import obalkyView class SoustredeniListView(generic.ListView): diff --git a/seminar/templates/seminar/archiv/cisla.html b/tvorba/templates/tvorba/archiv/cisla.html similarity index 100% rename from seminar/templates/seminar/archiv/cisla.html rename to tvorba/templates/tvorba/archiv/cisla.html diff --git a/seminar/templates/seminar/archiv/cislo.html b/tvorba/templates/tvorba/archiv/cislo.html similarity index 100% rename from seminar/templates/seminar/archiv/cislo.html rename to tvorba/templates/tvorba/archiv/cislo.html diff --git a/seminar/templates/seminar/archiv/cislo_vysledkovka.tex b/tvorba/templates/tvorba/archiv/cislo_vysledkovka.tex similarity index 100% rename from seminar/templates/seminar/archiv/cislo_vysledkovka.tex rename to tvorba/templates/tvorba/archiv/cislo_vysledkovka.tex diff --git a/seminar/templates/seminar/archiv/obalky.tex b/tvorba/templates/tvorba/archiv/obalky.tex similarity index 100% rename from seminar/templates/seminar/archiv/obalky.tex rename to tvorba/templates/tvorba/archiv/obalky.tex diff --git a/seminar/templates/seminar/archiv/odmeny.html b/tvorba/templates/tvorba/archiv/odmeny.html similarity index 100% rename from seminar/templates/seminar/archiv/odmeny.html rename to tvorba/templates/tvorba/archiv/odmeny.html diff --git a/seminar/templates/seminar/archiv/prispevek.html b/tvorba/templates/tvorba/archiv/prispevek.html similarity index 100% rename from seminar/templates/seminar/archiv/prispevek.html rename to tvorba/templates/tvorba/archiv/prispevek.html diff --git a/seminar/templates/seminar/archiv/problem.html b/tvorba/templates/tvorba/archiv/problem.html similarity index 100% rename from seminar/templates/seminar/archiv/problem.html rename to tvorba/templates/tvorba/archiv/problem.html diff --git a/seminar/templates/seminar/archiv/problem_clanek.html b/tvorba/templates/tvorba/archiv/problem_clanek.html similarity index 100% rename from seminar/templates/seminar/archiv/problem_clanek.html rename to tvorba/templates/tvorba/archiv/problem_clanek.html diff --git a/seminar/templates/seminar/archiv/problem_tema.html b/tvorba/templates/tvorba/archiv/problem_tema.html similarity index 100% rename from seminar/templates/seminar/archiv/problem_tema.html rename to tvorba/templates/tvorba/archiv/problem_tema.html diff --git a/seminar/templates/seminar/archiv/problem_uloha.html b/tvorba/templates/tvorba/archiv/problem_uloha.html similarity index 100% rename from seminar/templates/seminar/archiv/problem_uloha.html rename to tvorba/templates/tvorba/archiv/problem_uloha.html diff --git a/seminar/templates/seminar/archiv/problem_uloha_tema.html b/tvorba/templates/tvorba/archiv/problem_uloha_tema.html similarity index 100% rename from seminar/templates/seminar/archiv/problem_uloha_tema.html rename to tvorba/templates/tvorba/archiv/problem_uloha_tema.html diff --git a/seminar/templates/seminar/archiv/rocnik.html b/tvorba/templates/tvorba/archiv/rocnik.html similarity index 100% rename from seminar/templates/seminar/archiv/rocnik.html rename to tvorba/templates/tvorba/archiv/rocnik.html diff --git a/seminar/templates/seminar/archiv/rocnik_vysledkovka.tex b/tvorba/templates/tvorba/archiv/rocnik_vysledkovka.tex similarity index 100% rename from seminar/templates/seminar/archiv/rocnik_vysledkovka.tex rename to tvorba/templates/tvorba/archiv/rocnik_vysledkovka.tex diff --git a/seminar/templates/seminar/archiv/temata.html b/tvorba/templates/tvorba/archiv/temata.html similarity index 100% rename from seminar/templates/seminar/archiv/temata.html rename to tvorba/templates/tvorba/archiv/temata.html diff --git a/seminar/templates/seminar/archiv/tituly.tex b/tvorba/templates/tvorba/archiv/tituly.tex similarity index 100% rename from seminar/templates/seminar/archiv/tituly.tex rename to tvorba/templates/tvorba/archiv/tituly.tex diff --git a/seminar/templates/seminar/clanky/organizatorske_clanky.html b/tvorba/templates/tvorba/clanky/organizatorske_clanky.html similarity index 100% rename from seminar/templates/seminar/clanky/organizatorske_clanky.html rename to tvorba/templates/tvorba/clanky/organizatorske_clanky.html diff --git a/seminar/templates/seminar/clanky/resitelske_clanky.html b/tvorba/templates/tvorba/clanky/resitelske_clanky.html similarity index 100% rename from seminar/templates/seminar/clanky/resitelske_clanky.html rename to tvorba/templates/tvorba/clanky/resitelske_clanky.html diff --git a/tvorba/urls.py b/tvorba/urls.py new file mode 100644 index 00000000..c0516110 --- /dev/null +++ b/tvorba/urls.py @@ -0,0 +1,92 @@ +from django.urls import path +from personalni.utils import org_required +from . import views + +urlpatterns = [ + # Archiv + path( + 'archiv/rocniky/', + views.ArchivView.as_view(), + name="seminar_archiv_rocniky", + ), + path( + 'archiv/temata/', + views.ArchivTemataView.as_view(), + name="seminar_archiv_temata", + ), + # path( + # '/t/', + # views.TematkoView + # ), + + path( + 'rocnik//', + views.RocnikView.as_view(), + name='seminar_rocnik', + ), + path( + 'cislo/./', + views.CisloView.as_view(), + name='seminar_cislo', + ), + path( + 'problem//', + views.problemView, + name='seminar_problem', + ), + + # Clanky + path( + 'archiv/clanky/', + views.ClankyResitelView.as_view(), + name='clanky_resitel' + ), + # path( + # 'clanky/org/', + # views.ClankyOrganizatorView.as_view(), + # name='clanky_organizator' + # ), + + + # Stranky viditelne pouze pro orgy: + path( + 'rocnik//vysledkovka.tex', + org_required(views.RocnikVysledkovkaView.as_view()), + name='seminar_rocnik_vysledkovka', + ), + path( + 'rocnik//resitele.csv', + org_required(views.resiteleRocnikuCsvExportView), + name='seminar_rocnik_resitele_csv', + ), + path( + 'rocnik//tituly.tex', + org_required(views.TitulyViewRocnik), + name='seminar_rocnik_titul', + ), + path( + 'rocnik//posledni_vysledkovka.tex', + org_required(views.PosledniCisloVysledkovkaView.as_view()), + name='seminar_rocnik_posledni_vysledkovka', + ), + path( + 'cislo/./vysledkovka.tex', + org_required(views.CisloVysledkovkaView.as_view()), + name='seminar_cislo_vysledkovka', + ), + path( + 'cislo/./obalky.pdf', + org_required(views.cisloObalkyView), + name='seminar_cislo_obalky', + ), + path( + 'cislo/./tituly.tex', + org_required(views.TitulyView), + name='seminar_cislo_titul', + ), + path( + 'cislo/./odmeny/./', + org_required(views.OdmenyView.as_view()), + name="seminar_archiv_odmeny", + ), +] diff --git a/tvorba/utils.py b/tvorba/utils.py new file mode 100644 index 00000000..37d28577 --- /dev/null +++ b/tvorba/utils.py @@ -0,0 +1,21 @@ + +def group_by_rocnik(clanky): + """ Vezme zadaný seznam článků a seskupí je podle ročníku. + Vrátí seznam seznamů článků ze stejného ročníku.""" + if len(clanky) == 0: + return clanky + clanky.order_by('cislo__rocnik__rocnik') + skupiny_clanku = [] + skupina = [] + + rocnik = clanky.first().cislo.rocnik.rocnik # první ročník + for clanek in clanky: + if clanek.cislo.rocnik.rocnik == rocnik: + skupina.append(clanek) + else: + skupiny_clanku.append(skupina) + skupina = [] + skupina.append(clanek) + rocnik = clanek.cislo.rocnik.rocnik + skupiny_clanku.append(skupina) + return skupiny_clanku diff --git a/tvorba/views.py b/tvorba/views.py new file mode 100644 index 00000000..c262c31b --- /dev/null +++ b/tvorba/views.py @@ -0,0 +1,407 @@ +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