Merge pull request 'ruzne exporty resitelu - zatím určitě ne merge xd spíš potřebuji zpětnou vazbu...' (!89) from export_resitelskych_dat into master
Reviewed-on: #89
This commit is contained in:
		
						commit
						ff7d36a965
					
				
					 5 changed files with 236 additions and 1 deletions
				
			
		
							
								
								
									
										88
									
								
								personalni/templates/personalni/profil/export_lidi.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								personalni/templates/personalni/profil/export_lidi.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,88 @@ | |||
| {% extends "base.html" %} | ||||
| 
 | ||||
| 
 | ||||
| {% block content %} | ||||
| 
 | ||||
| <h2><strong>Export lidí</strong></h2> | ||||
| 
 | ||||
| <select name="select-one" id="select-one"> | ||||
|   <option value="0">---</option> | ||||
|   <option value="1">Řešitelé čísla</option> | ||||
|   <option value="2">Řešitelé ročníku</option> | ||||
|   <option value="3">Všichni řešitelé, kteří ještě neodmaturovali</option> | ||||
|   <option value="4">Organizátoři soustředění</option> | ||||
| </select> | ||||
| 
 | ||||
| <select name="select-two" id="select-two"> | ||||
| <!-- will be filled with ajax --> | ||||
| </select> | ||||
| 
 | ||||
| <button id="download-button">Stáhnout</button> | ||||
| 
 | ||||
| <script defer> | ||||
|   const select_one = document.getElementById("select-one") | ||||
|   const select_two = document.getElementById("select-two") | ||||
|   const download_button = document.getElementById("download-button") | ||||
| 
 | ||||
|   download_button.style.display = 'none' | ||||
|   select_two.style.display = 'none' | ||||
| 
 | ||||
|   const fetch_dict_string = '{{ typy_exportu|safe }}' | ||||
|   const fetch_dict = JSON.parse(fetch_dict_string) | ||||
| 
 | ||||
| 
 | ||||
|   select_one.addEventListener('change', (e) => { | ||||
|     value = e.target.value | ||||
|     select_two.style.display = 'none' | ||||
|     select_two.innerHTML = '' | ||||
|     // puvodni stav | ||||
|     if (value == 0) { | ||||
|       download_button.style.display = 'none' | ||||
|       select_two.style.display = 'none' | ||||
|       return | ||||
|     } | ||||
|     // v tomto pripade muzeme rovnou stahnout | ||||
|     if (!(value in fetch_dict)) { | ||||
|       download_button.style.display = 'block' | ||||
|       select_two.style.display = 'none' | ||||
|       return | ||||
|     } | ||||
|     download_button.style.display = 'none' | ||||
|     fetch("/profil/exporty_lidi/get/" + value) | ||||
|       .then(response => response.json()) | ||||
|       .then(data => { | ||||
|         const option = document.createElement('option') | ||||
|         option.value = 0 | ||||
|         option.text = '---' | ||||
|         select_two.appendChild(option) | ||||
|         for (const [key, value] of Object.entries(data)) { | ||||
|           const option = document.createElement('option') | ||||
|           option.value = value["id"] | ||||
|           option.text = value["display"] | ||||
|           select_two.appendChild(option) | ||||
|         } | ||||
|         select_two.style.display = 'block' | ||||
|       }) | ||||
| 
 | ||||
|   }) | ||||
| 
 | ||||
|   select_two.addEventListener('change', (e) => { | ||||
|     value = e.target.value | ||||
|     if (value == 0) { | ||||
|       download_button.style.display = 'none' | ||||
|       return | ||||
|     } | ||||
|     download_button.style.display = 'block' | ||||
|   }) | ||||
| 
 | ||||
|   download_button.addEventListener('click', (e) => { | ||||
|     if (select_two.innerHTML == '') { | ||||
|       window.location.href = "/profil/exporty_lidi/get_csv_only_one_step/" + select_one.value | ||||
|     } else { | ||||
|       window.location.href = "/profil/exporty_lidi/get_csv/" + select_one.value + "/" + select_two.value | ||||
|     } | ||||
|      | ||||
|   }) | ||||
| </script> | ||||
| 
 | ||||
| {% endblock %} | ||||
|  | @ -107,6 +107,13 @@ | |||
|     </li> | ||||
| </ul> | ||||
| 
 | ||||
| <hr /> | ||||
| <h2><strong>Exporty dat lidí v semináří</strong></h2> | ||||
| 
 | ||||
| <ul> | ||||
|   <li><a href="{% url 'exporty_lidi' %}">dostupné exporty</a></li> | ||||
| </ul> | ||||
| 
 | ||||
| <hr /> | ||||
| <p>Nemůžeš najít, co hledáš? Může to být v <a href="{% url 'admin:index' %}">administračním rozhraní webu</a>.</p> | ||||
| {% endblock content %} | ||||
|  |  | |||
|  | @ -38,6 +38,28 @@ urlpatterns = [ | |||
|         'org/propagace/jak-se-dozvedeli/', | ||||
|         org_required(views.JakSeDozvedeliView.as_view()), | ||||
|         name='jak_se_dozvedeli' | ||||
| 	), | ||||
|      | ||||
| 	# export dat o řešitelích | ||||
|     path( | ||||
|         'profil/exporty_lidi', | ||||
|         org_required(views.ExportLidiView.as_view()), | ||||
|         name='exporty_lidi',      | ||||
| 	), | ||||
|     path( | ||||
|         'profil/exporty_lidi/get/<int:type>', | ||||
|         org_required(views.get_export_options), | ||||
|         name='exporty_lidi_options', | ||||
|     ), | ||||
|     path( | ||||
| 		'profil/exporty_lidi/get_csv_only_one_step/<int:type>', | ||||
| 		org_required(views.download_export_csv_only_first_step), | ||||
| 		name='exporty_lidi_data', | ||||
| 	), | ||||
|     path( | ||||
|         'profil/exporty_lidi/get_csv/<int:type>/<int:id>', | ||||
|         org_required(views.download_export_csv), | ||||
|         name='exporty_lidi_download', | ||||
| 	) | ||||
| 
 | ||||
| ] | ||||
|  |  | |||
|  | @ -20,13 +20,16 @@ from django.utils import timezone | |||
| import personalni.models as m | ||||
| from soustredeni.models import Soustredeni | ||||
| from odevzdavatko.models import Hodnoceni | ||||
| from tvorba.models import Clanek, Uloha, Tema | ||||
| from tvorba.models import Clanek, Uloha, Tema, Cislo, Rocnik | ||||
| import tvorba.utils as tvorba_utils | ||||
| from various.models import Nastaveni | ||||
| from .forms import PrihlaskaForm, ProfileEditForm, PoMaturiteProfileEditForm | ||||
| 
 | ||||
| from datetime import date | ||||
| import logging | ||||
| import csv | ||||
| from enum import Enum | ||||
| import json | ||||
| 
 | ||||
| from various.views.pomocne import formularOKView | ||||
| from various.autentizace.views import LoginView | ||||
|  | @ -141,6 +144,54 @@ class OrgoRozcestnikView(TemplateView): | |||
| 		#content_type = 'text/plain; charset=UTF8' | ||||
| 	#XXX | ||||
| 	 | ||||
| class PrvniTypExportu(Enum): | ||||
| 	CISLA = 1 | ||||
| 	ROCNIKU = 2 | ||||
| 	SOUSTREDENI = 4 | ||||
| 
 | ||||
| class ExportLidiView(TemplateView): | ||||
| 	template_name = 'personalni/profil/export_lidi.html' | ||||
| 
 | ||||
| 	def get_context_data(self, **kwargs): | ||||
| 		context = super().get_context_data(**kwargs) | ||||
| 		context['typy_exportu'] = json.dumps({member.value: member.name.lower().capitalize() for member in PrvniTypExportu}) | ||||
| 		return context | ||||
| 	 | ||||
| 
 | ||||
| def get_export_options(request, type): | ||||
| 	if type == PrvniTypExportu.CISLA.value: | ||||
| 		data = [{"id": c.id, "display": str(c)} for c in Cislo.objects.all()] | ||||
| 	if type == PrvniTypExportu.ROCNIKU.value: | ||||
| 		data = [{"id": r.id, "display": str(r)} for r in Rocnik.objects.all()] | ||||
| 	if type == PrvniTypExportu.SOUSTREDENI.value: | ||||
| 		data = [{"id": s.id, "display": str(s)} for s in Soustredeni.objects.all()] | ||||
| 	return HttpResponse(json.dumps(data), content_type='application/json') | ||||
| 
 | ||||
| def download_export_csv_only_first_step(request, type): | ||||
| 	if type == 3: | ||||
| 		response = dataResiteluCsvResponse(tvorba_utils.resitele_co_neodmaturovali()) | ||||
| 		response['Content-Disposition'] = 'attachment; filename="resitele_co_neodmaturovali.csv"' | ||||
| 		return response | ||||
| 
 | ||||
| def download_export_csv(request, type, id): | ||||
| 	if type == PrvniTypExportu.CISLA.value: | ||||
| 		response = dataResiteluCsvResponse(tvorba_utils.resi_cislo(Cislo.objects.get(id=id))) | ||||
| 		name = str(Cislo.objects.get(id=id)).replace(" ", "_") + "_resitele_cisla.csv" | ||||
| 		response['Content-Disposition'] = 'attachment; filename="' + name + '"' | ||||
| 		return response | ||||
| 	if type == PrvniTypExportu.ROCNIKU.value: | ||||
| 		response = dataResiteluCsvResponse(tvorba_utils.resi_v_rocniku(Rocnik.objects.get(id=id))) | ||||
| 		name = str(Rocnik.objects.get(id=id)).replace(" ", "_") + "_resitele_rocniku.csv" | ||||
| 		response['Content-Disposition'] = 'attachment; filename="' + name + '"' | ||||
| 		return response | ||||
| 	if type == PrvniTypExportu.SOUSTREDENI.value: | ||||
| 		soustredeni = Soustredeni.objects.get(id=id) | ||||
| 		organizatori = soustredeni.organizatori.all() | ||||
| 		organizatoriOsoby = Osoba.objects.filter(org__in=organizatori) | ||||
| 		response = dataOsobCsvResponse(organizatoriOsoby, columns=("jmeno", "prijmeni", "email", "telefon",)) | ||||
| 		name = str(soustredeni).replace(" ", "_") + "_organizatori_soustredeni.csv" | ||||
| 		response['Content-Disposition'] = 'attachment; filename="' + name + '"' | ||||
| 		return response | ||||
| 
 | ||||
| class ResitelView(LoginRequiredMixin,generic.DetailView): | ||||
| 	model = m.Resitel | ||||
|  | @ -470,3 +521,46 @@ def dataResiteluCsvResponse(queryset, columns=None, with_header=True): | |||
| 	writer.writerows(queryset_list) | ||||
| 
 | ||||
| 	return response | ||||
| 
 | ||||
| def dataOsobCsvResponse(queryset, columns=None, with_header=True): | ||||
| 	"""Pomocná funkce pro vracení dat osob jako CSV. Musí dostat správný QuerySet, který dává Ososby""" | ||||
| 
 | ||||
| 	default_columns = ( | ||||
| 		'id', | ||||
| 		'jmeno', | ||||
| 		'prijmeni', | ||||
| 		'prezdivka', | ||||
| 		'email', | ||||
| 		'telefon', | ||||
| 		'datum_narozeni', | ||||
| 		'osloveni', | ||||
| 		'ulice', | ||||
| 		'mesto', | ||||
| 		'psc', | ||||
| 		'stat', | ||||
| 		'jak_se_dozvedeli', | ||||
| 		'poznamka', | ||||
| 		'datum_registrace', | ||||
| 		'datum_souhlasu_udaje', | ||||
| 		'datum_souhlasu_zasilani', | ||||
| 	) | ||||
| 
 | ||||
| 	if columns is None: columns = default_columns | ||||
| 
 | ||||
| 	def get_field_name(column_name): | ||||
| 		return column_name | ||||
| 	 | ||||
| 	response = HttpResponse(content_type='text/csv') | ||||
| 	writer = csv.writer(response) | ||||
| 	 | ||||
| 	# První řádek je záhlaví | ||||
| 	if with_header: | ||||
| 		writer.writerow(map(get_field_name, columns)) | ||||
| 	 | ||||
| 	# Data: | ||||
| 	queryset_list = queryset.values_list(*columns) | ||||
| 	writer.writerows(queryset_list) | ||||
| 
 | ||||
| 	return response | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -28,6 +28,30 @@ def resi_v_rocniku(rocnik, cislo=None): | |||
| 			reseni__hodnoceni__deadline_body__cislo__poradi__lte=cislo.poradi | ||||
| 		).distinct() | ||||
| 	 | ||||
| def resi_cislo(cislo): | ||||
| 	""" Vrátí seznam řešitelů, co vyřešili nějaký problém v daném čísle. | ||||
| 	Parametry: | ||||
| 		cislo (typu Cislo)	číslo, ve kterém chci řešitele, co něco odevzdali | ||||
| 	Výstup: | ||||
| 		QuerySet objektů typu Resitel  | ||||
| 	""" | ||||
| 
 | ||||
| 	return personalni.models.Resitel.objects.filter( | ||||
| 		reseni__hodnoceni__deadline_body__cislo=cislo | ||||
| 	).distinct() | ||||
| 
 | ||||
| def resitele_co_neodmaturovali(): | ||||
| 	""" Vrátí seznam řešitelů, co ještě neodmaturovali. | ||||
| 	Pokud ještě není srpen, tak zahrnuje i ty, kteří odmaturovali letos. | ||||
| 
 | ||||
| 	Výstup: | ||||
| 		QuerySet objektů typu Resitel """ | ||||
| 	from datetime import datetime | ||||
| 	current_year = datetime.now().year | ||||
| 	if datetime.now().month < 8: | ||||
| 		current_year -= 1 | ||||
| 	return personalni.models.Resitel.objects.filter(rok_maturity__gte=current_year) | ||||
| 
 | ||||
| 
 | ||||
| def aktivniResitele(cislo, pouze_letosni=False): | ||||
| 	""" Vrací QuerySet aktivních řešitelů, což jsou ti, co ještě neodmaturovali | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue