Merge pull request 'Vylepšení odevzdávátka' (!13) from vylepseni_odevzdavatka into master
Reviewed-on: #13
This commit is contained in:
		
						commit
						3110eb92a5
					
				
					 17 changed files with 218 additions and 170 deletions
				
			
		|  | @ -22,6 +22,7 @@ urlpatterns = [ | |||
| 	# Autocomplete | ||||
| 	path('api/autocomplete/skola/', views.SkolaAutocomplete.as_view(), name='autocomplete_skola'), | ||||
| 	path('api/autocomplete/resitel/', org_required(views.ResitelAutocomplete.as_view()), name='autocomplete_resitel'), | ||||
| 	path('api/autocomplete/resitel_public/', views.PublicResitelAutocomplete.as_view(), name='autocomplete_resitel_public'), | ||||
| 	path('api/autocomplete/problem/odevzdatelny', views.OdevzdatelnyProblemAutocomplete.as_view(), name='autocomplete_problem_odevzdatelny'), | ||||
| 	path('api/autocomplete/problem/vsechny', views.ProblemAutocomplete.as_view(), name='autocomplete_problem'), | ||||
| 
 | ||||
|  |  | |||
|  | @ -44,6 +44,29 @@ class ResitelAutocomplete(LoginRequiredAjaxMixin,autocomplete.Select2QuerySetVie | |||
| 			qs = qs.filter(query) | ||||
| 		return qs | ||||
| 
 | ||||
| 
 | ||||
| class PublicResitelAutocomplete(LoginRequiredAjaxMixin, autocomplete.Select2QuerySetView): | ||||
| 	""" | ||||
| 		View k :mod:`dal.autocomplete` pro vyhledávání řešitelů podle přezdívky | ||||
| 		především v odevzdávátku. | ||||
| 	""" | ||||
| 	def get_queryset(self): | ||||
| 		letos = m.Nastaveni.get_solo().aktualni_rocnik | ||||
| 		qs = m.Resitel.objects.filter( | ||||
| 			rok_maturity__gte=letos.druhy_rok() | ||||
| 		).filter( | ||||
| 			prezdivka_resitele__isnull=False | ||||
| 		).exclude( | ||||
| 			prezdivka_resitele="" | ||||
| 		).filter( | ||||
| 			prezdivka_resitele__icontains=self.q | ||||
| 		).all() | ||||
| 		return qs | ||||
| 
 | ||||
| 	def get_result_label(self, result): | ||||
| 		return result.prezdivka_resitele | ||||
| 
 | ||||
| 
 | ||||
| class OdevzdatelnyProblemAutocomplete(autocomplete.Select2QuerySetView): | ||||
| 	""" View k :mod:`dal.autocomplete` pro vyhledávání problémů především v odevzdávátku. """ | ||||
| 	def get_queryset(self): | ||||
|  |  | |||
|  | @ -63,7 +63,7 @@ class PosliReseniForm(forms.Form): | |||
| class NahrajReseniForm(forms.ModelForm): | ||||
| 	class Meta: | ||||
| 		model = m.Reseni | ||||
| 		fields = ('problem',) | ||||
| 		fields = ('problem', 'resitele') | ||||
| 		help_texts = {'problem':''} # Nezobrazovat help text ve formuláři | ||||
| 		 | ||||
| 		widgets = {'problem': | ||||
|  | @ -72,6 +72,13 @@ class NahrajReseniForm(forms.ModelForm): | |||
| 					attrs = {'data-placeholder--id': '-1', | ||||
| 						'data-placeholder--text' : '---', | ||||
| 						'data-allow-clear': 'true'}, | ||||
| 				), | ||||
| 				'resitele': | ||||
| 				autocomplete.ModelSelect2Multiple( | ||||
| 					url='autocomplete_resitel_public', | ||||
| 					attrs = {'data-placeholder--id': '-1', | ||||
| 						'data-placeholder--text' : '---', | ||||
| 						'data-allow-clear': 'true'}, | ||||
| 				) | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										22
									
								
								odevzdavatko/static/odevzdavatko/check_for_detail.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								odevzdavatko/static/odevzdavatko/check_for_detail.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| // Kontrola, že org neposílá nějakou blbost v detail.html
 | ||||
| 
 | ||||
| function zkontroluj_hodnoceni() { | ||||
|     const pocet = $('.hodnoceni').length; | ||||
|     if (pocet === 1) { // vidím pouze plusko
 | ||||
|         const vysledek = confirm("Odstranil jsi všechny problémy tohoto řešení. Nepůjde tedy dohledat přes problémy, co řeší, tj. například v došlých řešeních. Přesto odeslat?"); | ||||
|         if (!vysledek) { | ||||
|             event.preventDefault(); | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     function problem_is_empty(elem, index, array) {return elem.firstElementChild.children.length !== 1 && elem.firstElementChild.children[1].textContent === "";} | ||||
| 
 | ||||
|     if ($('.hodnoceni').toArray().some(problem_is_empty)) { | ||||
|         alert("Neuloženo! Nezadal jsi problém, ke kterému posíláš hodnocení. Pokud je toto hodnocení navíc, smaž ho prosím křížkem a znovu odešli.") | ||||
|         event.preventDefault() | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
|  | @ -0,0 +1,56 @@ | |||
| // FIXME: Necopypastovat! Tohle je zkopírované ze static/odevzdavatko/dynamic_formsets.js
 | ||||
| 
 | ||||
| 
 | ||||
| // Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0
 | ||||
| function updateElementIndex(el, prefix, ndx) { | ||||
| 	var id_regex = new RegExp('(' + prefix + '-\\d+)'); | ||||
| 	var replacement = prefix + '-' + ndx; | ||||
| 	if ($(el).attr("for")) { | ||||
| 		$(el).attr("for", $(el).attr("for").replace(id_regex, replacement)); | ||||
| 	} | ||||
| 	if (el.id) { | ||||
| 		el.id = el.id.replace(id_regex, replacement); | ||||
| 	} | ||||
| 	if (el.name) { | ||||
| 		el.name = el.name.replace(id_regex, replacement); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0
 | ||||
| function deleteForm(prefix, btn) { | ||||
|     var total = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val()); | ||||
|     if (total >= 1){ | ||||
|         btn.closest('tr').remove(); | ||||
|         var forms = $('.hodnoceni'); | ||||
|         var formCount = forms.length - 1; // There is one extra such form hidden as template!
 | ||||
|         $('#id_' + prefix + '-TOTAL_FORMS').val(formCount); | ||||
|         for (var i=0; i<formCount; i++) { | ||||
|             $(forms.get(i)).find(':input').each(function() { | ||||
|                 updateElementIndex(this, prefix, i); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| // Credit: https://simpleit.rocks/python/django/dynamic-add-form-with-add-button-in-django-modelformset-template/
 | ||||
| $(document).ready(function(){ | ||||
| 	$('#pridat_hodnoceni').click(function() { | ||||
| 		var form_idx = $('#id_form-TOTAL_FORMS').val(); | ||||
| 		var new_form = $('#empty_form').html().replace(/__prefix__/g, form_idx); | ||||
| 		$('#form_set').append(new_form); | ||||
| 		// Newly created form has not the binding between remove button and remove function
 | ||||
| 		// We need to add it manually
 | ||||
| 		$('.smazat_hodnoceni').click(function(){ | ||||
| 			deleteForm("form",this); | ||||
| 		}); | ||||
|         // Copy deadline
 | ||||
|         if (form_idx !== "0") { | ||||
|             $('#id_form-' + form_idx + '-deadline_body')[0].value = $('#id_form-' + (form_idx - 1) + '-deadline_body')[0].value | ||||
|         } | ||||
| 		$('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1); | ||||
| 	}); | ||||
| 	$('.smazat_hodnoceni').click(function(){ | ||||
| 		deleteForm("form",this); | ||||
| 	}); | ||||
| }); | ||||
|  | @ -4,68 +4,21 @@ | |||
| 
 | ||||
| {% block content %} | ||||
| 
 | ||||
| {# FIXME: Necopypastovat! Tohle je zkopírované ze static/odevzdavatko/dynamic_formsets.js #} | ||||
| <script type='text/javascript'> | ||||
| // Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0 | ||||
| function updateElementIndex(el, prefix, ndx) { | ||||
| 	var id_regex = new RegExp('(' + prefix + '-\\d+)'); | ||||
| 	var replacement = prefix + '-' + ndx; | ||||
| 	if ($(el).attr("for")) { | ||||
| 		$(el).attr("for", $(el).attr("for").replace(id_regex, replacement)); | ||||
| 	} | ||||
| 	if (el.id) { | ||||
| 		el.id = el.id.replace(id_regex, replacement); | ||||
| 	} | ||||
| 	if (el.name) { | ||||
| 		el.name = el.name.replace(id_regex, replacement); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0 | ||||
| function deleteForm(prefix, btn) { | ||||
|     var total = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val()); | ||||
|     if (total >= 1){ | ||||
|         btn.closest('tr').remove(); | ||||
|         var forms = $('.hodnoceni'); | ||||
|         var formCount = forms.length - 1; // There is one extra such form hidden as template! | ||||
|         $('#id_' + prefix + '-TOTAL_FORMS').val(formCount); | ||||
|         for (var i=0; i<formCount; i++) { | ||||
|             $(forms.get(i)).find(':input').each(function() { | ||||
|                 updateElementIndex(this, prefix, i); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| // Credit: https://simpleit.rocks/python/django/dynamic-add-form-with-add-button-in-django-modelformset-template/ | ||||
| $(document).ready(function(){ | ||||
| 	$('#pridat_hodnoceni').click(function() { | ||||
| 		var form_idx = $('#id_form-TOTAL_FORMS').val(); | ||||
| 		var new_form = $('#empty_form').html().replace(/__prefix__/g, form_idx); | ||||
| 		$('#form_set').append(new_form); | ||||
| 		// Newly created form has not the binding between remove button and remove function | ||||
| 		// We need to add it manually | ||||
| 		$('.smazat_hodnoceni').click(function(){ | ||||
| 			deleteForm("form",this); | ||||
| 		}); | ||||
|         // Copy deadline | ||||
|         if (form_idx !== "0") { | ||||
|             $('#id_form-' + form_idx + '-deadline_body')[0].value = $('#id_form-' + (form_idx - 1) + '-deadline_body')[0].value | ||||
|         } | ||||
| 		$('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1); | ||||
| 	}); | ||||
| 	$('.smazat_hodnoceni').click(function(){ | ||||
| 		deleteForm("form",this); | ||||
| 	}); | ||||
| }); | ||||
| </script> | ||||
|   {% if edit %} | ||||
|     <script src="{% static 'odevzdavatko/dynamic_formsets_for_detail.js' %}"></script> | ||||
|     <script src="{% static 'odevzdavatko/check_for_detail.js' %}"></script> | ||||
|   {% endif %} | ||||
| 
 | ||||
| 
 | ||||
| <p>Řešené problémy: {{ object.problem.all | join:", " }}</p> | ||||
| 
 | ||||
| <p>Řešitelé: {% for r in object.resitele.all %} {{ r }} (<a href="mailto:{{ r.osoba.email }}?subject={{ "Oprava řešení M&M " | urlencode }}{{ object.problem.all.0.hlavni_problem | urlencode }}">{{ r.osoba.email }}</a>) | ||||
| {% if forloop.revcounter0 != 0 %}, {% endif %} {% endfor %}</p> | ||||
| {% if edit %} | ||||
|   <p>Řešitelé: | ||||
|     {% for r in object.resitele.all %}{{ r }} (<a href="mailto:{{ r.osoba.email }}?subject={{ "Oprava řešení M&M " | urlencode }}{{ object.problem.all.0.hlavni_problem | urlencode }}">{{ r.osoba.email }}</a>){% if forloop.revcounter0 != 0 %}, {% endif %}{% endfor %} | ||||
|   </p> | ||||
| {% else %} | ||||
|   <p>Řešitelé: {{ object.resitele.all | join:", " }}</p> | ||||
| {% endif %} | ||||
| 
 | ||||
| {# https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.get_FOO_display #} | ||||
| <p>Forma: {{ object.get_forma_display }}</p> | ||||
|  | @ -82,13 +35,13 @@ $(document).ready(function(){ | |||
| 	<td><a href="{{ priloha.soubor.url }}" download>{{ priloha.split | last }}</a></td> | ||||
| 	<td>{{ priloha.res_poznamka }}</td> | ||||
| 	<td>{{ priloha.vytvoreno }}</td></tr> | ||||
| 	{# TODO: Orgo-poznámka, ideálně jako formulář #} | ||||
| {% endfor %} | ||||
| </table> | ||||
| {% else %} | ||||
| <p>Žádné přílohy</p> | ||||
| {% endif %} | ||||
| 
 | ||||
|   {% if edit %} | ||||
| <form method=post onsubmit="return zkontroluj_hodnoceni();"> | ||||
| {# Poznámka #} | ||||
| <h3>Neveřejná poznámka:</h3> | ||||
|  | @ -116,7 +69,7 @@ $(document).ready(function(){ | |||
| </table> | ||||
| 
 | ||||
| 
 | ||||
| <a href="#" title="Přidat hodnocení"> <img src="{% static "odevzdavatko/plus.png" %}" id="pridat_hodnoceni" alt="Přidat hodnocení"></a> </br> | ||||
| <a href="#" title="Přidat hodnocení"> <img src="{% static "odevzdavatko/plus.png" %}" id="pridat_hodnoceni" alt="Přidat hodnocení"></a> <br/> | ||||
| <input type=submit value="Uložit"></form> | ||||
| 
 | ||||
| <table id="empty_form" style="display: none;"> | ||||
|  | @ -129,28 +82,19 @@ $(document).ready(function(){ | |||
| 	</tr> | ||||
| </table> | ||||
| 
 | ||||
|   {% else %} | ||||
| <h3>Hodnocení:</h3> | ||||
| <table class="dosla_reseni"> | ||||
| <tr><th>Problém</th><th>Body</th><th>Zpětná vazba od opravovatele</th></tr> | ||||
| {% for h in hodnoceni %} | ||||
| 	<tr class="hodnoceni"> | ||||
| 		<td>{{ h.problem }}</td> | ||||
| 		<td>{{ h.body }}</td> | ||||
| 		<td>{{ h.feedback }}</td> | ||||
| 	</tr> | ||||
| {% endfor %} | ||||
| </table> | ||||
|   {% endif %} | ||||
| 
 | ||||
| <script type="text/javascript"> | ||||
|     function zkontroluj_hodnoceni() { | ||||
|         const pocet = $('.hodnoceni').length; | ||||
|         if (pocet === 1) { {# vydím pouze plusko #} | ||||
|             const vysledek = confirm("Odstranil jsi všechny problémy tohoto řešení. Nepůjde tedy dohledat přes problémy, co řeší, tj. například v došlých řešeních. Přesto odeslat?"); | ||||
|             if (!vysledek) { | ||||
|                 event.preventDefault(); | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         function problem_is_empty(elem, index, array) {return elem.firstElementChild.children.length !== 1 && elem.firstElementChild.children[1].textContent === "";} | ||||
| 
 | ||||
|         if ($('.hodnoceni').toArray().some(problem_is_empty)) { | ||||
|             alert("Neuloženo! Nezadal jsi problém, ke kterému posíláš hodnocení. Pokud je toto hodnocení navíc, smaž ho prosím křížkem a znovu odešli.") | ||||
|             event.preventDefault() | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
| </script> | ||||
| 
 | ||||
| {% endblock %} | ||||
|  |  | |||
|  | @ -1,51 +0,0 @@ | |||
| {% extends "base.html" %} | ||||
| {% load static %} | ||||
| {% load deadliny %} | ||||
| 
 | ||||
| {% block content %} | ||||
| 
 | ||||
| <p>Řešené problémy: {{ object.problem.all | join:", " }}</p> | ||||
| 
 | ||||
| <p>Řešitelé: {% for r in object.resitele.all %} {{ r }} | ||||
|     {% if forloop.revcounter0 != 0 %}, {% endif %} {% endfor %}</p> | ||||
| 
 | ||||
| {# https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.get_FOO_display #} | ||||
| <p>Forma: {{ object.get_forma_display }}</p> | ||||
| 
 | ||||
| <p>Doručeno {{ object.cas_doruceni }}, deadline: {{object.deadline_reseni | deadline_html }}</p> | ||||
| 
 | ||||
| {# Soubory: #} | ||||
| <h3>Přílohy:</h3> | ||||
| {% if object.prilohy.all %} | ||||
| <table class="dosla_reseni"> | ||||
| <tr><th>Soubor</th><th>Řešitelova poznámka</th><th>Datum</th></tr> | ||||
| {% for priloha in object.prilohy.all %} | ||||
| <tr> | ||||
| 	<td><a href="{{ priloha.soubor.url }}" download>{{ priloha.split | last }}</a></td> | ||||
| 	<td>{{ priloha.res_poznamka }}</td> | ||||
| 	<td>{{ priloha.vytvoreno }}</td></tr> | ||||
| 	{# TODO: Orgo-poznámka, ideálně jako formulář #} | ||||
| {% endfor %} | ||||
| </table> | ||||
| {% else %} | ||||
| <p>Žádné přílohy</p> | ||||
| {% endif %} | ||||
| 
 | ||||
| {#<h3>Poznámka:</h3>#} | ||||
| {#<p>{{ poznamka }}</p>#} | ||||
| 
 | ||||
| {# Hodnocení: #} | ||||
| <h3>Hodnocení:</h3> | ||||
| <table id="form_set" class="dosla_reseni"> | ||||
| <tr><th>Problém</th><th>Body</th><th>Zpětná vazba od opravovatele</th>{# <th>Deadline pro body</th> #}</tr> | ||||
| {% for h in hodnoceni %} | ||||
| 	<tr class="hodnoceni"> | ||||
| 		<td>{{ h.problem }}</td> | ||||
| 		<td>{{ h.body }}</td> | ||||
| 		<td>{{ h.feedback }}</td> | ||||
| {#		<td>{{ h.deadline_body }}</td>#} | ||||
| 	</tr> | ||||
| {% endfor %} | ||||
| </table> | ||||
| 
 | ||||
| {% endblock %} | ||||
|  | @ -13,6 +13,8 @@ | |||
| 
 | ||||
| <p style="text-align: justify">Když řešení různých témátek vložíš každé zvlášť, lépe se v nich vyznáme a třeba ti je i rychleji opravíme.</p> | ||||
| 
 | ||||
|   <p>Pokud řešíte ve více lidech, je <strong>nutné</strong> přidat tyto lidi jako „Autory řešení“. V tomto poli se vyhledává podle přezdívek, které si lze nastavit v „Osobní údaje“. Sebe vyplňovat nemusíte a za skupinu odevzdávejte pouze <strong>jednou</strong> (ne každý sám).</p> | ||||
| 
 | ||||
| <form enctype="multipart/form-data" action="{% url 'seminar_nahraj_reseni' %}" method="post" onsubmit="return zkontroluj_prilohy();"> | ||||
|   {% csrf_token %} | ||||
|   <table class='form' id="reseni"> | ||||
|  |  | |||
|  | @ -26,9 +26,9 @@ urlpatterns = [ | |||
| 	path('org/reseni/', org_required(views.TabulkaOdevzdanychReseniView.as_view()), name='odevzdavatko_tabulka'), | ||||
| 	path('org/reseni/rocnik/<int:rocnik>/', org_required(views.TabulkaOdevzdanychReseniView.as_view()), name='odevzdavatko_tabulka'), | ||||
| 	path('org/reseni/<int:problem>/<int:resitel>/', org_required(views.ReseniProblemuView.as_view()), name='odevzdavatko_reseni_resitele_k_problemu'), | ||||
| 	path('org/reseni/<int:pk>', org_required(viewMethodSwitch(get=views.DetailReseniView.as_view(), post=views.hodnoceniReseniView)), name='odevzdavatko_detail_reseni'), | ||||
| 	path('org/reseni/<int:pk>', org_required(viewMethodSwitch(get=views.EditReseniView.as_view(), post=views.hodnoceniReseniView)), name='odevzdavatko_detail_reseni'), | ||||
| 	path('org/reseni/all', org_required(views.SeznamReseniView.as_view())), | ||||
| 	path('org/reseni/akt', org_required(views.SeznamAktualnichReseniView.as_view())), | ||||
| 
 | ||||
| 	path('resitel/reseni/<int:pk>', resitel_or_org_required(views.ResitelReseniView.as_view()), name='odevzdavatko_resitel_reseni'), | ||||
| 	path('resitel/reseni/<int:pk>', resitel_or_org_required(views.DetailReseniView.as_view()), name='odevzdavatko_resitel_reseni'), | ||||
| ] | ||||
|  |  | |||
|  | @ -216,6 +216,7 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi | |||
| 
 | ||||
| ## XXX: https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#avoid-anything-more-complex | ||||
| class DetailReseniView(DetailView): | ||||
| 	""" Náhled na řešení. Editace je v :py:class:`EditReseniView`. """ | ||||
| 	model = m.Reseni | ||||
| 	template_name = 'odevzdavatko/detail.html' | ||||
| 	 | ||||
|  | @ -232,18 +233,44 @@ class DetailReseniView(DetailView): | |||
| 		return result | ||||
| 
 | ||||
| 	def get_context_data(self, **kw): | ||||
| 		self.check_access() | ||||
| 		ctx = super().get_context_data(**kw) | ||||
| 		ctx['form'] = f.OhodnoceniReseniFormSet( | ||||
| 				initial = self.aktualni_hodnoceni() | ||||
| 				) | ||||
| 		ctx['poznamka_form'] = f.PoznamkaReseniForm(instance=self.reseni) | ||||
| 		hodnoceni = self.aktualni_hodnoceni() | ||||
| 		ctx["hodnoceni"] = hodnoceni | ||||
| 		return ctx | ||||
| 
 | ||||
| 	def get(self, request, *args, **kwargs): | ||||
| 		""" | ||||
| 			Oproti :py:class:`django.views.generic.detail.BaseDetailView` | ||||
| 			kontroluje přístup pomocí :py:meth:`check_access` | ||||
| 		""" | ||||
| 		response = super().get(self, request, *args, **kwargs) | ||||
| 		self.check_access() | ||||
| 		return response | ||||
| 
 | ||||
| 	def check_access(self): | ||||
| 		""" Řešitel musí být součástí řešení, jinak se na něj nemá co dívat. """ | ||||
| 		if not self.object.resitele.filter(osoba__user=self.request.user).exists(): | ||||
| 			raise PermissionDenied() | ||||
| 
 | ||||
| 
 | ||||
| class EditReseniView(DetailReseniView): | ||||
| 	""" Editace (hlavně hodnocení) řešení.  """ | ||||
| 	def get_context_data(self, **kw): | ||||
| 		ctx = super().get_context_data(**kw) | ||||
| 		ctx['form'] = f.OhodnoceniReseniFormSet(initial=ctx["hodnoceni"]) | ||||
| 		ctx['poznamka_form'] = f.PoznamkaReseniForm(instance=self.reseni) | ||||
| 		ctx['edit'] = True | ||||
| 		return ctx | ||||
| 
 | ||||
| 	def check_access(self): | ||||
| 		# Na orga máme nároky už v urls.py ale better safe then sorry | ||||
| 		if not self.request.user.je_org: | ||||
| 			raise PermissionDenied() | ||||
| 
 | ||||
| 
 | ||||
| def hodnoceniReseniView(request, pk, *args, **kwargs): | ||||
| 	reseni = get_object_or_404(m.Reseni, pk=pk) | ||||
| 	template_name = 'odevzdavatko/detail.html' | ||||
| 	form_class = f.OhodnoceniReseniFormSet | ||||
| 	success_url = reverse('odevzdavatko_detail_reseni', kwargs={'pk': pk}) | ||||
| 
 | ||||
| 	# FIXME: Použit initial i tady a nebastlit hodnocení tak nízkoúrovňově | ||||
|  | @ -275,33 +302,6 @@ def hodnoceniReseniView(request, pk, *args, **kwargs): | |||
| 	return redirect(success_url) | ||||
| 
 | ||||
| 
 | ||||
| class ResitelReseniView(DetailView): | ||||
| 	model = m.Reseni | ||||
| 	template_name = 'odevzdavatko/detail_resitele.html' | ||||
| 
 | ||||
| 	def aktualni_hodnoceni(self): | ||||
| 		self.reseni = get_object_or_404(m.Reseni, id=self.kwargs['pk']) | ||||
| 		result = [] | ||||
| 		for hodn in m.Hodnoceni.objects.filter(reseni=self.reseni): | ||||
| 			result.append( | ||||
| 				{ | ||||
| 					"problem": hodn.problem, | ||||
| 					"body": hodn.body, | ||||
| 					"feedback": hodn.feedback, | ||||
| 					# "deadline_body": hodn.deadline_body, | ||||
| 				} | ||||
| 			) | ||||
| 		return result | ||||
| 
 | ||||
| 	def get_context_data(self, **kw): | ||||
| 		ctx = super().get_context_data(**kw) | ||||
| 		hodnoceni = self.aktualni_hodnoceni() | ||||
| 		if not self.reseni.resitele.filter(osoba__user=self.request.user).exists(): | ||||
| 			raise PermissionDenied() | ||||
| 		# ctx['poznamka'] = f.PoznamkaReseniForm(instance=self.reseni) | ||||
| 		ctx["hodnoceni"] = hodnoceni | ||||
| 		return ctx | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class PrehledOdevzdanychReseni(ListView): | ||||
|  | @ -413,6 +413,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): | |||
| 		with transaction.atomic(): | ||||
| 			self.object = form.save() | ||||
| 			self.object.resitele.add(m.Resitel.objects.get(osoba__user = self.request.user)) | ||||
| 			self.object.resitele.add(*form.cleaned_data["resitele"]) | ||||
| 			self.object.cas_doruceni = timezone.now() | ||||
| 			self.object.forma = m.Reseni.FORMA_UPLOAD | ||||
| 			self.object.save() | ||||
|  |  | |||
|  | @ -32,6 +32,7 @@ class PrihlaskaForm(PasswordResetForm): | |||
| 			help_text='Tímto jménem se následně budeš přihlašovat pro odevzdání řešení a další činnosti v semináři') | ||||
| 
 | ||||
| 	jmeno = forms.CharField(label='Jméno', max_length=256, required=True) | ||||
| 	prezdivka_resitele = forms.CharField(label='Přezdívka (veřejná)', max_length=256, required=False) | ||||
| 	prijmeni = forms.CharField(label='Příjmení', max_length=256, required=True) | ||||
| 	pohlavi_muz = forms.ChoiceField(label='Pohlaví', | ||||
| 			choices = ((True,'muž'),(False,'žena')), required=True) | ||||
|  | @ -105,6 +106,14 @@ class PrihlaskaForm(PasswordResetForm): | |||
| 			pass | ||||
| 		return email | ||||
| 
 | ||||
| 	def clean_prezdivka_resitele(self): | ||||
| 		prezdivka_resitele = self.cleaned_data.get('prezdivka_resitele') | ||||
| 		if prezdivka_resitele == '': | ||||
| 			return prezdivka_resitele | ||||
| 		if Resitel.objects.filter(prezdivka_resitele=prezdivka_resitele).count() > 0: | ||||
| 			raise forms.ValidationError('Přezdívka je již použita') | ||||
| 		return prezdivka_resitele | ||||
| 
 | ||||
| 	def clean_zasilat(self): | ||||
| 		zasilat = self.cleaned_data.get('zasilat') | ||||
| 		ulice = self.cleaned_data.get('ulice') | ||||
|  | @ -138,6 +147,7 @@ class ProfileEditForm(forms.Form): | |||
| 			disabled=True) | ||||
| 
 | ||||
| 	jmeno = forms.CharField(label='Jméno', max_length=256, required=True) | ||||
| 	prezdivka_resitele = forms.CharField(label='Přezdívka (veřejná)', max_length=256, required=False) | ||||
| 	prijmeni = forms.CharField(label='Příjmení', max_length=256, required=True) | ||||
| 	pohlavi_muz = forms.ChoiceField(label='Pohlaví', | ||||
| 			choices = ((True,'muž'),(False,'žena')), required=True) | ||||
|  | @ -190,6 +200,15 @@ class ProfileEditForm(forms.Form): | |||
| #			pass | ||||
| #		return username | ||||
| # | ||||
| 
 | ||||
| 	def clean_prezdivka_resitele(self): | ||||
| 		prezdivka_resitele = self.cleaned_data.get('prezdivka_resitele') | ||||
| 		if prezdivka_resitele == '': | ||||
| 			return prezdivka_resitele | ||||
| 		if Resitel.objects.filter(prezdivka_resitele=prezdivka_resitele).exclude(osoba__user__username=self.username).count() > 0: | ||||
| 			raise forms.ValidationError('Přezdívka je již použita') | ||||
| 		return prezdivka_resitele | ||||
| 
 | ||||
| 	def clean_email(self): | ||||
| 		err_logger = logging.getLogger('seminar.prihlaska.problem') | ||||
| 		email = self.cleaned_data.get('email') | ||||
|  |  | |||
|  | @ -44,6 +44,7 @@ | |||
|     </h4> | ||||
|       <table class="form"> | ||||
|        {% include "personalni/udaje/prihlaska_field.html" with field=form.jmeno %} | ||||
|        {% include "personalni/udaje/prihlaska_field.html" with field=form.prezdivka_resitele %} | ||||
|        {% include "personalni/udaje/prihlaska_field.html" with field=form.prijmeni %} | ||||
|        {% include "personalni/udaje/prihlaska_field.html" with field=form.pohlavi_muz%} | ||||
|        {% include "personalni/udaje/prihlaska_field.html" with field=form.email %} | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ Získáváme od Tebe údaje vyplněné v přihlášce do semináře (jméno, př | |||
| Slibujeme Ti, že Tvá osobní data nezneužijeme k ničemu, co by nesouviselo s M&M nebo s dalšími aktivitami Matfyzu, a nikdy je nepředáme nikomu cizímu. Údaje využíváme k zajištění chodu semináře a také je sdílíme s ostatními propagačními akcemi Matfyzu, abychom mohli vyhodnocovat úspěšnost akcí. Pokud budeš mít zájem, budeme Ti také posílat zajímavé zprávy a novinky týkajíci se Matfyzu. | ||||
| </p> | ||||
| <p class="gdpr"> | ||||
| Veřejně vystavujeme pouze výsledkové listiny, které také uchováváme pro archivní účely. Pokud ale z nějakého důvodu nebudeš chtít mít své jméno či školu uvedené ve výsledkové listině, není problém to zařídit, napiš nám. Z tištěných materiálů samozřejmě údaje už odstranit nemůžeme. | ||||
| Veřejně vystavujeme pouze seznam přezdívek (pro výběr spoluřešitelů k řešení) a výsledkové listiny, které také uchováváme pro archivní účely. Pokud ale z nějakého důvodu nebudeš chtít mít své jméno či školu uvedené ve výsledkové listině, není problém to zařídit, napiš nám. Z tištěných materiálů samozřejmě údaje už odstranit nemůžeme. | ||||
| </p> | ||||
| <p class="gdpr"> | ||||
| Na soustředěních a dalších akcích semináře navíc pořizujeme fotografie a videozáznamy a používáme je ke zpravodajským a propagačním účelům. Pro propagační účely si od Tebe vyžádáme samostatný souhlas na začátku akce. | ||||
|  |  | |||
|  | @ -46,6 +46,7 @@ | |||
|         </h4> | ||||
|           <table class="form"> | ||||
|             {% include "personalni/udaje/prihlaska_field.html" with field=form.jmeno %} | ||||
|             {% include "personalni/udaje/prihlaska_field.html" with field=form.prezdivka_resitele %} | ||||
|             {% include "personalni/udaje/prihlaska_field.html" with field=form.prijmeni %} | ||||
|             {% include "personalni/udaje/prihlaska_field.html" with field=form.pohlavi_muz%} | ||||
|             {% include "personalni/udaje/prihlaska_field.html" with field=form.email %} | ||||
|  |  | |||
|  | @ -160,6 +160,7 @@ def resitelEditView(request): | |||
| 
 | ||||
| 			if resitel_edit: | ||||
| 				## Změny v řešiteli | ||||
| 				resitel_edit.prezdivka_resitele = fcd['prezdivka_resitele'] | ||||
| 				resitel_edit.skola = fcd['skola'] | ||||
| 				resitel_edit.rok_maturity = fcd['rok_maturity'] | ||||
| 				resitel_edit.zasilat = fcd['zasilat'] | ||||
|  | @ -263,6 +264,7 @@ def prihlaskaView(request): | |||
| 					err_logger.warning(f'Zaregistrovala se osoba s kolizním jménem. ID osob: {[o.id for o in kolize]}') | ||||
| 
 | ||||
| 				r = s.Resitel( | ||||
| 					prezdivka_resitele=fcd['prezdivka_resitele'], | ||||
| 					rok_maturity = fcd['rok_maturity'], | ||||
| 					zasilat = fcd['zasilat'], | ||||
| 					zasilat_cislo_emailem = fcd['zasilat_cislo_emailem'] | ||||
|  |  | |||
							
								
								
									
										18
									
								
								seminar/migrations/0110_resitel_prezdivka.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								seminar/migrations/0110_resitel_prezdivka.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| # Generated by Django 2.2.28 on 2022-11-21 22:07 | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('seminar', '0109_hodnoceni_feedback'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.AddField( | ||||
|             model_name='resitel', | ||||
|             name='prezdivka_resitele', | ||||
|             field=models.CharField(blank=True, max_length=256, null=True, unique=True, verbose_name='přezdívka řešitele'), | ||||
|         ), | ||||
|     ] | ||||
|  | @ -211,6 +211,8 @@ class Resitel(SeminarModelBase): | |||
| 	# Interní ID | ||||
| 	id = models.AutoField(primary_key = True) | ||||
| 
 | ||||
| 	prezdivka_resitele = models.CharField('přezdívka řešitele', blank=True, null=True, max_length=256, unique=True) | ||||
| 
 | ||||
| 	osoba = models.OneToOneField(Osoba, blank=False, null=False, verbose_name='osoba', | ||||
| 		on_delete=models.PROTECT) | ||||
| 	 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue