From 5a1eedb7b192ab15371e2bfcd9fcb430815170b9 Mon Sep 17 00:00:00 2001
From: ticvac <vaclav.tichy180@gmail.com>
Date: Wed, 29 Jan 2025 18:17:00 +0100
Subject: [PATCH] ruzne exporty resitelu

---
 .../personalni/profil/export_lidi.html        |  88 +++++++++++++
 .../personalni/profil/orgorozcestnik.html     |   7 ++
 personalni/urls.py                            |  22 ++++
 personalni/views.py                           | 118 +++++++++++++++++-
 tvorba/utils.py                               |  22 ++++
 5 files changed, 256 insertions(+), 1 deletion(-)
 create mode 100644 personalni/templates/personalni/profil/export_lidi.html

diff --git a/personalni/templates/personalni/profil/export_lidi.html b/personalni/templates/personalni/profil/export_lidi.html
new file mode 100644
index 00000000..96778449
--- /dev/null
+++ b/personalni/templates/personalni/profil/export_lidi.html
@@ -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 %}
diff --git a/personalni/templates/personalni/profil/orgorozcestnik.html b/personalni/templates/personalni/profil/orgorozcestnik.html
index 2dbc853b..bd73fa6e 100644
--- a/personalni/templates/personalni/profil/orgorozcestnik.html
+++ b/personalni/templates/personalni/profil/orgorozcestnik.html
@@ -107,6 +107,13 @@
     </li>
 </ul>
 
+<hr />
+<h2><strong>Exporty dat lidí v semináří</strong></h2>
+
+<ul>
+  <li><a href="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 %}
diff --git a/personalni/urls.py b/personalni/urls.py
index 1805bbfe..9e173aae 100644
--- a/personalni/urls.py
+++ b/personalni/urls.py
@@ -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',
 	)
 
 ]
diff --git a/personalni/views.py b/personalni/views.py
index 49442c2d..5ae97e24 100644
--- a/personalni/views.py
+++ b/personalni/views.py
@@ -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
@@ -140,7 +143,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()
+		response = dataOsobCsvResponse(organizatori)
+		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 +520,69 @@ 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',
+		'osoba__jmeno',
+		'osoba__prijmeni',
+		'osoba__prezdivka',
+		'osoba__email',
+		'osoba__telefon',
+		'osoba__user__username',
+		'osoba__datum_narozeni',
+		'osoba__osloveni',
+		'osoba__ulice',
+		'osoba__mesto',
+		'osoba__psc',
+		'osoba__stat',
+		'osoba__jak_se_dozvedeli',
+		'osoba__poznamka',
+		'osoba__datum_registrace',
+		'osoba__datum_souhlasu_udaje',
+		'osoba__datum_souhlasu_zasilani',
+	)
+	if columns is None: columns = default_columns
+
+	field_name_overrides = {
+		# Zrušení prefixu "osoba__"
+		'osoba__jmeno':                  'jmeno',
+		'osoba__prijmeni':               'prijmeni',
+		'osoba__prezdivka':              'prezdivka',
+		'osoba__email':                  'email',
+		'osoba__telefon':                'telefon',
+		'osoba__user__username':         'user',
+		'osoba__datum_narozeni':         'datum_narozeni',
+		'osoba__osloveni':               'osloveni',
+		'osoba__ulice':                  'ulice',
+		'osoba__mesto':                  'mesto',
+		'osoba__psc':                    'psc',
+		'osoba__stat':                   'stat',
+		'osoba__jak_se_dozvedeli':       'jak_se_dozvedeli',
+		'osoba__poznamka':               'poznamka',
+		'osoba__datum_registrace':       'datum_registrace',
+		'osoba__datum_souhlasu_udaje':   'datum_souhlasu_udaje',
+		'osoba__datum_souhlasu_zasilani':'datum_souhlasu_zasilani',
+	}
+
+	def get_field_name(column_name):
+		if column_name in field_name_overrides:
+			return field_name_overrides[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
+
+
diff --git a/tvorba/utils.py b/tvorba/utils.py
index c2feadd9..1c8076c8 100644
--- a/tvorba/utils.py
+++ b/tvorba/utils.py
@@ -27,6 +27,28 @@ def resi_v_rocniku(rocnik, cislo=None):
 			reseni__hodnoceni__deadline_body__cislo__rocnik=rocnik,
 			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 
+		
+	Nejsem si moc jistý, jestli to tak funguje... Vašek """
+
+	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.
+	Výstup:
+		QuerySet objektů typu Resitel """
+	from datetime import datetime
+	current_year = datetime.now().year
+	'''Člověk odmaturuje když je jeho rok maturity menší nebo stejný jako aktuální rok? '''
+	return personalni.models.Resitel.objects.filter(rok_maturity__gte=current_year)
 
 
 def aktivniResitele(cislo, pouze_letosni=False):