Merge branch 'data_migrations' of gimli.ms.mff.cuni.cz:/akce/mam/git/mamweb into data_migrations
This commit is contained in:
commit
f960853239
26 changed files with 382 additions and 64 deletions
|
@ -695,13 +695,13 @@
|
|||
"alias": null,
|
||||
"description": "",
|
||||
"hidden": false,
|
||||
"hint": "",
|
||||
"hint": "To, co ŘEŠITELÉ poslali",
|
||||
"inbreadcrumbs": true,
|
||||
"inmenu": true,
|
||||
"insitetree": true,
|
||||
"parent": 21,
|
||||
"sort_order": 37,
|
||||
"title": "Odevzdaná řešení",
|
||||
"sort_order": 38,
|
||||
"title": "Došlá řešení",
|
||||
"tree": 1,
|
||||
"url": "odevzdavatko_tabulka",
|
||||
"urlaspattern": true
|
||||
|
@ -724,7 +724,7 @@
|
|||
"inmenu": true,
|
||||
"insitetree": true,
|
||||
"parent": 21,
|
||||
"sort_order": 38,
|
||||
"sort_order": 42,
|
||||
"title": "Odhlásit se",
|
||||
"tree": 1,
|
||||
"url": "logout",
|
||||
|
@ -806,5 +806,31 @@
|
|||
},
|
||||
"model": "sitetree.treeitem",
|
||||
"pk": 41
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"access_guest": false,
|
||||
"access_loggedin": false,
|
||||
"access_perm_type": 1,
|
||||
"access_permissions": [
|
||||
2
|
||||
],
|
||||
"access_restricted": true,
|
||||
"alias": null,
|
||||
"description": "",
|
||||
"hidden": false,
|
||||
"hint": "To, co jsem JÁ odevzdal",
|
||||
"inbreadcrumbs": true,
|
||||
"inmenu": true,
|
||||
"insitetree": true,
|
||||
"parent": 21,
|
||||
"sort_order": 37,
|
||||
"title": "Odevzdaná řešení",
|
||||
"tree": 1,
|
||||
"url": "seminar_resitel_odevzdana_reseni",
|
||||
"urlaspattern": true
|
||||
},
|
||||
"model": "sitetree.treeitem",
|
||||
"pk": 42
|
||||
}
|
||||
]
|
|
@ -78,6 +78,7 @@ TEMPLATES = [
|
|||
'django.contrib.messages.context_processors.messages',
|
||||
'sekizai.context_processors.sekizai',
|
||||
'header_fotky.context_processors.vzhled',
|
||||
'various.context_processors.april',
|
||||
)
|
||||
},
|
||||
},
|
||||
|
@ -132,6 +133,7 @@ INSTALLED_APPS = (
|
|||
'korektury',
|
||||
'prednasky',
|
||||
'header_fotky',
|
||||
'various',
|
||||
|
||||
# Admin upravy:
|
||||
|
||||
|
|
|
@ -131,6 +131,48 @@
|
|||
$("a[rel^='gallery-image']").prettyPhoto(prettyparams);
|
||||
});
|
||||
</script>
|
||||
{% if april == 2021 %}
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
function rotace(vektor, uhel_deg) {
|
||||
var uhel = uhel_deg *(Math.PI / 180);
|
||||
var x = vektor[0];
|
||||
var y = vektor[1];
|
||||
return [x*Math.cos(uhel) - y*Math.sin(uhel), x*Math.sin(uhel) + y*Math.cos(uhel)];
|
||||
}
|
||||
|
||||
function rotace_a_posun(obj, uhel) {
|
||||
var ow = obj.width();
|
||||
var oh = obj.height();
|
||||
|
||||
var rohy = [[0,0], [0,oh], [ow, 0], [ow, oh]];
|
||||
var minx = 0;
|
||||
var miny = 0;
|
||||
for (var roh of rohy) {
|
||||
var otoceny = rotace(roh, uhel);
|
||||
if (otoceny[0] < minx) {
|
||||
minx = otoceny[0];
|
||||
}
|
||||
if (otoceny[1] < miny) {
|
||||
miny = otoceny[1];
|
||||
}
|
||||
}
|
||||
|
||||
miny *= -1;
|
||||
minx *= -1;
|
||||
|
||||
var transf_str = "translateX("+minx+"px) translateY("+miny+"px) rotate("+uhel+"deg)";
|
||||
obj.css('transform-origin', 'top left');
|
||||
obj.css('transform', transf_str);
|
||||
}
|
||||
|
||||
function randomUhel() {
|
||||
return Math.floor(360*Math.random());
|
||||
}
|
||||
|
||||
$('.container').css('margin', 0);
|
||||
rotace_a_posun($('.container'), randomUhel());
|
||||
</script>
|
||||
{% endif %}
|
||||
{% render_block "js" %}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -142,6 +142,28 @@ class PrihlaskaForm(forms.Form):
|
|||
elif data.get('skola_adresa')=='':
|
||||
self.add_error('skola_adresa',forms.ValidationError('Je nutné vyplnit adresu školy'))
|
||||
|
||||
# Editační formulář bez řešitele.
|
||||
class ProfileEditFormPoMaturite(forms.Form):
|
||||
username = forms.CharField(label='Přihlašovací jméno',
|
||||
max_length=256,
|
||||
required=True)
|
||||
|
||||
jmeno = forms.CharField(label='Jméno', max_length=256, required=True)
|
||||
prijmeni = forms.CharField(label='Příjmení', max_length=256, required=True)
|
||||
pohlavi_muz = forms.ChoiceField(label='Pohlaví',
|
||||
choices = ((True,'muž'),(False,'žena')), required=True)
|
||||
email = forms.EmailField(label='E-mail',max_length=256, required=True)
|
||||
telefon = forms.CharField(widget=TelInput(),label='Telefon',max_length=256, required=False)
|
||||
datum_narozeni = forms.DateField(widget=DateInput(),label='Datum narození', required=False)
|
||||
ulice = forms.CharField(label='Ulice', max_length=256, required=False)
|
||||
mesto = forms.CharField(label='Město', max_length=256, required=False)
|
||||
psc = forms.CharField(label='PSČ', max_length=32, required=False)
|
||||
stat = forms.ChoiceField(label='Stát',
|
||||
choices = (('CZ', 'Česká Republika'),
|
||||
('SK', 'Slovenská Republika'),
|
||||
('other', 'Jiné')),
|
||||
required=False)
|
||||
stat_text = forms.CharField(label='Stát', max_length=256, required=False)
|
||||
|
||||
class ProfileEditForm(forms.Form):
|
||||
username = forms.CharField(label='Přihlašovací jméno',
|
||||
|
@ -181,7 +203,7 @@ class ProfileEditForm(forms.Form):
|
|||
|
||||
rok_maturity = forms.IntegerField(
|
||||
label='Rok maturity',
|
||||
min_value=date.today().year,
|
||||
min_value=date.today().year,
|
||||
max_value=date.today().year+8,
|
||||
required=True)
|
||||
zasilat = forms.ChoiceField(label='Kam zasílat čísla a řešení',choices = Resitel.ZASILAT_CHOICES, required=True)
|
||||
|
|
|
@ -107,22 +107,50 @@ def spoj_k_organizatorum_osoby(apps, scema_editor):
|
|||
for org in Organizator.objects.all():
|
||||
|
||||
# Spárování organizátora s osobou
|
||||
# Myšlenka: Když najdeme řešitele pro daného uživatele, tak se vezme Osoba příslušná uživateli,
|
||||
# Pokud nenajdeme uživatele, tak ještě zkusíme dohledat Osobu podle e-mailu
|
||||
user = org.user
|
||||
resitele = Resitel.objects.filter(user=user)
|
||||
if resitele.count() != 0:
|
||||
osoba = resitele.first().osoba
|
||||
else:
|
||||
if user is None:
|
||||
logger.error(f'Org {org} nemá uživatele!')
|
||||
# Je to podezřelé, ale prostě vyrobíme novou osobu.
|
||||
osoba = Osoba(user=user)
|
||||
# Téhle osobě nejdou nastavit detaily, protože žádné nemáme.
|
||||
else:
|
||||
logger.info(f'Org {org.user.email}(ID: {org.id}) má uživatele {user}')
|
||||
# 💢💢💢 Python nemá goto, ale prý má výjimky… 💢💢💢
|
||||
class EndException(Exception): pass
|
||||
try:
|
||||
# Hledáme podle uživatele
|
||||
resitele = Resitel.objects.filter(user=user)
|
||||
if resitele.count() != 0 and user is not None:
|
||||
osoba = resitele.first().osoba
|
||||
logger.info(f'Našel jsem řešitele {resitele.first().email} podle uživatele, používám jeho Osobu')
|
||||
raise EndException
|
||||
|
||||
# Hledáme podle e-mailu
|
||||
osoby = Osoba.objects.filter(email__iexact=user.email)
|
||||
if osoby.count() != 0 and user.email != '':
|
||||
osoba = osoby.first()
|
||||
if osoba.user is None:
|
||||
osoba.user = user
|
||||
logger.info(f'Našel jsem Osobu {osoby.first().email} podle e-mailu')
|
||||
raise EndException
|
||||
|
||||
# Přesun informací z usera do osoby
|
||||
# pro řešitele již v minule migraci
|
||||
osoba.jmeno = user.first_name
|
||||
osoba.prijmeni = user.last_name
|
||||
osoba.email = user.email
|
||||
user.jmeno = "Použij osobu!"
|
||||
user.prijmeni = "Použij osobu!"
|
||||
user.email = "Použij osobu!"
|
||||
user.save()
|
||||
# Fallback
|
||||
logger.warning(f'Org neměl řešitele, zakládám novou Osobu.')
|
||||
osoba = Osoba(user=user)
|
||||
|
||||
# Přesun informací z usera do osoby
|
||||
# pro osoby z řešitelů (jediné dosud existující osoby) již v minule migraci
|
||||
osoba.jmeno = user.first_name
|
||||
osoba.prijmeni = user.last_name
|
||||
osoba.email = user.email
|
||||
user.jmeno = "Použij osobu!"
|
||||
user.prijmeni = "Použij osobu!"
|
||||
user.email = "Použij osobu!"
|
||||
user.save()
|
||||
|
||||
except EndException: pass
|
||||
|
||||
# Přesun informací z organizátora do jeho osoby
|
||||
osoba.prezdivka = org.prezdivka if org.prezdivka is not None else ''
|
||||
|
@ -142,7 +170,10 @@ def fix_problem(apps, schema_editor):
|
|||
else:
|
||||
pr.autor = None
|
||||
if pr.opravovatel is not None:
|
||||
pr.opravovatele.add(Organizator.objects.filter(osoba__user=pr.opravovatel).first())
|
||||
if Organizator.objects.filter(osoba__user=pr.opravovatel).first() is not None:
|
||||
pr.opravovatele.add(Organizator.objects.filter(osoba__user=pr.opravovatel).first())
|
||||
else:
|
||||
logger.error(f'WTF, nespárovaný opravovatel {pr.opravovatel} problému {pr}')
|
||||
pr.save()
|
||||
|
||||
def fix_pohadka(apps, schema_editor):
|
||||
|
|
|
@ -28,26 +28,23 @@ def resitel_to_osoba(apps,schema_editor):
|
|||
if u.first_name:
|
||||
if not o.jmeno:
|
||||
o.jmeno = u.first_name
|
||||
u.first_name = 'Použij osobu!'
|
||||
u.first_name += ' (Uživatel!)'
|
||||
elif o.jmeno == u.first_name:
|
||||
u.first_name = 'Použij osobu!'
|
||||
u.first_name += ' (Uživatel!)'
|
||||
else:
|
||||
raise ValueError('jmeno a first_name rozdílné: "{}" vs. "{}"'.format(o.jmeno, u.first_name))
|
||||
if u.last_name:
|
||||
if not o.prijmeni:
|
||||
o.prijmeni = u.last_name
|
||||
u.last_name = 'Použij osobu!'
|
||||
u.last_name += ' (Uživatel!)'
|
||||
elif o.prijmeni == u.last_name:
|
||||
u.last_name = 'Použij osobu!'
|
||||
u.last_name += ' (Uživatel!)'
|
||||
else:
|
||||
raise ValueError('prijmeni a last_name rozdílné: "{}" vs. "{}"'.format(o.prijmeni, u.last_name))
|
||||
if u.email:
|
||||
if not o.email:
|
||||
o.email = u.email
|
||||
u.email = 'Použij osobu!'
|
||||
elif o.email == u.email:
|
||||
u.email = 'Použij osobu!'
|
||||
else:
|
||||
elif o.email != u.email:
|
||||
raise ValueError('o.email a u.email rozdílné: "{}" vs. "{}"'.format(o.email, u.email))
|
||||
u.save()
|
||||
|
||||
|
|
|
@ -4,6 +4,10 @@ from __future__ import unicode_literals
|
|||
|
||||
from django.db import migrations
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def spoj_k_organizatorum_osoby(apps, scema_editor):
|
||||
Organizator = apps.get_model('seminar', 'Organizator')
|
||||
Resitel = apps.get_model('seminar', 'Resitel')
|
||||
|
@ -11,22 +15,49 @@ def spoj_k_organizatorum_osoby(apps, scema_editor):
|
|||
for org in Organizator.objects.all():
|
||||
|
||||
# Spárování organizátora s osobou
|
||||
# Myšlenka: Když najdeme řešitele pro daného uživatele, tak se vezme Osoba příslušná uživateli,
|
||||
# Pokud nenajdeme uživatele, tak ještě zkusíme dohledat Osobu podle e-mailu
|
||||
user = org.user
|
||||
resitele = Resitel.objects.filter(user=user)
|
||||
if resitele.count() != 0:
|
||||
osoba = resitele.first().osoba
|
||||
else:
|
||||
if user is None:
|
||||
logger.error(f'Org {org} nemá uživatele!')
|
||||
# Je to podezřelé, ale prostě vyrobíme novou osobu.
|
||||
osoba = Osoba(user=user)
|
||||
# Téhle osobě nejdou nastavit detaily, protože žádné nemáme.
|
||||
else:
|
||||
logger.info(f'Org {org.user.email}(ID: {org.id}) má uživatele {user}')
|
||||
# 💢💢💢 Python nemá goto, ale prý má výjimky… 💢💢💢
|
||||
class EndException(Exception): pass
|
||||
try:
|
||||
# Hledáme podle uživatele
|
||||
resitele = Resitel.objects.filter(user=user)
|
||||
if resitele.count() != 0 and user is not None:
|
||||
osoba = resitele.first().osoba
|
||||
logger.info(f'Našel jsem řešitele {resitele.first().email} podle uživatele, používám jeho Osobu')
|
||||
raise EndException
|
||||
|
||||
# Hledáme podle e-mailu
|
||||
osoby = Osoba.objects.filter(email__iexact=user.email)
|
||||
if osoby.count() != 0 and user.email != '':
|
||||
osoba = osoby.first()
|
||||
if osoba.user is None:
|
||||
osoba.user = user
|
||||
logger.info(f'Našel jsem Osobu {osoby.first().email} podle e-mailu')
|
||||
raise EndException
|
||||
|
||||
# Přesun informací z usera do osoby
|
||||
# pro řešitele již v minule migraci
|
||||
osoba.jmeno = user.first_name
|
||||
osoba.prijmeni = user.last_name
|
||||
osoba.email = user.email
|
||||
user.jmeno = "Použij osobu!"
|
||||
user.prijmeni = "Použij osobu!"
|
||||
user.email = "Použij osobu!"
|
||||
user.save()
|
||||
# Fallback
|
||||
logger.warning(f'Org neměl řešitele, zakládám novou Osobu.')
|
||||
osoba = Osoba(user=user)
|
||||
|
||||
# Přesun informací z usera do osoby
|
||||
# pro osoby z řešitelů (jediné dosud existující osoby) již v minule migraci
|
||||
osoba.jmeno = user.first_name
|
||||
osoba.prijmeni = user.last_name
|
||||
osoba.email = user.email
|
||||
user.jmeno += " (Uživatel!)"
|
||||
user.prijmeni += " (Uživatel!)"
|
||||
user.save()
|
||||
|
||||
except EndException: pass
|
||||
|
||||
# Přesun informací z organizátora do jeho osoby
|
||||
osoba.prezdivka = org.prezdivka if org.prezdivka is not None else ''
|
||||
|
@ -46,7 +77,10 @@ def fix_problem(apps, schema_editor):
|
|||
else:
|
||||
pr.autor = None
|
||||
if pr.opravovatel is not None:
|
||||
pr.opravovatele.add(Organizator.objects.filter(osoba__user=pr.opravovatel).first())
|
||||
if Organizator.objects.filter(osoba__user=pr.opravovatel).first() is not None:
|
||||
pr.opravovatele.add(Organizator.objects.filter(osoba__user=pr.opravovatel).first())
|
||||
else:
|
||||
logger.error(f'WTF, nespárovaný opravovatel {pr.opravovatel} problému {pr}')
|
||||
pr.save()
|
||||
|
||||
def fix_pohadka(apps, schema_editor):
|
||||
|
|
|
@ -602,13 +602,18 @@ class Cislo(SeminarModelBase):
|
|||
png_filename = pathlib.Path(tempfile.mkdtemp(), 'nahled.png')
|
||||
|
||||
subprocess.run([
|
||||
"convert",
|
||||
"-density", "300x300",
|
||||
"-geometry", "{}x{}".format(VYSKA, sirka),
|
||||
"-background", "white",
|
||||
"-flatten",
|
||||
"{}[0]".format(self.pdf.path), # titulní strana
|
||||
png_filename
|
||||
"gs",
|
||||
"-sstdout=%stderr",
|
||||
"-dSAFER",
|
||||
"-dNOPAUSE",
|
||||
"-dBATCH",
|
||||
"-dNOPROMPT",
|
||||
"-sDEVICE=pngalpha",
|
||||
"-r{}x{}".format(VYSKA, sirka),
|
||||
"-dFirstPage=1d",
|
||||
"-dLastPage=1d",
|
||||
"-sOutputFile=" + str(png_filename),
|
||||
"-f%s" % self.pdf.path
|
||||
],
|
||||
check=True,
|
||||
capture_output=True
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% load deadliny %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
|
@ -62,7 +63,9 @@ $(document).ready(function(){
|
|||
<p>Řešitelé: {{ object.resitele.all | join:", " }}</p>
|
||||
|
||||
{# https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.get_FOO_display #}
|
||||
<p>Forma: {{ object.get_forma_display }}, doručeno {{ object.cas_doruceni }}</p>
|
||||
<p>Forma: {{ object.get_forma_display }}</p>
|
||||
|
||||
<p>Doručeno {{ object.cas_doruceni }}, deadline: {{object.cas_doruceni | deadline_html }}</p>
|
||||
|
||||
{# Soubory: #}
|
||||
<h3>Přílohy:</h3>
|
||||
|
|
38
seminar/templates/seminar/odevzdavatko/resitel_prehled.html
Normal file
38
seminar/templates/seminar/odevzdavatko/resitel_prehled.html
Normal file
|
@ -0,0 +1,38 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% load deadliny %}
|
||||
|
||||
{% block custom_css %}
|
||||
<style type=text/css>
|
||||
.dosla_reseni tr th {
|
||||
text-align: center;
|
||||
}
|
||||
.dosla_reseni tr th, .dosla_reseni tr td {
|
||||
border: 1px solid black;
|
||||
padding: 1px 10px 1px 10px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
</style>
|
||||
{% endblock custom_css %}
|
||||
|
||||
{% block content %}
|
||||
{% for rocnik, hodnoceni in podle_rocniku %}
|
||||
<h1>Ročník {{ rocnik }}</h1>
|
||||
<table class="dosla_reseni">
|
||||
<tr>
|
||||
<th>Doručeno</th>
|
||||
<th>Problém</th>
|
||||
<th>Body</th>
|
||||
<th>Deadline</th>
|
||||
</tr>
|
||||
{% for hodn in hodnoceni %}
|
||||
<tr>
|
||||
<td>{{ hodn.reseni.cas_doruceni }}</td>
|
||||
<td>{{ hodn.problem }}</td>
|
||||
<td>{{ hodn.body|default_if_none:"---" }}</td>
|
||||
<td>{{ hodn.reseni.cas_doruceni | deadline_html }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
|
@ -1,11 +1,15 @@
|
|||
{% extends "base.html" %}
|
||||
{% load deadliny %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% for dl, mnozina_reseni in reseni_podle_deadlinu.items %}
|
||||
<h1>{{ dl.2 | deadline_html }}</h1>
|
||||
<ul>
|
||||
{% for obj in object_list %}
|
||||
{% for obj in mnozina_reseni %}
|
||||
<li><a href="{% url 'odevzdavatko_detail_reseni' pk=obj.id %}">{{ obj }}</a> ({{ obj.get_forma_display }} {{ obj.cas_doruceni }})
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -61,6 +61,7 @@
|
|||
{% include "seminar/profil/prihlaska_field.html" with field=form.stat_text id="id_li_stat_text"%}
|
||||
</table>
|
||||
|
||||
{% if not po_maturite %} {# Vysloužilým účastníkům skrýt editaci školy apod. #}
|
||||
<hr>
|
||||
|
||||
<h4>
|
||||
|
@ -95,6 +96,7 @@
|
|||
</table>
|
||||
|
||||
<hr>
|
||||
{% endif %}
|
||||
|
||||
<input type="submit" value="Změnit">
|
||||
</form>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
<a href="{% url 'logout' %}">Odhlásit se</a><br>
|
||||
<a href="{% url 'seminar_resitel_edit' %}">Upravit údaje</a><br>
|
||||
<a href="{% url 'seminar_resitel_odevzdana_reseni' %}">Odevzdaná řešení</a><br>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
|
35
seminar/templatetags/deadliny.py
Normal file
35
seminar/templatetags/deadliny.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
from django import template
|
||||
from django.utils.safestring import mark_safe
|
||||
from seminar.utils import TypDeadline, deadline
|
||||
register = template.Library()
|
||||
|
||||
@register.filter(name='deadline')
|
||||
def deadline_text(datum):
|
||||
typ, cislo, dl = deadline(datum)
|
||||
strings = {
|
||||
TypDeadline.PredDeadline: f"1. deadline čísla {cislo} ({dl})",
|
||||
TypDeadline.SousDeadline: f"Soustřeďkový deadline čísla {cislo} ({dl})",
|
||||
TypDeadline.FinalDeadline: f"Finální deadline čísla {cislo} ({dl})",
|
||||
}
|
||||
return strings[typ]
|
||||
|
||||
@register.filter(name='deadline_kratseji')
|
||||
def deadline_kratsi_text(datum):
|
||||
typ, cislo, dl = deadline(datum)
|
||||
strings = {
|
||||
TypDeadline.PredDeadline: f"1. deadline {cislo}",
|
||||
TypDeadline.SousDeadline: f"Soustřeďkový deadline {cislo}",
|
||||
TypDeadline.FinalDeadline: f"Finální deadline {cislo}",
|
||||
}
|
||||
return strings[typ]
|
||||
|
||||
@register.filter(name='deadline_html')
|
||||
def deadline_html(datum):
|
||||
typ, _, _ = deadline(datum)
|
||||
text = deadline_kratsi_text(datum)
|
||||
classes = {
|
||||
TypDeadline.PredDeadline: 'preddeadline',
|
||||
TypDeadline.SousDeadline: 'sous_deadline',
|
||||
TypDeadline.FinalDeadline: 'final_deadline',
|
||||
}
|
||||
return mark_safe(f'<span class="{classes[typ]}">{text}</span>')
|
|
@ -135,6 +135,7 @@ urlpatterns = [
|
|||
path('prihlasit/', views.LoginView.as_view(), name='login'),
|
||||
path('odhlasit/', views.LogoutView.as_view(), name='logout'),
|
||||
path('resitel/', resitel_required(views.ResitelView.as_view()), name='seminar_resitel'),
|
||||
path('resitel/odevzdana_reseni/', resitel_required(views.PrehledOdevzdanychReseni.as_view()), name='seminar_resitel_odevzdana_reseni'),
|
||||
path('reset-hesla/', views.PasswordResetView.as_view(), name='reset_password'),
|
||||
path('zmena-hesla/', views.PasswordChangeView.as_view(), name='change_password'),
|
||||
path('reset-hesla/2/', views.PasswordResetDoneView.as_view(), name='reset_password_done'),
|
||||
|
|
|
@ -309,7 +309,7 @@ def deadline_v_rocniku(datum, rocnik):
|
|||
deadliny.append((TypDeadline.FinalDeadline, c, c.datum_deadline))
|
||||
deadliny = sorted(deadliny, key=lambda x: x[2]) # podle data
|
||||
for dl in deadliny:
|
||||
if datum <= dl:
|
||||
if datum <= dl[2]:
|
||||
# První takový deadline je ten nejtěsnější
|
||||
return dl
|
||||
|
||||
|
@ -319,20 +319,22 @@ def deadline(datum):
|
|||
Vrací trojici (TypDeadline, Cislo, datumDeadline: date).
|
||||
"""
|
||||
|
||||
if isinstance(datum, datetime.datetime):
|
||||
datum = datum.date()
|
||||
rok = datum.year
|
||||
# Dva ročníky podezřelé z obsahování dat
|
||||
pozdejsi_rocnik = m.Rocnik.filter(prvni_rok=rok)
|
||||
drivejsi_rocnik = m.Rocnik.filter(druhy_rok=rok)
|
||||
if any(
|
||||
pozdejsi_rocnik = m.Rocnik.objects.filter(prvni_rok=rok)
|
||||
drivejsi_rocnik = m.Rocnik.objects.filter(prvni_rok=rok-1)
|
||||
if any([
|
||||
pozdejsi_rocnik.count() > 1,
|
||||
drivejsi_rocnik.count() > 1,
|
||||
):
|
||||
]):
|
||||
raise ValueError(f"Více ročníků začíná/končí stejným rokem: {rok}")
|
||||
pozdejsi_rocnik = pozdejsi_rocnik.first() if pozdejsi_rocnik.count() > 0 else None
|
||||
drivejsi_rocnik = drivejsi_rocnik.first() if drivejsi_rocnik.count() > 0 else None
|
||||
|
||||
# Předpokládáme, že neexistuje číslo, které má deadline ale nemá finální deadline.
|
||||
posledni_deadline_drivejsiho_rocniku = m.Cislo.objects.get(rocnik=drivejsi_rocnik, datum_deadline__isnull=False).datum_deadline
|
||||
posledni_deadline_drivejsiho_rocniku = m.Cislo.objects.filter(rocnik=drivejsi_rocnik, datum_deadline__isnull=False).last().datum_deadline
|
||||
|
||||
if datum <= posledni_deadline_drivejsiho_rocniku:
|
||||
return deadline_v_rocniku(datum, drivejsi_rocnik)
|
||||
|
|
|
@ -8,12 +8,13 @@ from django.db import transaction
|
|||
|
||||
from dataclasses import dataclass
|
||||
import datetime
|
||||
from itertools import groupby
|
||||
import logging
|
||||
|
||||
import seminar.models as m
|
||||
import seminar.forms as f
|
||||
from seminar.forms import OdevzdavatkoTabulkaFiltrForm as FiltrForm
|
||||
from seminar.utils import aktivniResitele, resi_v_rocniku
|
||||
from seminar.utils import aktivniResitele, resi_v_rocniku, deadline
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -24,6 +25,8 @@ logger = logging.getLogger(__name__)
|
|||
# - ReseniProblemuView
|
||||
# - Detail konkrétního řešení -- všechny soubory, datum, ...
|
||||
# - DetailReseniView
|
||||
# - Pro řešitele: přehled jejich odevzdaných řešení
|
||||
# - PrehledOdevzdanychReseni
|
||||
#
|
||||
# Taky se může hodit:
|
||||
# - Tabulka všech řešitelů x všech problémů?
|
||||
|
@ -169,7 +172,14 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi
|
|||
return redirect(reverse("odevzdavatko_detail_reseni", kwargs={"pk": jedine_reseni.id}))
|
||||
context = self.get_context_data()
|
||||
return self.render_to_response(context)
|
||||
# Kontext automaticky?
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
ctx = super().get_context_data(*args, **kwargs)
|
||||
# XXX: Předat groupby do template nejde: https://stackoverflow.com/questions/6906593/itertools-groupby-in-a-django-template
|
||||
# Django má {% regroup %}, ale ten potřebuje, aby klíč byl atribut položky: https://docs.djangoproject.com/en/3.2/ref/templates/builtins/#regroup
|
||||
# Takže rozbalíme groupby do slovníku klíč → seznam sami (dictionary comphrehension)
|
||||
ctx['reseni_podle_deadlinu'] = {k: list(v) for k,v in groupby(ctx['object_list'], lambda r: deadline(r.cas_doruceni))}
|
||||
return ctx
|
||||
|
||||
## XXX: https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#avoid-anything-more-complex
|
||||
class DetailReseniView(DetailView):
|
||||
|
@ -230,6 +240,29 @@ def hodnoceniReseniView(request, pk, *args, **kwargs):
|
|||
return redirect(success_url)
|
||||
|
||||
|
||||
class PrehledOdevzdanychReseni(ListView):
|
||||
model = m.Hodnoceni
|
||||
template_name = 'seminar/odevzdavatko/resitel_prehled.html'
|
||||
|
||||
def get_queryset(self):
|
||||
if not self.request.user.is_authenticated:
|
||||
raise RuntimeError("Uživatel měl být přihlášený!")
|
||||
resitel = m.Resitel.objects.get(osoba__user=self.request.user)
|
||||
qs = super().get_queryset()
|
||||
qs = qs.filter(reseni__resitele__in=[resitel])
|
||||
return qs
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
ctx = super().get_context_data(*args, **kwargs)
|
||||
# Ročník určujeme podle čísla, do jehož deadlinu došlo řešení.
|
||||
# Chceme to mít seřazené, takže místo comphrerehsion ručně postavíme pole polí. Django templates neumí použít OrderedDict :-/
|
||||
podle_rocniku = []
|
||||
for rocnik, hodnoceni in groupby(ctx['object_list'], lambda ho: deadline(ho.reseni.cas_doruceni)[1].rocnik):
|
||||
podle_rocniku.append((rocnik, list(hodnoceni)))
|
||||
ctx['podle_rocniku'] = reversed(podle_rocniku) # Od nejnovějšího ročníku
|
||||
# TODO: Umožnit stažení / zobrazení řešení
|
||||
return ctx
|
||||
|
||||
# Přehled všech řešení kvůli debugování
|
||||
|
||||
class SeznamReseniView(ListView):
|
||||
|
|
|
@ -26,7 +26,7 @@ import seminar.models as m
|
|||
from seminar.models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici, Pohadka, Tema, Clanek, Osoba, Skola # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci
|
||||
#from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva
|
||||
from seminar import utils, treelib
|
||||
from seminar.forms import PrihlaskaForm, LoginForm, ProfileEditForm
|
||||
from seminar.forms import PrihlaskaForm, LoginForm, ProfileEditForm, ProfileEditFormPoMaturite
|
||||
import seminar.forms as f
|
||||
import seminar.templatetags.treenodes as tnltt
|
||||
import seminar.views.views_rest as vr
|
||||
|
@ -1095,15 +1095,23 @@ def resitelEditView(request):
|
|||
user_edit = osoba_edit.user
|
||||
## Vytvoření slovníku, kterým předvyplním formulář
|
||||
prefill_1=model_to_dict(user_edit)
|
||||
if resitel_edit:
|
||||
if resitel_edit and resitel_edit.rok_maturity >= date.today().year:
|
||||
prefill_2=model_to_dict(resitel_edit)
|
||||
prefill_1.update(prefill_2)
|
||||
prefill_3=model_to_dict(osoba_edit)
|
||||
prefill_1.update(prefill_3)
|
||||
form = ProfileEditForm(initial=prefill_1)
|
||||
if 'datum_narozeni' in prefill_1:
|
||||
prefill_1['datum_narozeni'] = str(prefill_1['datum_narozeni'])
|
||||
if resitel_edit and resitel_edit.rok_maturity < date.today().year:
|
||||
form = ProfileEditFormPoMaturite(initial=prefill_1)
|
||||
else:
|
||||
form = ProfileEditForm(initial=prefill_1)
|
||||
## Změna údajů a jejich uložení
|
||||
if request.method == 'POST':
|
||||
form = ProfileEditForm(request.POST)
|
||||
if resitel_edit and resitel_edit.rok_maturity < date.today().year:
|
||||
form = ProfileEditFormPoMaturite(request.POST)
|
||||
else:
|
||||
form = ProfileEditForm(request.POST)
|
||||
if form.is_valid():
|
||||
## Změny v osobě
|
||||
fcd = form.cleaned_data
|
||||
|
@ -1115,6 +1123,7 @@ def resitelEditView(request):
|
|||
osoba_edit.ulice = fcd['ulice']
|
||||
osoba_edit.mesto = fcd['mesto']
|
||||
osoba_edit.psc = fcd['psc']
|
||||
osoba_edit.datum_narozeni = fcd['datum_narozeni']
|
||||
## Změny v osobě s podmínkami
|
||||
if fcd.get('spam',False):
|
||||
osoba_edit.datum_souhlasu_zasilani = date.today()
|
||||
|
@ -1124,7 +1133,7 @@ def resitelEditView(request):
|
|||
## Neznámá země
|
||||
msg = "Unknown country {}".format(fcd['stat_text'])
|
||||
|
||||
if resitel_edit:
|
||||
if resitel_edit and resitel_edit.rok_maturity >= date.today().year:
|
||||
## Změny v řešiteli
|
||||
resitel_edit.skola = fcd['skola']
|
||||
resitel_edit.rok_maturity = fcd['rok_maturity']
|
||||
|
@ -1140,7 +1149,7 @@ def resitelEditView(request):
|
|||
return formularOKView(request)
|
||||
else:
|
||||
## Stránka před odeslaním formuláře = předvyplněný formulář
|
||||
return render(request, 'seminar/profil/edit.html', {'form': form})
|
||||
return render(request, 'seminar/profil/edit.html', {'form': form, 'po_maturite': resitel_edit and resitel_edit.rok_maturity < date.today().year})
|
||||
|
||||
def prihlaskaView(request):
|
||||
generic_logger = logging.getLogger('seminar.prihlaska')
|
||||
|
|
0
various/__init__.py
Normal file
0
various/__init__.py
Normal file
3
various/admin.py
Normal file
3
various/admin.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
5
various/apps.py
Normal file
5
various/apps.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class VariousConfig(AppConfig):
|
||||
name = 'various'
|
14
various/context_processors.py
Normal file
14
various/context_processors.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
def april(req):
|
||||
if 'X-April' in req.headers:
|
||||
try:
|
||||
year = int(req.headers['X-April'])
|
||||
return {'april': year}
|
||||
except:
|
||||
pass # Fall-back to regular behaviour
|
||||
|
||||
import datetime
|
||||
today = datetime.date.today()
|
||||
if today.day == 1 and today.month == 4:
|
||||
return {'april': today.year}
|
||||
return {}
|
||||
|
0
various/migrations/__init__.py
Normal file
0
various/migrations/__init__.py
Normal file
3
various/models.py
Normal file
3
various/models.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
3
various/tests.py
Normal file
3
various/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
3
various/views.py
Normal file
3
various/views.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
Loading…
Reference in a new issue