Browse Source

Merge remote-tracking branch 'gitea/master' into zadavatko_problemu

zadavatko_problemu
Pavel "LEdoian" Turinsky 2 years ago
parent
commit
9c06bc6f62
  1. 24
      .editorconfig
  2. 1
      api/urls.py
  3. 15
      api/views/autocomplete.py
  4. 3
      mamweb/settings_common.py
  5. 1
      mamweb/settings_local.py
  6. 4
      mamweb/static/css/mamweb.css
  7. 2
      mamweb/templates/admin/base_site.html
  8. 6
      mamweb/templates/menu_mobile.html
  9. 12
      odevzdavatko/forms.py
  10. 6
      odevzdavatko/templates/odevzdavatko/detail.html
  11. 3
      odevzdavatko/templates/odevzdavatko/detail_resitele.html
  12. 46
      odevzdavatko/views.py
  13. 4
      prednasky/urls.py
  14. 2
      requirements.txt
  15. 18
      seminar/migrations/0108_nastaveni_cena_sous.py
  16. 18
      seminar/migrations/0109_hodnoceni_feedback.py
  17. 2
      seminar/models/odevzdavatko.py
  18. 4
      seminar/models/tvorba.py
  19. 25
      seminar/static/seminar/stvrzenky.tex
  20. 3
      seminar/templates/seminar/archiv/cislo.html
  21. 33
      seminar/templates/seminar/archiv/cislo_obalkovani.html
  22. 20
      seminar/templates/seminar/archiv/odmeny.html
  23. 30
      seminar/templates/seminar/org/obalkovani.html
  24. 3
      seminar/templates/seminar/zadani/AktualniVysledkovka.html
  25. 17
      seminar/testutils.py
  26. 6
      seminar/urls.py
  27. 56
      seminar/views/views_all.py
  28. 37
      soustredeni/templates/soustredeni/stvrzenky.tex
  29. 5
      soustredeni/templates/soustredeni/ucastnici.tex
  30. 26
      soustredeni/views.py
  31. 2
      various/autentizace/templates/autentizace/login.html
  32. 32
      vysledkovky/templates/vysledkovky/vysledkovka_cisla.html
  33. 24
      vysledkovky/templates/vysledkovky/vysledkovka_rocnik.html
  34. 5
      vysledkovky/utils.py

24
.editorconfig

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

1
api/urls.py

@ -23,6 +23,7 @@ urlpatterns = [
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/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
# path('autocomplete/organizatori/',

15
api/views/autocomplete.py

@ -66,6 +66,21 @@ class OdevzdatelnyProblemAutocomplete(autocomplete.Select2QuerySetView):
Q(nazev__icontains=self.q))
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
# class OrganizatorAutocomplete(autocomplete.Select2QuerySetView):
# def get_queryset(self):

3
mamweb/settings_common.py

@ -160,6 +160,9 @@ INSTALLED_APPS = (
# 'admin_tools.menu',
# 'admin_tools.dashboard',
'django.contrib.admin',
# Nechat na konci (INSTALLED_APPS je uspořádané):
'django_cleanup.apps.CleanupConfig', # Uklízí media/
)
DEBUG_TOOLBAR_CONFIG = {

1
mamweb/settings_local.py

@ -31,6 +31,7 @@ TEMPLATES[0]['OPTIONS']['debug'] = True
from ipaddress import ip_network
ALLOWED_HOSTS = [str(ip) for ip in ip_network('192.168.0.0/16')]
ALLOWED_HOSTS.append('127.0.0.1')
ALLOWED_HOSTS.append('localhost')
# Database
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases

4
mamweb/static/css/mamweb.css

@ -421,6 +421,10 @@ input {
margin: 5px;
}
textarea.feedback {
margin: 5px;
}
/* titulni stranka */

2
mamweb/templates/admin/base_site.html

@ -10,5 +10,5 @@
{% block bodyclass %}{{ LOCAL_TEST_PROD }}web{% endblock %}
{% 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 %}

6
mamweb/templates/menu_mobile.html

@ -3,11 +3,9 @@
{% autoescape off %}
<ul class="menu_mobile">
{% for item in sitetree_items %}
<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 %}"
>
<li class="{% if item.has_children %}dropdown{% endif %} {% if item.is_current or item.in_current_branch %}active{% 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>
<div class="submenu_mobile {% if item.is_current or item.in_current_branch %}active{% endif %}">
{% if item.has_children %}

12
odevzdavatko/forms.py

@ -88,11 +88,12 @@ ReseniSPrilohamiFormSet = inlineformset_factory(m.Reseni,m.PrilohaReseni,
class JednoHodnoceniForm(forms.ModelForm):
class Meta:
model = m.Hodnoceni
fields = ('problem', 'body', 'deadline_body')
fields = ('problem', 'body', 'deadline_body', 'feedback',)
widgets = {
'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,
@ -155,10 +156,7 @@ class OdevzdavatkoTabulkaFiltrForm(forms.Form):
result = []
date_str = strftime(DATE_FORMAT, datetime.date.min.timetuple())
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"))
result.append(("0001-01-01", f"Odjakživa"))
for deadline in m.Deadline.objects.filter(
deadline__lte=timezone.now(),

6
odevzdavatko/templates/odevzdavatko/detail.html

@ -91,7 +91,7 @@ $(document).ready(function(){
<form method=post onsubmit="return zkontroluj_hodnoceni();">
{# Poznámka #}
<h3>Poznámka:</h3>
<h3>Neveřejná poznámka:</h3>
<p>{{ poznamka_form.poznamka }}</p>
{# Hodnocení: #}
@ -101,13 +101,14 @@ $(document).ready(function(){
{{ form.management_form }}
</table>
<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 %}
<tbody>
<tr class="hodnoceni">
<td>{{ subform.problem }}</td>
<td>{{ subform.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>
</tr>
</tbody>
@ -123,6 +124,7 @@ $(document).ready(function(){
<td>{{ form.empty_form.problem }}</td>
<td>{{ form.empty_form.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>
</tr>
</table>

3
odevzdavatko/templates/odevzdavatko/detail_resitele.html

@ -37,11 +37,12 @@
{# Hodnocení: #}
<h3>Hodnocení:</h3>
<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 %}
<tr class="hodnoceni">
<td>{{ h.problem }}</td>
<td>{{ h.body }}</td>
<td>{{ h.feedback }}</td>
{# <td>{{ h.deadline_body }}</td>#}
</tr>
{% endfor %}

46
odevzdavatko/views.py

@ -218,10 +218,11 @@ class DetailReseniView(DetailView):
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
for hodn in m.Hodnoceni.objects.filter(reseni=self.reseni):
result.append(
{"problem": hodn.problem,
result.append({
"problem": hodn.problem,
"body": hodn.body,
"deadline_body": hodn.deadline_body,
"feedback": hodn.feedback,
})
return result
@ -245,30 +246,26 @@ def hodnoceniReseniView(request, pk, *args, **kwargs):
formset = f.OhodnoceniReseniFormSet(request.POST)
poznamka_form = f.PoznamkaReseniForm(request.POST, instance=reseni)
# TODO: Napsat validaci formuláře a formsetu
# TODO: Implementovat větev, kdy formulář validní není.
if formset.is_valid() and poznamka_form.is_valid():
with transaction.atomic():
# Poznámka je jednoduchá na zpracování:
poznamka_form.save()
# Smažeme všechna dosavadní hodnocení tohoto řešení
qs = m.Hodnoceni.objects.filter(reseni=reseni)
logger.info(f"Will delete {qs.count()} objects: {qs}")
qs.delete()
# Vyrobíme nová podle formsetu
for form in formset:
problem = form.cleaned_data['problem']
body = form.cleaned_data['body']
deadline_body = form.cleaned_data['deadline_body']
hodnoceni = m.Hodnoceni(
problem=problem,
body=body,
deadline_body=deadline_body,
if not (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í
qs = m.Hodnoceni.objects.filter(reseni=reseni)
logger.info(f"Will delete {qs.count()} objects: {qs}")
qs.delete()
# Vyrobíme nová podle formsetu
for form in formset:
hodnoceni = m.Hodnoceni(
reseni=reseni,
**form.cleaned_data,
)
logger.info(f"Creating Hodnoceni: {hodnoceni}")
hodnoceni.save()
logger.info(f"Creating Hodnoceni: {hodnoceni}")
hodnoceni.save()
return redirect(success_url)
@ -285,6 +282,7 @@ class ResitelReseniView(DetailView):
{
"problem": hodn.problem,
"body": hodn.body,
"feedback": hodn.feedback,
# "deadline_body": hodn.deadline_body,
}
)

4
prednasky/urls.py

@ -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`
"""
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
urlpatterns = [
path(
'prednasky/',
resitel_required(views.newPrednaska)
resitel_or_org_required(views.newPrednaska)
),
path('prednasky/hotovo', views.Prednaska_hotovo),
path(

2
requirements.txt

@ -21,6 +21,7 @@ django-sekizai
django-countries
django-solo
django-ckeditor
django-cleanup # Uklízí media/ od smazaných „databázových“ souborů
django-flat-theme
django-taggit
django-autocomplete-light>=3.9.0rc1
@ -62,3 +63,4 @@ lorem
sphinx
sphinx_rtd_theme
myst_parser

18
seminar/migrations/0108_nastaveni_cena_sous.py

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

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

2
seminar/models/odevzdavatko.py

@ -113,6 +113,8 @@ class Hodnoceni(bm.SeminarModelBase):
problem = models.ForeignKey(am.Problem, verbose_name='problém',
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):
return "{}, {}, {}".format(self.problem, self.reseni, self.body)

4
seminar/models/tvorba.py

@ -718,6 +718,10 @@ class Nastaveni(SingletonModel):
aktualni_cislo = models.ForeignKey(Cislo, verbose_name='Aktuální číslo',
null=False, on_delete=models.PROTECT)
cena_sous = models.IntegerField(null=False,
verbose_name="Účastnický poplatek za soustředění",
default=1000)
@property
def aktualni_rocnik(self):
return self.aktualni_cislo.rocnik

25
seminar/static/seminar/stvrzenky.tex

@ -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$\sim$. % \termin též
\bigskip
\the\day.~\the\month.~\the\year, \misto\hfill Přijal(a): \hbox to 4cm{\hrulefill}
\bigskip
}
\repeat
\bye

3
seminar/templates/seminar/archiv/cislo.html

@ -40,8 +40,7 @@
<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="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>
</div>
{% endif %}

33
seminar/templates/seminar/archiv/cislo_obalkovani.html

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

20
seminar/templates/seminar/archiv/odmeny.html

@ -7,23 +7,11 @@
{% endblock %}
</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>
{% for z in zmeny_prvni_prvni %}
<li> {{z.jmeno}}: {{z.ftitul}} &rarr; {{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}} &rarr; {{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 %}
{% for z in zmeny %}
<li> {{z.jmeno}}: {{z.ftitul}} &rarr; {{z.ttitul}}</li>
{% endfor %}
</ul>

30
seminar/templates/seminar/org/obalkovani.html

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

3
seminar/templates/seminar/zadani/AktualniVysledkovka.html

@ -4,7 +4,8 @@
<h1>
{% block nadpis1a %}
Průběžné výsledky {{ rocnik.rocnik }}.&nbsp;ročníku
Výsledky {{ rocnik.rocnik }}.&nbsp;ročníku
{% if vysledkovka.do_deadlinu %}k datu {{ vysledkovka.do_deadlinu.deadline.date }}{% endif %}
{% endblock %}
</h1>

17
seminar/testutils.py

@ -13,7 +13,7 @@ import unidecode
import logging
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
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í.
if uloha.cislo_zadani.zlomovy_deadline_pro_papirove_cislo() is not None:
# 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
res = Reseni.objects.create(forma=rnd.choice(Reseni.FORMA_CHOICES)[0], cas_doruceni=cas_doruceni.astimezone(datetime.timezone.utc))
else:
@ -436,22 +436,23 @@ def gen_cisla(rnd, rocniky):
(mesic_vydani + 1) % 12 + 1,
rnd.randint(1, 28))
# posledni 2 cisla v rocniku nemaji deadline
if (ci + 2 > cisel):
deadline = None
cislo = Cislo.objects.create(
rocnik = rocnik,
poradi = str(ci),
datum_vydani=vydano,
datum_deadline=deadline,
verejne_db=True,
verejna_vysledkovka=True
)
node2 = CisloNode.objects.get(cislo = cislo)
node2.succ = node
node2.root = rocnik.rocniknode
cislo.save()
deadline = Deadline.objects.create(
cislo=cislo,
deadline=deadline,
typ=Deadline.TYP_CISLA,
verejna_vysledkovka=True,
)
deadline.save()
node = node2
if otec:
otec = False

6
seminar/urls.py

@ -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>/tituly.tex`` (seminar_cislo_titul) :func:`~seminar.views.views_all.TitulyView`
- ``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`
- Další
- `` `` (titulni_strana) :class:`~seminar.views.views_all.TitulniStranaView`
@ -102,11 +101,6 @@ urlpatterns = [
org_required(views.StavDatabazeView),
name='stav_databaze'
),
path(
'cislo/<int:rocnik>.<str:cislo>/obalkovani',
org_required(views.ObalkovaniView.as_view()),
name='seminar_cislo_resitel_obalkovani'
),
path(
'cislo/<int:trocnik>.<str:tcislo>/odmeny/<int:frocnik>.<str:fcislo>/',
org_required(views.OdmenyView.as_view()),

56
seminar/views/views_all.py

@ -53,23 +53,6 @@ logger = logging.getLogger(__name__)
def get_problemy_k_tematu(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!
#class ProblemView(generic.DetailView):
@ -484,18 +467,11 @@ class OdmenyView(generic.TemplateView):
context["from_cislo"] = fromcislo
context["to_cislo"] = tocislo
context["zmeny_prvni_prvni"] = get_diff(
fromcislo.zlomovy_deadline_pro_papirove_cislo(),
tocislo.zlomovy_deadline_pro_papirove_cislo()
)
context["zmeny_prvni_posledni"] = get_diff(
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()
)
from_deadline = posledni_deadline_oprava(fromcislo)
to_deadline = posledni_deadline_oprava(tocislo)
context["from_deadline"] = from_deadline
context["to_deadline"] = to_deadline
context["zmeny"] = get_diff(from_deadline, to_deadline)
return context
@ -597,28 +573,6 @@ def obalkyView(request, resitele):
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
def TitulyViewRocnik(request, rocnik):
return TitulyView(request, rocnik, None)

37
soustredeni/templates/soustredeni/stvrzenky.tex

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

5
soustredeni/templates/soustredeni/ucastnici.tex

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

26
soustredeni/views.py

@ -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.views import generic
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 tempfile
import shutil
@ -62,20 +63,17 @@ def soustredeniUcastniciExportView(request, soustredeni):
def soustredeniStvrzenkyView(request, soustredeni):
soustredeni = get_object_or_404(Soustredeni, id=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())
shutil.copy(static / 'images/logomm.pdf', tempdir)
shutil.copy(static / 'seminar/stvrzenky.tex', tempdir)
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:
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)
with open(tempdir / "stvrzenky.pdf", "rb") as pdffile:
response = HttpResponse(pdffile.read(), content_type='application/pdf')
shutil.rmtree(tempdir)
return response

2
various/autentizace/templates/autentizace/login.html

@ -24,7 +24,7 @@
<br>
<p><a href="{% url 'reset_password' %}">
Zapomněl jsem heslo
Zapomněl jsem heslo nebo uživatelské jméno
</a></p>
<hr>

32
vysledkovky/templates/vysledkovky/vysledkovka_cisla.html

@ -1,49 +1,49 @@
<div style="overflow-x: auto;">
<table class='vysledkovka'>
<tr class='border-b'>
<th class='border-r'>#
<th class='border-r'>Jméno
<th class='border-r'>#</th>
<th class='border-r'>Jméno</th>
{% 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 #}
{% 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 %}
{# TODELETE #}
{% 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 #}
{% 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 %}
{# TODELETE #}
<th class='border-r'>Za číslo
<th class='border-r'>Za ročník
<th class='border-r'>Odjakživa
<th class='border-r'>Za číslo</th>
<th class='border-r'>Za ročník</th>
<th class='border-r'>Odjakživa</th>
{% for rv in vysledkovka.radky_vysledkovky %}
<tr>
<td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %}
<td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %}</td>
<th class='border-r'>
{% if rv.titul %}
{{ rv.titul }}<sup>MM</sup>
{% endif %}
{{ rv.resitel.osoba.plne_jmeno }}
{{ rv.resitel.osoba.plne_jmeno }}</th>
{% 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 %}
<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 %}
<td class='border-r'>{{ rv.body_cislo }}
<td class='border-r'><b>{{ rv.body_rocnik }}</b>
<td class='border-r'>{{ rv.body_celkem_odjakziva }}
<td class='border-r'>{{ rv.body_cislo }}</td>
<td class='border-r'><b>{{ rv.body_rocnik }}</b></td>
<td class='border-r'>{{ rv.body_celkem_odjakziva }}</td>
</tr>
{% endfor %}
</table>

24
vysledkovky/templates/vysledkovky/vysledkovka_rocnik.html

@ -1,29 +1,29 @@
<table class='vysledkovka'>
<tr class='border-b'>
<th class='border-r'>#
<th class='border-r'>Jméno
<th class='border-r'>R.
<th class='border-r'>Odjakživa
<th class='border-r'>#</th>
<th class='border-r'>Jméno</th>
<th class='border-r'>R.</th>
<th class='border-r'>Odjakživa</th>
{% for c in vysledkovka.cisla_rocniku %}
<th class='border-r'><a href="{{ c.verejne_url }}">
{{c.rocnik.rocnik}}.{{ c.poradi }}</a>
{{c.rocnik.rocnik}}.{{ c.poradi }}</a></th>
{% endfor %}
<th class='border-r'>Celkem
<th class='border-r'>Celkem</th>
{% for rv in vysledkovka.radky_vysledkovky %}
<tr>
<td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %}
<td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %}</td>
<th class='border-r'>
{% if rv.titul %}
{{ rv.titul }}<sup>MM</sup>
{% endif %}
{{ rv.resitel.osoba.plne_jmeno }}
<td class='border-r'>{{ rv.rocnik_resitele }}
<td class='border-r'>{{ rv.body_celkem_odjakziva }}
{{ rv.resitel.osoba.plne_jmeno }}</th>
<td class='border-r'>{{ rv.rocnik_resitele }}</td>
<td class='border-r'>{{ rv.body_celkem_odjakziva }}</td>
{% for b in rv.body_cisla_seznam %}
<td class='border-r'>{{ b }}
<td class='border-r'>{{ b }}</td>
{% endfor %}
<td class='border-r'><b>{{ rv.body_rocnik }}</b>
<td class='border-r'><b>{{ rv.body_rocnik }}</b></td>
</tr>
{% endfor %}
</table>

5
vysledkovky/utils.py

@ -146,7 +146,10 @@ class VysledkovkaRocniku(Vysledkovka):
def __init__(self, rocnik: m.Rocnik, jen_verejne: bool = True):
self.rocnik = rocnik
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
def aktivni_resitele(self) -> list[m.Resitel]:

Loading…
Cancel
Save