Browse Source

Merge branch 'master' into split-apps

Aby se to snáz mergeovalo
pull/43/head
Pavel "LEdoian" Turinsky 7 months ago
parent
commit
5070a4d914
  1. 5
      mamweb/settings_common.py
  2. 42
      mamweb/static/css/mamweb.css
  3. 38
      mamweb/static/css/rozliseni.css
  4. 23
      mamweb/templates/base.html
  5. 4
      mamweb/urls.py
  6. 6
      odevzdavatko/templates/odevzdavatko/tabulka.html
  7. 39
      requirements.txt
  8. 6
      seminar/templates/seminar/archiv/cislo.html
  9. 6
      seminar/templates/seminar/archiv/problem.html
  10. 3
      seminar/templates/seminar/archiv/rocnik.html
  11. 1
      seminar/templatetags/utils.py
  12. 5
      seminar/testutils.py
  13. 26
      seminar/views/views_all.py
  14. 4
      sifrovacka/admin.py
  15. 14
      sifrovacka/forms.py
  16. 65
      sifrovacka/migrations/0004_napoveda_napovezenoucastnikovi.py
  17. 17
      sifrovacka/models.py
  18. 50
      sifrovacka/templates/sifrovacka/napoveda.html
  19. 23
      sifrovacka/templates/sifrovacka/napovedy_list.html
  20. 54
      sifrovacka/templates/sifrovacka/preskoceni.html
  21. 4
      sifrovacka/templates/sifrovacka/sifrovacka.html
  22. 17
      sifrovacka/urls.py
  23. 43
      sifrovacka/views.py
  24. 8
      soustredeni/templates/soustredeni/seznam_soustredeni.html
  25. 38
      soustredeni/views.py
  26. 9
      treenode/templates/treenode/orphanage.html

5
mamweb/settings_common.py

@ -87,7 +87,6 @@ TEMPLATES = [
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',
'django.template.context_processors.request', 'django.template.context_processors.request',
'django.contrib.messages.context_processors.messages', 'django.contrib.messages.context_processors.messages',
'sekizai.context_processors.sekizai',
'header_fotky.context_processors.vzhled', 'header_fotky.context_processors.vzhled',
'various.context_processors.rozliseni', 'various.context_processors.rozliseni',
'various.context_processors.april', 'various.context_processors.april',
@ -110,7 +109,6 @@ INSTALLED_APPS = (
'django.contrib.auth', 'django.contrib.auth',
# Utilities # Utilities
'sekizai',
'reversion', 'reversion',
'django_countries', 'django_countries',
'solo', 'solo',
@ -120,9 +118,6 @@ INSTALLED_APPS = (
'dal', 'dal',
'dal_select2', 'dal_select2',
'crispy_forms',
'django_comments',
'django.contrib.flatpages', 'django.contrib.flatpages',
'django.contrib.humanize', 'django.contrib.humanize',

42
mamweb/static/css/mamweb.css

@ -39,7 +39,7 @@ div.login-bar {
position: fixed; position: fixed;
margin-top: -20px; margin-top: -20px;
min-height: 20px; min-height: 20px;
z-index: 20; z-index: 4086;
padding-left: 5px; padding-left: 5px;
padding-right: 5px; padding-right: 5px;
@ -199,22 +199,13 @@ h1 {
margin-top: 0px; margin-top: 0px;
} }
/* Comments */
#id_comment {
width: 100%;
height: 6em;
}
/* Headline & Header */ /* Headline & Header */
#title { /*dělá blbosti šířka, je to kvůli fixed pozici, zatím natvrdo, vyřešit*/ #title { /*dělá blbosti šířka, je to kvůli fixed pozici, zatím natvrdo, vyřešit*/
height: 55px; height: 55px;
width: 970px; width: 970px;
position: fixed; position: fixed;
z-index: 10; z-index: 2048;
background-color: #e84e10; background-color: #e84e10;
filter: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.4)); filter: drop-shadow(0px 5px 5px rgba(0, 0, 0, 0.4));
@ -1252,6 +1243,35 @@ div.gdpr {
background: rgb(253, 237, 213); 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 { .odevzdana_reseni tr th, .odevzdana_reseni tr td {
border: 1px solid black; border: 1px solid black;

38
mamweb/static/css/rozliseni.css

@ -1,29 +1,35 @@
/* Rozlišení mezi lokálním, test a produkčním webem */ /* Rozlišení mezi lokálním, test a produkčním webem */
.localweb { body.localweb:before, body.localweb:after,
border-left: 20px solid greenyellow; body.testweb:before, body.testweb:after,
border-right: 20px solid greenyellow; body.suprodweb:before, body.suprodweb:after {
content: "";
position: fixed;
width: 20px;
height: 100%;
top: 0;
} }
.localweb .login-bar { body.localweb:before,
margin-left: -20px; body.testweb:before,
body.suprodweb:before {
left: 0;
} }
.testweb { body.localweb:after,
border-left: 20px solid darkorange; body.testweb:after,
border-right: 20px solid darkorange; body.suprodweb:after {
right: 0;
} }
.testweb .login-bar { body.localweb:before, body.localweb:after {
margin-left: -20px; background: greenyellow;
} }
/* Produkční web z pohledu superuživatele */ body.testweb:before, body.testweb:after {
.suprodweb { background: darkorange;
border-left: 20px solid red;
border-right: 20px solid red;
} }
.suprodweb .login-bar { body.suprodweb:before, body.suprodweb:after {
margin-left: -20px; background: red;
} }

23
mamweb/templates/base.html

@ -1,4 +1,4 @@
{% load static sekizai_tags %} {% load static %}
{% load sitetree %} {% load sitetree %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang='cs'> <html lang='cs'>
@ -7,7 +7,6 @@
<title>{% block title %}{% block nadpis1a %}🦊{% endblock %} | Korespondenční seminář M&amp;M{% endblock title %}</title> <title>{% block title %}{% block nadpis1a %}🦊{% endblock %} | Korespondenční seminář M&amp;M{% endblock title %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <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"> <link rel="shortcut icon" href="{% static 'images/MATFYZ_MM_barevne.svg' %}" type="image/x-icon">
{# {% render_block css %}#}
{% block custom_css %}{% endblock %} {% block custom_css %}{% endblock %}
<link href="{% static 'css/bootstrap-theme.css' %}" rel="stylesheet"> <link href="{% static 'css/bootstrap-theme.css' %}" rel="stylesheet">
<link href="{% static 'css/bootstrap.css' %}" rel="stylesheet"> <link href="{% static 'css/bootstrap.css' %}" rel="stylesheet">
@ -189,6 +188,24 @@
walkText(document.body); walkText(document.body);
</script> </script>
{% endif %} {% 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> </body>
</html> </html>

4
mamweb/urls.py

@ -13,7 +13,6 @@ Soubor sloužící jako základní „router“, tj. zde se includují veškeré
- :mod:`api.urls` - :mod:`api.urls`
- :mod:`treenode.urls` - :mod:`treenode.urls`
- :mod:`aesop.urls` - :mod:`aesop.urls`
- ``comments_dj/`` :mod:`django_comments.urls`
""" """
from django.urls import path, include from django.urls import path, include
from django.contrib.staticfiles.urls import staticfiles_urlpatterns from django.contrib.staticfiles.urls import staticfiles_urlpatterns
@ -62,9 +61,6 @@ urlpatterns = [
# Aesop (ma vlastni podadresare) # Aesop (ma vlastni podadresare)
path('', include('aesop.urls')), path('', include('aesop.urls')),
# Comments (interni i verejne)
path('comments_dj/', include('django_comments.urls')),
# REST API # REST API
# path('api/', include(router.urls)), # path('api/', include(router.urls)),

6
odevzdavatko/templates/odevzdavatko/tabulka.html

@ -21,8 +21,8 @@ Do data (včetně): {{ filtr.reseni_do }}
<input type=submit value="Změň ročník"> <input type=submit value="Změň ročník">
</form> </form>
<div style="overflow-x: scroll;">
<table class="dosla_reseni"> <table class="dosla_reseni">
<thead>
<tr> <tr>
<td></td> {# Prázdná buňka v levém horním rohu #} <td></td> {# Prázdná buňka v levém horním rohu #}
{% for p in problemy %} {% for p in problemy %}
@ -32,6 +32,8 @@ Do data (včetně): {{ filtr.reseni_do }}
</th> </th>
{% endfor %} {% endfor %}
</tr> </tr>
</thead>
<tbody>
{% for resitel,hodnoty in radky%} {% for resitel,hodnoty in radky%}
<tr> <tr>
<td> <td>
@ -52,8 +54,8 @@ Do data (včetně): {{ filtr.reseni_do }}
{% endfor %} {% endfor %}
</tr> </tr>
{% endfor %} {% endfor %}
</tbody>
</table> </table>
</div>
{% endblock %} {% endblock %}

39
requirements.txt

@ -1,43 +1,32 @@
# -*- coding: utf-8 -*-
-c constraints.txt -c constraints.txt
# basic libs # basic libs
psycopg2 psycopg2 # PostgreSQL adaptér
html5lib ipython # Interaktivní shell
ipython Unidecode # Přepisuje unicode do ASCII (např. soubory nebo e-maily)
Pillow Pillow
pilkit>=3.0 # Kvůli kompatibilitě s Pillow>=10.0.0 pilkit>=3.0 # Kvůli kompatibilitě s Pillow>=10.0.0
pytz
six
pexpect
traitlets
Unidecode
# Django and modules # Django and modules
Django<5.0 Django<5.0
#django-bootstrap-sass #django-bootstrap-sass
django-mptt django-reversion # Version control na datech v databázi
django-reversion django-countries # Políčko ve formu / field v modelu ohledně států
django-sekizai django-solo # Singleton model (speciálně Nastavení)
django-countries django-ckeditor # Editor htmlka (hlavně v adminu u flatpages)
django-solo
django-ckeditor
django-cleanup # Uklízí media/ od smazaných „databázových“ souborů django-cleanup # Uklízí media/ od smazaných „databázových“ souborů
django-flat-theme django-taggit # Taggy v djangu (speciálně zaměření problémů)
django-taggit django-autocomplete-light>=3.9.0 # Automatické doplňování (problémů, účastníků, …) ve formulářích
django-autocomplete-light>=3.9.0 django-imagekit # Všechny možné obrázky v Djangu
django-crispy-forms django-polymorphic # Polymorfismus na django modelech (hlavně Problém nebo treenode)
django-imagekit django-sitetree # Struktura stránek, hlavně pro meníčko
django-polymorphic django_reverse_admin # Lepší handlování OneToOne fieldů v adminu
django-sitetree
django_reverse_admin
django-rest-framework django-rest-framework
django-webpack-loader django-webpack-loader
django-rest-polymorphic django-rest-polymorphic
# Comments
django-contrib-comments
# debug tools/extensions # debug tools/extensions
django-debug-toolbar django-debug-toolbar

6
seminar/templates/seminar/archiv/cislo.html

@ -38,9 +38,11 @@
<h2> Orgovské odkazy </h2> <h2> Orgovské odkazy </h2>
<ul> <ul>
<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, 2. deadline předchozího čísla a 1.deadline tohoto)</a></li>
<li><a href="vysledkovka.tex" download>Výsledkovka (TeX)</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="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> </ul>
</div> </div>
{% endif %} {% endif %}

6
seminar/templates/seminar/archiv/problem.html

@ -1,7 +1,5 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load comments %}
{% block content %} {% block content %}
<div {% if not problem.verejne and user.je_org %}class="mam-org-only"{% endif %}> <div {% if not problem.verejne and user.je_org %}class="mam-org-only"{% endif %}>
{% block problem %} {% block problem %}
@ -13,10 +11,6 @@
<h2>Text - org</h2> <h2>Text - org</h2>
{{ problem.text_org |safe }} {{ problem.text_org |safe }}
<h2>Diskuse - org</h2>
{% render_comment_list for object %}
{% render_comment_form for object %}
</div> </div>
{% endif %} {% endif %}

3
seminar/templates/seminar/archiv/rocnik.html

@ -120,8 +120,7 @@
{% if user.je_org %} {% if user.je_org %}
<div class='mam-org-only'> <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='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="tituly.tex" download>Tituly (TeX, včetně neveřejných, všechny, nevhodné do mamtexu)</a></p>
<p><a href="posledni_vysledkovka.tex" download>Výsledkovka posledního čísla</a></p>
{# FIXME: Sice to sem asi nepatří sémanticky, ale bylo to nejjednodušší… #} {# 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> <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> <h2>Výsledková listina včetně neveřejných bodů</h2>

1
seminar/templatetags/utils.py

@ -1,7 +1,6 @@
from django import template from django import template
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from datetime import datetime, timedelta from datetime import datetime, timedelta
from pytz import timezone
from mamweb.settings import TIME_ZONE from mamweb.settings import TIME_ZONE
import logging import logging
register = template.Library() register = template.Library()

5
seminar/testutils.py

@ -4,7 +4,6 @@ import datetime
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from pytz import timezone
import random import random
import lorem import lorem
import django.contrib.auth import django.contrib.auth
@ -177,13 +176,13 @@ def gen_organizatori(rnd, osoby, last_rocnik):
year=1993 + pusobnost, year=1993 + pusobnost,
month=rnd.randint(1, 12), month=rnd.randint(1, 12),
day=rnd.randint(1, 28), day=rnd.randint(1, 28),
tzinfo=timezone('CET'), tzinfo=datetime.timezone.utc,
) )
do = datetime.datetime( do = datetime.datetime(
year=od.year + rnd.randint(1, 6), year=od.year + rnd.randint(1, 6),
month=rnd.randint(1, 12), month=rnd.randint(1, 12),
day=rnd.randint(1, 28), day=rnd.randint(1, 28),
tzinfo=timezone('CET'), tzinfo=datetime.timezone.utc,
) )
#aktualni organizatori jeste nemaji vyplnene organizuje_do #aktualni organizatori jeste nemaji vyplnene organizuje_do

26
seminar/views/views_all.py

@ -8,6 +8,7 @@ from django.http import Http404
from django.db.models import Q, Sum, Count from django.db.models import Q, Sum, Count
from django.views.generic.base import RedirectView from django.views.generic.base import RedirectView
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.contrib.staticfiles.finders import find
import seminar.models as s import seminar.models as s
import seminar.models as m import seminar.models as m
@ -36,6 +37,7 @@ import unicodedata
import logging import logging
import time import time
from collections.abc import Sequence from collections.abc import Sequence
import http
from seminar.utils import aktivniResitele from seminar.utils import aktivniResitele
@ -562,17 +564,25 @@ def cisloObalkyView(request, rocnik, cislo):
def obalkyView(request, resitele): 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 tex = render(request,'seminar/archiv/obalky.tex', {'resitele': resitele}).content
tempdir = tempfile.mkdtemp() with tempfile.TemporaryDirectory() as tempdir:
with open(tempdir+"/obalky.tex","w") as texfile: with open(tempdir+"/obalky.tex","w") as texfile:
texfile.write(tex.decode()) texfile.write(tex.decode())
shutil.copy(os.path.join(settings.STATIC_ROOT, 'seminar/lisak.pdf'), tempdir) shutil.copy(find('seminar/lisak.pdf'), tempdir)
subprocess.call(["pdflatex","obalky.tex"], cwd = tempdir) subprocess.call(["pdflatex","obalky.tex"], cwd = tempdir)
with open(tempdir+"/obalky.pdf","rb") as pdffile: with open(tempdir+"/obalky.pdf","rb") as pdffile:
response = HttpResponse(pdffile.read(), content_type='application/pdf') response = HttpResponse(pdffile.read(), content_type='application/pdf')
shutil.rmtree(tempdir)
return response return response

4
sifrovacka/admin.py

@ -1,8 +1,10 @@
from django.contrib import admin from django.contrib import admin
from .models import OdpovedUcastnika, SpravnaOdpoved from .models import OdpovedUcastnika, SpravnaOdpoved, NapovezenoUcastnikovi, Napoveda
# Register your models here. # Register your models here.
admin.site.register(OdpovedUcastnika) admin.site.register(OdpovedUcastnika)
admin.site.register(SpravnaOdpoved) admin.site.register(SpravnaOdpoved)
admin.site.register(Napoveda)
admin.site.register(NapovezenoUcastnikovi)

14
sifrovacka/forms.py

@ -1,6 +1,6 @@
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.forms import ModelForm, Textarea from django.forms import ModelForm, Textarea
from .models import OdpovedUcastnika, SpravnaOdpoved from .models import OdpovedUcastnika, SpravnaOdpoved, NapovezenoUcastnikovi, Napoveda
class SifrovackaForm(ModelForm): class SifrovackaForm(ModelForm):
@ -16,3 +16,15 @@ class SifrovackaForm(ModelForm):
if SpravnaOdpoved.objects.filter(sifra=sifra).count() == 0: if SpravnaOdpoved.objects.filter(sifra=sifra).count() == 0:
raise ValidationError("Tohle číslo šifry v databázi nemáme. Zkontrolujte si ho prosím.") raise ValidationError("Tohle číslo šifry v databázi nemáme. Zkontrolujte si ho prosím.")
return sifra 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

@ -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"],
},
),
]

17
sifrovacka/models.py

@ -25,3 +25,20 @@ class SpravnaOdpoved(models.Model):
def __str__(self): def __str__(self):
return f"{self.sifra}: {self.odpoved}" 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

@ -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

@ -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

@ -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 %}

4
sifrovacka/templates/sifrovacka/sifrovacka.html

@ -43,4 +43,8 @@
<input type="submit" value="Tak pravím!"> <input type="submit" value="Tak pravím!">
</form> </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 %} {% endblock content %}

17
sifrovacka/urls.py

@ -1,7 +1,7 @@
from django.urls import path from django.urls import path
from seminar.utils import org_required, resitel_or_org_required from seminar.utils import org_required, resitel_or_org_required
from .views import SifrovackaView, SifrovackaListView from .views import SifrovackaView, SifrovackaListView, NapovedaView, NapovedaListView, PreskoceniView
urlpatterns = [ urlpatterns = [
path( path(
@ -14,4 +14,19 @@ urlpatterns = [
org_required(SifrovackaListView.as_view()), org_required(SifrovackaListView.as_view()),
name='sifrovacka_odpovedi' 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'
),
] ]

43
sifrovacka/views.py

@ -2,8 +2,8 @@ from django.urls import reverse
from django.views.generic import FormView, ListView from django.views.generic import FormView, ListView
from seminar.views import formularOKView from seminar.views import formularOKView
from .forms import SifrovackaForm from .forms import SifrovackaForm, NapovedaForm
from .models import OdpovedUcastnika, SpravnaOdpoved from .models import OdpovedUcastnika, SpravnaOdpoved, Napoveda, NapovezenoUcastnikovi
from seminar.models import Resitel from seminar.models import Resitel
@ -24,10 +24,47 @@ class SifrovackaView(FormView):
instance.uspech = True instance.uspech = True
instance.save() instance.save()
return formularOKView(self.request, f'<h1>{sifra.skryty_text}</h1> <p><a href="{reverse("sifrovacka")}">Odevzdat další.</a></p><br><br><br>') 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): class SifrovackaListView(ListView):
template_name = 'sifrovacka/odpovedi_list.html' template_name = 'sifrovacka/odpovedi_list.html'
model = OdpovedUcastnika 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>')

8
soustredeni/templates/soustredeni/seznam_soustredeni.html

@ -58,16 +58,16 @@
{# Účastníci #} {# Účastníci #}
<h2>Soustředění se zúčastnili tito účastníci:</h2> <h2>Soustředění se zúčastnili tito účastníci:</h2>
<p> <p>
{% for i in soustredeni.soustredeni_ucastnici_set.all %} {% for i in soustredeni.ucastnici.all %}
{{i.resitel}}{% if forloop.last %}.{% else %},{% endif %} {{i}}{% if forloop.last %}.{% else %},{% endif %}
{% empty %} {% empty %}
Nic! Nic!
{% endfor %} {% endfor %}
</p> </p>
<h2>Soustředění se účastnili tito organizátoři:</h2> <h2>Soustředění se účastnili tito organizátoři:</h2>
<p> <p>
{% for i in soustredeni.soustredeni_organizatori_set.all %} {% for i in soustredeni.organizatori.all %}
{{i.organizator}}{% if forloop.last %}.{% else %},{% endif %} {{i}}{% if forloop.last %}.{% else %},{% endif %}
{% empty %} {% empty %}
Nic! Nic!
{% endfor %} {% endfor %}

38
soustredeni/views.py

@ -9,6 +9,7 @@ import tempfile
import shutil import shutil
import subprocess import subprocess
from pathlib import Path from pathlib import Path
import http
from seminar.views import obalkyView from seminar.views import obalkyView
@ -17,6 +18,19 @@ class SoustredeniListView(generic.ListView):
model = Soustredeni model = Soustredeni
template_name = 'soustredeni/seznam_soustredeni.html' 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): def soustredeniObalkyView(request, soustredeni):
soustredeni = get_object_or_404(Soustredeni, id=soustredeni) soustredeni = get_object_or_404(Soustredeni, id=soustredeni)
@ -63,17 +77,25 @@ 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)
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 castka = Nastaveni.get_solo().cena_sous
tex = render(request, 'soustredeni/stvrzenky.tex', {'ucastnici': ucastnici, 'soustredeni': soustredeni, 'castka': castka}).content tex = render(request, 'soustredeni/stvrzenky.tex', {'ucastnici': ucastnici, 'soustredeni': soustredeni, 'castka': castka}).content
tempdir = Path(tempfile.mkdtemp()) with tempfile.TemporaryDirectory() as tempdirfn:
with open(tempdir / "stvrzenky.tex", "w") as texfile: tempdir = Path(tempdirfn)
texfile.write(tex.decode()) with open(tempdir / "stvrzenky.tex", "w") as texfile:
texfile.write(tex.decode())
shutil.copy(find('images/logomm.pdf'), tempdir) shutil.copy(find('images/logomm.pdf'), tempdir)
subprocess.call(["pdflatex", "stvrzenky.tex"], cwd = tempdir, stdout=subprocess.DEVNULL) subprocess.call(["pdflatex", "stvrzenky.tex"], cwd = tempdir, stdout=subprocess.DEVNULL)
with open(tempdir / "stvrzenky.pdf", "rb") as pdffile: 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)
return response return response

9
treenode/templates/treenode/orphanage.html

@ -1,19 +1,10 @@
{% extends "seminar/archiv/base.html" %} {% extends "seminar/archiv/base.html" %}
{% load static %} {% 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 %} {% block custom_css %}
<link rel="stylesheet" type="text/css" href="{% static 'css/mamweb-dev.css' %}" /> <link rel="stylesheet" type="text/css" href="{% static 'css/mamweb-dev.css' %}" />
{% endblock custom_css %} {% endblock custom_css %}
{% load comments %}
{% block content %} {% block content %}
<ul> <ul>
{% for obj in object_list %} {% for obj in object_list %}

Loading…
Cancel
Save