Move personalni do aplikace personalni
This commit is contained in:
parent
61b6f4bfd9
commit
0cd1c3ef1a
27 changed files with 1330 additions and 1248 deletions
|
@ -140,6 +140,7 @@ INSTALLED_APPS = (
|
||||||
'aesop',
|
'aesop',
|
||||||
'odevzdavatko',
|
'odevzdavatko',
|
||||||
'vysledkovky',
|
'vysledkovky',
|
||||||
|
'personalni',
|
||||||
|
|
||||||
# Admin upravy:
|
# Admin upravy:
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,10 @@ urlpatterns = [
|
||||||
# Prednaskova aplikace (ma vlastni podadresare)
|
# Prednaskova aplikace (ma vlastni podadresare)
|
||||||
path('', include('prednasky.urls')),
|
path('', include('prednasky.urls')),
|
||||||
|
|
||||||
|
# Personalni aplikace (ma vlastni podadresare)
|
||||||
|
# (profil, osobní údaje, ..., ne autentizace, viz dále)
|
||||||
|
path('', include('personalni.urls')),
|
||||||
|
|
||||||
# Autentizační aplikace (ma vlastni podadresare)
|
# Autentizační aplikace (ma vlastni podadresare)
|
||||||
path('', include('various.autentizace.urls')),
|
path('', include('various.autentizace.urls')),
|
||||||
|
|
||||||
|
|
4
personalni/__init__.py
Normal file
4
personalni/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
"""
|
||||||
|
Obsahuje vše okolo registrace a osobních údajů (ne přihlášení a změnu hesla).
|
||||||
|
Také obsahuje rozcestníky a Řešitele s Organizátorem.
|
||||||
|
"""
|
48
personalni/admin.py
Normal file
48
personalni/admin.py
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.contrib.auth.models import Group
|
||||||
|
from django_reverse_admin import ReverseModelAdmin
|
||||||
|
import seminar.models as m
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(m.Osoba)
|
||||||
|
class OsobaAdmin(admin.ModelAdmin):
|
||||||
|
actions = ['synchronizuj_maily', 'udelej_orgem']
|
||||||
|
|
||||||
|
def synchronizuj_maily(self, request, queryset):
|
||||||
|
for o in queryset:
|
||||||
|
if o.user is not None:
|
||||||
|
u = o.user
|
||||||
|
u.email = o.email
|
||||||
|
u.save()
|
||||||
|
self.message_user(request, "E-maily synchronizovány.")
|
||||||
|
synchronizuj_maily.short_description = "Synchronizuj vybraným osobám e-maily do uživatelů"
|
||||||
|
|
||||||
|
def udelej_orgem(self,request,queryset):
|
||||||
|
org_group = Group.objects.get(name='org')
|
||||||
|
print(queryset)
|
||||||
|
for o in queryset:
|
||||||
|
user = o.user
|
||||||
|
print(user)
|
||||||
|
user.groups.add(org_group)
|
||||||
|
user.is_staff = True
|
||||||
|
user.save()
|
||||||
|
org = m.Organizator.objects.create(osoba=o)
|
||||||
|
org.save()
|
||||||
|
udelej_orgem.short_description = "Udělej vybraných osob organizátory"
|
||||||
|
|
||||||
|
@admin.register(m.Organizator)
|
||||||
|
class OrganizatorAdmin(admin.ModelAdmin):
|
||||||
|
search_fields = ['osoba__jmeno', 'osoba__prijmeni', 'osoba__prezdivka']
|
||||||
|
|
||||||
|
class OsobaInline(admin.TabularInline):
|
||||||
|
model = m.Osoba
|
||||||
|
|
||||||
|
@admin.register(m.Resitel)
|
||||||
|
class ResitelAdmin(ReverseModelAdmin):
|
||||||
|
search_fields = ['osoba__jmeno', 'osoba__prijmeni', 'osoba__prezdivka']
|
||||||
|
ordering = ('osoba__jmeno','osoba__prijmeni')
|
||||||
|
inline_type = 'stacked'
|
||||||
|
inline_reverse = ['osoba']
|
||||||
|
|
||||||
|
admin.site.register(m.Skola)
|
||||||
|
admin.site.register(m.Prijemce)
|
5
personalni/apps.py
Normal file
5
personalni/apps.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class PersonalniConfig(AppConfig):
|
||||||
|
name = 'personalni'
|
221
personalni/forms.py
Normal file
221
personalni/forms.py
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
from django import forms
|
||||||
|
from dal import autocomplete
|
||||||
|
from django.contrib.auth.forms import PasswordResetForm
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
from seminar.models import Skola, Resitel, Osoba
|
||||||
|
|
||||||
|
from datetime import date
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# pro přidání políčka do formuláře je potřeba
|
||||||
|
# - mít v modelu tu položku, kterou chci upravovat
|
||||||
|
# - přidat do views (prihlaskaView, resitelEditView)
|
||||||
|
# - přidat do forms
|
||||||
|
# - includovat do html
|
||||||
|
|
||||||
|
class DateInput(forms.DateInput):
|
||||||
|
# aby se datum dalo vybírat z kalendáře
|
||||||
|
input_type = 'date'
|
||||||
|
|
||||||
|
class TelInput(forms.TextInput):
|
||||||
|
# tohle je možná k niřemu, ale alepsoň to mění input type a nic to nekazí
|
||||||
|
input_type = 'tel'
|
||||||
|
input_pattern="^[+]?[()/0-9. -]{9,}$"
|
||||||
|
|
||||||
|
|
||||||
|
class PrihlaskaForm(PasswordResetForm):
|
||||||
|
username = forms.CharField(label='Přihlašovací jméno',
|
||||||
|
max_length=256,
|
||||||
|
required=True,
|
||||||
|
help_text='Tímto jménem se následně budeš přihlašovat pro odevzdání řešení a další činnosti v semináři')
|
||||||
|
|
||||||
|
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 a číslo popisné', 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)
|
||||||
|
|
||||||
|
skola = forms.ModelChoiceField(label="Škola",
|
||||||
|
queryset=Skola.objects.all(),
|
||||||
|
widget=autocomplete.ModelSelect2(
|
||||||
|
url='autocomplete_skola',
|
||||||
|
attrs = {'data-placeholder--id': '-1',
|
||||||
|
'data-placeholder--text' : '---',
|
||||||
|
'data-allow-clear': 'true'})
|
||||||
|
,required=False)
|
||||||
|
|
||||||
|
skola_nazev = forms.CharField(label='Název školy', max_length=256, required=False)
|
||||||
|
skola_adresa = forms.CharField(label='Adresa školy', max_length=256, required=False)
|
||||||
|
|
||||||
|
# trida = forms.CharField(label='Třída',max_length=10, required=True)
|
||||||
|
|
||||||
|
rok_maturity = forms.IntegerField(
|
||||||
|
label='Rok maturity',
|
||||||
|
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)
|
||||||
|
zasilat_cislo_emailem = forms.BooleanField(label='Chci dostávat e-mailem upozornění na vydání nového čísla', required=False)
|
||||||
|
|
||||||
|
gdpr = forms.BooleanField(label='Souhlasím se zpracováním osobních údajů', required=True)
|
||||||
|
spam = forms.BooleanField(label='Souhlasím se zasíláním materiálů od MFF UK', required=False)
|
||||||
|
|
||||||
|
def clean_username(self):
|
||||||
|
err_logger = logging.getLogger('seminar.prihlaska.problem')
|
||||||
|
username = self.cleaned_data.get('username')
|
||||||
|
try:
|
||||||
|
User.objects.get(username=username)
|
||||||
|
msg = "Username {} exists".format(username)
|
||||||
|
err_logger.info(msg)
|
||||||
|
raise forms.ValidationError('Přihlašovací jméno je již použito')
|
||||||
|
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
pass
|
||||||
|
return username
|
||||||
|
|
||||||
|
def clean_email(self):
|
||||||
|
err_logger = logging.getLogger('seminar.prihlaska.problem')
|
||||||
|
email = self.cleaned_data.get('email')
|
||||||
|
try:
|
||||||
|
osoba = Osoba.objects.get(email=email)
|
||||||
|
msg = "Email {} exists".format(email)
|
||||||
|
if osoba.user is not None:
|
||||||
|
err_logger.info(msg)
|
||||||
|
raise forms.ValidationError('E-mail je již použit')
|
||||||
|
else:
|
||||||
|
msg += ', but currently has no User, so allowing registration.'
|
||||||
|
err_logger.info(msg)
|
||||||
|
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
pass
|
||||||
|
return email
|
||||||
|
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
super().clean()
|
||||||
|
|
||||||
|
err_logger = logging.getLogger('seminar.prihlaska.problem')
|
||||||
|
|
||||||
|
data = self.cleaned_data
|
||||||
|
if data.get('stat') != 'other' and data.get('stat_text') != '':
|
||||||
|
self.add_error('stat',forms.ValidationError('Nelze mít vybraný stát z menu a zároven zapsaný textem'))
|
||||||
|
if data.get('skola') and (data.get('skola_nazev') or data.get('skola_adresa')):
|
||||||
|
self.add_error('skola',forms.ValidationError('Pokud je škola v seznamu, nevypisujte ji ručně, pokud není, zrušte výběr ze seznamu (křížek vpravo)'))
|
||||||
|
if not data.get('skola'):
|
||||||
|
if data.get('skola_nazev')=='' and data.get('skola_adresa')=='':
|
||||||
|
self.add_error('skola',forms.ValidationError('Je nutné vyplnit školu'))
|
||||||
|
elif data.get('skola_nazev')=='':
|
||||||
|
self.add_error('skola_nazev',forms.ValidationError('Je nutné vyplnit název školy'))
|
||||||
|
elif data.get('skola_adresa')=='':
|
||||||
|
self.add_error('skola_adresa',forms.ValidationError('Je nutné vyplnit adresu školy'))
|
||||||
|
|
||||||
|
|
||||||
|
class ProfileEditForm(forms.Form):
|
||||||
|
username = forms.CharField(label='Přihlašovací jméno',
|
||||||
|
max_length=256,
|
||||||
|
required=False,
|
||||||
|
disabled=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)
|
||||||
|
|
||||||
|
skola = forms.ModelChoiceField(label="Škola",
|
||||||
|
queryset=Skola.objects.all(),
|
||||||
|
widget=autocomplete.ModelSelect2(
|
||||||
|
url='autocomplete_skola',
|
||||||
|
attrs = {'data-placeholder--id': '-1',
|
||||||
|
'data-placeholder--text' : '---',
|
||||||
|
'data-allow-clear': 'true'})
|
||||||
|
,required=False)
|
||||||
|
|
||||||
|
skola_nazev = forms.CharField(label='Název školy', max_length=256, required=False)
|
||||||
|
skola_adresa = forms.CharField(label='Adresa školy', max_length=256, required=False)
|
||||||
|
|
||||||
|
# trida = forms.CharField(label='Třída',max_length=10, required=True)
|
||||||
|
|
||||||
|
rok_maturity = forms.IntegerField(
|
||||||
|
label='Rok maturity',
|
||||||
|
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)
|
||||||
|
zasilat_cislo_emailem = forms.BooleanField(label='Chci dostávat email s upozorněním na vydání nového čísla', required=False)
|
||||||
|
|
||||||
|
spam = forms.BooleanField(label='Souhlasím se zasíláním materiálů od MFF UK', required=False)
|
||||||
|
# def clean_username(self):
|
||||||
|
# err_logger = logging.getLogger('seminar.prihlaska.problem')
|
||||||
|
# username = self.cleaned_data.get('username')
|
||||||
|
# try:
|
||||||
|
# User.objects.get(username=username)
|
||||||
|
# msg = "Username {} exists".format(username)
|
||||||
|
# err_logger.info(msg)
|
||||||
|
# raise forms.ValidationError('Přihlašovací jméno je již použito')
|
||||||
|
#
|
||||||
|
# except ObjectDoesNotExist:
|
||||||
|
# pass
|
||||||
|
# return username
|
||||||
|
#
|
||||||
|
def clean_email(self):
|
||||||
|
err_logger = logging.getLogger('seminar.prihlaska.problem')
|
||||||
|
email = self.cleaned_data.get('email')
|
||||||
|
try:
|
||||||
|
Osoba.objects.exclude(user__username=self.username).get(email=email)
|
||||||
|
msg = "Email {} exists (in edit)".format(email)
|
||||||
|
err_logger.info(msg)
|
||||||
|
raise forms.ValidationError('Email je již použit')
|
||||||
|
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
pass
|
||||||
|
return email
|
||||||
|
#def clean(self):
|
||||||
|
# super().clean()
|
||||||
|
#
|
||||||
|
# err_logger = logging.getLogger('seminar.prihlaska.problem')
|
||||||
|
|
||||||
|
# data = self.cleaned_data
|
||||||
|
# if data.get('password') != data.get('password_check'):
|
||||||
|
# self.add_error('password_check',forms.ValidationError('Hesla se neshodují'))
|
||||||
|
# if data.get('stat') != '' and data.get('stat_text') != '':
|
||||||
|
# self.add_error('stat',forms.ValidationError('Nelze mít vybraný stát z menu a zároven zapsaný textem'))
|
||||||
|
# if data.get('skola') and (data.get('skola_nazev') or data.get('skola_adresa')):
|
||||||
|
# self.add_error('skola',forms.ValidationError('Pokud je škola v seznamu, nevypisujte ji ručně, pokud není, zrušte výběr ze seznamu (křížek vpravo)'))
|
||||||
|
# if not data.get('skola'):
|
||||||
|
# if data.get('skola_nazev')=='' and data.get('skola_adresa')=='':
|
||||||
|
# self.add_error('skola',forms.ValidationError('Je nutné vyplnit školu'))
|
||||||
|
# elif data.get('skola_nazev')=='':
|
||||||
|
# self.add_error('skola_nazev',forms.ValidationError('Je nutné vyplnit název školy'))
|
||||||
|
# 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)
|
0
personalni/migrations/__init__.py
Normal file
0
personalni/migrations/__init__.py
Normal file
105
personalni/templates/personalni/udaje/edit.html
Normal file
105
personalni/templates/personalni/udaje/edit.html
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
<script src="{% static 'personalni/prihlaska.js' %}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
# pro přidání políčka do formuláře je potřeba
|
||||||
|
# - mít v modelu tu položku, kterou chci upravovat
|
||||||
|
# - přidat do views (prihlaskaView, resitelEditView)
|
||||||
|
# - přidat do forms
|
||||||
|
# - includovat do html
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>
|
||||||
|
{% block nadpis1a %}{% block nadpis1b %}
|
||||||
|
Změna osobních údajů
|
||||||
|
{% endblock %}{% endblock %}
|
||||||
|
</h1>
|
||||||
|
<form action="{% url 'seminar_resitel_edit' %}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{form.non_field_errors}}
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h4>
|
||||||
|
Přihlašovací údaje
|
||||||
|
</h4>
|
||||||
|
<table class="form">
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.username %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h4>
|
||||||
|
Osobní údaje
|
||||||
|
</h4>
|
||||||
|
<table class="form">
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.jmeno %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.prijmeni %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.pohlavi_muz%}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.email %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.telefon %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.datum_narozeni %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h4>
|
||||||
|
Bydliště
|
||||||
|
</h4>
|
||||||
|
<table class="form">
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.ulice %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.mesto %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.psc %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.stat %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.stat_text id="id_li_stat_text"%}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h4>
|
||||||
|
Škola
|
||||||
|
</h4>
|
||||||
|
<table class="form">
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.skola %}
|
||||||
|
<tr><td colspan="2" ><button id="id_skola_text_button" type="button">Škola není v seznamu</button></td></tr>
|
||||||
|
<tr><td id="id_li_skola_vypln" colspan="2">Vyplň prosím celý název a adresu školy.</td></tr>
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.skola_nazev id="id_li_skola_nazev" %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.skola_adresa id="id_li_skola_adresa" %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.rok_maturity %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h4>
|
||||||
|
Pošta
|
||||||
|
</h4>
|
||||||
|
<table class="form">
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.zasilat %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.zasilat_cislo_emailem %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h4>
|
||||||
|
Zasílání propagačních materiálů
|
||||||
|
</h4>
|
||||||
|
<table class="form">
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.spam %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<input type="submit" value="Změnit">
|
||||||
|
</form>
|
||||||
|
<script>
|
||||||
|
$("#id_stat").on("change",addrCountryChanged);
|
||||||
|
$("#id_skola_text_button").on("click",schoolNotInList);
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
123
personalni/templates/personalni/udaje/prihlaska.html
Normal file
123
personalni/templates/personalni/udaje/prihlaska.html
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
<script src="{% static 'personalni/prihlaska.js' %}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
# pro přidání políčka do formuláře je potřeba
|
||||||
|
# - mít v modelu tu položku, kterou chci upravovat
|
||||||
|
# - přidat do views (prihlaskaView, resitelEditView)
|
||||||
|
# - přidat do forms
|
||||||
|
# - includovat do html
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>
|
||||||
|
{% block nadpis1a %}{% block nadpis1b %}
|
||||||
|
Přihláška do semináře
|
||||||
|
{% endblock %}{% endblock %}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p><b>Tučně</b> popsaná pole jsou povinná.</p>
|
||||||
|
|
||||||
|
<form action="{% url 'seminar_prihlaska' %}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{form.non_field_errors}}
|
||||||
|
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<h4>
|
||||||
|
Přihlašovací údaje
|
||||||
|
</h4>
|
||||||
|
<table class="form">
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.username %}
|
||||||
|
{# {% include "personalni/udaje/prihlaska_field.html" with field=form.password %}#}
|
||||||
|
{# {% include "personalni/udaje/prihlaska_field.html" with field=form.password_check %}#}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h4>
|
||||||
|
Osobní údaje
|
||||||
|
</h4>
|
||||||
|
<table class="form">
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.jmeno %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.prijmeni %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.pohlavi_muz%}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.email %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.telefon %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.datum_narozeni %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h4>
|
||||||
|
Bydliště
|
||||||
|
</h4>
|
||||||
|
<table class="form">
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.ulice %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.mesto %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.psc %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.stat %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.stat_text id="id_li_stat_text"%}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h4>
|
||||||
|
Škola
|
||||||
|
</h4>
|
||||||
|
<table class="form">
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.skola %}
|
||||||
|
<tr><td colspan="2" ><button id="id_skola_text_button" type="button">Škola není v seznamu</button></td></tr>
|
||||||
|
<tr><td id="id_li_skola_vypln" colspan="2">Vyplň prosím celý název a adresu školy.</td></tr>
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.skola_nazev id="id_li_skola_nazev" %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.skola_adresa id="id_li_skola_adresa" %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.rok_maturity %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h4>
|
||||||
|
Pošta
|
||||||
|
</h4>
|
||||||
|
<table class="form">
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.zasilat %}
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.zasilat_cislo_emailem %}
|
||||||
|
</table>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h4>
|
||||||
|
GDPR
|
||||||
|
</h4>
|
||||||
|
{% include "personalni/udaje/gdpr.html" %}
|
||||||
|
<table class="form">
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.gdpr %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h4>
|
||||||
|
Zasílání propagačních materiálů
|
||||||
|
</h4>
|
||||||
|
<table class="form">
|
||||||
|
{% include "personalni/udaje/prihlaska_field.html" with field=form.spam %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<input type="submit" value="Odeslat">
|
||||||
|
</form>
|
||||||
|
<script>
|
||||||
|
$("#id_stat").on("change",addrCountryChanged);
|
||||||
|
$("#id_skola_text_button").on("click",schoolNotInList);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
24
personalni/urls.py
Normal file
24
personalni/urls.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
from django.urls import path
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from . import views
|
||||||
|
from seminar.utils import org_required
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path(
|
||||||
|
'org/rozcestnik/',
|
||||||
|
org_required(views.OrgoRozcestnikView.as_view()),
|
||||||
|
name='seminar_org_rozcestnik'
|
||||||
|
),
|
||||||
|
|
||||||
|
path('prihlaska/', views.prihlaskaView, name='seminar_prihlaska'),
|
||||||
|
|
||||||
|
path(
|
||||||
|
'resitel/osobni-udaje/',
|
||||||
|
login_required(views.resitelEditView),
|
||||||
|
name='seminar_resitel_edit'
|
||||||
|
),
|
||||||
|
|
||||||
|
# Obecný view na profil -- orgům dá rozcestník, řešitelům jejich stránku
|
||||||
|
path('profil/', views.profilView, name='profil'),
|
||||||
|
|
||||||
|
]
|
306
personalni/views.py
Normal file
306
personalni/views.py
Normal file
|
@ -0,0 +1,306 @@
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.views import generic
|
||||||
|
from django.db.models import Q
|
||||||
|
from django.views.decorators.debug import sensitive_post_parameters
|
||||||
|
from django.views.generic.base import TemplateView
|
||||||
|
from django.contrib.auth.models import User, Permission, Group
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
|
import seminar.models as s
|
||||||
|
import seminar.models as m
|
||||||
|
from .forms import PrihlaskaForm, ProfileEditForm, PoMaturiteProfileEditForm
|
||||||
|
|
||||||
|
from datetime import date
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from seminar.views import formularOKView
|
||||||
|
from various.autentizace.views import LoginView
|
||||||
|
from various.autentizace.utils import posli_reset_hesla
|
||||||
|
|
||||||
|
from django.forms.models import model_to_dict
|
||||||
|
|
||||||
|
|
||||||
|
class OrgoRozcestnikView(TemplateView):
|
||||||
|
""" Zobrazí organizátorský rozcestník."""
|
||||||
|
|
||||||
|
template_name = 'personalni/profil/orgorozcestnik.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context['posledni_soustredeni'] = s.Soustredeni.objects.order_by('-datum_konce').first()
|
||||||
|
nastaveni = s.Nastaveni.objects.first()
|
||||||
|
aktualni_rocnik = nastaveni.aktualni_rocnik
|
||||||
|
context['posledni_cislo_url'] = nastaveni.aktualni_cislo.verejne_url()
|
||||||
|
# TODO možná chceme odkazovat na právě rozpracované číslo, a ne to poslední vydané
|
||||||
|
# pokud nechceme haluzit kód (= poradi) dalšího čísla, bude asi potřeba jít
|
||||||
|
# přes treenody (a dát si přitom pozor na MezicisloNode)
|
||||||
|
|
||||||
|
neobodovana_reseni = s.Hodnoceni.objects.filter(body__isnull=True)
|
||||||
|
reseni_mimo_cislo = s.Hodnoceni.objects.filter(cislo_body__isnull=True)
|
||||||
|
context['pocet_neobodovanych_reseni'] = neobodovana_reseni.count()
|
||||||
|
context['pocet_reseni_mimo_cislo'] = reseni_mimo_cislo.count()
|
||||||
|
|
||||||
|
u = self.request.user
|
||||||
|
os = s.Osoba.objects.get(user=u)
|
||||||
|
organizator = s.Organizator.objects.get(osoba=os)
|
||||||
|
|
||||||
|
context['muj_pocet_neobodovanych_reseni'] = neobodovana_reseni.filter(Q(problem__garant=organizator) | Q(problem__autor=organizator) | Q(problem__opravovatele__in=[organizator])).distinct().count()
|
||||||
|
context['muj_pocet_reseni_mimo_cislo'] = reseni_mimo_cislo.filter(Q(problem__garant=organizator) | Q(problem__autor=organizator) | Q(problem__opravovatele__in=[organizator])).count()
|
||||||
|
|
||||||
|
#FIXME: přidat stav='STAV_ZADANY'
|
||||||
|
temata = s.Tema.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]),
|
||||||
|
rocnik=aktualni_rocnik).distinct()
|
||||||
|
ulohy = s.Uloha.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]),
|
||||||
|
cislo_zadani__rocnik=aktualni_rocnik).distinct()
|
||||||
|
clanky = s.Clanek.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]),
|
||||||
|
cislo__rocnik=aktualni_rocnik).distinct()
|
||||||
|
|
||||||
|
context['temata'] = temata
|
||||||
|
context['ulohy'] = ulohy
|
||||||
|
context['clanky'] = clanky
|
||||||
|
context['organizator'] = organizator
|
||||||
|
return context
|
||||||
|
|
||||||
|
#content_type = 'text/plain; charset=UTF8'
|
||||||
|
#XXX
|
||||||
|
|
||||||
|
|
||||||
|
class ResitelView(LoginRequiredMixin,generic.DetailView):
|
||||||
|
model = s.Resitel
|
||||||
|
template_name = 'personalni/profil/resitel.html'
|
||||||
|
|
||||||
|
def get_object(self, queryset=None):
|
||||||
|
print(self.request.user)
|
||||||
|
return s.Resitel.objects.get(osoba__user=self.request.user)
|
||||||
|
|
||||||
|
### Formulare
|
||||||
|
|
||||||
|
# pro přidání políčka do formuláře je potřeba
|
||||||
|
# - mít v modelu tu položku, kterou chci upravovat
|
||||||
|
# - přidat do views (prihlaskaView, resitelEditView)
|
||||||
|
# - přidat do forms
|
||||||
|
# - includovat do html
|
||||||
|
|
||||||
|
def prihlaska_log_gdpr_safe(logger, gdpr_logger, msg, form_data):
|
||||||
|
msg = "{}, form_hash:{}".format(msg,hash(frozenset(form_data.items)))
|
||||||
|
logger.warn(msg)
|
||||||
|
gdpr_logger.warn(msg+", form:{}".format(form_data))
|
||||||
|
|
||||||
|
|
||||||
|
@sensitive_post_parameters('jmeno', 'prijmeni', 'email', 'telefon', 'datum_narozeni', 'ulice', 'mesto', 'psc', 'skola')
|
||||||
|
def resitelEditView(request):
|
||||||
|
err_logger = logging.getLogger('seminar.prihlaska.problem')
|
||||||
|
## Načtení objektů Osoba a Resitel patřících k aktuálně přihlášenému uživateli
|
||||||
|
u = request.user
|
||||||
|
osoba_edit = s.Osoba.objects.get(user=u)
|
||||||
|
if hasattr(osoba_edit,'resitel'):
|
||||||
|
resitel_edit = osoba_edit.resitel
|
||||||
|
else:
|
||||||
|
resitel_edit = None
|
||||||
|
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:
|
||||||
|
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 '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':
|
||||||
|
POST = request.POST.copy()
|
||||||
|
POST["username"] = osoba_edit.user.username
|
||||||
|
|
||||||
|
if 'rok_maturity' not in prefill_1 or prefill_1['rok_maturity'] < date.today().year:
|
||||||
|
form = PoMaturiteProfileEditForm(POST)
|
||||||
|
else:
|
||||||
|
form = ProfileEditForm(POST)
|
||||||
|
form.username = user_edit.username
|
||||||
|
if form.is_valid():
|
||||||
|
## Změny v osobě
|
||||||
|
fcd = form.cleaned_data
|
||||||
|
form_hash = hash(frozenset(fcd.items()))
|
||||||
|
form_logger = logging.getLogger('seminar.prihlaska.form')
|
||||||
|
form_logger.info("EDIT:" + str(fcd) + str(form_hash)) # TODO možná logovat jinak
|
||||||
|
osoba_edit.jmeno = fcd['jmeno']
|
||||||
|
osoba_edit.prijmeni = fcd['prijmeni']
|
||||||
|
osoba_edit.pohlavi_muz = fcd['pohlavi_muz']
|
||||||
|
osoba_edit.email = fcd['email']
|
||||||
|
osoba_edit.telefon = fcd['telefon']
|
||||||
|
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()
|
||||||
|
if fcd.get('stat','') in ('CZ','SK'):
|
||||||
|
osoba_edit.stat = fcd['stat']
|
||||||
|
else:
|
||||||
|
## Neznámá země
|
||||||
|
msg = "Unknown country {}".format(fcd['stat_text'])
|
||||||
|
|
||||||
|
if resitel_edit:
|
||||||
|
## Změny v řešiteli
|
||||||
|
resitel_edit.skola = fcd['skola']
|
||||||
|
resitel_edit.rok_maturity = fcd['rok_maturity']
|
||||||
|
resitel_edit.zasilat = fcd['zasilat']
|
||||||
|
resitel_edit.zasilat_cislo_emailem = fcd['zasilat_cislo_emailem']
|
||||||
|
if fcd.get('skola'):
|
||||||
|
resitel_edit.skola = fcd['skola']
|
||||||
|
else:
|
||||||
|
# Unknown school - log it
|
||||||
|
msg = "Unknown school {}, {}".format(fcd['skola_nazev'],fcd['skola_adresa'])
|
||||||
|
resitel_edit.save()
|
||||||
|
osoba_edit.save()
|
||||||
|
return formularOKView(request, text=f'Údaje byly úspěšně uloženy. <a href="{reverse("profil")}">Vrátit se zpět na profil.</a>')
|
||||||
|
|
||||||
|
return render(request, 'personalni/udaje/edit.html', {'form': form})
|
||||||
|
|
||||||
|
|
||||||
|
@sensitive_post_parameters('jmeno', 'prijmeni', 'email', 'telefon', 'datum_narozeni', 'ulice', 'mesto', 'psc', 'skola')
|
||||||
|
def prihlaskaView(request):
|
||||||
|
generic_logger = logging.getLogger('seminar.prihlaska')
|
||||||
|
err_logger = logging.getLogger('seminar.prihlaska.problem')
|
||||||
|
form_logger = logging.getLogger('seminar.prihlaska.form')
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = PrihlaskaForm(request.POST)
|
||||||
|
# TODO vyresit, co se bude v jakych situacich zobrazovat
|
||||||
|
if form.is_valid():
|
||||||
|
generic_logger.info("Form valid")
|
||||||
|
fcd = form.cleaned_data
|
||||||
|
form_hash = hash(frozenset(fcd.items()))
|
||||||
|
form_logger.info(str(fcd) + str(form_hash)) # TODO možná logovat jinak
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
u = User.objects.create_user(
|
||||||
|
username=fcd['username'],
|
||||||
|
email = fcd['email'])
|
||||||
|
u.save()
|
||||||
|
resitel_perm = Permission.objects.filter(codename__exact='resitel').first()
|
||||||
|
u.user_permissions.add(resitel_perm)
|
||||||
|
resitel_grp = Group.objects.filter(name__exact='resitel').first()
|
||||||
|
u.groups.add(resitel_grp)
|
||||||
|
|
||||||
|
o = s.Osoba(
|
||||||
|
jmeno = fcd['jmeno'],
|
||||||
|
prijmeni = fcd['prijmeni'],
|
||||||
|
pohlavi_muz = fcd['pohlavi_muz'],
|
||||||
|
email = fcd['email'],
|
||||||
|
telefon = fcd.get('telefon',''),
|
||||||
|
datum_narozeni = fcd.get('datum_narozeni',None),
|
||||||
|
datum_souhlasu_udaje = date.today(),
|
||||||
|
datum_registrace = date.today(),
|
||||||
|
ulice = fcd.get('ulice',''),
|
||||||
|
mesto = fcd.get('mesto',''),
|
||||||
|
psc = fcd.get('psc',''),
|
||||||
|
poznamka = str(fcd)
|
||||||
|
)
|
||||||
|
|
||||||
|
if fcd.get('spam',False):
|
||||||
|
o.datum_souhlasu_zasilani = date.today()
|
||||||
|
if fcd.get('stat','') in ('CZ','SK'):
|
||||||
|
o.stat = fcd['stat']
|
||||||
|
else:
|
||||||
|
# Unknown country - log it
|
||||||
|
msg = "Unknown country {}".format(fcd['stat_text'])
|
||||||
|
err_logger.warn(msg + str(form_hash))
|
||||||
|
|
||||||
|
|
||||||
|
# Dovolujeme doregistraci uživatele pro existující mail, takže naopak chceme doplnit/aktualizovat údaje do stávajícího objektu
|
||||||
|
try:
|
||||||
|
orig_osoba = m.Osoba.objects.get(email=fcd['email'])
|
||||||
|
orig_osoba.poznamka += '\nDOREGISTRACE K EXISTUJÍCÍMU E-MAILU, diff níže.'
|
||||||
|
except m.Osoba.DoesNotExist:
|
||||||
|
# Trik: Budeme aktualizovat údaje nové osoby, takže se asi nic nezmění, ale fungovat to bude.
|
||||||
|
orig_osoba = o
|
||||||
|
|
||||||
|
# Porovnání údajů
|
||||||
|
assert orig_osoba.user is None, "Právě-registrující-se osoba už má Uživatele!"
|
||||||
|
osoba_attrs = ['jmeno', 'prijmeni', 'pohlavi_muz', 'email', 'telefon', 'datum_narozeni', 'ulice', 'mesto', 'psc', 'stat', 'datum_souhlasu_udaje', 'datum_souhlasu_zasilani', 'datum_registrace']
|
||||||
|
diffattrs = []
|
||||||
|
for attr in osoba_attrs:
|
||||||
|
new = getattr(o, attr)
|
||||||
|
old = getattr(orig_osoba, attr)
|
||||||
|
if new != old:
|
||||||
|
orig_osoba.poznamka += f'\nRozdíl v {attr}: Původní {old}, nový {new}'
|
||||||
|
diffattrs.append(f'Osoba.{attr}')
|
||||||
|
setattr(orig_osoba, attr, new)
|
||||||
|
# Datum registrace chceme původní / nižší:
|
||||||
|
orig_osoba.datum_registrace = min(orig_osoba.datum_registrace, o.datum_registrace)
|
||||||
|
|
||||||
|
# Od této chvíle dál je správná osoba ta "původní", novou podle formuláře si ale zachováme
|
||||||
|
o, o_form = orig_osoba, o
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
o.save()
|
||||||
|
o.user = u
|
||||||
|
o.save()
|
||||||
|
|
||||||
|
# Jednoduchá kvazi-kontrola duplicitních Osob
|
||||||
|
kolize = m.Osoba.objects.filter(jmeno=o.jmeno, prijmeni=o.prijmeni)
|
||||||
|
if kolize.count() > 1: # Jednu z nich jsme právě uložili
|
||||||
|
err_logger.warning(f'Zaregistrovala se osoba s kolizním jménem. ID osob: {[o.id for o in kolize]}')
|
||||||
|
|
||||||
|
r = s.Resitel(
|
||||||
|
rok_maturity = fcd['rok_maturity'],
|
||||||
|
zasilat = fcd['zasilat'],
|
||||||
|
zasilat_cislo_emailem = fcd['zasilat_cislo_emailem']
|
||||||
|
)
|
||||||
|
|
||||||
|
if fcd.get('skola'):
|
||||||
|
r.skola = fcd['skola']
|
||||||
|
else:
|
||||||
|
# Unknown school - log it
|
||||||
|
msg = "Unknown school {}, {}".format(fcd['skola_nazev'],fcd['skola_adresa'])
|
||||||
|
err_logger.warn(msg + str(form_hash))
|
||||||
|
|
||||||
|
# Porovnání údajů u řešitele
|
||||||
|
try:
|
||||||
|
orig_resitel = o.resitel
|
||||||
|
orig_resitel.poznamka += '\nDOREGISTRACE ŘEŠITELE, diff:'
|
||||||
|
except m.Resitel.DoesNotExist:
|
||||||
|
# Stejný trik:
|
||||||
|
orig_resitel = r
|
||||||
|
resitel_attrs = ['skola', 'rok_maturity', 'zasilat', 'zasilat_cislo_emailem']
|
||||||
|
for attr in resitel_attrs:
|
||||||
|
new = getattr(r, attr)
|
||||||
|
old = getattr(orig_resitel, attr)
|
||||||
|
if new != old:
|
||||||
|
orig_resitel.poznamka += f'\nRozdíl v {attr}: Původní {old}, nový {new}'
|
||||||
|
diffattrs.append(f'Resitel.{attr}')
|
||||||
|
setattr(orig_resitel, attr, new)
|
||||||
|
r, r_form = orig_resitel, r
|
||||||
|
|
||||||
|
r.osoba = o # Tohle by mělo být bezpečné…
|
||||||
|
r.save()
|
||||||
|
|
||||||
|
if diffattrs: err_logger.warning(f'Different fields when matching Řešitel id {r.id} or Osoba id {o.id}: {diffattrs}')
|
||||||
|
|
||||||
|
posli_reset_hesla(u, request)
|
||||||
|
return formularOKView(request, text='Na tvůj e-mail jsme právě poslali odkaz pro nastavení hesla.')
|
||||||
|
|
||||||
|
# if a GET (or any other method) we'll create a blank form
|
||||||
|
else:
|
||||||
|
form = PrihlaskaForm()
|
||||||
|
|
||||||
|
return render(request, 'personalni/udaje/prihlaska.html', {'form': form})
|
||||||
|
|
||||||
|
|
||||||
|
# Jen hloupé rozhazovátko
|
||||||
|
def profilView(request):
|
||||||
|
user = request.user
|
||||||
|
if user.has_perm('auth.org'):
|
||||||
|
return OrgoRozcestnikView.as_view()(request)
|
||||||
|
if user.has_perm('auth.resitel'):
|
||||||
|
return ResitelView.as_view()(request)
|
||||||
|
else:
|
||||||
|
return LoginView.as_view()(request)
|
|
@ -1,12 +1,9 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.auth.models import Group
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.forms import widgets, ModelForm
|
from django.forms import widgets, ModelForm
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter
|
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter
|
||||||
from reversion.admin import VersionAdmin
|
|
||||||
from django_reverse_admin import ReverseModelAdmin
|
|
||||||
from solo.admin import SingletonModelAdmin
|
from solo.admin import SingletonModelAdmin
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
|
@ -17,8 +14,6 @@ from seminar.utils import hlavni_problem
|
||||||
import seminar.models as m
|
import seminar.models as m
|
||||||
import seminar.treelib as tl
|
import seminar.treelib as tl
|
||||||
|
|
||||||
admin.site.register(m.Skola)
|
|
||||||
admin.site.register(m.Prijemce)
|
|
||||||
admin.site.register(m.Rocnik)
|
admin.site.register(m.Rocnik)
|
||||||
|
|
||||||
class CisloForm(ModelForm):
|
class CisloForm(ModelForm):
|
||||||
|
@ -105,45 +100,6 @@ class CisloAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
force_publish.short_description = 'Zveřejnit vybraná čísla a všechny návrhy úloh v nich učinit zadanými'
|
force_publish.short_description = 'Zveřejnit vybraná čísla a všechny návrhy úloh v nich učinit zadanými'
|
||||||
|
|
||||||
@admin.register(m.Osoba)
|
|
||||||
class OsobaAdmin(admin.ModelAdmin):
|
|
||||||
actions = ['synchronizuj_maily', 'udelej_orgem']
|
|
||||||
|
|
||||||
def synchronizuj_maily(self, request, queryset):
|
|
||||||
for o in queryset:
|
|
||||||
if o.user is not None:
|
|
||||||
u = o.user
|
|
||||||
u.email = o.email
|
|
||||||
u.save()
|
|
||||||
self.message_user(request, "E-maily synchronizovány.")
|
|
||||||
synchronizuj_maily.short_description = "Synchronizuj vybraným osobám e-maily do uživatelů"
|
|
||||||
|
|
||||||
def udelej_orgem(self,request,queryset):
|
|
||||||
org_group = Group.objects.get(name='org')
|
|
||||||
print(queryset)
|
|
||||||
for o in queryset:
|
|
||||||
user = o.user
|
|
||||||
print(user)
|
|
||||||
user.groups.add(org_group)
|
|
||||||
user.is_staff = True
|
|
||||||
user.save()
|
|
||||||
org = m.Organizator.objects.create(osoba=o)
|
|
||||||
org.save()
|
|
||||||
udelej_orgem.short_description = "Udělej vybraných osob organizátory"
|
|
||||||
|
|
||||||
@admin.register(m.Organizator)
|
|
||||||
class OrganizatorAdmin(admin.ModelAdmin):
|
|
||||||
search_fields = ['osoba__jmeno', 'osoba__prijmeni', 'osoba__prezdivka']
|
|
||||||
|
|
||||||
class OsobaInline(admin.TabularInline):
|
|
||||||
model = m.Osoba
|
|
||||||
|
|
||||||
@admin.register(m.Resitel)
|
|
||||||
class ResitelAdmin(ReverseModelAdmin):
|
|
||||||
search_fields = ['osoba__jmeno', 'osoba__prijmeni', 'osoba__prezdivka']
|
|
||||||
ordering = ('osoba__jmeno','osoba__prijmeni')
|
|
||||||
inline_type = 'stacked'
|
|
||||||
inline_reverse = ['osoba']
|
|
||||||
|
|
||||||
@admin.register(m.Problem)
|
@admin.register(m.Problem)
|
||||||
class ProblemAdmin(PolymorphicParentModelAdmin):
|
class ProblemAdmin(PolymorphicParentModelAdmin):
|
||||||
|
|
213
seminar/forms.py
213
seminar/forms.py
|
@ -1,225 +1,12 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from dal import autocomplete
|
|
||||||
from django.contrib.auth.forms import PasswordResetForm
|
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
|
|
||||||
from .models import Skola, Resitel, Osoba
|
|
||||||
import seminar.models as m
|
import seminar.models as m
|
||||||
|
|
||||||
from datetime import date
|
|
||||||
import logging
|
|
||||||
|
|
||||||
# pro přidání políčka do formuláře je potřeba
|
# pro přidání políčka do formuláře je potřeba
|
||||||
# - mít v modelu tu položku, kterou chci upravovat
|
# - mít v modelu tu položku, kterou chci upravovat
|
||||||
# - přidat do views (prihlaskaView, resitelEditView)
|
# - přidat do views (prihlaskaView, resitelEditView)
|
||||||
# - přidat do forms
|
# - přidat do forms
|
||||||
# - includovat do html
|
# - includovat do html
|
||||||
|
|
||||||
class DateInput(forms.DateInput):
|
|
||||||
# aby se datum dalo vybírat z kalendáře
|
|
||||||
input_type = 'date'
|
|
||||||
|
|
||||||
class TelInput(forms.TextInput):
|
|
||||||
# tohle je možná k niřemu, ale alepsoň to mění input type a nic to nekazí
|
|
||||||
input_type = 'tel'
|
|
||||||
input_pattern="^[+]?[()/0-9. -]{9,}$"
|
|
||||||
|
|
||||||
|
|
||||||
class PrihlaskaForm(PasswordResetForm):
|
|
||||||
username = forms.CharField(label='Přihlašovací jméno',
|
|
||||||
max_length=256,
|
|
||||||
required=True,
|
|
||||||
help_text='Tímto jménem se následně budeš přihlašovat pro odevzdání řešení a další činnosti v semináři')
|
|
||||||
|
|
||||||
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 a číslo popisné', 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)
|
|
||||||
|
|
||||||
skola = forms.ModelChoiceField(label="Škola",
|
|
||||||
queryset=Skola.objects.all(),
|
|
||||||
widget=autocomplete.ModelSelect2(
|
|
||||||
url='autocomplete_skola',
|
|
||||||
attrs = {'data-placeholder--id': '-1',
|
|
||||||
'data-placeholder--text' : '---',
|
|
||||||
'data-allow-clear': 'true'})
|
|
||||||
,required=False)
|
|
||||||
|
|
||||||
skola_nazev = forms.CharField(label='Název školy', max_length=256, required=False)
|
|
||||||
skola_adresa = forms.CharField(label='Adresa školy', max_length=256, required=False)
|
|
||||||
|
|
||||||
# trida = forms.CharField(label='Třída',max_length=10, required=True)
|
|
||||||
|
|
||||||
rok_maturity = forms.IntegerField(
|
|
||||||
label='Rok maturity',
|
|
||||||
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)
|
|
||||||
zasilat_cislo_emailem = forms.BooleanField(label='Chci dostávat e-mailem upozornění na vydání nového čísla', required=False)
|
|
||||||
|
|
||||||
gdpr = forms.BooleanField(label='Souhlasím se zpracováním osobních údajů', required=True)
|
|
||||||
spam = forms.BooleanField(label='Souhlasím se zasíláním materiálů od MFF UK', required=False)
|
|
||||||
|
|
||||||
def clean_username(self):
|
|
||||||
err_logger = logging.getLogger('seminar.prihlaska.problem')
|
|
||||||
username = self.cleaned_data.get('username')
|
|
||||||
try:
|
|
||||||
User.objects.get(username=username)
|
|
||||||
msg = "Username {} exists".format(username)
|
|
||||||
err_logger.info(msg)
|
|
||||||
raise forms.ValidationError('Přihlašovací jméno je již použito')
|
|
||||||
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
pass
|
|
||||||
return username
|
|
||||||
|
|
||||||
def clean_email(self):
|
|
||||||
err_logger = logging.getLogger('seminar.prihlaska.problem')
|
|
||||||
email = self.cleaned_data.get('email')
|
|
||||||
try:
|
|
||||||
osoba = Osoba.objects.get(email=email)
|
|
||||||
msg = "Email {} exists".format(email)
|
|
||||||
if osoba.user is not None:
|
|
||||||
err_logger.info(msg)
|
|
||||||
raise forms.ValidationError('E-mail je již použit')
|
|
||||||
else:
|
|
||||||
msg += ', but currently has no User, so allowing registration.'
|
|
||||||
err_logger.info(msg)
|
|
||||||
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
pass
|
|
||||||
return email
|
|
||||||
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
super().clean()
|
|
||||||
|
|
||||||
err_logger = logging.getLogger('seminar.prihlaska.problem')
|
|
||||||
|
|
||||||
data = self.cleaned_data
|
|
||||||
if data.get('stat') != 'other' and data.get('stat_text') != '':
|
|
||||||
self.add_error('stat',forms.ValidationError('Nelze mít vybraný stát z menu a zároven zapsaný textem'))
|
|
||||||
if data.get('skola') and (data.get('skola_nazev') or data.get('skola_adresa')):
|
|
||||||
self.add_error('skola',forms.ValidationError('Pokud je škola v seznamu, nevypisujte ji ručně, pokud není, zrušte výběr ze seznamu (křížek vpravo)'))
|
|
||||||
if not data.get('skola'):
|
|
||||||
if data.get('skola_nazev')=='' and data.get('skola_adresa')=='':
|
|
||||||
self.add_error('skola',forms.ValidationError('Je nutné vyplnit školu'))
|
|
||||||
elif data.get('skola_nazev')=='':
|
|
||||||
self.add_error('skola_nazev',forms.ValidationError('Je nutné vyplnit název školy'))
|
|
||||||
elif data.get('skola_adresa')=='':
|
|
||||||
self.add_error('skola_adresa',forms.ValidationError('Je nutné vyplnit adresu školy'))
|
|
||||||
|
|
||||||
|
|
||||||
class ProfileEditForm(forms.Form):
|
|
||||||
username = forms.CharField(label='Přihlašovací jméno',
|
|
||||||
max_length=256,
|
|
||||||
required=False,
|
|
||||||
disabled=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)
|
|
||||||
|
|
||||||
skola = forms.ModelChoiceField(label="Škola",
|
|
||||||
queryset=Skola.objects.all(),
|
|
||||||
widget=autocomplete.ModelSelect2(
|
|
||||||
url='autocomplete_skola',
|
|
||||||
attrs = {'data-placeholder--id': '-1',
|
|
||||||
'data-placeholder--text' : '---',
|
|
||||||
'data-allow-clear': 'true'})
|
|
||||||
,required=False)
|
|
||||||
|
|
||||||
skola_nazev = forms.CharField(label='Název školy', max_length=256, required=False)
|
|
||||||
skola_adresa = forms.CharField(label='Adresa školy', max_length=256, required=False)
|
|
||||||
|
|
||||||
# trida = forms.CharField(label='Třída',max_length=10, required=True)
|
|
||||||
|
|
||||||
rok_maturity = forms.IntegerField(
|
|
||||||
label='Rok maturity',
|
|
||||||
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)
|
|
||||||
zasilat_cislo_emailem = forms.BooleanField(label='Chci dostávat email s upozorněním na vydání nového čísla', required=False)
|
|
||||||
|
|
||||||
spam = forms.BooleanField(label='Souhlasím se zasíláním materiálů od MFF UK', required=False)
|
|
||||||
# def clean_username(self):
|
|
||||||
# err_logger = logging.getLogger('seminar.prihlaska.problem')
|
|
||||||
# username = self.cleaned_data.get('username')
|
|
||||||
# try:
|
|
||||||
# User.objects.get(username=username)
|
|
||||||
# msg = "Username {} exists".format(username)
|
|
||||||
# err_logger.info(msg)
|
|
||||||
# raise forms.ValidationError('Přihlašovací jméno je již použito')
|
|
||||||
#
|
|
||||||
# except ObjectDoesNotExist:
|
|
||||||
# pass
|
|
||||||
# return username
|
|
||||||
#
|
|
||||||
def clean_email(self):
|
|
||||||
err_logger = logging.getLogger('seminar.prihlaska.problem')
|
|
||||||
email = self.cleaned_data.get('email')
|
|
||||||
try:
|
|
||||||
Osoba.objects.exclude(user__username=self.username).get(email=email)
|
|
||||||
msg = "Email {} exists (in edit)".format(email)
|
|
||||||
err_logger.info(msg)
|
|
||||||
raise forms.ValidationError('Email je již použit')
|
|
||||||
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
pass
|
|
||||||
return email
|
|
||||||
#def clean(self):
|
|
||||||
# super().clean()
|
|
||||||
#
|
|
||||||
# err_logger = logging.getLogger('seminar.prihlaska.problem')
|
|
||||||
|
|
||||||
# data = self.cleaned_data
|
|
||||||
# if data.get('password') != data.get('password_check'):
|
|
||||||
# self.add_error('password_check',forms.ValidationError('Hesla se neshodují'))
|
|
||||||
# if data.get('stat') != '' and data.get('stat_text') != '':
|
|
||||||
# self.add_error('stat',forms.ValidationError('Nelze mít vybraný stát z menu a zároven zapsaný textem'))
|
|
||||||
# if data.get('skola') and (data.get('skola_nazev') or data.get('skola_adresa')):
|
|
||||||
# self.add_error('skola',forms.ValidationError('Pokud je škola v seznamu, nevypisujte ji ručně, pokud není, zrušte výběr ze seznamu (křížek vpravo)'))
|
|
||||||
# if not data.get('skola'):
|
|
||||||
# if data.get('skola_nazev')=='' and data.get('skola_adresa')=='':
|
|
||||||
# self.add_error('skola',forms.ValidationError('Je nutné vyplnit školu'))
|
|
||||||
# elif data.get('skola_nazev')=='':
|
|
||||||
# self.add_error('skola_nazev',forms.ValidationError('Je nutné vyplnit název školy'))
|
|
||||||
# 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 NahrajObrazekKTreeNoduForm(forms.ModelForm):
|
class NahrajObrazekKTreeNoduForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
from .models_all import *
|
from .models_all import *
|
||||||
from .odevzdavatko import *
|
from .odevzdavatko import *
|
||||||
|
from .base import *
|
||||||
|
from .personalni import *
|
||||||
|
|
22
seminar/models/base.py
Normal file
22
seminar/models/base.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class SeminarModelBase(models.Model):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
def verejne(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# def get_absolute_url(self):
|
||||||
|
# return "https://" + str(get_current_site(None)) + self.verejne_url()
|
||||||
|
|
||||||
|
def admin_url(self):
|
||||||
|
model_name = self.__class__.__name__.lower()
|
||||||
|
return reverse('admin:seminar_{}_change'.format(model_name), args=(self.id, ))
|
||||||
|
|
||||||
|
# def verejne_url(self):
|
||||||
|
# return None
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import os
|
import os
|
||||||
import random
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import pathlib
|
import pathlib
|
||||||
import tempfile
|
import tempfile
|
||||||
|
@ -8,21 +7,17 @@ import logging
|
||||||
|
|
||||||
from django.contrib.sites.shortcuts import get_current_site
|
from django.contrib.sites.shortcuts import get_current_site
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib import auth
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.encoding import force_text
|
from django.urls import reverse
|
||||||
from django.utils.text import slugify
|
|
||||||
from django.urls import reverse, reverse_lazy
|
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.utils.text import get_valid_filename
|
from django.utils.text import get_valid_filename
|
||||||
from imagekit.models import ImageSpecField, ProcessedImageField
|
from imagekit.models import ImageSpecField
|
||||||
from imagekit.processors import ResizeToFit, Transpose
|
from imagekit.processors import ResizeToFit
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
from django_countries.fields import CountryField
|
|
||||||
from solo.models import SingletonModel
|
from solo.models import SingletonModel
|
||||||
from taggit.managers import TaggableManager
|
from taggit.managers import TaggableManager
|
||||||
|
|
||||||
|
@ -39,394 +34,12 @@ from polymorphic.models import PolymorphicModel
|
||||||
from django.core.mail import EmailMessage
|
from django.core.mail import EmailMessage
|
||||||
from seminar.utils import aktivniResitele
|
from seminar.utils import aktivniResitele
|
||||||
|
|
||||||
|
from . import personalni as pm
|
||||||
|
|
||||||
|
from .base import SeminarModelBase
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class SeminarModelBase(models.Model):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
def verejne(self):
|
|
||||||
return False
|
|
||||||
|
|
||||||
# def get_absolute_url(self):
|
|
||||||
# return "https://" + str(get_current_site(None)) + self.verejne_url()
|
|
||||||
|
|
||||||
def admin_url(self):
|
|
||||||
model_name = self.__class__.__name__.lower()
|
|
||||||
return reverse('admin:seminar_{}_change'.format(model_name), args=(self.id, ))
|
|
||||||
|
|
||||||
# def verejne_url(self):
|
|
||||||
# return None
|
|
||||||
|
|
||||||
@reversion.register(ignore_duplicates=True)
|
|
||||||
class Osoba(SeminarModelBase):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
db_table = 'seminar_osoby'
|
|
||||||
verbose_name = 'Osoba'
|
|
||||||
verbose_name_plural = 'Osoby'
|
|
||||||
ordering = ['prijmeni','jmeno']
|
|
||||||
|
|
||||||
id = models.AutoField(primary_key = True)
|
|
||||||
|
|
||||||
jmeno = models.CharField('jméno', max_length=256)
|
|
||||||
|
|
||||||
prijmeni = models.CharField('příjmení', max_length=256)
|
|
||||||
|
|
||||||
prezdivka = models.CharField('přezdívka', blank=True, null=True, max_length=256)
|
|
||||||
|
|
||||||
# User, pokud má na webu účet
|
|
||||||
user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=True, null=True,
|
|
||||||
verbose_name='uživatel', on_delete=models.DO_NOTHING)
|
|
||||||
|
|
||||||
# Pohlaví. Že ho neznáme se snad nestane (a ušetří to práci při programování)
|
|
||||||
pohlavi_muz = models.BooleanField('pohlaví (muž)', default=False)
|
|
||||||
|
|
||||||
email = models.EmailField('e-mail', max_length=256, blank=True, default='')
|
|
||||||
|
|
||||||
telefon = models.CharField('telefon', max_length=256, blank=True, default='')
|
|
||||||
|
|
||||||
datum_narozeni = models.DateField('datum narození', blank=True, null=True)
|
|
||||||
|
|
||||||
# NULL dokud nedali souhlas
|
|
||||||
datum_souhlasu_udaje = models.DateField('datum souhlasu (údaje)', blank=True, null=True,
|
|
||||||
help_text='Datum souhlasu se zpracováním osobních údajů')
|
|
||||||
|
|
||||||
# NULL dokud nedali souhlas
|
|
||||||
datum_souhlasu_zasilani = models.DateField('datum souhlasu (spam)', blank=True, null=True,
|
|
||||||
help_text='Datum souhlasu se zasíláním MFF materiálů')
|
|
||||||
|
|
||||||
# Alespoň odhad (rok či i měsíc)
|
|
||||||
datum_registrace = models.DateField('datum registrace do semináře', default=timezone.now)
|
|
||||||
|
|
||||||
# Ulice může být i jen číslo
|
|
||||||
ulice = models.CharField('ulice', max_length=256, blank=True, default='')
|
|
||||||
|
|
||||||
mesto = models.CharField('město', max_length=256, blank=True, default='')
|
|
||||||
|
|
||||||
psc = models.CharField('PSČ', max_length=32, blank=True, default='')
|
|
||||||
|
|
||||||
# ISO 3166-1 dvojznakovy kod zeme velkym pismem (CZ, SK)
|
|
||||||
# Ekvivalentní s CharField(max_length=2, default='CZ', ...)
|
|
||||||
stat = CountryField('stát', default='CZ',
|
|
||||||
help_text='ISO 3166-1 kód země velkými písmeny (CZ, SK, ...)')
|
|
||||||
|
|
||||||
poznamka = models.TextField('neveřejná poznámka', blank=True,
|
|
||||||
help_text='Neveřejná poznámka k osobě (plain text)')
|
|
||||||
|
|
||||||
foto = ProcessedImageField(verbose_name='Fotografie osoby',
|
|
||||||
upload_to='image_osoby/velke/%Y/', null = True, blank = True,
|
|
||||||
help_text = 'Vlož fotografii osoby o libovolné velikosti',
|
|
||||||
processors=[
|
|
||||||
Transpose(Transpose.AUTO),
|
|
||||||
ResizeToFit(500, 500, upscale=False)
|
|
||||||
],
|
|
||||||
options={'quality': 95})
|
|
||||||
foto_male = ImageSpecField(source='foto',
|
|
||||||
processors=[
|
|
||||||
ResizeToFit(200, 200, upscale=False)
|
|
||||||
],
|
|
||||||
options={'quality': 95})
|
|
||||||
|
|
||||||
# má OneToOneField nejvýše s:
|
|
||||||
# Resitel
|
|
||||||
# Prijemce
|
|
||||||
# Organizator
|
|
||||||
|
|
||||||
def plne_jmeno(self):
|
|
||||||
return '{} {}'.format(self.jmeno, self.prijmeni)
|
|
||||||
|
|
||||||
def inicial_krestni(self):
|
|
||||||
jmena = self.jmeno.split()
|
|
||||||
return " ".join(['{}.'.format(jmeno[0]) for jmeno in jmena])
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.plne_jmeno()
|
|
||||||
|
|
||||||
# Overridujeme save Osoby, aby když si změní e-mail, aby se projevil i v
|
|
||||||
# Userovi (a tak se dal poslat mail s resetem hesla)
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
if self.user is not None:
|
|
||||||
u = self.user
|
|
||||||
# U svatého tučňáka, prosím ať tohle funguje.
|
|
||||||
# (Takhle se kódit asi nemá...)
|
|
||||||
u.email = self.email
|
|
||||||
u.save()
|
|
||||||
super().save()
|
|
||||||
|
|
||||||
#
|
|
||||||
# Mělo by být částečně vytaženo z Aesopa
|
|
||||||
# viz https://ovvp.mff.cuni.cz/wiki/aesop/export-skol.
|
|
||||||
#
|
|
||||||
|
|
||||||
@reversion.register(ignore_duplicates=True)
|
|
||||||
class Skola(SeminarModelBase):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
db_table = 'seminar_skoly'
|
|
||||||
verbose_name = 'Škola'
|
|
||||||
verbose_name_plural = 'Školy'
|
|
||||||
ordering = ['mesto', 'nazev']
|
|
||||||
|
|
||||||
# Interní ID
|
|
||||||
id = models.AutoField(primary_key = True)
|
|
||||||
|
|
||||||
# Aesopi ID "izo:..." nebo "aesop:..."
|
|
||||||
# NULL znamená v exportu do aesopa "ufo"
|
|
||||||
aesop_id = models.CharField('Aesop ID', max_length=32, blank=True, default='',
|
|
||||||
help_text='Aesopi ID typu "izo:..." nebo "aesop:..."')
|
|
||||||
|
|
||||||
# IZO školy (jen české školy)
|
|
||||||
izo = models.CharField('IZO', max_length=32, blank=True,
|
|
||||||
help_text='IZO školy (jen české školy)')
|
|
||||||
|
|
||||||
# Celý název školy
|
|
||||||
nazev = models.CharField('název', max_length=256,
|
|
||||||
help_text='Celý název školy')
|
|
||||||
|
|
||||||
# Zkraceny nazev pro zobrazení ve výsledkovce, volitelné.
|
|
||||||
# Není v Aesopovi, musíme vytvářet sami.
|
|
||||||
kratky_nazev = models.CharField('zkrácený název', max_length=256, blank=True,
|
|
||||||
help_text="Zkrácený název pro zobrazení ve výsledkovce")
|
|
||||||
|
|
||||||
# Ulice může být jen číslo
|
|
||||||
ulice = models.CharField('ulice', max_length=256)
|
|
||||||
|
|
||||||
mesto = models.CharField('město', max_length=256)
|
|
||||||
|
|
||||||
psc = models.CharField('PSČ', max_length=32)
|
|
||||||
|
|
||||||
# ISO 3166-1 dvojznakovy kod zeme velkym pismem (CZ, SK)
|
|
||||||
# Ekvivalentní s CharField(max_length=2, default='CZ', ...)
|
|
||||||
stat = CountryField('stát', default='CZ',
|
|
||||||
help_text='ISO 3166-1 kód země velkými písmeny (CZ, SK, ...)')
|
|
||||||
|
|
||||||
# Jaké vzdělání škpla poskytuje?
|
|
||||||
je_zs = models.BooleanField('základní stupeň', default=True)
|
|
||||||
je_ss = models.BooleanField('střední stupeň', default=True)
|
|
||||||
|
|
||||||
poznamka = models.TextField('neveřejná poznámka', blank=True,
|
|
||||||
help_text='Neveřejná poznámka ke škole (plain text)')
|
|
||||||
|
|
||||||
kontaktni_osoba = models.ForeignKey(Osoba, verbose_name='Kontaktní osoba',
|
|
||||||
blank=True, null=True, on_delete=models.SET_NULL)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return '{}, {}, {}'.format(self.nazev, self.ulice, self.mesto)
|
|
||||||
|
|
||||||
class Prijemce(SeminarModelBase):
|
|
||||||
class Meta:
|
|
||||||
db_table = 'seminar_prijemce'
|
|
||||||
verbose_name = 'příjemce'
|
|
||||||
verbose_name_plural = 'příjemce'
|
|
||||||
|
|
||||||
|
|
||||||
# Interní ID
|
|
||||||
id = models.AutoField(primary_key = True)
|
|
||||||
|
|
||||||
poznamka = models.TextField('neveřejná poznámka', blank=True,
|
|
||||||
help_text='Neveřejná poznámka k příemci čísel (plain text)')
|
|
||||||
|
|
||||||
osoba = models.OneToOneField(Osoba, verbose_name='komu', blank=False, null=False,
|
|
||||||
help_text='Které osobě či na jakou adresu se mají zasílat čísla',
|
|
||||||
on_delete=models.CASCADE)
|
|
||||||
|
|
||||||
# FIXME: možná chceme něco jako vazbu na osobu XOR školu a počet kusů k zaslání
|
|
||||||
# FIXME: a možná taky posílání na mail a možná taky přes něj chceme posílat i řešitelům
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.osoba.plne_jmeno()
|
|
||||||
|
|
||||||
|
|
||||||
@reversion.register(ignore_duplicates=True)
|
|
||||||
class Resitel(SeminarModelBase):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
db_table = 'seminar_resitele'
|
|
||||||
verbose_name = 'Řešitel'
|
|
||||||
verbose_name_plural = 'Řešitelé'
|
|
||||||
ordering = ['osoba']
|
|
||||||
|
|
||||||
# Interní ID
|
|
||||||
id = models.AutoField(primary_key = True)
|
|
||||||
|
|
||||||
osoba = models.OneToOneField(Osoba, blank=False, null=False, verbose_name='osoba',
|
|
||||||
on_delete=models.PROTECT)
|
|
||||||
|
|
||||||
|
|
||||||
skola = models.ForeignKey(Skola, blank=True, null=True, verbose_name='škola',
|
|
||||||
on_delete=models.SET_NULL)
|
|
||||||
|
|
||||||
# Očekávaný rok maturity a vyřazení z aktivních řešitelů
|
|
||||||
rok_maturity = models.IntegerField('rok maturity', blank=True, null=True)
|
|
||||||
|
|
||||||
ZASILAT_DOMU = 'domu'
|
|
||||||
ZASILAT_DO_SKOLY = 'do_skoly'
|
|
||||||
ZASILAT_NIKAM = 'nikam'
|
|
||||||
ZASILAT_CHOICES = [
|
|
||||||
(ZASILAT_DOMU, 'Domů'),
|
|
||||||
(ZASILAT_DO_SKOLY, 'Do školy'),
|
|
||||||
(ZASILAT_NIKAM, 'Nikam'),
|
|
||||||
]
|
|
||||||
|
|
||||||
zasilat = models.CharField('kam zasílat', max_length=32, choices=ZASILAT_CHOICES, blank=False, default=ZASILAT_DOMU)
|
|
||||||
|
|
||||||
zasilat_cislo_emailem = models.BooleanField('zasílat číslo emailem', help_text='True pokud chce řešitel dostávat číslo emailem', default=False)
|
|
||||||
|
|
||||||
poznamka = models.TextField('neveřejná poznámka', blank=True,
|
|
||||||
help_text='Neveřejná poznámka k řešiteli (plain text)')
|
|
||||||
|
|
||||||
|
|
||||||
def export_row(self):
|
|
||||||
"Slovnik pro pouziti v AESOP exportu"
|
|
||||||
return {
|
|
||||||
'id': self.id,
|
|
||||||
'name': self.osoba.jmeno,
|
|
||||||
'surname': self.osoba.prijmeni,
|
|
||||||
'gender': 'M' if self.osoba.pohlavi_muz else 'F',
|
|
||||||
'born': self.osoba.datum_narozeni.isoformat() if self.osoba.datum_narozeni else '',
|
|
||||||
'email': self.osoba.email,
|
|
||||||
'end-year': self.rok_maturity or '',
|
|
||||||
|
|
||||||
'street': self.osoba.ulice,
|
|
||||||
'town': self.osoba.mesto,
|
|
||||||
'postcode': self.osoba.psc,
|
|
||||||
'country': self.osoba.stat,
|
|
||||||
|
|
||||||
'spam-flag': 'Y' if self.osoba.datum_souhlasu_zasilani else '',
|
|
||||||
'spam-date': self.osoba.datum_souhlasu_zasilani.isoformat() if self.osoba.datum_souhlasu_zasilani else '',
|
|
||||||
|
|
||||||
'school': self.skola.aesop_id if self.skola else '',
|
|
||||||
'school-name': str(self.skola) if self.skola else 'Skola neni znama',
|
|
||||||
}
|
|
||||||
|
|
||||||
def rocnik(self, rocnik):
|
|
||||||
"""Vrati skolni rocnik resitele pro zadany Rocnik.
|
|
||||||
Vraci '' pro neznamy rok maturity resitele, Z* pro ekvivalent ZŠ."""
|
|
||||||
if self.rok_maturity is None:
|
|
||||||
return ''
|
|
||||||
rozdil = 5 - (self.rok_maturity - rocnik.prvni_rok)
|
|
||||||
if rozdil >= 1:
|
|
||||||
return str(rozdil)
|
|
||||||
else:
|
|
||||||
return 'Z' + str(rozdil + 9)
|
|
||||||
|
|
||||||
def vsechny_body(self):
|
|
||||||
"Spočítá body odjakživa."
|
|
||||||
vsechna_reseni = self.reseni_set.all()
|
|
||||||
from .odevzdavatko import Hodnoceni
|
|
||||||
vsechna_hodnoceni = Hodnoceni.objects.filter(
|
|
||||||
reseni__in=vsechna_reseni)
|
|
||||||
return sum(h.body for h in list(vsechna_hodnoceni) if h.body is not None)
|
|
||||||
|
|
||||||
|
|
||||||
def get_titul(self, body=None):
|
|
||||||
"Vrati titul jako řetězec."
|
|
||||||
|
|
||||||
# Nejprve si zadefinujeme titul
|
|
||||||
from enum import Enum
|
|
||||||
from functools import total_ordering
|
|
||||||
@total_ordering
|
|
||||||
class Titul(Enum):
|
|
||||||
""" Třída reprezentující možné tituly. Hodnoty jsou dvojice (dolní hranice, stringifikace). """
|
|
||||||
nic = (0, '')
|
|
||||||
bc = (20, 'Bc.')
|
|
||||||
mgr = (50, 'Mgr.')
|
|
||||||
dr = (100, 'Dr.')
|
|
||||||
doc = (200, 'Doc.')
|
|
||||||
prof = (500, 'Prof.')
|
|
||||||
akad = (1000, 'Akad.')
|
|
||||||
|
|
||||||
def __lt__(self, other):
|
|
||||||
return True if self.value[0] < other.value[0] else False
|
|
||||||
def __eq__(self, other): # Měla by být implicitní, ale klidně explicitně.
|
|
||||||
return True if self.value[0] == other.value[0] else False
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.value[1]
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def z_bodu(cls, body):
|
|
||||||
aktualni = cls.nic
|
|
||||||
# TODO: ověřit, že to funguje
|
|
||||||
for titul in cls: # Kdyžtak použít __members__.items()
|
|
||||||
if titul.value[0] <= body:
|
|
||||||
aktualni = titul
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
return aktualni
|
|
||||||
|
|
||||||
# Hledáme body v databázi
|
|
||||||
# V listopadu 2020 jsme se na filosofické schůzce shodli o změně hranic titulů:
|
|
||||||
# - body z 25. ročníku a dříve byly shledány dvakrát hodnotnějšími
|
|
||||||
# - proto se započítávají dvojnásobně a byly posunuté hranice titulů
|
|
||||||
# - staré tituly se ale nemají odebrat, pokud řešitel v t.č. minulém (26.) ročníku měl titul, má ho mít pořád.
|
|
||||||
from .odevzdavatko import Hodnoceni
|
|
||||||
hodnoceni_do_25_rocniku = Hodnoceni.objects.filter(cislo_body__rocnik__rocnik__lte=25,reseni__in=self.reseni_set.all())
|
|
||||||
novejsi_hodnoceni = Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()).difference(hodnoceni_do_25_rocniku)
|
|
||||||
|
|
||||||
def body_z_hodnoceni(hh : list):
|
|
||||||
return sum(h.body for h in hh if h.body is not None)
|
|
||||||
|
|
||||||
stare_body = body_z_hodnoceni(hodnoceni_do_25_rocniku)
|
|
||||||
if body is None:
|
|
||||||
nove_body = body_z_hodnoceni(novejsi_hodnoceni)
|
|
||||||
else:
|
|
||||||
# Zjistíme, kolik bodů jsou staré, tedy hodnotnější
|
|
||||||
nove_body = max(0, body - stare_body) # Všechny body nad počet původních hodnotnějších
|
|
||||||
stare_body = min(stare_body, body) # Skutečný počet hodnotnějších bodů
|
|
||||||
logicke_body = 2*stare_body + nove_body
|
|
||||||
|
|
||||||
|
|
||||||
# Titul se určí následovně:
|
|
||||||
# - Pokud se řeší body, které jsou starší, než do 26 ročníku (včetně), dáváme tituly postaru.
|
|
||||||
# - Jinak dáváme tituly po novu...
|
|
||||||
# - ... ale titul se nesmí odebrat, pokud se zmenšil.
|
|
||||||
def titul_do_26_rocniku(body):
|
|
||||||
""" Původní hranice bodů za tituly """
|
|
||||||
if body < 10:
|
|
||||||
return Titul.nic
|
|
||||||
elif body < 20:
|
|
||||||
return Titul.bc
|
|
||||||
elif body < 50:
|
|
||||||
return Titul.mgr
|
|
||||||
elif body < 100:
|
|
||||||
return Titul.dr
|
|
||||||
elif body < 200:
|
|
||||||
return Titul.doc
|
|
||||||
elif body < 500:
|
|
||||||
return Titul.prof
|
|
||||||
else:
|
|
||||||
return Titul.akad
|
|
||||||
|
|
||||||
from .odevzdavatko import Hodnoceni
|
|
||||||
hodnoceni_do_26_rocniku = Hodnoceni.objects.filter(cislo_body__rocnik__rocnik__lte=26,reseni__in=self.reseni_set.all())
|
|
||||||
novejsi_body = body_z_hodnoceni(
|
|
||||||
Hodnoceni.objects.filter(reseni__in=self.reseni_set.all())
|
|
||||||
.difference(hodnoceni_do_26_rocniku)
|
|
||||||
)
|
|
||||||
starsi_body = body_z_hodnoceni(hodnoceni_do_26_rocniku)
|
|
||||||
if body is not None:
|
|
||||||
# Ještě z toho vybereme ty správně staré body
|
|
||||||
novejsi_body = max(0, body - starsi_body)
|
|
||||||
starsi_body = min(starsi_body, body)
|
|
||||||
|
|
||||||
# Titul pro 26. ročník
|
|
||||||
stary_titul = titul_do_26_rocniku(starsi_body)
|
|
||||||
# Titul podle aktuálních pravidel
|
|
||||||
novy_titul = Titul.z_bodu(logicke_body)
|
|
||||||
|
|
||||||
if novejsi_body == 0:
|
|
||||||
# Žádné nové body -- titul podle starých pravidel
|
|
||||||
return str(stary_titul)
|
|
||||||
return str(max(novy_titul, stary_titul))
|
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.osoba.plne_jmeno()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@reversion.register(ignore_duplicates=True)
|
@reversion.register(ignore_duplicates=True)
|
||||||
class Rocnik(SeminarModelBase):
|
class Rocnik(SeminarModelBase):
|
||||||
|
@ -703,59 +316,6 @@ class Cislo(SeminarModelBase):
|
||||||
if self.datum_deadline_soustredeni is not None and self.datum_deadline_soustredeni > self.datum_deadline:
|
if self.datum_deadline_soustredeni is not None and self.datum_deadline_soustredeni > self.datum_deadline:
|
||||||
raise ValidationError({'datum_deadline_soustredeni': "Soustřeďkový deadline musí předcházet finálnímu deadlinu"})
|
raise ValidationError({'datum_deadline_soustredeni': "Soustřeďkový deadline musí předcházet finálnímu deadlinu"})
|
||||||
|
|
||||||
@reversion.register(ignore_duplicates=True)
|
|
||||||
class Organizator(SeminarModelBase):
|
|
||||||
# zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu
|
|
||||||
|
|
||||||
osoba = models.OneToOneField(Osoba, verbose_name='osoba', related_name='org',
|
|
||||||
help_text='osobní údaje organizátora', null=False, blank=False,
|
|
||||||
on_delete=models.PROTECT)
|
|
||||||
|
|
||||||
vytvoreno = models.DateTimeField(
|
|
||||||
'Vytvořeno',
|
|
||||||
default=timezone.now,
|
|
||||||
blank=True,
|
|
||||||
editable=False
|
|
||||||
)
|
|
||||||
|
|
||||||
organizuje_od = models.DateTimeField('Organizuje od', blank=True, null=True)
|
|
||||||
|
|
||||||
organizuje_do = models.DateTimeField('Organizuje do', blank=True, null=True)
|
|
||||||
|
|
||||||
studuje = models.CharField('Studium aj.', max_length = 256,
|
|
||||||
null = True, blank = True,
|
|
||||||
help_text="Např. 'Studuje Obecnou fyziku (Bc.), 3. ročník', "
|
|
||||||
"'Vystudovala Diskrétní modely a algoritmy (Mgr.)' nebo "
|
|
||||||
"'Přednáší na MFF'")
|
|
||||||
|
|
||||||
strucny_popis_organizatora = models.TextField('Stručný popis organizátora',
|
|
||||||
null = True, blank = True)
|
|
||||||
|
|
||||||
skola = models.CharField('Škola, kterou studuje', max_length = 256, null=True, blank=True,
|
|
||||||
help_text="Škola, např. MFF, VŠCHT, VUT, ... prostě aby se nemuselo psát do studuje"
|
|
||||||
"školu, ale jen obor, možnost zobrazit zvlášť")
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
if self.organizuje_od and self.organizuje_do and (self.organizuje_od > self.organizuje_do):
|
|
||||||
raise ValidationError("Organizátor nemůže skončit s organizováním dříve než začal!")
|
|
||||||
super().clean()
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
if self.osoba.prezdivka:
|
|
||||||
return "{} '{}' {}".format(self.osoba.jmeno,
|
|
||||||
self.osoba.prezdivka,
|
|
||||||
self.osoba.prijmeni)
|
|
||||||
else:
|
|
||||||
return "{} {}".format(self.osoba.jmeno, self.osoba.prijmeni)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = 'Organizátor'
|
|
||||||
verbose_name_plural = 'Organizátoři'
|
|
||||||
# Řadí aktivní orgy na začátek, pod tím v pořadí od nejstarších neaktivní orgy.
|
|
||||||
# TODO: Chtěl bych spíš mít nejstarší orgy dole.
|
|
||||||
# TODO: Zohledňovat přezdívky?
|
|
||||||
# TODO: Sjednotit s tím, jak se řadí organizátoři v seznau orgů na webu
|
|
||||||
ordering = ['-organizuje_do', 'osoba__jmeno', 'osoba__prijmeni']
|
|
||||||
|
|
||||||
@reversion.register(ignore_duplicates=True)
|
@reversion.register(ignore_duplicates=True)
|
||||||
class Soustredeni(SeminarModelBase):
|
class Soustredeni(SeminarModelBase):
|
||||||
|
@ -783,10 +343,10 @@ class Soustredeni(SeminarModelBase):
|
||||||
misto = models.CharField('místo soustředění', max_length=256, blank=True, default='',
|
misto = models.CharField('místo soustředění', max_length=256, blank=True, default='',
|
||||||
help_text='Místo (název obce, volitelně též objektu')
|
help_text='Místo (název obce, volitelně též objektu')
|
||||||
|
|
||||||
ucastnici = models.ManyToManyField(Resitel, verbose_name='účastníci soustředění',
|
ucastnici = models.ManyToManyField(pm.Resitel, verbose_name='účastníci soustředění',
|
||||||
help_text='Seznam účastníků soustředění', through='Soustredeni_Ucastnici')
|
help_text='Seznam účastníků soustředění', through='Soustredeni_Ucastnici')
|
||||||
|
|
||||||
organizatori = models.ManyToManyField(Organizator,
|
organizatori = models.ManyToManyField(pm.Organizator,
|
||||||
verbose_name='Organizátoři soustředění',
|
verbose_name='Organizátoři soustředění',
|
||||||
help_text='Seznam organizátorů soustředění',
|
help_text='Seznam organizátorů soustředění',
|
||||||
through='Soustredeni_Organizatori')
|
through='Soustredeni_Organizatori')
|
||||||
|
@ -865,15 +425,15 @@ class Problem(SeminarModelBase,PolymorphicModel):
|
||||||
poznamka = models.TextField('org poznámky (HTML)', blank=True,
|
poznamka = models.TextField('org poznámky (HTML)', blank=True,
|
||||||
help_text='Neveřejný návrh úlohy, návrh řešení, text zadání, poznámky ...')
|
help_text='Neveřejný návrh úlohy, návrh řešení, text zadání, poznámky ...')
|
||||||
|
|
||||||
autor = models.ForeignKey(Organizator, verbose_name='autor problému',
|
autor = models.ForeignKey(pm.Organizator, verbose_name='autor problému',
|
||||||
related_name='autor_problemu_%(class)s', null=True, blank=True,
|
related_name='autor_problemu_%(class)s', null=True, blank=True,
|
||||||
on_delete=models.SET_NULL)
|
on_delete=models.SET_NULL)
|
||||||
|
|
||||||
garant = models.ForeignKey(Organizator, verbose_name='garant zadaného problému',
|
garant = models.ForeignKey(pm.Organizator, verbose_name='garant zadaného problému',
|
||||||
related_name='garant_problemu_%(class)s', null=True, blank=True,
|
related_name='garant_problemu_%(class)s', null=True, blank=True,
|
||||||
on_delete=models.SET_NULL)
|
on_delete=models.SET_NULL)
|
||||||
|
|
||||||
opravovatele = models.ManyToManyField(Organizator, verbose_name='opravovatelé',
|
opravovatele = models.ManyToManyField(pm.Organizator, verbose_name='opravovatelé',
|
||||||
blank=True, related_name='opravovatele_%(class)s')
|
blank=True, related_name='opravovatele_%(class)s')
|
||||||
|
|
||||||
kod = models.CharField('lokální kód', max_length=32, blank=True, default='',
|
kod = models.CharField('lokální kód', max_length=32, blank=True, default='',
|
||||||
|
@ -1126,7 +686,7 @@ class Pohadka(SeminarModelBase):
|
||||||
id = models.AutoField(primary_key=True)
|
id = models.AutoField(primary_key=True)
|
||||||
|
|
||||||
autor = models.ForeignKey(
|
autor = models.ForeignKey(
|
||||||
Organizator,
|
pm.Organizator,
|
||||||
verbose_name="Autor pohádky",
|
verbose_name="Autor pohádky",
|
||||||
|
|
||||||
# Při nahrávání z TeXu není vyplnění vyžadováno, v adminu je
|
# Při nahrávání z TeXu není vyplnění vyžadováno, v adminu je
|
||||||
|
@ -1171,7 +731,7 @@ class Soustredeni_Ucastnici(SeminarModelBase):
|
||||||
# Interní ID
|
# Interní ID
|
||||||
id = models.AutoField(primary_key = True)
|
id = models.AutoField(primary_key = True)
|
||||||
|
|
||||||
resitel = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
|
resitel = models.ForeignKey(pm.Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
|
||||||
|
|
||||||
soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění',
|
soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění',
|
||||||
on_delete=models.PROTECT)
|
on_delete=models.PROTECT)
|
||||||
|
@ -1196,7 +756,7 @@ class Soustredeni_Organizatori(SeminarModelBase):
|
||||||
# Interní ID
|
# Interní ID
|
||||||
id = models.AutoField(primary_key = True)
|
id = models.AutoField(primary_key = True)
|
||||||
|
|
||||||
organizator = models.ForeignKey(Organizator, verbose_name='organizátor',
|
organizator = models.ForeignKey(pm.Organizator, verbose_name='organizátor',
|
||||||
on_delete=models.PROTECT)
|
on_delete=models.PROTECT)
|
||||||
|
|
||||||
soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění',
|
soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění',
|
||||||
|
@ -1225,7 +785,7 @@ class Konfera(Problem):
|
||||||
help_text='Abstrakt konfery tak, jak byl uveden ve sborníku')
|
help_text='Abstrakt konfery tak, jak byl uveden ve sborníku')
|
||||||
|
|
||||||
# FIXME: Umíme omezit jen na účastníky daného soustřeďka?
|
# FIXME: Umíme omezit jen na účastníky daného soustřeďka?
|
||||||
ucastnici = models.ManyToManyField(Resitel, verbose_name='účastníci konfery',
|
ucastnici = models.ManyToManyField(pm.Resitel, verbose_name='účastníci konfery',
|
||||||
help_text='Seznam účastníků konfery', through='Konfery_Ucastnici')
|
help_text='Seznam účastníků konfery', through='Konfery_Ucastnici')
|
||||||
|
|
||||||
soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění',
|
soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění',
|
||||||
|
@ -1266,7 +826,7 @@ class Konfery_Ucastnici(models.Model):
|
||||||
# Interní ID
|
# Interní ID
|
||||||
id = models.AutoField(primary_key = True)
|
id = models.AutoField(primary_key = True)
|
||||||
|
|
||||||
resitel = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
|
resitel = models.ForeignKey(pm.Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
|
||||||
|
|
||||||
konfera = models.ForeignKey(Konfera, verbose_name='konfera', on_delete=models.CASCADE)
|
konfera = models.ForeignKey(Konfera, verbose_name='konfera', on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
@ -1452,7 +1012,7 @@ class OrgTextNode(TreeNode):
|
||||||
verbose_name = 'Organizátorský článek (Node)'
|
verbose_name = 'Organizátorský článek (Node)'
|
||||||
verbose_name_plural = 'Organizátorské články (Node)'
|
verbose_name_plural = 'Organizátorské články (Node)'
|
||||||
|
|
||||||
organizator = models.ForeignKey(Organizator,
|
organizator = models.ForeignKey(pm.Organizator,
|
||||||
null=False,
|
null=False,
|
||||||
blank=False,
|
blank=False,
|
||||||
on_delete=models.DO_NOTHING,
|
on_delete=models.DO_NOTHING,
|
||||||
|
@ -1598,7 +1158,7 @@ class Novinky(models.Model):
|
||||||
],
|
],
|
||||||
options={'quality': 95})
|
options={'quality': 95})
|
||||||
|
|
||||||
autor = models.ForeignKey(Organizator, verbose_name='Autor novinky', null=True,
|
autor = models.ForeignKey(pm.Organizator, verbose_name='Autor novinky', null=True,
|
||||||
on_delete=models.SET_NULL)
|
on_delete=models.SET_NULL)
|
||||||
|
|
||||||
zverejneno = models.BooleanField('Zveřejněno', default=False)
|
zverejneno = models.BooleanField('Zveřejněno', default=False)
|
||||||
|
|
|
@ -10,6 +10,8 @@ from django.utils import timezone
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from seminar.models import models_all as am
|
from seminar.models import models_all as am
|
||||||
|
from seminar.models import personalni as pm
|
||||||
|
from seminar.models.base import SeminarModelBase
|
||||||
|
|
||||||
|
|
||||||
@reversion.register(ignore_duplicates=True)
|
@reversion.register(ignore_duplicates=True)
|
||||||
|
@ -29,7 +31,7 @@ class Reseni(am.SeminarModelBase):
|
||||||
problem = models.ManyToManyField(am.Problem, verbose_name='problém', help_text='Problém',
|
problem = models.ManyToManyField(am.Problem, verbose_name='problém', help_text='Problém',
|
||||||
through='Hodnoceni')
|
through='Hodnoceni')
|
||||||
|
|
||||||
resitele = models.ManyToManyField(am.Resitel, verbose_name='autoři řešení',
|
resitele = models.ManyToManyField(pm.Resitel, verbose_name='autoři řešení',
|
||||||
help_text='Seznam autorů řešení', through='Reseni_Resitele')
|
help_text='Seznam autorů řešení', through='Reseni_Resitele')
|
||||||
|
|
||||||
|
|
||||||
|
@ -160,7 +162,7 @@ class Reseni_Resitele(models.Model):
|
||||||
# Interní ID
|
# Interní ID
|
||||||
id = models.AutoField(primary_key = True)
|
id = models.AutoField(primary_key = True)
|
||||||
|
|
||||||
resitele = models.ForeignKey(am.Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
|
resitele = models.ForeignKey(pm.Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
|
||||||
|
|
||||||
reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE)
|
reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
|
438
seminar/models/personalni.py
Normal file
438
seminar/models/personalni.py
Normal file
|
@ -0,0 +1,438 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from imagekit.models import ImageSpecField, ProcessedImageField
|
||||||
|
from imagekit.processors import ResizeToFit, Transpose
|
||||||
|
|
||||||
|
from django_countries.fields import CountryField
|
||||||
|
|
||||||
|
from reversion import revisions as reversion
|
||||||
|
|
||||||
|
from .base import SeminarModelBase
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@reversion.register(ignore_duplicates=True)
|
||||||
|
class Osoba(SeminarModelBase):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = 'seminar_osoby'
|
||||||
|
verbose_name = 'Osoba'
|
||||||
|
verbose_name_plural = 'Osoby'
|
||||||
|
ordering = ['prijmeni','jmeno']
|
||||||
|
|
||||||
|
id = models.AutoField(primary_key = True)
|
||||||
|
|
||||||
|
jmeno = models.CharField('jméno', max_length=256)
|
||||||
|
|
||||||
|
prijmeni = models.CharField('příjmení', max_length=256)
|
||||||
|
|
||||||
|
prezdivka = models.CharField('přezdívka', blank=True, null=True, max_length=256)
|
||||||
|
|
||||||
|
# User, pokud má na webu účet
|
||||||
|
user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=True, null=True,
|
||||||
|
verbose_name='uživatel', on_delete=models.DO_NOTHING)
|
||||||
|
|
||||||
|
# Pohlaví. Že ho neznáme se snad nestane (a ušetří to práci při programování)
|
||||||
|
pohlavi_muz = models.BooleanField('pohlaví (muž)', default=False)
|
||||||
|
|
||||||
|
email = models.EmailField('e-mail', max_length=256, blank=True, default='')
|
||||||
|
|
||||||
|
telefon = models.CharField('telefon', max_length=256, blank=True, default='')
|
||||||
|
|
||||||
|
datum_narozeni = models.DateField('datum narození', blank=True, null=True)
|
||||||
|
|
||||||
|
# NULL dokud nedali souhlas
|
||||||
|
datum_souhlasu_udaje = models.DateField('datum souhlasu (údaje)', blank=True, null=True,
|
||||||
|
help_text='Datum souhlasu se zpracováním osobních údajů')
|
||||||
|
|
||||||
|
# NULL dokud nedali souhlas
|
||||||
|
datum_souhlasu_zasilani = models.DateField('datum souhlasu (spam)', blank=True, null=True,
|
||||||
|
help_text='Datum souhlasu se zasíláním MFF materiálů')
|
||||||
|
|
||||||
|
# Alespoň odhad (rok či i měsíc)
|
||||||
|
datum_registrace = models.DateField('datum registrace do semináře', default=timezone.now)
|
||||||
|
|
||||||
|
# Ulice může být i jen číslo
|
||||||
|
ulice = models.CharField('ulice', max_length=256, blank=True, default='')
|
||||||
|
|
||||||
|
mesto = models.CharField('město', max_length=256, blank=True, default='')
|
||||||
|
|
||||||
|
psc = models.CharField('PSČ', max_length=32, blank=True, default='')
|
||||||
|
|
||||||
|
# ISO 3166-1 dvojznakovy kod zeme velkym pismem (CZ, SK)
|
||||||
|
# Ekvivalentní s CharField(max_length=2, default='CZ', ...)
|
||||||
|
stat = CountryField('stát', default='CZ',
|
||||||
|
help_text='ISO 3166-1 kód země velkými písmeny (CZ, SK, ...)')
|
||||||
|
|
||||||
|
poznamka = models.TextField('neveřejná poznámka', blank=True,
|
||||||
|
help_text='Neveřejná poznámka k osobě (plain text)')
|
||||||
|
|
||||||
|
foto = ProcessedImageField(verbose_name='Fotografie osoby',
|
||||||
|
upload_to='image_osoby/velke/%Y/', null = True, blank = True,
|
||||||
|
help_text = 'Vlož fotografii osoby o libovolné velikosti',
|
||||||
|
processors=[
|
||||||
|
Transpose(Transpose.AUTO),
|
||||||
|
ResizeToFit(500, 500, upscale=False)
|
||||||
|
],
|
||||||
|
options={'quality': 95})
|
||||||
|
foto_male = ImageSpecField(source='foto',
|
||||||
|
processors=[
|
||||||
|
ResizeToFit(200, 200, upscale=False)
|
||||||
|
],
|
||||||
|
options={'quality': 95})
|
||||||
|
|
||||||
|
# má OneToOneField nejvýše s:
|
||||||
|
# Resitel
|
||||||
|
# Prijemce
|
||||||
|
# Organizator
|
||||||
|
|
||||||
|
def plne_jmeno(self):
|
||||||
|
return '{} {}'.format(self.jmeno, self.prijmeni)
|
||||||
|
|
||||||
|
def inicial_krestni(self):
|
||||||
|
jmena = self.jmeno.split()
|
||||||
|
return " ".join(['{}.'.format(jmeno[0]) for jmeno in jmena])
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.plne_jmeno()
|
||||||
|
|
||||||
|
# Overridujeme save Osoby, aby když si změní e-mail, aby se projevil i v
|
||||||
|
# Userovi (a tak se dal poslat mail s resetem hesla)
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if self.user is not None:
|
||||||
|
u = self.user
|
||||||
|
# U svatého tučňáka, prosím ať tohle funguje.
|
||||||
|
# (Takhle se kódit asi nemá...)
|
||||||
|
u.email = self.email
|
||||||
|
u.save()
|
||||||
|
super().save()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Mělo by být částečně vytaženo z Aesopa
|
||||||
|
# viz https://ovvp.mff.cuni.cz/wiki/aesop/export-skol.
|
||||||
|
#
|
||||||
|
|
||||||
|
@reversion.register(ignore_duplicates=True)
|
||||||
|
class Skola(SeminarModelBase):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = 'seminar_skoly'
|
||||||
|
verbose_name = 'Škola'
|
||||||
|
verbose_name_plural = 'Školy'
|
||||||
|
ordering = ['mesto', 'nazev']
|
||||||
|
|
||||||
|
# Interní ID
|
||||||
|
id = models.AutoField(primary_key = True)
|
||||||
|
|
||||||
|
# Aesopi ID "izo:..." nebo "aesop:..."
|
||||||
|
# NULL znamená v exportu do aesopa "ufo"
|
||||||
|
aesop_id = models.CharField('Aesop ID', max_length=32, blank=True, default='',
|
||||||
|
help_text='Aesopi ID typu "izo:..." nebo "aesop:..."')
|
||||||
|
|
||||||
|
# IZO školy (jen české školy)
|
||||||
|
izo = models.CharField('IZO', max_length=32, blank=True,
|
||||||
|
help_text='IZO školy (jen české školy)')
|
||||||
|
|
||||||
|
# Celý název školy
|
||||||
|
nazev = models.CharField('název', max_length=256,
|
||||||
|
help_text='Celý název školy')
|
||||||
|
|
||||||
|
# Zkraceny nazev pro zobrazení ve výsledkovce, volitelné.
|
||||||
|
# Není v Aesopovi, musíme vytvářet sami.
|
||||||
|
kratky_nazev = models.CharField('zkrácený název', max_length=256, blank=True,
|
||||||
|
help_text="Zkrácený název pro zobrazení ve výsledkovce")
|
||||||
|
|
||||||
|
# Ulice může být jen číslo
|
||||||
|
ulice = models.CharField('ulice', max_length=256)
|
||||||
|
|
||||||
|
mesto = models.CharField('město', max_length=256)
|
||||||
|
|
||||||
|
psc = models.CharField('PSČ', max_length=32)
|
||||||
|
|
||||||
|
# ISO 3166-1 dvojznakovy kod zeme velkym pismem (CZ, SK)
|
||||||
|
# Ekvivalentní s CharField(max_length=2, default='CZ', ...)
|
||||||
|
stat = CountryField('stát', default='CZ',
|
||||||
|
help_text='ISO 3166-1 kód země velkými písmeny (CZ, SK, ...)')
|
||||||
|
|
||||||
|
# Jaké vzdělání škpla poskytuje?
|
||||||
|
je_zs = models.BooleanField('základní stupeň', default=True)
|
||||||
|
je_ss = models.BooleanField('střední stupeň', default=True)
|
||||||
|
|
||||||
|
poznamka = models.TextField('neveřejná poznámka', blank=True,
|
||||||
|
help_text='Neveřejná poznámka ke škole (plain text)')
|
||||||
|
|
||||||
|
kontaktni_osoba = models.ForeignKey(Osoba, verbose_name='Kontaktní osoba',
|
||||||
|
blank=True, null=True, on_delete=models.SET_NULL)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '{}, {}, {}'.format(self.nazev, self.ulice, self.mesto)
|
||||||
|
|
||||||
|
class Prijemce(SeminarModelBase):
|
||||||
|
class Meta:
|
||||||
|
db_table = 'seminar_prijemce'
|
||||||
|
verbose_name = 'příjemce'
|
||||||
|
verbose_name_plural = 'příjemce'
|
||||||
|
|
||||||
|
|
||||||
|
# Interní ID
|
||||||
|
id = models.AutoField(primary_key = True)
|
||||||
|
|
||||||
|
poznamka = models.TextField('neveřejná poznámka', blank=True,
|
||||||
|
help_text='Neveřejná poznámka k příemci čísel (plain text)')
|
||||||
|
|
||||||
|
osoba = models.OneToOneField(Osoba, verbose_name='komu', blank=False, null=False,
|
||||||
|
help_text='Které osobě či na jakou adresu se mají zasílat čísla',
|
||||||
|
on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
# FIXME: možná chceme něco jako vazbu na osobu XOR školu a počet kusů k zaslání
|
||||||
|
# FIXME: a možná taky posílání na mail a možná taky přes něj chceme posílat i řešitelům
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.osoba.plne_jmeno()
|
||||||
|
|
||||||
|
|
||||||
|
@reversion.register(ignore_duplicates=True)
|
||||||
|
class Resitel(SeminarModelBase):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = 'seminar_resitele'
|
||||||
|
verbose_name = 'Řešitel'
|
||||||
|
verbose_name_plural = 'Řešitelé'
|
||||||
|
ordering = ['osoba']
|
||||||
|
|
||||||
|
# Interní ID
|
||||||
|
id = models.AutoField(primary_key = True)
|
||||||
|
|
||||||
|
osoba = models.OneToOneField(Osoba, blank=False, null=False, verbose_name='osoba',
|
||||||
|
on_delete=models.PROTECT)
|
||||||
|
|
||||||
|
|
||||||
|
skola = models.ForeignKey(Skola, blank=True, null=True, verbose_name='škola',
|
||||||
|
on_delete=models.SET_NULL)
|
||||||
|
|
||||||
|
# Očekávaný rok maturity a vyřazení z aktivních řešitelů
|
||||||
|
rok_maturity = models.IntegerField('rok maturity', blank=True, null=True)
|
||||||
|
|
||||||
|
ZASILAT_DOMU = 'domu'
|
||||||
|
ZASILAT_DO_SKOLY = 'do_skoly'
|
||||||
|
ZASILAT_NIKAM = 'nikam'
|
||||||
|
ZASILAT_CHOICES = [
|
||||||
|
(ZASILAT_DOMU, 'Domů'),
|
||||||
|
(ZASILAT_DO_SKOLY, 'Do školy'),
|
||||||
|
(ZASILAT_NIKAM, 'Nikam'),
|
||||||
|
]
|
||||||
|
|
||||||
|
zasilat = models.CharField('kam zasílat', max_length=32, choices=ZASILAT_CHOICES, blank=False, default=ZASILAT_DOMU)
|
||||||
|
|
||||||
|
zasilat_cislo_emailem = models.BooleanField('zasílat číslo emailem', help_text='True pokud chce řešitel dostávat číslo emailem', default=False)
|
||||||
|
|
||||||
|
poznamka = models.TextField('neveřejná poznámka', blank=True,
|
||||||
|
help_text='Neveřejná poznámka k řešiteli (plain text)')
|
||||||
|
|
||||||
|
|
||||||
|
def export_row(self):
|
||||||
|
"Slovnik pro pouziti v AESOP exportu"
|
||||||
|
return {
|
||||||
|
'id': self.id,
|
||||||
|
'name': self.osoba.jmeno,
|
||||||
|
'surname': self.osoba.prijmeni,
|
||||||
|
'gender': 'M' if self.osoba.pohlavi_muz else 'F',
|
||||||
|
'born': self.osoba.datum_narozeni.isoformat() if self.osoba.datum_narozeni else '',
|
||||||
|
'email': self.osoba.email,
|
||||||
|
'end-year': self.rok_maturity or '',
|
||||||
|
|
||||||
|
'street': self.osoba.ulice,
|
||||||
|
'town': self.osoba.mesto,
|
||||||
|
'postcode': self.osoba.psc,
|
||||||
|
'country': self.osoba.stat,
|
||||||
|
|
||||||
|
'spam-flag': 'Y' if self.osoba.datum_souhlasu_zasilani else '',
|
||||||
|
'spam-date': self.osoba.datum_souhlasu_zasilani.isoformat() if self.osoba.datum_souhlasu_zasilani else '',
|
||||||
|
|
||||||
|
'school': self.skola.aesop_id if self.skola else '',
|
||||||
|
'school-name': str(self.skola) if self.skola else 'Skola neni znama',
|
||||||
|
}
|
||||||
|
|
||||||
|
def rocnik(self, rocnik):
|
||||||
|
"""Vrati skolni rocnik resitele pro zadany Rocnik.
|
||||||
|
Vraci '' pro neznamy rok maturity resitele, Z* pro ekvivalent ZŠ."""
|
||||||
|
if self.rok_maturity is None:
|
||||||
|
return ''
|
||||||
|
rozdil = 5 - (self.rok_maturity - rocnik.prvni_rok)
|
||||||
|
if rozdil >= 1:
|
||||||
|
return str(rozdil)
|
||||||
|
else:
|
||||||
|
return 'Z' + str(rozdil + 9)
|
||||||
|
|
||||||
|
def vsechny_body(self):
|
||||||
|
"Spočítá body odjakživa."
|
||||||
|
vsechna_reseni = self.reseni_set.all()
|
||||||
|
from .odevzdavatko import Hodnoceni
|
||||||
|
vsechna_hodnoceni = Hodnoceni.objects.filter(
|
||||||
|
reseni__in=vsechna_reseni)
|
||||||
|
return sum(h.body for h in list(vsechna_hodnoceni) if h.body is not None)
|
||||||
|
|
||||||
|
|
||||||
|
def get_titul(self, body=None):
|
||||||
|
"Vrati titul jako řetězec."
|
||||||
|
|
||||||
|
# Nejprve si zadefinujeme titul
|
||||||
|
from enum import Enum
|
||||||
|
from functools import total_ordering
|
||||||
|
@total_ordering
|
||||||
|
class Titul(Enum):
|
||||||
|
""" Třída reprezentující možné tituly. Hodnoty jsou dvojice (dolní hranice, stringifikace). """
|
||||||
|
nic = (0, '')
|
||||||
|
bc = (20, 'Bc.')
|
||||||
|
mgr = (50, 'Mgr.')
|
||||||
|
dr = (100, 'Dr.')
|
||||||
|
doc = (200, 'Doc.')
|
||||||
|
prof = (500, 'Prof.')
|
||||||
|
akad = (1000, 'Akad.')
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
return True if self.value[0] < other.value[0] else False
|
||||||
|
def __eq__(self, other): # Měla by být implicitní, ale klidně explicitně.
|
||||||
|
return True if self.value[0] == other.value[0] else False
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.value[1]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def z_bodu(cls, body):
|
||||||
|
aktualni = cls.nic
|
||||||
|
# TODO: ověřit, že to funguje
|
||||||
|
for titul in cls: # Kdyžtak použít __members__.items()
|
||||||
|
if titul.value[0] <= body:
|
||||||
|
aktualni = titul
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
return aktualni
|
||||||
|
|
||||||
|
# Hledáme body v databázi
|
||||||
|
# V listopadu 2020 jsme se na filosofické schůzce shodli o změně hranic titulů:
|
||||||
|
# - body z 25. ročníku a dříve byly shledány dvakrát hodnotnějšími
|
||||||
|
# - proto se započítávají dvojnásobně a byly posunuté hranice titulů
|
||||||
|
# - staré tituly se ale nemají odebrat, pokud řešitel v t.č. minulém (26.) ročníku měl titul, má ho mít pořád.
|
||||||
|
from .odevzdavatko import Hodnoceni
|
||||||
|
hodnoceni_do_25_rocniku = Hodnoceni.objects.filter(cislo_body__rocnik__rocnik__lte=25,reseni__in=self.reseni_set.all())
|
||||||
|
novejsi_hodnoceni = Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()).difference(hodnoceni_do_25_rocniku)
|
||||||
|
|
||||||
|
def body_z_hodnoceni(hh : list):
|
||||||
|
return sum(h.body for h in hh if h.body is not None)
|
||||||
|
|
||||||
|
stare_body = body_z_hodnoceni(hodnoceni_do_25_rocniku)
|
||||||
|
if body is None:
|
||||||
|
nove_body = body_z_hodnoceni(novejsi_hodnoceni)
|
||||||
|
else:
|
||||||
|
# Zjistíme, kolik bodů jsou staré, tedy hodnotnější
|
||||||
|
nove_body = max(0, body - stare_body) # Všechny body nad počet původních hodnotnějších
|
||||||
|
stare_body = min(stare_body, body) # Skutečný počet hodnotnějších bodů
|
||||||
|
logicke_body = 2*stare_body + nove_body
|
||||||
|
|
||||||
|
|
||||||
|
# Titul se určí následovně:
|
||||||
|
# - Pokud se řeší body, které jsou starší, než do 26 ročníku (včetně), dáváme tituly postaru.
|
||||||
|
# - Jinak dáváme tituly po novu...
|
||||||
|
# - ... ale titul se nesmí odebrat, pokud se zmenšil.
|
||||||
|
def titul_do_26_rocniku(body):
|
||||||
|
""" Původní hranice bodů za tituly """
|
||||||
|
if body < 10:
|
||||||
|
return Titul.nic
|
||||||
|
elif body < 20:
|
||||||
|
return Titul.bc
|
||||||
|
elif body < 50:
|
||||||
|
return Titul.mgr
|
||||||
|
elif body < 100:
|
||||||
|
return Titul.dr
|
||||||
|
elif body < 200:
|
||||||
|
return Titul.doc
|
||||||
|
elif body < 500:
|
||||||
|
return Titul.prof
|
||||||
|
else:
|
||||||
|
return Titul.akad
|
||||||
|
|
||||||
|
from .odevzdavatko import Hodnoceni
|
||||||
|
hodnoceni_do_26_rocniku = Hodnoceni.objects.filter(cislo_body__rocnik__rocnik__lte=26,reseni__in=self.reseni_set.all())
|
||||||
|
novejsi_body = body_z_hodnoceni(
|
||||||
|
Hodnoceni.objects.filter(reseni__in=self.reseni_set.all())
|
||||||
|
.difference(hodnoceni_do_26_rocniku)
|
||||||
|
)
|
||||||
|
starsi_body = body_z_hodnoceni(hodnoceni_do_26_rocniku)
|
||||||
|
if body is not None:
|
||||||
|
# Ještě z toho vybereme ty správně staré body
|
||||||
|
novejsi_body = max(0, body - starsi_body)
|
||||||
|
starsi_body = min(starsi_body, body)
|
||||||
|
|
||||||
|
# Titul pro 26. ročník
|
||||||
|
stary_titul = titul_do_26_rocniku(starsi_body)
|
||||||
|
# Titul podle aktuálních pravidel
|
||||||
|
novy_titul = Titul.z_bodu(logicke_body)
|
||||||
|
|
||||||
|
if novejsi_body == 0:
|
||||||
|
# Žádné nové body -- titul podle starých pravidel
|
||||||
|
return str(stary_titul)
|
||||||
|
return str(max(novy_titul, stary_titul))
|
||||||
|
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.osoba.plne_jmeno()
|
||||||
|
|
||||||
|
|
||||||
|
@reversion.register(ignore_duplicates=True)
|
||||||
|
class Organizator(SeminarModelBase):
|
||||||
|
osoba = models.OneToOneField(Osoba, verbose_name='osoba', related_name='org',
|
||||||
|
help_text='osobní údaje organizátora', null=False, blank=False,
|
||||||
|
on_delete=models.PROTECT)
|
||||||
|
|
||||||
|
vytvoreno = models.DateTimeField(
|
||||||
|
'Vytvořeno',
|
||||||
|
default=timezone.now,
|
||||||
|
blank=True,
|
||||||
|
editable=False
|
||||||
|
)
|
||||||
|
|
||||||
|
organizuje_od = models.DateTimeField('Organizuje od', blank=True, null=True)
|
||||||
|
|
||||||
|
organizuje_do = models.DateTimeField('Organizuje do', blank=True, null=True)
|
||||||
|
|
||||||
|
studuje = models.CharField('Studium aj.', max_length = 256,
|
||||||
|
null = True, blank = True,
|
||||||
|
help_text="Např. 'Studuje Obecnou fyziku (Bc.), 3. ročník', "
|
||||||
|
"'Vystudovala Diskrétní modely a algoritmy (Mgr.)' nebo "
|
||||||
|
"'Přednáší na MFF'")
|
||||||
|
|
||||||
|
strucny_popis_organizatora = models.TextField('Stručný popis organizátora',
|
||||||
|
null = True, blank = True)
|
||||||
|
|
||||||
|
skola = models.CharField('Škola, kterou studuje', max_length = 256, null=True, blank=True,
|
||||||
|
help_text="Škola, např. MFF, VŠCHT, VUT, ... prostě aby se nemuselo psát do studuje"
|
||||||
|
"školu, ale jen obor, možnost zobrazit zvlášť")
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
if self.organizuje_od and self.organizuje_do and (self.organizuje_od > self.organizuje_do):
|
||||||
|
raise ValidationError("Organizátor nemůže skončit s organizováním dříve než začal!")
|
||||||
|
super().clean()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.osoba.prezdivka:
|
||||||
|
return "{} '{}' {}".format(self.osoba.jmeno,
|
||||||
|
self.osoba.prezdivka,
|
||||||
|
self.osoba.prijmeni)
|
||||||
|
else:
|
||||||
|
return "{} {}".format(self.osoba.jmeno, self.osoba.prijmeni)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = 'Organizátor'
|
||||||
|
verbose_name_plural = 'Organizátoři'
|
||||||
|
# Řadí aktivní orgy na začátek, pod tím v pořadí od nejstarších neaktivní orgy.
|
||||||
|
# TODO: Chtěl bych spíš mít nejstarší orgy dole.
|
||||||
|
# TODO: Zohledňovat přezdívky?
|
||||||
|
# TODO: Sjednotit s tím, jak se řadí organizátoři v seznau orgů na webu
|
||||||
|
ordering = ['-organizuje_do', 'osoba__jmeno', 'osoba__prijmeni']
|
|
@ -1,105 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
{% load staticfiles %}
|
|
||||||
|
|
||||||
{% block script %}
|
|
||||||
<script src="{% static 'seminar/prihlaska.js' %}"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
# pro přidání políčka do formuláře je potřeba
|
|
||||||
# - mít v modelu tu položku, kterou chci upravovat
|
|
||||||
# - přidat do views (prihlaskaView, resitelEditView)
|
|
||||||
# - přidat do forms
|
|
||||||
# - includovat do html
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1>
|
|
||||||
{% block nadpis1a %}{% block nadpis1b %}
|
|
||||||
Změna osobních údajů
|
|
||||||
{% endblock %}{% endblock %}
|
|
||||||
</h1>
|
|
||||||
<form action="{% url 'seminar_resitel_edit' %}" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{form.non_field_errors}}
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h4>
|
|
||||||
Přihlašovací údaje
|
|
||||||
</h4>
|
|
||||||
<table class="form">
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.username %}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h4>
|
|
||||||
Osobní údaje
|
|
||||||
</h4>
|
|
||||||
<table class="form">
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.jmeno %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.prijmeni %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.pohlavi_muz%}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.email %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.telefon %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.datum_narozeni %}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h4>
|
|
||||||
Bydliště
|
|
||||||
</h4>
|
|
||||||
<table class="form">
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.ulice %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.mesto %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.psc %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.stat %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.stat_text id="id_li_stat_text"%}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h4>
|
|
||||||
Škola
|
|
||||||
</h4>
|
|
||||||
<table class="form">
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.skola %}
|
|
||||||
<tr><td colspan="2" ><button id="id_skola_text_button" type="button">Škola není v seznamu</button></td></tr>
|
|
||||||
<tr><td id="id_li_skola_vypln" colspan="2">Vyplň prosím celý název a adresu školy.</td></tr>
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.skola_nazev id="id_li_skola_nazev" %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.skola_adresa id="id_li_skola_adresa" %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.rok_maturity %}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h4>
|
|
||||||
Pošta
|
|
||||||
</h4>
|
|
||||||
<table class="form">
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.zasilat %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.zasilat_cislo_emailem %}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h4>
|
|
||||||
Zasílání propagačních materiálů
|
|
||||||
</h4>
|
|
||||||
<table class="form">
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.spam %}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<input type="submit" value="Změnit">
|
|
||||||
</form>
|
|
||||||
<script>
|
|
||||||
$("#id_stat").on("change",addrCountryChanged);
|
|
||||||
$("#id_skola_text_button").on("click",schoolNotInList);
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -1,123 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
{% load staticfiles %}
|
|
||||||
|
|
||||||
{% block script %}
|
|
||||||
<script src="{% static 'seminar/prihlaska.js' %}"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
# pro přidání políčka do formuláře je potřeba
|
|
||||||
# - mít v modelu tu položku, kterou chci upravovat
|
|
||||||
# - přidat do views (prihlaskaView, resitelEditView)
|
|
||||||
# - přidat do forms
|
|
||||||
# - includovat do html
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1>
|
|
||||||
{% block nadpis1a %}{% block nadpis1b %}
|
|
||||||
Přihláška do semináře
|
|
||||||
{% endblock %}{% endblock %}
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<p><b>Tučně</b> popsaná pole jsou povinná.</p>
|
|
||||||
|
|
||||||
<form action="{% url 'seminar_prihlaska' %}" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{form.non_field_errors}}
|
|
||||||
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<h4>
|
|
||||||
Přihlašovací údaje
|
|
||||||
</h4>
|
|
||||||
<table class="form">
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.username %}
|
|
||||||
{# {% include "seminar/profil/prihlaska_field.html" with field=form.password %}#}
|
|
||||||
{# {% include "seminar/profil/prihlaska_field.html" with field=form.password_check %}#}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h4>
|
|
||||||
Osobní údaje
|
|
||||||
</h4>
|
|
||||||
<table class="form">
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.jmeno %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.prijmeni %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.pohlavi_muz%}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.email %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.telefon %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.datum_narozeni %}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h4>
|
|
||||||
Bydliště
|
|
||||||
</h4>
|
|
||||||
<table class="form">
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.ulice %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.mesto %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.psc %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.stat %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.stat_text id="id_li_stat_text"%}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h4>
|
|
||||||
Škola
|
|
||||||
</h4>
|
|
||||||
<table class="form">
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.skola %}
|
|
||||||
<tr><td colspan="2" ><button id="id_skola_text_button" type="button">Škola není v seznamu</button></td></tr>
|
|
||||||
<tr><td id="id_li_skola_vypln" colspan="2">Vyplň prosím celý název a adresu školy.</td></tr>
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.skola_nazev id="id_li_skola_nazev" %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.skola_adresa id="id_li_skola_adresa" %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.rok_maturity %}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h4>
|
|
||||||
Pošta
|
|
||||||
</h4>
|
|
||||||
<table class="form">
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.zasilat %}
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.zasilat_cislo_emailem %}
|
|
||||||
</table>
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h4>
|
|
||||||
GDPR
|
|
||||||
</h4>
|
|
||||||
{% include "seminar/profil/gdpr.html" %}
|
|
||||||
<table class="form">
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.gdpr %}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h4>
|
|
||||||
Zasílání propagačních materiálů
|
|
||||||
</h4>
|
|
||||||
<table class="form">
|
|
||||||
{% include "seminar/profil/prihlaska_field.html" with field=form.spam %}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<input type="submit" value="Odeslat">
|
|
||||||
</form>
|
|
||||||
<script>
|
|
||||||
$("#id_stat").on("change",addrCountryChanged);
|
|
||||||
$("#id_skola_text_button").on("click",schoolNotInList);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
|
|
@ -1,5 +1,4 @@
|
||||||
from django.urls import path, include, re_path
|
from django.urls import path, include, re_path
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from . import views
|
from . import views
|
||||||
from .utils import org_required
|
from .utils import org_required
|
||||||
|
|
||||||
|
@ -107,23 +106,6 @@ urlpatterns = [
|
||||||
org_required(views.soustredeniObalkyView),
|
org_required(views.soustredeniObalkyView),
|
||||||
name='seminar_soustredeni_obalky'
|
name='seminar_soustredeni_obalky'
|
||||||
),
|
),
|
||||||
# příprava na nestatický orgorozcestník
|
|
||||||
path(
|
|
||||||
'org/rozcestnik/',
|
|
||||||
org_required(views.OrgoRozcestnikView.as_view()),
|
|
||||||
name='seminar_org_rozcestnik'
|
|
||||||
),
|
|
||||||
|
|
||||||
path('prihlaska/',views.prihlaskaView, name='seminar_prihlaska'),
|
|
||||||
|
|
||||||
path(
|
|
||||||
'resitel/osobni-udaje/',
|
|
||||||
login_required(views.resitelEditView),
|
|
||||||
name='seminar_resitel_edit'
|
|
||||||
),
|
|
||||||
|
|
||||||
# Obecný view na profil -- orgům dá rozcestník, řešitelům jejich stránku
|
|
||||||
path('profil/', views.profilView, name='profil'),
|
|
||||||
|
|
||||||
re_path(r'^temp/vue/.*$',views.VueTestView.as_view(),name='vue_test_view'),
|
re_path(r'^temp/vue/.*$',views.VueTestView.as_view(),name='vue_test_view'),
|
||||||
path('temp/image_upload/', views.NahrajObrazekKTreeNoduView.as_view()),
|
path('temp/image_upload/', views.NahrajObrazekKTreeNoduView.as_view()),
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from django.forms import model_to_dict
|
||||||
from django.shortcuts import get_object_or_404, render, redirect
|
from django.shortcuts import get_object_or_404, render, redirect
|
||||||
from django.http import HttpResponse, JsonResponse
|
from django.http import HttpResponse, JsonResponse
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
@ -6,20 +7,16 @@ from django.views import generic
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.db.models import Q, Sum, Count
|
from django.db.models import Q, Sum, Count
|
||||||
from django.views.decorators.debug import sensitive_post_parameters
|
|
||||||
from django.views.generic.edit import CreateView
|
from django.views.generic.edit import CreateView
|
||||||
from django.views.generic.base import TemplateView, RedirectView
|
from django.views.generic.base import RedirectView
|
||||||
from django.contrib.auth.models import User, Permission, Group
|
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.db import transaction
|
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
|
|
||||||
import seminar.models as s
|
import seminar.models as s
|
||||||
import seminar.models as m
|
import seminar.models as m
|
||||||
from seminar.models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici, Tema, Clanek, Osoba # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci
|
from seminar.models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici, Tema, Clanek # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci
|
||||||
#from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva
|
#from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva
|
||||||
from seminar import utils, treelib
|
from seminar import utils, treelib
|
||||||
from seminar.forms import PrihlaskaForm, ProfileEditForm, PoMaturiteProfileEditForm
|
|
||||||
import seminar.forms as f
|
import seminar.forms as f
|
||||||
import seminar.templatetags.treenodes as tnltt
|
import seminar.templatetags.treenodes as tnltt
|
||||||
import seminar.views.views_rest as vr
|
import seminar.views.views_rest as vr
|
||||||
|
@ -41,9 +38,7 @@ import csv
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from seminar.utils import aktivniResitele, problemy_rocniku, cisla_rocniku, hlavni_problemy_f
|
from seminar.utils import aktivniResitele
|
||||||
from various.autentizace.views import LoginView
|
|
||||||
from various.autentizace.utils import posli_reset_hesla
|
|
||||||
|
|
||||||
# ze starého modelu
|
# ze starého modelu
|
||||||
#def verejna_temata(rocnik):
|
#def verejna_temata(rocnik):
|
||||||
|
@ -825,51 +820,6 @@ def oldObalkovaniView(request, rocnik, cislo):
|
||||||
{'cislo': cislo, 'problemy': problemy, 'reseni': reseni}
|
{'cislo': cislo, 'problemy': problemy, 'reseni': reseni}
|
||||||
)
|
)
|
||||||
|
|
||||||
### Orgostránky
|
|
||||||
|
|
||||||
class OrgoRozcestnikView(TemplateView):
|
|
||||||
''' Zobrazí organizátorský rozcestník.'''
|
|
||||||
|
|
||||||
template_name = 'seminar/orgorozcestnik.html'
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['posledni_soustredeni'] = Soustredeni.objects.order_by('-datum_konce').first()
|
|
||||||
nastaveni = Nastaveni.objects.first()
|
|
||||||
aktualni_rocnik = nastaveni.aktualni_rocnik
|
|
||||||
context['posledni_cislo_url'] = nastaveni.aktualni_cislo.verejne_url()
|
|
||||||
# TODO možná chceme odkazovat na právě rozpracované číslo, a ne to poslední vydané
|
|
||||||
# pokud nechceme haluzit kód (= poradi) dalšího čísla, bude asi potřeba jít
|
|
||||||
# přes treenody (a dát si přitom pozor na MezicisloNode)
|
|
||||||
|
|
||||||
neobodovana_reseni = s.Hodnoceni.objects.filter(body__isnull=True)
|
|
||||||
reseni_mimo_cislo = s.Hodnoceni.objects.filter(cislo_body__isnull=True)
|
|
||||||
context['pocet_neobodovanych_reseni'] = neobodovana_reseni.count()
|
|
||||||
context['pocet_reseni_mimo_cislo'] = reseni_mimo_cislo.count()
|
|
||||||
|
|
||||||
u = self.request.user
|
|
||||||
os = s.Osoba.objects.get(user=u)
|
|
||||||
organizator = s.Organizator.objects.get(osoba=os)
|
|
||||||
|
|
||||||
context['muj_pocet_neobodovanych_reseni'] = neobodovana_reseni.filter(Q(problem__garant=organizator) | Q(problem__autor=organizator) | Q(problem__opravovatele__in=[organizator])).distinct().count()
|
|
||||||
context['muj_pocet_reseni_mimo_cislo'] = reseni_mimo_cislo.filter(Q(problem__garant=organizator) | Q(problem__autor=organizator) | Q(problem__opravovatele__in=[organizator])).count()
|
|
||||||
|
|
||||||
#FIXME: přidat stav='STAV_ZADANY'
|
|
||||||
temata = s.Tema.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]),
|
|
||||||
rocnik=aktualni_rocnik).distinct()
|
|
||||||
ulohy = s.Uloha.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]),
|
|
||||||
cislo_zadani__rocnik=aktualni_rocnik).distinct()
|
|
||||||
clanky = s.Clanek.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]),
|
|
||||||
cislo__rocnik=aktualni_rocnik).distinct()
|
|
||||||
|
|
||||||
context['temata'] = temata
|
|
||||||
context['ulohy'] = ulohy
|
|
||||||
context['clanky'] = clanky
|
|
||||||
context['organizator'] = organizator
|
|
||||||
return context
|
|
||||||
|
|
||||||
#content_type = 'text/plain; charset=UTF8'
|
|
||||||
#XXX
|
|
||||||
|
|
||||||
### Tituly
|
### Tituly
|
||||||
|
|
||||||
|
@ -1014,15 +964,6 @@ def StavDatabazeView(request):
|
||||||
'jmena_zen': utils.histogram([r.osoba.jmeno for r in zeny]),
|
'jmena_zen': utils.histogram([r.osoba.jmeno for r in zeny]),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
class ResitelView(LoginRequiredMixin,generic.DetailView):
|
|
||||||
model = Resitel
|
|
||||||
template_name = 'seminar/profil/resitel.html'
|
|
||||||
|
|
||||||
def get_object(self, queryset=None):
|
|
||||||
print(self.request.user)
|
|
||||||
return Resitel.objects.get(osoba__user=self.request.user)
|
|
||||||
|
|
||||||
### Formulare
|
### Formulare
|
||||||
|
|
||||||
# pro přidání políčka do formuláře je potřeba
|
# pro přidání políčka do formuláře je potřeba
|
||||||
|
@ -1031,216 +972,6 @@ class ResitelView(LoginRequiredMixin,generic.DetailView):
|
||||||
# - přidat do forms
|
# - přidat do forms
|
||||||
# - includovat do html
|
# - includovat do html
|
||||||
|
|
||||||
def prihlaska_log_gdpr_safe(logger, gdpr_logger, msg, form_data):
|
|
||||||
msg = "{}, form_hash:{}".format(msg,hash(frozenset(form_data.items)))
|
|
||||||
logger.warn(msg)
|
|
||||||
gdpr_logger.warn(msg+", form:{}".format(form_data))
|
|
||||||
|
|
||||||
from django.forms.models import model_to_dict
|
|
||||||
@sensitive_post_parameters('jmeno', 'prijmeni', 'email', 'telefon', 'datum_narozeni', 'ulice', 'mesto', 'psc', 'skola')
|
|
||||||
def resitelEditView(request):
|
|
||||||
err_logger = logging.getLogger('seminar.prihlaska.problem')
|
|
||||||
## Načtení objektů Osoba a Resitel patřících k aktuálně přihlášenému uživateli
|
|
||||||
u = request.user
|
|
||||||
osoba_edit = Osoba.objects.get(user=u)
|
|
||||||
if hasattr(osoba_edit,'resitel'):
|
|
||||||
resitel_edit = osoba_edit.resitel
|
|
||||||
else:
|
|
||||||
resitel_edit = None
|
|
||||||
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:
|
|
||||||
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 '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':
|
|
||||||
POST = request.POST.copy()
|
|
||||||
POST["username"] = osoba_edit.user.username
|
|
||||||
|
|
||||||
if 'rok_maturity' not in prefill_1 or prefill_1['rok_maturity'] < date.today().year:
|
|
||||||
form = PoMaturiteProfileEditForm(POST)
|
|
||||||
else:
|
|
||||||
form = ProfileEditForm(POST)
|
|
||||||
form.username = user_edit.username
|
|
||||||
if form.is_valid():
|
|
||||||
## Změny v osobě
|
|
||||||
fcd = form.cleaned_data
|
|
||||||
form_hash = hash(frozenset(fcd.items()))
|
|
||||||
form_logger = logging.getLogger('seminar.prihlaska.form')
|
|
||||||
form_logger.info("EDIT:" + str(fcd) + str(form_hash)) # TODO možná logovat jinak
|
|
||||||
osoba_edit.jmeno = fcd['jmeno']
|
|
||||||
osoba_edit.prijmeni = fcd['prijmeni']
|
|
||||||
osoba_edit.pohlavi_muz = fcd['pohlavi_muz']
|
|
||||||
osoba_edit.email = fcd['email']
|
|
||||||
osoba_edit.telefon = fcd['telefon']
|
|
||||||
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()
|
|
||||||
if fcd.get('stat','') in ('CZ','SK'):
|
|
||||||
osoba_edit.stat = fcd['stat']
|
|
||||||
else:
|
|
||||||
## Neznámá země
|
|
||||||
msg = "Unknown country {}".format(fcd['stat_text'])
|
|
||||||
|
|
||||||
if resitel_edit:
|
|
||||||
## Změny v řešiteli
|
|
||||||
resitel_edit.skola = fcd['skola']
|
|
||||||
resitel_edit.rok_maturity = fcd['rok_maturity']
|
|
||||||
resitel_edit.zasilat = fcd['zasilat']
|
|
||||||
resitel_edit.zasilat_cislo_emailem = fcd['zasilat_cislo_emailem']
|
|
||||||
if fcd.get('skola'):
|
|
||||||
resitel_edit.skola = fcd['skola']
|
|
||||||
else:
|
|
||||||
# Unknown school - log it
|
|
||||||
msg = "Unknown school {}, {}".format(fcd['skola_nazev'],fcd['skola_adresa'])
|
|
||||||
resitel_edit.save()
|
|
||||||
osoba_edit.save()
|
|
||||||
return formularOKView(request, text=f'Údaje byly úspěšně uloženy. <a href="{reverse("profil")}">Vrátit se zpět na profil.</a>')
|
|
||||||
|
|
||||||
return render(request, 'seminar/profil/edit.html', {'form': form})
|
|
||||||
|
|
||||||
@sensitive_post_parameters('jmeno', 'prijmeni', 'email', 'telefon', 'datum_narozeni', 'ulice', 'mesto', 'psc', 'skola')
|
|
||||||
def prihlaskaView(request):
|
|
||||||
generic_logger = logging.getLogger('seminar.prihlaska')
|
|
||||||
err_logger = logging.getLogger('seminar.prihlaska.problem')
|
|
||||||
form_logger = logging.getLogger('seminar.prihlaska.form')
|
|
||||||
if request.method == 'POST':
|
|
||||||
form = PrihlaskaForm(request.POST)
|
|
||||||
# TODO vyresit, co se bude v jakych situacich zobrazovat
|
|
||||||
if form.is_valid():
|
|
||||||
generic_logger.info("Form valid")
|
|
||||||
fcd = form.cleaned_data
|
|
||||||
form_hash = hash(frozenset(fcd.items()))
|
|
||||||
form_logger.info(str(fcd) + str(form_hash)) # TODO možná logovat jinak
|
|
||||||
|
|
||||||
with transaction.atomic():
|
|
||||||
u = User.objects.create_user(
|
|
||||||
username=fcd['username'],
|
|
||||||
email = fcd['email'])
|
|
||||||
u.save()
|
|
||||||
resitel_perm = Permission.objects.filter(codename__exact='resitel').first()
|
|
||||||
u.user_permissions.add(resitel_perm)
|
|
||||||
resitel_grp = Group.objects.filter(name__exact='resitel').first()
|
|
||||||
u.groups.add(resitel_grp)
|
|
||||||
|
|
||||||
o = Osoba(
|
|
||||||
jmeno = fcd['jmeno'],
|
|
||||||
prijmeni = fcd['prijmeni'],
|
|
||||||
pohlavi_muz = fcd['pohlavi_muz'],
|
|
||||||
email = fcd['email'],
|
|
||||||
telefon = fcd.get('telefon',''),
|
|
||||||
datum_narozeni = fcd.get('datum_narozeni',None),
|
|
||||||
datum_souhlasu_udaje = date.today(),
|
|
||||||
datum_registrace = date.today(),
|
|
||||||
ulice = fcd.get('ulice',''),
|
|
||||||
mesto = fcd.get('mesto',''),
|
|
||||||
psc = fcd.get('psc',''),
|
|
||||||
poznamka = str(fcd)
|
|
||||||
)
|
|
||||||
|
|
||||||
if fcd.get('spam',False):
|
|
||||||
o.datum_souhlasu_zasilani = date.today()
|
|
||||||
if fcd.get('stat','') in ('CZ','SK'):
|
|
||||||
o.stat = fcd['stat']
|
|
||||||
else:
|
|
||||||
# Unknown country - log it
|
|
||||||
msg = "Unknown country {}".format(fcd['stat_text'])
|
|
||||||
err_logger.warn(msg + str(form_hash))
|
|
||||||
|
|
||||||
|
|
||||||
# Dovolujeme doregistraci uživatele pro existující mail, takže naopak chceme doplnit/aktualizovat údaje do stávajícího objektu
|
|
||||||
try:
|
|
||||||
orig_osoba = m.Osoba.objects.get(email=fcd['email'])
|
|
||||||
orig_osoba.poznamka += '\nDOREGISTRACE K EXISTUJÍCÍMU E-MAILU, diff níže.'
|
|
||||||
except m.Osoba.DoesNotExist:
|
|
||||||
# Trik: Budeme aktualizovat údaje nové osoby, takže se asi nic nezmění, ale fungovat to bude.
|
|
||||||
orig_osoba = o
|
|
||||||
|
|
||||||
# Porovnání údajů
|
|
||||||
assert orig_osoba.user is None, "Právě-registrující-se osoba už má Uživatele!"
|
|
||||||
osoba_attrs = ['jmeno', 'prijmeni', 'pohlavi_muz', 'email', 'telefon', 'datum_narozeni', 'ulice', 'mesto', 'psc', 'stat', 'datum_souhlasu_udaje', 'datum_souhlasu_zasilani', 'datum_registrace']
|
|
||||||
diffattrs = []
|
|
||||||
for attr in osoba_attrs:
|
|
||||||
new = getattr(o, attr)
|
|
||||||
old = getattr(orig_osoba, attr)
|
|
||||||
if new != old:
|
|
||||||
orig_osoba.poznamka += f'\nRozdíl v {attr}: Původní {old}, nový {new}'
|
|
||||||
diffattrs.append(f'Osoba.{attr}')
|
|
||||||
setattr(orig_osoba, attr, new)
|
|
||||||
# Datum registrace chceme původní / nižší:
|
|
||||||
orig_osoba.datum_registrace = min(orig_osoba.datum_registrace, o.datum_registrace)
|
|
||||||
|
|
||||||
# Od této chvíle dál je správná osoba ta "původní", novou podle formuláře si ale zachováme
|
|
||||||
o, o_form = orig_osoba, o
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
o.save()
|
|
||||||
o.user = u
|
|
||||||
o.save()
|
|
||||||
|
|
||||||
# Jednoduchá kvazi-kontrola duplicitních Osob
|
|
||||||
kolize = m.Osoba.objects.filter(jmeno=o.jmeno, prijmeni=o.prijmeni)
|
|
||||||
if kolize.count() > 1: # Jednu z nich jsme právě uložili
|
|
||||||
err_logger.warning(f'Zaregistrovala se osoba s kolizním jménem. ID osob: {[o.id for o in kolize]}')
|
|
||||||
|
|
||||||
r = Resitel(
|
|
||||||
rok_maturity = fcd['rok_maturity'],
|
|
||||||
zasilat = fcd['zasilat'],
|
|
||||||
zasilat_cislo_emailem = fcd['zasilat_cislo_emailem']
|
|
||||||
)
|
|
||||||
|
|
||||||
if fcd.get('skola'):
|
|
||||||
r.skola = fcd['skola']
|
|
||||||
else:
|
|
||||||
# Unknown school - log it
|
|
||||||
msg = "Unknown school {}, {}".format(fcd['skola_nazev'],fcd['skola_adresa'])
|
|
||||||
err_logger.warn(msg + str(form_hash))
|
|
||||||
|
|
||||||
# Porovnání údajů u řešitele
|
|
||||||
try:
|
|
||||||
orig_resitel = o.resitel
|
|
||||||
orig_resitel.poznamka += '\nDOREGISTRACE ŘEŠITELE, diff:'
|
|
||||||
except m.Resitel.DoesNotExist:
|
|
||||||
# Stejný trik:
|
|
||||||
orig_resitel = r
|
|
||||||
resitel_attrs = ['skola', 'rok_maturity', 'zasilat', 'zasilat_cislo_emailem']
|
|
||||||
for attr in resitel_attrs:
|
|
||||||
new = getattr(r, attr)
|
|
||||||
old = getattr(orig_resitel, attr)
|
|
||||||
if new != old:
|
|
||||||
orig_resitel.poznamka += f'\nRozdíl v {attr}: Původní {old}, nový {new}'
|
|
||||||
diffattrs.append(f'Resitel.{attr}')
|
|
||||||
setattr(orig_resitel, attr, new)
|
|
||||||
r, r_form = orig_resitel, r
|
|
||||||
|
|
||||||
r.osoba = o # Tohle by mělo být bezpečné…
|
|
||||||
r.save()
|
|
||||||
|
|
||||||
if diffattrs: err_logger.warning(f'Different fields when matching Řešitel id {r.id} or Osoba id {o.id}: {diffattrs}')
|
|
||||||
|
|
||||||
posli_reset_hesla(u, request)
|
|
||||||
return formularOKView(request, text='Na tvůj e-mail jsme právě poslali odkaz pro nastavení hesla.')
|
|
||||||
|
|
||||||
# if a GET (or any other method) we'll create a blank form
|
|
||||||
else:
|
|
||||||
form = PrihlaskaForm()
|
|
||||||
|
|
||||||
return render(request, 'seminar/profil/prihlaska.html', {'form': form})
|
|
||||||
|
|
||||||
|
|
||||||
class VueTestView(generic.TemplateView):
|
class VueTestView(generic.TemplateView):
|
||||||
template_name = 'seminar/vuetest.html'
|
template_name = 'seminar/vuetest.html'
|
||||||
|
@ -1268,17 +999,6 @@ class NahrajObrazekKTreeNoduView(LoginRequiredMixin, CreateView):
|
||||||
return JsonResponse({"url":self.object.na_web.url})
|
return JsonResponse({"url":self.object.na_web.url})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Jen hloupé rozhazovátko
|
|
||||||
def profilView(request):
|
|
||||||
user = request.user
|
|
||||||
if user.has_perm('auth.org'):
|
|
||||||
return OrgoRozcestnikView.as_view()(request)
|
|
||||||
if user.has_perm('auth.resitel'):
|
|
||||||
return ResitelView.as_view()(request)
|
|
||||||
else:
|
|
||||||
return LoginView.as_view()(request)
|
|
||||||
|
|
||||||
# Interní, nemá se nikdy objevit v urls (jinak to účastníci vytrolí)
|
# Interní, nemá se nikdy objevit v urls (jinak to účastníci vytrolí)
|
||||||
def formularOKView(request, text=''):
|
def formularOKView(request, text=''):
|
||||||
template_name = 'seminar/formular_ok.html'
|
template_name = 'seminar/formular_ok.html'
|
||||||
|
|
Loading…
Reference in a new issue