Browse Source

Vypreparování views, templates a urls tvorby ze semináře

v3
Jonas Havelka 1 year ago
parent
commit
c96ada8b37
  1. 3
      mamweb/urls.py
  2. 57
      seminar/urls.py
  3. 443
      seminar/views/views_all.py
  4. 2
      soustredeni/views.py
  5. 0
      tvorba/templates/tvorba/archiv/cisla.html
  6. 0
      tvorba/templates/tvorba/archiv/cislo.html
  7. 0
      tvorba/templates/tvorba/archiv/cislo_vysledkovka.tex
  8. 0
      tvorba/templates/tvorba/archiv/obalky.tex
  9. 0
      tvorba/templates/tvorba/archiv/odmeny.html
  10. 0
      tvorba/templates/tvorba/archiv/prispevek.html
  11. 0
      tvorba/templates/tvorba/archiv/problem.html
  12. 0
      tvorba/templates/tvorba/archiv/problem_clanek.html
  13. 0
      tvorba/templates/tvorba/archiv/problem_tema.html
  14. 0
      tvorba/templates/tvorba/archiv/problem_uloha.html
  15. 0
      tvorba/templates/tvorba/archiv/problem_uloha_tema.html
  16. 0
      tvorba/templates/tvorba/archiv/rocnik.html
  17. 0
      tvorba/templates/tvorba/archiv/rocnik_vysledkovka.tex
  18. 0
      tvorba/templates/tvorba/archiv/temata.html
  19. 0
      tvorba/templates/tvorba/archiv/tituly.tex
  20. 0
      tvorba/templates/tvorba/clanky/organizatorske_clanky.html
  21. 0
      tvorba/templates/tvorba/clanky/resitelske_clanky.html
  22. 92
      tvorba/urls.py
  23. 21
      tvorba/utils.py
  24. 407
      tvorba/views.py

3
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')),

57
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('<int:rocnik>/t<int:tematko>/', 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/<int:rocnik>/', views.RocnikView.as_view(), name='seminar_rocnik'),
path('cislo/<int:rocnik>.<str:cislo>/', views.CisloView.as_view(), name='seminar_cislo'),
path('problem/<int:pk>/', 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/<int:rocnik>/vysledkovka.tex',
org_required(views.RocnikVysledkovkaView.as_view()),
name='seminar_rocnik_vysledkovka'
),
path(
'rocnik/<int:rocnik>/resitele.csv',
org_required(views.resiteleRocnikuCsvExportView),
name='seminar_rocnik_resitele_csv'
),
path(
'rocnik/<int:rocnik>/tituly.tex',
org_required(views.TitulyViewRocnik),
name='seminar_rocnik_titul'
),
path(
'rocnik/<int:rocnik>/posledni_vysledkovka.tex',
org_required(views.PosledniCisloVysledkovkaView.as_view()),
name='seminar_rocnik_posledni_vysledkovka'
),
path(
'cislo/<int:rocnik>.<str:cislo>/vysledkovka.tex',
org_required(views.CisloVysledkovkaView.as_view()),
name='seminar_cislo_vysledkovka'
),
path(
'cislo/<int:rocnik>.<str:cislo>/obalky.pdf',
org_required(views.cisloObalkyView),
name='seminar_cislo_obalky'
),
path(
'cislo/<int:rocnik>.<str:cislo>/tituly.tex',
org_required(views.TitulyView),
name='seminar_cislo_titul'
),
path(
'stav',
org_required(various.views.StavDatabazeView),
name='stav_databaze'
),
path(
'cislo/<int:trocnik>.<str:tcislo>/odmeny/<int:frocnik>.<str:fcislo>/',
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'),

443
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)<F12>:
# 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í)

2
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):

0
seminar/templates/seminar/archiv/cisla.html → tvorba/templates/tvorba/archiv/cisla.html

0
seminar/templates/seminar/archiv/cislo.html → tvorba/templates/tvorba/archiv/cislo.html

0
seminar/templates/seminar/archiv/cislo_vysledkovka.tex → tvorba/templates/tvorba/archiv/cislo_vysledkovka.tex

0
seminar/templates/seminar/archiv/obalky.tex → tvorba/templates/tvorba/archiv/obalky.tex

0
seminar/templates/seminar/archiv/odmeny.html → tvorba/templates/tvorba/archiv/odmeny.html

0
seminar/templates/seminar/archiv/prispevek.html → tvorba/templates/tvorba/archiv/prispevek.html

0
seminar/templates/seminar/archiv/problem.html → tvorba/templates/tvorba/archiv/problem.html

0
seminar/templates/seminar/archiv/problem_clanek.html → tvorba/templates/tvorba/archiv/problem_clanek.html

0
seminar/templates/seminar/archiv/problem_tema.html → tvorba/templates/tvorba/archiv/problem_tema.html

0
seminar/templates/seminar/archiv/problem_uloha.html → tvorba/templates/tvorba/archiv/problem_uloha.html

0
seminar/templates/seminar/archiv/problem_uloha_tema.html → tvorba/templates/tvorba/archiv/problem_uloha_tema.html

0
seminar/templates/seminar/archiv/rocnik.html → tvorba/templates/tvorba/archiv/rocnik.html

0
seminar/templates/seminar/archiv/rocnik_vysledkovka.tex → tvorba/templates/tvorba/archiv/rocnik_vysledkovka.tex

0
seminar/templates/seminar/archiv/temata.html → tvorba/templates/tvorba/archiv/temata.html

0
seminar/templates/seminar/archiv/tituly.tex → tvorba/templates/tvorba/archiv/tituly.tex

0
seminar/templates/seminar/clanky/organizatorske_clanky.html → tvorba/templates/tvorba/clanky/organizatorske_clanky.html

0
seminar/templates/seminar/clanky/resitelske_clanky.html → tvorba/templates/tvorba/clanky/resitelske_clanky.html

92
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(
# '<int:rocnik>/t<int:tematko>/',
# views.TematkoView
# ),
path(
'rocnik/<int:rocnik>/',
views.RocnikView.as_view(),
name='seminar_rocnik',
),
path(
'cislo/<int:rocnik>.<str:cislo>/',
views.CisloView.as_view(),
name='seminar_cislo',
),
path(
'problem/<int:pk>/',
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/<int:rocnik>/vysledkovka.tex',
org_required(views.RocnikVysledkovkaView.as_view()),
name='seminar_rocnik_vysledkovka',
),
path(
'rocnik/<int:rocnik>/resitele.csv',
org_required(views.resiteleRocnikuCsvExportView),
name='seminar_rocnik_resitele_csv',
),
path(
'rocnik/<int:rocnik>/tituly.tex',
org_required(views.TitulyViewRocnik),
name='seminar_rocnik_titul',
),
path(
'rocnik/<int:rocnik>/posledni_vysledkovka.tex',
org_required(views.PosledniCisloVysledkovkaView.as_view()),
name='seminar_rocnik_posledni_vysledkovka',
),
path(
'cislo/<int:rocnik>.<str:cislo>/vysledkovka.tex',
org_required(views.CisloVysledkovkaView.as_view()),
name='seminar_cislo_vysledkovka',
),
path(
'cislo/<int:rocnik>.<str:cislo>/obalky.pdf',
org_required(views.cisloObalkyView),
name='seminar_cislo_obalky',
),
path(
'cislo/<int:rocnik>.<str:cislo>/tituly.tex',
org_required(views.TitulyView),
name='seminar_cislo_titul',
),
path(
'cislo/<int:trocnik>.<str:tcislo>/odmeny/<int:frocnik>.<str:fcislo>/',
org_required(views.OdmenyView.as_view()),
name="seminar_archiv_odmeny",
),
]

21
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

407
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)<F12>:
# 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
Loading…
Cancel
Save