ruzne exporty resitelu

This commit is contained in:
ticvac 2025-01-29 18:17:00 +01:00
parent 7563dd728c
commit 5a1eedb7b1
5 changed files with 256 additions and 1 deletions
personalni
tvorba

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

View file

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

View file

@ -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',
)
]

View file

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

View file

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