Merge branch 'master' into korekturovatko
This commit is contained in:
		
						commit
						8ec06d0c90
					
				
					 18 changed files with 313 additions and 93 deletions
				
			
		|  | @ -42,7 +42,11 @@ | ||||||
| 
 | 
 | ||||||
|   window.addEventListener("load", _ => { |   window.addEventListener("load", _ => { | ||||||
|     aktualizuj_vse({}, true, () => { |     aktualizuj_vse({}, true, () => { | ||||||
|       if (location.hash !== "") location.hash = location.hash; // Po rozházení korektur sescrollujeme na kotvu v URL |       if (location.hash !== "") { // Po rozházení korektur sescrollujeme na kotvu v URL | ||||||
|  |         const h = location.hash.substring(1); | ||||||
|  |         location.hash = "HACK"; | ||||||
|  |         location.hash = h; | ||||||
|  |       } | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -26,9 +26,10 @@ | ||||||
| 
 | 
 | ||||||
| 	.tres { | 	.tres { | ||||||
| 		flex: 1; | 		flex: 1; | ||||||
|  | 		text-align: end; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	.grey { | 	.half-opacity { | ||||||
| 		opacity: 0.5; | 		opacity: 0.5; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -18,7 +18,7 @@ | ||||||
|   {% for osoba in object_list %} |   {% for osoba in object_list %} | ||||||
|   <div class="osoba"> |   <div class="osoba"> | ||||||
|     <div class="uno">{{ osoba.jmeno }} {{ osoba.prijmeni }}</div> |     <div class="uno">{{ osoba.jmeno }} {{ osoba.prijmeni }}</div> | ||||||
|     <div class="dos {% if not osoba.jak_se_dozvedeli %}grey{% endif %}">{% if osoba.jak_se_dozvedeli %} {{osoba.jak_se_dozvedeli}} {% else %} NEZADÁNO {% endif %}</div> |     <div class="dos {% if not osoba.jak_se_dozvedeli %}half-opacity{% endif %}">{% if osoba.jak_se_dozvedeli %} {{osoba.jak_se_dozvedeli}} {% else %} NEZADÁNO {% endif %}</div> | ||||||
|     <div class="tres">{{ osoba.datum_registrace }}</div> |     <div class="tres">{{ osoba.datum_registrace }}</div> | ||||||
|   </div> |   </div> | ||||||
|   {% endfor %} |   {% endfor %} | ||||||
|  |  | ||||||
							
								
								
									
										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> |     </li> | ||||||
| </ul> | </ul> | ||||||
| 
 | 
 | ||||||
|  | <hr /> | ||||||
|  | <h2><strong>Exporty dat lidí v semináří</strong></h2> | ||||||
|  | 
 | ||||||
|  | <ul> | ||||||
|  |   <li><a href="{% url 'exporty_lidi' %}">dostupné exporty</a></li> | ||||||
|  | </ul> | ||||||
|  | 
 | ||||||
| <hr /> | <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> | <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 %} | {% endblock content %} | ||||||
|  |  | ||||||
|  | @ -38,6 +38,28 @@ urlpatterns = [ | ||||||
|         'org/propagace/jak-se-dozvedeli/', |         'org/propagace/jak-se-dozvedeli/', | ||||||
|         org_required(views.JakSeDozvedeliView.as_view()), |         org_required(views.JakSeDozvedeliView.as_view()), | ||||||
|         name='jak_se_dozvedeli' |         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 | import personalni.models as m | ||||||
| from soustredeni.models import Soustredeni | from soustredeni.models import Soustredeni | ||||||
| from odevzdavatko.models import Hodnoceni | 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 various.models import Nastaveni | ||||||
| from .forms import PrihlaskaForm, ProfileEditForm, PoMaturiteProfileEditForm | from .forms import PrihlaskaForm, ProfileEditForm, PoMaturiteProfileEditForm | ||||||
| 
 | 
 | ||||||
| from datetime import date | from datetime import date | ||||||
| import logging | import logging | ||||||
| import csv | import csv | ||||||
|  | from enum import Enum | ||||||
|  | import json | ||||||
| 
 | 
 | ||||||
| from various.views.pomocne import formularOKView | from various.views.pomocne import formularOKView | ||||||
| from various.autentizace.views import LoginView | from various.autentizace.views import LoginView | ||||||
|  | @ -141,6 +144,54 @@ class OrgoRozcestnikView(TemplateView): | ||||||
| 		#content_type = 'text/plain; charset=UTF8' | 		#content_type = 'text/plain; charset=UTF8' | ||||||
| 	#XXX | 	#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): | class ResitelView(LoginRequiredMixin,generic.DetailView): | ||||||
| 	model = m.Resitel | 	model = m.Resitel | ||||||
|  | @ -470,3 +521,46 @@ def dataResiteluCsvResponse(queryset, columns=None, with_header=True): | ||||||
| 	writer.writerows(queryset_list) | 	writer.writerows(queryset_list) | ||||||
| 
 | 
 | ||||||
| 	return response | 	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 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -10,7 +10,7 @@ from soustredeni.models import Soustredeni | ||||||
| 
 | 
 | ||||||
| class Seznam_PrednaskaInline(admin.TabularInline): | class Seznam_PrednaskaInline(admin.TabularInline): | ||||||
| 	""" | 	""" | ||||||
| 		Pomůcka pro :py:class:`prednasky.admin.SeznamAdmin` zobrazující hezky :py:class:`Přednášky <prednasky.models.Prednaska>` | 		:py:class:`Inline <django.contrib.admin.TabularInline>` pro :py:class:`prednasky.admin.SeznamAdmin` zobrazující :py:class:`Přednášky <prednasky.models.Prednaska>` | ||||||
| 		v adminu :py:class:`Seznamu <prednasky.models.Seznam>`. | 		v adminu :py:class:`Seznamu <prednasky.models.Seznam>`. | ||||||
| 	""" | 	""" | ||||||
| 	model = Prednaska.seznamy.through | 	model = Prednaska.seznamy.through | ||||||
|  | @ -60,7 +60,7 @@ class Seznam_PrednaskaInline(admin.TabularInline): | ||||||
| 
 | 
 | ||||||
| class Seznam_ZnalostInline(admin.TabularInline): | class Seznam_ZnalostInline(admin.TabularInline): | ||||||
| 	""" | 	""" | ||||||
| 		Pomůcka pro :py:class:`prednasky.admin.SeznamAdmin` zobrazující hezky :py:class:`Znalosti <prednasky.models.Znalost>` | 		:py:class:`Inline <django.contrib.admin.TabularInline>` pro :py:class:`prednasky.admin.SeznamAdmin` zobrazující :py:class:`Znalosti <prednasky.models.Znalost>` | ||||||
| 		v adminu :py:class:`Seznamu <prednasky.models.Seznam>`. | 		v adminu :py:class:`Seznamu <prednasky.models.Seznam>`. | ||||||
| 	""" | 	""" | ||||||
| 	model = Znalost.seznamy.through | 	model = Znalost.seznamy.through | ||||||
|  | @ -97,7 +97,7 @@ admin.site.register(Seznam, SeznamAdmin) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class PrednaskaAdmin(VersionAdmin): | class PrednaskaAdmin(VersionAdmin): | ||||||
| 	""" Admin pro :py:class:`Přednášku <prednasky.models.Prednaska> """ | 	""" Admin pro :py:class:`Přednášku <prednasky.models.Prednaska>` """ | ||||||
| 	list_display = ['nazev', 'org', 'obor'] | 	list_display = ['nazev', 'org', 'obor'] | ||||||
| 	list_filter = ['org', 'obor'] | 	list_filter = ['org', 'obor'] | ||||||
| 	search_fields = ['nazev'] | 	search_fields = ['nazev'] | ||||||
|  | @ -138,7 +138,7 @@ admin.site.register(Prednaska, PrednaskaAdmin) | ||||||
| 
 | 
 | ||||||
| class ZnalostAdmin(PrednaskaAdmin): # Trochu hack, ať nemusím vypisovat všechno znovu | class ZnalostAdmin(PrednaskaAdmin): # Trochu hack, ať nemusím vypisovat všechno znovu | ||||||
| 	""" | 	""" | ||||||
| 		Admin pro :py:class:`Znalost <prednasky.models.Znalost> | 		Admin pro :py:class:`Znalost <prednasky.models.Znalost>` | ||||||
| 		TODO předělat, aby nedědila z :py:class:`prednasky.admin.PrednaskaAdmin`, ale společné věci byly zvlášť | 		TODO předělat, aby nedědila z :py:class:`prednasky.admin.PrednaskaAdmin`, ale společné věci byly zvlášť | ||||||
| 	""" | 	""" | ||||||
| 	list_display = ("__str__",) | 	list_display = ("__str__",) | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ from django import forms | ||||||
| from .models import Hlasovani, HlasovaniOZnalostech | from .models import Hlasovani, HlasovaniOZnalostech | ||||||
| 
 | 
 | ||||||
| class HlasovaniPrednaskaForm(forms.Form): | class HlasovaniPrednaskaForm(forms.Form): | ||||||
| 	""" :py:class:`Formulář <django.forms.Form>` pro pro :py:class:`Hlasování <prednasky.models.Hlasovani>` o jedné :py:class:`Přednášce <prednasky.models.Prednaska>` | 	""" :py:class:`Formulář <django.forms.Form>` pro :py:class:`Hlasování <prednasky.models.Hlasovani>` o jedné :py:class:`Přednášce <prednasky.models.Prednaska>` | ||||||
| 	(neobsahuje téměř nic, většina se musí doplnit jiným způsobem) | 	(neobsahuje téměř nic, většina se musí doplnit jiným způsobem) | ||||||
| 	""" | 	""" | ||||||
| 
 | 
 | ||||||
|  | @ -17,7 +17,7 @@ class HlasovaniPrednaskaForm(forms.Form): | ||||||
| HlasovaniPrednaskaFormSet = forms.formset_factory(HlasovaniPrednaskaForm, extra=0) | HlasovaniPrednaskaFormSet = forms.formset_factory(HlasovaniPrednaskaForm, extra=0) | ||||||
| 
 | 
 | ||||||
| class HlasovaniZnalostiForm(forms.Form): | class HlasovaniZnalostiForm(forms.Form): | ||||||
| 	""" :py:class:`Formulář <django.forms.Form>` pro pro :py:class:`HlasováníOZnalostech <prednasky.models.HlasovaniOZnalostech>` o jedné :py:class:`Znalosti <prednasky.models.Znalost>` | 	""" :py:class:`Formulář <django.forms.Form>` pro :py:class:`HlasováníOZnalostech <prednasky.models.HlasovaniOZnalostech>` o jedné :py:class:`Znalosti <prednasky.models.Znalost>` | ||||||
| 	(neobsahuje téměř nic, většina se musí doplnit jiným způsobem) | 	(neobsahuje téměř nic, většina se musí doplnit jiným způsobem) | ||||||
| 	""" | 	""" | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										20
									
								
								prednasky/migrations/0023_hlasovani_ucastnik_osoba.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								prednasky/migrations/0023_hlasovani_ucastnik_osoba.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | ||||||
|  | # Generated by Django 4.2.16 on 2025-02-19 17:31 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations, models | ||||||
|  | import django.db.models.deletion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('personalni', '0019_rename_upozorneni_resitel_upozornovat_na_opravy_reseni'), | ||||||
|  |         ('prednasky', '0022_preklep_u_odpovedi_hlasovanioznalostech'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AddField( | ||||||
|  |             model_name='hlasovani', | ||||||
|  |             name='ucastnik_osoba', | ||||||
|  |             field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='personalni.osoba'), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
|  | @ -6,7 +6,7 @@ from personalni.models import Organizator, Osoba | ||||||
| 
 | 
 | ||||||
| class Seznam(models.Model): | class Seznam(models.Model): | ||||||
| 	""" | 	""" | ||||||
| 		Spojuje :py:class:`Přednášky <prednasky.models.Prednaska>` | 		Spojuje :py:class:`Přednášky <prednasky.models.Prednaska>` a :py:class:`Znalosti <prednasky.models.Znalost> | ||||||
| 		se :py:class:`Soustředěními <soustredeni.models.Soustredeni>`, | 		se :py:class:`Soustředěními <soustredeni.models.Soustredeni>`, | ||||||
| 		kde by mohly zaznít, nebo zazní/zazněly. | 		kde by mohly zaznít, nebo zazní/zazněly. | ||||||
| 	""" | 	""" | ||||||
|  | @ -19,8 +19,8 @@ class Seznam(models.Model): | ||||||
| 
 | 
 | ||||||
| 	class Stav(models.IntegerChoices): | 	class Stav(models.IntegerChoices): | ||||||
| 		""" Stav seznamu přednášek (NAVRH se používá k hlasování viz :py:func:`daný view <prednasky.views.newPrednaska>`). """ | 		""" Stav seznamu přednášek (NAVRH se používá k hlasování viz :py:func:`daný view <prednasky.views.newPrednaska>`). """ | ||||||
| 		NAVRH = 1, "Návrh" | 		NAVRH = 1, "Návrh" #: odpovídá před-soustřeďkové představě o tom, jaké přednášky dělat (dá se o nich třeba hlasovat ap.) | ||||||
| 		BUDE = 2, "Bude" | 		BUDE = 2, "Bude" #: odpovídá definitivní představě o tom, co bude/bylo a dá se porovnávat s novými návrhy | ||||||
| 
 | 
 | ||||||
| 	id = models.AutoField(primary_key=True) | 	id = models.AutoField(primary_key=True) | ||||||
| 	soustredeni = models.ForeignKey(Soustredeni, null=True, default=None, on_delete=models.PROTECT) | 	soustredeni = models.ForeignKey(Soustredeni, null=True, default=None, on_delete=models.PROTECT) | ||||||
|  | @ -88,6 +88,7 @@ class Hlasovani(models.Model): | ||||||
| 	#: že všechna předchozí hlasování zde mají náhodný string…) | 	#: že všechna předchozí hlasování zde mají náhodný string…) | ||||||
| 	#: TODO Změnit to na Osobu* | 	#: TODO Změnit to na Osobu* | ||||||
| 	ucastnik = models.CharField("Účastník", max_length=100) | 	ucastnik = models.CharField("Účastník", max_length=100) | ||||||
|  | 	ucastnik_osoba = models.ForeignKey(Osoba, on_delete=models.CASCADE, blank=False, null=True) | ||||||
| 	seznam = models.ForeignKey(Seznam, null=True, on_delete=models.SET_NULL) | 	seznam = models.ForeignKey(Seznam, null=True, on_delete=models.SET_NULL) | ||||||
| 
 | 
 | ||||||
| 	def __str__(self): | 	def __str__(self): | ||||||
|  | @ -98,6 +99,9 @@ class Znalost(models.Model): | ||||||
| 	""" | 	""" | ||||||
| 		Reprezentuje znalost, na kterou se můžeme účastníka ptát (nechat je hlasovat). | 		Reprezentuje znalost, na kterou se můžeme účastníka ptát (nechat je hlasovat). | ||||||
| 		(Viz :py:class:`HlasováníOZnalostech <prednasky.models.HlasovaniOZnalostech>`.) | 		(Viz :py:class:`HlasováníOZnalostech <prednasky.models.HlasovaniOZnalostech>`.) | ||||||
|  | 
 | ||||||
|  | 		(V podstatě :py:class:`Přednáška <prednasky.models.Prednaska>, jen neobsahuje | ||||||
|  | 		tolik detailů a v hlasování má jiné odpovědi.) | ||||||
| 	""" | 	""" | ||||||
| 	class Meta: | 	class Meta: | ||||||
| 		db_table = "prednasky_znalost" | 		db_table = "prednasky_znalost" | ||||||
|  | @ -117,6 +121,9 @@ class HlasovaniOZnalostech(models.Model): | ||||||
| 		Reprezentuje hlasování jednoho účastníka | 		Reprezentuje hlasování jednoho účastníka | ||||||
| 		o jedné :py:class:`Znalosti <prednasky.models.Znalost>` | 		o jedné :py:class:`Znalosti <prednasky.models.Znalost>` | ||||||
| 		v jednom :py:class:`Seznamu <prednasky.models.Seznam>` (účastníkův pohled se totiž mezi sousy změnit) | 		v jednom :py:class:`Seznamu <prednasky.models.Seznam>` (účastníkův pohled se totiž mezi sousy změnit) | ||||||
|  | 
 | ||||||
|  | 		(V podstatě totéž, co :py:class:`Hlasování <prednasky.models.Hlasovani>`, jen má jiné komentáře | ||||||
|  | 		u odpovědí a místo přednášky odkazuje na znalost.) | ||||||
| 	""" | 	""" | ||||||
| 	class Odpoved(models.IntegerChoices): | 	class Odpoved(models.IntegerChoices): | ||||||
| 		""" Na kolik danou znalost účastník ovládá v daném Hlasování (větší číslo = víc zná) """ | 		""" Na kolik danou znalost účastník ovládá v daném Hlasování (větší číslo = víc zná) """ | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ | ||||||
| <p>Obtížnost 1 je nejlehčí, 3 nejtěžší.</p> | <p>Obtížnost 1 je nejlehčí, 3 nejtěžší.</p> | ||||||
|   {{ form_set_prednasky.management_form }} |   {{ form_set_prednasky.management_form }} | ||||||
|   {% for f, p in formy_a_prednasky %} |   {% for f, p in formy_a_prednasky %} | ||||||
|  |     <div class="hlasovani-prednaska"> | ||||||
|     <h4>{{p.nazev}} ({{p.org}})</h4> |     <h4>{{p.nazev}} ({{p.org}})</h4> | ||||||
|     <p class="textprednasky">{{p.anotace | linebreaksbr}}</p> |     <p class="textprednasky">{{p.anotace | linebreaksbr}}</p> | ||||||
|     <label>Obor: </label> {{p.obor}}<br> |     <label>Obor: </label> {{p.obor}}<br> | ||||||
|  | @ -22,17 +23,20 @@ | ||||||
|     <br> |     <br> | ||||||
|     {{ f }} |     {{ f }} | ||||||
|     <br> |     <br> | ||||||
|  |     </div> | ||||||
|   {% empty %} |   {% empty %} | ||||||
|     Nejsou žádné přednášky o kterých by šlo hlasovat. |     Nejsou žádné přednášky o kterých by šlo hlasovat. | ||||||
|   {% endfor %} |   {% endfor %} | ||||||
| 
 | 
 | ||||||
|   {{ form_set_znalosti.management_form }} |   {{ form_set_znalosti.management_form }} | ||||||
|   {% for f, z in formy_a_znalosti %} |   {% for f, z in formy_a_znalosti %} | ||||||
|  |     <div class="hlasovani-znalost"> | ||||||
|     {% if forloop.first %}<hr/><h3>Jak moc znáš následující?</h3>{% endif %} |     {% if forloop.first %}<hr/><h3>Jak moc znáš následující?</h3>{% endif %} | ||||||
|     <h4>{{z.nazev}}</h4> |     <h4>{{z.nazev}}</h4> | ||||||
|     <p class="textznalosti">{{z.text | linebreaksbr}}</p> |     <p class="textznalosti">{{z.text | linebreaksbr}}</p> | ||||||
|     {{ f }} |     {{ f }} | ||||||
|     <br> |     <br> | ||||||
|  |     </div> | ||||||
|   {% endfor %} |   {% endfor %} | ||||||
|   <input type="submit" value="Odeslat"/> |   <input type="submit" value="Odeslat"/> | ||||||
| </form> | </form> | ||||||
|  |  | ||||||
|  | @ -2,19 +2,19 @@ | ||||||
| 
 | 
 | ||||||
| {% block content %} | {% block content %} | ||||||
|   <h1>{% block nadpis1a %} |   <h1>{% block nadpis1a %} | ||||||
| 	  Hlasování o přednáškách | 	  Výsledky hlasování o přednáškách | ||||||
| 	  {% endblock %}</h1> | 	  {% endblock %}</h1> | ||||||
|   {# Projdi vsechny seznamy #} |   {# Projdi vsechny seznamy #} | ||||||
|   <div class="mam-org-only"> |   <div class="mam-org-only"> | ||||||
|   <ul> |   <ul> | ||||||
|   {% for seznam in object_list %} |   {% for seznam in object_list %} | ||||||
|     <li> |     <li> | ||||||
|     {% if seznam.stav == 1 %} {# STAV_NAHRH = 1 #}  |     {% if seznam.stav == seznam.Stav.NAVRH %} | ||||||
|         <a href="/prednasky/seznam_prednasek/{{seznam.id}}">Návrh přednášek na soustředění {{seznam.soustredeni.misto}} </a> |       Návrh přednášek na soustředění {{seznam.soustredeni.misto}} | ||||||
|     {% else %} |     {% else %} | ||||||
|         <a href="/prednasky/seznam_prednasek/{{seznam.id}}">Seznam přednášek na soustředění {{seznam.soustredeni.misto}} </a> |       Seznam přednášek na soustředění {{seznam.soustredeni.misto}} | ||||||
|     {% endif %} |     {% endif %} | ||||||
|     <a href="/prednasky/seznam_prednasek/{{seznam.id}}/hlasovani.csv">Export</a> |       (<a href='{% url "seznam-export-csv" seznam=seznam.id %}'>CSV</a>) | ||||||
|     </li> |     </li> | ||||||
|   {% endfor %} |   {% endfor %} | ||||||
|   </ul> |   </ul> | ||||||
|  |  | ||||||
|  | @ -12,19 +12,9 @@ urlpatterns = [ | ||||||
| 		'prednasky/metaseznam_prednasek', | 		'prednasky/metaseznam_prednasek', | ||||||
| 		org_required(views.MetaSeznamListView.as_view()), | 		org_required(views.MetaSeznamListView.as_view()), | ||||||
| 		name='metaseznam-list'), | 		name='metaseznam-list'), | ||||||
| 	# path( |  | ||||||
| 	# 	'prednasky/seznam_prednasek/<int:seznam>/export', |  | ||||||
| 	# 	org_required(views.SeznamExportView), |  | ||||||
| 	# 	name='seznam-export' |  | ||||||
| 	# ), |  | ||||||
| 	path( | 	path( | ||||||
| 		'prednasky/seznam_prednasek/<int:seznam>/hlasovani.csv', | 		'prednasky/seznam_prednasek/<int:seznam>/hlasovani.csv', | ||||||
| 		org_required(views.PrednaskyExportView), | 		org_required(views.PrednaskyExportView), | ||||||
| 		name='seznam-export-csv' | 		name='seznam-export-csv' | ||||||
| 	), | 	), | ||||||
| 	path( |  | ||||||
| 		'prednasky/seznam_prednasek/<int:seznam>/', |  | ||||||
| 		org_required(views.SeznamListView.as_view()), |  | ||||||
| 		name='seznam-list' |  | ||||||
| 	), |  | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | @ -64,6 +64,7 @@ def newPrednaska(request: HttpRequest) -> HttpResponse: | ||||||
| 						prednaska=prednaska, | 						prednaska=prednaska, | ||||||
| 						body=form.cleaned_data['body'], | 						body=form.cleaned_data['body'], | ||||||
| 						ucastnik=ucastnik, | 						ucastnik=ucastnik, | ||||||
|  | 						ucastnik_osoba=osoba, | ||||||
| 						seznam=seznam, | 						seznam=seznam, | ||||||
| 					) | 					) | ||||||
| 
 | 
 | ||||||
|  | @ -116,8 +117,8 @@ def newPrednaska(request: HttpRequest) -> HttpResponse: | ||||||
| 		'prednasky/base.html', | 		'prednasky/base.html', | ||||||
| 		{ | 		{ | ||||||
| 			'form_set_prednasky': form_set_prednasky, 'form_set_znalosti': form_set_znalosti, | 			'form_set_prednasky': form_set_prednasky, 'form_set_znalosti': form_set_znalosti, | ||||||
| 			'formy_a_prednasky': zip(form_set_prednasky, prednasky), | 			'formy_a_prednasky': list(zip(form_set_prednasky, prednasky)), | ||||||
| 			'formy_a_znalosti': zip(form_set_znalosti, znalosti), | 			'formy_a_znalosti': list(zip(form_set_znalosti, znalosti)), | ||||||
| 		} | 		} | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
|  | @ -132,64 +133,6 @@ class MetaSeznamListView(generic.ListView): | ||||||
| 	template_name = 'prednasky/metaseznam_prednasek.html' | 	template_name = 'prednasky/metaseznam_prednasek.html' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class SeznamListView(generic.ListView): |  | ||||||
| 	""" |  | ||||||
| 		Náhled na to, kolik má která přednáška v :py:class:`Seznamu <prednasky.models.Seznam>` :py:class:`hlasů <prednasky.models.Hlasovani.Body>`. |  | ||||||
| 		(Je otázka, zda tento View vůbec chceme. Pokud ano, hodilo by se do něj přidat i znalosti.) |  | ||||||
| 	""" |  | ||||||
| 	template_name = 'prednasky/seznam_prednasek.html' |  | ||||||
| 
 |  | ||||||
| 	def get_queryset(self): |  | ||||||
| 		self.seznam = get_object_or_404(Seznam, id=self.kwargs["seznam"]) |  | ||||||
| 		prednasky = Prednaska.objects.filter(seznamy=self.seznam).order_by( |  | ||||||
| 			'org__osoba__user__first_name', 'org__osoba__user__last_name' |  | ||||||
| 		) |  | ||||||
| 		return prednasky |  | ||||||
| 
 |  | ||||||
| 	# FIXME nahradit anotaci s filtrem po prechodu na Django 2.2 |  | ||||||
| 	def get_context_data(self,**kwargs): |  | ||||||
| 		context = super(SeznamListView, self).get_context_data(**kwargs) |  | ||||||
| 
 |  | ||||||
| 		# hlasovani se vztahuje k nejnovejsimu soustredeni |  | ||||||
| 		sous = Soustredeni.objects.first() |  | ||||||
| 		seznam = Seznam.objects.filter(soustredeni = sous, stav=Seznam.Stav.NAVRH).first() |  | ||||||
| 	 |  | ||||||
| 		for obj in self.object_list: |  | ||||||
| 			hlasovani_set = obj.hlasovani_set.filter(seznam=seznam).only('body') |  | ||||||
| 			obj.body = sum(map(lambda x: x.body,hlasovani_set)) |  | ||||||
| 
 |  | ||||||
| 		return context |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # def SeznamExportView(request, seznam): |  | ||||||
| # 	"""Vypíše výsledky hlasování ve formátu pro prologovský optimalizátor""" |  | ||||||
| # 	# TODO zřejmě se nepoužívá, časem vyřadit? nahradit tabulkou vhodnější pro |  | ||||||
| # 	# lidi? |  | ||||||
| # 	hlasovani = Hlasovani.objects.filter(seznam=seznam) |  | ||||||
| # 	prednasky = Prednaska.objects.filter(seznamy=seznam) |  | ||||||
| # 	orgove = set(p.org for p in prednasky) |  | ||||||
| # 	ucastnici = set(h.ucastnik for h in hlasovani) |  | ||||||
| # |  | ||||||
| # 	for p in prednasky: |  | ||||||
| # 		p.body = [] |  | ||||||
| # 		for u in ucastnici: |  | ||||||
| # 			try: |  | ||||||
| # 				p.body.append(hlasovani.get(ucastnik=u, prednaska=p).body) |  | ||||||
| # 			except ObjectDoesNotExist: |  | ||||||
| # 				# účastník nehlasoval |  | ||||||
| # 				p.body.append("?") |  | ||||||
| # |  | ||||||
| # 	for h in hlasovani: |  | ||||||
| # 		h.ucastnik = hash(h.ucastnik) |  | ||||||
| # |  | ||||||
| # 	return render( |  | ||||||
| # 		request, |  | ||||||
| # 		'prednasky/seznam_prednasek_export.txt', |  | ||||||
| # 		{"hlasovani": hlasovani, "prednasky": prednasky, "orgove": orgove}, |  | ||||||
| # 		content_type="text/plain" |  | ||||||
| # 	) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def PrednaskyExportView(request: HttpRequest, seznam: int, **kwargs) -> HttpResponse: | def PrednaskyExportView(request: HttpRequest, seznam: int, **kwargs) -> HttpResponse: | ||||||
| 	""" | 	""" | ||||||
| 		Vrátí všechna :py:class:`Hlasování <prednasky.models.Hlasovani>` | 		Vrátí všechna :py:class:`Hlasování <prednasky.models.Hlasovani>` | ||||||
|  | @ -214,6 +157,8 @@ def PrednaskyExportView(request: HttpRequest, seznam: int, **kwargs) -> HttpResp | ||||||
| 	# A po inicializaci sloupců vyplníme tabulku | 	# A po inicializaci sloupců vyplníme tabulku | ||||||
| 	table: [str, list[str|Prednaska|Znalost,]] = {} | 	table: [str, list[str|Prednaska|Znalost,]] = {} | ||||||
| 
 | 
 | ||||||
|  | 	errors = [] | ||||||
|  | 
 | ||||||
| 	for h in hlasovani: | 	for h in hlasovani: | ||||||
| 		if h.ucastnik not in table: # Pokud jsme účastníka ještě neviděli, předgenerujeme si jeho řádek | 		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) | 			table[h.ucastnik] = [h.ucastnik] + ([""] * width) | ||||||
|  | @ -221,7 +166,7 @@ def PrednaskyExportView(request: HttpRequest, seznam: int, **kwargs) -> HttpResp | ||||||
| 		if h.prednaska.id in prednasky_map: | 		if h.prednaska.id in prednasky_map: | ||||||
| 			table[h.ucastnik][prednasky_map[h.prednaska.id]] = h.body | 			table[h.ucastnik][prednasky_map[h.prednaska.id]] = h.body | ||||||
| 		else: | 		else: | ||||||
| 			pass # TODO Padat hlasitě? | 			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: | 	for h in hlasovani_o_znalostech: | ||||||
| 		ucastnik = str(h.ucastnik) + ' ' + str(h.ucastnik.id) # id, kvůli kolizi jmen | 		ucastnik = str(h.ucastnik) + ' ' + str(h.ucastnik.id) # id, kvůli kolizi jmen | ||||||
|  | @ -231,8 +176,10 @@ def PrednaskyExportView(request: HttpRequest, seznam: int, **kwargs) -> HttpResp | ||||||
| 		if h.znalost.id in znalosti_map: | 		if h.znalost.id in znalosti_map: | ||||||
| 			table[ucastnik][znalosti_map[h.znalost.id]] = h.odpoved | 			table[ucastnik][znalosti_map[h.znalost.id]] = h.odpoved | ||||||
| 		else: | 		else: | ||||||
| 			pass # TODO Padat hlasitě? | 			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 = HttpResponse(content_type="text/csv", charset="utf-8") | ||||||
| 	response["Content-Disposition"] = 'attachment; filename="hlasovani.csv"' | 	response["Content-Disposition"] = 'attachment; filename="hlasovani.csv"' | ||||||
|  |  | ||||||
|  | @ -16,4 +16,11 @@ | ||||||
| 	  {% endfor %} | 	  {% endfor %} | ||||||
|   </ul> |   </ul> | ||||||
| 
 | 
 | ||||||
|  |   <h2>Seznam účastníků – červená znamená že jim nechodí fyzické číslo</h2> | ||||||
|  |   <ul> | ||||||
|  |     {% for resitel in resitele %} | ||||||
|  |       <li {% if resitel.neposilame %}style="color: white; background-color: red;"{% endif %}>{{ resitel.jmeno }}: {% if resitel.bodydiff > 3 %}🧦{% endif %} {% if resitel.ttitul != resitel.ftitul %} {{resitel.ftitul}} → {{resitel.ttitul}} {% endif %}</li> | ||||||
|  |     {% endfor %} | ||||||
|  |   </ul> | ||||||
|  | 
 | ||||||
| {% endblock content %} | {% endblock content %} | ||||||
|  |  | ||||||
|  | @ -28,6 +28,30 @@ def resi_v_rocniku(rocnik, cislo=None): | ||||||
| 			reseni__hodnoceni__deadline_body__cislo__poradi__lte=cislo.poradi | 			reseni__hodnoceni__deadline_body__cislo__poradi__lte=cislo.poradi | ||||||
| 		).distinct() | 		).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): | def aktivniResitele(cislo, pouze_letosni=False): | ||||||
| 	""" Vrací QuerySet aktivních řešitelů, což jsou ti, co ještě neodmaturovali | 	""" Vrací QuerySet aktivních řešitelů, což jsou ti, co ještě neodmaturovali | ||||||
|  |  | ||||||
|  | @ -375,7 +375,8 @@ class OdmenyView(generic.TemplateView): | ||||||
| 		tocislo = get_object_or_404(Cislo, rocnik=self.kwargs.get('trocnik'), poradi=self.kwargs.get('tcislo')) | 		tocislo = get_object_or_404(Cislo, rocnik=self.kwargs.get('trocnik'), poradi=self.kwargs.get('tcislo')) | ||||||
| 		resitele = utils.aktivniResitele(tocislo) | 		resitele = utils.aktivniResitele(tocislo) | ||||||
| 
 | 
 | ||||||
| 		def get_diff(from_deadline: Deadline, to_deadline: Deadline): | 		def get_diff(from_deadline: Deadline, to_deadline: Deadline, probody=False): | ||||||
|  | 			"""Co je probody? pokud True, funkce vrací všechny rešitele a k nim potřebné informace, pokud False, vrací jen ty, kteří mají změnu v titulu.""" | ||||||
| 			frombody = body_resitelu(resitele=resitele, jen_verejne=False, do=from_deadline) | 			frombody = body_resitelu(resitele=resitele, jen_verejne=False, do=from_deadline) | ||||||
| 			tobody = body_resitelu(resitele=resitele, jen_verejne=False, do=to_deadline) | 			tobody = body_resitelu(resitele=resitele, jen_verejne=False, do=to_deadline) | ||||||
| 			outlist = [] | 			outlist = [] | ||||||
|  | @ -384,6 +385,9 @@ class OdmenyView(generic.TemplateView): | ||||||
| 				tbody = tobody.get(resitel.id, 0) | 				tbody = tobody.get(resitel.id, 0) | ||||||
| 				ftitul = resitel.get_titul(fbody) | 				ftitul = resitel.get_titul(fbody) | ||||||
| 				ttitul = resitel.get_titul(tbody) | 				ttitul = resitel.get_titul(tbody) | ||||||
|  | 				if probody: | ||||||
|  | 					outlist.append({'jmeno': resitel.osoba.plne_jmeno(), 'fbody': fbody, 'tbody': tbody, 'ftitul': ftitul, 'ttitul': ttitul, 'bodydiff': tbody - fbody, "neposilame": not(resitel.zasilat_cislo_papirove)}) | ||||||
|  | 				else: | ||||||
| 					if ftitul != ttitul: | 					if ftitul != ttitul: | ||||||
| 						outlist.append({'jmeno': resitel.osoba.plne_jmeno(), 'ftitul': ftitul, 'ttitul': ttitul}) | 						outlist.append({'jmeno': resitel.osoba.plne_jmeno(), 'ftitul': ftitul, 'ttitul': ttitul}) | ||||||
| 			return outlist | 			return outlist | ||||||
|  | @ -401,6 +405,7 @@ class OdmenyView(generic.TemplateView): | ||||||
| 		context["from_deadline"] = from_deadline | 		context["from_deadline"] = from_deadline | ||||||
| 		context["to_deadline"] = to_deadline | 		context["to_deadline"] = to_deadline | ||||||
| 		context["zmeny"] = get_diff(from_deadline, to_deadline) | 		context["zmeny"] = get_diff(from_deadline, to_deadline) | ||||||
|  | 		context["resitele"] = get_diff(from_deadline, to_deadline, probody=resitele.order_by("osoba__prijmeni")) | ||||||
| 
 | 
 | ||||||
| 		return context | 		return context | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue