Fix: #1427
This commit is contained in:
		
							parent
							
								
									6ee9c4e380
								
							
						
					
					
						commit
						c17afece9d
					
				
					 5 changed files with 132 additions and 116 deletions
				
			
		
							
								
								
									
										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) { // 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; | ||||
| } | ||||
|  | @ -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,67 +4,15 @@ | |||
| 
 | ||||
| {% 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>) | ||||
| <p>Řešitelé: {% for r in object.resitele.all %} {{ r }} {% if edit %}(<a href="mailto:{{ r.osoba.email }}?subject={{ "Oprava řešení M&M " | urlencode }}{{ object.problem.all.0.hlavni_problem | urlencode }}">{{ r.osoba.email }}</a>){% endif %} | ||||
| {% if forloop.revcounter0 != 0 %}, {% endif %} {% endfor %}</p> | ||||
| 
 | ||||
| {# https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.get_FOO_display #} | ||||
|  | @ -82,13 +30,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 +64,7 @@ $(document).ready(function(){ | |||
| </table> | ||||
| 
 | ||||
| 
 | ||||
| <a href="#"> <img src="{% static "odevzdavatko/plus.png" %}" id="pridat_hodnoceni" alt="Přidat hodnocení"></a> </br> | ||||
| <a href="#"> <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 +77,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 %} | ||||
|  |  | |||
|  | @ -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'), | ||||
| ] | ||||
|  |  | |||
|  | @ -211,6 +211,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' | ||||
| 	 | ||||
|  | @ -227,18 +228,43 @@ 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 """ | ||||
| 		pass | ||||
| 
 | ||||
| 
 | ||||
| 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ě | ||||
|  | @ -270,33 +296,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): | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue