193 lines
		
	
	
	
		
			7.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			193 lines
		
	
	
	
		
			7.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import csv
 | |
| import http
 | |
| import logging
 | |
| 
 | |
| from django.http import HttpResponse, HttpRequest
 | |
| from django.shortcuts import render, get_object_or_404
 | |
| from django.views import generic
 | |
| from django.shortcuts import HttpResponseRedirect
 | |
| from django.core.exceptions import ObjectDoesNotExist
 | |
| from django.db import transaction
 | |
| 
 | |
| from various.views.pomocne import formularOKView
 | |
| from .forms import HlasovaniPrednaskaFormSet, HlasovaniZnalostiFormSet
 | |
| 
 | |
| from various.models import Nastaveni
 | |
| from prednasky.models import Prednaska, Hlasovani, Znalost, HlasovaniOZnalostech, Seznam
 | |
| from soustredeni.models import Soustredeni
 | |
| from personalni.models import Osoba
 | |
| 
 | |
| PREDNASKY_PREFIX = "prednasky"
 | |
| ZNALOSTI_PREFIX = "znalosti"
 | |
| 
 | |
| logger = logging.getLogger(__name__)
 | |
| 
 | |
| def newPrednaska(request: HttpRequest) -> HttpResponse:
 | |
| 	"""
 | |
| 		View zobrazující a ukládající účastnické hlasování
 | |
| 		(:py:class:`Hlasování <prednasky.models.Hlasovani>`
 | |
| 		a :py:class:`HlasováníOZnalostech <prednasky.models.HlasovaniOZnalostech>`)
 | |
| 		o :py:class:`Přednáškách <prednasky.models.Prednaska>`
 | |
| 		a :py:class:`Znalostech <prednasky.models.Znalost>`
 | |
| 	"""
 | |
| 	# hlasovani se vztahuje k nejnovejsimu soustredeni
 | |
| 	sous = Nastaveni.get_solo().aktualni_sous
 | |
| 	seznam = Seznam.objects.filter(soustredeni = sous, stav=Seznam.Stav.NAVRH).first()
 | |
| 	if sous is None or seznam is None:
 | |
| 		return render(request, 'universal.html', {
 | |
| 			'title': "Nelze hlasovat",
 | |
| 			'text': "Není žádný seznam přednášek, o kterém by se dalo hlasovat.",
 | |
| 		}, status=http.HTTPStatus.NOT_FOUND)
 | |
| 
 | |
| 	osoba = Osoba.objects.filter(user=request.user).first()
 | |
| 	ucastnik = osoba.plne_jmeno() + ' ' + str(osoba.id) # id, kvůli kolizi jmen
 | |
| 
 | |
| 	if request.method == 'POST': # Když to byl POST, tak ukládáme.
 | |
| 		# Načteme data do formsetů
 | |
| 		form_set_prednasky = HlasovaniPrednaskaFormSet(request.POST, prefix=PREDNASKY_PREFIX)
 | |
| 		form_set_znalosti = HlasovaniZnalostiFormSet(request.POST, prefix=ZNALOSTI_PREFIX)
 | |
| 
 | |
| 		if form_set_prednasky.is_valid() and form_set_znalosti.is_valid():
 | |
| 			with transaction.atomic():
 | |
| 				# Místo updatování data prostě smažeme a vytvoříme nová
 | |
| 				seznam.hlasovani_set.filter(ucastnik=ucastnik).delete()
 | |
| 				seznam.hlasovanioznalostech_set.filter(ucastnik=osoba).delete()
 | |
| 
 | |
| 				for form in form_set_prednasky:
 | |
| 					prednaska_id = form.cleaned_data['prednaska_id']
 | |
| 					prednaska = Prednaska.objects.filter(id=prednaska_id).first()
 | |
| 					if prednaska is None:
 | |
| 						logger.error(f"Účastník {ucastnik} hodnotil neexistující přednášku {prednaska_id} číslem {form.cleaned_data['body']}")
 | |
| 						continue
 | |
| 
 | |
| 					Hlasovani.objects.create(
 | |
| 						prednaska=prednaska,
 | |
| 						body=form.cleaned_data['body'],
 | |
| 						ucastnik=ucastnik,
 | |
| 						ucastnik_osoba=osoba,
 | |
| 						seznam=seznam,
 | |
| 					)
 | |
| 
 | |
| 				for form in form_set_znalosti:
 | |
| 					znalost_id = form.cleaned_data['znalost_id']
 | |
| 					znalost = Znalost.objects.filter(id=znalost_id).first()
 | |
| 					if znalost is None:
 | |
| 						logger.error(f"Účastník {ucastnik} hodnotil neexistující znalost {znalost_id} číslem {form.cleaned_data['odpoved']}")
 | |
| 						continue
 | |
| 
 | |
| 					HlasovaniOZnalostech.objects.create(
 | |
| 						odpoved=form.cleaned_data['odpoved'],
 | |
| 						znalost=znalost,
 | |
| 						ucastnik=osoba,
 | |
| 						seznam=seznam,
 | |
| 					)
 | |
| 
 | |
| 			return HttpResponseRedirect('./hotovo')
 | |
| 
 | |
| 		else: # Pokud je nějaký formset nevalidní, vracíme je k přepracování
 | |
| 			prednasky = seznam.prednaska_set.all()
 | |
| 			znalosti = seznam.znalost_set.all()
 | |
| 			# FIXME Spadnout, pokud nesedí přednáška/znalost s formulářem. (Nějak se mi to nepovedlo.)
 | |
| 			# Může se totiž stát, že se mezitím změnily přednášky (nějaká byla přidána/odebrána)
 | |
| 
 | |
| 	else: # Když to nebyl POST, tak inicializujeme (pokud už o přednášce/znalosti účastník hlasoval, předvyplníme mu to).
 | |
| 		def odpoved_prednasky(p: Prednaska) -> Hlasovani.Body:
 | |
| 			hlasovani = p.hlasovani_set.filter(ucastnik=ucastnik).first()
 | |
| 			return hlasovani.body if hlasovani else Hlasovani.Body.JEDNO
 | |
| 
 | |
| 		def odpoved_znalosti(z: Znalost) -> HlasovaniOZnalostech.Odpoved:
 | |
| 			hlasovani = z.hlasovanioznalostech_set.filter(ucastnik=osoba).first()
 | |
| 			return hlasovani.odpoved if hlasovani else HlasovaniOZnalostech.Odpoved.CIRCA
 | |
| 
 | |
| 		prednasky = seznam.prednaska_set.all()
 | |
| 		znalosti = seznam.znalost_set.all()
 | |
| 
 | |
| 		form_set_prednasky = HlasovaniPrednaskaFormSet(initial=[
 | |
| 			{"prednaska_id": p.id, "body": odpoved_prednasky(p)} for p in prednasky
 | |
| 		], prefix=PREDNASKY_PREFIX)
 | |
| 
 | |
| 		form_set_znalosti = HlasovaniZnalostiFormSet(initial=[
 | |
| 			{"znalost_id": z.id, "odpoved": odpoved_znalosti(z)} for z in znalosti
 | |
| 		], prefix=ZNALOSTI_PREFIX)
 | |
| 
 | |
| 
 | |
| 	# V případě nePOSTu nebo chyby při ukládání vracíme hlasování
 | |
| 	return render(
 | |
| 		request,
 | |
| 		'prednasky/base.html',
 | |
| 		{
 | |
| 			'form_set_prednasky': form_set_prednasky, 'form_set_znalosti': form_set_znalosti,
 | |
| 			'formy_a_prednasky': list(zip(form_set_prednasky, prednasky)),
 | |
| 			'formy_a_znalosti': list(zip(form_set_znalosti, znalosti)),
 | |
| 		}
 | |
| 	)
 | |
| 
 | |
| 
 | |
| def Prednaska_hotovo(request: HttpRequest) -> HttpResponse:
 | |
| 	""" View po vyplnění :py:func:`hlasování <prednasky.views.newPrednaska>` """
 | |
| 	return formularOKView(request, "Děkujeme za vyplnění hlasování o přednáškách a těšíme se na soustředění.")
 | |
| 
 | |
| class MetaSeznamListView(generic.ListView):
 | |
| 	""" Seznam všech :py:class:`Seznamů <prednasky.models.Seznam>` s odkazy na exporty """
 | |
| 	model = Seznam
 | |
| 	template_name = 'prednasky/metaseznam_prednasek.html'
 | |
| 
 | |
| 
 | |
| def PrednaskyExportView(request: HttpRequest, seznam: int, **kwargs) -> HttpResponse:
 | |
| 	"""
 | |
| 		Vrátí všechna :py:class:`Hlasování <prednasky.models.Hlasovani>`
 | |
| 		i :py:class:`HlasováníOZnalostech <prednasky.models.HlasovaniOZnalostech>`
 | |
| 		v daném :py:class:`Seznamu <prednasky.models.Seznam>`
 | |
| 		jako csv soubor (řádky = účastníci, sloupce = přednášky&znalosti).
 | |
| 
 | |
| 		:param seznam: ID daného :py:class:`Seznamu <prednasky.models.Seznam>`
 | |
| 	"""
 | |
| 	hlasovani = Hlasovani.objects.filter(seznam=seznam).select_related("prednaska")
 | |
| 	hlasovani_o_znalostech = HlasovaniOZnalostech.objects.filter(seznam=seznam).select_related('ucastnik', 'znalost')
 | |
| 
 | |
| 	# Inicializujeme sloupce
 | |
| 	prednasky = list(Prednaska.objects.filter(seznamy=seznam))
 | |
| 	znalosti = list(Znalost.objects.filter(seznamy=seznam))
 | |
| 
 | |
| 	prednasky_map: dict[int, int] = {p.id: i for i, p in enumerate(prednasky, 1)}
 | |
| 	offset = len(prednasky_map)
 | |
| 	znalosti_map: dict[int, int] = {z.id: i for i, z in enumerate(znalosti, offset + 1)}
 | |
| 	width = offset + len(znalosti_map)
 | |
| 
 | |
| 	# A po inicializaci sloupců vyplníme tabulku
 | |
| 	table: [str, list[str|Prednaska|Znalost,]] = {}
 | |
| 
 | |
| 	errors = []
 | |
| 
 | |
| 	for h in hlasovani:
 | |
| 		if h.ucastnik not in table: # Pokud jsme účastníka ještě neviděli, předgenerujeme si jeho řádek
 | |
| 			table[h.ucastnik] = [h.ucastnik] + ([""] * width)
 | |
| 
 | |
| 		if h.prednaska.id in prednasky_map:
 | |
| 			table[h.ucastnik][prednasky_map[h.prednaska.id]] = h.body
 | |
| 		else:
 | |
| 			errors.append(f"Přednáška {h.prednaska.id} ({h.prednaska}) dostala od Účastníka {h.ucastnik} následující hodnocení: {h.body}")
 | |
| 
 | |
| 	for h in hlasovani_o_znalostech:
 | |
| 		ucastnik = str(h.ucastnik) + ' ' + str(h.ucastnik.id) # id, kvůli kolizi jmen
 | |
| 		if ucastnik not in table: # Pokud jsme účastníka ještě neviděli, předgenerujeme si jeho řádek
 | |
| 			table[ucastnik] = [ucastnik] + ([""] * width)
 | |
| 
 | |
| 		if h.znalost.id in znalosti_map:
 | |
| 			table[ucastnik][znalosti_map[h.znalost.id]] = h.odpoved
 | |
| 		else:
 | |
| 			errors.append(f"Znalost {h.znalost.id} ({h.znalost}) dostala od Účastníka {h.ucastnik.id} následující odpověď: {h.odpoved}")
 | |
| 
 | |
| 	if len(errors) > 0:
 | |
| 		logger.error("Při exportování hlasování o přednáškách a znalostech se neexportovali hodnocení a přednášky (pravděpodobně se od hlasování vyškrtla nějaká znalost/přednáška ze seznamu):\n" + "\n".join(errors))
 | |
| 
 | |
| 	response = HttpResponse(content_type="text/csv", charset="utf-8")
 | |
| 	response["Content-Disposition"] = 'attachment; filename="hlasovani.csv"'
 | |
| 
 | |
| 	writer = csv.writer(response)
 | |
| 	writer.writerow(["jména \\ přednáška|znalost"] + list(map(str, prednasky + znalosti)))
 | |
| 	for row in table.values():
 | |
| 		writer.writerow(list(map(str, row)))
 | |
| 	return response
 | |
| 
 | |
| 
 | 
