Browse Source

Merge branch 'master' into kontaktnicek_pro_vsecny

pull/71/head
Jonas Havelka 2 days ago
parent
commit
fa00652a69
  1. 0
      mamweb/vsechno.py
  2. 4
      odevzdavatko/templates/odevzdavatko/prehled_reseni.html
  3. 7
      odevzdavatko/views.py
  4. 2
      soustredeni/templates/soustredeni/stvrzenky.tex
  5. 4
      soustredeni/urls.py
  6. 105
      soustredeni/views.py
  7. 79
      various/views/generic.py

0
seminar/models.py → mamweb/vsechno.py

4
odevzdavatko/templates/odevzdavatko/prehled_reseni.html

@ -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 %}

7
odevzdavatko/views.py

@ -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

2
soustredeni/templates/soustredeni/stvrzenky.tex

@ -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 %}

4
soustredeni/urls.py

@ -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(

105
soustredeni/views.py

@ -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.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
def soustredeniStvrzenkyView(request, soustredeni): writer = csv.writer(response)
soustredeni = get_object_or_404(Soustredeni, id=soustredeni) writer.writerow(["jmeno", "prijmeni", "rok_maturity", "telefon", "email", "ulice", "mesto", "psc","stat"])
ucastnici = soustredeni.ucastnici.all() for u in self.object_list:
if ucastnici.count() == 0: o = u.resitel.osoba
return HttpResponse( writer.writerow([o.jmeno, o.prijmeni, str(u.resitel.rok_maturity), o.telefon, o.email, o.ulice, o.mesto, o.psc, o.stat.name])
render(request, 'universal.html', { return response
'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())
shutil.copy(find('soustredeni/logomm.pdf'), tempdir) class SoustredeniStvrzenkyView(
subprocess.call(["pdflatex", "stvrzenky.tex"], cwd = tempdir, stdout=subprocess.DEVNULL) various.views.generic.TeXResponseMixin,
SoustredeniUcastniciBaseView,
):
template_name = 'soustredeni/stvrzenky.tex'
dalsi_potrebne_soubory = [find('soustredeni/logomm.pdf')]
with open(tempdir / "stvrzenky.pdf", "rb") as pdffile: if_prazdny_title = "Není pro koho vyrobit stvrzenky."
response = HttpResponse(pdffile.read(), content_type='application/pdf') 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řů :-)"
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): class SoustredeniAbstraktyView(generic.DetailView):
model = Soustredeni model = Soustredeni

79
various/views/generic.py

@ -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…
Cancel
Save