Předělání sousových views #57
					 3 changed files with 125 additions and 58 deletions
				
			
		|  | @ -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,15 +1,11 @@ | ||||||
| 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 | ||||||
| 
 | 
 | ||||||
| import csv | import csv | ||||||
| import tempfile |  | ||||||
| import shutil |  | ||||||
| import subprocess |  | ||||||
| from pathlib import Path |  | ||||||
| import http |  | ||||||
| 
 | 
 | ||||||
|  | import various.views | ||||||
| from seminar.views import obalkyView | from seminar.views import obalkyView | ||||||
| 
 | 
 | ||||||
| from .models import Soustredeni, Soustredeni_Ucastnici | from .models import Soustredeni, Soustredeni_Ucastnici | ||||||
|  | @ -34,70 +30,69 @@ class SoustredeniListView(generic.ListView): | ||||||
| 		) | 		) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | 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(KonkretniSoustredeniMixin, various.views.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): | ||||||
|  | 		return Soustredeni_Ucastnici.objects.filter( | ||||||
|  | 			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): | def soustredeniObalkyView(request, soustredeni): | ||||||
| 	soustredeni = get_object_or_404(Soustredeni, id=soustredeni) | 	soustredeni = get_object_or_404(Soustredeni, id=soustredeni) | ||||||
| 	return obalkyView(request, soustredeni.ucastnici.all()) | 	return obalkyView(request, soustredeni.ucastnici.all()) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class SoustredeniUcastniciBaseView(generic.ListView): |  | ||||||
| 	model = Soustredeni_Ucastnici |  | ||||||
| 
 |  | ||||||
| 	def get_queryset(self): |  | ||||||
| 		soustredeni = get_object_or_404( |  | ||||||
| 			Soustredeni, |  | ||||||
| 			pk=self.kwargs["soustredeni"] |  | ||||||
| 		) |  | ||||||
| 		return Soustredeni_Ucastnici.objects.filter( |  | ||||||
| 			soustredeni=soustredeni).select_related('resitel') |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 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 | ||||||
| 
				
				zelvuska marked this conversation as resolved
				
			 | |||||||
| 		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(various.views.TeXResponseMixin, SoustredeniUcastniciBaseView): | ||||||
| 		tempdir = Path(tempdirfn) | 	template_name = 'soustredeni/stvrzenky.tex' | ||||||
| 		with open(tempdir / "stvrzenky.tex", "w") as texfile: | 	dalsi_potrebne_soubory = [find('images/logomm.pdf')] | ||||||
| 			texfile.write(tex.decode()) |  | ||||||
| 
 | 
 | ||||||
| 		shutil.copy(find('images/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 | ||||||
|  |  | ||||||
|  | @ -1,7 +1,14 @@ | ||||||
| from django.http import HttpResponseForbidden | import http | ||||||
| from django.shortcuts import render | import tempfile | ||||||
|  | import shutil | ||||||
|  | import subprocess | ||||||
| 
 | 
 | ||||||
| # Create your views here. | from pathlib import Path | ||||||
|  | 
 | ||||||
|  | from django.http import HttpResponseForbidden, HttpResponse | ||||||
|  | from django.shortcuts import render | ||||||
|  | from django.views import generic | ||||||
|  | from django.template.loader import render_to_string | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def csrf_error(request, reason=""): | def csrf_error(request, reason=""): | ||||||
|  | @ -11,3 +18,68 @@ def csrf_error(request, reason=""): | ||||||
| 		{"url": request.META.get("HTTP_REFERER", None), "reason": reason}, | 		{"url": request.META.get("HTTP_REFERER", None), "reason": reason}, | ||||||
| 		status=HttpResponseForbidden.status_code, | 		status=HttpResponseForbidden.status_code, | ||||||
| 	) | 	) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 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 | ||||||
|  | 	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 = "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, "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
	
	
Nemám to teď nacachované, ale pročže nepoužijeme generický export řešitelů, který máme už u výsledkovek?
Protože to takhle už bylo (tj. není to předmětem tohodle pullrequestu). Teď bych to nechal a můžem upravit časem.