Merge branch 'master' into split-apps
Aby se to snáz mergeovalo
This commit is contained in:
		
						commit
						5070a4d914
					
				
					 26 changed files with 435 additions and 115 deletions
				
			
		|  | @ -87,7 +87,6 @@ TEMPLATES = [ | |||
| 				'django.contrib.auth.context_processors.auth', | ||||
| 		'django.template.context_processors.request', | ||||
| 				'django.contrib.messages.context_processors.messages', | ||||
| 				'sekizai.context_processors.sekizai', | ||||
| 				'header_fotky.context_processors.vzhled', | ||||
| 				'various.context_processors.rozliseni', | ||||
| 				'various.context_processors.april', | ||||
|  | @ -110,7 +109,6 @@ INSTALLED_APPS = ( | |||
| 	'django.contrib.auth', | ||||
| 
 | ||||
| 	# Utilities | ||||
| 	'sekizai', | ||||
| 	'reversion', | ||||
| 	'django_countries', | ||||
| 	'solo', | ||||
|  | @ -120,9 +118,6 @@ INSTALLED_APPS = ( | |||
| 	'dal', | ||||
| 	'dal_select2', | ||||
| 
 | ||||
| 	'crispy_forms', | ||||
| 	'django_comments', | ||||
| 
 | ||||
| 	'django.contrib.flatpages', | ||||
| 	'django.contrib.humanize', | ||||
| 
 | ||||
|  |  | |||
|  | @ -39,7 +39,7 @@ div.login-bar { | |||
| 	position: fixed; | ||||
| 	margin-top: -20px; | ||||
| 	min-height: 20px; | ||||
| 	z-index: 20; | ||||
| 	z-index: 4086; | ||||
| 
 | ||||
| 	padding-left: 5px; | ||||
| 	padding-right: 5px; | ||||
|  | @ -199,22 +199,13 @@ h1 { | |||
| 	margin-top: 0px; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /* Comments */ | ||||
| 
 | ||||
| #id_comment { | ||||
|   width: 100%; | ||||
|   height: 6em; | ||||
| } | ||||
| 
 | ||||
| /* Headline & Header */ | ||||
| 
 | ||||
| #title { /*dělá blbosti šířka, je to kvůli fixed pozici, zatím natvrdo, vyřešit*/ | ||||
| 	height: 55px; | ||||
| 	width: 970px; | ||||
| 	position: fixed; | ||||
| 	z-index: 10; | ||||
| 	z-index: 2048; | ||||
| 	background-color: #e84e10; | ||||
| 	filter: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.4)); | ||||
| 
 | ||||
|  | @ -1252,6 +1243,35 @@ div.gdpr { | |||
| 	background: rgb(253, 237, 213); | ||||
| } | ||||
| 
 | ||||
| /*Přichycování prvního sloupce a řádku*/ | ||||
| .dosla_reseni { | ||||
| 	display: block; | ||||
| 	max-height: 90vh; | ||||
| 	max-width: 90vw; | ||||
| 	overflow: auto; | ||||
| 	margin-left: 5vw; | ||||
| } | ||||
| 
 | ||||
| .dosla_reseni thead tr { | ||||
| 	position: sticky; | ||||
| 	top: 0; | ||||
| 	z-index: 1; | ||||
| } | ||||
| 
 | ||||
| .dosla_reseni tr:nth-child(even) td:first-child, .dosla_reseni thead tr, .dosla_reseni thead tr:first-child td:first-child { | ||||
| 	background: rgb(253, 237, 213); | ||||
| } | ||||
| 
 | ||||
| .dosla_reseni tr:nth-child(odd) td:first-child { | ||||
| 	background: #fffbf6; | ||||
| } | ||||
| 
 | ||||
| .dosla_reseni tr td:first-child { | ||||
| 	position: sticky; | ||||
| 	left: 0; | ||||
| } | ||||
| /*                                    */ | ||||
| 
 | ||||
| 
 | ||||
| .odevzdana_reseni tr th, .odevzdana_reseni tr td { | ||||
| 	border: 1px solid black; | ||||
|  |  | |||
|  | @ -1,29 +1,35 @@ | |||
| /* Rozlišení mezi lokálním, test a produkčním webem */ | ||||
| 
 | ||||
| .localweb { | ||||
|     border-left: 20px solid greenyellow; | ||||
|     border-right: 20px solid greenyellow; | ||||
| body.localweb:before, body.localweb:after, | ||||
| body.testweb:before, body.testweb:after, | ||||
| body.suprodweb:before, body.suprodweb:after { | ||||
| 	content: ""; | ||||
| 	position: fixed; | ||||
| 	width: 20px; | ||||
| 	height: 100%; | ||||
| 	top: 0; | ||||
| } | ||||
| 
 | ||||
| .localweb .login-bar { | ||||
|     margin-left: -20px; | ||||
| body.localweb:before, | ||||
| body.testweb:before, | ||||
| body.suprodweb:before { | ||||
| 	left: 0; | ||||
| } | ||||
| 
 | ||||
| .testweb { | ||||
|     border-left: 20px solid darkorange; | ||||
|     border-right: 20px solid darkorange; | ||||
| body.localweb:after, | ||||
| body.testweb:after, | ||||
| body.suprodweb:after { | ||||
| 	right: 0; | ||||
| } | ||||
| 
 | ||||
| .testweb .login-bar { | ||||
|     margin-left: -20px; | ||||
| body.localweb:before, body.localweb:after { | ||||
| 	background: greenyellow; | ||||
| } | ||||
| 
 | ||||
| /* Produkční web z pohledu superuživatele */ | ||||
| .suprodweb { | ||||
|     border-left: 20px solid red; | ||||
|     border-right: 20px solid red; | ||||
| body.testweb:before, body.testweb:after { | ||||
| 	background: darkorange; | ||||
| } | ||||
| 
 | ||||
| .suprodweb .login-bar { | ||||
|     margin-left: -20px; | ||||
| body.suprodweb:before, body.suprodweb:after { | ||||
| 	background: red; | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| {% load static sekizai_tags %} | ||||
| {% load static %} | ||||
| {% load sitetree %} | ||||
| <!DOCTYPE html> | ||||
| <html lang='cs'> | ||||
|  | @ -7,7 +7,6 @@ | |||
|     <title>{% block title %}{% block nadpis1a %}🦊{% endblock %} | Korespondenční seminář M&M{% endblock title %}</title> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|     <link rel="shortcut icon" href="{% static 'images/MATFYZ_MM_barevne.svg' %}" type="image/x-icon"> | ||||
| {#    {% render_block css %}#} | ||||
|     {% block custom_css %}{% endblock %} | ||||
|     <link href="{% static 'css/bootstrap-theme.css' %}" rel="stylesheet"> | ||||
|     <link href="{% static 'css/bootstrap.css' %}" rel="stylesheet"> | ||||
|  | @ -189,6 +188,24 @@ | |||
|       walkText(document.body); | ||||
|     </script> | ||||
|   {% endif %} | ||||
| 	{% render_block "js" %} | ||||
| 
 | ||||
|   {% if april == 2024 %} | ||||
|     <script> | ||||
|       {#  By https://stackoverflow.com/a/34559316 #} | ||||
|       function walkText(node) { | ||||
|         if (node.nodeType == 3) { | ||||
|           node.data = node.data.replace(/M&M/g, "W8W"); | ||||
|         } | ||||
|         if (node.nodeType == 1 && node.nodeName != "SCRIPT") { | ||||
|           for (var i = 0; i < node.childNodes.length; i++) { | ||||
|             walkText(node.childNodes[i]); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       walkText(document.body); | ||||
|     </script> | ||||
|   {% endif %} | ||||
|      | ||||
| 	{% block js %}{% endblock %} | ||||
|   </body> | ||||
| </html> | ||||
|  |  | |||
|  | @ -13,7 +13,6 @@ Soubor sloužící jako základní „router“, tj. zde se includují veškeré | |||
| - :mod:`api.urls` | ||||
| - :mod:`treenode.urls` | ||||
| - :mod:`aesop.urls` | ||||
| - ``comments_dj/`` :mod:`django_comments.urls` | ||||
| """ | ||||
| from django.urls import path, include | ||||
| from django.contrib.staticfiles.urls import staticfiles_urlpatterns | ||||
|  | @ -62,9 +61,6 @@ urlpatterns = [ | |||
| 	# Aesop (ma vlastni podadresare) | ||||
| 	path('', include('aesop.urls')), | ||||
| 
 | ||||
| 	# Comments (interni i verejne) | ||||
| 	path('comments_dj/', include('django_comments.urls')), | ||||
| 
 | ||||
| 	# REST API | ||||
| #	path('api/', include(router.urls)), | ||||
| 
 | ||||
|  |  | |||
|  | @ -21,8 +21,8 @@ Do data (včetně): {{ filtr.reseni_do }} | |||
| 	<input type=submit value="Změň ročník"> | ||||
| </form> | ||||
| 
 | ||||
| <div style="overflow-x: scroll;"> | ||||
| <table class="dosla_reseni"> | ||||
|   <thead> | ||||
| 	<tr> | ||||
| 		<td></td> {# Prázdná buňka v levém horním rohu #} | ||||
| 		{% for p in problemy %} | ||||
|  | @ -32,6 +32,8 @@ Do data (včetně): {{ filtr.reseni_do }} | |||
| 		</th> | ||||
| 		{% endfor %} | ||||
| 	</tr> | ||||
|   </thead> | ||||
|   <tbody> | ||||
| 	{% for resitel,hodnoty in radky%} | ||||
| 	<tr> | ||||
| 		<td> | ||||
|  | @ -52,8 +54,8 @@ Do data (včetně): {{ filtr.reseni_do }} | |||
| 		{% endfor %} | ||||
| 	</tr> | ||||
| 	{% endfor %} | ||||
|   </tbody> | ||||
| </table> | ||||
| </div> | ||||
| 
 | ||||
| {% endblock %} | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,43 +1,32 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| -c constraints.txt | ||||
| # basic libs | ||||
| 
 | ||||
| psycopg2 | ||||
| html5lib | ||||
| ipython | ||||
| psycopg2 # PostgreSQL adaptér | ||||
| ipython # Interaktivní shell | ||||
| Unidecode # Přepisuje unicode do ASCII (např. soubory nebo e-maily) | ||||
| Pillow | ||||
| pilkit>=3.0 # Kvůli kompatibilitě s Pillow>=10.0.0 | ||||
| pytz | ||||
| six | ||||
| pexpect | ||||
| traitlets | ||||
| Unidecode | ||||
| 
 | ||||
| # Django and modules | ||||
| 
 | ||||
| Django<5.0 | ||||
| #django-bootstrap-sass | ||||
| django-mptt | ||||
| django-reversion | ||||
| django-sekizai | ||||
| django-countries | ||||
| django-solo | ||||
| django-ckeditor | ||||
| django-reversion # Version control na datech v databázi | ||||
| django-countries # Políčko ve formu / field v modelu ohledně států | ||||
| django-solo # Singleton model (speciálně Nastavení) | ||||
| django-ckeditor # Editor htmlka (hlavně v adminu u flatpages) | ||||
| django-cleanup  # Uklízí media/ od smazaných „databázových“ souborů | ||||
| django-flat-theme | ||||
| django-taggit | ||||
| django-autocomplete-light>=3.9.0 | ||||
| django-crispy-forms | ||||
| django-imagekit | ||||
| django-polymorphic | ||||
| django-sitetree | ||||
| django_reverse_admin | ||||
| django-taggit # Taggy v djangu (speciálně zaměření problémů) | ||||
| django-autocomplete-light>=3.9.0 # Automatické doplňování (problémů, účastníků, …) ve formulářích | ||||
| django-imagekit # Všechny možné obrázky v Djangu | ||||
| django-polymorphic # Polymorfismus na django modelech (hlavně Problém nebo treenode) | ||||
| django-sitetree # Struktura stránek, hlavně pro meníčko | ||||
| django_reverse_admin # Lepší handlování OneToOne fieldů v adminu | ||||
| django-rest-framework | ||||
| django-webpack-loader | ||||
| django-rest-polymorphic | ||||
| 
 | ||||
| # Comments | ||||
| django-contrib-comments | ||||
| 
 | ||||
| # debug tools/extensions | ||||
| 
 | ||||
| django-debug-toolbar | ||||
|  |  | |||
|  | @ -38,9 +38,11 @@ | |||
|         <h2> Orgovské odkazy </h2> | ||||
|         <ul> | ||||
|           <li><a href="obalky.pdf">Obálky (PDF)</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="tituly.tex" download>Tituly (TeX, 2. deadline předchozího čísla a 1.deadline tohoto)</a></li> | ||||
|           <li><a href="vysledkovka.tex" download>Výsledkovka (TeX, 2. deadline předchozího čísla a 1.deadline tohoto)</a></li> | ||||
|           <li><a href="odmeny/{{prevcislo.rocnik.rocnik}}.{{prevcislo.poradi}}/">Odměny</a></li> | ||||
|           <li><a href="{% url "seminar_rocnik_titul" rocnik=cislo.rocnik.rocnik %}" download="posledni_tituly.tex">Tituly do závěrečného čísla (TeX, 2. deadline předchozího čísla a oba tohoto)</a></li> | ||||
|           <li><a href="{% url "seminar_rocnik_posledni_vysledkovka" rocnik=cislo.rocnik.rocnik %}" download>Výsledkovka závěrečného čísla ročníku (TeX, 2. deadline předchozího čísla a oba tohoto)</a></li> | ||||
|         </ul> | ||||
|       </div> | ||||
|   {% endif %} | ||||
|  |  | |||
|  | @ -1,7 +1,5 @@ | |||
| {% extends "base.html" %} | ||||
| 
 | ||||
| {% load comments %} | ||||
| 
 | ||||
| {% block content %} | ||||
| <div {% if not problem.verejne and user.je_org %}class="mam-org-only"{% endif %}> | ||||
|   {% block problem %} | ||||
|  | @ -13,10 +11,6 @@ | |||
|     <h2>Text - org</h2> | ||||
|     {{ problem.text_org |safe }} | ||||
| 
 | ||||
|     <h2>Diskuse - org</h2> | ||||
|     {% render_comment_list for object %} | ||||
|     {% render_comment_form for object %} | ||||
| 
 | ||||
|   </div> | ||||
|   {% endif %} | ||||
| 
 | ||||
|  |  | |||
|  | @ -120,8 +120,7 @@ | |||
|   {% if user.je_org %} | ||||
|     <div class='mam-org-only'> | ||||
|     <p><a href='vysledkovka.tex' download>Výsledkovka ročníku (LaTeX, včetně neveřejných)</a></p> | ||||
|     <p><a href="tituly.tex" download>Tituly (TeX, do konce ročníku = pro poslední číslo)</a></p> | ||||
|     <p><a href="posledni_vysledkovka.tex" download>Výsledkovka posledního čísla</a></p> | ||||
|     <p><a href="tituly.tex" download>Tituly (TeX, včetně neveřejných, všechny, nevhodné do mamtexu)</a></p> | ||||
|     {# FIXME: Sice to sem asi nepatří sémanticky, ale bylo to nejjednodušší… #} | ||||
|     <p><a href='{% url 'seminar_rocnik_resitele_csv' rocnik=rocnik.rocnik %}' download>CSV export řešitelů</a></p> | ||||
|     <h2>Výsledková listina včetně neveřejných bodů</h2> | ||||
|  |  | |||
|  | @ -1,7 +1,6 @@ | |||
| from django import template | ||||
| from django.utils.safestring import mark_safe | ||||
| from datetime import datetime, timedelta | ||||
| from pytz import timezone | ||||
| from mamweb.settings import TIME_ZONE | ||||
| import logging | ||||
| register = template.Library() | ||||
|  |  | |||
|  | @ -4,7 +4,6 @@ import datetime | |||
| 
 | ||||
| from django.contrib.auth.models import Permission | ||||
| from django.contrib.auth.models import Group | ||||
| from pytz import timezone | ||||
| import random | ||||
| import lorem | ||||
| import django.contrib.auth | ||||
|  | @ -177,13 +176,13 @@ def gen_organizatori(rnd, osoby, last_rocnik): | |||
| 				year=1993 + pusobnost, | ||||
| 				month=rnd.randint(1, 12), | ||||
| 				day=rnd.randint(1, 28), | ||||
| 				tzinfo=timezone('CET'), | ||||
| 				tzinfo=datetime.timezone.utc, | ||||
| 				) | ||||
| 			do = datetime.datetime( | ||||
| 				year=od.year + rnd.randint(1, 6), | ||||
| 				month=rnd.randint(1, 12), | ||||
| 				day=rnd.randint(1, 28), | ||||
| 				tzinfo=timezone('CET'), | ||||
| 				tzinfo=datetime.timezone.utc, | ||||
| 				) | ||||
| 			#aktualni organizatori jeste nemaji vyplnene organizuje_do | ||||
| 
 | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ from django.http import Http404 | |||
| from django.db.models import Q, Sum, Count | ||||
| from django.views.generic.base import RedirectView | ||||
| from django.core.exceptions import PermissionDenied | ||||
| from django.contrib.staticfiles.finders import find | ||||
| 
 | ||||
| import seminar.models as s | ||||
| import seminar.models as m | ||||
|  | @ -36,6 +37,7 @@ import unicodedata | |||
| import logging | ||||
| import time | ||||
| from collections.abc import Sequence | ||||
| import http | ||||
| 
 | ||||
| from seminar.utils import aktivniResitele | ||||
| 
 | ||||
|  | @ -562,17 +564,25 @@ def cisloObalkyView(request, rocnik, cislo): | |||
| 
 | ||||
| 
 | ||||
| def obalkyView(request, resitele): | ||||
| 	if len(resitele) == 0: | ||||
| 		return HttpResponse( | ||||
| 			render(request, 'universal.html', { | ||||
| 				'title': 'Není pro koho vyrobit obálky.', | ||||
| 				'text': 'Právě ses pokusil/a vygenerovat obálky pro prázdnou množinu lidí. Můžeš to zkusit změnit, případně se zeptej webařů :-)', | ||||
| 				}), | ||||
| 			status=http.HTTPStatus.NOT_FOUND, | ||||
| 			) | ||||
| 
 | ||||
| 	tex = render(request,'seminar/archiv/obalky.tex', {'resitele': resitele}).content | ||||
| 
 | ||||
| 	tempdir = tempfile.mkdtemp() | ||||
| 	with open(tempdir+"/obalky.tex","w") as texfile: | ||||
| 		texfile.write(tex.decode()) | ||||
| 	shutil.copy(os.path.join(settings.STATIC_ROOT, 'seminar/lisak.pdf'), tempdir) | ||||
| 	subprocess.call(["pdflatex","obalky.tex"], cwd = tempdir) | ||||
| 	with tempfile.TemporaryDirectory() as tempdir: | ||||
| 		with open(tempdir+"/obalky.tex","w") as texfile: | ||||
| 			texfile.write(tex.decode()) | ||||
| 		shutil.copy(find('seminar/lisak.pdf'), tempdir) | ||||
| 		subprocess.call(["pdflatex","obalky.tex"], cwd = tempdir) | ||||
| 
 | ||||
| 	with open(tempdir+"/obalky.pdf","rb") as pdffile: | ||||
| 		response = HttpResponse(pdffile.read(), content_type='application/pdf') | ||||
| 	shutil.rmtree(tempdir) | ||||
| 		with open(tempdir+"/obalky.pdf","rb") as pdffile: | ||||
| 			response = HttpResponse(pdffile.read(), content_type='application/pdf') | ||||
| 	return response | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,8 +1,10 @@ | |||
| from django.contrib import admin | ||||
| 
 | ||||
| from .models import OdpovedUcastnika, SpravnaOdpoved | ||||
| from .models import OdpovedUcastnika, SpravnaOdpoved, NapovezenoUcastnikovi, Napoveda | ||||
| 
 | ||||
| # Register your models here. | ||||
| 
 | ||||
| admin.site.register(OdpovedUcastnika) | ||||
| admin.site.register(SpravnaOdpoved) | ||||
| admin.site.register(Napoveda) | ||||
| admin.site.register(NapovezenoUcastnikovi) | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| from django.core.exceptions import ValidationError | ||||
| from django.forms import ModelForm, Textarea | ||||
| from .models import OdpovedUcastnika, SpravnaOdpoved | ||||
| from .models import OdpovedUcastnika, SpravnaOdpoved, NapovezenoUcastnikovi, Napoveda | ||||
| 
 | ||||
| 
 | ||||
| class SifrovackaForm(ModelForm): | ||||
|  | @ -16,3 +16,15 @@ class SifrovackaForm(ModelForm): | |||
| 		if SpravnaOdpoved.objects.filter(sifra=sifra).count() == 0: | ||||
| 			raise ValidationError("Tohle číslo šifry v databázi nemáme. Zkontrolujte si ho prosím.") | ||||
| 		return sifra | ||||
| 
 | ||||
| 
 | ||||
| class NapovedaForm(ModelForm): | ||||
| 	class Meta: | ||||
| 		model = NapovezenoUcastnikovi | ||||
| 		fields = ["sifra",] | ||||
| 
 | ||||
| 	def clean_sifra(self): | ||||
| 		sifra = self.cleaned_data.get('sifra') | ||||
| 		if Napoveda.objects.filter(sifra=sifra).count() == 0: | ||||
| 			raise ValidationError("K tomuto číslu šifry nemáme nápovědu. Zkontrolujte si ho prosím.") | ||||
| 		return sifra | ||||
|  |  | |||
							
								
								
									
										65
									
								
								sifrovacka/migrations/0004_napoveda_napovezenoucastnikovi.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								sifrovacka/migrations/0004_napoveda_napovezenoucastnikovi.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,65 @@ | |||
| # Generated by Django 4.2.8 on 2024-04-14 12:57 | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| import django.db.models.deletion | ||||
| import django.utils.timezone | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
|     dependencies = [ | ||||
|         ( | ||||
|             "seminar", | ||||
|             "0114_related_name_se_zmenilo_a_django_chce_migraci_tak_dostane_migraci", | ||||
|         ), | ||||
|         ("sifrovacka", "0003_odpoveducastnika_uspech"), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.CreateModel( | ||||
|             name="Napoveda", | ||||
|             fields=[ | ||||
|                 ( | ||||
|                     "id", | ||||
|                     models.AutoField( | ||||
|                         auto_created=True, | ||||
|                         primary_key=True, | ||||
|                         serialize=False, | ||||
|                         verbose_name="ID", | ||||
|                     ), | ||||
|                 ), | ||||
|                 ("text", models.TextField()), | ||||
|                 ("sifra", models.IntegerField()), | ||||
|             ], | ||||
|         ), | ||||
|         migrations.CreateModel( | ||||
|             name="NapovezenoUcastnikovi", | ||||
|             fields=[ | ||||
|                 ( | ||||
|                     "id", | ||||
|                     models.AutoField( | ||||
|                         auto_created=True, | ||||
|                         primary_key=True, | ||||
|                         serialize=False, | ||||
|                         verbose_name="ID", | ||||
|                     ), | ||||
|                 ), | ||||
|                 ("sifra", models.IntegerField(verbose_name="Číslo šifry")), | ||||
|                 ( | ||||
|                     "timestamp", | ||||
|                     models.DateTimeField( | ||||
|                         default=django.utils.timezone.now, verbose_name="Timestamp" | ||||
|                     ), | ||||
|                 ), | ||||
|                 ( | ||||
|                     "resitel", | ||||
|                     models.ForeignKey( | ||||
|                         on_delete=django.db.models.deletion.CASCADE, | ||||
|                         to="seminar.resitel", | ||||
|                     ), | ||||
|                 ), | ||||
|             ], | ||||
|             options={ | ||||
|                 "ordering": ["-timestamp"], | ||||
|             }, | ||||
|         ), | ||||
|     ] | ||||
|  | @ -25,3 +25,20 @@ class SpravnaOdpoved(models.Model): | |||
| 
 | ||||
| 	def __str__(self): | ||||
| 		return f"{self.sifra}: {self.odpoved}" | ||||
| 
 | ||||
| 
 | ||||
| class NapovezenoUcastnikovi(models.Model): | ||||
| 	class Meta: | ||||
| 		ordering = ["-timestamp"] | ||||
| 
 | ||||
| 	resitel = models.ForeignKey(Resitel, blank=False, null=False, on_delete=models.CASCADE) | ||||
| 	sifra = models.IntegerField("Číslo šifry", blank=False, null=False,) | ||||
| 	timestamp = models.DateTimeField("Timestamp", blank=False, null=False, default=timezone.now) | ||||
| 
 | ||||
| 
 | ||||
| class Napoveda(models.Model): | ||||
| 	text = models.TextField(blank=False, null=False,) | ||||
| 	sifra = models.IntegerField(blank=False, null=False,) | ||||
| 
 | ||||
| 	def __str__(self): | ||||
| 		return f"{self.sifra}: {self.text}" | ||||
|  |  | |||
							
								
								
									
										50
									
								
								sifrovacka/templates/sifrovacka/napoveda.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								sifrovacka/templates/sifrovacka/napoveda.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,50 @@ | |||
| {% extends "base.html" %} | ||||
| 
 | ||||
| {% block content %} | ||||
| 
 | ||||
|   <br> | ||||
| 
 | ||||
|   <h1>{% block nadpis1a %}M&Mí šifrovačka{% endblock nadpis1a %}</h1> | ||||
| 
 | ||||
|   <br> | ||||
| 
 | ||||
|   <h2>Získat nápovědu k šifře:</h2> | ||||
| 
 | ||||
|   <form action="{% url 'sifrovacka_napoveda' %}" method="post"> | ||||
|     <table class="form"> | ||||
|       {{form.non_field_errors}} | ||||
|       {% for field in form %} | ||||
|         <tr> | ||||
|           <td> | ||||
|             <label class="field-label{% if field.field.required %} field-required{% endif %}" for="{{ field.id_for_label }}"> | ||||
|               {{ field.label }} | ||||
|             </label> | ||||
| 
 | ||||
|           </td> | ||||
| 
 | ||||
|           <td {% if field.help_text %} class="field-with-comment"{% endif %}> | ||||
|             {{ field }} | ||||
|             <span class="field-comment">{{ field.help_text|safe }}</span> | ||||
|           </td> | ||||
| 
 | ||||
|         </tr> | ||||
| 
 | ||||
| 
 | ||||
|         {% if field.errors %} | ||||
|           <tr> | ||||
|             <td colspan="2"><span class="field-error">{{ field.errors }}</span></td> | ||||
|           </tr> | ||||
|         {% endif %} | ||||
|       {% endfor %} | ||||
|     </table> | ||||
| 
 | ||||
|     {% csrf_token %} | ||||
| 
 | ||||
|     <input type="submit" value="Chci nápovědu"> | ||||
|   </form> | ||||
| 
 | ||||
|   <p><a href="{% url 'sifrovacka' %}">Nechceme nápovědu, známe řešení!</a></p> | ||||
| 
 | ||||
|   <p><a href="{% url 'sifrovacka_preskoceni' %}">Přeskoč šifru</a></p> | ||||
| 
 | ||||
| {% endblock content %} | ||||
							
								
								
									
										23
									
								
								sifrovacka/templates/sifrovacka/napovedy_list.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								sifrovacka/templates/sifrovacka/napovedy_list.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | |||
| {% extends "base.html" %} | ||||
| 
 | ||||
| {% block content %} | ||||
| 
 | ||||
|   <h1>{% block nadpis1a %}Šifrovačka vzaté nápovědy{% endblock nadpis1a %}</h1> | ||||
| 
 | ||||
|   <table class="dosla_reseni"> | ||||
|     <tr> | ||||
|       <th>Timestamp</th> | ||||
|       <th>Řešitel</th> | ||||
|       <th>Šifra</th> | ||||
|     </tr> | ||||
| 
 | ||||
|     {% for u in object_list %} | ||||
|       <tr> | ||||
|         <td>{{ u.timestamp }}</td> | ||||
|         <td>{{ u.resitel }}</td> | ||||
|         <td>{{ u.sifra }}</td> | ||||
|       </tr> | ||||
|     {% endfor %} | ||||
|   </table> | ||||
| 
 | ||||
| {% endblock content %} | ||||
							
								
								
									
										54
									
								
								sifrovacka/templates/sifrovacka/preskoceni.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								sifrovacka/templates/sifrovacka/preskoceni.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,54 @@ | |||
| {% extends "base.html" %} | ||||
| 
 | ||||
| {% block content %} | ||||
| 
 | ||||
|   <br> | ||||
| 
 | ||||
|   <h1>{% block nadpis1a %}M&Mí šifrovačka{% endblock nadpis1a %}</h1> | ||||
| 
 | ||||
|   <br> | ||||
| 
 | ||||
|   <h2>Přeskočit šifru:</h2> | ||||
| 
 | ||||
|   <form action="{% url 'sifrovacka_preskoceni' %}" method="post"> | ||||
|     <table class="form"> | ||||
|       {{form.non_field_errors}} | ||||
|       {% for field in form %} | ||||
|         {% if field.id_for_label != "id_odpoved" %} | ||||
|           <tr> | ||||
|             <td> | ||||
|               <label class="field-label{% if field.field.required %} field-required{% endif %}" for="{{ field.id_for_label }}"> | ||||
|                 {{ field.label }} | ||||
|               </label> | ||||
| 
 | ||||
|             </td> | ||||
| 
 | ||||
|             <td {% if field.help_text %} class="field-with-comment"{% endif %}> | ||||
|               {{ field }} | ||||
|               <span class="field-comment">{{ field.help_text|safe }}</span> | ||||
|             </td> | ||||
| 
 | ||||
|           </tr> | ||||
| 
 | ||||
| 
 | ||||
|           {% if field.errors %} | ||||
|             <tr> | ||||
|               <td colspan="2"><span class="field-error">{{ field.errors }}</span></td> | ||||
|             </tr> | ||||
|           {% endif %} | ||||
|         {% else %} | ||||
|           {{ field.as_hidden }} | ||||
|         {% endif %} | ||||
|       {% endfor %} | ||||
|     </table> | ||||
| 
 | ||||
|     {% csrf_token %} | ||||
| 
 | ||||
|     <input type="submit" value="Chceme další stanoviště bez vyřešení šifry"> | ||||
|   </form> | ||||
| 
 | ||||
|   <p><a href="{% url 'sifrovacka' %}">Nechceme přeskočit, známe řešení!</a></p> | ||||
| 
 | ||||
|   <p><a href="{% url 'sifrovacka_napoveda' %}">Nechceme přeskakovat, ale nápověda by se šikla.</a></p> | ||||
| 
 | ||||
| {% endblock content %} | ||||
|  | @ -43,4 +43,8 @@ | |||
|     <input type="submit" value="Tak pravím!"> | ||||
|   </form> | ||||
| 
 | ||||
|   <p><a href="{% url 'sifrovacka_napoveda' %}">Získat nápovědu</a></p> | ||||
| 
 | ||||
|   <p><a href="{% url 'sifrovacka_preskoceni' %}">Přeskoč šifru</a></p> | ||||
| 
 | ||||
| {% endblock content %} | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| from django.urls import path | ||||
| 
 | ||||
| from seminar.utils import org_required, resitel_or_org_required | ||||
| from .views import SifrovackaView, SifrovackaListView | ||||
| from .views import SifrovackaView, SifrovackaListView, NapovedaView, NapovedaListView, PreskoceniView | ||||
| 
 | ||||
| urlpatterns = [ | ||||
| 	path( | ||||
|  | @ -14,4 +14,19 @@ urlpatterns = [ | |||
| 		org_required(SifrovackaListView.as_view()), | ||||
| 		name='sifrovacka_odpovedi' | ||||
| 	), | ||||
| 	path( | ||||
| 		'napoveda/', | ||||
| 		resitel_or_org_required(NapovedaView.as_view()), | ||||
| 		name='sifrovacka_napoveda' | ||||
| 	), | ||||
| 	path( | ||||
| 		'napovedy/', | ||||
| 		org_required(NapovedaListView.as_view()), | ||||
| 		name='sifrovacka_napovedy' | ||||
| 	), | ||||
| 	path( | ||||
| 		'preskoceni/', | ||||
| 		resitel_or_org_required(PreskoceniView.as_view()), | ||||
| 		name='sifrovacka_preskoceni' | ||||
| 	), | ||||
| ] | ||||
|  |  | |||
|  | @ -2,8 +2,8 @@ from django.urls import reverse | |||
| from django.views.generic import FormView, ListView | ||||
| 
 | ||||
| from seminar.views import formularOKView | ||||
| from .forms import SifrovackaForm | ||||
| from .models import OdpovedUcastnika, SpravnaOdpoved | ||||
| from .forms import SifrovackaForm, NapovedaForm | ||||
| from .models import OdpovedUcastnika, SpravnaOdpoved, Napoveda, NapovezenoUcastnikovi | ||||
| from seminar.models import Resitel | ||||
| 
 | ||||
| 
 | ||||
|  | @ -24,10 +24,47 @@ class SifrovackaView(FormView): | |||
| 
 | ||||
| 		instance.uspech = True | ||||
| 		instance.save() | ||||
| 		 | ||||
| 
 | ||||
| 		return formularOKView(self.request, f'<h1>{sifra.skryty_text}</h1> <p><a href="{reverse("sifrovacka")}">Odevzdat další.</a></p><br><br><br>') | ||||
| 
 | ||||
| 
 | ||||
| class SifrovackaListView(ListView): | ||||
| 	template_name = 'sifrovacka/odpovedi_list.html' | ||||
| 	model = OdpovedUcastnika | ||||
| 
 | ||||
| 
 | ||||
| class NapovedaView(FormView): | ||||
| 	template_name = 'sifrovacka/napoveda.html' | ||||
| 	form_class = NapovedaForm | ||||
| 
 | ||||
| 	def form_valid(self, form): | ||||
| 		instance = form.save(commit=False) | ||||
| 		resitel = Resitel.objects.get(osoba__user=self.request.user) | ||||
| 		instance.resitel = resitel | ||||
| 
 | ||||
| 		if NapovezenoUcastnikovi.objects.filter(resitel=resitel, sifra=instance.sifra).first() is None: | ||||
| 			instance.save() | ||||
| 
 | ||||
| 		napoveda = Napoveda.objects.filter(sifra=instance.sifra).first() | ||||
| 		return formularOKView(self.request, f'<h1>Nápověda k šifře číslo {instance.sifra} je:</h1><p>{napoveda.text}</p> <p><a href="{reverse("sifrovacka")}">Odevzdat řešení.</a></p><br><br><br>') | ||||
| 
 | ||||
| 
 | ||||
| class NapovedaListView(ListView): | ||||
| 	template_name = 'sifrovacka/napovedy_list.html' | ||||
| 	model = NapovezenoUcastnikovi | ||||
| 
 | ||||
| 
 | ||||
| class PreskoceniView(FormView): | ||||
| 	template_name = 'sifrovacka/preskoceni.html' | ||||
| 	form_class = SifrovackaForm | ||||
| 	initial = {"odpoved": "=======PŘESKOČENO======="} | ||||
| 
 | ||||
| 	def form_valid(self, form): | ||||
| 		instance = form.save(commit=False) | ||||
| 		instance.odpoved = "=======PŘESKOČENO=======" | ||||
| 		resitel = Resitel.objects.get(osoba__user=self.request.user) | ||||
| 		instance.resitel = resitel | ||||
| 		instance.save() | ||||
| 		sifra = SpravnaOdpoved.objects.filter(sifra=instance.sifra).first()  # FIXME co když je více "správných" odpovědí? | ||||
| 
 | ||||
| 		return formularOKView(self.request, f'<h1>{sifra.skryty_text}</h1> <p><a href="{reverse("sifrovacka")}">Zpět na odevzdávátko.</a></p><br><br><br>') | ||||
|  |  | |||
|  | @ -58,16 +58,16 @@ | |||
|         {# Účastníci #} | ||||
|         <h2>Soustředění se zúčastnili tito účastníci:</h2> | ||||
|         <p> | ||||
|         {% for i in soustredeni.soustredeni_ucastnici_set.all %} | ||||
|           {{i.resitel}}{% if forloop.last %}.{% else %},{% endif %} | ||||
|         {% for i in soustredeni.ucastnici.all %} | ||||
|           {{i}}{% if forloop.last %}.{% else %},{% endif %} | ||||
|         {% empty %} | ||||
|           Nic! | ||||
|         {% endfor %} | ||||
|         </p> | ||||
|         <h2>Soustředění se účastnili tito organizátoři:</h2> | ||||
|         <p> | ||||
|         {% for i in soustredeni.soustredeni_organizatori_set.all %} | ||||
|           {{i.organizator}}{% if forloop.last %}.{% else %},{% endif %} | ||||
|         {% for i in soustredeni.organizatori.all %} | ||||
|           {{i}}{% if forloop.last %}.{% else %},{% endif %} | ||||
|         {% empty %} | ||||
|           Nic! | ||||
|         {% endfor %} | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ import tempfile | |||
| import shutil | ||||
| import subprocess | ||||
| from pathlib import Path | ||||
| import http | ||||
| 
 | ||||
| from seminar.views import obalkyView | ||||
| 
 | ||||
|  | @ -17,6 +18,19 @@ class SoustredeniListView(generic.ListView): | |||
| 	model = Soustredeni | ||||
| 	template_name = 'soustredeni/seznam_soustredeni.html' | ||||
| 
 | ||||
| 	def get_queryset(self): | ||||
| 		if not self.request.user.je_org: | ||||
| 			return super().get_queryset() | ||||
| 		return ( | ||||
| 			Soustredeni.objects | ||||
| 			.prefetch_related( | ||||
| 				"ucastnici", "ucastnici__osoba", | ||||
| 				"organizatori", "organizatori__osoba", | ||||
| 				"galerie_set", | ||||
| 			) | ||||
| 			.select_related("rocnik") | ||||
| 		) | ||||
| 
 | ||||
| 
 | ||||
| def soustredeniObalkyView(request, soustredeni): | ||||
| 	soustredeni = get_object_or_404(Soustredeni, id=soustredeni) | ||||
|  | @ -63,17 +77,25 @@ def soustredeniUcastniciExportView(request, soustredeni): | |||
| def soustredeniStvrzenkyView(request, soustredeni): | ||||
| 	soustredeni = get_object_or_404(Soustredeni, id=soustredeni) | ||||
| 	ucastnici = Resitel.objects.filter(soustredeni=soustredeni) | ||||
| 	if ucastnici.count() == 0: | ||||
| 		return HttpResponse( | ||||
| 			render(request, 'universal.html', { | ||||
| 				'title': 'Není pro koho vyrobit stvrzenky.', | ||||
| 				'text': 'Právě ses pokusil/a vygenerovat stvrzenky pro prázdnou množinu lidí. Můžeš to zkusit změnit, případně se zeptej webařů :-)', | ||||
| 				}), | ||||
| 			status=http.HTTPStatus.NOT_FOUND, | ||||
| 			) | ||||
| 	castka = Nastaveni.get_solo().cena_sous | ||||
| 	tex = render(request, 'soustredeni/stvrzenky.tex', {'ucastnici': ucastnici, 'soustredeni': soustredeni, 'castka': castka}).content | ||||
| 
 | ||||
| 	tempdir = Path(tempfile.mkdtemp()) | ||||
| 	with open(tempdir / "stvrzenky.tex", "w") as texfile: | ||||
| 		texfile.write(tex.decode()) | ||||
| 	with tempfile.TemporaryDirectory() as tempdirfn: | ||||
| 		tempdir = Path(tempdirfn) | ||||
| 		with open(tempdir / "stvrzenky.tex", "w") as texfile: | ||||
| 			texfile.write(tex.decode()) | ||||
| 
 | ||||
| 	shutil.copy(find('images/logomm.pdf'), tempdir) | ||||
| 	subprocess.call(["pdflatex", "stvrzenky.tex"], cwd = tempdir, stdout=subprocess.DEVNULL) | ||||
| 		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') | ||||
| 	shutil.rmtree(tempdir) | ||||
| 		with open(tempdir / "stvrzenky.pdf", "rb") as pdffile: | ||||
| 			response = HttpResponse(pdffile.read(), content_type='application/pdf') | ||||
| 	return response | ||||
|  |  | |||
|  | @ -1,19 +1,10 @@ | |||
| {% extends "seminar/archiv/base.html" %} | ||||
| {% load static %} | ||||
| {% load sekizai_tags %} | ||||
| 
 | ||||
| {# toto z nejakeho duvodu nefunguje #} | ||||
| {% addtoblock css %} | ||||
| dfsdfs  | ||||
| <link rel="stylesheet" type="text/css" href="{% static 'css/mamweb-dev.css' %}" /> | ||||
| {% endaddtoblock "css" %} | ||||
| 
 | ||||
| {% block custom_css %} | ||||
| <link rel="stylesheet" type="text/css" href="{% static 'css/mamweb-dev.css' %}" /> | ||||
| {% endblock custom_css %} | ||||
| 
 | ||||
| {% load comments %} | ||||
| 
 | ||||
| {% block content %} | ||||
| <ul> | ||||
| {% for obj in object_list %} | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Pavel "LEdoian" Turinsky
						Pavel "LEdoian" Turinsky