Merge branch 'odevzdavatko' into data_migrations
Je to WIP, ale jako sneak-peek dobrý :-)
This commit is contained in:
		
						commit
						0b32d312da
					
				
					 6 changed files with 209 additions and 43 deletions
				
			
		|  | @ -2,6 +2,7 @@ from django import forms | ||||||
| from dal import autocomplete | from dal import autocomplete | ||||||
| from django.core.exceptions import ObjectDoesNotExist | from django.core.exceptions import ObjectDoesNotExist | ||||||
| from django.contrib.auth.models import User | from django.contrib.auth.models import User | ||||||
|  | from django.forms import formset_factory | ||||||
| from django.forms.models import inlineformset_factory | from django.forms.models import inlineformset_factory | ||||||
| 
 | 
 | ||||||
| from .models import Skola, Resitel, Osoba, Problem | from .models import Skola, Resitel, Osoba, Problem | ||||||
|  | @ -301,3 +302,17 @@ class NahrajObrazekKTreeNoduForm(forms.ModelForm): | ||||||
| 		model = m.Obrazek | 		model = m.Obrazek | ||||||
| 		fields = ('na_web',) | 		fields = ('na_web',) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | class JednoHodnoceniForm(forms.ModelForm): | ||||||
|  | 	class Meta: | ||||||
|  | 		model = m.Hodnoceni | ||||||
|  | 		fields = ('problem', 'body', 'cislo_body') | ||||||
|  | 		widgets = { | ||||||
|  | 			'problem': autocomplete.ModelSelect2( | ||||||
|  | 				url='autocomplete_problem_odevzdatelny',   # FIXME: Dovolit i starší? | ||||||
|  | 				) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | OhodnoceniReseniFormSet = formset_factory(JednoHodnoceniForm, | ||||||
|  | 		extra = 0, | ||||||
|  | 		) | ||||||
|  |  | ||||||
|  | @ -2,6 +2,59 @@ | ||||||
| 
 | 
 | ||||||
| {% block content %} | {% block content %} | ||||||
| 
 | 
 | ||||||
|  | {# FIXME: Necopypastovat! Tohle je zkopírované ze static/seminar/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'); | ||||||
|  |         $('#id_' + prefix + '-TOTAL_FORMS').val(forms.length); | ||||||
|  |         for (var i=0, formCount=forms.length; 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); | ||||||
|  | 		}); | ||||||
|  | 		$('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1); | ||||||
|  | 	}); | ||||||
|  | 	$('.smazat_hodnoceni').click(function(){ | ||||||
|  | 		deleteForm("form",this); | ||||||
|  | 	}); | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| <p>Řešené problémy: {{ object.problem.all | join:", " }}</p> | <p>Řešené problémy: {{ object.problem.all | join:", " }}</p> | ||||||
| 
 | 
 | ||||||
| <p>Řešitelé: {{ object.resitele.all | join:", " }}</p> | <p>Řešitelé: {{ object.resitele.all | join:", " }}</p> | ||||||
|  | @ -27,21 +80,33 @@ | ||||||
| {% endif %} | {% endif %} | ||||||
| 
 | 
 | ||||||
| {# Hodnocení: #} | {# Hodnocení: #} | ||||||
| {# FIXME: Udělat jako formulář #} |  | ||||||
| <h3>Hodnocení:</h3> | <h3>Hodnocení:</h3> | ||||||
| {% if object.hodnoceni_set.all %} | <form method=post><table> | ||||||
| <table> | {% csrf_token %} | ||||||
|  | {{ form.management_form }} | ||||||
|  | <table id="form_set"> | ||||||
| <tr><th>Problém</th><th>Body</th><th>Číslo pro body</th></tr> | <tr><th>Problém</th><th>Body</th><th>Číslo pro body</th></tr> | ||||||
| {% for h in object.hodnoceni_set.all %} | {% for subform in form %} | ||||||
| <tr> | 	<tr class="hodnoceni"> | ||||||
| 	<td>{{ h.problem }}</a></td> | 		<td>{{ subform.problem }}</td> | ||||||
| 	<td>{{ h.body }}</td> | 		<td>{{ subform.body }}</td> | ||||||
| 	<td>{{ h.cislo_body }}</td></tr> | 		<td>{{ subform.cislo_body }}</td> | ||||||
|  | 		<td><input type=button class="smazat_hodnoceni" value="Smazat" id="id_{{subform.prefix}}-jsremove"></td> | ||||||
|  | 	</tr> | ||||||
| {% endfor %} | {% endfor %} | ||||||
| </table> | </table> | ||||||
| {% else %} |  | ||||||
| <p>Ještě nebylo hodnoceno</p> |  | ||||||
| {% endif %} |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | <input type=button id="pridat_hodnoceni" value="Přidat hodnocení"> | ||||||
|  | <input type=submit></form> | ||||||
|  | 
 | ||||||
|  | <table id="empty_form" style="display: none;"> | ||||||
|  | 	<tr class="hodnoceni"> | ||||||
|  | 		<td>{{ form.empty_form.problem }}</td> | ||||||
|  | 		<td>{{ form.empty_form.body }}</td> | ||||||
|  | 		<td>{{ form.empty_form.cislo_body }}</td> | ||||||
|  | 		<td><input type=button class="smazat_hodnoceni" value="Smazat" id="id_{{form.empty_form.prefix}}-jsremove"></td> | ||||||
|  | 	</tr> | ||||||
|  | </table> | ||||||
|  | 
 | ||||||
| {% endblock %} | {% endblock %} | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| from django.urls import path, include, re_path | from django.urls import path, include, re_path | ||||||
| from django.contrib.auth.decorators import login_required | from django.contrib.auth.decorators import login_required | ||||||
| from . import views, export | from . import views, export | ||||||
| from .utils import org_required, resitel_required | from .utils import org_required, resitel_required, viewMethodSwitch | ||||||
| from django.views.generic.base import RedirectView | from django.views.generic.base import RedirectView | ||||||
| 
 | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
|  | @ -124,11 +124,6 @@ urlpatterns = [ | ||||||
| 		org_required(views.soustredeniObalkyView), | 		org_required(views.soustredeniObalkyView), | ||||||
| 		name='seminar_soustredeni_obalky' | 		name='seminar_soustredeni_obalky' | ||||||
| 	), | 	), | ||||||
| 	path( |  | ||||||
| 		'org/vloz_body/<int:tema>/', |  | ||||||
| 		org_required(views.VlozBodyView.as_view()), |  | ||||||
| 		name='seminar_org_vlozbody' |  | ||||||
| 	), |  | ||||||
| 	# příprava na nestatický orgorozcestník | 	# příprava na nestatický orgorozcestník | ||||||
| 	path( | 	path( | ||||||
| 		'org/rozcestnik/', | 		'org/rozcestnik/', | ||||||
|  | @ -175,8 +170,7 @@ urlpatterns = [ | ||||||
| 
 | 
 | ||||||
| 	path('temp/reseni/', org_required(views.TabulkaOdevzdanychReseniView.as_view()), name='odevzdavatko_tabulka'), | 	path('temp/reseni/', org_required(views.TabulkaOdevzdanychReseniView.as_view()), name='odevzdavatko_tabulka'), | ||||||
| 	path('temp/reseni/<int:problem>/<int:resitel>/', org_required(views.ReseniProblemuView.as_view()), name='odevzdavatko_reseni_resitele_k_problemu'), | 	path('temp/reseni/<int:problem>/<int:resitel>/', org_required(views.ReseniProblemuView.as_view()), name='odevzdavatko_reseni_resitele_k_problemu'), | ||||||
| 	path('temp/reseni/<int:pk>', org_required(views.DetailReseniView.as_view()), name='odevzdavatko_detail_reseni'), | 	path('temp/reseni/<int:pk>', org_required(viewMethodSwitch(get=views.DetailReseniView.as_view(), post=views.hodnoceniReseniView)), name='odevzdavatko_detail_reseni'), | ||||||
| 	path('temp/reseni/all', org_required(views.SeznamReseniView.as_view())), | 	path('temp/reseni/all', org_required(views.SeznamReseniView.as_view())), | ||||||
| 	path('temp/reseni/akt', org_required(views.SeznamAktualnichReseniView.as_view())), | 	path('temp/reseni/akt', org_required(views.SeznamAktualnichReseniView.as_view())), | ||||||
| 
 |  | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import datetime | ||||||
| from django.contrib.auth import get_user_model | from django.contrib.auth import get_user_model | ||||||
| from django.contrib.auth.decorators import permission_required | from django.contrib.auth.decorators import permission_required | ||||||
| from html.parser import HTMLParser | from html.parser import HTMLParser | ||||||
|  | from django import views as DjangoViews | ||||||
| 
 | 
 | ||||||
| from django.contrib.auth.models import AnonymousUser | from django.contrib.auth.models import AnonymousUser | ||||||
| from django.contrib.contenttypes.models import ContentType | from django.contrib.contenttypes.models import ContentType | ||||||
|  | @ -191,3 +192,30 @@ def aktivniResitele(cislo, pouze_letosni=False): | ||||||
| 	else: | 	else: | ||||||
| 		# spojíme querysety s řešiteli loni a letos do daného čísla | 		# spojíme querysety s řešiteli loni a letos do daného čísla | ||||||
| 		return (resi_v_rocniku(loni) | resi_v_rocniku(letos, cislo)).distinct() | 		return (resi_v_rocniku(loni) | resi_v_rocniku(letos, cislo)).distinct() | ||||||
|  | 
 | ||||||
|  | def viewMethodSwitch(get, post): | ||||||
|  | 	""" | ||||||
|  | 	Vrátí view, který zavolá různé jiné views podle toho, kterou metodou je zavolán. | ||||||
|  | 
 | ||||||
|  | 	Inspirováno https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#an-alternative-better-solution, jen jsem to udělal genericky. | ||||||
|  | 
 | ||||||
|  | 	Parametry: | ||||||
|  | 		post	view pro metodu POST | ||||||
|  | 		get	view pro metodu GET | ||||||
|  | 	 | ||||||
|  | 	V obou případech se míní už view jakožto funkce, takže u class-based views se už má použít .as_view() | ||||||
|  | 
 | ||||||
|  | 	TODO: Podpora i pro metodu HEAD? A možná i pro FILES? | ||||||
|  | 	""" | ||||||
|  | 
 | ||||||
|  | 	theGetView = get | ||||||
|  | 	thePostView = post | ||||||
|  | 
 | ||||||
|  | 	class NewView(DjangoViews.View): | ||||||
|  | 		def get(self, request, *args, **kwargs): | ||||||
|  | 			return theGetView(request, *args, **kwargs) | ||||||
|  | 		def post(self, request, *args, **kwargs): | ||||||
|  | 			return thePostView(request, *args, **kwargs) | ||||||
|  | 	 | ||||||
|  | 	return NewView.as_view() | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -1,12 +1,21 @@ | ||||||
| from django.views.generic import ListView, DetailView | from django.views.generic import ListView, DetailView, FormView | ||||||
| from django.views.generic.base import TemplateView | from django.views.generic.list import MultipleObjectTemplateResponseMixin,MultipleObjectMixin | ||||||
|  | from django.views.generic.base import View | ||||||
|  | from django.views.generic.detail import SingleObjectMixin | ||||||
|  | from django.shortcuts import redirect, get_object_or_404 | ||||||
|  | from django.urls import reverse | ||||||
|  | from django.db import transaction | ||||||
| 
 | 
 | ||||||
| from dataclasses import dataclass | from dataclasses import dataclass | ||||||
| import datetime | import datetime | ||||||
|  | import logging | ||||||
| 
 | 
 | ||||||
| import seminar.models as m | import seminar.models as m | ||||||
|  | import seminar.forms as f | ||||||
| from seminar.utils import aktivniResitele, resi_v_rocniku | from seminar.utils import aktivniResitele, resi_v_rocniku | ||||||
| 
 | 
 | ||||||
|  | logger = logging.getLogger(__name__) | ||||||
|  | 
 | ||||||
| # Co chceme? | # Co chceme? | ||||||
| # - "Tabulku" aktuální řešitelé x zveřejněné problémy, v buňkách počet řešení | # - "Tabulku" aktuální řešitelé x zveřejněné problémy, v buňkách počet řešení | ||||||
| # 	- TabulkaOdevzdanychReseniView | # 	- TabulkaOdevzdanychReseniView | ||||||
|  | @ -30,15 +39,22 @@ class TabulkaOdevzdanychReseniView(ListView): | ||||||
| 	template_name = 'seminar/odevzdavatko/tabulka.html' | 	template_name = 'seminar/odevzdavatko/tabulka.html' | ||||||
| 	model = m.Hodnoceni | 	model = m.Hodnoceni | ||||||
| 
 | 
 | ||||||
|  | 	def inicializuj_osy_tabulky(self): | ||||||
|  | 		"""Vyrobí prvotní querysety pro sloupce a řádky, tj. seznam všech řešitelů a problémů""" | ||||||
|  | 		# NOTE: Tenhle blok nemůže být přímo ve třídě, protože před vyrobením databáze neexistují ty objekty (?). TODO: Otestovat | ||||||
|  | 		# TODO: Prefetches, Select related, ... | ||||||
|  | 		self.resitele = m.Resitel.objects.all() | ||||||
|  | 		self.problemy = m.Problem.objects.all() | ||||||
|  | 
 | ||||||
| 	def get_queryset(self): | 	def get_queryset(self): | ||||||
| 		# FIXME: Tenhle blok nemůže být přímo ve třídě, protože před vyrobením databáze neexistuje Nastavení. | 		self.inicializuj_osy_tabulky() | ||||||
| 		self.akt_rocnik = m.Nastaveni.get_solo().aktualni_rocnik	# .get_solo() vrátí tu jedinou instanci, asi... | 		self.akt_rocnik = m.Nastaveni.get_solo().aktualni_rocnik	# .get_solo() vrátí tu jedinou instanci, asi... | ||||||
| 		self.resitele = resi_v_rocniku(self.akt_rocnik) | 		self.resitele = resi_v_rocniku(self.akt_rocnik) | ||||||
| 		# NOTE: Protože řešení odkazuje přímo na Problém a QuerySet na Hodnocení je nepolymorfní, musíme porovnávat taky s nepolymorfními Problémy. | 		# NOTE: Protože řešení odkazuje přímo na Problém a QuerySet na Hodnocení je nepolymorfní, musíme porovnávat taky s nepolymorfními Problémy. | ||||||
| 		self.zadane_problemy = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY).non_polymorphic() | 		self.problemy = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY).non_polymorphic() | ||||||
| 
 | 
 | ||||||
| 		qs = super().get_queryset() | 		qs = super().get_queryset() | ||||||
| 		qs = qs.filter(problem__in=self.zadane_problemy).select_related('reseni', 'problem').prefetch_related('reseni__resitele__osoba') | 		qs = qs.filter(problem__in=self.problemy).select_related('reseni', 'problem').prefetch_related('reseni__resitele__osoba') | ||||||
| 		return qs | 		return qs | ||||||
| 
 | 
 | ||||||
| 	def get_context_data(self, *args, **kwargs): | 	def get_context_data(self, *args, **kwargs): | ||||||
|  | @ -46,10 +62,10 @@ class TabulkaOdevzdanychReseniView(ListView): | ||||||
| 		self.akt_rocnik = m.Nastaveni.get_solo().aktualni_rocnik	# .get_solo() vrátí tu jedinou instanci, asi... | 		self.akt_rocnik = m.Nastaveni.get_solo().aktualni_rocnik	# .get_solo() vrátí tu jedinou instanci, asi... | ||||||
| 		self.resitele = resi_v_rocniku(self.akt_rocnik) | 		self.resitele = resi_v_rocniku(self.akt_rocnik) | ||||||
| 		# NOTE: Protože řešení odkazuje přímo na Problém a QuerySet na Hodnocení je nepolymorfní, musíme porovnávat taky s nepolymorfními Problémy. | 		# NOTE: Protože řešení odkazuje přímo na Problém a QuerySet na Hodnocení je nepolymorfní, musíme porovnávat taky s nepolymorfními Problémy. | ||||||
| 		self.zadane_problemy = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY).non_polymorphic() | 		self.problemy = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY).non_polymorphic() | ||||||
| 
 | 
 | ||||||
| 		ctx = super().get_context_data(*args, **kwargs) | 		ctx = super().get_context_data(*args, **kwargs) | ||||||
| 		ctx['problemy'] = self.zadane_problemy | 		ctx['problemy'] = self.problemy | ||||||
| 		ctx['resitele'] = self.resitele | 		ctx['resitele'] = self.resitele | ||||||
| 		tabulka = dict() | 		tabulka = dict() | ||||||
| 
 | 
 | ||||||
|  | @ -76,7 +92,7 @@ class TabulkaOdevzdanychReseniView(ListView): | ||||||
| 		hodnoty = [] | 		hodnoty = [] | ||||||
| 		for resitel in self.resitele: | 		for resitel in self.resitele: | ||||||
| 			resiteluv_radek = [] | 			resiteluv_radek = [] | ||||||
| 			for problem in self.zadane_problemy: | 			for problem in self.problemy: | ||||||
| 				if problem in tabulka and resitel in tabulka[problem]: | 				if problem in tabulka and resitel in tabulka[problem]: | ||||||
| 					resiteluv_radek.append(tabulka[problem][resitel]) | 					resiteluv_radek.append(tabulka[problem][resitel]) | ||||||
| 				else: | 				else: | ||||||
|  | @ -86,7 +102,8 @@ class TabulkaOdevzdanychReseniView(ListView): | ||||||
| 
 | 
 | ||||||
| 		return ctx | 		return ctx | ||||||
| 
 | 
 | ||||||
| class ReseniProblemuView(ListView): | # Velmi silně inspirováno zdrojáky, FIXME: Nedá se to udělat smysluplněji? | ||||||
|  | class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixin, View): | ||||||
| 	model = m.Reseni | 	model = m.Reseni | ||||||
| 	template_name = 'seminar/odevzdavatko/seznam.html' | 	template_name = 'seminar/odevzdavatko/seznam.html' | ||||||
| 	 | 	 | ||||||
|  | @ -107,12 +124,73 @@ class ReseniProblemuView(ListView): | ||||||
| 			) | 			) | ||||||
| 		return qs | 		return qs | ||||||
| 	 | 	 | ||||||
|  | 	def get(self, request, *args, **kwargs): | ||||||
|  | 		self.object_list = self.get_queryset() | ||||||
|  | 		if self.object_list.count() == 1: | ||||||
|  | 			jedine_reseni = self.object_list.first() | ||||||
|  | 			return redirect(reverse("odevzdavatko_detail_reseni", kwargs={"pk": jedine_reseni.id})) | ||||||
|  | 		context = self.get_context_data() | ||||||
|  | 		return self.render_to_response(context) | ||||||
| 	# Kontext automaticky? | 	# Kontext automaticky? | ||||||
| 
 | 
 | ||||||
|  | ## XXX: https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#avoid-anything-more-complex | ||||||
| class DetailReseniView(DetailView): | class DetailReseniView(DetailView): | ||||||
| 	model = m.Reseni | 	model = m.Reseni | ||||||
| 	template_name = 'seminar/odevzdavatko/detail.html' | 	template_name = 'seminar/odevzdavatko/detail.html' | ||||||
| 	# To je všechno? Najde se to podle pk... | 	 | ||||||
|  | 	def aktualni_hodnoceni(self): | ||||||
|  | 		reseni = get_object_or_404(m.Reseni, id=self.kwargs['pk']) | ||||||
|  | 		result = [] # Slovníky s klíči problem, body, cislo_body -- initial data pro f.OhodnoceniReseniFormSet | ||||||
|  | 		for hodn in m.Hodnoceni.objects.filter(reseni=reseni): | ||||||
|  | 			result.append( | ||||||
|  | 				{"problem": hodn.problem,  | ||||||
|  | 				"body": hodn.body, | ||||||
|  | 				"cislo_body": hodn.cislo_body, | ||||||
|  | 				}) | ||||||
|  | 		return result | ||||||
|  | 
 | ||||||
|  | 	def get_context_data(self, **kw): | ||||||
|  | 		ctx = super().get_context_data(**kw) | ||||||
|  | 		ctx['form'] = f.OhodnoceniReseniFormSet( | ||||||
|  | 				initial = self.aktualni_hodnoceni() | ||||||
|  | 				) | ||||||
|  | 		return ctx | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def hodnoceniReseniView(request, pk, *args, **kwargs): | ||||||
|  | 	reseni = get_object_or_404(m.Reseni, pk=pk) | ||||||
|  | 	template_name = 'seminar/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ě | ||||||
|  | 	# Also: https://docs.djangoproject.com/en/2.2/topics/forms/modelforms/#django.forms.ModelForm | ||||||
|  | 	formset = f.OhodnoceniReseniFormSet(request.POST) | ||||||
|  | 	# TODO: Napsat validaci formuláře a formsetu | ||||||
|  | 	# TODO: Implementovat větev, kdy formulář validní není. | ||||||
|  | 	if formset.is_valid(): | ||||||
|  | 		with transaction.atomic(): | ||||||
|  | 			# Smažeme všechna dosavadní hodnocení tohoto řešení | ||||||
|  | 			qs = m.Hodnoceni.objects.filter(reseni=reseni) | ||||||
|  | 			logger.info(f"Will delete {qs.count()} objects: {qs}") | ||||||
|  | 			qs.delete() | ||||||
|  | 			 | ||||||
|  | 			# Vyrobíme nová podle formsetu | ||||||
|  | 			for form in formset: | ||||||
|  | 				problem = form.cleaned_data['problem'] | ||||||
|  | 				body = form.cleaned_data['body'] | ||||||
|  | 				cislo_body = form.cleaned_data['cislo_body'] | ||||||
|  | 				hodnoceni = m.Hodnoceni( | ||||||
|  | 					problem=problem, | ||||||
|  | 					body=body, | ||||||
|  | 					cislo_body=cislo_body, | ||||||
|  | 					reseni=reseni, | ||||||
|  | 					) | ||||||
|  | 				logger.info(f"Creating Hodnoceni: {hodnoceni}") | ||||||
|  | 				hodnoceni.save() | ||||||
|  | 
 | ||||||
|  | 	return redirect(success_url) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| # Přehled všech řešení kvůli debugování | # Přehled všech řešení kvůli debugování | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -63,20 +63,6 @@ from seminar.utils import aktivniResitele, resi_v_rocniku | ||||||
| def get_problemy_k_tematu(tema): | def get_problemy_k_tematu(tema): | ||||||
| 	return Problem.objects.filter(nadproblem = tema) | 	return Problem.objects.filter(nadproblem = tema) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| class VlozBodyView(generic.ListView): |  | ||||||
| 	template_name = 'seminar/org/vloz_body.html' |  | ||||||
| 
 |  | ||||||
| 	def get_queryset(self): |  | ||||||
| 		self.tema = get_object_or_404(Problem,id=self.kwargs['tema']) |  | ||||||
| 		print(self.tema) |  | ||||||
| 		self.problemy = Problem.objects.filter(nadproblem = self.tema) |  | ||||||
| 		print(self.problemy) |  | ||||||
| 		self.reseni = Reseni.objects.filter(problem__in=self.problemy)	 |  | ||||||
| 		print(self.reseni) |  | ||||||
| 		return self.reseni |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class ObalkovaniView(generic.ListView): | class ObalkovaniView(generic.ListView): | ||||||
| 	template_name = 'seminar/org/obalkovani.html' | 	template_name = 'seminar/org/obalkovani.html' | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Pavel "LEdoian" Turinsky
						Pavel "LEdoian" Turinsky