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>
|
||||
|
||||
{% for rocnik, hodnoceni in podle_rocniku %}
|
||||
{% for rocnik, hodnoceni, suma_bodu in podle_rocniku %}
|
||||
<h1>Ročník {{ rocnik }}</h1>
|
||||
<table class="moje_reseni plne_ohranicena_tabulka">
|
||||
<tr>
|
||||
|
@ -33,7 +33,7 @@
|
|||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<p>Celkový počet bodů {{suma_bodu}}</p>
|
||||
<br>
|
||||
|
||||
{% 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 :-/
|
||||
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):
|
||||
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
|
||||
# TODO: Umožnit stažení / zobrazení řešení
|
||||
return ctx
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
}
|
||||
|
||||
{% for u in ucastnici %}
|
||||
{% with o=u.osoba %}
|
||||
{% with o=u.resitel.osoba %}
|
||||
\stvrzenka{{o.jmeno|sloz}}{{o.prijmeni|sloz}}
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
|
|
|
@ -26,12 +26,12 @@ urlpatterns = [
|
|||
),
|
||||
path(
|
||||
'export_ucastniku',
|
||||
org_required(views.soustredeniUcastniciExportView),
|
||||
org_required(views.SoustredeniUcastniciExportView.as_view()),
|
||||
name='soustredeni_ucastnici_export'
|
||||
),
|
||||
path(
|
||||
'stvrzenky.pdf',
|
||||
org_required(views.soustredeniStvrzenkyView),
|
||||
org_required(views.SoustredeniStvrzenkyView.as_view()),
|
||||
name='soustredeni_ucastnici_stvrzenky'
|
||||
),
|
||||
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.views import generic
|
||||
from django.contrib.staticfiles.finders import find
|
||||
|
@ -6,13 +6,9 @@ from django.http import Http404
|
|||
from django.core.exceptions import PermissionDenied
|
||||
|
||||
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 various.models import Nastaveni
|
||||
|
@ -36,73 +32,78 @@ class SoustredeniListView(generic.ListView):
|
|||
)
|
||||
|
||||
|
||||
def soustredeniObalkyView(request, soustredeni):
|
||||
soustredeni = get_object_or_404(Soustredeni, id=soustredeni)
|
||||
return personalni.views.obalkyView(request, soustredeni.ucastnici.all())
|
||||
class KonkretniSoustredeniMixin:
|
||||
""" Přidá k View s parametrem `soustredeni` atribut `self.soustredeni` """
|
||||
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
|
||||
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):
|
||||
soustredeni = get_object_or_404(
|
||||
Soustredeni,
|
||||
pk=self.kwargs["soustredeni"]
|
||||
)
|
||||
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):
|
||||
""" Seznam e-mailů řešitelů oddělených čárkami. """
|
||||
model = Soustredeni_Ucastnici
|
||||
template_name = 'soustredeni/maily_ucastniku.txt'
|
||||
|
||||
|
||||
class SoustredeniUcastniciView(SoustredeniUcastniciBaseView):
|
||||
""" HTML tabulka účastníků pro tisk. """
|
||||
model = Soustredeni_Ucastnici
|
||||
template_name = 'soustredeni/seznam_ucastniku.html'
|
||||
|
||||
|
||||
def soustredeniUcastniciExportView(request, soustredeni):
|
||||
soustredeni = get_object_or_404(Soustredeni, id=soustredeni)
|
||||
ucastnici = soustredeni.ucastnici.all()
|
||||
response = HttpResponse(content_type='text/csv')
|
||||
response['Content-Disposition'] = 'attachment; filename="ucastnici.csv"'
|
||||
class SoustredeniUcastniciExportView(SoustredeniUcastniciBaseView):
|
||||
""" CSV tabulka účastníků. """
|
||||
def render(self, request, *args, **kwargs):
|
||||
response = HttpResponse(content_type='text/csv')
|
||||
response['Content-Disposition'] = 'attachment; filename="ucastnici.csv"'
|
||||
|
||||
writer = csv.writer(response)
|
||||
writer.writerow(["jmeno", "prijmeni", "rok_maturity", "telefon", "email", "ulice", "mesto", "psc","stat"])
|
||||
for u in ucastnici:
|
||||
o = u.osoba
|
||||
writer.writerow([o.jmeno, o.prijmeni, str(u.rok_maturity), o.telefon, o.email, o.ulice, o.mesto, o.psc, o.stat.name])
|
||||
return response
|
||||
writer = csv.writer(response)
|
||||
writer.writerow(["jmeno", "prijmeni", "rok_maturity", "telefon", "email", "ulice", "mesto", "psc","stat"])
|
||||
for u in self.object_list:
|
||||
o = u.resitel.osoba
|
||||
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
|
||||
|
||||
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:
|
||||
tempdir = Path(tempdirfn)
|
||||
with open(tempdir / "stvrzenky.tex", "w") as texfile:
|
||||
texfile.write(tex.decode())
|
||||
class SoustredeniStvrzenkyView(
|
||||
various.views.generic.TeXResponseMixin,
|
||||
SoustredeniUcastniciBaseView,
|
||||
):
|
||||
template_name = 'soustredeni/stvrzenky.tex'
|
||||
dalsi_potrebne_soubory = [find('soustredeni/logomm.pdf')]
|
||||
|
||||
shutil.copy(find('soustredeni/logomm.pdf'), tempdir)
|
||||
subprocess.call(["pdflatex", "stvrzenky.tex"], cwd = tempdir, stdout=subprocess.DEVNULL)
|
||||
if_prazdny_title = "Není pro koho vyrobit stvrzenky."
|
||||
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:
|
||||
response = HttpResponse(pdffile.read(), content_type='application/pdf')
|
||||
return response
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context["castka"] = Nastaveni.get_solo().cena_sous
|
||||
context["soustredeni"] = self.soustredeni
|
||||
context["ucastnici"] = self.object_list
|
||||
return context
|
||||
|
||||
class SoustredeniAbstraktyView(generic.DetailView):
|
||||
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
|
||||
|
||||
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):
|
||||
"""
|
||||
|
@ -27,3 +44,65 @@ def viewMethodSwitch(get, post):
|
|||
return thePostView(request, *args, **kwargs)
|
||||
|
||||
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