Merge branch 'master' into kontaktnicek_pro_vsecny
This commit is contained in:
commit
fa00652a69
7 changed files with 142 additions and 57 deletions
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
{% for rocnik, hodnoceni in podle_rocniku %}
|
{% for rocnik, hodnoceni, suma_bodu in podle_rocniku %}
|
||||||
<h1>Ročník {{ rocnik }}</h1>
|
<h1>Ročník {{ rocnik }}</h1>
|
||||||
<table class="moje_reseni plne_ohranicena_tabulka">
|
<table class="moje_reseni plne_ohranicena_tabulka">
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
<p>Celkový počet bodů {{suma_bodu}}</p>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -356,7 +356,12 @@ class PrehledOdevzdanychReseni(ListView):
|
||||||
# Chceme to mít seřazené, takže místo comphrerehsion ručně postavíme pole polí. Django templates neumí použít OrderedDict :-/
|
# Chceme to mít seřazené, takže místo comphrerehsion ručně postavíme pole polí. Django templates neumí použít OrderedDict :-/
|
||||||
podle_rocniku = []
|
podle_rocniku = []
|
||||||
for rocnik, hodnoceni in groupby(ctx['object_list'], lambda ho: ho.deadline_body.cislo.rocnik if ho.deadline_body is not None else None):
|
for rocnik, hodnoceni in groupby(ctx['object_list'], lambda ho: ho.deadline_body.cislo.rocnik if ho.deadline_body is not None else None):
|
||||||
podle_rocniku.append((rocnik, list(hodnoceni)))
|
suma_bodu = 0
|
||||||
|
hodnoceni = list(hodnoceni)
|
||||||
|
for i in hodnoceni :
|
||||||
|
if i.body != None : suma_bodu += i.body
|
||||||
|
podle_rocniku.append((rocnik, hodnoceni, suma_bodu))
|
||||||
|
|
||||||
ctx['podle_rocniku'] = reversed(podle_rocniku) # Od nejnovějšího ročníku
|
ctx['podle_rocniku'] = reversed(podle_rocniku) # Od nejnovějšího ročníku
|
||||||
# TODO: Umožnit stažení / zobrazení řešení
|
# TODO: Umožnit stažení / zobrazení řešení
|
||||||
return ctx
|
return ctx
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
{% for u in ucastnici %}
|
{% for u in ucastnici %}
|
||||||
{% with o=u.osoba %}
|
{% with o=u.resitel.osoba %}
|
||||||
\stvrzenka{{o.jmeno|sloz}}{{o.prijmeni|sloz}}
|
\stvrzenka{{o.jmeno|sloz}}{{o.prijmeni|sloz}}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -26,12 +26,12 @@ urlpatterns = [
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
'export_ucastniku',
|
'export_ucastniku',
|
||||||
org_required(views.soustredeniUcastniciExportView),
|
org_required(views.SoustredeniUcastniciExportView.as_view()),
|
||||||
name='soustredeni_ucastnici_export'
|
name='soustredeni_ucastnici_export'
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
'stvrzenky.pdf',
|
'stvrzenky.pdf',
|
||||||
org_required(views.soustredeniStvrzenkyView),
|
org_required(views.SoustredeniStvrzenkyView.as_view()),
|
||||||
name='soustredeni_ucastnici_stvrzenky'
|
name='soustredeni_ucastnici_stvrzenky'
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from django.shortcuts import get_object_or_404, render
|
from django.shortcuts import get_object_or_404
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.views import generic
|
from django.views import generic
|
||||||
from django.contrib.staticfiles.finders import find
|
from django.contrib.staticfiles.finders import find
|
||||||
|
@ -6,13 +6,9 @@ from django.http import Http404
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
|
|
||||||
import csv
|
import csv
|
||||||
import tempfile
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
from pathlib import Path
|
|
||||||
import http
|
|
||||||
|
|
||||||
import personalni.views
|
import various.views.generic
|
||||||
|
from personalni.views import obalkyView
|
||||||
|
|
||||||
from .models import Soustredeni, Soustredeni_Ucastnici
|
from .models import Soustredeni, Soustredeni_Ucastnici
|
||||||
from various.models import Nastaveni
|
from various.models import Nastaveni
|
||||||
|
@ -36,73 +32,78 @@ class SoustredeniListView(generic.ListView):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def soustredeniObalkyView(request, soustredeni):
|
class KonkretniSoustredeniMixin:
|
||||||
soustredeni = get_object_or_404(Soustredeni, id=soustredeni)
|
""" Přidá k View s parametrem `soustredeni` atribut `self.soustredeni` """
|
||||||
return personalni.views.obalkyView(request, soustredeni.ucastnici.all())
|
def setup(self, request, *args, **kwargs):
|
||||||
|
super().setup(request, *args, **kwargs)
|
||||||
|
soustredeni_id = self.kwargs["soustredeni"]
|
||||||
|
self.soustredeni = get_object_or_404(Soustredeni, id=soustredeni_id)
|
||||||
|
|
||||||
|
|
||||||
class SoustredeniUcastniciBaseView(generic.ListView):
|
class SoustredeniUcastniciBaseView(
|
||||||
|
KonkretniSoustredeniMixin,
|
||||||
|
various.views.generic.NeprazdnyListView,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Slouží jako ListView účastníků soustředění
|
||||||
|
+ háže inteligentní chybu při soustředění bez účastníků
|
||||||
|
"""
|
||||||
model = Soustredeni_Ucastnici
|
model = Soustredeni_Ucastnici
|
||||||
|
if_prazdny_title = "K soustředění nejsou přidaní žádní účastníci"
|
||||||
|
if_prazdny_text = "K tebou zvolenému soustředění nejsou přidaní žádní účastníci, tedy není co zobrazit. Můžeš to zkusit změnit v adminu, případně se zeptej webařů :-)"
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
soustredeni = get_object_or_404(
|
|
||||||
Soustredeni,
|
|
||||||
pk=self.kwargs["soustredeni"]
|
|
||||||
)
|
|
||||||
return Soustredeni_Ucastnici.objects.filter(
|
return Soustredeni_Ucastnici.objects.filter(
|
||||||
soustredeni=soustredeni).select_related('resitel')
|
soustredeni=self.soustredeni).select_related('resitel', 'resitel__osoba')
|
||||||
|
|
||||||
|
|
||||||
|
# FIXME předělat jako ostatní (vyžaduje předělání `obalkyView`)
|
||||||
|
def soustredeniObalkyView(request, soustredeni):
|
||||||
|
soustredeni = get_object_or_404(Soustredeni, id=soustredeni)
|
||||||
|
return obalkyView(request, soustredeni.ucastnici.all())
|
||||||
|
|
||||||
|
|
||||||
class SoustredeniMailyUcastnikuView(SoustredeniUcastniciBaseView):
|
class SoustredeniMailyUcastnikuView(SoustredeniUcastniciBaseView):
|
||||||
""" Seznam e-mailů řešitelů oddělených čárkami. """
|
""" Seznam e-mailů řešitelů oddělených čárkami. """
|
||||||
model = Soustredeni_Ucastnici
|
|
||||||
template_name = 'soustredeni/maily_ucastniku.txt'
|
template_name = 'soustredeni/maily_ucastniku.txt'
|
||||||
|
|
||||||
|
|
||||||
class SoustredeniUcastniciView(SoustredeniUcastniciBaseView):
|
class SoustredeniUcastniciView(SoustredeniUcastniciBaseView):
|
||||||
""" HTML tabulka účastníků pro tisk. """
|
""" HTML tabulka účastníků pro tisk. """
|
||||||
model = Soustredeni_Ucastnici
|
|
||||||
template_name = 'soustredeni/seznam_ucastniku.html'
|
template_name = 'soustredeni/seznam_ucastniku.html'
|
||||||
|
|
||||||
|
|
||||||
def soustredeniUcastniciExportView(request, soustredeni):
|
class SoustredeniUcastniciExportView(SoustredeniUcastniciBaseView):
|
||||||
soustredeni = get_object_or_404(Soustredeni, id=soustredeni)
|
""" CSV tabulka účastníků. """
|
||||||
ucastnici = soustredeni.ucastnici.all()
|
def render(self, request, *args, **kwargs):
|
||||||
response = HttpResponse(content_type='text/csv')
|
response = HttpResponse(content_type='text/csv')
|
||||||
response['Content-Disposition'] = 'attachment; filename="ucastnici.csv"'
|
response['Content-Disposition'] = 'attachment; filename="ucastnici.csv"'
|
||||||
|
|
||||||
writer = csv.writer(response)
|
writer = csv.writer(response)
|
||||||
writer.writerow(["jmeno", "prijmeni", "rok_maturity", "telefon", "email", "ulice", "mesto", "psc","stat"])
|
writer.writerow(["jmeno", "prijmeni", "rok_maturity", "telefon", "email", "ulice", "mesto", "psc","stat"])
|
||||||
for u in ucastnici:
|
for u in self.object_list:
|
||||||
o = u.osoba
|
o = u.resitel.osoba
|
||||||
writer.writerow([o.jmeno, o.prijmeni, str(u.rok_maturity), o.telefon, o.email, o.ulice, o.mesto, o.psc, o.stat.name])
|
writer.writerow([o.jmeno, o.prijmeni, str(u.resitel.rok_maturity), o.telefon, o.email, o.ulice, o.mesto, o.psc, o.stat.name])
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def soustredeniStvrzenkyView(request, soustredeni):
|
|
||||||
soustredeni = get_object_or_404(Soustredeni, id=soustredeni)
|
|
||||||
ucastnici = soustredeni.ucastnici.all()
|
|
||||||
if ucastnici.count() == 0:
|
|
||||||
return HttpResponse(
|
|
||||||
render(request, 'universal.html', {
|
|
||||||
'title': 'Není pro koho vyrobit stvrzenky.',
|
|
||||||
'text': 'Právě ses pokusil/a vygenerovat stvrzenky pro prázdnou množinu lidí. Můžeš to zkusit změnit, případně se zeptej webařů :-)',
|
|
||||||
}),
|
|
||||||
status=http.HTTPStatus.NOT_FOUND,
|
|
||||||
)
|
|
||||||
castka = Nastaveni.get_solo().cena_sous
|
|
||||||
tex = render(request, 'soustredeni/stvrzenky.tex', {'ucastnici': ucastnici, 'soustredeni': soustredeni, 'castka': castka}).content
|
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as tempdirfn:
|
class SoustredeniStvrzenkyView(
|
||||||
tempdir = Path(tempdirfn)
|
various.views.generic.TeXResponseMixin,
|
||||||
with open(tempdir / "stvrzenky.tex", "w") as texfile:
|
SoustredeniUcastniciBaseView,
|
||||||
texfile.write(tex.decode())
|
):
|
||||||
|
template_name = 'soustredeni/stvrzenky.tex'
|
||||||
|
dalsi_potrebne_soubory = [find('soustredeni/logomm.pdf')]
|
||||||
|
|
||||||
shutil.copy(find('soustredeni/logomm.pdf'), tempdir)
|
if_prazdny_title = "Není pro koho vyrobit stvrzenky."
|
||||||
subprocess.call(["pdflatex", "stvrzenky.tex"], cwd = tempdir, stdout=subprocess.DEVNULL)
|
if_prazdny_text = "Právě ses pokusil/a vygenerovat stvrzenky pro prázdnou množinu lidí. Můžeš to zkusit změnit, případně se zeptej webařů :-)"
|
||||||
|
|
||||||
with open(tempdir / "stvrzenky.pdf", "rb") as pdffile:
|
def get_context_data(self, **kwargs):
|
||||||
response = HttpResponse(pdffile.read(), content_type='application/pdf')
|
context = super().get_context_data(**kwargs)
|
||||||
return response
|
|
||||||
|
context["castka"] = Nastaveni.get_solo().cena_sous
|
||||||
|
context["soustredeni"] = self.soustredeni
|
||||||
|
context["ucastnici"] = self.object_list
|
||||||
|
return context
|
||||||
|
|
||||||
class SoustredeniAbstraktyView(generic.DetailView):
|
class SoustredeniAbstraktyView(generic.DetailView):
|
||||||
model = Soustredeni
|
model = Soustredeni
|
||||||
|
|
|
@ -1,5 +1,22 @@
|
||||||
|
"""
|
||||||
|
Stejně jako je `django.views.generic` jsou zde generické Views
|
||||||
|
a pár mixinů, které upravují chování Views.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import http
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import django.views
|
import django.views
|
||||||
|
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from django.views import generic
|
||||||
|
|
||||||
|
|
||||||
def viewMethodSwitch(get, post):
|
def viewMethodSwitch(get, post):
|
||||||
"""
|
"""
|
||||||
|
@ -27,3 +44,65 @@ def viewMethodSwitch(get, post):
|
||||||
return thePostView(request, *args, **kwargs)
|
return thePostView(request, *args, **kwargs)
|
||||||
|
|
||||||
return NewView.as_view()
|
return NewView.as_view()
|
||||||
|
|
||||||
|
|
||||||
|
class NeprazdnyListView(generic.ListView):
|
||||||
|
"""
|
||||||
|
Použití jako generic.ListView, jen při prázdném listu vyhodí M&M stránku
|
||||||
|
s titlem `self.if_prazdny_title` a textem `self.if_prazdny_text`
|
||||||
|
a způsob renderování (např. CSV) lze změnit přepsáním metody render.
|
||||||
|
"""
|
||||||
|
allow_empty = False # Interní djangová věc
|
||||||
|
if_prazdny_title = "V seznamu nic není"
|
||||||
|
if_prazdny_text = "V seznamu nic není. Zkus to napravit v adminu, nebo se zeptej webařů."
|
||||||
|
|
||||||
|
# Skoro copy-paste generic.list.ListView.get,
|
||||||
|
# protože nemůžu chytat 404, neboť může nastat i v get_context_data
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
self.object_list = self.get_queryset()
|
||||||
|
|
||||||
|
if self.get_paginate_by(self.object_list) is not None and hasattr(
|
||||||
|
self.object_list, "exists"
|
||||||
|
):
|
||||||
|
is_empty = not self.object_list.exists()
|
||||||
|
else:
|
||||||
|
is_empty = not self.object_list
|
||||||
|
if is_empty:
|
||||||
|
return render(request, 'universal.html', {
|
||||||
|
'title': self.if_prazdny_title,
|
||||||
|
'text': self.if_prazdny_text,
|
||||||
|
}, status=http.HTTPStatus.NOT_FOUND)
|
||||||
|
|
||||||
|
return self.render(request, *args, **kwargs)
|
||||||
|
|
||||||
|
# Tohle jsem vyčlenil, aby šlo generovat i něco jiného než template
|
||||||
|
def render(self, request, *args, **kwargs):
|
||||||
|
context = self.get_context_data()
|
||||||
|
return self.render_to_response(context)
|
||||||
|
|
||||||
|
|
||||||
|
class TeXResponseMixin:
|
||||||
|
"""
|
||||||
|
Mixin pro TemplateView, aby výsledek projel TeXem a vrátil rovnou PDF.
|
||||||
|
Obrázky a jiné soubory lze přidat nastavením `dalsi_potrebne_soubory`
|
||||||
|
(např. na `[django.contrib.staticfiles.finders.find('bla')]`,
|
||||||
|
nebo jiný seznam absolutních cest).
|
||||||
|
"""
|
||||||
|
dalsi_potrebne_soubory = []
|
||||||
|
tex_prikaz = "pdflatex"
|
||||||
|
|
||||||
|
def render_to_response(self, context, **response_kwargs):
|
||||||
|
zdrojak = render_to_string(self.get_template_names(), context)
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as tempdirfn:
|
||||||
|
tempdir = Path(tempdirfn)
|
||||||
|
with open(tempdir / "main.tex", "w") as texfile:
|
||||||
|
texfile.write(zdrojak)
|
||||||
|
for file in self.dalsi_potrebne_soubory:
|
||||||
|
shutil.copy(file, tempdir)
|
||||||
|
subprocess.call([self.tex_prikaz, "main.tex"], cwd=tempdir, stdout=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
with open(tempdir / "main.pdf", "rb") as pdffile:
|
||||||
|
response = HttpResponse(pdffile.read(), content_type='application/pdf', **response_kwargs)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue