WIP: Nástroj pro plošné vyrábění problémů #17
					 34 changed files with 231 additions and 260 deletions
				
			
		
							
								
								
									
										24
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | ||||||
|  | # Univerzální popis našich konvencí pro nastavení editorů. | ||||||
|  | # Vizte https://editorconfig.org pro detaily | ||||||
|  | 
 | ||||||
|  | root = true | ||||||
|  | 
 | ||||||
|  | [*] | ||||||
|  | charset = utf-8 | ||||||
|  | # Unixové řádky | ||||||
|  | end_of_line = lf | ||||||
|  | insert_final_newline = true | ||||||
|  | 
 | ||||||
|  | [*.{py,css}] | ||||||
|  | indent_style = tab | ||||||
|  | # Nenařizujeme konkrétní šířku tabulátoru | ||||||
|  | indent_size = unset | ||||||
|  | 
 | ||||||
|  | # Automaticky generované migrace dodržují PEP-8, nemá smysl s tím moc bojovat. | ||||||
|  | [*/migrations/*.py] | ||||||
|  | indent_style = space | ||||||
|  | indent_size = 4 | ||||||
|  | 
 | ||||||
|  | [*.html] | ||||||
|  | indent_style = space | ||||||
|  | indent_size = 2 | ||||||
|  | @ -23,6 +23,7 @@ urlpatterns = [ | ||||||
| 	path('api/autocomplete/skola/', views.SkolaAutocomplete.as_view(), name='autocomplete_skola'), | 	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/', org_required(views.ResitelAutocomplete.as_view()), name='autocomplete_resitel'), | ||||||
| 	path('api/autocomplete/problem/odevzdatelny', views.OdevzdatelnyProblemAutocomplete.as_view(), name='autocomplete_problem_odevzdatelny'), | 	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'), | ||||||
| 
 | 
 | ||||||
| 	# Ceka na autocomplete v3 | 	# Ceka na autocomplete v3 | ||||||
| 	# path('autocomplete/organizatori/', | 	# path('autocomplete/organizatori/', | ||||||
|  |  | ||||||
|  | @ -66,6 +66,21 @@ class OdevzdatelnyProblemAutocomplete(autocomplete.Select2QuerySetView): | ||||||
| 					Q(nazev__icontains=self.q)) | 					Q(nazev__icontains=self.q)) | ||||||
| 		return qs | 		return qs | ||||||
| 
 | 
 | ||||||
|  | class ProblemAutocomplete(autocomplete.Select2QuerySetView): | ||||||
|  | 	""" View k :mod:`dal.autocomplete` pro vyhledávání problémů především v odevzdávátku. """ | ||||||
|  | 	def get_queryset(self): | ||||||
|  | 		# FIXME i starší úlohy | ||||||
|  | 		nastaveni = get_object_or_404(m.Nastaveni) | ||||||
|  | 		rocnik = nastaveni.aktualni_rocnik | ||||||
|  | 		temaQ = Q(Tema___rocnik = rocnik) | ||||||
|  | 		ulohaQ = Q(Uloha___cislo_zadani__rocnik=rocnik) | ||||||
|  | 		clanekQ = Q(Clanek___cislo__rocnik=rocnik) | ||||||
|  | 		qs = m.Problem.objects.filter(temaQ | ulohaQ | clanekQ).order_by("-stav", "nazev") | ||||||
|  | 		if self.q: | ||||||
|  | 			qs = qs.filter( | ||||||
|  | 					Q(nazev__icontains=self.q)) | ||||||
|  | 		return qs | ||||||
|  | 
 | ||||||
| # Ceka na autocomplete v3 | # Ceka na autocomplete v3 | ||||||
| # class OrganizatorAutocomplete(autocomplete.Select2QuerySetView): | # class OrganizatorAutocomplete(autocomplete.Select2QuerySetView): | ||||||
| # 	def get_queryset(self): | # 	def get_queryset(self): | ||||||
|  |  | ||||||
|  | @ -160,6 +160,9 @@ INSTALLED_APPS = ( | ||||||
| #    'admin_tools.menu', | #    'admin_tools.menu', | ||||||
| #    'admin_tools.dashboard', | #    'admin_tools.dashboard', | ||||||
|     'django.contrib.admin', |     'django.contrib.admin', | ||||||
|  | 
 | ||||||
|  |     # Nechat na konci (INSTALLED_APPS je uspořádané): | ||||||
|  |     'django_cleanup.apps.CleanupConfig',  # Uklízí media/ | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| DEBUG_TOOLBAR_CONFIG = { | DEBUG_TOOLBAR_CONFIG = { | ||||||
|  |  | ||||||
|  | @ -31,6 +31,7 @@ TEMPLATES[0]['OPTIONS']['debug'] = True | ||||||
| from ipaddress import ip_network | from ipaddress import ip_network | ||||||
| ALLOWED_HOSTS = [str(ip) for ip in ip_network('192.168.0.0/16')] | ALLOWED_HOSTS = [str(ip) for ip in ip_network('192.168.0.0/16')] | ||||||
| ALLOWED_HOSTS.append('127.0.0.1') | ALLOWED_HOSTS.append('127.0.0.1') | ||||||
|  | ALLOWED_HOSTS.append('localhost') | ||||||
| 
 | 
 | ||||||
| # Database | # Database | ||||||
| # https://docs.djangoproject.com/en/1.7/ref/settings/#databases | # https://docs.djangoproject.com/en/1.7/ref/settings/#databases | ||||||
|  |  | ||||||
|  | @ -421,6 +421,10 @@ input { | ||||||
| 	margin: 5px; | 	margin: 5px; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | textarea.feedback { | ||||||
|  | 	margin: 5px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| /* titulni stranka */ | /* titulni stranka */ | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -10,5 +10,5 @@ | ||||||
| {% block bodyclass %}{{ LOCAL_TEST_PROD }}web{% endblock %} | {% block bodyclass %}{{ LOCAL_TEST_PROD }}web{% endblock %} | ||||||
| 
 | 
 | ||||||
| {% block branding %} | {% block branding %} | ||||||
| <h1 id="site-name"><a href="/"> M&M GWP web </a></h1> | <h1 id="site-name"><a href="/"> M&M web </a></h1> | ||||||
| {% endblock %} | {% endblock %} | ||||||
|  |  | ||||||
|  | @ -3,11 +3,9 @@ | ||||||
| {% autoescape off %} | {% autoescape off %} | ||||||
| <ul class="menu_mobile"> | <ul class="menu_mobile"> | ||||||
|     {% for item in sitetree_items %} |     {% for item in sitetree_items %} | ||||||
|         <li class="{% if item.has_children %}dropdown{% endif %} {% if item.is_current or item.in_current_branch %}active{% endif %}" |         <li class="{% if item.has_children %}dropdown{% endif %} {% if item.is_current or item.in_current_branch %}active{% endif %}"> | ||||||
|             style="{% if item.title == "HIDDEN" %}display:none{% endif %}" |  | ||||||
|         > |  | ||||||
|             <a href="{% if item.has_children %}#{% else %}{% sitetree_url for item %}{% endif %}" {% if item.has_children %}class="dropdown-toggle" data-toggle="dropdown"{% endif %}> |             <a href="{% if item.has_children %}#{% else %}{% sitetree_url for item %}{% endif %}" {% if item.has_children %}class="dropdown-toggle" data-toggle="dropdown"{% endif %}> | ||||||
|                 {{ item.title_resolved }} |                 {% if item.title == "HIDDEN" %}Korektury{% else %}{{ item.title_resolved }}{% endif %} | ||||||
|             </a> |             </a> | ||||||
|             <div class="submenu_mobile {% if item.is_current or item.in_current_branch %}active{% endif %}"> |             <div class="submenu_mobile {% if item.is_current or item.in_current_branch %}active{% endif %}"> | ||||||
|             {% if item.has_children %} |             {% if item.has_children %} | ||||||
|  |  | ||||||
|  | @ -88,11 +88,12 @@ ReseniSPrilohamiFormSet = inlineformset_factory(m.Reseni,m.PrilohaReseni, | ||||||
| class JednoHodnoceniForm(forms.ModelForm): | class JednoHodnoceniForm(forms.ModelForm): | ||||||
| 	class Meta: | 	class Meta: | ||||||
| 		model = m.Hodnoceni | 		model = m.Hodnoceni | ||||||
| 		fields = ('problem', 'body', 'deadline_body') | 		fields = ('problem', 'body', 'deadline_body', 'feedback',) | ||||||
| 		widgets = { | 		widgets = { | ||||||
| 			'problem': autocomplete.ModelSelect2( | 			'problem': autocomplete.ModelSelect2( | ||||||
| 				url='autocomplete_problem_odevzdatelny',   # FIXME: Dovolit i starší? | 				url='autocomplete_problem', | ||||||
| 				) | 				), | ||||||
|  | 			'feedback': forms.Textarea(attrs={'rows': 1, 'cols': 30, 'class': 'feedback'}), | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| OhodnoceniReseniFormSet = formset_factory(JednoHodnoceniForm, | OhodnoceniReseniFormSet = formset_factory(JednoHodnoceniForm, | ||||||
|  | @ -155,10 +156,7 @@ class OdevzdavatkoTabulkaFiltrForm(forms.Form): | ||||||
| 
 | 
 | ||||||
| 		result = [] | 		result = [] | ||||||
| 
 | 
 | ||||||
| 		date_str = strftime(DATE_FORMAT, datetime.date.min.timetuple()) | 		result.append(("0001-01-01", f"Odjakživa")) | ||||||
| 		if date_str[0] == '1':  # Někde očividně vrací strftime rok bez nul. |  | ||||||
| 			date_str = "000" + date_str |  | ||||||
| 		result.append((date_str, f"Odjakživa")) |  | ||||||
| 
 | 
 | ||||||
| 		for deadline in m.Deadline.objects.filter( | 		for deadline in m.Deadline.objects.filter( | ||||||
| 				deadline__lte=timezone.now(), | 				deadline__lte=timezone.now(), | ||||||
|  |  | ||||||
|  | @ -91,7 +91,7 @@ $(document).ready(function(){ | ||||||
| 
 | 
 | ||||||
| <form method=post onsubmit="return zkontroluj_hodnoceni();"> | <form method=post onsubmit="return zkontroluj_hodnoceni();"> | ||||||
| {# Poznámka #} | {# Poznámka #} | ||||||
| <h3>Poznámka:</h3> | <h3>Neveřejná poznámka:</h3> | ||||||
| <p>{{ poznamka_form.poznamka }}</p> | <p>{{ poznamka_form.poznamka }}</p> | ||||||
| 
 | 
 | ||||||
| {# Hodnocení: #} | {# Hodnocení: #} | ||||||
|  | @ -101,13 +101,14 @@ $(document).ready(function(){ | ||||||
| {{ form.management_form }} | {{ form.management_form }} | ||||||
| </table> | </table> | ||||||
| <table id="form_set"> | <table id="form_set"> | ||||||
| <tr><th>Problém</th><th>Body</th><th>Deadline pro body</th></tr> | <tr><th>Problém</th><th>Body</th><th>Deadline pro body</th><th>Zpětná vazba pro řešitele</th></tr> | ||||||
| {% for subform in form %} | {% for subform in form %} | ||||||
|     <tbody> |     <tbody> | ||||||
| 	<tr class="hodnoceni"> | 	<tr class="hodnoceni"> | ||||||
| 		<td>{{ subform.problem }}</td> | 		<td>{{ subform.problem }}</td> | ||||||
| 		<td>{{ subform.body }}</td> | 		<td>{{ subform.body }}</td> | ||||||
| 		<td>{{ subform.deadline_body }}</td> | 		<td>{{ subform.deadline_body }}</td> | ||||||
|  | 		<td>{{ subform.feedback }}</td> | ||||||
| 		<td><a href="#" class="smazat_hodnoceni" id="id_{{subform.prefix}}-jsremove"><img src="{% static "odevzdavatko/cross.png" %}" alt="Smazat"></a></td> | 		<td><a href="#" class="smazat_hodnoceni" id="id_{{subform.prefix}}-jsremove"><img src="{% static "odevzdavatko/cross.png" %}" alt="Smazat"></a></td> | ||||||
| 	</tr> | 	</tr> | ||||||
|     </tbody> |     </tbody> | ||||||
|  | @ -123,6 +124,7 @@ $(document).ready(function(){ | ||||||
| 		<td>{{ form.empty_form.problem }}</td> | 		<td>{{ form.empty_form.problem }}</td> | ||||||
| 		<td>{{ form.empty_form.body }}</td> | 		<td>{{ form.empty_form.body }}</td> | ||||||
| 		<td>{{ form.empty_form.deadline_body }}</td> | 		<td>{{ form.empty_form.deadline_body }}</td> | ||||||
|  | 		<td>{{ form.empty_form.feedback }}</td> | ||||||
| 		<td><a href="#" class="smazat_hodnoceni" id="id_{{form.empty_form.prefix}}-jsremove"><img src="{% static "odevzdavatko/cross.png" %}" alt="Smazat"></a></td> | 		<td><a href="#" class="smazat_hodnoceni" id="id_{{form.empty_form.prefix}}-jsremove"><img src="{% static "odevzdavatko/cross.png" %}" alt="Smazat"></a></td> | ||||||
| 	</tr> | 	</tr> | ||||||
| </table> | </table> | ||||||
|  |  | ||||||
|  | @ -37,11 +37,12 @@ | ||||||
| {# Hodnocení: #} | {# Hodnocení: #} | ||||||
| <h3>Hodnocení:</h3> | <h3>Hodnocení:</h3> | ||||||
| <table id="form_set" class="dosla_reseni"> | <table id="form_set" class="dosla_reseni"> | ||||||
| <tr><th>Problém</th><th>Body</th>{# <th>Deadline pro body</th> #}</tr> | <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 %} | {% for h in hodnoceni %} | ||||||
| 	<tr class="hodnoceni"> | 	<tr class="hodnoceni"> | ||||||
| 		<td>{{ h.problem }}</td> | 		<td>{{ h.problem }}</td> | ||||||
| 		<td>{{ h.body }}</td> | 		<td>{{ h.body }}</td> | ||||||
|  | 		<td>{{ h.feedback }}</td> | ||||||
| {#		<td>{{ h.deadline_body }}</td>#} | {#		<td>{{ h.deadline_body }}</td>#} | ||||||
| 	</tr> | 	</tr> | ||||||
| {% endfor %} | {% endfor %} | ||||||
|  |  | ||||||
|  | @ -218,10 +218,11 @@ class DetailReseniView(DetailView): | ||||||
| 		self.reseni = get_object_or_404(m.Reseni, id=self.kwargs['pk']) | 		self.reseni = get_object_or_404(m.Reseni, id=self.kwargs['pk']) | ||||||
| 		result = [] # Slovníky s klíči problem, body, deadline_body -- initial data pro f.OhodnoceniReseniFormSet | 		result = [] # Slovníky s klíči problem, body, deadline_body -- initial data pro f.OhodnoceniReseniFormSet | ||||||
| 		for hodn in m.Hodnoceni.objects.filter(reseni=self.reseni): | 		for hodn in m.Hodnoceni.objects.filter(reseni=self.reseni): | ||||||
| 			result.append( | 			result.append({ | ||||||
| 				{"problem": hodn.problem,  | 				"problem": hodn.problem, | ||||||
| 				"body": hodn.body, | 				"body": hodn.body, | ||||||
| 				"deadline_body": hodn.deadline_body, | 				"deadline_body": hodn.deadline_body, | ||||||
|  | 				"feedback": hodn.feedback, | ||||||
| 				}) | 				}) | ||||||
| 		return result | 		return result | ||||||
| 
 | 
 | ||||||
|  | @ -245,30 +246,26 @@ def hodnoceniReseniView(request, pk, *args, **kwargs): | ||||||
| 	formset = f.OhodnoceniReseniFormSet(request.POST) | 	formset = f.OhodnoceniReseniFormSet(request.POST) | ||||||
| 	poznamka_form = f.PoznamkaReseniForm(request.POST, instance=reseni) | 	poznamka_form = f.PoznamkaReseniForm(request.POST, instance=reseni) | ||||||
| 	# TODO: Napsat validaci formuláře a formsetu | 	# TODO: Napsat validaci formuláře a formsetu | ||||||
| 	# TODO: Implementovat větev, kdy formulář validní není. | 	if not (formset.is_valid() and poznamka_form.is_valid()): | ||||||
| 	if formset.is_valid() and poznamka_form.is_valid(): | 		raise ValueError(formset.errors, poznamka_form.errors) | ||||||
| 		with transaction.atomic(): |  | ||||||
| 			# Poznámka je jednoduchá na zpracování: |  | ||||||
| 			poznamka_form.save() |  | ||||||
| 
 | 
 | ||||||
| 			# Smažeme všechna dosavadní hodnocení tohoto řešení | 	with transaction.atomic(): | ||||||
| 			qs = m.Hodnoceni.objects.filter(reseni=reseni) | 		# Poznámka je jednoduchá na zpracování: | ||||||
| 			logger.info(f"Will delete {qs.count()} objects: {qs}") | 		poznamka_form.save() | ||||||
| 			qs.delete() | 
 | ||||||
| 			 | 		# Smažeme všechna dosavadní hodnocení tohoto řešení | ||||||
| 			# Vyrobíme nová podle formsetu | 		qs = m.Hodnoceni.objects.filter(reseni=reseni) | ||||||
| 			for form in formset: | 		logger.info(f"Will delete {qs.count()} objects: {qs}") | ||||||
| 				problem = form.cleaned_data['problem'] | 		qs.delete() | ||||||
| 				body = form.cleaned_data['body'] | 
 | ||||||
| 				deadline_body = form.cleaned_data['deadline_body'] | 		# Vyrobíme nová podle formsetu | ||||||
| 				hodnoceni = m.Hodnoceni( | 		for form in formset: | ||||||
| 					problem=problem, | 			hodnoceni = m.Hodnoceni( | ||||||
| 					body=body, |  | ||||||
| 					deadline_body=deadline_body, |  | ||||||
| 					reseni=reseni, | 					reseni=reseni, | ||||||
|  | 					**form.cleaned_data, | ||||||
| 					) | 					) | ||||||
| 				logger.info(f"Creating Hodnoceni: {hodnoceni}") | 			logger.info(f"Creating Hodnoceni: {hodnoceni}") | ||||||
| 				hodnoceni.save() | 			hodnoceni.save() | ||||||
| 
 | 
 | ||||||
| 	return redirect(success_url) | 	return redirect(success_url) | ||||||
| 
 | 
 | ||||||
|  | @ -285,6 +282,7 @@ class ResitelReseniView(DetailView): | ||||||
| 				{ | 				{ | ||||||
| 					"problem": hodn.problem, | 					"problem": hodn.problem, | ||||||
| 					"body": hodn.body, | 					"body": hodn.body, | ||||||
|  | 					"feedback": hodn.feedback, | ||||||
| 					# "deadline_body": hodn.deadline_body, | 					# "deadline_body": hodn.deadline_body, | ||||||
| 				} | 				} | ||||||
| 			) | 			) | ||||||
|  |  | ||||||
|  | @ -8,13 +8,13 @@ Soubor sloužící jako „router“, tj. zde se definují url adresy a na co uk | ||||||
| - ``prednasky/seznam_prednasek/<int:seznam>/`` (seznam-list) :class:`~prednasky.views.SeznamListView` | - ``prednasky/seznam_prednasek/<int:seznam>/`` (seznam-list) :class:`~prednasky.views.SeznamListView` | ||||||
| """ | """ | ||||||
| from django.urls import path | from django.urls import path | ||||||
| from seminar.utils import org_required, resitel_required | from seminar.utils import org_required, resitel_or_org_required | ||||||
| from . import views | from . import views | ||||||
| 
 | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
| 	path( | 	path( | ||||||
| 		'prednasky/', | 		'prednasky/', | ||||||
| 		resitel_required(views.newPrednaska) | 		resitel_or_org_required(views.newPrednaska) | ||||||
| 	), | 	), | ||||||
| 	path('prednasky/hotovo', views.Prednaska_hotovo), | 	path('prednasky/hotovo', views.Prednaska_hotovo), | ||||||
| 	path( | 	path( | ||||||
|  |  | ||||||
|  | @ -21,6 +21,7 @@ django-sekizai | ||||||
| django-countries | django-countries | ||||||
| django-solo | django-solo | ||||||
| django-ckeditor | django-ckeditor | ||||||
|  | django-cleanup  # Uklízí media/ od smazaných „databázových“ souborů | ||||||
| django-flat-theme | django-flat-theme | ||||||
| django-taggit | django-taggit | ||||||
| django-autocomplete-light>=3.9.0rc1 | django-autocomplete-light>=3.9.0rc1 | ||||||
|  | @ -62,3 +63,4 @@ lorem | ||||||
| 
 | 
 | ||||||
| sphinx | sphinx | ||||||
| sphinx_rtd_theme | sphinx_rtd_theme | ||||||
|  | myst_parser | ||||||
|  |  | ||||||
							
								
								
									
										18
									
								
								seminar/migrations/0108_nastaveni_cena_sous.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								seminar/migrations/0108_nastaveni_cena_sous.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | # Generated by Django 3.2.16 on 2022-11-14 20:51 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations, models | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('seminar', '0107_zmrazenavysledkovka'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AddField( | ||||||
|  |             model_name='nastaveni', | ||||||
|  |             name='cena_sous', | ||||||
|  |             field=models.IntegerField(default=1000, verbose_name='Účastnický poplatek za soustředění'), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										18
									
								
								seminar/migrations/0109_hodnoceni_feedback.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								seminar/migrations/0109_hodnoceni_feedback.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | # Generated by Django 3.2.16 on 2022-11-14 19:30 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations, models | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('seminar', '0108_nastaveni_cena_sous'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AddField( | ||||||
|  |             model_name='hodnoceni', | ||||||
|  |             name='feedback', | ||||||
|  |             field=models.TextField(blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)', verbose_name='zpětná vazba'), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
|  | @ -113,6 +113,8 @@ class Hodnoceni(bm.SeminarModelBase): | ||||||
|     problem = models.ForeignKey(am.Problem, verbose_name='problém', |     problem = models.ForeignKey(am.Problem, verbose_name='problém', | ||||||
|                                 related_name='hodnoceni', on_delete=models.PROTECT) |                                 related_name='hodnoceni', on_delete=models.PROTECT) | ||||||
| 
 | 
 | ||||||
|  |     feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)') | ||||||
|  | 
 | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         return "{}, {}, {}".format(self.problem, self.reseni, self.body) |         return "{}, {}, {}".format(self.problem, self.reseni, self.body) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -718,6 +718,10 @@ class Nastaveni(SingletonModel): | ||||||
| 	aktualni_cislo = models.ForeignKey(Cislo, verbose_name='Aktuální číslo',  | 	aktualni_cislo = models.ForeignKey(Cislo, verbose_name='Aktuální číslo',  | ||||||
| 		null=False, on_delete=models.PROTECT) | 		null=False, on_delete=models.PROTECT) | ||||||
| 
 | 
 | ||||||
|  | 	cena_sous = models.IntegerField(null=False, | ||||||
|  | 									verbose_name="Účastnický poplatek za soustředění", | ||||||
|  | 									default=1000) | ||||||
|  | 
 | ||||||
| 	@property | 	@property | ||||||
| 	def aktualni_rocnik(self): | 	def aktualni_rocnik(self): | ||||||
| 		return self.aktualni_cislo.rocnik | 		return self.aktualni_cislo.rocnik | ||||||
|  |  | ||||||
|  | @ -1,25 +0,0 @@ | ||||||
| \input opmac |  | ||||||
| \chyph |  | ||||||
| \nopagenumbers |  | ||||||
| \parindent=0pt |  | ||||||
| 
 |  | ||||||
| \def\castka{1000} |  | ||||||
| 
 |  | ||||||
| \newread\data |  | ||||||
| \openin\data=/dev/stdin |  | ||||||
| \read\data to\termin |  | ||||||
| \read\data to\misto |  | ||||||
| 
 |  | ||||||
| \loop |  | ||||||
| 	\read\data to\ucastnik |  | ||||||
| 	\unless\ifeof\data |  | ||||||
| 		\vbox{\picw=2cm\inspic logomm.pdf \smallskip\hrule\medskip |  | ||||||
| 		Potvrzujeme, že \ucastnik se zúčastnil(a) soustředění Korespondenčního semináře M\&M konaného % \ucastnik má na konci mezeru |  | ||||||
| 		v~termínu \termin a že zaplatil(a) účastnický poplatek ve výši $\sim$\castka Kč$\sim$. % \termin též |  | ||||||
| 		\bigskip |  | ||||||
| 		\the\day.~\the\month.~\the\year, \misto\hfill Přijal(a): \hbox to 4cm{\hrulefill} |  | ||||||
| 		\bigskip |  | ||||||
| 		} |  | ||||||
| \repeat |  | ||||||
| 
 |  | ||||||
| \bye |  | ||||||
|  | @ -40,8 +40,7 @@ | ||||||
|           <li><a href="obalky.pdf">Obálky (PDF)</a></li> |           <li><a href="obalky.pdf">Obálky (PDF)</a></li> | ||||||
|           <li><a href="tituly.tex" download>Tituly (TeX)</a></li> |           <li><a href="tituly.tex" download>Tituly (TeX)</a></li> | ||||||
|           <li><a href="vysledkovka.tex" download>Výsledkovka (TeX)</a></li> |           <li><a href="vysledkovka.tex" download>Výsledkovka (TeX)</a></li> | ||||||
|           <li><a href="obalkovani">Obálkování</a></li> |           <li><a href="odmeny/{{prevcislo.rocnik.rocnik}}.{{prevcislo.poradi}}/">Odměny</a></li> | ||||||
| 	  <li><a href="odmeny/{{prevcislo.rocnik.rocnik}}.{{prevcislo.poradi}}/">Odměny</a></li> |  | ||||||
|         </ul> |         </ul> | ||||||
|       </div> |       </div> | ||||||
|   {% endif %} |   {% endif %} | ||||||
|  |  | ||||||
|  | @ -1,33 +0,0 @@ | ||||||
| {% extends "base.html" %} |  | ||||||
| 
 |  | ||||||
| {% block content %} |  | ||||||
|   <h1> |  | ||||||
|     {% block nadpis1a %} |  | ||||||
|       Obálkování {{ cislo }} |  | ||||||
|     {% endblock %} |  | ||||||
|   </h1> |  | ||||||
| 
 |  | ||||||
|   Obálkovat se budou tyto problémy: |  | ||||||
|   <ul> |  | ||||||
|     {% for p in problemy %} |  | ||||||
| 
 |  | ||||||
|       <li> {{ p.kod_v_rocniku }} {{ p }} |  | ||||||
|     {% endfor %} |  | ||||||
|   </ul> |  | ||||||
| 
 |  | ||||||
|   {% for r in reseni %} |  | ||||||
|     {% ifchanged r.resitel %} |  | ||||||
|       {% if not forloop.first %} |  | ||||||
|         </ul> |  | ||||||
|       {% endif %} |  | ||||||
|       <h4>{{ r.resitel }}</h4> |  | ||||||
|         <ul> |  | ||||||
|     {% endifchanged %} |  | ||||||
| 
 |  | ||||||
|     <li> |  | ||||||
|       {{ r.problem.kod_v_rocniku }} {{ r.problem.nazev }} ({{ r.body }}) |  | ||||||
| 
 |  | ||||||
|   {% endfor %} |  | ||||||
|   </ul> |  | ||||||
| 
 |  | ||||||
| {% endblock content %} |  | ||||||
|  | @ -7,23 +7,11 @@ | ||||||
|     {% endblock %} |     {% endblock %} | ||||||
|   </h1> |   </h1> | ||||||
| 
 | 
 | ||||||
|   <h2>Od prvního deadlinu {{ from_cislo }} do prvního deadlinu {{ to_cislo }}</h2> |   <h2>Od čísla {{ from_cislo }} do čísla {{ to_cislo }}</h2> | ||||||
|  |     (Od „{{ from_deadline }}“ (vyjma) do „{{ to_deadline }}“ (včetně)) <br/> | ||||||
|  |     <div class="field-error"><b>Posílat použe po opravení všeho v čísle {{ to_cislo }}!</b></div> | ||||||
|   <ul> |   <ul> | ||||||
| 	  {% for z in zmeny_prvni_prvni %} | 	  {% for z in zmeny %} | ||||||
| 	  <li> {{z.jmeno}}: {{z.ftitul}} → {{z.ttitul}}</li> |  | ||||||
| 	  {% endfor %} |  | ||||||
|   </ul> |  | ||||||
| 
 |  | ||||||
|   <h2>Od {{ from_cislo }} do prvního deadlinu {{ to_cislo }} (pro první číslo)</h2> |  | ||||||
|   <ul> |  | ||||||
| 	  {% for z in zmeny_posledni_prvni %} |  | ||||||
| 	  <li> {{z.jmeno}}: {{z.ftitul}} → {{z.ttitul}}</li> |  | ||||||
| 	  {% endfor %} |  | ||||||
|   </ul> |  | ||||||
| 
 |  | ||||||
|   <h2>Od prvního deadlinu {{ from_cislo }} do {{ to_cislo }} (pro poslední číslo)</h2> |  | ||||||
|   <ul> |  | ||||||
| 	  {% for z in zmeny_prvni_posledni %} |  | ||||||
| 	  <li> {{z.jmeno}}: {{z.ftitul}} → {{z.ttitul}}</li> | 	  <li> {{z.jmeno}}: {{z.ftitul}} → {{z.ttitul}}</li> | ||||||
| 	  {% endfor %} | 	  {% endfor %} | ||||||
|   </ul> |   </ul> | ||||||
|  |  | ||||||
|  | @ -1,30 +0,0 @@ | ||||||
| {% extends "base.html" %} |  | ||||||
| 
 |  | ||||||
| {% block content %} |  | ||||||
|   <h1> |  | ||||||
|     {% block nadpis1a %} |  | ||||||
|       Obálkování {{ cislo }} |  | ||||||
|     {% endblock %} |  | ||||||
|   </h1> |  | ||||||
|   <ul> |  | ||||||
|   {% for reseni in object_list %} |  | ||||||
| 	{% ifchanged reseni.resitele %} |  | ||||||
| 		{% if not forloop.first %} |  | ||||||
| 		 </ul> |  | ||||||
| 		{% endif %} |  | ||||||
| 		<h4>{% for resitel in reseni.resitele.all %}{{resitel.osoba}},{% endfor %}</h4> |  | ||||||
| 	<ul> |  | ||||||
| 	{% endifchanged %} |  | ||||||
| 
 |  | ||||||
|   <li>Celkem {{reseni.hodnoceni__body__sum}} bodů z {{reseni.hodnoceni__count}} hodnocení |  | ||||||
| 	<ul> |  | ||||||
| 		{% for h in reseni.hodnoceni_set.all %} |  | ||||||
| 		<li> {{ h.problem }}: {{ h.body }}b </li> |  | ||||||
| 		{% endfor %} |  | ||||||
| 	</ul> |  | ||||||
|   </li> |  | ||||||
|   {% endfor %} |  | ||||||
|   </ul> |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| {% endblock content %} |  | ||||||
|  | @ -4,7 +4,8 @@ | ||||||
| 
 | 
 | ||||||
|   <h1> |   <h1> | ||||||
|     {% block nadpis1a %} |     {% block nadpis1a %} | ||||||
|       Průběžné výsledky {{ rocnik.rocnik }}. ročníku |       Výsledky {{ rocnik.rocnik }}. ročníku | ||||||
|  |         {% if vysledkovka.do_deadlinu %}k datu {{ vysledkovka.do_deadlinu.deadline.date }}{% endif %} | ||||||
|     {% endblock %} |     {% endblock %} | ||||||
|   </h1> |   </h1> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ import unidecode | ||||||
| import logging | import logging | ||||||
| 
 | 
 | ||||||
| from korektury.testutils import create_test_pdf | from korektury.testutils import create_test_pdf | ||||||
| from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni, Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Osoba, Organizator, Prijemce, Tema, Uloha, Konfera, TextNode, UlohaVzorakNode, RocnikNode, CisloNode, TemaVCisleNode, Text, Hodnoceni, UlohaZadaniNode, Novinky, TreeNode | from seminar.models import Skola, Resitel, Rocnik, Cislo, Deadline, Problem, Reseni, PrilohaReseni, Nastaveni, Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Osoba, Organizator, Prijemce, Tema, Uloha, Konfera, TextNode, UlohaVzorakNode, RocnikNode, CisloNode, TemaVCisleNode, Text, Hodnoceni, UlohaZadaniNode, Novinky, TreeNode | ||||||
| import seminar.models as m | import seminar.models as m | ||||||
| 
 | 
 | ||||||
| from django.contrib.flatpages.models import FlatPage | from django.contrib.flatpages.models import FlatPage | ||||||
|  | @ -299,7 +299,7 @@ def gen_reseni_ulohy(rnd, cisla, uloha, pocet_resitelu, poradi_cisla, resitele_c | ||||||
| 		# Vytvoření řešení. | 		# Vytvoření řešení. | ||||||
| 		if uloha.cislo_zadani.zlomovy_deadline_pro_papirove_cislo() is not None: | 		if uloha.cislo_zadani.zlomovy_deadline_pro_papirove_cislo() is not None: | ||||||
| 			# combine, abychom dostali plný čas a ne jen datum | 			# combine, abychom dostali plný čas a ne jen datum | ||||||
| 			cas_doruceni = datetime.datetime.combine(uloha.cislo_zadani.datum_deadline, datetime.datetime.min.time()) - datetime.timedelta(days=random.randint(0, 40)) - datetime.timedelta(minutes=random.randint(0, 60*24)) | 			cas_doruceni = uloha.cislo_zadani.deadline_v_cisle.first().deadline - datetime.timedelta(days=random.randint(0, 40)) - datetime.timedelta(minutes=random.randint(0, 60*24)) | ||||||
| 			# astimezone, protože jinak vyhazuje warning o nenastavené TZ | 			# astimezone, protože jinak vyhazuje warning o nenastavené TZ | ||||||
| 			res = Reseni.objects.create(forma=rnd.choice(Reseni.FORMA_CHOICES)[0], cas_doruceni=cas_doruceni.astimezone(datetime.timezone.utc)) | 			res = Reseni.objects.create(forma=rnd.choice(Reseni.FORMA_CHOICES)[0], cas_doruceni=cas_doruceni.astimezone(datetime.timezone.utc)) | ||||||
| 		else: | 		else: | ||||||
|  | @ -436,22 +436,23 @@ def gen_cisla(rnd, rocniky): | ||||||
| 				(mesic_vydani + 1) % 12 + 1, | 				(mesic_vydani + 1) % 12 + 1, | ||||||
| 				rnd.randint(1, 28)) | 				rnd.randint(1, 28)) | ||||||
| 
 | 
 | ||||||
| 			# posledni 2 cisla v rocniku nemaji deadline |  | ||||||
| 			if (ci + 2 > cisel): |  | ||||||
| 				deadline = None |  | ||||||
| 
 |  | ||||||
| 			cislo = Cislo.objects.create( | 			cislo = Cislo.objects.create( | ||||||
| 				rocnik = rocnik, | 				rocnik = rocnik, | ||||||
| 				poradi = str(ci),  | 				poradi = str(ci),  | ||||||
| 				datum_vydani=vydano, | 				datum_vydani=vydano, | ||||||
| 				datum_deadline=deadline, |  | ||||||
| 				verejne_db=True, | 				verejne_db=True, | ||||||
| 				verejna_vysledkovka=True |  | ||||||
| 			) | 			) | ||||||
| 			node2 = CisloNode.objects.get(cislo = cislo) | 			node2 = CisloNode.objects.get(cislo = cislo) | ||||||
| 			node2.succ = node | 			node2.succ = node | ||||||
| 			node2.root = rocnik.rocniknode | 			node2.root = rocnik.rocniknode | ||||||
| 			cislo.save() | 			cislo.save() | ||||||
|  | 			deadline = Deadline.objects.create( | ||||||
|  | 				cislo=cislo, | ||||||
|  | 				deadline=deadline, | ||||||
|  | 				typ=Deadline.TYP_CISLA, | ||||||
|  | 				verejna_vysledkovka=True, | ||||||
|  | 			) | ||||||
|  | 			deadline.save() | ||||||
| 			node = node2 | 			node = node2 | ||||||
| 			if otec: | 			if otec: | ||||||
| 				otec = False | 				otec = False | ||||||
|  |  | ||||||
|  | @ -23,7 +23,6 @@ Soubor sloužící jako „router“, tj. zde se definují url adresy a na co uk | ||||||
| 	- ``cislo/<int:rocnik>.<str:cislo>/obalky.pdf`` (seminar_cislo_obalky) :func:`~seminar.views.views_all.cisloObalkyView` | 	- ``cislo/<int:rocnik>.<str:cislo>/obalky.pdf`` (seminar_cislo_obalky) :func:`~seminar.views.views_all.cisloObalkyView` | ||||||
| 	- ``cislo/<int:rocnik>.<str:cislo>/tituly.tex`` (seminar_cislo_titul) :func:`~seminar.views.views_all.TitulyView` | 	- ``cislo/<int:rocnik>.<str:cislo>/tituly.tex`` (seminar_cislo_titul) :func:`~seminar.views.views_all.TitulyView` | ||||||
| 	- ``stav`` (stav_databaze) :func:`~seminar.views.views_all.StavDatabazeView` | 	- ``stav`` (stav_databaze) :func:`~seminar.views.views_all.StavDatabazeView` | ||||||
| 	- ``cislo/<int:rocnik>.<str:cislo>/obalkovani`` (seminar_cislo_resitel_obalkovani) :class:`~seminar.views.views_all.ObalkovaniView` |  | ||||||
| 	- ``cislo/<int:trocnik>.<str:tcislo>/odmeny/<int:frocnik>.<str:fcislo>/`` (seminar_archiv_odmeny) :class:`~seminar.views.views_all.OdmenyView` | 	- ``cislo/<int:trocnik>.<str:tcislo>/odmeny/<int:frocnik>.<str:fcislo>/`` (seminar_archiv_odmeny) :class:`~seminar.views.views_all.OdmenyView` | ||||||
| - Další | - Další | ||||||
| 	- `` `` (titulni_strana) :class:`~seminar.views.views_all.TitulniStranaView` | 	- `` `` (titulni_strana) :class:`~seminar.views.views_all.TitulniStranaView` | ||||||
|  | @ -102,11 +101,6 @@ urlpatterns = [ | ||||||
| 		org_required(views.StavDatabazeView), | 		org_required(views.StavDatabazeView), | ||||||
| 		name='stav_databaze' | 		name='stav_databaze' | ||||||
| 	), | 	), | ||||||
| 	path( |  | ||||||
| 		'cislo/<int:rocnik>.<str:cislo>/obalkovani', |  | ||||||
| 		org_required(views.ObalkovaniView.as_view()), |  | ||||||
| 		name='seminar_cislo_resitel_obalkovani' |  | ||||||
| 	), |  | ||||||
| 	path( | 	path( | ||||||
| 		'cislo/<int:trocnik>.<str:tcislo>/odmeny/<int:frocnik>.<str:fcislo>/', | 		'cislo/<int:trocnik>.<str:tcislo>/odmeny/<int:frocnik>.<str:fcislo>/', | ||||||
| 		org_required(views.OdmenyView.as_view()), | 		org_required(views.OdmenyView.as_view()), | ||||||
|  |  | ||||||
|  | @ -53,23 +53,6 @@ logger = logging.getLogger(__name__) | ||||||
| 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 ObalkovaniView(generic.ListView): |  | ||||||
| 	template_name = 'seminar/org/obalkovani.html' |  | ||||||
| 
 |  | ||||||
| 	def get_queryset(self): |  | ||||||
| 		rocnik = get_object_or_404(Rocnik,rocnik=self.kwargs['rocnik']) |  | ||||||
| 		cislo = get_object_or_404(Cislo,rocnik=rocnik,poradi=self.kwargs['cislo']) |  | ||||||
| 		self.cislo = cislo |  | ||||||
| 		self.hodnoceni = s.Hodnoceni.objects.filter(cislo_body=cislo) |  | ||||||
| 		self.reseni = Reseni.objects.filter(hodnoceni__in = self.hodnoceni).annotate(Sum('hodnoceni__body')).annotate(Count('hodnoceni')).order_by('resitele__osoba') |  | ||||||
| 		return self.reseni |  | ||||||
| 
 |  | ||||||
| 	def get_context_data(self, **kwargs): |  | ||||||
| 		context = super(ObalkovaniView, self).get_context_data(**kwargs) |  | ||||||
| 		print(self.cislo) |  | ||||||
| 		context['cislo'] = self.cislo |  | ||||||
| 		return context |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| # FIXME: Pozor, níž je ještě jeden ProblemView! | # FIXME: Pozor, níž je ještě jeden ProblemView! | ||||||
| #class ProblemView(generic.DetailView): | #class ProblemView(generic.DetailView): | ||||||
|  | @ -484,18 +467,11 @@ class OdmenyView(generic.TemplateView): | ||||||
| 
 | 
 | ||||||
| 		context["from_cislo"] = fromcislo | 		context["from_cislo"] = fromcislo | ||||||
| 		context["to_cislo"] = tocislo | 		context["to_cislo"] = tocislo | ||||||
| 		context["zmeny_prvni_prvni"] = get_diff( | 		from_deadline = posledni_deadline_oprava(fromcislo) | ||||||
| 			fromcislo.zlomovy_deadline_pro_papirove_cislo(), | 		to_deadline = posledni_deadline_oprava(tocislo) | ||||||
| 			tocislo.zlomovy_deadline_pro_papirove_cislo() | 		context["from_deadline"] = from_deadline | ||||||
| 		) | 		context["to_deadline"] = to_deadline | ||||||
| 		context["zmeny_prvni_posledni"] = get_diff( | 		context["zmeny"] = get_diff(from_deadline, to_deadline) | ||||||
| 			fromcislo.zlomovy_deadline_pro_papirove_cislo(), |  | ||||||
| 			posledni_deadline_oprava(tocislo) |  | ||||||
| 		) |  | ||||||
| 		context["zmeny_posledni_prvni"] = get_diff( |  | ||||||
| 			posledni_deadline_oprava(fromcislo), |  | ||||||
| 			tocislo.zlomovy_deadline_pro_papirove_cislo() |  | ||||||
| 		) |  | ||||||
| 
 | 
 | ||||||
| 		return context | 		return context | ||||||
| 
 | 
 | ||||||
|  | @ -597,28 +573,6 @@ def obalkyView(request, resitele): | ||||||
| 	return response | 	return response | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def oldObalkovaniView(request, rocnik, cislo): |  | ||||||
| 	rocnik = Rocnik.objects.get(rocnik=rocnik) |  | ||||||
| 	cislo = Cislo.objects.get(rocnik=rocnik, cislo=cislo) |  | ||||||
| 
 |  | ||||||
| 	reseni = ( |  | ||||||
| 		Reseni.objects.filter(cislo_body=cislo) |  | ||||||
| 		.order_by( |  | ||||||
| 			'resitel__prijmeni', |  | ||||||
| 			'resitel__jmeno', |  | ||||||
| 			'problem__typ', |  | ||||||
| 			'problem__kod' |  | ||||||
| 		) |  | ||||||
| 	) |  | ||||||
| 
 |  | ||||||
| 	problemy = sorted(set(r.problem for r in reseni), key=lambda p: (p.typ, p.kod)) |  | ||||||
| 	return render( |  | ||||||
| 		request, |  | ||||||
| 		'seminar/archiv/cislo_obalkovani.html', |  | ||||||
| 		{'cislo': cislo, 'problemy': problemy, 'reseni': reseni} |  | ||||||
| 	) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| ### Tituly | ### Tituly | ||||||
| def TitulyViewRocnik(request, rocnik): | def TitulyViewRocnik(request, rocnik): | ||||||
| 	return TitulyView(request, rocnik, None) | 	return TitulyView(request, rocnik, None) | ||||||
|  |  | ||||||
							
								
								
									
										37
									
								
								soustredeni/templates/soustredeni/stvrzenky.tex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								soustredeni/templates/soustredeni/stvrzenky.tex
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | ||||||
|  | {% autoescape off %} | ||||||
|  | {% load static %} | ||||||
|  | {% load tex %} | ||||||
|  | \documentclass[11pt,a4paper]{article} | ||||||
|  | \usepackage[left=0.75in, right=0.75in,top=0.5in,bottom=0.5in]{geometry} | ||||||
|  | \usepackage[T1]{fontenc} | ||||||
|  | \usepackage[utf8]{inputenc} | ||||||
|  | \usepackage[czech]{babel} | ||||||
|  | \usepackage{graphicx} | ||||||
|  | \begin{document} | ||||||
|  | \pagenumbering{gobble} | ||||||
|  | \parindent=0pt | ||||||
|  | 
 | ||||||
|  | \def\stvrzenka#1#2{ | ||||||
|  |   \vbox{% | ||||||
|  |     \includegraphics[width=2cm]{logomm.pdf} | ||||||
|  |     \smallskip\hrule\medskip | ||||||
|  | 	{% with soustredeni as s %} | ||||||
|  | 		Potvrzujeme, že #1 #2 se zúčastnil(a) soustředění Korespondenčního semináře M\&M konaného | ||||||
|  | 		v~termínu {{s.datum_zacatku|date:"j.~n.~Y"|sloz}} -- | ||||||
|  |     {{s.datum_konce|date:"j.~n.~Y"|sloz}} a~že zaplatil(a) účastnický poplatek ve | ||||||
|  |     výši $\sim${{castka|sloz}}Kč$\sim$. | ||||||
|  | 
 | ||||||
|  | 		\bigskip | ||||||
|  | 		{{s.datum_zacatku|date:"j.~n.~Y"|sloz}}, {{s.misto|sloz}} \hfill Přijal(a): \hbox to 4cm{\hrulefill} | ||||||
|  | 		\bigskip | ||||||
|  | 		} | ||||||
|  | 	{% endwith %} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | {% for u in ucastnici %} | ||||||
|  | 	{% with o=u.osoba %} | ||||||
|  | 	\stvrzenka{{o.jmeno|sloz}}{{o.prijmeni|sloz}} | ||||||
|  | 	{% endwith %} | ||||||
|  | {% endfor %} | ||||||
|  | \end{document} | ||||||
|  | {% endautoescape %} | ||||||
|  | @ -1,5 +0,0 @@ | ||||||
| {% load tex %} |  | ||||||
| \newcommand{\datum}{{datum|date:"j. n. Y"|sloz}} |  | ||||||
| {% for u in ucastnici %} |  | ||||||
| \stvrzenka{{u.cislo_stvrzenky|sloz}}{{u.jmeno|sloz}}{{u.prijmeni|sloz}}{{u.ulice|sloz}}{{u.psc|sloz}}{{u.mesto|sloz}} |  | ||||||
| {% endfor %} |  | ||||||
|  | @ -1,8 +1,9 @@ | ||||||
| from django.shortcuts import get_object_or_404 | from django.shortcuts import get_object_or_404, render | ||||||
| from django.http import HttpResponse | from django.http import HttpResponse | ||||||
| from django.views import generic | from django.views import generic | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from seminar.models import Soustredeni, Resitel, Soustredeni_Ucastnici # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci | from django.contrib.staticfiles.finders import find | ||||||
|  | from seminar.models import Soustredeni, Resitel, Soustredeni_Ucastnici, Nastaveni # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci | ||||||
| import csv | import csv | ||||||
| import tempfile | import tempfile | ||||||
| import shutil | import shutil | ||||||
|  | @ -62,20 +63,17 @@ def soustredeniUcastniciExportView(request, soustredeni): | ||||||
| def soustredeniStvrzenkyView(request, soustredeni): | def soustredeniStvrzenkyView(request, soustredeni): | ||||||
| 	soustredeni = get_object_or_404(Soustredeni, id=soustredeni) | 	soustredeni = get_object_or_404(Soustredeni, id=soustredeni) | ||||||
| 	ucastnici = Resitel.objects.filter(soustredeni=soustredeni) | 	ucastnici = Resitel.objects.filter(soustredeni=soustredeni) | ||||||
|  | 	castka = Nastaveni.get_solo().cena_sous | ||||||
|  | 	tex = render(request, 'soustredeni/stvrzenky.tex', {'ucastnici': ucastnici, 'soustredeni': soustredeni, 'castka': castka}).content | ||||||
| 
 | 
 | ||||||
| 	static = Path(settings.STATIC_ROOT) |  | ||||||
| 	tempdir = Path(tempfile.mkdtemp()) | 	tempdir = Path(tempfile.mkdtemp()) | ||||||
| 	shutil.copy(static / 'images/logomm.pdf', tempdir) | 	with open(tempdir / "stvrzenky.tex", "w") as texfile: | ||||||
| 	shutil.copy(static / 'seminar/stvrzenky.tex', tempdir) | 		texfile.write(tex.decode()) | ||||||
| 	subprocess.run( |  | ||||||
| 			['pdfcsplain', 'stvrzenky.tex'], |  | ||||||
| 			cwd=tempdir, |  | ||||||
| 			encoding='utf-8', |  | ||||||
| 			input=f'{soustredeni.datum_zacatku.strftime("%-d.~%-m.~%Y")} -- {soustredeni.datum_konce.strftime("%-d.~%-m.~%Y")}\n{soustredeni.misto}\n' |  | ||||||
| 			+ '\n'.join([f'{u.osoba.jmeno} {u.osoba.prijmeni}' for u in ucastnici]) |  | ||||||
| 			) |  | ||||||
| 
 | 
 | ||||||
| 	with open(tempdir / 'stvrzenky.pdf', 'rb') as pdffile: | 	shutil.copy(find('images/logomm.pdf'), tempdir) | ||||||
|  | 	subprocess.call(["pdflatex", "stvrzenky.tex"], cwd = tempdir, stdout=subprocess.DEVNULL) | ||||||
|  | 
 | ||||||
|  | 	with open(tempdir / "stvrzenky.pdf", "rb") as pdffile: | ||||||
| 		response = HttpResponse(pdffile.read(), content_type='application/pdf') | 		response = HttpResponse(pdffile.read(), content_type='application/pdf') | ||||||
| 	shutil.rmtree(tempdir) | 	shutil.rmtree(tempdir) | ||||||
| 	return response | 	return response | ||||||
|  |  | ||||||
|  | @ -24,7 +24,7 @@ | ||||||
| 
 | 
 | ||||||
| <br> | <br> | ||||||
| <p><a href="{% url 'reset_password' %}"> | <p><a href="{% url 'reset_password' %}"> | ||||||
|     Zapomněl jsem heslo |     Zapomněl jsem heslo nebo uživatelské jméno | ||||||
| </a></p> | </a></p> | ||||||
| 
 | 
 | ||||||
| <hr> | <hr> | ||||||
|  |  | ||||||
|  | @ -1,49 +1,49 @@ | ||||||
| <div style="overflow-x: auto;"> | <div style="overflow-x: auto;"> | ||||||
| <table class='vysledkovka'> | <table class='vysledkovka'> | ||||||
|     <tr class='border-b'> |     <tr class='border-b'> | ||||||
|         <th class='border-r'># |         <th class='border-r'>#</th> | ||||||
|         <th class='border-r'>Jméno |         <th class='border-r'>Jméno</th> | ||||||
|             {% for p in vysledkovka.temata_a_spol%} |             {% for p in vysledkovka.temata_a_spol%} | ||||||
|                 <th class='border-r' id="problem{{ oznaceni_vysledkovky }}_{{ forloop.counter0 }}">{# <a href="{{ p.verejne_url }}"> #}{{ p.kod_v_rocniku }}{# </a> #} |                 <th class='border-r' id="problem{{ oznaceni_vysledkovky }}_{{ forloop.counter0 }}">{# <a href="{{ p.verejne_url }}"> #}{{ p.kod_v_rocniku }}{# </a> #}</th> | ||||||
| 
 | 
 | ||||||
|                     {# TODELETE #} |                     {# TODELETE #} | ||||||
|                     {% for podproblemy in vysledkovka.podproblemy_iter.next %} |                     {% for podproblemy in vysledkovka.podproblemy_iter.next %} | ||||||
|                         <th class='border-r podproblem{{ oznaceni_vysledkovky }}_{{ forloop.parentloop.counter0 }} podproblem'>{# <a href="{{ podproblemy.verejne_url }}"> #}{{ podproblemy.kod_v_rocniku }}{# </a> #} |                         <th class='border-r podproblem{{ oznaceni_vysledkovky }}_{{ forloop.parentloop.counter0 }} podproblem'>{# <a href="{{ podproblemy.verejne_url }}"> #}{{ podproblemy.kod_v_rocniku }}{# </a> #}</th> | ||||||
|                     {% endfor %} |                     {% endfor %} | ||||||
|                 {# TODELETE #} |                 {# TODELETE #} | ||||||
| 
 | 
 | ||||||
|             {% endfor %} |             {% endfor %} | ||||||
|         {% if vysledkovka.je_nejake_ostatni %}<th class='border-r' id='problem{{ oznaceni_vysledkovky }}_{{ vysledkovka.temata_a_spol| length }}'>Ostatní {% endif %} |         {% if vysledkovka.je_nejake_ostatni %}<th class='border-r' id='problem{{ oznaceni_vysledkovky }}_{{ vysledkovka.temata_a_spol| length }}'>Ostatní</th>{% endif %} | ||||||
| 
 | 
 | ||||||
|         {# TODELETE #} |         {# TODELETE #} | ||||||
|         {% for podproblemy in vysledkovka.podproblemy_iter.next %} |         {% for podproblemy in vysledkovka.podproblemy_iter.next %} | ||||||
|             <th class='border-r podproblem{{ oznaceni_vysledkovky }}_{{ vysledkovka.temata_a_spol| length }} podproblem'>{# <a href="{{ podproblemy.verejne_url }}"> #}{{ podproblemy.kod_v_rocniku }}{# </a> #} |             <th class='border-r podproblem{{ oznaceni_vysledkovky }}_{{ vysledkovka.temata_a_spol| length }} podproblem'>{# <a href="{{ podproblemy.verejne_url }}"> #}{{ podproblemy.kod_v_rocniku }}{# </a> #}</th> | ||||||
|         {% endfor %} |         {% endfor %} | ||||||
|         {# TODELETE #} |         {# TODELETE #} | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         <th class='border-r'>Za číslo |         <th class='border-r'>Za číslo</th> | ||||||
|         <th class='border-r'>Za ročník |         <th class='border-r'>Za ročník</th> | ||||||
|         <th class='border-r'>Odjakživa |         <th class='border-r'>Odjakživa</th> | ||||||
|             {% for rv in vysledkovka.radky_vysledkovky %} |             {% for rv in vysledkovka.radky_vysledkovky %} | ||||||
|                 <tr> |                 <tr> | ||||||
|                     <td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %} |                     <td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %}</td> | ||||||
|                     <th class='border-r'> |                     <th class='border-r'> | ||||||
|                         {% if rv.titul %} |                         {% if rv.titul %} | ||||||
|                             {{ rv.titul }}<sup>MM</sup> |                             {{ rv.titul }}<sup>MM</sup> | ||||||
|                         {% endif %} |                         {% endif %} | ||||||
|                         {{ rv.resitel.osoba.plne_jmeno }} |                         {{ rv.resitel.osoba.plne_jmeno }}</th> | ||||||
|                         {% for b in rv.body_za_temata_seznam %} |                         {% for b in rv.body_za_temata_seznam %} | ||||||
|                             <td class='border-r'>{{ b }} |                             <td class='border-r'>{{ b }}</td> | ||||||
| 
 | 
 | ||||||
|                                 {% for body_podproblemu in rv.body_podproblemy_iter.next %} |                                 {% for body_podproblemu in rv.body_podproblemy_iter.next %} | ||||||
|                                     <td class='border-r podproblem{{ oznaceni_vysledkovky }}_{{ forloop.parentloop.counter0 }} podproblem'>{{ body_podproblemu }} |                                     <td class='border-r podproblem{{ oznaceni_vysledkovky }}_{{ forloop.parentloop.counter0 }} podproblem'>{{ body_podproblemu }}</td> | ||||||
|                                 {% endfor %} |                                 {% endfor %} | ||||||
| 
 | 
 | ||||||
|                         {% endfor %} |                         {% endfor %} | ||||||
|                     <td class='border-r'>{{ rv.body_cislo }} |                     <td class='border-r'>{{ rv.body_cislo }}</td> | ||||||
|                     <td class='border-r'><b>{{ rv.body_rocnik }}</b> |                     <td class='border-r'><b>{{ rv.body_rocnik }}</b></td> | ||||||
|                     <td class='border-r'>{{ rv.body_celkem_odjakziva }} |                     <td class='border-r'>{{ rv.body_celkem_odjakziva }}</td> | ||||||
|                 </tr> |                 </tr> | ||||||
|             {% endfor %} |             {% endfor %} | ||||||
| </table> | </table> | ||||||
|  |  | ||||||
|  | @ -1,29 +1,29 @@ | ||||||
| <table class='vysledkovka'> | <table class='vysledkovka'> | ||||||
|   <tr class='border-b'> |   <tr class='border-b'> | ||||||
|     <th class='border-r'># |     <th class='border-r'>#</th> | ||||||
|     <th class='border-r'>Jméno |     <th class='border-r'>Jméno</th> | ||||||
|     <th class='border-r'>R. |     <th class='border-r'>R.</th> | ||||||
|     <th class='border-r'>Odjakživa |     <th class='border-r'>Odjakživa</th> | ||||||
|         {% for c in vysledkovka.cisla_rocniku %} |         {% for c in vysledkovka.cisla_rocniku %} | ||||||
|     <th class='border-r'><a href="{{ c.verejne_url }}"> |     <th class='border-r'><a href="{{ c.verejne_url }}"> | ||||||
|             {{c.rocnik.rocnik}}.{{ c.poradi }}</a> |         {{c.rocnik.rocnik}}.{{ c.poradi }}</a></th> | ||||||
|     {% endfor %} |     {% endfor %} | ||||||
|     <th class='border-r'>Celkem |     <th class='border-r'>Celkem</th> | ||||||
| 
 | 
 | ||||||
| {% for rv in vysledkovka.radky_vysledkovky %} | {% for rv in vysledkovka.radky_vysledkovky %} | ||||||
|   <tr> |   <tr> | ||||||
|     <td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %} |     <td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %}</td> | ||||||
|     <th class='border-r'> |     <th class='border-r'> | ||||||
|       {% if rv.titul %} |       {% if rv.titul %} | ||||||
|         {{ rv.titul }}<sup>MM</sup> |         {{ rv.titul }}<sup>MM</sup> | ||||||
|       {% endif %} |       {% endif %} | ||||||
|       {{ rv.resitel.osoba.plne_jmeno }} |       {{ rv.resitel.osoba.plne_jmeno }}</th> | ||||||
|     <td class='border-r'>{{ rv.rocnik_resitele }} |     <td class='border-r'>{{ rv.rocnik_resitele }}</td> | ||||||
|     <td class='border-r'>{{ rv.body_celkem_odjakziva }} |     <td class='border-r'>{{ rv.body_celkem_odjakziva }}</td> | ||||||
|     {% for b in rv.body_cisla_seznam %} |     {% for b in rv.body_cisla_seznam %} | ||||||
|     <td class='border-r'>{{ b }} |         <td class='border-r'>{{ b }}</td> | ||||||
|     {% endfor %} |     {% endfor %} | ||||||
|     <td class='border-r'><b>{{ rv.body_rocnik }}</b> |     <td class='border-r'><b>{{ rv.body_rocnik }}</b></td> | ||||||
|   </tr> |   </tr> | ||||||
| {% endfor %} | {% endfor %} | ||||||
| </table> | </table> | ||||||
|  |  | ||||||
|  | @ -146,7 +146,10 @@ class VysledkovkaRocniku(Vysledkovka): | ||||||
| 	def __init__(self, rocnik: m.Rocnik, jen_verejne: bool = True): | 	def __init__(self, rocnik: m.Rocnik, jen_verejne: bool = True): | ||||||
| 		self.rocnik = rocnik | 		self.rocnik = rocnik | ||||||
| 		self.jen_verejne = jen_verejne | 		self.jen_verejne = jen_verejne | ||||||
| 		self.do_deadlinu = m.Deadline.objects.filter(cislo__rocnik=rocnik).last() | 		deadliny = m.Deadline.objects.filter(cislo__rocnik=rocnik) | ||||||
|  | 		if jen_verejne: | ||||||
|  | 			deadliny = deadliny.filter(verejna_vysledkovka=True) | ||||||
|  | 		self.do_deadlinu = deadliny.order_by("deadline").last() | ||||||
| 
 | 
 | ||||||
| 	@cached_property | 	@cached_property | ||||||
| 	def aktivni_resitele(self) -> list[m.Resitel]: | 	def aktivni_resitele(self) -> list[m.Resitel]: | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue