Merge remote-tracking branch 'gitea/master' into zadavatko_problemu
This commit is contained in:
commit
9c06bc6f62
34 changed files with 231 additions and 260 deletions
24
.editorconfig
Normal file
24
.editorconfig
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Univerzální popis našich konvencí pro nastavení editorů.
|
||||
# Vizte https://editorconfig.org pro detaily
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
# Unixové řádky
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{py,css}]
|
||||
indent_style = tab
|
||||
# Nenařizujeme konkrétní šířku tabulátoru
|
||||
indent_size = unset
|
||||
|
||||
# Automaticky generované migrace dodržují PEP-8, nemá smysl s tím moc bojovat.
|
||||
[*/migrations/*.py]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.html]
|
||||
indent_style = space
|
||||
indent_size = 2
|
|
@ -23,6 +23,7 @@ urlpatterns = [
|
|||
path('api/autocomplete/skola/', views.SkolaAutocomplete.as_view(), name='autocomplete_skola'),
|
||||
path('api/autocomplete/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/',
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -421,6 +421,10 @@ input {
|
|||
margin: 5px;
|
||||
}
|
||||
|
||||
textarea.feedback {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
|
||||
/* titulni stranka */
|
||||
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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()
|
||||
if not (formset.is_valid() and poznamka_form.is_valid()):
|
||||
raise ValueError(formset.errors, poznamka_form.errors)
|
||||
|
||||
# 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,
|
||||
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,
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
Normal file
18
seminar/migrations/0108_nastaveni_cena_sous.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.16 on 2022-11-14 20:51
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0107_zmrazenavysledkovka'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='nastaveni',
|
||||
name='cena_sous',
|
||||
field=models.IntegerField(default=1000, verbose_name='Účastnický poplatek za soustředění'),
|
||||
),
|
||||
]
|
18
seminar/migrations/0109_hodnoceni_feedback.py
Normal file
18
seminar/migrations/0109_hodnoceni_feedback.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.16 on 2022-11-14 19:30
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0108_nastaveni_cena_sous'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='hodnoceni',
|
||||
name='feedback',
|
||||
field=models.TextField(blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)', verbose_name='zpětná vazba'),
|
||||
),
|
||||
]
|
|
@ -113,6 +113,8 @@ class Hodnoceni(bm.SeminarModelBase):
|
|||
problem = models.ForeignKey(am.Problem, verbose_name='problém',
|
||||
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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
\input opmac
|
||||
\chyph
|
||||
\nopagenumbers
|
||||
\parindent=0pt
|
||||
|
||||
\def\castka{1000}
|
||||
|
||||
\newread\data
|
||||
\openin\data=/dev/stdin
|
||||
\read\data to\termin
|
||||
\read\data to\misto
|
||||
|
||||
\loop
|
||||
\read\data to\ucastnik
|
||||
\unless\ifeof\data
|
||||
\vbox{\picw=2cm\inspic logomm.pdf \smallskip\hrule\medskip
|
||||
Potvrzujeme, že \ucastnik se zúčastnil(a) soustředění Korespondenčního semináře M\&M konaného % \ucastnik má na konci mezeru
|
||||
v~termínu \termin a že zaplatil(a) účastnický poplatek ve výši $\sim$\castka Kč$\sim$. % \termin též
|
||||
\bigskip
|
||||
\the\day.~\the\month.~\the\year, \misto\hfill Přijal(a): \hbox to 4cm{\hrulefill}
|
||||
\bigskip
|
||||
}
|
||||
\repeat
|
||||
|
||||
\bye
|
|
@ -40,8 +40,7 @@
|
|||
<li><a href="obalky.pdf">Obálky (PDF)</a></li>
|
||||
<li><a href="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 %}
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% block nadpis1a %}
|
||||
Obálkování {{ cislo }}
|
||||
{% endblock %}
|
||||
</h1>
|
||||
|
||||
Obálkovat se budou tyto problémy:
|
||||
<ul>
|
||||
{% for p in problemy %}
|
||||
|
||||
<li> {{ p.kod_v_rocniku }} {{ p }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{% for r in reseni %}
|
||||
{% ifchanged r.resitel %}
|
||||
{% if not forloop.first %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
<h4>{{ r.resitel }}</h4>
|
||||
<ul>
|
||||
{% endifchanged %}
|
||||
|
||||
<li>
|
||||
{{ r.problem.kod_v_rocniku }} {{ r.problem.nazev }} ({{ r.body }})
|
||||
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{% endblock content %}
|
|
@ -7,23 +7,11 @@
|
|||
{% endblock %}
|
||||
</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}} → {{z.ttitul}}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<h2>Od {{ from_cislo }} do prvního deadlinu {{ to_cislo }} (pro první číslo)</h2>
|
||||
<ul>
|
||||
{% for z in zmeny_posledni_prvni %}
|
||||
<li> {{z.jmeno}}: {{z.ftitul}} → {{z.ttitul}}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<h2>Od prvního deadlinu {{ from_cislo }} do {{ to_cislo }} (pro poslední číslo)</h2>
|
||||
<ul>
|
||||
{% for z in zmeny_prvni_posledni %}
|
||||
{% for z in zmeny %}
|
||||
<li> {{z.jmeno}}: {{z.ftitul}} → {{z.ttitul}}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% block nadpis1a %}
|
||||
Obálkování {{ cislo }}
|
||||
{% endblock %}
|
||||
</h1>
|
||||
<ul>
|
||||
{% for reseni in object_list %}
|
||||
{% ifchanged reseni.resitele %}
|
||||
{% if not forloop.first %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
<h4>{% for resitel in reseni.resitele.all %}{{resitel.osoba}},{% endfor %}</h4>
|
||||
<ul>
|
||||
{% endifchanged %}
|
||||
|
||||
<li>Celkem {{reseni.hodnoceni__body__sum}} bodů z {{reseni.hodnoceni__count}} hodnocení
|
||||
<ul>
|
||||
{% for h in reseni.hodnoceni_set.all %}
|
||||
<li> {{ h.problem }}: {{ h.body }}b </li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
|
||||
{% endblock content %}
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
<h1>
|
||||
{% block nadpis1a %}
|
||||
Průběžné výsledky {{ rocnik.rocnik }}. ročníku
|
||||
Výsledky {{ rocnik.rocnik }}. ročníku
|
||||
{% if vysledkovka.do_deadlinu %}k datu {{ vysledkovka.do_deadlinu.deadline.date }}{% endif %}
|
||||
{% endblock %}
|
||||
</h1>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()),
|
||||
|
|
|
@ -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
Normal file
37
soustredeni/templates/soustredeni/stvrzenky.tex
Normal file
|
@ -0,0 +1,37 @@
|
|||
{% autoescape off %}
|
||||
{% load static %}
|
||||
{% load tex %}
|
||||
\documentclass[11pt,a4paper]{article}
|
||||
\usepackage[left=0.75in, right=0.75in,top=0.5in,bottom=0.5in]{geometry}
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[czech]{babel}
|
||||
\usepackage{graphicx}
|
||||
\begin{document}
|
||||
\pagenumbering{gobble}
|
||||
\parindent=0pt
|
||||
|
||||
\def\stvrzenka#1#2{
|
||||
\vbox{%
|
||||
\includegraphics[width=2cm]{logomm.pdf}
|
||||
\smallskip\hrule\medskip
|
||||
{% with soustredeni as s %}
|
||||
Potvrzujeme, že #1 #2 se zúčastnil(a) soustředění Korespondenčního semináře M\&M konaného
|
||||
v~termínu {{s.datum_zacatku|date:"j.~n.~Y"|sloz}} --
|
||||
{{s.datum_konce|date:"j.~n.~Y"|sloz}} a~že zaplatil(a) účastnický poplatek ve
|
||||
výši $\sim${{castka|sloz}}Kč$\sim$.
|
||||
|
||||
\bigskip
|
||||
{{s.datum_zacatku|date:"j.~n.~Y"|sloz}}, {{s.misto|sloz}} \hfill Přijal(a): \hbox to 4cm{\hrulefill}
|
||||
\bigskip
|
||||
}
|
||||
{% endwith %}
|
||||
}
|
||||
|
||||
{% for u in ucastnici %}
|
||||
{% with o=u.osoba %}
|
||||
\stvrzenka{{o.jmeno|sloz}}{{o.prijmeni|sloz}}
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
\end{document}
|
||||
{% endautoescape %}
|
|
@ -1,5 +0,0 @@
|
|||
{% load tex %}
|
||||
\newcommand{\datum}{{datum|date:"j. n. Y"|sloz}}
|
||||
{% for u in ucastnici %}
|
||||
\stvrzenka{{u.cislo_stvrzenky|sloz}}{{u.jmeno|sloz}}{{u.prijmeni|sloz}}{{u.ulice|sloz}}{{u.psc|sloz}}{{u.mesto|sloz}}
|
||||
{% endfor %}
|
|
@ -1,8 +1,9 @@
|
|||
from django.shortcuts import get_object_or_404
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.http import HttpResponse
|
||||
from django.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.tex", "w") as texfile:
|
||||
texfile.write(tex.decode())
|
||||
|
||||
with open(tempdir / 'stvrzenky.pdf', 'rb') as pdffile:
|
||||
shutil.copy(find('images/logomm.pdf'), tempdir)
|
||||
subprocess.call(["pdflatex", "stvrzenky.tex"], cwd = tempdir, stdout=subprocess.DEVNULL)
|
||||
|
||||
with open(tempdir / "stvrzenky.pdf", "rb") as pdffile:
|
||||
response = HttpResponse(pdffile.read(), content_type='application/pdf')
|
||||
shutil.rmtree(tempdir)
|
||||
return response
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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…
Reference in a new issue