Merge branch 'data_migrations' into new_design
This commit is contained in:
commit
d5447a2e64
49 changed files with 4354 additions and 1835 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -10,7 +10,7 @@
|
|||
|
||||
# aux files
|
||||
*.pyc
|
||||
*.swp
|
||||
*.sw[mnop]
|
||||
|
||||
# secrets
|
||||
/django.secret
|
||||
|
|
|
@ -13,11 +13,13 @@ Use git :-)
|
|||
|
||||
Quickstart
|
||||
----------
|
||||
|
||||
Run the following commands:
|
||||
make install_venv
|
||||
. env/bin/activate
|
||||
make install_web
|
||||
|
||||
After finishing development, run "deactivate".
|
||||
|
||||
Make commands
|
||||
-------------
|
||||
|
||||
|
@ -56,6 +58,8 @@ Make commands
|
|||
|
||||
* `./manage.py test` - run the tests.
|
||||
|
||||
* `./manage.py shell` - run commands, list elemements of database, check syntax
|
||||
by importing files, etc.
|
||||
|
||||
Configurations
|
||||
--------------
|
||||
|
|
|
@ -5,7 +5,6 @@ from django.contrib import admin
|
|||
from django.http import HttpResponseRedirect
|
||||
from django import forms
|
||||
from django.db import models
|
||||
from autocomplete_light import shortcuts as autocomplete_light
|
||||
|
||||
# akction
|
||||
|
||||
|
@ -39,11 +38,12 @@ class GalerieInline(admin.TabularInline):
|
|||
|
||||
class ObrazekAdmin(admin.ModelAdmin):
|
||||
list_display = ('obrazek_velky', 'nazev', 'popis', 'obrazek_maly_tag')
|
||||
search_fields = ['nazev','popis']
|
||||
|
||||
class GalerieAdmin(admin.ModelAdmin):
|
||||
form = autocomplete_light.modelform_factory(Galerie, autocomplete_fields=['titulni_obrazek'], fields=['titulni_obrazek'])
|
||||
model = Galerie
|
||||
fields = ('zobrazit', 'nazev', 'titulni_obrazek', 'popis', 'galerie_up', 'soustredeni', 'poradi')
|
||||
autocomplete_fields = ['titulni_obrazek']
|
||||
list_display = ('nazev', 'soustredeni', 'galerie_up', 'poradi', 'zobrazit', 'datum_zmeny')
|
||||
inlines = [GalerieInline]
|
||||
actions = [zverejnit_fotogalerii, prepnout_fotogalerii_do_org_rezimu]
|
||||
|
|
|
@ -53,7 +53,7 @@ AUTHENTICATION_BACKENDS = (
|
|||
|
||||
|
||||
MIDDLEWARE = (
|
||||
'reversion.middleware.RevisionMiddleware',
|
||||
# 'reversion.middleware.RevisionMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
|
@ -98,13 +98,14 @@ INSTALLED_APPS = (
|
|||
|
||||
# Utilities
|
||||
'sekizai',
|
||||
'reversion',
|
||||
# 'reversion',
|
||||
'django_countries',
|
||||
'solo',
|
||||
'ckeditor',
|
||||
'ckeditor_uploader',
|
||||
'taggit',
|
||||
'autocomplete_light',
|
||||
'dal',
|
||||
'dal_select2',
|
||||
|
||||
'fluent_comments',
|
||||
'crispy_forms',
|
||||
|
@ -118,6 +119,8 @@ INSTALLED_APPS = (
|
|||
|
||||
'imagekit',
|
||||
|
||||
'polymorphic',
|
||||
|
||||
# MaMweb
|
||||
'mamweb',
|
||||
'seminar',
|
||||
|
@ -215,6 +218,14 @@ LOGGING = {
|
|||
'handlers': ['console'],
|
||||
'level': 'DEBUG',
|
||||
},
|
||||
'seminar.prihlaska.form':{
|
||||
'handlers': ['console','registration_logfile'],
|
||||
'level': 'INFO'
|
||||
},
|
||||
'seminar.prihlaska.problem':{
|
||||
'handlers': ['console','mail_registration','registration_error_log'],
|
||||
'level': 'INFO'
|
||||
},
|
||||
|
||||
# Catch-all logger
|
||||
'': {
|
||||
|
@ -237,6 +248,24 @@ LOGGING = {
|
|||
'class': 'django.utils.log.AdminEmailHandler',
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
'mail_registraion': {
|
||||
'level': 'WARN',
|
||||
'class': 'django.utils.log.AdminEmailHandler',
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
'registration_logfile':{
|
||||
'level': 'INFO',
|
||||
'class': 'logging.FileHandler',
|
||||
# filename declared in specific configuration files
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
'registration_error_log':{
|
||||
'level': 'INFO',
|
||||
'class': 'logging.FileHandler',
|
||||
# filename declared in specific configuration files
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
|
|
13
mamweb/settings_debug.py
Normal file
13
mamweb/settings_debug.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Debugovaci nastaveni settings.py
|
||||
# Pro vyber tohoto nastaveni muzete pouzit tez:
|
||||
# DJANGO_SETTINGS_MODULE=mamweb.settings_debug ./manage.py ...
|
||||
|
||||
# Import local settings
|
||||
from .settings_local import *
|
||||
|
||||
# Vypisovani databazovych dotazu do konzole
|
||||
LOGGING['loggers']['django.db.backends'] = {
|
||||
'level': 'DEBUG',
|
||||
'handlers': ['console'],
|
||||
'propagate': False,
|
||||
}
|
|
@ -78,6 +78,11 @@ LOGGING = {
|
|||
# 'handlers': ['console'],
|
||||
# 'propagate': False,
|
||||
#},
|
||||
'werkzeug': {
|
||||
'handlers': ['console'],
|
||||
'level': 'DEBUG',
|
||||
'propagate': True,
|
||||
},
|
||||
'': {
|
||||
'handlers': ['console'],
|
||||
'level': 'DEBUG',
|
||||
|
@ -88,3 +93,5 @@ LOGGING = {
|
|||
|
||||
# set to 'DEBUG' for EXTRA verbose output
|
||||
# LOGGING['handlers']['console']['level'] = 'INFO'
|
||||
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||
|
|
|
@ -61,6 +61,8 @@ CSRF_COOKIE_SECURE = True
|
|||
|
||||
LOGGING['loggers']['']['handlers'] = ['console', 'mail_admins']
|
||||
LOGGING['loggers']['django']['handlers'] = ['console', 'mail_admins']
|
||||
LOGGING['handlers']['registration_logfile']['filename'] = '/home/mam-web/logs/prod/registration.log'
|
||||
LOGGING['handlers']['registration_error_log']['filename'] = '/home/mam-web/logs/prod/registration_errors.log'
|
||||
|
||||
|
||||
# E-MAIL NOTIFICATIONS
|
||||
|
|
|
@ -65,3 +65,5 @@ CSRF_COOKIE_SECURE = True
|
|||
|
||||
LOGGING['loggers']['']['handlers'] = ['console', 'mail_admins']
|
||||
LOGGING['loggers']['django']['handlers'] = ['console', 'mail_admins']
|
||||
LOGGING['handlers']['registration_logfile']['filename'] = '/home/mam-web/logs/test/registration.log'
|
||||
LOGGING['handlers']['registration_error_log']['filename'] = '/home/mam-web/logs/test/registration_errors.log'
|
||||
|
|
|
@ -862,3 +862,27 @@ div.nahledy_cisel {
|
|||
div.nahledy_cisel div, div.nahledy_cisel img {
|
||||
position: absolute;
|
||||
}
|
||||
ul.form {
|
||||
list-style-type: none;
|
||||
padding-left: 0px;
|
||||
}
|
||||
label.field-label {
|
||||
font-weight: normal;
|
||||
}
|
||||
label.field-required {
|
||||
font-weight: bold;
|
||||
}
|
||||
.field-error {
|
||||
font-size: 14px;
|
||||
color: red;
|
||||
}
|
||||
ul.form li{
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
p.gdpr {
|
||||
font-size: 6pt;
|
||||
margin-bottom: .66em;
|
||||
}
|
||||
div.gdpr {
|
||||
font-size: 6pt;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
{% block extrahead %}
|
||||
<link rel="shortcut icon" href="{% static 'favicon.ico' %}" type="image/x-icon">
|
||||
<script src="{% static 'js/jquery-1.11.1.js' %}"></script>
|
||||
{% include 'autocomplete_light/static.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block branding %}
|
||||
|
|
|
@ -27,8 +27,8 @@
|
|||
}
|
||||
});
|
||||
</script>
|
||||
<script type="text/javascript"
|
||||
src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
|
||||
<script type="text/javascript" async
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.6/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
|
||||
</script>
|
||||
|
||||
{# script specifický pro stránku #}
|
||||
|
@ -97,7 +97,27 @@
|
|||
|
||||
|
||||
<div class='row content'>
|
||||
<div class='col-md-12'>
|
||||
{% sitetree_menu from "main_menu" include "trunk" %}
|
||||
|
||||
{#
|
||||
{% for item in menu_top %}
|
||||
<li class="{% if item.selected %} active {% endif %}">
|
||||
<a href="{{ item.url }}"> <i class="{{ item.icon_class }}"></i> {{ item.name }}</a>
|
||||
</li>
|
||||
{% if item.submenu %}
|
||||
<ul>
|
||||
{% for menu in item.submenu %}
|
||||
<li class="{% if menu.selected %} active {% endif %}">
|
||||
<a href="{{ menu.url }}">{{ menu.name }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
#}
|
||||
|
||||
|
||||
<div class='col-md-12'>
|
||||
{% block content %}
|
||||
{% endblock content %}
|
||||
</div>
|
||||
|
|
|
@ -11,7 +11,6 @@ urlpatterns = [
|
|||
# Admin a nastroje
|
||||
path('admin/', admin.site.urls), # NOQA
|
||||
path('ckeditor/', include('ckeditor_uploader.urls')),
|
||||
path('autocomplete/', include('autocomplete_light.urls')),
|
||||
|
||||
# Seminarova aplikace (ma vlastni podadresare)
|
||||
path('', include('seminar.urls')),
|
||||
|
|
|
@ -22,11 +22,12 @@ django-solo
|
|||
django-ckeditor
|
||||
django-flat-theme
|
||||
django-taggit
|
||||
django-autocomplete-light==2.3.6
|
||||
django-autocomplete-light
|
||||
django-crispy-forms
|
||||
django-imagekit
|
||||
django-polymorphic
|
||||
django-sitetree
|
||||
django_reverse_admin
|
||||
|
||||
# Comments
|
||||
akismet==1.0.1
|
||||
|
|
173
seminar/admin.py
173
seminar/admin.py
|
@ -1,36 +1,171 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter
|
||||
from reversion.admin import VersionAdmin
|
||||
from django_reverse_admin import ReverseModelAdmin
|
||||
|
||||
# Todo: reversion
|
||||
|
||||
import seminar.models as m
|
||||
|
||||
admin.site.register(m.Osoba)
|
||||
admin.site.register(m.Skola)
|
||||
admin.site.register(m.Prijemce)
|
||||
admin.site.register(m.Resitel)
|
||||
admin.site.register(m.Rocnik)
|
||||
admin.site.register(m.Cislo)
|
||||
admin.site.register(m.Organizator)
|
||||
admin.site.register(m.Soustredeni)
|
||||
admin.site.register(m.Problem)
|
||||
admin.site.register(m.Tema)
|
||||
admin.site.register(m.Clanek)
|
||||
|
||||
@admin.register(m.Osoba)
|
||||
class OsobaAdmin(admin.ModelAdmin):
|
||||
actions = ['synchronizuj_maily']
|
||||
|
||||
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ů"
|
||||
|
||||
@admin.register(m.Problem)
|
||||
class ProblemAdmin(PolymorphicParentModelAdmin):
|
||||
base_model = m.Problem
|
||||
child_models = [
|
||||
m.Tema,
|
||||
m.Clanek,
|
||||
m.Uloha,
|
||||
]
|
||||
|
||||
@admin.register(m.Tema)
|
||||
class TemaAdmin(PolymorphicChildModelAdmin):
|
||||
base_model = m.Tema
|
||||
show_in_index = True
|
||||
|
||||
@admin.register(m.Clanek)
|
||||
class ClanekAdmin(PolymorphicChildModelAdmin):
|
||||
base_model = m.Clanek
|
||||
show_in_index = True
|
||||
|
||||
@admin.register(m.Uloha)
|
||||
class UlohaAdmin(PolymorphicChildModelAdmin):
|
||||
base_model = m.Uloha
|
||||
show_in_index = True
|
||||
|
||||
class TextAdminInline(admin.TabularInline):
|
||||
model = m.Text
|
||||
exclude = ['text_zkraceny_set','text_zkraceny']
|
||||
admin.site.register(m.Text)
|
||||
admin.site.register(m.Uloha)
|
||||
admin.site.register(m.Reseni)
|
||||
admin.site.register(m.Hodnoceni)
|
||||
|
||||
class ResitelInline(admin.TabularInline):
|
||||
model = m.Resitel
|
||||
extra = 1
|
||||
admin.site.register(m.Resitel)
|
||||
|
||||
class PrilohaReseniInline(admin.TabularInline):
|
||||
model = m.PrilohaReseni
|
||||
extra = 1
|
||||
admin.site.register(m.PrilohaReseni)
|
||||
|
||||
class Reseni_ResiteleInline(admin.TabularInline):
|
||||
model = m.Reseni_Resitele
|
||||
|
||||
@admin.register(m.Reseni)
|
||||
class ReseniAdmin(ReverseModelAdmin):
|
||||
base_model = m.Reseni
|
||||
inline_type = 'tabular'
|
||||
inline_reverse = ['text_cely','resitele']
|
||||
exclude = ['text_zkraceny', 'text_zkraceny_set']
|
||||
inlines = [PrilohaReseniInline]
|
||||
# FAIL in template
|
||||
# inlines = [PrilohaReseniInline,Reseni_ResiteleInline]
|
||||
|
||||
admin.site.register(m.Hodnoceni)
|
||||
admin.site.register(m.Pohadka)
|
||||
admin.site.register(m.Konfera)
|
||||
admin.site.register(m.Obrazek)
|
||||
admin.site.register(m.TreeNode)
|
||||
admin.site.register(m.RocnikNode)
|
||||
admin.site.register(m.CisloNode)
|
||||
admin.site.register(m.MezicisloNode)
|
||||
admin.site.register(m.TemaVCisleNode)
|
||||
admin.site.register(m.KonferaNode)
|
||||
admin.site.register(m.ClanekNode)
|
||||
admin.site.register(m.UlohaZadaniNode)
|
||||
admin.site.register(m.PohadkaNode)
|
||||
admin.site.register(m.UlohaVzorakNode)
|
||||
admin.site.register(m.TextNode)
|
||||
|
||||
|
||||
# Polymorfismus pro stromy
|
||||
# TODO: Inlines podle https://django-polymorphic.readthedocs.io/en/stable/admin.html
|
||||
|
||||
@admin.register(m.TreeNode)
|
||||
class TreeNodeAdmin(PolymorphicParentModelAdmin):
|
||||
base_model = m.TreeNode
|
||||
child_models = [
|
||||
m.RocnikNode,
|
||||
m.CisloNode,
|
||||
m.MezicisloNode,
|
||||
m.TemaVCisleNode,
|
||||
m.KonferaNode,
|
||||
m.ClanekNode,
|
||||
m.UlohaZadaniNode,
|
||||
m.PohadkaNode,
|
||||
m.UlohaVzorakNode,
|
||||
m.TextNode,
|
||||
]
|
||||
|
||||
actions = ['aktualizuj_nazvy']
|
||||
|
||||
# XXX: nejspíš je to totální DB HOG, nechcete to použít moc často.
|
||||
def aktualizuj_nazvy(self, request, queryset):
|
||||
newqs = queryset.get_real_instances()
|
||||
for tn in newqs:
|
||||
tn.aktualizuj_nazev()
|
||||
tn.save()
|
||||
self.message_user(request, "Názvy aktualizovány.")
|
||||
aktualizuj_nazvy.short_description = "Aktualizuj vybraným TreeNodům názvy"
|
||||
|
||||
@admin.register(m.RocnikNode)
|
||||
class RocnikNodeAdmin(PolymorphicChildModelAdmin):
|
||||
base_model = m.RocnikNode
|
||||
show_in_index = True
|
||||
|
||||
@admin.register(m.CisloNode)
|
||||
class CisloNodeAdmin(PolymorphicChildModelAdmin):
|
||||
base_model = m.CisloNode
|
||||
show_in_index = True
|
||||
|
||||
@admin.register(m.MezicisloNode)
|
||||
class MezicisloNodeAdmin(PolymorphicChildModelAdmin):
|
||||
base_model = m.MezicisloNode
|
||||
show_in_index = True
|
||||
|
||||
@admin.register(m.TemaVCisleNode)
|
||||
class TemaVCisleNodeAdmin(PolymorphicChildModelAdmin):
|
||||
base_model = m.TemaVCisleNode
|
||||
show_in_index = True
|
||||
|
||||
@admin.register(m.KonferaNode)
|
||||
class KonferaNodeAdmin(PolymorphicChildModelAdmin):
|
||||
base_model = m.KonferaNode
|
||||
show_in_index = True
|
||||
|
||||
@admin.register(m.ClanekNode)
|
||||
class ClanekNodeAdmin(PolymorphicChildModelAdmin):
|
||||
base_model = m.ClanekNode
|
||||
show_in_index = True
|
||||
|
||||
@admin.register(m.UlohaZadaniNode)
|
||||
class UlohaZadaniNodeAdmin(PolymorphicChildModelAdmin):
|
||||
base_model = m.UlohaZadaniNode
|
||||
show_in_index = True
|
||||
|
||||
@admin.register(m.PohadkaNode)
|
||||
class PohadkaNodeAdmin(PolymorphicChildModelAdmin):
|
||||
base_model = m.PohadkaNode
|
||||
show_in_index = True
|
||||
|
||||
@admin.register(m.UlohaVzorakNode)
|
||||
class UlohaVzorakNodeAdmin(PolymorphicChildModelAdmin):
|
||||
base_model = m.UlohaVzorakNode
|
||||
show_in_index = True
|
||||
|
||||
@admin.register(m.TextNode)
|
||||
class TextNodeAdmin(PolymorphicChildModelAdmin):
|
||||
base_model = m.TextNode
|
||||
show_in_index = True
|
||||
|
||||
|
||||
admin.site.register(m.Nastaveni)
|
||||
admin.site.register(m.Novinky)
|
||||
|
|
255
seminar/forms.py
255
seminar/forms.py
|
@ -1,6 +1,257 @@
|
|||
from django import forms
|
||||
from dal import autocomplete
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from .models import Skola, Resitel, Osoba, Problem
|
||||
import seminar.models as m
|
||||
|
||||
from datetime import date
|
||||
import logging
|
||||
|
||||
class LoginForm(forms.Form):
|
||||
username = forms.CharField(label='Přihlašovací jméno',
|
||||
max_length=256,
|
||||
required=True)
|
||||
password = forms.CharField(
|
||||
label='Heslo',
|
||||
max_length=256,
|
||||
required=True,
|
||||
widget=forms.PasswordInput())
|
||||
|
||||
|
||||
class PrihlaskaForm(forms.Form):
|
||||
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')
|
||||
password = forms.CharField(
|
||||
label='Heslo',
|
||||
max_length=256,
|
||||
required=True,
|
||||
widget=forms.PasswordInput())
|
||||
password_check = forms.CharField(
|
||||
label='Ověření hesla',
|
||||
max_length=256,
|
||||
required=True,
|
||||
widget=forms.PasswordInput())
|
||||
|
||||
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(label='Telefon',max_length=256, required=False)
|
||||
datum_narozeni = forms.DateField(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)
|
||||
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.objects.get(email=email)
|
||||
msg = "Email {} exists".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 ProfileEditForm(forms.Form):
|
||||
username = forms.CharField(label='Přihlašovací jméno',
|
||||
max_length=256,
|
||||
required=True)
|
||||
|
||||
jmeno = forms.CharField(label='Jméno', max_length=256, required=True)
|
||||
prijmeni = forms.CharField(label='Příjmení', max_length=256, required=True)
|
||||
pohlavi_muz = forms.ChoiceField(label='Pohlaví',
|
||||
choices = ((True,'muž'),(False,'žena')), required=True)
|
||||
email = forms.EmailField(label='E-mail',max_length=256, required=True)
|
||||
telefon = forms.CharField(label='Telefon',max_length=256, required=False)
|
||||
datum_narozeni = forms.DateField(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)
|
||||
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.get(email=email)
|
||||
# msg = "Email {} exists".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 VlozReseniForm(forms.Form):
|
||||
#FIXME jen podproblémy daného problému
|
||||
problem = forms.ModelChoiceField(label='Problém',queryset=m.Problem.objects.all())
|
||||
# to_field_name
|
||||
#problem = models.ManyToManyField(Problem, verbose_name='problém', help_text='Problém',
|
||||
# through='Hodnoceni')
|
||||
|
||||
# FIXME pridat vice resitelu
|
||||
resitel = forms.ModelChoiceField(label="Řešitel",
|
||||
queryset=Resitel.objects.all(),
|
||||
widget=autocomplete.ModelSelect2(
|
||||
url='autocomplete_resitel',
|
||||
attrs = {'data-placeholder--id': '-1',
|
||||
'data-placeholder--text' : '---',
|
||||
'data-allow-clear': 'true'})
|
||||
)
|
||||
|
||||
|
||||
#resitele = models.ManyToManyField(Resitel, verbose_name='autoři řešení',
|
||||
# help_text='Seznam autorů řešení', through='Reseni_Resitele')
|
||||
|
||||
cas_doruceni = forms.DateField(label="Čas doručení")
|
||||
|
||||
#cas_doruceni = models.DateTimeField('čas_doručení', default=timezone.now, blank=True)
|
||||
|
||||
forma = forms.ChoiceField(label="Forma řešení",choices = m.Reseni.FORMA_CHOICES)
|
||||
#forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False,
|
||||
# default=FORMA_EMAIL)
|
||||
|
||||
poznamka = forms.CharField(label='Neveřejná poznámka')
|
||||
#poznamka = models.TextField('neveřejná poznámka', blank=True,
|
||||
# help_text='Neveřejná poznámka k řešení (plain text)')
|
||||
|
||||
#TODO body do cisla
|
||||
#TODO prilohy
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
#self.fields['favorite_color'] = forms.ChoiceField(choices=[(color.id, color.name) for color in Resitel.objects.all()])
|
||||
|
||||
|
||||
class NameForm(forms.Form):
|
||||
your_name = forms.CharField(label='Your name', max_length=100)
|
||||
|
||||
|
||||
|
|
20
seminar/management/commands/nukedb.py
Normal file
20
seminar/management/commands/nukedb.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
from mamweb.settings import INSTALLED_APPS
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.core.management import call_command
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Odmigruje všechny moduly (i.e. smaže všechny tabulky, ale databázi nechá)"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
# TODO: --force (makat a neblábolit)
|
||||
pass
|
||||
def handle(self, *args, **options):
|
||||
# TODO: zeptat se
|
||||
for app in INSTALLED_APPS:
|
||||
app = app.split('.')[-1]
|
||||
try:
|
||||
call_command('migrate', app, 'zero')
|
||||
except CommandError:
|
||||
# app nemá migrace (aspoň typicky)
|
||||
pass
|
||||
call_command('showmigrations')
|
|
@ -15,21 +15,34 @@ User = django.contrib.auth.get_user_model()
|
|||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Clear database and load testing data."
|
||||
help = "Clear database and load testing data."
|
||||
|
||||
def handle(self, *args, **options):
|
||||
assert settings.DEBUG == True
|
||||
dbfile = settings.DATABASES['default']['NAME']
|
||||
if os.path.exists(dbfile):
|
||||
os.rename(dbfile, dbfile + '.old')
|
||||
self.stderr.write('Stara databaze prejmenovana na "%s"' % (dbfile + '.old'))
|
||||
call_command('migrate', no_input=True)
|
||||
self.stdout.write('Vytvarim uzivatele "admin" (heslo "admin") a pseudo-nahodna data ...')
|
||||
create_test_data(size=8)
|
||||
self.stdout.write('Vytvoreno {} uzivatelu, {} skol, {} resitelu, {} rocniku, {} cisel,'
|
||||
' {} problemu, {} reseni.'.format(User.objects.count(), Skola.objects.count(),
|
||||
Resitel.objects.count(), Rocnik.objects.count(), Cislo.objects.count(),
|
||||
Problem.objects.count(), Reseni.objects.count()))
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--no-clean',
|
||||
action='store_true',
|
||||
help='Změny se provedou v aktuální DB, ne v čisté. Aktuální DB se nezachová. (jen k debugování)',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-migrate',
|
||||
action='store_true',
|
||||
help='Neprovádět migrace před generováním testovacích dat (jen k debugování)',
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
assert settings.DEBUG == True
|
||||
dbfile = settings.DATABASES['default']['NAME']
|
||||
if os.path.exists(dbfile) and not options['no_clean']:
|
||||
os.rename(dbfile, dbfile + '.old')
|
||||
self.stderr.write('Stara databaze prejmenovana na "%s"' % (dbfile + '.old'))
|
||||
if not options['no_migrate']:
|
||||
call_command('migrate', no_input=True)
|
||||
self.stdout.write('Vytvarim uzivatele "admin" (heslo "admin") a pseudo-nahodna data ...')
|
||||
create_test_data(size=8)
|
||||
self.stdout.write('Vytvoreno {} uzivatelu, {} skol, {} resitelu, {} rocniku, {} cisel,'
|
||||
' {} problemu, {} reseni.'.format(User.objects.count(), Skola.objects.count(),
|
||||
Resitel.objects.count(), Rocnik.objects.count(), Cislo.objects.count(),
|
||||
Problem.objects.count(), Reseni.objects.count()))
|
||||
|
||||
|
||||
|
||||
|
|
2164
seminar/migrations/0001_squashed_0067_auto_20190814_0805.py
Normal file
2164
seminar/migrations/0001_squashed_0067_auto_20190814_0805.py
Normal file
File diff suppressed because it is too large
Load diff
31
seminar/migrations/0065_treenode_polymorphic_ctype.py
Normal file
31
seminar/migrations/0065_treenode_polymorphic_ctype.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
# Generated by Django 2.2.4 on 2019-08-13 19:36
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
def vyrob_treenodum_ctypes(apps, schema_editor):
|
||||
# Kód zkopírovaný z dokumentace: https://django-polymorphic.readthedocs.io/en/stable/migrating.html
|
||||
# XXX: Nevím, jestli se tohle náhodou nemělo spustit na všech childech (jen/i)
|
||||
TreeNode = apps.get_model('seminar', 'TreeNode')
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
|
||||
new_ct = ContentType.objects.get_for_model(TreeNode)
|
||||
TreeNode.objects.filter(polymorphic_ctype__isnull=True).update(polymorphic_ctype=new_ct)
|
||||
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
('seminar', '0064_auto_20190610_2358'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='treenode',
|
||||
name='polymorphic_ctype',
|
||||
field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_seminar.treenode_set+', to='contenttypes.ContentType'),
|
||||
),
|
||||
migrations.RunPython(vyrob_treenodum_ctypes, migrations.RunPython.noop),
|
||||
]
|
29
seminar/migrations/0066_problem_polymorphic_ctype.py
Normal file
29
seminar/migrations/0066_problem_polymorphic_ctype.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Generated by Django 2.2.4 on 2019-08-13 19:45
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
def vyrob_problemum_ctypes(apps, schema_editor):
|
||||
# Kód zkopírovaný z dokumentace: https://django-polymorphic.readthedocs.io/en/stable/migrating.html
|
||||
# XXX: Nevím, jestli se tohle náhodou nemělo spustit na všech childech (jen/i)
|
||||
Problem = apps.get_model('seminar', 'Problem')
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
|
||||
new_ct = ContentType.objects.get_for_model(Problem)
|
||||
Problem.objects.filter(polymorphic_ctype__isnull=True).update(polymorphic_ctype=new_ct)
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
('seminar', '0065_treenode_polymorphic_ctype'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='problem',
|
||||
name='polymorphic_ctype',
|
||||
field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_seminar.problem_set+', to='contenttypes.ContentType'),
|
||||
),
|
||||
migrations.RunPython(vyrob_problemum_ctypes, migrations.RunPython.noop),
|
||||
]
|
18
seminar/migrations/0067_auto_20190814_0805.py
Normal file
18
seminar/migrations/0067_auto_20190814_0805.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.2.4 on 2019-08-14 06:05
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0066_problem_polymorphic_ctype'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='konfera',
|
||||
name='nazev',
|
||||
field=models.CharField(help_text='Název konfery', max_length=100, verbose_name='název konfery'),
|
||||
),
|
||||
]
|
107
seminar/migrations/0068_treenode_nazev.py
Normal file
107
seminar/migrations/0068_treenode_nazev.py
Normal file
|
@ -0,0 +1,107 @@
|
|||
# Generated by Django 2.2.5 on 2019-09-26 19:35
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
# Migrace nejspíš neumí volat metody modelů:
|
||||
# https://stackoverflow.com/questions/28777338/django-migrations-runpython-not-able-to-call-model-methods#37685925
|
||||
|
||||
def fix_RocnikNode_names(apps,schema_editor):
|
||||
Objects = apps.get_model('seminar', 'RocnikNode')
|
||||
for obj in Objects.objects.all():
|
||||
obj.nazev = str(obj.rocnik)+" (RocnikNode)"
|
||||
obj.save()
|
||||
|
||||
def fix_CisloNode_names(apps,schema_editor):
|
||||
Objects = apps.get_model('seminar', 'CisloNode')
|
||||
for obj in Objects.objects.all():
|
||||
obj.nazev = str(obj.cislo)+" (CisloNode)"
|
||||
obj.save()
|
||||
|
||||
def fix_MezicisloNode_names(apps,schema_editor):
|
||||
Objects = apps.get_model('seminar', 'MezicisloNode')
|
||||
for obj in Objects.objects.all():
|
||||
if obj.prev:
|
||||
if (obj.prev.get_real_instance_class() != CisloNode and
|
||||
obj.prev.get_real_instance_class() != MezicisloNode):
|
||||
raise ValueError("Předchůdce není číslo!")
|
||||
posledni = obj.prev.cislo
|
||||
obj.nazev = "Mezičíslo po čísle"+str(posledni)+" (MezicisloNode)"
|
||||
elif obj.root:
|
||||
if obj.root.get_real_instance_class() != RocnikNode:
|
||||
raise ValueError("Kořen stromu není ročník!")
|
||||
rocnik = obj.root.rocnik
|
||||
obj.nazev = "První mezičíslo ročníku "+" (MezicisloNode)"
|
||||
else:
|
||||
print("!!!!! Nějaké neidentifikované mezičíslo !!!!!")
|
||||
obj.nazev = "Neidentifikovatelné mezičíslo! (MezicisloNode)"
|
||||
obj.save()
|
||||
|
||||
def fix_TemaVCisleNode_names(apps,schema_editor):
|
||||
Objects = apps.get_model('seminar', 'TemaVCisleNode')
|
||||
for obj in Objects.objects.all():
|
||||
obj.nazev = str(obj.tema)+" (TemaVCisleNode)"
|
||||
obj.save()
|
||||
|
||||
def fix_KonferaNode_names(apps,schema_editor):
|
||||
Objects = apps.get_model('seminar', 'KonferaNode')
|
||||
for obj in Objects.objects.all():
|
||||
obj.nazev = str(obj.konfera)+" (KonferaNode)"
|
||||
obj.save()
|
||||
|
||||
def fix_ClanekNode_names(apps,schema_editor):
|
||||
Objects = apps.get_model('seminar', 'ClanekNode')
|
||||
for obj in Objects.objects.all():
|
||||
obj.nazev = str(obj.clanek)+" (ClanekNode)"
|
||||
obj.save()
|
||||
|
||||
def fix_UlohaZadaniNode_names(apps,schema_editor):
|
||||
Objects = apps.get_model('seminar', 'UlohaZadaniNode')
|
||||
for obj in Objects.objects.all():
|
||||
obj.nazev = str(obj.uloha)+" (UlohaZadaniNode)"
|
||||
obj.save()
|
||||
|
||||
def fix_PohadkaNode_names(apps,schema_editor):
|
||||
Objects = apps.get_model('seminar', 'PohadkaNode')
|
||||
for obj in Objects.objects.all():
|
||||
obj.nazev = str(obj.pohadka)+" (PohadkaNode)"
|
||||
obj.save()
|
||||
|
||||
def fix_UlohaVzorakNode_names(apps,schema_editor):
|
||||
Objects = apps.get_model('seminar', 'UlohaVzorakNode')
|
||||
for obj in Objects.objects.all():
|
||||
obj.nazev = str(obj.uloha)+" (UlohaVzorakNode)"
|
||||
obj.save()
|
||||
|
||||
def fix_TextNode_names(apps,schema_editor):
|
||||
Objects = apps.get_model('seminar', 'TextNode')
|
||||
for obj in Objects.objects.all():
|
||||
obj.nazev = str(obj.text)+" (TextNode)"
|
||||
obj.save()
|
||||
|
||||
def fix_all_names(apps,schema_editor):
|
||||
fix_RocnikNode_names(apps,schema_editor)
|
||||
fix_CisloNode_names(apps,schema_editor)
|
||||
fix_MezicisloNode_names(apps,schema_editor)
|
||||
fix_TemaVCisleNode_names(apps,schema_editor)
|
||||
fix_KonferaNode_names(apps,schema_editor)
|
||||
fix_ClanekNode_names(apps,schema_editor)
|
||||
fix_UlohaZadaniNode_names(apps,schema_editor)
|
||||
fix_PohadkaNode_names(apps,schema_editor)
|
||||
fix_UlohaVzorakNode_names(apps,schema_editor)
|
||||
fix_TextNode_names(apps,schema_editor)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0067_auto_20190814_0805'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='treenode',
|
||||
name='nazev',
|
||||
field=models.TextField(help_text='Tento název se zobrazuje v nabídkách pro výběr vhodného TreeNode', null=True, verbose_name='název tohoto node'),
|
||||
),
|
||||
migrations.RunPython(fix_all_names),
|
||||
]
|
28
seminar/migrations/0069_auto_20191120_2115.py
Normal file
28
seminar/migrations/0069_auto_20191120_2115.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Generated by Django 2.2.7 on 2019-11-20 20:15
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0068_treenode_nazev'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='cislo',
|
||||
options={'ordering': ['-rocnik__rocnik', '-poradi'], 'verbose_name': 'Číslo', 'verbose_name_plural': 'Čísla'},
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='cislo',
|
||||
old_name='cislo',
|
||||
new_name='poradi',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='problem',
|
||||
name='nadproblem',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='podproblem', to='seminar.Problem', verbose_name='nadřazený problém'),
|
||||
),
|
||||
]
|
23
seminar/migrations/0070_auto_20191120_2357.py
Normal file
23
seminar/migrations/0070_auto_20191120_2357.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 2.2.7 on 2019-11-20 22:57
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0069_auto_20191120_2115'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='tema',
|
||||
name='abstrakt',
|
||||
field=models.TextField(blank=True, verbose_name='Abstrakt na rozcestník'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='tema',
|
||||
name='obrazek',
|
||||
field=models.ImageField(null=True, upload_to='', verbose_name='Obrázek na rozcestník'),
|
||||
),
|
||||
]
|
17
seminar/migrations/0071_remove_nastaveni_aktualni_rocnik.py
Normal file
17
seminar/migrations/0071_remove_nastaveni_aktualni_rocnik.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 2.2.7 on 2019-11-21 17:38
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0070_auto_20191120_2357'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='nastaveni',
|
||||
name='aktualni_rocnik',
|
||||
),
|
||||
]
|
23
seminar/migrations/0072_auto_20191204_2257.py
Normal file
23
seminar/migrations/0072_auto_20191204_2257.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 2.2.7 on 2019-12-04 21:57
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0071_remove_nastaveni_aktualni_rocnik'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='treenode',
|
||||
name='srolovatelne',
|
||||
field=models.BooleanField(blank=True, help_text='Bude na stránce témátka možnost tuto položku skrýt', null=True, verbose_name='Srolovatelné'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='treenode',
|
||||
name='zajimave',
|
||||
field=models.BooleanField(default=False, help_text='Zobrazí se daná věc na rozcestníku témátek', verbose_name='Zajímavé'),
|
||||
),
|
||||
]
|
22
seminar/migrations/0073_copy_osoba_email_to_user_email.py
Normal file
22
seminar/migrations/0073_copy_osoba_email_to_user_email.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Generated by Django 2.2.9 on 2020-01-15 21:28
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
def copy_mails(apps, schema_editor):
|
||||
Osoba = apps.get_model('seminar', 'Osoba')
|
||||
|
||||
for o in Osoba.objects.all():
|
||||
if o.user is not None:
|
||||
u = o.user
|
||||
u.email = o.email
|
||||
u.save()
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0072_auto_20191204_2257'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(copy_mails, migrations.RunPython.noop)
|
||||
]
|
|
@ -21,10 +21,11 @@ from taggit.managers import TaggableManager
|
|||
|
||||
from reversion import revisions as reversion
|
||||
|
||||
from seminar.utils import roman
|
||||
from seminar.utils import roman, FirstTagParser # Pro získání úryvku z TextNode
|
||||
|
||||
from unidecode import unidecode
|
||||
from unidecode import unidecode # Používám pro získání ID odkazu (ještě je to někde po někom zakomentované)
|
||||
|
||||
from polymorphic.models import PolymorphicModel
|
||||
|
||||
class SeminarModelBase(models.Model):
|
||||
|
||||
|
@ -129,6 +130,17 @@ class Osoba(SeminarModelBase):
|
|||
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.
|
||||
|
@ -352,7 +364,7 @@ class Rocnik(SeminarModelBase):
|
|||
|
||||
def verejna_cisla(self):
|
||||
vc = [c for c in self.cisla.all() if c.verejne()]
|
||||
vc.sort(key=lambda c: c.cislo)
|
||||
vc.sort(key=lambda c: c.poradi)
|
||||
return vc
|
||||
|
||||
def posledni_verejne_cislo(self):
|
||||
|
@ -361,7 +373,7 @@ class Rocnik(SeminarModelBase):
|
|||
|
||||
def verejne_vysledkovky_cisla(self):
|
||||
vc = list(self.cisla.filter(verejna_vysledkovka=True))
|
||||
vc.sort(key=lambda c: c.cislo)
|
||||
vc.sort(key=lambda c: c.poradi)
|
||||
return vc
|
||||
|
||||
def posledni_zverejnena_vysledkovka_cislo(self):
|
||||
|
@ -383,10 +395,18 @@ class Rocnik(SeminarModelBase):
|
|||
cache.set(name, c, 300)
|
||||
return c
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save(*args, **kwargs)
|
||||
# *Node.save() aktualizuje název *Nodu.
|
||||
try:
|
||||
self.rocniknode.save()
|
||||
except ObjectDoesNotExist:
|
||||
# Neexistující *Node nemá smysl aktualizovat.
|
||||
pass
|
||||
|
||||
def cislo_pdf_filename(self, filename):
|
||||
rocnik = str(self.rocnik.rocnik)
|
||||
return os.path.join('cislo', 'pdf', rocnik, '{}-{}.pdf'.format(rocnik, self.cislo))
|
||||
return os.path.join('cislo', 'pdf', rocnik, '{}-{}.pdf'.format(rocnik, self.poradi))
|
||||
|
||||
@reversion.register(ignore_duplicates=True)
|
||||
class Cislo(SeminarModelBase):
|
||||
|
@ -395,7 +415,7 @@ class Cislo(SeminarModelBase):
|
|||
db_table = 'seminar_cisla'
|
||||
verbose_name = 'Číslo'
|
||||
verbose_name_plural = 'Čísla'
|
||||
ordering = ['-rocnik__rocnik', '-cislo']
|
||||
ordering = ['-rocnik__rocnik', '-poradi']
|
||||
|
||||
# Interní ID
|
||||
id = models.AutoField(primary_key = True)
|
||||
|
@ -403,7 +423,7 @@ class Cislo(SeminarModelBase):
|
|||
rocnik = models.ForeignKey(Rocnik, verbose_name='ročník', related_name='cisla',
|
||||
db_index=True,on_delete=models.PROTECT)
|
||||
|
||||
cislo = models.CharField('název čísla', max_length=32, db_index=True,
|
||||
poradi = models.CharField('název čísla', max_length=32, db_index=True,
|
||||
help_text='Většinou jen "1", vyjímečně "7-8", lexikograficky určuje pořadí v ročníku!')
|
||||
|
||||
datum_vydani = models.DateField('datum vydání', blank=True, null=True,
|
||||
|
@ -436,20 +456,20 @@ class Cislo(SeminarModelBase):
|
|||
# CisloNode
|
||||
|
||||
def kod(self):
|
||||
return '%s.%s' % (self.rocnik.rocnik, self.cislo)
|
||||
return '%s.%s' % (self.rocnik.rocnik, self.poradi)
|
||||
kod.short_description = 'Kód čísla'
|
||||
|
||||
def __str__(self):
|
||||
# Potenciální DB HOG, pokud by se ročník necachoval
|
||||
r = Rocnik.cached_rocnik(self.rocnik_id)
|
||||
return '{}.{}'.format(r.rocnik, self.cislo)
|
||||
return '{}.{}'.format(r.rocnik, self.poradi)
|
||||
|
||||
def verejne(self):
|
||||
return self.verejne_db
|
||||
verejne.boolean = True
|
||||
|
||||
def verejne_url(self):
|
||||
return reverse('seminar_cislo', kwargs={'rocnik': self.rocnik.rocnik, 'cislo': self.cislo})
|
||||
return reverse('seminar_cislo', kwargs={'rocnik': self.rocnik.rocnik, 'cislo': self.poradi})
|
||||
|
||||
def nasledujici(self):
|
||||
"Vrací None, pokud je toto poslední"
|
||||
|
@ -471,11 +491,20 @@ class Cislo(SeminarModelBase):
|
|||
def get(cls, rocnik, cislo):
|
||||
try:
|
||||
r = Rocnik.objects.get(rocnik=rocnik)
|
||||
c = r.cisla.get(cislo=cislo)
|
||||
c = r.cisla.get(poradi=cislo)
|
||||
except ObjectDoesNotExist:
|
||||
return None
|
||||
return c
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save(*args, **kwargs)
|
||||
# *Node.save() aktualizuje název *Nodu.
|
||||
try:
|
||||
self.cislonode.save()
|
||||
except ObjectDoesNotExist:
|
||||
# Neexistující *Node nemá smysl aktualizovat.
|
||||
pass
|
||||
|
||||
@reversion.register(ignore_duplicates=True)
|
||||
class Organizator(SeminarModelBase):
|
||||
# zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu
|
||||
|
@ -583,7 +612,8 @@ class Soustredeni(SeminarModelBase):
|
|||
|
||||
|
||||
@reversion.register(ignore_duplicates=True)
|
||||
class Problem(SeminarModelBase):
|
||||
# Pozor na následující řádek. *Nekrmit, asi kouše!*
|
||||
class Problem(SeminarModelBase,PolymorphicModel):
|
||||
|
||||
class Meta:
|
||||
# Není abstraktní, protože se na něj jinak nedají dělat ForeignKeys.
|
||||
|
@ -601,11 +631,11 @@ class Problem(SeminarModelBase):
|
|||
id = models.AutoField(primary_key = True)
|
||||
|
||||
# Název
|
||||
nazev = models.CharField('název', max_length=256)
|
||||
nazev = models.CharField('název', max_length=256) # Zveřejnitelný na stránky
|
||||
|
||||
# Problém má podproblémy
|
||||
nadproblem = models.ForeignKey('self', verbose_name='nadřazený problém',
|
||||
related_name='nadproblem_%(class)s', null=True, blank=True,
|
||||
related_name='podproblem', null=True, blank=True,
|
||||
on_delete=models.SET_NULL)
|
||||
|
||||
STAV_NAVRH = 'navrh'
|
||||
|
@ -698,6 +728,9 @@ class Tema(Problem):
|
|||
rocnik = models.ForeignKey(Rocnik, verbose_name='ročník',blank=True, null=True,
|
||||
on_delete=models.PROTECT)
|
||||
|
||||
abstrakt = models.TextField('Abstrakt na rozcestník', blank=True)
|
||||
obrazek = models.ImageField('Obrázek na rozcestník', null=True)
|
||||
|
||||
def kod_v_rocniku(self):
|
||||
if self.stav == 'zadany':
|
||||
if self.nadproblem:
|
||||
|
@ -705,6 +738,12 @@ class Tema(Problem):
|
|||
return "t{}".format(self.kod)
|
||||
return '<Není zadaný>'
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save(*args, **kwargs)
|
||||
# *Node.save() aktualizuje název *Nodu.
|
||||
for tvcn in self.temavcislenode_set.all():
|
||||
tvcn.save()
|
||||
|
||||
class Clanek(Problem):
|
||||
class Meta:
|
||||
db_table = 'seminar_clanky'
|
||||
|
@ -725,6 +764,15 @@ class Clanek(Problem):
|
|||
return "c{}".format(self.kod)
|
||||
return '<Není zadaný>'
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save(*args, **kwargs)
|
||||
# *Node.save() aktualizuje název *Nodu.
|
||||
try:
|
||||
self.claneknode.save()
|
||||
except ObjectDoesNotExist:
|
||||
# Neexistující *Node nemá smysl aktualizovat.
|
||||
pass
|
||||
|
||||
class Text(SeminarModelBase):
|
||||
class Meta:
|
||||
db_table = 'seminar_texty'
|
||||
|
@ -742,8 +790,17 @@ class Text(SeminarModelBase):
|
|||
|
||||
# obrázky mají návaznost opačným směrem (vazba z druhé strany)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save(*args, **kwargs)
|
||||
# *Node.save() aktualizuje název *Nodu.
|
||||
for tn in self.textnode_set.all():
|
||||
tn.save()
|
||||
|
||||
|
||||
def __str__(self):
|
||||
parser = FirstTagParser()
|
||||
parser.feed(str(self.na_web))
|
||||
return parser.firstTag
|
||||
|
||||
class Uloha(Problem):
|
||||
class Meta:
|
||||
db_table = 'seminar_ulohy'
|
||||
|
@ -770,12 +827,26 @@ class Uloha(Problem):
|
|||
|
||||
def kod_v_rocniku(self):
|
||||
if self.stav == 'zadany':
|
||||
name="{}.u{}".format(self.cislo_zadani.cislo,self.kod)
|
||||
name="{}.u{}".format(self.cislo_zadani.poradi,self.kod)
|
||||
if self.nadproblem:
|
||||
return self.nadproblem.kod_v_rocniku()+name
|
||||
return name
|
||||
return '<Není zadaný>'
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save(*args, **kwargs)
|
||||
# *Node.save() aktualizuje název *Nodu.
|
||||
try:
|
||||
self.ulohazadaninode.save()
|
||||
except ObjectDoesNotExist:
|
||||
# Neexistující *Node nemá smysl aktualizovat.
|
||||
pass
|
||||
try:
|
||||
self.ulohavzoraknode.save()
|
||||
except ObjectDoesNotExist:
|
||||
# Neexistující *Node nemá smysl aktualizovat.
|
||||
pass
|
||||
|
||||
|
||||
@reversion.register(ignore_duplicates=True)
|
||||
class Reseni(SeminarModelBase):
|
||||
|
@ -828,7 +899,7 @@ class Reseni(SeminarModelBase):
|
|||
# Konfera
|
||||
|
||||
def __str__(self):
|
||||
return "{}: {}".format(self.resitel.osoba.plne_jmeno(), self.problem.nazev)
|
||||
return "{}({}): {}({})".format(self.resitele.first(),len(self.resitele.all()), self.problem.first() ,len(self.problem.all()))
|
||||
# NOTE: Potenciální DB HOG (bez select_related)
|
||||
|
||||
## Pravdepodobne uz nebude potreba:
|
||||
|
@ -856,7 +927,8 @@ class Hodnoceni(SeminarModelBase):
|
|||
|
||||
reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE)
|
||||
|
||||
problem = models.ForeignKey(Problem, verbose_name='problém', on_delete=models.PROTECT)
|
||||
problem = models.ForeignKey(Problem, verbose_name='problém',
|
||||
related_name='hodnoceni', on_delete=models.PROTECT)
|
||||
|
||||
def __str__(self):
|
||||
return "{}, {}, {}".format(self.problem, self.reseni, self.body)
|
||||
|
@ -957,6 +1029,14 @@ class Pohadka(SeminarModelBase):
|
|||
uryvek = self.text if len(self.text) < 50 else self.text[:(50-3)]+"..."
|
||||
return uryvek
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save(*args, **kwargs)
|
||||
# *Node.save() aktualizuje název *Nodu.
|
||||
try:
|
||||
self.pohadkanode.save()
|
||||
except ObjectDoesNotExist:
|
||||
# Neexistující *Node nemá smysl aktualizovat.
|
||||
pass
|
||||
|
||||
@reversion.register(ignore_duplicates=True)
|
||||
class Soustredeni_Ucastnici(SeminarModelBase):
|
||||
|
@ -1020,7 +1100,7 @@ class Konfera(models.Model):
|
|||
# Interní ID
|
||||
id = models.AutoField(primary_key = True)
|
||||
|
||||
nazev = models.CharField('název konfery', max_length=40, help_text = 'Název konfery')
|
||||
nazev = models.CharField('název konfery', max_length=100, help_text = 'Název konfery')
|
||||
|
||||
anotace = models.TextField('anotace', blank=True,
|
||||
help_text='Popis, o čem bude konfera.')
|
||||
|
@ -1067,6 +1147,15 @@ class Konfera(models.Model):
|
|||
def __str__(self):
|
||||
return "{}: ({})".format(self.nazev, self.soustredeni)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save(*args, **kwargs)
|
||||
# *Node.save() aktualizuje název *Nodu.
|
||||
try:
|
||||
self.konferanode.save()
|
||||
except ObjectDoesNotExist:
|
||||
# Neexistující *Node nemá smysl aktualizovat.
|
||||
pass
|
||||
|
||||
|
||||
# Vazebna tabulka. Mozna se generuje automaticky.
|
||||
@reversion.register(ignore_duplicates=True)
|
||||
|
@ -1139,12 +1228,13 @@ class Obrazek(SeminarModelBase):
|
|||
help_text = 'Černobílá verze obrázku do čísla',
|
||||
upload_to = 'obrazky/%Y/%m/%d/', blank=True, null=True)
|
||||
|
||||
class TreeNode(models.Model):
|
||||
class TreeNode(PolymorphicModel):
|
||||
class Meta:
|
||||
db_table = "seminar_nodes_treenode"
|
||||
verbose_name = "TreeNode"
|
||||
verbose_name_plural = "TreeNody"
|
||||
|
||||
# TODO: Nechceme radši jako root vyžadovat přímo RocnikNode?
|
||||
root = models.ForeignKey('TreeNode',
|
||||
related_name="potomci_set",
|
||||
null = True,
|
||||
|
@ -1162,14 +1252,59 @@ class TreeNode(models.Model):
|
|||
blank = True,
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name="další element na stejné úrovni")
|
||||
nazev = models.TextField("název tohoto node",
|
||||
help_text = "Tento název se zobrazuje v nabídkách pro výběr vhodného TreeNode",
|
||||
blank=False,
|
||||
null=True) # Nezveřejnitelný název na stránky - pouze do adminu
|
||||
zajimave = models.BooleanField(default = False,
|
||||
verbose_name = "Zajímavé",
|
||||
help_text = "Zobrazí se daná věc na rozcestníku témátek")
|
||||
srolovatelne = models.BooleanField(null = True, blank = True,
|
||||
verbose_name = "Srolovatelné",
|
||||
help_text = "Bude na stránce témátka možnost tuto položku skrýt")
|
||||
|
||||
# Slouží k debugování pro rychlé získání představy o podobě podstromu pod tímto TreeNode.
|
||||
def print_tree(self,indent=0):
|
||||
print("{}TreeNode({})".format(" "*indent,self.id))
|
||||
# FIXME: Tady se spoléháme na to, že nedeklarovaný primární klíč se jmenuje by default 'id', což není úplně správně
|
||||
print("{}{} (id: {})".format(" "*indent,self, self.id))
|
||||
if self.first_child:
|
||||
self.first_child.print_tree(indent=indent+2)
|
||||
if self.succ:
|
||||
self.succ.print_tree(indent=indent)
|
||||
|
||||
|
||||
def getOdkazStr(self): # String na rozcestník
|
||||
return self.first_child.getOdkazStr()
|
||||
|
||||
def getOdkaz(self): # ID HTML tagu, na který se bude scrollovat #{{self.getOdkaz}}
|
||||
# Jsem si vědom, že tu potenciálně vznikají kolize.
|
||||
# Přijdou mi natolik nepravděpodobné, že je neřeším
|
||||
# Chtěl jsem ale hezké odkazy
|
||||
string = unidecode(self.getOdkazStr())
|
||||
returnVal = ""
|
||||
i = 0
|
||||
while len(returnVal) < 16: # Max 15 znaků
|
||||
if i == len(string):
|
||||
break
|
||||
if string[i] == " ":
|
||||
returnVal += "-"
|
||||
if string[i].isalnum():
|
||||
returnVal += string[i].lower()
|
||||
i += 1
|
||||
return returnVal
|
||||
|
||||
def __str__(self):
|
||||
if self.nazev:
|
||||
return self.nazev
|
||||
else:
|
||||
#TODO: logování
|
||||
return "Nepojmenovaný Treenode"
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.aktualizuj_nazev()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def aktualizuj_nazev(self):
|
||||
raise NotImplementedError("Pokus o aktualizaci názvu obecného TreeNode místo konkrétní instance")
|
||||
|
||||
class RocnikNode(TreeNode):
|
||||
class Meta:
|
||||
|
@ -1180,6 +1315,9 @@ class RocnikNode(TreeNode):
|
|||
on_delete = models.PROTECT, # Pokud chci mazat ročník, musím si Node pořešit ručně
|
||||
verbose_name = "ročník")
|
||||
|
||||
def aktualizuj_nazev(self):
|
||||
self.nazev = "RocnikNode: "+str(self.rocnik)
|
||||
|
||||
class CisloNode(TreeNode):
|
||||
class Meta:
|
||||
db_table = 'seminar_nodes_cislo'
|
||||
|
@ -1189,12 +1327,36 @@ class CisloNode(TreeNode):
|
|||
on_delete = models.PROTECT, # Pokud chci mazat číslo, musím si Node pořešit ručně
|
||||
verbose_name = "číslo")
|
||||
|
||||
def aktualizuj_nazev(self):
|
||||
self.nazev = "CisloNode: "+str(self.cislo)
|
||||
|
||||
def getOdkazStr(self):
|
||||
return "Číslo " + str(self.cislo)
|
||||
|
||||
class MezicisloNode(TreeNode):
|
||||
class Meta:
|
||||
db_table = 'seminar_nodes_mezicislo'
|
||||
verbose_name = 'Mezičíslo (Node)'
|
||||
verbose_name_plural = 'Mezičísla (Node)'
|
||||
|
||||
def aktualizuj_nazev(self):
|
||||
if self.prev:
|
||||
if (self.prev.get_real_instance_class() != CisloNode and
|
||||
self.prev.get_real_instance_class() != MezicisloNode):
|
||||
raise ValueError("Předchůdce není číslo!")
|
||||
posledni = self.prev.cislo
|
||||
self.nazev = "MezicisloNode: Mezičíslo po čísle"+str(posledni)
|
||||
elif self.root:
|
||||
if self.root.get_real_instance_class() != RocnikNode:
|
||||
raise ValueError("Kořen stromu není ročník!")
|
||||
rocnik = self.root.rocnik
|
||||
self.nazev = "MezicisloNode: První mezičíslo ročníku "+str(rocnik)
|
||||
else:
|
||||
print("!!!!! Nějaké neidentifikované mezičíslo !!!!!")
|
||||
self.nazev = "MezicisloNode: Neidentifikovatelné mezičíslo!"
|
||||
def getOdkazStr(self):
|
||||
return "Obsah dostupný pouze na webu"
|
||||
|
||||
class TemaVCisleNode(TreeNode):
|
||||
""" Obsahuje příspěvky k tématu v daném čísle """
|
||||
class Meta:
|
||||
|
@ -1205,6 +1367,12 @@ class TemaVCisleNode(TreeNode):
|
|||
on_delete=models.PROTECT, # Pokud chci mazat téma, musím si Node pořešit ručně
|
||||
verbose_name = "téma v čísle")
|
||||
|
||||
def aktualizuj_nazev(self):
|
||||
self.nazev = "TemaVCisleNode: "+str(self.tema)
|
||||
|
||||
def getOdkazStr(self):
|
||||
return str(self.tema)
|
||||
|
||||
class KonferaNode(TreeNode):
|
||||
class Meta:
|
||||
db_table = 'seminar_nodes_konfera'
|
||||
|
@ -1216,6 +1384,9 @@ class KonferaNode(TreeNode):
|
|||
null=True,
|
||||
blank=False)
|
||||
|
||||
def aktualizuj_nazev(self):
|
||||
self.nazev = "KonferaNode: "+str(self.konfera)
|
||||
|
||||
class ClanekNode(TreeNode):
|
||||
class Meta:
|
||||
db_table = 'seminar_nodes_clanek'
|
||||
|
@ -1227,6 +1398,13 @@ class ClanekNode(TreeNode):
|
|||
null=True,
|
||||
blank=False)
|
||||
|
||||
def aktualizuj_nazev(self):
|
||||
self.nazev = "ClanekNode: "+str(self.clanek)
|
||||
|
||||
def getOdkazStr(self):
|
||||
return str(self.clanek)
|
||||
|
||||
|
||||
class UlohaZadaniNode(TreeNode):
|
||||
class Meta:
|
||||
db_table = 'seminar_nodes_uloha_zadani'
|
||||
|
@ -1238,6 +1416,13 @@ class UlohaZadaniNode(TreeNode):
|
|||
null=True,
|
||||
blank=False)
|
||||
|
||||
def aktualizuj_nazev(self):
|
||||
self.nazev = "UlohaZadaniNode: "+str(self.uloha)
|
||||
|
||||
def getOdkazStr(self):
|
||||
return str(self.uloha)
|
||||
|
||||
|
||||
class PohadkaNode(TreeNode):
|
||||
class Meta:
|
||||
db_table = 'seminar_nodes_pohadka'
|
||||
|
@ -1248,6 +1433,9 @@ class PohadkaNode(TreeNode):
|
|||
verbose_name = "pohádka",
|
||||
)
|
||||
|
||||
def aktualizuj_nazev(self):
|
||||
self.nazev = "PohadkaNode: "+str(self.pohadka)
|
||||
|
||||
class UlohaVzorakNode(TreeNode):
|
||||
class Meta:
|
||||
db_table = 'seminar_nodes_uloha_vzorak'
|
||||
|
@ -1259,6 +1447,13 @@ class UlohaVzorakNode(TreeNode):
|
|||
null=True,
|
||||
blank=False)
|
||||
|
||||
def aktualizuj_nazev(self):
|
||||
self.nazev = "UlohaVzorakNode: "+str(self.uloha)
|
||||
|
||||
def getOdkazStr(self):
|
||||
return str(self.uloha)
|
||||
|
||||
|
||||
class TextNode(TreeNode):
|
||||
class Meta:
|
||||
db_table = 'seminar_nodes_obsah'
|
||||
|
@ -1267,6 +1462,13 @@ class TextNode(TreeNode):
|
|||
text = models.ForeignKey(Text,
|
||||
on_delete=models.PROTECT,
|
||||
verbose_name = 'text')
|
||||
|
||||
def aktualizuj_nazev(self):
|
||||
self.nazev = "TextNode: "+str(self.text)
|
||||
|
||||
def getOdkazStr(self):
|
||||
return str(self.text)
|
||||
|
||||
|
||||
## FIXME: Logiku přesunout do views.
|
||||
#class VysledkyBase(SeminarModelBase):
|
||||
|
@ -1292,7 +1494,7 @@ class TextNode(TreeNode):
|
|||
#
|
||||
# def __str__(self):
|
||||
# return "%s: %sb (%s)".format(self.resitel.plne_jmeno(), self.body,
|
||||
# str(self.cislo))
|
||||
# str(self.poradi))
|
||||
# # NOTE: DB zatez pri vypisu (ale nepouzivany)
|
||||
|
||||
|
||||
|
@ -1342,7 +1544,7 @@ class TextNode(TreeNode):
|
|||
#
|
||||
# def __str__(self):
|
||||
# # NOTE: DB HOG (ale nepouzivany)
|
||||
# return "%s: %sb / %sb (do %s)" % (self.resitel.plne_jmeno(), self.body, self.body_celkem, str(self.cislo))
|
||||
# return "%s: %sb / %sb (do %s)" % (self.resitel.plne_jmeno(), self.body, self.body_celkem, str(self.poradi))
|
||||
##mozna potreba upravit
|
||||
|
||||
|
||||
|
@ -1353,12 +1555,16 @@ class Nastaveni(SingletonModel):
|
|||
db_table = 'seminar_nastaveni'
|
||||
verbose_name = 'Nastavení semináře'
|
||||
|
||||
aktualni_rocnik = models.ForeignKey(Rocnik, verbose_name='aktuální ročník',
|
||||
null=False, on_delete=models.PROTECT)
|
||||
# aktualni_rocnik = models.ForeignKey(Rocnik, verbose_name='aktuální ročník',
|
||||
# null=False, on_delete=models.PROTECT)
|
||||
|
||||
aktualni_cislo = models.ForeignKey(Cislo, verbose_name='poslední vydané číslo',
|
||||
null=False, on_delete=models.PROTECT)
|
||||
|
||||
@property
|
||||
def aktualni_rocnik(self):
|
||||
return self.aktualni_cislo.rocnik
|
||||
|
||||
def __str__(self):
|
||||
return 'Nastavení semináře'
|
||||
|
||||
|
@ -1399,3 +1605,35 @@ class Novinky(models.Model):
|
|||
return '[' + str(self.datum) + '] ' + self.text[0:50]
|
||||
else:
|
||||
return '[' + str(self.datum) + '] '
|
||||
|
||||
|
||||
|
||||
|
||||
# FIXME: Tohle nepatří do aplikace 'seminar'
|
||||
# Nefunkční alternativa vestavěného Usera, který má jméno a mail v přidružené Osobě
|
||||
# from django.contrib.auth.models import User as Django_User
|
||||
#
|
||||
# class Uzivatel(Django_User):
|
||||
# class Meta:
|
||||
# proxy = True
|
||||
#
|
||||
# @property
|
||||
# def first_name(self):
|
||||
# osoby = Osoba.objects.filter(user=self)
|
||||
# if len(osoby) == 0:
|
||||
# return None
|
||||
# return osoby.first().krestni_jmeno
|
||||
#
|
||||
# @property
|
||||
# def last_name(self):
|
||||
# osoby = Osoba.objects.filter(user=self)
|
||||
# if len(osoby) == 0:
|
||||
# return None
|
||||
# return osoby.first().prijmeni
|
||||
#
|
||||
# @property
|
||||
# def email(self):
|
||||
# osoby = Osoba.objects.filter(user=self)
|
||||
# if len(osoby) == 0:
|
||||
# return None
|
||||
# return osoby.first().email
|
||||
|
|
File diff suppressed because it is too large
Load diff
BIN
seminar/static/seminar/lisak.pdf
Normal file
BIN
seminar/static/seminar/lisak.pdf
Normal file
Binary file not shown.
32
seminar/static/seminar/prihlaska.js
Normal file
32
seminar/static/seminar/prihlaska.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
function addrCountryChanged(){
|
||||
var stat_select = document.getElementById('id_stat');
|
||||
var stat_text = document.getElementById('id_li_stat_text');
|
||||
var stat = stat_select[stat_select.selectedIndex].value;
|
||||
if (stat === "other"){
|
||||
stat_text.style.display="block";
|
||||
} else {
|
||||
stat_text.style.display="none";
|
||||
$('#id_stat_text').val("");
|
||||
}
|
||||
}
|
||||
function hideSchoolTextfields(){
|
||||
var skola_nazev = document.getElementById('id_li_skola_nazev');
|
||||
var skola_adresa = document.getElementById('id_li_skola_adresa');
|
||||
skola_nazev.style.display="none";
|
||||
skola_adresa.style.display="none";
|
||||
|
||||
}
|
||||
function schoolNotInList(){
|
||||
var skola_nazev = document.getElementById('id_li_skola_nazev');
|
||||
var skola_adresa = document.getElementById('id_li_skola_adresa');
|
||||
// FIXME nefunguje a nevim proc (TypeError: $(...).select2 is not a function)
|
||||
//var skola_select = $('#id_skola').select2();
|
||||
//skola_select.val(null).trigger('change');
|
||||
skola_nazev.style.display="block";
|
||||
skola_adresa.style.display="block";
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function(){
|
||||
addrCountryChanged();
|
||||
hideSchoolTextfields();
|
||||
});
|
|
@ -86,7 +86,8 @@
|
|||
% Tohle makro vysází samotnou obálku
|
||||
\def\obalka#1#2#3#4#5#6#7{
|
||||
% Horní a pravý okraj je zároveň okraj stránky, resetujeme odsazení
|
||||
\includegraphics[height=2.55cm]{lisak.eps}\hskip 1 em\vbox{%
|
||||
\includegraphics[height=2.55cm]{lisak.pdf}
|
||||
\vbox{%
|
||||
\adresaMaM}
|
||||
\vskip 7.3 cm % Od oka
|
||||
\hskip\toskip%
|
||||
|
|
78
seminar/templates/seminar/edit.html
Normal file
78
seminar/templates/seminar/edit.html
Normal file
|
@ -0,0 +1,78 @@
|
|||
{% extends "seminar/zadani/base.html" %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block script %}
|
||||
<!--script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script!-->
|
||||
{{form.media}}
|
||||
<script src="{% static 'seminar/prihlaska.js' %}"></script>
|
||||
{% endblock %}
|
||||
{% 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}}
|
||||
<ul class="form">
|
||||
<li>
|
||||
Přihlašovací údaje
|
||||
</li><li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.username %}
|
||||
</li><li>
|
||||
Osobní údaje
|
||||
</li><li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.jmeno %}
|
||||
</li><li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.prijmeni %}
|
||||
</li><li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.pohlavi_muz%}
|
||||
</li><li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.email %}
|
||||
</li><li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.telefon %}
|
||||
</li><li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.datum_narozeni %}
|
||||
</li><li>
|
||||
<hr>
|
||||
Bydliště
|
||||
</li><li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.ulice %}
|
||||
</li><li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.mesto %}
|
||||
</li><li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.psc %}
|
||||
</li><li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.stat %}
|
||||
</li>
|
||||
<li id="id_li_stat_text">
|
||||
{% include "seminar/prihlaska_field.html" with field=form.stat_text %}
|
||||
</li><li>
|
||||
<hr>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.skola %}
|
||||
</li><li>
|
||||
<button id="id_skola_text_button" type="button">Škola není v seznamu</button>
|
||||
</li>
|
||||
<li id="id_li_skola_nazev">
|
||||
Vyplň prosím celý název a adresu školy.<br>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.skola_nazev %}
|
||||
</li>
|
||||
<li id="id_li_skola_adresa">
|
||||
{% include "seminar/prihlaska_field.html" with field=form.skola_adresa %}
|
||||
</li><li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.rok_maturity %}
|
||||
</li><li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.zasilat %}
|
||||
</li><li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.spam %}
|
||||
</li>
|
||||
</ul>
|
||||
<input type="submit" value="Změnit">
|
||||
</form>
|
||||
<script>
|
||||
$("#id_stat").on("change",addrCountryChanged);
|
||||
$("#id_skola_text_button").on("click",schoolNotInList);
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
49
seminar/templates/seminar/gdpr.html
Normal file
49
seminar/templates/seminar/gdpr.html
Normal file
|
@ -0,0 +1,49 @@
|
|||
<p>
|
||||
TL;DR:
|
||||
K tomu, abychom mohli zpracovávat Tvá data (uložit si tvou adresu, zobrazit Tvé jméno ve výsledkové listině, opravit Tvá řešení) od Tebe potřebujeme souhlas.
|
||||
Pokud se zpracováváním souhlasíš dle níže uvedených podmínek, zaškrtni políčko níže.
|
||||
</p>
|
||||
<div class="gdpr">
|
||||
<p class="gdpr">
|
||||
Získáváme od Tebe údaje vyplněné v přihlášce do semináře (jméno, příjmení, poštovní a e-mailovou adresu, školu, kterou navštěvuješ a rok maturity), případně v přihlášce na soustředění (navíc datum narození, telefonní číslo). Také uchováváme všechna řešení, která nám pošleš, a jejich hodnocení.
|
||||
</p>
|
||||
<p class="gdpr">
|
||||
Slibujeme Ti, že Tvá osobní data nezneužijeme k ničemu, co by nesouviselo s M&M nebo s dalšími aktivitami Matfyzu, a nikdy je nepředáme nikomu cizímu. Údaje využíváme k zajištění chodu semináře a také je sdílíme s ostatními propagačními akcemi Matfyzu, abychom mohli vyhodnocovat úspěšnost akcí. Pokud budeš mít zájem, budeme Ti také posílat zajímavé zprávy a novinky týkajíci se Matfyzu.
|
||||
</p>
|
||||
<p class="gdpr">
|
||||
Veřejně vystavujeme pouze výsledkové listiny, které také uchováváme pro archivní účely. Pokud ale z nějakého důvodu nebudeš chtít mít své jméno či školu uvedené ve výsledkové listině, není problém to zařídit, napiš nám. Z tištěných materiálů samozřejmě údaje už odstranit nemůžeme.
|
||||
</p>
|
||||
<p class="gdpr">
|
||||
Na soustředěních a dalších akcích semináře navíc pořizujeme fotografie a videozáznamy a používáme je ke zpravodajským a propagačním účelům. Pro propagační účely si od Tebe vyžádáme samostatný souhlas na začátku akce.
|
||||
</p>
|
||||
<p class="gdpr">
|
||||
<i>Souhlas se zpracováním osobních údajů pro potřeby chodu semináře</i>
|
||||
</p>
|
||||
<p class="gdpr">
|
||||
Tímto uděluji souhlas Univerzitě Karlově, se sídlem Ovocný trh 560/5, 116 36 Praha 1, IČO 00216208 (dále jen UK), která je správcem osobních údajů všech fakult a součástí UK, ke zpracování osobních údajů pro potřeby Korespondenčního semináře M&M a Matematicko-fyzikální fakulty UK (dále jen M&M a MFF UK).
|
||||
</p>
|
||||
<p class="gdpr">
|
||||
Tento souhlas uděluji pro všechny výše uvedené osobní údaje, a to po dobu účasti v semináři a 10 let poté, a dále souhlasím s uchováváním potřebných dat pro archivní účely i po této lhůtě (vystavené výsledkové listiny aj.).
|
||||
</p>
|
||||
<p class="gdpr">
|
||||
MFF UK tyto údaje zpracovává za účelem evidence řešitelů a účastníků M&M, k zajištění celoročního fungování semináře, analýze účinnosti jednotlivých propagačních akcí MFF UK a zpravodajským účelům. Osobám, které o to projeví zájem v nastavení svého účtu, bude MFF UK také zasílat propagační materiály.
|
||||
</p>
|
||||
<p class="gdpr">
|
||||
Údaje nebudou předány třetí osobě ani využívány k jiným účelům, než ke kterým byly poskytnuty.
|
||||
</p>
|
||||
<p class="gdpr">
|
||||
Tento souhlas uděluji ze své vlastní a svobodné vůle a beru na vědomí, že jej mohu kdykoliv odvolat zasláním e-mailu na adresu mam@matfyz.cz. Stejně tak může být požadováno vymazání i z archivních údajů M&M, pokud to bude technicky možné. Beru na vědomí, že údaje z tištěných publikací není možné zpětně odstranit.
|
||||
</p>
|
||||
<p class="gdpr">
|
||||
Dále máte právo:
|
||||
<ul>
|
||||
<li>požádat o informaci, jaké osobní údaje jsou o vás zpracovávány,
|
||||
<li>požadovat opravu osobních údajů, pokud jsou neplatné nebo zastaralé,
|
||||
<li>požadovat, aby nebyly vaše osobní údaje zpracovávány do doby, než bude vyřešena oprávněnost výše uvedených požadavků,
|
||||
<li>požadovat, aby byly vaše osobní údaje předány jinému správci,
|
||||
<li>podat stížnost u dozorového úřadu.
|
||||
</p>
|
||||
<p class="gdpr">
|
||||
V případě jakéhokoliv dotazu nebo uplatnění svých práv můžete kontaktovat pověřence pro ochranu osobních údajů na e-mailové adrese gdpr@cuni.cz.
|
||||
</p>
|
||||
</div>
|
26
seminar/templates/seminar/login.html
Normal file
26
seminar/templates/seminar/login.html
Normal file
|
@ -0,0 +1,26 @@
|
|||
{% extends "seminar/zadani/base.html" %}
|
||||
{% load staticfiles %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% block nadpis1a %}{% block nadpis1b %}
|
||||
Přihlášení
|
||||
{% endblock %}{% endblock %}
|
||||
</h1>
|
||||
<form action="{% url 'login' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<ul class="form">
|
||||
{{ form.as_ul }}
|
||||
</ul>
|
||||
{# Django si posílá jméno další stránky jako obsah formuláře a výchozí hodnota (mi přišlo, že) nejde změnit... #}
|
||||
<input type="hidden" name='next' value="{{ next }}">
|
||||
<input type="submit" value="Přihlásit">
|
||||
</form>
|
||||
|
||||
<a href="{% url 'reset_password' %}">Zapomněl jsem heslo</a><br>
|
||||
<a href="{% url 'seminar_prihlaska' %}">Zaregistrovat</a><br>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
18
seminar/templates/seminar/logout.html
Normal file
18
seminar/templates/seminar/logout.html
Normal file
|
@ -0,0 +1,18 @@
|
|||
{% extends "seminar/zadani/base.html" %}
|
||||
{% load staticfiles %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% block nadpis1a %}{% block nadpis1b %}
|
||||
Odhlášení
|
||||
{% endblock %}{% endblock %}
|
||||
</h1>
|
||||
|
||||
Byl jsi úspěšně odhlášen
|
||||
{# Tohle by se asi mělo udělat přes kontext (title), ale kašlu na to, stejně je to jen jednojazyčná stránka #}
|
||||
|
||||
{# TODO: odkaz na znovupřihlášení? #}
|
||||
|
||||
{% endblock %}
|
||||
|
30
seminar/templates/seminar/org/obalkovani.html
Normal file
30
seminar/templates/seminar/org/obalkovani.html
Normal file
|
@ -0,0 +1,30 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% block nadpis1a %}{% block nadpis1b %}
|
||||
Obálkování {{ cislo }}
|
||||
{% endblock %}{% endblock %}
|
||||
</h1>
|
||||
<ul>
|
||||
{% for reseni in object_list %}
|
||||
{% ifchanged reseni.resitele %}
|
||||
{% if not forloop.first %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
<h4>{% for resitel in reseni.resitele.all %}{{resitel.osoba}},{% endfor %}</h4>
|
||||
<ul>
|
||||
{% endifchanged %}
|
||||
|
||||
<li>Celkem {{reseni.hodnoceni__body__sum}} bodů z {{reseni.hodnoceni__count}} hodnocení
|
||||
<ul>
|
||||
{% for h in reseni.hodnoceni_set.all %}
|
||||
<li> {{ h.problem }}: {{ h.body }}b </li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
|
||||
{% endblock content %}
|
21
seminar/templates/seminar/org/vloz_reseni.html
Normal file
21
seminar/templates/seminar/org/vloz_reseni.html
Normal file
|
@ -0,0 +1,21 @@
|
|||
{% extends "seminar/zadani/base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% block script %}
|
||||
<!--script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script!-->
|
||||
{{form.media}}
|
||||
<script src="{% static 'seminar/prihlaska.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% block nadpis1a %}{% block nadpis1b %}
|
||||
Vložit řešení
|
||||
{% endblock %}{% endblock %}
|
||||
</h1>
|
||||
<form action="{% url 'seminar_vloz_reseni' %}" method="post">
|
||||
{% csrf_token %}
|
||||
{{form.as_p}}
|
||||
<input type="submit" value="Vložit">
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
|
@ -1,5 +1,112 @@
|
|||
<form action="/prihlaska/" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form }}
|
||||
<input type="submit" value="Submit">
|
||||
{% extends "seminar/zadani/base.html" %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block script %}
|
||||
<!--script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script!-->
|
||||
{{form.media}}
|
||||
<script src="{% static 'seminar/prihlaska.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% block nadpis1a %}{% block nadpis1b %}
|
||||
Přihláška do semináře
|
||||
{% endblock %}{% endblock %}
|
||||
</h1>
|
||||
|
||||
|
||||
<form action="{% url 'seminar_prihlaska' %}" method="post">
|
||||
{% csrf_token %}
|
||||
{{form.non_field_errors}}
|
||||
<ul class="form">
|
||||
<li>
|
||||
Přihlašovací údaje
|
||||
</li>
|
||||
<li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.username %}
|
||||
</li>
|
||||
<li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.password %}
|
||||
</li>
|
||||
<li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.password_check %}
|
||||
</li>
|
||||
<li>
|
||||
Osobní údaje
|
||||
</li>
|
||||
<li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.jmeno %}
|
||||
</li>
|
||||
<li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.prijmeni %}
|
||||
</li>
|
||||
<li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.pohlavi_muz%}
|
||||
</li>
|
||||
<li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.email %}
|
||||
</li>
|
||||
<li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.telefon %}
|
||||
</li>
|
||||
<li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.datum_narozeni %}
|
||||
</li>
|
||||
<li>
|
||||
<hr>
|
||||
Bydliště
|
||||
</li>
|
||||
<li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.ulice %}
|
||||
</li>
|
||||
<li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.mesto %}
|
||||
</li>
|
||||
<li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.psc %}
|
||||
</li>
|
||||
<li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.stat %}
|
||||
</li>
|
||||
<li id="id_li_stat_text">
|
||||
{% include "seminar/prihlaska_field.html" with field=form.stat_text %}
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<hr>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.skola %}
|
||||
</li>
|
||||
<li>
|
||||
<button id="id_skola_text_button" type="button">Škola není v seznamu</button>
|
||||
</li>
|
||||
<li id="id_li_skola_nazev">
|
||||
Vyplň prosím celý název a adresu školy.<br>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.skola_nazev %}
|
||||
</li>
|
||||
<li id="id_li_skola_adresa">
|
||||
{% include "seminar/prihlaska_field.html" with field=form.skola_adresa %}
|
||||
</li>
|
||||
<li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.rok_maturity %}
|
||||
</li>
|
||||
<li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.zasilat %}
|
||||
</li>
|
||||
<li>
|
||||
{% include "seminar/gdpr.html" %}
|
||||
{% include "seminar/prihlaska_field.html" with field=form.gdpr %}
|
||||
</li>
|
||||
<li>
|
||||
{% include "seminar/prihlaska_field.html" with field=form.spam %}
|
||||
</li>
|
||||
</ul>
|
||||
<input type="submit" value="Odeslat">
|
||||
</form>
|
||||
<script>
|
||||
$("#id_stat").on("change",addrCountryChanged);
|
||||
$("#id_skola_text_button").on("click",schoolNotInList);
|
||||
</script>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
4
seminar/templates/seminar/prihlaska_field.html
Normal file
4
seminar/templates/seminar/prihlaska_field.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
<label class="field-label{% if field.field.required %} field-required{% endif %}" for="{{ field.id_for_label }}">{{ field.label }}:</label>
|
||||
{{field}}
|
||||
{% if field.help_text %}<span class="field-helptext">{{ field.help_text|safe }}</span>{% endif %}
|
||||
{% if field.errors %}<span class="field-error">{{ field.errors }}</span>{% endif %}
|
17
seminar/templates/seminar/resitel.html
Normal file
17
seminar/templates/seminar/resitel.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
{% extends "seminar/zadani/base.html" %}
|
||||
{% load staticfiles %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% block nadpis1a %}{% block nadpis1b %}
|
||||
Stránka řešitele - {{ object.osoba.jmeno }} {{ object.osoba.prijmeni }}
|
||||
{% endblock %}{% endblock %}
|
||||
</h1>
|
||||
|
||||
<a href="{% url 'logout' %}">Odhlásit se</a><br>
|
||||
<a href="{% url 'seminar_resitel_edit' %}">Upravit údaje</a><br>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
14
seminar/templates/seminar/tematka/rozcestnik.html
Normal file
14
seminar/templates/seminar/tematka/rozcestnik.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
{% for tematko in tematka %}
|
||||
<h1>{{tematko.nazev}}</h1>
|
||||
<p>{{tematko.abstrakt}}</p>
|
||||
<ul>
|
||||
{% for cislo in tematko.cisla %}
|
||||
<li><a href="/{{rocnik}}/t{{tematko.kod}}/#{{cislo.0.1}}">{{cislo.0.0}}</a></li>
|
||||
<ul>
|
||||
{% for odkaz in cislo.1 %}
|
||||
<li><a href="/{{rocnik}}/t{{tematko.kod}}/#{{odkaz.1}}">{{odkaz.0}}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endfor %}
|
1
seminar/templates/seminar/tematka/toaletak.html
Normal file
1
seminar/templates/seminar/tematka/toaletak.html
Normal file
|
@ -0,0 +1 @@
|
|||
Stránká témátka
|
|
@ -1,11 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import datetime
|
||||
from pytz import timezone
|
||||
import random
|
||||
import lorem
|
||||
import django.contrib.auth
|
||||
from django.db import transaction
|
||||
import unidecode
|
||||
import logging
|
||||
|
||||
from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni, Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Osoba, Organizator, Prijemce, Tema, Uloha, Konfera, KonferaNode, TextNode, UlohaVzorakNode, RocnikNode, CisloNode, TemaVCisleNode, Text, Hodnoceni, UlohaZadaniNode, Novinky
|
||||
|
||||
|
@ -15,7 +17,11 @@ from django.contrib.sites.models import Site
|
|||
User = django.contrib.auth.get_user_model()
|
||||
zlinska = None # tohle bude speciální škola, které později dodáme kontaktní osobu
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def gen_osoby(rnd, size):
|
||||
logger.info('Generuji osoby (size={})...'.format(size))
|
||||
|
||||
jmena_m = ['Aleš', 'Tomáš', 'Martin', 'Jakub', 'Petr', 'Lukáš', 'Cyril', 'Pavel Karel']
|
||||
jmena_f = ['Eva', 'Karolína', 'Zuzana', 'Sylvie', 'Iva', 'Jana', 'Marie',
|
||||
'Marta Iva', 'Shu Shan']
|
||||
|
@ -82,6 +88,8 @@ def gen_osoby(rnd, size):
|
|||
|
||||
|
||||
def gen_skoly(): #TODO někdy to přepsat, aby jich bylo více
|
||||
logger.info('Generuji školy...')
|
||||
|
||||
skoly = []
|
||||
prvnizs = Skola.objects.create(mesto='Praha', stat='CZ', psc='101 00',
|
||||
ulice='Krátká 5', nazev='První ZŠ', je_zs=True, je_ss=False)
|
||||
|
@ -105,30 +113,43 @@ def gen_skoly(): #TODO někdy to přepsat, aby jich bylo více
|
|||
return skoly
|
||||
|
||||
def gen_resitele(rnd, osoby, skoly):
|
||||
logger.info('Generuji řešitele...')
|
||||
|
||||
resitele = []
|
||||
for os in osoby:
|
||||
rand = rnd.randint(0, 8)
|
||||
if not (rand % 8 == 0):
|
||||
resitele.append(Resitel.objects.create(osoba=os, skola=rnd.choice(skoly),
|
||||
rok_maturity=rnd.randint(2019, 2029),
|
||||
zasilat=rnd.choice(Resitel.ZASILAT_CHOICES)))
|
||||
zasilat=rnd.choice(Resitel.ZASILAT_CHOICES)[0]))
|
||||
return resitele
|
||||
|
||||
def gen_prijemci(rnd, osoby, kolik=10):
|
||||
logger.info('Generuji příjemce (kolik={})...'.format(kolik))
|
||||
prijemci = []
|
||||
for i in rnd.sample(osoby, kolik):
|
||||
prijemci.append(Prijemce.objects.create(osoba=i))
|
||||
return prijemci
|
||||
|
||||
def gen_organizatori(rnd, osoby, last_rocnik, users):
|
||||
logger.info('Generuji organizátory...')
|
||||
organizatori = []
|
||||
for os in osoby:
|
||||
rand = rnd.randint(0, 8)
|
||||
if (rand % 8 == 0):
|
||||
pusobnost = rnd.randint(1, last_rocnik)
|
||||
od = datetime.date(1993 + pusobnost, rnd.randint(1, 12), rnd.randint(1, 28))
|
||||
do = datetime.date(od.year + rnd.randint(1, 6), rnd.randint(1, 12),
|
||||
rnd.randint(1, 28))
|
||||
od = datetime.datetime(
|
||||
year=1993 + pusobnost,
|
||||
month=rnd.randint(1, 12),
|
||||
day=rnd.randint(1, 28),
|
||||
tzinfo=timezone('CET'),
|
||||
)
|
||||
do = datetime.datetime(
|
||||
year=od.year + rnd.randint(1, 6),
|
||||
month=rnd.randint(1, 12),
|
||||
day=rnd.randint(1, 28),
|
||||
tzinfo=timezone('CET'),
|
||||
)
|
||||
#aktualni organizatori jeste nemaji vyplnene organizuje_do
|
||||
if do.year > datetime.datetime.now().year:
|
||||
do = None
|
||||
|
@ -137,6 +158,8 @@ def gen_organizatori(rnd, osoby, last_rocnik, users):
|
|||
return organizatori
|
||||
|
||||
def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size):
|
||||
logger.info('Generuji úlohy do čísla (size={})...'.format(size))
|
||||
|
||||
# ulohy resene v cisle
|
||||
jaka = ["Šachová", "Černá", "Větrná", "Dlouhá", "Křehká", "Rychlá",
|
||||
"Zákeřná", "Fyzikální"]
|
||||
|
@ -159,7 +182,14 @@ def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size)
|
|||
for rocnik in rocniky:
|
||||
k+=1
|
||||
cisla = rocnik_cisla[k-1]
|
||||
for ci in range(3, len(cisla)+1):
|
||||
for ci in range(3, len(cisla)+1): # pro všechna čísla
|
||||
resitele_size = round(7/8 * 30 * size) # očekáváný celkový počet řešitelů
|
||||
poc_res = rnd.randint(round(resitele_size/8), round(3*resitele_size/4))
|
||||
# dané číslo řeší něco mezi osminou a tříčtvrtinou všech řešitelů
|
||||
# (náhodná hausnumera, možno změnit)
|
||||
# účelem je, aby se řešení generovala z menší množiny řešitelů a tedy
|
||||
# bylo více řešení od jednoho řešitele daného čísla
|
||||
resitele_cisla = rnd.sample(resitele, poc_res)
|
||||
for pi in range(1, ((size + 1) // 2) + 1):
|
||||
|
||||
poc_op = rnd.randint(1, 4) # počet opravovatelů
|
||||
|
@ -200,6 +230,7 @@ def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size)
|
|||
p.ulohazadaninode = uloha_zadani
|
||||
otec_syn(cisla[ci-2-1].cislonode, uloha_zadani)
|
||||
|
||||
# generování vzorového textu
|
||||
text_vzoraku = Text.objects.create(
|
||||
na_web = rnd.choice(reseni),
|
||||
do_cisla = rnd.choice(reseni)
|
||||
|
@ -212,17 +243,18 @@ def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size)
|
|||
p.opravovatele.set(rnd.sample(organizatori,poc_op))
|
||||
p.save()
|
||||
|
||||
# reseni ulohy
|
||||
# generování řešení
|
||||
poc_reseni = rnd.randint(size // 2, size * 2)
|
||||
#poc_resitel = rnd.randint(1, 3) <- k čemu je himbajs tahle proměnná?
|
||||
# vybereme nahodny vzorek resitelu o delce poctu reseni
|
||||
# (nebo skoro vsechny resitele, pokud jich je mene nez pocet reseni)
|
||||
# generujeme náhodný počet řešení
|
||||
for ri in range(poc_reseni):
|
||||
res_vyber = rnd.sample(resitele, rnd.randint(1, 5))
|
||||
if rnd.randint(1, 10) == 6:
|
||||
# cca desetina řešení od více řešitelů
|
||||
res_vyber = rnd.sample(resitele_cisla, rnd.randint(2, 5))
|
||||
else:
|
||||
res_vyber = rnd.sample(resitele_cisla, 1)
|
||||
res = Reseni.objects.create(forma=rnd.choice(Reseni.FORMA_CHOICES)[0])
|
||||
# problem a resitele přiřadíme později, ManyToManyField
|
||||
# se nedá vyplnit v create()
|
||||
res = Reseni.objects.create(forma=rnd.choice(Reseni.FORMA_CHOICES))
|
||||
#res.save() <- asi smazat
|
||||
res.resitele.set(res_vyber)
|
||||
res.save()
|
||||
hod = Hodnoceni.objects.create(
|
||||
|
@ -235,29 +267,33 @@ def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size)
|
|||
return
|
||||
|
||||
def gen_soustredeni(rnd, resitele, organizatori):
|
||||
soustredeni = []
|
||||
for _ in range(1, 10): #FIXME Tu range si změňte jak chcete, nevím, co přesně znamená size (asi Anet?)
|
||||
datum_zacatku=datetime.date(rnd.randint(2000, 2020), rnd.randint(1, 12), rnd.randint(1, 28))
|
||||
working_sous = Soustredeni.objects.create(
|
||||
rocnik=Rocnik.objects.order_by('?').first(),
|
||||
verejne_db=rnd.choice([True, False]),
|
||||
misto=rnd.choice(['Kremrolovice', 'Indiánov', 'U zmzliny', 'Vafláreň', 'Větrník', 'Horní Rakvička', 'Dolní cheesecake']),
|
||||
typ=rnd.choice(['jarni', 'podzimni', 'vikend']),
|
||||
datum_zacatku=datum_zacatku,
|
||||
datum_konce=datum_zacatku + datetime.timedelta(days=7))
|
||||
ucastnici = rnd.sample(resitele, min(len(resitele), 20))
|
||||
working_sous.ucastnici.set(ucastnici)
|
||||
#for res in rnd.sample(resitele, min(len(resitele), 20)):
|
||||
# Soustredeni_Ucastnici.objects.create(resitel=res, soutredeni=working_sous)
|
||||
orgove_vyber = rnd.sample(organizatori, min(len(organizatori), 20))
|
||||
working_sous.organizatori.set(orgove_vyber)
|
||||
#for org in rnd.sample(organizatori, min(len(organizatori), 20)):
|
||||
# Soustredeni_Organizatori.objects.create(organizator=org, soutredeni=working_sous)
|
||||
working_sous.save()
|
||||
soustredeni.append(working_sous)
|
||||
return soustredeni
|
||||
logger.info('Generuji soustředění...')
|
||||
|
||||
soustredeni = []
|
||||
for _ in range(1, 10): #FIXME Tu range si změňte jak chcete, nevím, co přesně znamená size (asi Anet?)
|
||||
datum_zacatku=datetime.date(rnd.randint(2000, 2020), rnd.randint(1, 12), rnd.randint(1, 28))
|
||||
working_sous = Soustredeni.objects.create(
|
||||
rocnik=Rocnik.objects.order_by('?').first(),
|
||||
verejne_db=rnd.choice([True, False]),
|
||||
misto=rnd.choice(['Kremrolovice', 'Indiánov', 'U zmzliny', 'Vafláreň', 'Větrník', 'Horní Rakvička', 'Dolní cheesecake']),
|
||||
typ=rnd.choice(['jarni', 'podzimni', 'vikend']),
|
||||
datum_zacatku=datum_zacatku,
|
||||
datum_konce=datum_zacatku + datetime.timedelta(days=7))
|
||||
ucastnici = rnd.sample(resitele, min(len(resitele), 20))
|
||||
working_sous.ucastnici.set(ucastnici)
|
||||
#for res in rnd.sample(resitele, min(len(resitele), 20)):
|
||||
# Soustredeni_Ucastnici.objects.create(resitel=res, soutredeni=working_sous)
|
||||
orgove_vyber = rnd.sample(organizatori, min(len(organizatori), 20))
|
||||
working_sous.organizatori.set(orgove_vyber)
|
||||
#for org in rnd.sample(organizatori, min(len(organizatori), 20)):
|
||||
# Soustredeni_Organizatori.objects.create(organizator=org, soutredeni=working_sous)
|
||||
working_sous.save()
|
||||
soustredeni.append(working_sous)
|
||||
return soustredeni
|
||||
|
||||
def gen_rocniky(last_rocnik, size):
|
||||
logger.info('Generuji ročníky (size={})...'.format(size))
|
||||
|
||||
rocniky = []
|
||||
node = None
|
||||
for ri in range(min(last_rocnik - size, 1), last_rocnik + 1):
|
||||
|
@ -269,6 +305,8 @@ def gen_rocniky(last_rocnik, size):
|
|||
return rocniky
|
||||
|
||||
def gen_konfery(size, rnd, organizatori, resitele, soustredeni):
|
||||
logger.info('Generuji konfery (size={})...'.format(size))
|
||||
|
||||
konfery = []
|
||||
for _ in range(1, size): #FIXME Tu range si změňte jak chcete, nevím, co přesně znamená size (asi Anet?)
|
||||
# Anet: size je parametr udávající velikost testovacích dat a dá se pomocí ní škálovat,
|
||||
|
@ -293,6 +331,8 @@ def gen_konfery(size, rnd, organizatori, resitele, soustredeni):
|
|||
return konfery
|
||||
|
||||
def gen_cisla(rnd, rocniky):
|
||||
logger.info('Generuji čísla...')
|
||||
|
||||
rocnik_cisla = []
|
||||
for rocnik in rocniky:
|
||||
otec = True
|
||||
|
@ -318,7 +358,7 @@ def gen_cisla(rnd, rocniky):
|
|||
|
||||
cislo = Cislo.objects.create(
|
||||
rocnik = rocnik,
|
||||
cislo = str(ci),
|
||||
poradi = str(ci),
|
||||
datum_vydani=vydano,
|
||||
datum_deadline=deadline,
|
||||
verejne_db=True
|
||||
|
@ -336,6 +376,8 @@ def gen_cisla(rnd, rocniky):
|
|||
return rocnik_cisla
|
||||
|
||||
def gen_temata(rnd, rocniky, rocnik_cisla, organizatori):
|
||||
logger.info('Generuji témata...')
|
||||
|
||||
jake = ["Hravé", "Fyzikální", "Nejlepší", "Totálně masakrální",
|
||||
"Šokující", "Magnetické", "Modré", "Překvapivé",
|
||||
"Plasmatické", "Novoroční"]
|
||||
|
@ -362,8 +404,9 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori):
|
|||
garant=rnd.choice(organizatori),
|
||||
kod=str(n),
|
||||
# atributy třídy Téma
|
||||
tema_typ=rnd.choice(Tema.TEMA_CHOICES),
|
||||
rocnik=rocnik
|
||||
tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0],
|
||||
rocnik=rocnik,
|
||||
abstrakt = "Abstrakt tematka {}".format(n)
|
||||
)
|
||||
konec_tematu = min(rnd.randint(ci, 7), len(cisla))
|
||||
for i in range(ci, konec_tematu+1):
|
||||
|
@ -379,6 +422,8 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori):
|
|||
|
||||
|
||||
def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori):
|
||||
logger.info('Generuji úlohy k tématům...')
|
||||
|
||||
# ulohy resene v cisle
|
||||
jaka = ["Šachová", "Černá", "Větrná", "Dlouhá", "Křehká", "Rychlá",
|
||||
"Zákeřná", "Fyzikální"]
|
||||
|
@ -475,6 +520,8 @@ def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori)
|
|||
return
|
||||
|
||||
def gen_novinky(rnd, organizatori):
|
||||
logger.info('Generuji novinky...')
|
||||
|
||||
|
||||
jake = ["zábavné", "veselé", "dobrodružné", "skvělé"]
|
||||
co = ["soustředění", "Fyziklání", "víkendové setkání"]
|
||||
|
@ -496,6 +543,8 @@ def otec_syn(otec, syn):
|
|||
|
||||
@transaction.atomic
|
||||
def create_test_data(size = 6, rnd = None):
|
||||
logger.info('Vyrábím testovací data (size={})...'.format(size))
|
||||
|
||||
assert size >= 1
|
||||
# pevna pseudo-nahodnost
|
||||
rnd = rnd or random.Random(x=42)
|
||||
|
@ -576,5 +625,5 @@ def create_test_data(size = 6, rnd = None):
|
|||
|
||||
|
||||
# obecné nastavení semináře, musí být už přidané ročníky a čísla, jinak se nastaví divně
|
||||
nastaveni = Nastaveni.objects.create(aktualni_rocnik = Rocnik.objects.last(),
|
||||
nastaveni = Nastaveni.objects.create(
|
||||
aktualni_cislo = Cislo.objects.all()[1])
|
||||
|
|
|
@ -3,10 +3,14 @@ from django.contrib.auth.decorators import user_passes_test
|
|||
from . import views, export
|
||||
from .utils import staff_member_required
|
||||
from django.views.generic.base import RedirectView
|
||||
from django.contrib.auth import views as auth_views
|
||||
|
||||
staff_member_required = user_passes_test(lambda u: u.is_staff)
|
||||
|
||||
urlpatterns = [
|
||||
path('aktualni/temata/', views.TemataRozcestnikView),
|
||||
path('<int:rocnik>/t<int:tematko>/', views.TematkoView),
|
||||
|
||||
# REDIRECTy
|
||||
path('jak-resit/', RedirectView.as_view(url='/co-je-MaM/jak-resit/')),
|
||||
|
||||
|
@ -85,19 +89,36 @@ urlpatterns = [
|
|||
path('stav',
|
||||
staff_member_required(views.StavDatabazeView), name='stav_databaze'),
|
||||
path('cislo/<int:rocnik>.<int:cislo>/obalkovani',
|
||||
staff_member_required(views.obalkovaniView), name='seminar_cislo_resitel_obalkovani'),
|
||||
staff_member_required(views.ObalkovaniView.as_view()), name='seminar_cislo_resitel_obalkovani'),
|
||||
path('cislo/<int:rocnik>.<int:cislo>/tex-download.json',
|
||||
staff_member_required(views.texDownloadView), name='seminar_tex_download'),
|
||||
path('soustredeni/<int:soustredeni>/obalky.pdf',
|
||||
staff_member_required(views.soustredeniObalkyView), name='seminar_soustredeni_obalky'),
|
||||
|
||||
path('tex-upload/login/', views.LoginView, name='seminar_login'),
|
||||
path('tex-upload/login/', views.TeXUploadLoginView, name='seminar_login'),
|
||||
path(
|
||||
'tex-upload/',
|
||||
staff_member_required(views.texUploadView),
|
||||
name='seminar_tex_upload'
|
||||
),
|
||||
path('prihlaska/',views.get_name),
|
||||
path('org/vloz_body/<int:tema>/',
|
||||
staff_member_required(views.VlozBodyView.as_view()),name='seminar_org_vlozbody'),
|
||||
path('auth/prihlaska/',views.prihlaskaView, name='seminar_prihlaska'),
|
||||
path('auth/login/', views.LoginView.as_view(), name='login'),
|
||||
path('auth/logout/', views.LogoutView.as_view(), name='logout'),
|
||||
path('auth/resitel/', views.ResitelView.as_view(), name='seminar_resitel'),
|
||||
path('autocomplete/skola/',views.SkolaAutocomplete.as_view(), name='autocomplete_skola'),
|
||||
path('autocomplete/resitel/',views.ResitelAutocomplete.as_view(), name='autocomplete_resitel'),
|
||||
path('auth/reset_password/', views.PasswordResetView.as_view(), name='reset_password'),
|
||||
path('auth/change_password/', views.PasswordChangeView.as_view(), name='change_password'),
|
||||
path('auth/reset_password_done/', views.PasswordResetDoneView.as_view(), name='reset_password_done'),
|
||||
path('auth/reset_password_confirm/<uidb64>/<token>/', views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
|
||||
path('auth/reset_password_complete/', views.PasswordResetCompleteView.as_view(), name='reset_password_complete'),
|
||||
path('auth/resitel_edit', views.resitelEditView, name='seminar_resitel_edit'),
|
||||
|
||||
|
||||
path('temp/add_solution', views.AddSolutionView.as_view(),name='seminar_vloz_reseni'),
|
||||
|
||||
path('', views.TitulniStranaView.as_view(), name='titulni_strana'),
|
||||
|
||||
# Ceka na autocomplete v3
|
||||
|
|
|
@ -2,9 +2,18 @@
|
|||
|
||||
import datetime
|
||||
from django.contrib.auth.decorators import user_passes_test
|
||||
from html.parser import HTMLParser
|
||||
|
||||
staff_member_required = user_passes_test(lambda u: u.is_staff)
|
||||
|
||||
class FirstTagParser(HTMLParser):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.firstTag = None
|
||||
super().__init__(*args, **kwargs)
|
||||
def handle_data(self, data):
|
||||
if self.firstTag == None:
|
||||
self.firstTag = data
|
||||
|
||||
def histogram(seznam):
|
||||
d = {}
|
||||
for i in seznam:
|
||||
|
|
652
seminar/views.py
652
seminar/views.py
|
@ -2,22 +2,31 @@
|
|||
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, JsonResponse
|
||||
from django.urls import reverse
|
||||
from django.urls import reverse,reverse_lazy
|
||||
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
|
||||
from django.views import generic
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.http import Http404,HttpResponseBadRequest,HttpResponseRedirect
|
||||
from django.db.models import Q
|
||||
from django.db.models import Q, Sum, Count
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
from django.contrib.auth import authenticate, login
|
||||
from django.views.generic.edit import FormView
|
||||
from django.contrib.auth import authenticate, login, get_user_model, logout
|
||||
from django.contrib.auth import views as auth_views
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.db import transaction
|
||||
from dal import autocomplete
|
||||
|
||||
from .models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici, Pohadka, Tema, Clanek
|
||||
import seminar.models as s
|
||||
from .models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici, Pohadka, Tema, Clanek, Osoba, Skola # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci
|
||||
#from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva
|
||||
from . import utils
|
||||
from .unicodecsv import UnicodeWriter
|
||||
from .forms import NameForm
|
||||
from .forms import PrihlaskaForm, LoginForm, ProfileEditForm
|
||||
import seminar.forms as f
|
||||
|
||||
from datetime import timedelta, date, datetime
|
||||
from django.utils import timezone
|
||||
from itertools import groupby
|
||||
import tempfile
|
||||
import subprocess
|
||||
|
@ -30,6 +39,7 @@ import json
|
|||
import traceback
|
||||
import sys
|
||||
import csv
|
||||
import logging
|
||||
|
||||
|
||||
def verejna_temata(rocnik):
|
||||
|
@ -37,6 +47,45 @@ def verejna_temata(rocnik):
|
|||
"""
|
||||
return Problem.objects.filter(typ=Problem.TYP_TEMA, cislo_zadani__rocnik=rocnik, cislo_zadani__verejne_db=True).order_by('kod')
|
||||
|
||||
def temata_v_rocniku(rocnik):
|
||||
return Problem.objects.filter(typ=Problem.TYP_TEMA, rocnik=rocnik)
|
||||
|
||||
def get_problemy_k_tematu(tema):
|
||||
return Problemy.objects.filter(nadproblem = tema)
|
||||
|
||||
|
||||
class VlozBodyView(generic.ListView):
|
||||
template_name = 'seminar/org/vloz_body.html'
|
||||
|
||||
def get_queryset(self):
|
||||
self.tema = get_object_or_404(Problem,id=self.kwargs['tema'])
|
||||
print(self.tema)
|
||||
self.problemy = Problem.objects.filter(nadproblem = self.tema)
|
||||
print(self.problemy)
|
||||
self.reseni = Reseni.objects.filter(problem__in=self.problemy)
|
||||
print(self.reseni)
|
||||
return self.reseni
|
||||
|
||||
|
||||
class ObalkovaniView(generic.ListView):
|
||||
template_name = 'seminar/org/obalkovani.html'
|
||||
|
||||
def get_queryset(self):
|
||||
rocnik = get_object_or_404(Rocnik,rocnik=self.kwargs['rocnik'])
|
||||
cislo = get_object_or_404(Cislo,rocnik=rocnik,poradi=self.kwargs['cislo'])
|
||||
self.cislo = cislo
|
||||
self.hodnoceni = s.Hodnoceni.objects.filter(cislo_body=cislo)
|
||||
self.reseni = Reseni.objects.filter(hodnoceni__in = self.hodnoceni).annotate(Sum('hodnoceni__body')).annotate(Count('hodnoceni')).order_by('resitele__osoba')
|
||||
return self.reseni
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ObalkovaniView, self).get_context_data(**kwargs)
|
||||
print(self.cislo)
|
||||
context['cislo'] = self.cislo
|
||||
return context
|
||||
|
||||
|
||||
|
||||
|
||||
def AktualniZadaniView(request):
|
||||
nastaveni = get_object_or_404(Nastaveni)
|
||||
|
@ -67,6 +116,99 @@ def ZadaniTemataView(request):
|
|||
}
|
||||
)
|
||||
|
||||
# TODO Napsat tuto funkci znovu rekurzivně podle Jethrorad. Potom se podívat, jak lehce se dá modifikovat pro Rozcestník. Pokud lehce, rozšířit ji. Pokud složitě - použít tuhle
|
||||
def vytahniZLesaSeznam(tematko, koren, pouze_zajimave=False):
|
||||
returnVal = []
|
||||
|
||||
stack = []
|
||||
stack.append((koren.first_child, 0, False)) #Tuple of node, depth and relevance
|
||||
|
||||
while len(stack) > 0:
|
||||
wn, wd, wr = stack.pop()
|
||||
|
||||
if wn.succ != None:
|
||||
stack.append((wn.succ, wd, wr))
|
||||
if isinstance(wn, s.TemaVCisleNode):
|
||||
print("TEMA")
|
||||
print(wn.tema.id)
|
||||
print(tematko.id)
|
||||
if wn.tema.id == tematko.id:
|
||||
returnVal.append((posledni_cislo, 0))
|
||||
print("PRIDANO")
|
||||
wr = True
|
||||
wd = 1
|
||||
|
||||
if wn.srolovatelne:
|
||||
tagOpen = s.Text(na_web = "Otevírací srolovací tag")
|
||||
tagOpenNode = s.TextNode(text = tagOpen)
|
||||
tagClose = s.Text(na_web = "Zavírací srolovací tag")
|
||||
tagCloseNode = s.TextNode(text = tagClose)
|
||||
stack.append((tagCloseNode, wd, True))
|
||||
|
||||
if wn.first_child != None:
|
||||
stack.append((wn.first_child, wd + 1, wr))
|
||||
|
||||
if isinstance(wn, s.CisloNode):
|
||||
posledni_cislo = wn
|
||||
print(wn)
|
||||
|
||||
if wr:
|
||||
print("ZAJIMAVE")
|
||||
if pouze_zajimave:
|
||||
if not wn.zajimave:
|
||||
continue
|
||||
returnVal.append((wn, wd))
|
||||
return returnVal
|
||||
|
||||
def TematkoView(request, rocnik, tematko):
|
||||
nastaveni = s.Nastaveni.objects.first()
|
||||
rocnik_object = s.Rocnik.objects.filter(rocnik=rocnik)
|
||||
tematko_object = s.Tema.objects.filter(rocnik=rocnik_object[0], kod=tematko)
|
||||
seznam = vytahniZLesaSeznam(tematko_object[0], nastaveni.aktualni_rocnik().rocniknode)
|
||||
for node, depth in seznam:
|
||||
if node.isinstance(node, s.KonferaNode):
|
||||
raise Exception("Not implemented yet")
|
||||
if node.isinstance(node, s.PohadkaNode): # Mohu ignorovat, má pod sebou
|
||||
pass
|
||||
|
||||
return render(request, 'seminar/tematka/toaletak.html', {})
|
||||
|
||||
|
||||
def TemataRozcestnikView(request):
|
||||
print("=============================================")
|
||||
nastaveni = s.Nastaveni.objects.first()
|
||||
tematka_objects = s.Tema.objects.filter(rocnik=nastaveni.aktualni_rocnik())
|
||||
tematka = [] #List tematka obsahuje pro kazde tematko object a list vsech TemaVCisleNodu - implementované pomocí slovníku
|
||||
for tematko_object in tematka_objects:
|
||||
print("AKTUALNI TEMATKO")
|
||||
print(tematko_object.id)
|
||||
odkazy = vytahniZLesaSeznam(tematko_object, nastaveni.aktualni_rocnik().rocniknode, pouze_zajimave = True) #Odkazy jsou tuply (node, depth) v listu
|
||||
print(odkazy)
|
||||
cisla = [] # List tuplů (nazev cisla, list odkazů)
|
||||
vcisle = []
|
||||
cislo = None
|
||||
for odkaz in odkazy:
|
||||
if odkaz[1] == 0:
|
||||
if cislo != None:
|
||||
cisla.append((cislo, vcisle))
|
||||
cislo = (odkaz[0].getOdkazStr(), odkaz[0].getOdkaz())
|
||||
vcisle = []
|
||||
else:
|
||||
print(odkaz[0].getOdkaz())
|
||||
vcisle.append((odkaz[0].getOdkazStr(), odkaz[0].getOdkaz()))
|
||||
if cislo != None:
|
||||
cisla.append((cislo, vcisle))
|
||||
|
||||
print(cisla)
|
||||
tematka.append({
|
||||
"kod" : tematko_object.kod,
|
||||
"nazev" : tematko_object.nazev,
|
||||
"abstrakt" : tematko_object.abstrakt,
|
||||
"obrazek": tematko_object.obrazek,
|
||||
"cisla" : cisla
|
||||
})
|
||||
return render(request, 'seminar/tematka/rozcestnik.html', {"tematka": tematka, "rocnik" : nastaveni.aktualni_rocnik().rocnik})
|
||||
|
||||
|
||||
#def ZadaniAktualniVysledkovkaView(request):
|
||||
# nastaveni = get_object_or_404(Nastaveni)
|
||||
|
@ -143,7 +285,7 @@ class StareNovinkyView(generic.ListView):
|
|||
|
||||
|
||||
# Organizatori
|
||||
def aktivniOrganizatori(datum=date.today()):
|
||||
def aktivniOrganizatori(datum=timezone.now()):
|
||||
return Organizator.objects.exclude(
|
||||
organizuje_do__isnull=False,
|
||||
organizuje_do__lt=datum
|
||||
|
@ -252,26 +394,115 @@ class ArchivView(generic.ListView):
|
|||
context["nahledy"] = "\n".join(tags)
|
||||
return context
|
||||
|
||||
### Výsledky
|
||||
|
||||
def sloupec_s_poradim(vysledky):
|
||||
# počet řešitelů ve výsledkovce nad aktuálním
|
||||
lepsich_resitelu = 0
|
||||
# ze setřízeného(!) seznamu všech bodů vytvoří seznam s pořadími (včetně 3.-5. a pak 2 volná místa atp.)
|
||||
def sloupec_s_poradim(seznam_s_body):
|
||||
aktualni_poradi = 1
|
||||
sloupec_s_poradim = []
|
||||
|
||||
poradi_l = []
|
||||
# projdeme skupiny řešitelů se stejným počtem bodů
|
||||
for skupina in (list(x) for _, x in groupby(vysledky, lambda x: x.body)):
|
||||
|
||||
# připravíme si obsahy buněk ve sloupci pořadí pro skupinu
|
||||
if len(skupina) == 1:
|
||||
poradi_l += ["{}.".format(lepsich_resitelu + 1)]
|
||||
# je-li účastníků se stejným počtem bodů víc, pořadí (rozsah X.-Y.) je jen u prvního
|
||||
# seskupíme seznam všech bodů podle hodnot
|
||||
for index in range(0, len(seznam_s_body)):
|
||||
# pokud je pořadí větší než číslo řádku, tak jsme vypsali větší rozsah a chceme
|
||||
# vypsat už jen prázdné místo, než dojdeme na správný řádek
|
||||
if (index + 1) < aktualni_poradi:
|
||||
sloupec_s_poradim.append("")
|
||||
continue
|
||||
velikost_skupiny = 0
|
||||
# zjistíme počet po sobě jdoucích stejných hodnot
|
||||
while seznam_s_body[index] == seznam_s_body[index + velikost_skupiny]:
|
||||
velikost_skupiny = velikost_skupiny + 1
|
||||
# na konci musíme ošetřit přetečení seznamu
|
||||
if (index + velikost_skupiny) > len(seznam_s_body) - 1:
|
||||
break
|
||||
# pokud je velikost skupiny 1, vypíšu pořadí
|
||||
if velikost_skupiny == 1:
|
||||
sloupec_s_poradim.append("{}.".format(aktualni_poradi))
|
||||
# pokud je skupina větší, vypíšu rozsah
|
||||
else:
|
||||
poradi_l += [u"{}.–{}.".format(lepsich_resitelu + 1, lepsich_resitelu + len(skupina))] + [""] * (len(skupina)-1)
|
||||
lepsich_resitelu += len(skupina)
|
||||
#pomlcka je opravdu pomlcka v unicode!!dulezite pro vysledkovku v TeXu
|
||||
sloupec_s_poradim.append("{}.–{}.".format(aktualni_poradi,
|
||||
aktualni_poradi+velikost_skupiny-1))
|
||||
# zvětšíme aktuální pořadí o tolik, kolik pozic bylo přeskočeno
|
||||
aktualni_poradi = aktualni_poradi + velikost_skupiny
|
||||
return sloupec_s_poradim
|
||||
|
||||
return poradi_l
|
||||
# spočítá součet bodů získaných daným řešitelem za zadaný problém a všechny jeho podproblémy
|
||||
def __soucet_resitele_problemu(problem, resitel, cislo, soucet):
|
||||
# sečteme body za daný problém přes všechna řešení daného problému
|
||||
# od daného řešitele
|
||||
reseni_resitele = problem.hodnoceni_set.filter(reseni__resitele=resitel,
|
||||
cislo_body=cislo)
|
||||
# XXX chyba na řádku výše - řešení může mít více řešitelů, asi chceme contains
|
||||
# nebo in
|
||||
for r in reseni_resitele:
|
||||
soucet += r.body
|
||||
|
||||
# a přičteme k tomu hodnocení všech podproblémů
|
||||
for p in problem.podproblem.all():
|
||||
# i přes jméno by to měla být množina jeho podproblémů
|
||||
soucet += __soucet_resitele_problemu(p, resitel, soucet)
|
||||
return soucet
|
||||
|
||||
# spočítá součet všech bodů ze všech podproblémů daného problému daného řešitele
|
||||
def body_resitele_problemu_v_cisle(problem, resitel, cislo):
|
||||
# probably FIXED: nezohledňuje číslo, do kterého se body počítají
|
||||
return __soucet_resitele_problemu(problem, resitel, cislo, 0)
|
||||
|
||||
# vrátí list všech problémů s body v daném čísle, které již nemají nadproblém
|
||||
def hlavni_problemy_cisla(cislo):
|
||||
hodnoceni = cislo.hodnoceni.select_related('problem', 'reseni').all() # hodnocení, která se vážou k danému číslu
|
||||
|
||||
reseni = [h.reseni for h in hodnoceni]
|
||||
problemy = [h.problem for h in hodnoceni]
|
||||
problemy_set = set(problemy) # chceme každý problém unikátně,
|
||||
problemy = (list(problemy_set)) # převedení na množinu a zpět to zaručí
|
||||
|
||||
# hlavní problémy čísla
|
||||
# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém)
|
||||
hlavni_problemy = []
|
||||
for p in problemy:
|
||||
while not(p.nadproblem == None):
|
||||
p = p.nadproblem
|
||||
hlavni_problemy.append(p)
|
||||
|
||||
# zunikátnění
|
||||
hlavni_problemy_set = set(hlavni_problemy)
|
||||
hlavni_problemy = list(hlavni_problemy_set)
|
||||
hlavni_problemy.sort(key=lambda k: k.kod_v_rocniku()) # setřídit podle t1, t2, c3, ...
|
||||
|
||||
return hlavni_problemy
|
||||
|
||||
def body_resitele_odjakziva(resitel):
|
||||
body = 0
|
||||
resitelova_hodnoceni = Hodnoceni.objects.select_related('body').all().filter(reseni_resitele=resitel)
|
||||
# TODO: v radku nahore chceme _in nebo _contains
|
||||
for hodnoceni in resitelova_hodnoceni:
|
||||
body = body + hodnoceni.body
|
||||
return body
|
||||
|
||||
# spočítá součet všech bodů řešitele za dané číslo
|
||||
def body_resitele_v_cisle(resitel, cislo):
|
||||
hlavni_problemy = hlavni_problemy_cisla(cislo)
|
||||
body_resitele = 0
|
||||
for h in hlavni_problemy:
|
||||
body_resitele = body_resitele + body_resitele_problemu_v_cisle(h, resitel, cislo)
|
||||
# TODO: je rozdíl mezi odevzdanou úlohou za 0 a tím, když řešitel nic neodevzdal
|
||||
# řešit přes kontrolu velikosti množiny řešení daného problému do daného čísla?
|
||||
# Tady to ale nevadí, tady se počítá součet za číslo.
|
||||
return body_resitele
|
||||
|
||||
# spočítá součet všech bodů řešitele za daný rok (nebo jen do daného čísla včetně)
|
||||
def body_resitele_v_rocniku(resitel, rocnik, do_cisla=None):
|
||||
# pokud do_cisla=None, tak do posledního čísla v ročníku
|
||||
# do_cisla je objekt Cislo
|
||||
cisla = rocnik.cisla.all() # funkce vrátí pole objektů
|
||||
# Cislo už lexikograficky setřízené, viz models
|
||||
body = 0
|
||||
for cislo in cisla:
|
||||
if cislo.poradi == do_cisla.poradi: break
|
||||
# druhá část zaručuje, že máme výsledky do daného čísla včetně
|
||||
body = body + body_resitele_v_cisle(resitel, cislo)
|
||||
return body
|
||||
|
||||
#def vysledkovka_rocniku(rocnik, jen_verejne=True):
|
||||
# """Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve
|
||||
|
@ -288,7 +519,7 @@ def sloupec_s_poradim(vysledky):
|
|||
# return None
|
||||
#
|
||||
# #vybere vsechny vysledky z posledniho (verejneho) cisla a setridi sestupne dle bodu
|
||||
# vysledky = list(cisla_v_rocniku.filter(cislo = cisla_v_rocniku[0].cislo).order_by('-body', 'resitel__prijmeni', 'resitel__jmeno').select_related('resitel'))
|
||||
# vysledky = list(cisla_v_rocniku.filter(cislo = cisla_v_rocniku[0].poradi).order_by('-body', 'resitel__prijmeni', 'resitel__jmeno').select_related('resitel'))
|
||||
#
|
||||
# class Vysledkovka:
|
||||
# def __init__(self):
|
||||
|
@ -304,7 +535,7 @@ def sloupec_s_poradim(vysledky):
|
|||
# v.poradi = poradi
|
||||
# v.resitel.rocnik = v.resitel.rocnik(rocnik)
|
||||
#
|
||||
# verejne_vysl_odjakziva = VysledkyKCisluOdjakziva.objects.filter(cislo__rocnik=rocnik, cislo=cisla_v_rocniku[0].cislo)
|
||||
# verejne_vysl_odjakziva = VysledkyKCisluOdjakziva.objects.filter(cislo__rocnik=rocnik, cislo=cisla_v_rocniku[0].poradi)
|
||||
# if jen_verejne:
|
||||
# verejne_vysl_odjakziva = verejne_vysl_odjakziva.filter(cislo__verejna_vysledkovka=True)
|
||||
#
|
||||
|
@ -380,16 +611,17 @@ class ProblemView(generic.DetailView):
|
|||
|
||||
class VysledkyResitele(object):
|
||||
"""Pro daného řešitele ukládá počet bodů za jednotlivé úlohy a celkový
|
||||
počet bodů za číslo."""
|
||||
počet bodů za konkrétní ročník do daného čísla a za dané číslo."""
|
||||
|
||||
def __init__(self, jmeno, prijmeni):
|
||||
resitel_jmeno = jmeno
|
||||
resitel_prijmeni = prijmeni
|
||||
body = {}
|
||||
body_cislo = 0
|
||||
|
||||
def body_za_cislo(self):
|
||||
return sum(body.values())
|
||||
def __init__(self, resitel, cislo, rocnik):
|
||||
self.resitel = resitel
|
||||
self.cislo = cislo
|
||||
self.body_cislo = body_resitele_v_cisle(resitel, cislo)
|
||||
self.body = []
|
||||
self.rocnik = rocnik
|
||||
self.body_rocnik = body_resitele_v_rocniku(resitel, rocnik, cislo)
|
||||
self.body_celkem_odjakziva = resitel.vsechny_body()
|
||||
self.poradi = 0
|
||||
|
||||
class CisloView(generic.DetailView):
|
||||
model = Cislo
|
||||
|
@ -400,8 +632,8 @@ class CisloView(generic.DetailView):
|
|||
if queryset is None:
|
||||
queryset = self.get_queryset()
|
||||
rocnik_arg = self.kwargs.get('rocnik')
|
||||
cislo_arg = self.kwargs.get('cislo')
|
||||
queryset = queryset.filter(rocnik__rocnik=rocnik_arg, cislo=cislo_arg)
|
||||
poradi_arg = self.kwargs.get('cislo')
|
||||
queryset = queryset.filter(rocnik__rocnik=rocnik_arg, poradi=poradi_arg)
|
||||
|
||||
try:
|
||||
obj = queryset.get()
|
||||
|
@ -410,88 +642,53 @@ class CisloView(generic.DetailView):
|
|||
{'verbose_name': queryset.model._meta.verbose_name})
|
||||
return obj
|
||||
|
||||
# spočítá součet bodů získaných daným řešitelem za zadaný problém a všechny jeho podproblémy
|
||||
def __soucet_resitele_problemu(problem, resitel, soucet):
|
||||
# FIXME: správně je nadproblem_(typ problemu), ale to by bylo potřeba nějak
|
||||
# zjistit, jaký typ nodu to vlastně je a aplikovat to ve volání funkce
|
||||
|
||||
# sečteme body za daný problém přes všechna řešení daného problému
|
||||
# od daného řešitele
|
||||
reseni_resitele = problem.hodnoceni_set.filter(reseni_resitele__contains=resitel)
|
||||
for r in reseni_resitele:
|
||||
soucet += r.body
|
||||
|
||||
for p in problem.nadproblem_set:
|
||||
# i přes jméno by to měla být množina jeho podproblémů
|
||||
soucet += __soucet_resitele_problemu(p, resitel, soucet)
|
||||
return soucet
|
||||
|
||||
|
||||
def vysledky_resitele_problemu(problem, resitel, cislo):
|
||||
return __soucet_resitele_problemu(problem, resitel, 0)
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(CisloView, self).get_context_data(**kwargs)
|
||||
|
||||
## TODO upravit dle nového modelu
|
||||
cislo = context['cislo']
|
||||
hodnoceni = cislo.hodnoceni_set # hodnocení, která se vážou k danému číslu
|
||||
|
||||
reseni = [h.reseni for h in hodnoceni]
|
||||
problemy = [h.problem for h in hodnoceni]
|
||||
problemy_set = set(problemy) # chceme každý problém unikátně,
|
||||
problemy = (list(problemy_set)) # převedení na množinu a zpět to zaručí
|
||||
|
||||
# hlavní problémy čísla
|
||||
# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém)
|
||||
hlavni_problemy = []
|
||||
for p in problemy:
|
||||
while not(p.nadproblem == None):
|
||||
p = nadproblem
|
||||
hlavni_problemy.append(p)
|
||||
|
||||
# zunikátnění
|
||||
hlavni_problemy_set = set(hlavni_problemy)
|
||||
hlavni_problemy = list(hlavni_problemy_set)
|
||||
hlavni_problemy = hlavni_problemy_cisla(cislo)
|
||||
|
||||
## TODO dostat pro tyto problémy součet v daném čísle pro daného řešitele
|
||||
## TODO možná chytřeji vybírat aktivní řešitele
|
||||
## chceme letos něco poslal
|
||||
aktivni_resitele = Resitel.objects.filter(
|
||||
rok_maturity__gte=context['rocnik'].druhy_rok())
|
||||
rok_maturity__gte=cislo.rocnik.druhy_rok())
|
||||
# TODO: zkusit hodnoceni__rocnik...
|
||||
#.filter(hodnoceni_set__rocnik__eq=cislo_rocnik)
|
||||
radky_vysledkovky = []
|
||||
for ar in aktivni_resitele:
|
||||
vr = VysledkyResitele(ar.jmeno, ar.prijmeni)
|
||||
for h in hlavni_problemy:
|
||||
body = vysledky_resitele_problemu(h, ar, cislo)
|
||||
vr.body[h.kod_v_rocniku] = body
|
||||
vr.body_cislo = vr.body_cislo + body
|
||||
# získáme výsledky řešitele - součty přes číslo a ročník
|
||||
vr = VysledkyResitele(ar, cislo, cislo.rocnik)
|
||||
for hp in hlavni_problemy:
|
||||
vr.body.append(
|
||||
body_resitele_problemu_v_cisle(hp, ar, cislo))
|
||||
radky_vysledkovky.append(vr)
|
||||
|
||||
## TODO: spočítat počet bodů řešitele v daném ročníku a seřadit je podle toho
|
||||
## TODO: možná použít tyto funkce i v RocnikVysledkovkaView (a umístit sem nebo tam)?
|
||||
# setřídíme řádky výsledkovky/objekty VysledkyResitele podle bodů
|
||||
radky_vysledkovky.sort(key=lambda vr: vr.body_rocnik, reverse=True)
|
||||
|
||||
# generujeme sloupec s pořadím pomocí stejně zvané funkce
|
||||
pocty_bodu = [rv.body_rocnik for rv in radky_vysledkovky]
|
||||
sloupec_poradi = sloupec_s_poradim(pocty_bodu)
|
||||
|
||||
# každému řádku výsledkovky přidáme jeho pořadí
|
||||
i = 0
|
||||
for rv in radky_vysledkovky:
|
||||
rv.poradi = sloupec_poradi[i]
|
||||
i = i + 1
|
||||
|
||||
# vysledky = VysledkyKCisluZaRocnik.objects.filter(cislo = context['cislo']).\
|
||||
# order_by('-body', 'resitel__prijmeni', 'resitel__jmeno')
|
||||
# reseni = Reseni.objects.filter(cislo_body = context['cislo']).select_related("resitel")
|
||||
# vytahané informace předáváme do kontextu
|
||||
context['cislo'] = cislo
|
||||
context['radky_vysledkovky'] = radky_vysledkovky
|
||||
context['problemy'] = hlavni_problemy
|
||||
# context['v_cisle_zadane'] = TODO
|
||||
# context['resene_problemy'] = resene_problemy
|
||||
#XXX testovat
|
||||
#XXX opravit to, že se nezobrazují body za jednotlivé úlohy
|
||||
|
||||
# typy úloh, které se mají zobrazovat u čísla, tj. těch, které byly
|
||||
# v čísle skutečně zadány
|
||||
# typy_skutecne_zadanych = [Problem.TYP_ULOHA, Problem.TYP_SERIAL, Problem.TYP_ORG_CLANEK]
|
||||
# v_cisle_zadane = Problem.objects.filter(cislo_zadani=context['cislo']).filter(typ__in=typy_skutecne_zadanych).order_by('kod')
|
||||
return context
|
||||
|
||||
# resene_problemy = Problem.objects.filter(cislo_reseni=context['cislo']).filter(typ__in=typy_skutecne_zadanych).order_by('cislo_zadani__cislo', 'kod')
|
||||
#
|
||||
# poradi_typu = {
|
||||
# Problem.TYP_ULOHA: 1,
|
||||
# Problem.TYP_SERIAL: 2,
|
||||
# Problem.TYP_ORG_CLANEK: 3,
|
||||
# Problem.TYP_TEMA: 4,
|
||||
# Problem.TYP_RES_CLANEK: 5
|
||||
# }
|
||||
# problemy = sorted(set(r.problem for r in reseni), key=lambda x:(poradi_typu[x.typ], x.kod_v_rocniku()))
|
||||
# #setridi problemy podle typu a poradi zadani
|
||||
# problem_index = {}
|
||||
|
@ -529,7 +726,6 @@ class CisloView(generic.DetailView):
|
|||
# context['problemy'] = problemy
|
||||
# context['v_cisle_zadane'] = v_cisle_zadane
|
||||
# context['resene_problemy'] = resene_problemy
|
||||
# return context
|
||||
|
||||
class ArchivTemataView(generic.ListView):
|
||||
model = Problem
|
||||
|
@ -601,9 +797,9 @@ def obalkyView(request,resitele):
|
|||
tex = render(request,'seminar/archiv/obalky.tex', {'resitele': resitele}).content
|
||||
|
||||
tempdir = tempfile.mkdtemp()
|
||||
with open(tempdir+"/obalky.tex","wb") as texfile:
|
||||
with open(tempdir+"/obalky.tex","w") as texfile:
|
||||
texfile.write(tex)
|
||||
shutil.copy(os.path.join(settings.STATIC_ROOT, 'seminar/lisak.eps'),tempdir)
|
||||
shutil.copy(os.path.join(settings.STATIC_ROOT, 'seminar/lisak.pdf'),tempdir)
|
||||
subprocess.call(["pdflatex","obalky.tex"],cwd = tempdir)
|
||||
|
||||
with open(tempdir+"/obalky.pdf","rb") as pdffile:
|
||||
|
@ -612,7 +808,7 @@ def obalkyView(request,resitele):
|
|||
return response
|
||||
|
||||
|
||||
def obalkovaniView(request, rocnik, cislo):
|
||||
def oldObalkovaniView(request, rocnik, cislo):
|
||||
rocnik = Rocnik.objects.get(rocnik=rocnik)
|
||||
cislo = Cislo.objects.get(rocnik=rocnik, cislo=cislo)
|
||||
|
||||
|
@ -749,7 +945,7 @@ def StavDatabazeView(request):
|
|||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
def LoginView(request):
|
||||
def TeXUploadLoginView(request):
|
||||
"""Pro přihlášení při nahrávání z texu"""
|
||||
q = request.POST
|
||||
# nastavení cookie csrftoken
|
||||
|
@ -938,7 +1134,7 @@ def texDownloadView(request, rocnik, cislo):
|
|||
"body": p.body,
|
||||
"zadani": p.text_zadani,
|
||||
"reseni": p.text_reseni,
|
||||
"cislo_zadani": p.cislo_zadani.cislo,
|
||||
"cislo_zadani": p.cislo_zadani.poradi,
|
||||
} for p in resene
|
||||
],
|
||||
}
|
||||
|
@ -947,26 +1143,213 @@ def texDownloadView(request, rocnik, cislo):
|
|||
cislo.save()
|
||||
return JsonResponse(response)
|
||||
|
||||
## Formulare
|
||||
class ResitelView(LoginRequiredMixin,generic.DetailView):
|
||||
model = Resitel
|
||||
template_name = 'seminar/resitel.html'
|
||||
|
||||
def get_name(request):
|
||||
# if this is a POST request we need to process the form data
|
||||
def get_object(self, queryset=None):
|
||||
print(self.request.user)
|
||||
return Resitel.objects.get(osoba__user=self.request.user)
|
||||
|
||||
## Formulare
|
||||
class AddSolutionView(LoginRequiredMixin, FormView):
|
||||
template_name = 'seminar/org/vloz_reseni.html'
|
||||
form_class = f.VlozReseniForm
|
||||
success_url = '/'
|
||||
|
||||
def resetPasswordView(request):
|
||||
pass
|
||||
|
||||
def loginView(request):
|
||||
if request.method == 'POST':
|
||||
# create a form instance and populate it with data from the request:
|
||||
form = NameForm(request.POST)
|
||||
# check whether it's valid:
|
||||
form = LoginForm(request.POST)
|
||||
if form.is_valid():
|
||||
# process the data in form.cleaned_data as required
|
||||
# ...
|
||||
# redirect to a new URL:
|
||||
user = authenticate(request,
|
||||
username=form.cleaned_data['username'],
|
||||
password=form.cleaned_data['password'])
|
||||
print(form.cleaned_data)
|
||||
if user is not None:
|
||||
login(request,user)
|
||||
return HttpResponseRedirect('/')
|
||||
else:
|
||||
return render(request,
|
||||
'seminar/login.html',
|
||||
{'form': form, 'login_error': 'Neplatné jméno nebo heslo'})
|
||||
|
||||
else:
|
||||
form = LoginForm()
|
||||
return render(request, 'seminar/login.html', {'form': form})
|
||||
|
||||
def logoutView(request):
|
||||
form = LoginForm()
|
||||
if request.user.is_authenticated:
|
||||
logout(request)
|
||||
return render(request, 'seminar/login.html', {'form': form, 'login_error': 'Byli jste úspěšně odhlášeni'})
|
||||
return render(request, 'seminar/login.html', {'form': form})
|
||||
|
||||
|
||||
def prihlaska_log_gdpr_safe(logger, gdpr_logger, msg, form_data):
|
||||
msg = "{}, form_hash:{}".format(msg,hash(form_data))
|
||||
logger.warn(msg)
|
||||
gdpr_logger.warn(msg+", form:{}".format(form_data))
|
||||
|
||||
from django.forms.models import model_to_dict
|
||||
def resitelEditView(request):
|
||||
err_logger = logging.getLogger('seminar.prihlaska.problem')
|
||||
## Načtení objektu Osoba a Resitel, patrici k aktuálně přihlášenému uživately
|
||||
u = request.user
|
||||
osoba_edit = Osoba.objects.get(user=u)
|
||||
resitel_edit = osoba_edit.resitel
|
||||
user_edit = osoba_edit.user
|
||||
## Vytvoření slovníku, kterým předvyplním formulář
|
||||
prefill_1=model_to_dict(user_edit)
|
||||
prefill_2=model_to_dict(resitel_edit)
|
||||
prefill_3=model_to_dict(osoba_edit)
|
||||
prefill_1.update(prefill_2)
|
||||
prefill_1.update(prefill_3)
|
||||
form = ProfileEditForm(initial=prefill_1)
|
||||
## Změna údajů a jejich uložení
|
||||
if request.method == 'POST':
|
||||
form = ProfileEditForm(request.POST)
|
||||
if form.is_valid():
|
||||
## Změny v osobě
|
||||
fcd = form.cleaned_data
|
||||
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']
|
||||
## 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'])
|
||||
|
||||
## Změny v řešiteli
|
||||
resitel_edit.skola = fcd['skola']
|
||||
resitel_edit.rok_maturity = fcd['rok_maturity']
|
||||
resitel_edit.zasilat = fcd['zasilat']
|
||||
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 HttpResponseRedirect('/thanks/')
|
||||
else:
|
||||
## Stránka před odeslaním formuláře = předvyplněný formulář
|
||||
return render(request, 'seminar/edit.html', {'form': form})
|
||||
|
||||
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(fcd)
|
||||
form_logger.info(fcd,form_hash=form_hash)
|
||||
|
||||
with transaction.atomic():
|
||||
u = User.objects.create_user(
|
||||
username=fcd['username'],
|
||||
password=fcd['password'],
|
||||
email = fcd['email'])
|
||||
u.save()
|
||||
|
||||
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,form_hash=form_hash)
|
||||
|
||||
o.save()
|
||||
o.user = u
|
||||
o.save()
|
||||
|
||||
r = Resitel(
|
||||
rok_maturity = fcd['rok_maturity'],
|
||||
zasilat = fcd['zasilat']
|
||||
)
|
||||
|
||||
r.save()
|
||||
r.osoba = o
|
||||
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,form_hash=form_hash)
|
||||
r.save()
|
||||
|
||||
|
||||
return HttpResponseRedirect('/thanks/')
|
||||
|
||||
# if a GET (or any other method) we'll create a blank form
|
||||
else:
|
||||
form = NameForm()
|
||||
form = PrihlaskaForm()
|
||||
|
||||
return render(request, 'seminar/prihlaska.html', {'form': form})
|
||||
|
||||
class SkolaAutocomplete(autocomplete.Select2QuerySetView):
|
||||
def get_queryset(self):
|
||||
# Don't forget to filter out results depending on the visitor !
|
||||
qs = Skola.objects.all()
|
||||
if self.q:
|
||||
qs = qs.filter(
|
||||
Q(nazev__istartswith=self.q)|
|
||||
Q(kratky_nazev__istartswith=self.q)|
|
||||
Q(ulice__istartswith=self.q)|
|
||||
Q(mesto__istartswith=self.q))
|
||||
|
||||
return qs
|
||||
|
||||
class LoginRequiredAjaxMixin(object):
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
#if request.is_ajax() and not request.user.is_authenticated: # Pokud to otevřu jako stránku, tak se omezení neuplatní, takže to asi nechceme
|
||||
if not request.user.is_authenticated:
|
||||
return JsonResponse(data={'results': [], 'pagination': {}}, status=401)
|
||||
return super(LoginRequiredAjaxMixin, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
class ResitelAutocomplete(LoginRequiredAjaxMixin,autocomplete.Select2QuerySetView):
|
||||
def get_queryset(self):
|
||||
qs = Resitel.objects.all()
|
||||
if self.q:
|
||||
qs = qs.filter(
|
||||
Q(osoba__jmeno__startswith=self.q)|
|
||||
Q(osoba__prijmeni__startswith=self.q)|
|
||||
Q(osoba__prezdivka__startswith=self.q)
|
||||
)
|
||||
return qs
|
||||
|
||||
|
||||
# Ceka na autocomplete v3
|
||||
# class OrganizatorAutocomplete(autocomplete.Select2QuerySetView):
|
||||
# def get_queryset(self):
|
||||
|
@ -987,3 +1370,46 @@ def get_name(request):
|
|||
# Q(user__last_name__isstartswith=query))
|
||||
#
|
||||
# return qs
|
||||
|
||||
# FIXME: Tohle asi vlastně vůbec nepatří do aplikace 'seminar'
|
||||
class LoginView(auth_views.LoginView):
|
||||
# Jen vezmeme vestavěný a dáme mu vhodný template a přesměrovací URL
|
||||
template_name = 'seminar/login.html'
|
||||
|
||||
# Přesměrovací URL má být v kontextu:
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['next'] = reverse('titulni_strana')
|
||||
return ctx
|
||||
|
||||
class LogoutView(auth_views.LogoutView):
|
||||
# Jen vezmeme vestavěný a dáme mu vhodný template a přesměrovací URL
|
||||
template_name = 'seminar/logout.html'
|
||||
# Pavel: Vůbec nevím, proč to s _lazy funguje, ale bez toho to bylo rozbité.
|
||||
next_page = reverse_lazy('titulni_strana')
|
||||
|
||||
# "Chci resetovat heslo"
|
||||
class PasswordResetView(auth_views.PasswordResetView):
|
||||
#template_name = 'seminar/password_reset.html'
|
||||
# TODO: vlastní email_template_name a subject_template_name a html_email_template_name
|
||||
success_url = reverse_lazy('reset_password_done')
|
||||
from_email = 'login@mam.mff.cuni.cz'
|
||||
|
||||
# "Poslali jsme e-mail (pokud bylo kam))"
|
||||
class PasswordResetDoneView(auth_views.PasswordResetDoneView):
|
||||
#template_name = 'seminar/password_reset_done.html'
|
||||
pass
|
||||
|
||||
# "Vymysli si heslo"
|
||||
class PasswordResetConfirmView(auth_views.PasswordResetConfirmView):
|
||||
#template_name = 'seminar/password_confirm_done.html'
|
||||
success_url = reverse_lazy('reset_password_complete')
|
||||
|
||||
# "Heslo se asi změnilo."
|
||||
class PasswordResetCompleteView(auth_views.PasswordResetCompleteView):
|
||||
#template_name = 'seminar/password_complete_done.html'
|
||||
pass
|
||||
|
||||
class PasswordChangeView(auth_views.PasswordChangeView):
|
||||
#template_name = 'seminar/password_change.html'
|
||||
success_url = reverse_lazy('titulni_strana')
|
||||
|
|
Loading…
Reference in a new issue