Browse Source

Merge branch 'data_migrations' into test

middleware_test
Jonas Havelka 4 years ago
parent
commit
f7a234b0c6
  1. 34
      data/sitetree.json
  2. 1
      mamweb/settings_common.py
  3. 8
      mamweb/static/css/mamweb.css
  4. BIN
      mamweb/static/images/MSMT_logo_bez_textu_black.eps
  5. 354
      mamweb/static/images/mozaika-footer.svg
  6. 8
      mamweb/templates/graph.svg
  7. 5
      seminar/export.py
  8. 37
      seminar/forms.py
  9. 14
      seminar/models.py
  10. 16
      seminar/templates/seminar/odevzdavatko/resitel_prehled.html
  11. 2
      seminar/templates/seminar/profil/edit.html
  12. 4
      seminar/templates/seminar/profil/login.html
  13. 21
      seminar/templatetags/deadliny.py
  14. 3
      seminar/test_deadlines.py
  15. 3
      seminar/urls.py
  16. 14
      seminar/utils.py
  17. 2
      seminar/views/odevzdavatko.py
  18. 47
      seminar/views/views_all.py

34
data/sitetree.json

@ -334,7 +334,7 @@
"inmenu": true,
"insitetree": true,
"parent": 3,
"sort_order": 33,
"sort_order": 43,
"title": "Výsledková listina",
"tree": 1,
"url": "seminar_aktualni_vysledky",
@ -695,13 +695,13 @@
"alias": null,
"description": "",
"hidden": false,
"hint": "To, co ŘEŠITELÉ poslali",
"hint": "",
"inbreadcrumbs": true,
"inmenu": true,
"insitetree": true,
"parent": 21,
"sort_order": 38,
"title": "Došlá řešení",
"sort_order": 37,
"title": "Odevzdaná řešení",
"tree": 1,
"url": "odevzdavatko_tabulka",
"urlaspattern": true
@ -724,7 +724,7 @@
"inmenu": true,
"insitetree": true,
"parent": 21,
"sort_order": 42,
"sort_order": 38,
"title": "Odhlásit se",
"tree": 1,
"url": "logout",
@ -832,5 +832,29 @@
},
"model": "sitetree.treeitem",
"pk": 42
},
{
"fields": {
"access_guest": false,
"access_loggedin": false,
"access_perm_type": 1,
"access_permissions": [],
"access_restricted": false,
"alias": null,
"description": "",
"hidden": false,
"hint": "",
"inbreadcrumbs": true,
"inmenu": true,
"insitetree": true,
"parent": 3,
"sort_order": 33,
"title": "Aktuální ročník",
"tree": 1,
"url": "seminar_aktualni_rocnik",
"urlaspattern": true
},
"model": "sitetree.treeitem",
"pk": 43
}
]

1
mamweb/settings_common.py

@ -44,6 +44,7 @@ STATICFILES_FINDERS = (
# Where redirect for login required services
LOGIN_URL = 'login'
LOGIN_REDIRECT_URL = 'profil'
# Modules configuration

8
mamweb/static/css/mamweb.css

@ -1149,11 +1149,13 @@ div.gdpr {
}
/* tabulka odevzdaných řešení */
.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;
text-align: center;
}
.dosla_reseni tr td#problem {
text-align: left;
}

BIN
mamweb/static/images/MSMT_logo_bez_textu_black.eps

Binary file not shown.

354
mamweb/static/images/mozaika-footer.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 128 KiB

8
mamweb/templates/graph.svg

@ -409,7 +409,7 @@
</g>
</a>
<a
href="/aktualni/temata/"
href="/aktualni/zadani/"
id="temata"
transform="matrix(0.70138313,0,0,0.7462289,-192.38886,20.298351)">
<g
@ -439,7 +439,7 @@
</g>
</a>
<a
href="/aktualni/temata/"
href="/aktualni/zadani/"
id="a74"
transform="matrix(0.70138313,0,0,0.7462289,-192.38886,20.298351)">
<g
@ -469,7 +469,7 @@
</g>
</a>
<a
href="/aktualni/temata/"
href="/aktualni/zadani/"
id="a80"
transform="matrix(0.70138313,0,0,0.7462289,-192.38886,20.298351)">
<g
@ -499,7 +499,7 @@
</g>
</a>
<a
href="/aktualni/temata/"
href="/aktualni/zadani/"
id="a86"
transform="matrix(0.70138313,0,0,0.7462289,-192.38886,20.298351)">
<g

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

5
seminar/export.py

@ -9,6 +9,7 @@ from .models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni
#from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva
from .ovvpfile import OvvpFile
from seminar import views
from seminar.views import vysledkovka
from seminar.utils import aktivniResitele
class ExportIndexView(generic.View):
@ -78,8 +79,8 @@ class ExportRocnikView(generic.View):
rocnik = get_object_or_404(Rocnik, prvni_rok=pr, exportovat=True)
cislo = rocnik.posledni_zverejnena_vysledkovka_cislo()
resitele = aktivniResitele(cislo, True)
slovnik_body = views.secti_body_za_rocnik(cislo, resitele)
_, setrizeni_resitele, setrizene_body = views.setrid_resitele_a_body(slovnik_body)
slovnik_body = vysledkovka.secti_body_za_rocnik(cislo, resitele, False)
_, setrizeni_resitele, body = vysledkovka.setrid_resitele_a_body(slovnik_body)
of = default_ovvpfile('MaM.rocnik', rocnik)
of.headers['comment'] = u'MaM-Web export aktivnich resitelu rocniku {rocnik} do cisla {cislo}'.format(rocnik=rocnik, cislo=cislo)

37
seminar/forms.py

@ -142,28 +142,6 @@ 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',
@ -203,7 +181,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)
@ -255,6 +233,11 @@ class ProfileEditForm(forms.Form):
# elif data.get('skola_adresa')=='':
# self.add_error('skola_adresa',forms.ValidationError('Je nutné vyplnit adresu školy'))
class PoMaturiteProfileEditForm(ProfileEditForm):
rok_maturity = forms.IntegerField(
label='Rok maturity',
required=True)
class VlozReseniForm(forms.Form):
#FIXME jen podproblémy daného problému
problem = forms.ModelChoiceField(label='Problém',queryset=m.Problem.objects.all())
@ -284,16 +267,16 @@ class VlozReseniForm(forms.Form):
#forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False,
# default=FORMA_EMAIL)
poznamka = forms.CharField(label='Neveřejná poznámka')
poznamka = forms.CharField(label='Neveřejná poznámka', required=False)
#poznamka = models.TextField('neveřejná poznámka', blank=True,
# help_text='Neveřejná poznámka k řešení (plain text)')
#TODO body do cisla
#TODO prilohy
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
#self.fields['favorite_color'] = forms.ChoiceField(choices=[(color.id, color.name) for color in Resitel.objects.all()])
##def __init__(self, *args, **kwargs):
## super().__init__(*args, **kwargs)
## #self.fields['favorite_color'] = forms.ChoiceField(choices=[(color.id, color.name) for color in Resitel.objects.all()])
class NahrajReseniForm(forms.ModelForm):
class Meta:

14
seminar/models.py

@ -525,17 +525,17 @@ class Cislo(SeminarModelBase):
datum_vydani = models.DateField('datum vydání', blank=True, null=True,
help_text='Datum vydání finální verze')
datum_deadline = models.DateField('datum deadline', blank=True, null=True,
help_text='Datum pro příjem řešení úloh zadaných v tomto čísle')
datum_preddeadline = models.DateField('datum předdeadline', blank=True, null=True,
help_text='Datum pro příjem řešení, která se otisknou v dalším čísle')
datum_deadline_soustredeni = models.DateField(
'datum deadline soustředění',
blank=True, null=True,
help_text='Datum pro příjem řešení pro účast na soustředění')
datum_preddeadline = models.DateField('datum předdeadline', blank=True, null=True,
help_text='Datum pro příjem řešení, která se otisknou v dalším čísle')
datum_deadline = models.DateField('datum deadline', blank=True, null=True,
help_text='Datum pro příjem řešení úloh zadaných v tomto čísle')
verejne_db = models.BooleanField('číslo zveřejněno',
db_column='verejne', default=False)

16
seminar/templates/seminar/odevzdavatko/resitel_prehled.html

@ -3,6 +3,15 @@
{% load deadliny %}
{% block content %}
<h3>Označení deadlinů</h3>
<ul>
<li>Ⓢ deadline pro účast na soustředění</li>
<li>♲ 1. deadline</li>
<li>✓ 2. deadline</li>
</ul>
<br>
{% for rocnik, hodnoceni in podle_rocniku %}
<h1>Ročník {{ rocnik }}</h1>
<table class="dosla_reseni">
@ -14,12 +23,15 @@
</tr>
{% for hodn in hodnoceni %}
<tr>
<td>{{ hodn.reseni.cas_doruceni }}</td>
<td>{{ hodn.problem }}</td>
<td>{{ hodn.reseni.cas_doruceni | date:"d.m.Y H:i"}}</td>
<td id="problem"><span title="{{ hodn.problem.nazev }}">{{ hodn.problem.nazev | zkrat_nazev_problemu }}</span></td>
<td>{{ hodn.body|default_if_none:"---" }}</td>
<td>{{ hodn.reseni.cas_doruceni | deadline_html }}</td>
</tr>
{% endfor %}
</table>
<br>
{% endfor %}
{% endblock %}

2
seminar/templates/seminar/profil/edit.html

@ -61,7 +61,6 @@
{% 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>
@ -96,7 +95,6 @@
</table>
<hr>
{% endif %}
<input type="submit" value="Změnit">
</form>

4
seminar/templates/seminar/profil/login.html

@ -8,6 +8,10 @@
Přihlášení
{% endblock %}{% endblock %}
</h1>
{# Obšlehnuto z Admina :-) #}
{% if user.is_authenticated %}
<p>K této stránce nejspíš nemáte přístup. Můžete se zkusit přihlásit jako uživatel, který přístup má.</p>
{% endif %}
<form action="{% url 'login' %}" method="post">
{% csrf_token %}
<table class="form">

21
seminar/templatetags/deadliny.py

@ -5,6 +5,8 @@ register = template.Library()
@register.filter(name='deadline')
def deadline_text(datum):
if deadline(datum) is None:
return 'Neznámý deadline'
typ, cislo, dl = deadline(datum)
strings = {
TypDeadline.PredDeadline: f"1. deadline čísla {cislo} ({dl})",
@ -15,16 +17,20 @@ def deadline_text(datum):
@register.filter(name='deadline_kratseji')
def deadline_kratsi_text(datum):
if deadline(datum) is None:
return 'NONE'
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}",
TypDeadline.PredDeadline: f"{cislo}",
TypDeadline.SousDeadline: f"{cislo}",
TypDeadline.FinalDeadline: f"{cislo}",
}
return strings[typ]
@register.filter(name='deadline_html')
def deadline_html(datum):
if deadline(datum) is None:
return 'Neznámý deadline'
typ, _, _ = deadline(datum)
text = deadline_kratsi_text(datum)
classes = {
@ -33,3 +39,12 @@ def deadline_html(datum):
TypDeadline.FinalDeadline: 'final_deadline',
}
return mark_safe(f'<span class="{classes[typ]}">{text}</span>')
@register.filter(name='zkrat_nazev_problemu')
def zkrat_nazev_problemu(nazev):
if len(nazev) > 10:
if nazev[9] == " ":
nazev = nazev[:9] + "..."
else:
nazev = nazev[:10] + "..."
return nazev

3
seminar/test_deadlines.py

@ -144,3 +144,6 @@ class DeadlineTestCase(TestCase):
def test_deadline_pro_datetime(self):
"""Testuje, že i pro datetime dostáváme správné deadliny"""
self.skipTest('Chybí implementace testu')
def test_moc_pozdni_deadline(self):
self.assertIsNone(deadline(date.max))

3
seminar/urls.py

@ -61,6 +61,7 @@ urlpatterns = [
path('aktualni/zadani/', views.AktualniZadaniView, name='seminar_aktualni_zadani'),
#path('aktualni/temata/', views.ZadaniTemataView, name='seminar_temata'),
path('aktualni/vysledkova-listina/', views.ZadaniAktualniVysledkovkaView, name='seminar_aktualni_vysledky'),
path('aktualni/rocnik/', views.AktualniRocnikRedirectView.as_view(), name='seminar_aktualni_rocnik'),
path('stare-novinky/', views.StareNovinkyView.as_view(), name='stare_novinky'),
# Clanky
@ -133,7 +134,9 @@ urlpatterns = [
path('prihlaska/',views.prihlaskaView, name='seminar_prihlaska'),
path('prihlasit/', views.LoginView.as_view(), name='login'),
path('login/', RedirectView.as_view(pattern_name='login', permanent=True, query_string=True)),
path('odhlasit/', views.LogoutView.as_view(), name='logout'),
path('logout/', RedirectView.as_view(pattern_name='login', permanent=True, query_string=True)),
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'),

14
seminar/utils.py

@ -14,11 +14,15 @@ from django.core.exceptions import ObjectDoesNotExist
from enum import Enum
from enum import auto
import logging
import seminar.models as m
import seminar.treelib as t
org_required = permission_required('auth.org', raise_exception=True)
resitel_required = permission_required('auth.resitel', raise_exception=True)
logger = logging.getLogger(__name__)
org_required = permission_required('auth.org')
resitel_required = permission_required('auth.resitel')
User = get_user_model()
# Není to úplně hezké, ale budeme doufat, že to je funkční...
User.je_org = property(lambda self: self.has_perm('auth.org'))
@ -312,11 +316,12 @@ def deadline_v_rocniku(datum, rocnik):
if datum <= dl[2]:
# První takový deadline je ten nejtěsnější
return dl
logger.error(f'Pro datum {datum} v ročníku {rocnik} neexistuje deadline.')
def deadline(datum):
"""Funkce pro dohledání, ke kterému deadlinu se datum váže.
Vrací trojici (TypDeadline, Cislo, datumDeadline: date).
Vrací trojici (TypDeadline, Cislo, datumDeadline: date). Pokud se deadline nenajde, vrátí None
"""
if isinstance(datum, datetime.datetime):
@ -338,9 +343,12 @@ def deadline(datum):
# Seznam čísel je potřeba ručně setřídit chronologicky, protože Model říká, že se řadí od nejnovějšího
posledni_deadline_drivejsiho_rocniku = m.Cislo.objects.filter(rocnik=drivejsi_rocnik, datum_deadline__isnull=False).order_by('poradi').last().datum_deadline
logger.debug(f'Nalezené ročníky: {drivejsi_rocnik}, {pozdejsi_rocnik}')
if drivejsi_rocnik is not None and datum <= posledni_deadline_drivejsiho_rocniku:
logger.debug(f'Hledám v dřívějším ročníku: {drivejsi_rocnik}')
return deadline_v_rocniku(datum, drivejsi_rocnik)
else:
logger.debug(f'Hledám v pozdějším ročníku: {pozdejsi_rocnik}')
return deadline_v_rocniku(datum, pozdejsi_rocnik)

2
seminar/views/odevzdavatko.py

@ -257,7 +257,7 @@ class PrehledOdevzdanychReseni(ListView):
# 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):
for rocnik, hodnoceni in groupby(ctx['object_list'], lambda ho: deadline(ho.reseni.cas_doruceni)[1].rocnik if deadline(ho.reseni.cas_doruceni) is not None else None):
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í

47
seminar/views/views_all.py

@ -11,7 +11,7 @@ from django.http import Http404,HttpResponseBadRequest,HttpResponseRedirect
from django.db.models import Q, Sum, Count
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.generic.edit import FormView, CreateView
from django.views.generic.base import TemplateView
from django.views.generic.base import TemplateView, RedirectView
from django.contrib.auth import authenticate, login, get_user_model, logout
from django.contrib.auth import views as auth_views
from django.contrib.auth.models import User, Permission
@ -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, ProfileEditFormPoMaturite
from seminar.forms import PrihlaskaForm, LoginForm, ProfileEditForm, PoMaturiteProfileEditForm
import seminar.forms as f
import seminar.templatetags.treenodes as tnltt
import seminar.views.views_rest as vr
@ -1026,7 +1026,20 @@ class ResitelView(LoginRequiredMixin,generic.DetailView):
class AddSolutionView(LoginRequiredMixin, FormView):
template_name = 'seminar/org/vloz_reseni.html'
form_class = f.VlozReseniForm
success_url = '/'
def form_valid(self, form):
data = form.cleaned_data
nove_reseni = m.Reseni.objects.create(
cas_doruceni=data['cas_doruceni'],
forma=data['forma'],
poznamka=data['poznamka'],
)
nove_reseni.resitele.add(data['resitel'])
nove_reseni.problem.add(data['problem'])
nove_reseni.save()
# Chtěl jsem, aby bylo vidět, že se to uložilo, tak přesměrovávám na profil.
return redirect(reverse('profil'))
class NahrajReseniView(LoginRequiredMixin, CreateView):
model = s.Reseni
@ -1095,21 +1108,21 @@ 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 and resitel_edit.rok_maturity >= date.today().year:
if resitel_edit:
prefill_2=model_to_dict(resitel_edit)
prefill_1.update(prefill_2)
prefill_3=model_to_dict(osoba_edit)
prefill_1.update(prefill_3)
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)
if 'rok_maturity' not in prefill_1 or prefill_1['rok_maturity'] < date.today().year:
form = PoMaturiteProfileEditForm(initial=prefill_1)
else:
form = ProfileEditForm(initial=prefill_1)
## Změna údajů a jejich uložení
if request.method == 'POST':
if resitel_edit and resitel_edit.rok_maturity < date.today().year:
form = ProfileEditFormPoMaturite(request.POST)
if 'rok_maturity' not in prefill_1 or prefill_1['rok_maturity'] < date.today().year:
form = PoMaturiteProfileEditForm(request.POST)
else:
form = ProfileEditForm(request.POST)
if form.is_valid():
@ -1133,7 +1146,7 @@ def resitelEditView(request):
## Neznámá země
msg = "Unknown country {}".format(fcd['stat_text'])
if resitel_edit and resitel_edit.rok_maturity >= date.today().year:
if resitel_edit:
## Změny v řešiteli
resitel_edit.skola = fcd['skola']
resitel_edit.rok_maturity = fcd['rok_maturity']
@ -1149,7 +1162,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, 'po_maturite': resitel_edit and resitel_edit.rok_maturity < date.today().year})
return render(request, 'seminar/profil/edit.html', {'form': form})
def prihlaskaView(request):
generic_logger = logging.getLogger('seminar.prihlaska')
@ -1230,12 +1243,6 @@ class LoginView(auth_views.LoginView):
# Jen vezmeme vestavěný a dáme mu vhodný template a přesměrovací URL
template_name = 'seminar/profil/login.html'
# Přesměrovací URL má být v kontextu:
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['next'] = reverse('profil')
return ctx
class LogoutView(auth_views.LogoutView):
# Jen vezmeme vestavěný a dáme mu vhodný template a přesměrovací URL
template_name = 'seminar/profil/logout.html'
@ -1327,3 +1334,11 @@ class JakResitView(generic.ListView):
def get_queryset(self):
return None
class AktualniRocnikRedirectView(RedirectView):
permanent=False
pattern_name = 'seminar_rocnik'
def get_redirect_url(self, *args, **kwargs):
aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik.rocnik
return super().get_redirect_url(rocnik=aktualni_rocnik, *args, **kwargs)

Loading…
Cancel
Save