Merge pull request 'Upgrade odevzdavatka' (!30) from upgrade_odevzdavatka into master
Reviewed-on: #30
This commit is contained in:
		
						commit
						0528dbbb9c
					
				
					 15 changed files with 205 additions and 49 deletions
				
			
		|  | @ -70,23 +70,17 @@ class PublicResitelAutocomplete(LoginRequiredAjaxMixin, autocomplete.Select2Quer | |||
| 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): | ||||
| 		nastaveni = get_object_or_404(m.Nastaveni) | ||||
| 		rocnik = nastaveni.aktualni_rocnik | ||||
| 		# Od tohoto místa dál jsem zkoušel spoustu variací podle https://django-polymorphic.readthedocs.io/en/stable/advanced.html | ||||
| 		temaQ = Q(Tema___rocnik = rocnik, stav=m.Problem.STAV_ZADANY) | ||||
| 		ulohaQ = Q(Uloha___cislo_zadani__rocnik = rocnik, stav=m.Problem.STAV_ZADANY) | ||||
| 		clanekQ = Q(Clanek___cislo__rocnik = rocnik, stav=m.Problem.STAV_ZADANY) | ||||
| 		qs = m.Problem.objects.filter(temaQ | ulohaQ | clanekQ) | ||||
| 		#print(temata, ulohy, clanky) | ||||
| 		#ulohy.union(temata, all=True) | ||||
| 		#print(ulohy) | ||||
| 		#ulohy.union(clanky, all=True) | ||||
| 		#print(ulohy) | ||||
| 		#qs = ulohy | ||||
| 		print(qs) | ||||
| 		qs = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY) | ||||
| 		if self.q: | ||||
| 			qs = qs.filter( | ||||
| 					Q(nazev__icontains=self.q)) | ||||
| 
 | ||||
| 		nadproblem_id = int(self.forwarded.get("nadproblem_id", -1)) | ||||
| 		if nadproblem_id != -1: | ||||
| 			# Seřadíme tak, aby ty s nadproblem==None byly dole (větší motivace tam naklikat konkrétní úlohy) a pak nějak rozumně. | ||||
| 			# Tohle je řazení pro odevzdávátko, kde je definován nadproblém, proto je to v tomto ifu. (Jinde si to netroufám řadit) | ||||
| 			qs = qs.order_by("nadproblem", "kod", "nazev") | ||||
| 			qs = list(filter(lambda problem: problem.hlavni_problem.id == nadproblem_id, qs)) | ||||
| 		return qs | ||||
| 
 | ||||
| class ProblemAutocomplete(autocomplete.Select2QuerySetView): | ||||
|  |  | |||
|  | @ -437,7 +437,7 @@ | |||
| 			"insitetree": true, | ||||
| 			"parent": 21, | ||||
| 			"sort_order": 36, | ||||
| 			"title": "Poslat řešení", | ||||
| 			"title": "Nahrát řešení", | ||||
| 			"tree": 1, | ||||
| 			"url": "seminar_nahraj_reseni", | ||||
| 			"urlaspattern": true | ||||
|  | @ -719,7 +719,7 @@ | |||
| 			"insitetree": true, | ||||
| 			"parent": 21, | ||||
| 			"sort_order": 36, | ||||
| 			"title": "Nahrát řešení", | ||||
| 			"title": "Vložit řešení", | ||||
| 			"tree": 1, | ||||
| 			"url": "seminar_vloz_reseni", | ||||
| 			"urlaspattern": true | ||||
|  | @ -1026,6 +1026,36 @@ | |||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 51 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [ | ||||
| 				[ | ||||
| 					"resitel", | ||||
| 					"auth", | ||||
| 					"user" | ||||
| 				] | ||||
| 			], | ||||
| 			"access_restricted": true, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 23, | ||||
| 			"sort_order": 52, | ||||
| 			"title": "Nahrát řešení k nadproblému {{nadproblem_id}}", | ||||
| 			"tree": 1, | ||||
| 			"url": "seminar_nahraj_reseni nadproblem_id", | ||||
| 			"urlaspattern": true | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 52 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
|  | @ -1041,13 +1071,13 @@ | |||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 28, | ||||
| 			"sort_order": 52, | ||||
| 			"sort_order": 53, | ||||
| 			"title": "Přidat PDF", | ||||
| 			"tree": 1, | ||||
| 			"url": "/admin/korektury/korekturovanepdf/add/", | ||||
| 			"urlaspattern": false | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 52 | ||||
| 		"pk": 53 | ||||
| 	} | ||||
| ] | ||||
| ] | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ Generuje se za pomocí:: | |||
| 
 | ||||
| nebo (v případě meníčka):: | ||||
| 
 | ||||
|     ./manage.py dumpdata sitetree --natrual-foreign > data/sitetree_new.json | ||||
|     ./manage.py dumpdata sitetree --natural-foreign > data/sitetree_new.json | ||||
|     ./fix_json.py data/sitetree_new.json data/sitetree.json | ||||
| 
 | ||||
| deploy_v2 | ||||
|  |  | |||
|  | @ -1260,3 +1260,22 @@ label[for=id_skola] { | |||
| .bodovani>input { | ||||
| 	width: 4em; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Select2 používaný hlavně multiple selectem. Přidání checkboxů a změna barvy. */ | ||||
| /* Podle https://stackoverflow.com/a/48290544 */ | ||||
| /* U autocomplete.ModelSelect2Multiple vyžaduje 'data-dropdown-css-class': 's2m-se-zaskrtavatky' */ | ||||
| .s2m-se-zaskrtavatky .select2-results__option[aria-selected=true]:before { | ||||
| 	content: '☑ '; | ||||
| 	padding: 0 0 0 8px; | ||||
| } | ||||
| 
 | ||||
| .s2m-se-zaskrtavatky .select2-results__option[aria-selected=false]:before { | ||||
| 	content: '◻ '; | ||||
| 	padding: 0 0 0 8px; | ||||
| } | ||||
| 
 | ||||
| /* Oranžové zvýraznění v Select2 */ | ||||
| .select2-results__option--highlighted { | ||||
| 	background-color: #e84e10 !important; | ||||
| } | ||||
|  |  | |||
|  | @ -4,8 +4,8 @@ Obsahuje vše, co se týká odevzdávání (+ nahrávání) a opravování řeš | |||
| Slovníček: | ||||
|     Moje řešení = Přehled řešení = Řešení, která odevzdal aktuálního uživatel sám. | ||||
|     Došlá řešení = Tabulka + seznam + detail + ... = Řešení, která poslal někdo jiný. | ||||
|     Poslat řešení = Odevdat mé řešení. (Tj. řešení se vztahem k aktuálnímu uživateli.) | ||||
|     Nahrát řešení = Nahrání řešení bez vztahu k aktuálnímu uživateli. | ||||
|     Nahrát řešení = Odevdat mé řešení. (Tj. řešení se vztahem k aktuálnímu uživateli.) | ||||
|     Vlož řešení = Vložit řešení bez vztahu k aktuálnímu uživateli. | ||||
| 
 | ||||
| TODO: Místo vložit řešení v nahrávání a posílání řešení dát něco jiného? | ||||
| """ | ||||
| """ | ||||
|  |  | |||
|  | @ -29,6 +29,8 @@ class PosliReseniForm(forms.Form): | |||
| 			attrs={ | ||||
| 				'data-placeholder--id': '-1', | ||||
| 				'data-placeholder--text': '---', | ||||
| 				'data-close-on-select': 'false', | ||||
| 				'data-dropdown-css-class': 's2m-se-zaskrtavatky', | ||||
| 				'data-allow-clear': 'true' | ||||
| 				}, | ||||
| 			), | ||||
|  | @ -43,6 +45,8 @@ class PosliReseniForm(forms.Form): | |||
| 			url='autocomplete_resitel', | ||||
| 			attrs = {'data-placeholder--id': '-1', | ||||
| 				'data-placeholder--text' : '---', | ||||
| 				'data-close-on-select': 'false', | ||||
| 				'data-dropdown-css-class': 's2m-se-zaskrtavatky', | ||||
| 				'data-allow-clear': 'true'}) | ||||
|     		) | ||||
| 
 | ||||
|  | @ -62,12 +66,6 @@ class PosliReseniForm(forms.Form): | |||
| 	#poznamka = models.TextField('neveřejná poznámka', blank=True, | ||||
| 	#	help_text='Neveřejná poznámka k řešení (plain text)') | ||||
| 
 | ||||
| 	#TODO body do cisla | ||||
| 	#TODO prilohy | ||||
| 
 | ||||
| 	##def __init__(self, *args, **kwargs): | ||||
| 	##	super().__init__(*args, **kwargs) | ||||
| 	##	#self.fields['favorite_color'] = forms.ChoiceField(choices=[(color.id, color.name) for color in Resitel.objects.all()]) | ||||
| 
 | ||||
| class NahrajReseniForm(forms.ModelForm): | ||||
| 	class Meta: | ||||
|  | @ -80,23 +78,40 @@ class NahrajReseniForm(forms.ModelForm): | |||
| 					url='autocomplete_problem_odevzdatelny', | ||||
| 					attrs = {'data-placeholder--id': '-1', | ||||
| 						'data-placeholder--text' : '---', | ||||
| 						'data-close-on-select': 'false', | ||||
| 						'data-dropdown-css-class': 's2m-se-zaskrtavatky', | ||||
| 						'data-allow-clear': 'true'}, | ||||
| 					forward=["nadproblem_id"], | ||||
| 				), | ||||
| 				'resitele': | ||||
| 				autocomplete.ModelSelect2Multiple( | ||||
| 					url='autocomplete_resitel_public', | ||||
| 					attrs = {'data-placeholder--id': '-1', | ||||
| 						'data-placeholder--text' : '---', | ||||
| 						'data-close-on-select': 'false', | ||||
| 						'data-dropdown-css-class': 's2m-se-zaskrtavatky', | ||||
| 						'data-allow-clear': 'true'}, | ||||
| 				) | ||||
| 		} | ||||
| 
 | ||||
| 	nadproblem_id = forms.IntegerField(required=False, disabled=True, widget=forms.HiddenInput()) | ||||
| 
 | ||||
| 	def __init__(self, *args, **kwargs): | ||||
| 		super().__init__(*args, **kwargs) | ||||
| 		# FIXME Z nějakého důvodu se do této třídy dostaneme i bez resitele | ||||
| 		if 'resitele' in self.fields: | ||||
| 			# FIXME Mnohem hezčí by to bylo u definice resitele výše, ale nepodařilo se mi to. | ||||
| 			self.fields['resitele'].required = False | ||||
| 			self.fields['resitele'].label = "Další autoři" | ||||
| 		if 'problem' in self.fields: | ||||
| 			self.fields['problem'].label = "Všechny řešené problémy" | ||||
| 
 | ||||
| 	def clean_problem(self): | ||||
| 		problem = self.cleaned_data.get('problem') | ||||
| 		for p in problem: | ||||
| 			if p.stav != m.Problem.STAV_ZADANY: | ||||
| 				raise forms.ValidationError("Problém " + str(p) + " již nelze řešit!") | ||||
| 		return problem | ||||
| 
 | ||||
| ReseniSPrilohamiFormSet = inlineformset_factory(m.Reseni,m.PrilohaReseni,  | ||||
| 		form = NahrajReseniForm, | ||||
|  |  | |||
|  | @ -7,19 +7,20 @@ | |||
| {% block content %} | ||||
| <h1> | ||||
|   {% block nadpis1a %} | ||||
|     Vložit řešení | ||||
|     Nahrát řešení | ||||
|   {% endblock %} | ||||
| </h1> | ||||
| 
 | ||||
| <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();"> | ||||
| <form enctype="multipart/form-data" action="{% url 'seminar_nahraj_reseni' nadproblem_id %}" method="post" onsubmit="return zkontroluj_prilohy();"> | ||||
|   {% csrf_token %} | ||||
|   <table class='form' id="reseni"> | ||||
|   <table class='form'> | ||||
|     <tr> | ||||
|       <td><label class="field-label field-required" for="tema">Téma:</label></td> | ||||
|       <td><input id="tema" disabled="" type="text" value="{{ nadproblem }}"></td> | ||||
|     </tr> | ||||
| 
 | ||||
|     {% with field=form.problem %} | ||||
|     <tr> | ||||
|       {% for field in form %} | ||||
|       <td> | ||||
|         <label class="field-label{% if field.field.required %} field-required{% endif %}" for="{{ field.id_for_label }}"> | ||||
|           {{ field.label }}: | ||||
|  | @ -28,15 +29,54 @@ | |||
|       <td> | ||||
|         {{ field }} | ||||
|       </td> | ||||
|       {% endfor %} | ||||
|     </tr> | ||||
| 
 | ||||
|     {% if field.errors %} | ||||
|       <tr> | ||||
|         <td colspan="2"><span class="field-error">{{ field.errors }}</span></td> | ||||
|       </tr> | ||||
|     {% endif %} | ||||
| 
 | ||||
|     {% endwith %} | ||||
|   </table> | ||||
| 
 | ||||
|   {% for field in form.hidden_fields %} | ||||
|     {{ field }} | ||||
|   {% endfor %} | ||||
| 
 | ||||
|   <hr> | ||||
|   <h4>Spolupráce s dalšími řešiteli</h4> | ||||
| 
 | ||||
|   <p>Pokud řešíte ve více lidech, je <strong>potřeba</strong> přidat tyto lidi jako „Další autory“. V tomto poli se vyhledává podle přezdívek, které si lze nastavit v „Osobních údajích“. Sebe vyplňovat nemusíte a za skupinu odevzdávejte pouze <strong>jednou</strong> (ne každý sám).</p> | ||||
| 
 | ||||
|   <table class='form'> | ||||
|     {% with field=form.resitele %} | ||||
|       <tr> | ||||
|         <td> | ||||
|           <label class="field-label{% if field.field.required %} field-required{% endif %}" for="{{ field.id_for_label }}"> | ||||
|             {{ field.label }}: | ||||
|           </label> | ||||
|         </td> | ||||
|         <td> | ||||
|           {{ field }} | ||||
|         </td> | ||||
|       </tr> | ||||
| 
 | ||||
|       {% if field.errors %} | ||||
|         <tr> | ||||
|           <td colspan="2"><span class="field-error">{{ field.errors }}</span></td> | ||||
|         </tr> | ||||
|       {% endif %} | ||||
| 
 | ||||
|     {% endwith %} | ||||
|   </table> | ||||
| 
 | ||||
| <hr> | ||||
| 
 | ||||
| {% include "odevzdavatko/prilohy.html" %} | ||||
| 
 | ||||
| {{form.non_field_errors}} | ||||
| 
 | ||||
| <hr> | ||||
| <h4>Odevzdat řešení</h4> | ||||
| <input type="submit" value="Odevzdat"> | ||||
|  |  | |||
|  | @ -0,0 +1,21 @@ | |||
| {% extends "base.html" %} | ||||
| {% load static %} | ||||
| 
 | ||||
| {% block content %} | ||||
| <h1> | ||||
|   {% block nadpis1a %} | ||||
|     Nahrát řešení | ||||
|   {% endblock %} | ||||
| </h1> | ||||
| 
 | ||||
| <h4>Seznam témat k odevzdání</h4> | ||||
| 
 | ||||
| <ul> | ||||
|   {% for problem in object_list %} | ||||
|     <li><a href="{% url 'seminar_nahraj_reseni' problem.id %}">{{ problem }}</a></li> | ||||
|   {% empty %} | ||||
|     <li>Nelze nic odevzdávat.</li> | ||||
|   {% endfor %} | ||||
| </ul> | ||||
| 
 | ||||
| {% endblock %} | ||||
|  | @ -2,8 +2,9 @@ | |||
| 
 | ||||
| <h4>Soubory s řešením</h4> | ||||
| 
 | ||||
| <p style="text-align: justify">Maximální součet velikostí příloh je cca 49 MB. Pokud je to možné a dává to smysl, pošli nám prosím své řešení ve formátu PDF, ostatní formáty nemusíme umět otevřít.</p> | ||||
| <p style="text-align: justify">Pokud svůj soubor rozumně pojmenuješ, urychlíš opravování a předejdeš tomu, že si nějakého tvého řešení nevšimneme. Například z <code>img_250921_101205.pdf</code> nepoznáme, kterou úlohu jsi odevzdal, zato <code>uloha_3.pdf</code> nebo <code>tema_1.pdf</code>, to už je něco jiného. Případně můžeš využít i poznámku řešitele.</p> | ||||
| <p style="text-align: justify">Pokud je to možné a dává to smysl (tj. není to třeba kód nebo doprovodný obrázek), pošli nám prosím své řešení ve formátu <strong>PDF</strong>, ostatní formáty nemusíme umět otevřít.</p> | ||||
| <p style="text-align: justify">Pokud svůj soubor <strong>rozumně pojmenuješ</strong>, urychlíš opravování a předejdeš tomu, že si nějakého tvého řešení nevšimneme. Například z <code>img_250921_101205.pdf</code> nepoznáme, kterou úlohu jsi odevzdal, zato <code>uloha_3.pdf</code> nebo <code>tema_1.pdf</code>, to už je něco jiného. Případně můžeš využít i poznámku řešitele.</p> | ||||
| <p style="text-align: justify">Maximální součet velikostí příloh je cca <strong>49 MB</strong>.</p> | ||||
| 
 | ||||
| <div id="form_set"> | ||||
| {% for form in prilohy.forms %} | ||||
|  |  | |||
|  | @ -19,8 +19,9 @@ from seminar.utils import org_required, resitel_required, viewMethodSwitch, \ | |||
| from . import views | ||||
| 
 | ||||
| urlpatterns = [ | ||||
| 	path('org/add_solution', org_required(views.PosliReseniView.as_view()), name='seminar_vloz_reseni'), | ||||
| 	path('resitel/nahraj_reseni', resitel_required(views.NahrajReseniView.as_view()), name='seminar_nahraj_reseni'), | ||||
| 	path('org/add_solution', org_required(views.VlozReseniView.as_view()), name='seminar_vloz_reseni'), | ||||
| 	path('resitel/nahraj_reseni', resitel_required(views.NahrajReseniRozcestnikTematekView.as_view()), name='seminar_nahraj_reseni'), | ||||
| 	path('resitel/nahraj_reseni/<int:nadproblem_id>/', resitel_required(views.NahrajReseniView.as_view()), name='seminar_nahraj_reseni'), | ||||
| 	path('resitel/odevzdana_reseni/', resitel_or_org_required(views.PrehledOdevzdanychReseni.as_view()), name='seminar_resitel_odevzdana_reseni'), | ||||
| 
 | ||||
| 	path('org/reseni/', org_required(views.TabulkaOdevzdanychReseniView.as_view()), name='odevzdavatko_tabulka'), | ||||
|  |  | |||
|  | @ -367,8 +367,8 @@ class SeznamAktualnichReseniView(SeznamReseniView): | |||
| 		return qs | ||||
| 
 | ||||
| 
 | ||||
| class PosliReseniView(LoginRequiredMixin, FormView): | ||||
| 	template_name = 'odevzdavatko/posli_reseni.html' | ||||
| class VlozReseniView(LoginRequiredMixin, FormView): | ||||
| 	template_name = 'odevzdavatko/vloz_reseni.html' | ||||
| 	form_class = f.PosliReseniForm | ||||
| 
 | ||||
| 	def form_valid(self, form): | ||||
|  | @ -399,12 +399,27 @@ class PosliReseniView(LoginRequiredMixin, FormView): | |||
| 		return data | ||||
| 
 | ||||
| 
 | ||||
| class NahrajReseniRozcestnikTematekView(LoginRequiredMixin, ListView): | ||||
| 	model = m.Problem | ||||
| 	template_name = 'odevzdavatko/nahraj_reseni_nadproblem.html' | ||||
| 
 | ||||
| 	def get_queryset(self): | ||||
| 		return super().get_queryset().filter(stav=m.Problem.STAV_ZADANY, nadproblem__isnull=True) | ||||
| 
 | ||||
| 
 | ||||
| class NahrajReseniView(LoginRequiredMixin, CreateView): | ||||
| 	model = m.Reseni | ||||
| 	template_name = 'odevzdavatko/nahraj_reseni.html' | ||||
| 	form_class = f.NahrajReseniForm | ||||
| 
 | ||||
| 	def get(self, request, *args, **kwargs): | ||||
| 		# Zaříznutí nezadaných problémů | ||||
| 		nadproblem_id = self.kwargs["nadproblem_id"] | ||||
| 		self.nadproblem = get_object_or_404(m.Problem, id=nadproblem_id) | ||||
| 		if self.nadproblem.stav != "zadany": | ||||
| 			raise PermissionDenied() | ||||
| 
 | ||||
| 		 | ||||
| 		# Zaříznutí starých řešitelů: | ||||
| 		# FIXME: Je to tady dost naprasené, mělo by to asi být jinde… | ||||
| 		osoba = m.Osoba.objects.get(user=self.request.user) | ||||
|  | @ -417,12 +432,23 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): | |||
| 			}) | ||||
| 		return super().get(request, *args, **kwargs) | ||||
| 
 | ||||
| 	def get_initial(self): | ||||
| 		nadproblem_id = self.nadproblem.id | ||||
| 		return { | ||||
| 			"nadproblem_id": nadproblem_id, | ||||
| 			"problem": [] if self.nadproblem.podproblem.filter(stav=m.Problem.STAV_ZADANY).exists() else nadproblem_id | ||||
| 
 | ||||
| 		} | ||||
| 
 | ||||
| 	def get_context_data(self,**kwargs): | ||||
| 		data = super().get_context_data(**kwargs) | ||||
| 		if self.request.POST: | ||||
| 			data['prilohy'] = f.ReseniSPrilohamiFormSet(self.request.POST,self.request.FILES) | ||||
| 		else: | ||||
| 			data['prilohy'] = f.ReseniSPrilohamiFormSet() | ||||
| 
 | ||||
| 		data["nadproblem_id"] = self.nadproblem.id | ||||
| 		data["nadproblem"] = get_object_or_404(m.Problem, id=self.nadproblem.id) | ||||
| 		return data | ||||
| 
 | ||||
| 	# FIXME prepsat tak, aby form_valid se volalo jen tehdy, kdyz je form i formset validni | ||||
|  | @ -474,4 +500,8 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): | |||
| 			to=list(prijemci), | ||||
| 		).send() | ||||
| 
 | ||||
| 		return formularOKView(self.request, text='Řešení úspěšně odevzdáno') | ||||
| 		return formularOKView( | ||||
| 			self.request, | ||||
| 			text='Řešení úspěšně odevzdáno', | ||||
| 			dalsi_odkazy=[("Odevzdat další řešení", reverse("seminar_nahraj_reseni"))], | ||||
| 		) | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ | |||
| 
 | ||||
| <a href="{% url 'logout' %}">Odhlásit se</a><br> | ||||
| <a href="{% url 'seminar_resitel_edit' %}">Upravit údaje</a><br> | ||||
| <a href="{% url 'seminar_nahraj_reseni' %}">Poslat řešení</a><br> | ||||
| <a href="{% url 'seminar_nahraj_reseni' %}">Nahrát řešení</a><br> | ||||
| <a href="{% url 'seminar_resitel_odevzdana_reseni' %}">Již odevzdaná řešení</a><br> | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -173,7 +173,11 @@ def resitelEditView(request): | |||
| 					msg = "Unknown school {}, {}".format(fcd['skola_nazev'],fcd['skola_adresa']) | ||||
| 				resitel_edit.save() | ||||
| 			osoba_edit.save() | ||||
| 			return formularOKView(request, text=f'Údaje byly úspěšně uloženy. <a href="{reverse("profil")}">Vrátit se zpět na profil.</a>') | ||||
| 			return formularOKView( | ||||
| 				request, | ||||
| 				text='Údaje byly úspěšně uloženy.', | ||||
| 				dalsi_odkazy=[("Vrátit se zpět na profil", reverse("profil"))], | ||||
| 			) | ||||
| 
 | ||||
| 	return render(request, 'personalni/udaje/edit.html', {'form': form}) | ||||
| 
 | ||||
|  |  | |||
|  | @ -35,6 +35,7 @@ from django.conf import settings | |||
| import unicodedata | ||||
| import logging | ||||
| import time | ||||
| from collections.abc import Sequence | ||||
| 
 | ||||
| from seminar.utils import aktivniResitele | ||||
| 
 | ||||
|  | @ -677,9 +678,9 @@ def StavDatabazeView(request): | |||
| 
 | ||||
| 
 | ||||
| # Interní, nemá se nikdy objevit v urls (jinak to účastníci vytrolí) | ||||
| def formularOKView(request, text=''): | ||||
| def formularOKView(request, text='', dalsi_odkazy: Sequence[tuple[str, str]] = ()): | ||||
| 	template_name = 'seminar/formular_ok.html' | ||||
| 	odkazy = [ | ||||
| 	odkazy = list(dalsi_odkazy) + [ | ||||
| 		# (Text, odkaz) | ||||
| 		('Vrátit se na titulní stránku', reverse('titulni_strana')), | ||||
| 		('Zobrazit aktuální zadání', reverse('seminar_aktualni_zadani')), | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue