Merge branch 'data_migrations' of gimli.ms.mff.cuni.cz:/akce/mam/git/mamweb into data_migrations
This commit is contained in:
commit
d9558a750a
21 changed files with 1003 additions and 154 deletions
BIN
Schema_new.dia
BIN
Schema_new.dia
Binary file not shown.
332
flat.json
Normal file
332
flat.json
Normal file
File diff suppressed because one or more lines are too long
|
@ -2,6 +2,7 @@ from django import forms
|
|||
from dal import autocomplete
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.contrib.auth.models import User
|
||||
from django.forms.models import inlineformset_factory
|
||||
|
||||
from .models import Skola, Resitel, Osoba, Problem
|
||||
import seminar.models as m
|
||||
|
@ -252,6 +253,25 @@ class VlozReseniForm(forms.Form):
|
|||
super().__init__(*args, **kwargs)
|
||||
#self.fields['favorite_color'] = forms.ChoiceField(choices=[(color.id, color.name) for color in Resitel.objects.all()])
|
||||
|
||||
class NahrajReseniForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = m.Reseni
|
||||
fields = ('problem',)
|
||||
|
||||
widgets = {'problem':
|
||||
autocomplete.ModelSelect2Multiple(
|
||||
url='autocomplete_problem_odevzdatelny',
|
||||
attrs = {'data-placeholder--id': '-1',
|
||||
'data-placeholder--text' : '---',
|
||||
'data-allow-clear': 'true'},
|
||||
)
|
||||
}
|
||||
|
||||
ReseniSPrilohamiFormSet = inlineformset_factory(m.Reseni,m.PrilohaReseni,
|
||||
form = NahrajReseniForm,
|
||||
fields = ('soubor','res_poznamka'),
|
||||
widgets = {'res_poznamka':forms.TextInput()},
|
||||
extra = 1,
|
||||
|
||||
)
|
||||
|
||||
|
|
24
seminar/migrations/0074_auto_20200228_1401.py
Normal file
24
seminar/migrations/0074_auto_20200228_1401.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Generated by Django 2.2.9 on 2020-02-28 13:01
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0073_copy_osoba_email_to_user_email'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='prilohareseni',
|
||||
name='res_poznamka',
|
||||
field=models.TextField(blank=True, help_text='Poznámka k příloze řešení, např. co daný soubor obsahuje', verbose_name='poznámka řešitele'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='hodnoceni',
|
||||
name='problem',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.Problem', verbose_name='problém'),
|
||||
),
|
||||
]
|
18
seminar/migrations/0075_auto_20200228_2010.py
Normal file
18
seminar/migrations/0075_auto_20200228_2010.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.2.9 on 2020-02-28 19:10
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0074_auto_20200228_1401'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='hodnoceni',
|
||||
name='body',
|
||||
field=models.DecimalField(decimal_places=1, max_digits=8, null=True, verbose_name='body'),
|
||||
),
|
||||
]
|
19
seminar/migrations/0076_auto_20200228_2013.py
Normal file
19
seminar/migrations/0076_auto_20200228_2013.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 2.2.9 on 2020-02-28 19:13
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0075_auto_20200228_2010'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='hodnoceni',
|
||||
name='cislo_body',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.Cislo', verbose_name='číslo pro body'),
|
||||
),
|
||||
]
|
32
seminar/migrations/0077_auto_20200318_2146.py
Normal file
32
seminar/migrations/0077_auto_20200318_2146.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
# Generated by Django 2.2.9 on 2020-03-18 20:46
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0076_auto_20200228_2013'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CastNode',
|
||||
fields=[
|
||||
('treenode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='seminar.TreeNode')),
|
||||
('nadpis', models.CharField(help_text='Nadpis podvěšené části obsahu', max_length=100, verbose_name='Nadpis')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Část (Node)',
|
||||
'verbose_name_plural': 'Části (Node)',
|
||||
'db_table': 'seminar_nodes_cast',
|
||||
},
|
||||
bases=('seminar.treenode',),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='treenode',
|
||||
name='first_child',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='father_of_first', to='seminar.TreeNode', verbose_name='první potomek'),
|
||||
),
|
||||
]
|
|
@ -920,10 +920,10 @@ class Hodnoceni(SeminarModelBase):
|
|||
|
||||
|
||||
body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body',
|
||||
blank=False, null=False)
|
||||
blank=False, null=True)
|
||||
|
||||
cislo_body = models.ForeignKey(Cislo, verbose_name='číslo pro body',
|
||||
related_name='hodnoceni', blank=False, null=False, on_delete=models.PROTECT)
|
||||
related_name='hodnoceni', blank=False, null=True, on_delete=models.PROTECT)
|
||||
|
||||
reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE)
|
||||
|
||||
|
@ -935,17 +935,16 @@ class Hodnoceni(SeminarModelBase):
|
|||
|
||||
|
||||
|
||||
## FIXME: Budeme řešit později, pokud to bude potřeba.
|
||||
#def aux_generate_filename(self, filename):
|
||||
# """Pomocná funkce generující ošetřený název souboru v adresáři s datem"""
|
||||
# clean = get_valid_filename(
|
||||
# unidecode(filename.replace('/', '-').replace('\0', ''))
|
||||
# )
|
||||
# datedir = timezone.now().strftime('%Y-%m')
|
||||
# fname = "%s_%s" % (
|
||||
# timezone.now().strftime('%Y-%m-%d-%H:%M'),
|
||||
# clean)
|
||||
# return os.path.join(datedir, fname)
|
||||
def aux_generate_filename(self, filename):
|
||||
"""Pomocná funkce generující ošetřený název souboru v adresáři s datem"""
|
||||
clean = get_valid_filename(
|
||||
unidecode(filename.replace('/', '-').replace('\0', ''))
|
||||
)
|
||||
datedir = timezone.now().strftime('%Y-%m')
|
||||
fname = "{}_{}".format(
|
||||
timezone.now().strftime('%Y-%m-%d-%H:%M'),
|
||||
clean)
|
||||
return os.path.join(datedir, fname)
|
||||
|
||||
# Django neumí jednoduše serializovat partial nebo třídu s __call__
|
||||
# (https://docs.djangoproject.com/en/1.8/topics/migrations/),
|
||||
|
@ -988,9 +987,12 @@ class PrilohaReseni(SeminarModelBase):
|
|||
|
||||
poznamka = models.TextField('neveřejná poznámka', blank=True,
|
||||
help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu')
|
||||
|
||||
res_poznamka = models.TextField('poznámka řešitele', blank=True,
|
||||
help_text='Poznámka k příloze řešení, např. co daný soubor obsahuje')
|
||||
|
||||
def __str__(self):
|
||||
return self.soubor
|
||||
return str(self.soubor)
|
||||
|
||||
|
||||
class Pohadka(SeminarModelBase):
|
||||
|
@ -1228,6 +1230,8 @@ class Obrazek(SeminarModelBase):
|
|||
help_text = 'Černobílá verze obrázku do čísla',
|
||||
upload_to = 'obrazky/%Y/%m/%d/', blank=True, null=True)
|
||||
|
||||
# TODO placement hint - chci ho tady / pred textem / za textem
|
||||
|
||||
class TreeNode(PolymorphicModel):
|
||||
class Meta:
|
||||
db_table = "seminar_nodes_treenode"
|
||||
|
@ -1242,6 +1246,7 @@ class TreeNode(PolymorphicModel):
|
|||
on_delete = models.SET_NULL, # Vrcholy s null kořenem jsou sirotci bez ročníku
|
||||
verbose_name="kořen stromu")
|
||||
first_child = models.ForeignKey('TreeNode',
|
||||
related_name='father_of_first',
|
||||
null = True,
|
||||
blank = True,
|
||||
on_delete=models.SET_NULL,
|
||||
|
@ -1262,15 +1267,6 @@ class TreeNode(PolymorphicModel):
|
|||
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):
|
||||
# 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()
|
||||
|
@ -1339,6 +1335,7 @@ class MezicisloNode(TreeNode):
|
|||
verbose_name = 'Mezičíslo (Node)'
|
||||
verbose_name_plural = 'Mezičísla (Node)'
|
||||
|
||||
# TODO: Využít TreeLib
|
||||
def aktualizuj_nazev(self):
|
||||
if self.prev:
|
||||
if (self.prev.get_real_instance_class() != CisloNode and
|
||||
|
@ -1469,6 +1466,14 @@ class TextNode(TreeNode):
|
|||
def getOdkazStr(self):
|
||||
return str(self.text)
|
||||
|
||||
class CastNode(TreeNode):
|
||||
class Meta:
|
||||
db_table = 'seminar_nodes_cast'
|
||||
verbose_name = 'Část (Node)'
|
||||
verbose_name_plural = 'Části (Node)'
|
||||
|
||||
nadpis = models.CharField('Nadpis', max_length=100, help_text = 'Nadpis podvěšené části obsahu')
|
||||
|
||||
|
||||
## FIXME: Logiku přesunout do views.
|
||||
#class VysledkyBase(SeminarModelBase):
|
||||
|
|
49
seminar/static/seminar/dynamic_formsets.js
Normal file
49
seminar/static/seminar/dynamic_formsets.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0
|
||||
function updateElementIndex(el, prefix, ndx) {
|
||||
var id_regex = new RegExp('(' + prefix + '-\\d+)');
|
||||
var replacement = prefix + '-' + ndx;
|
||||
if ($(el).attr("for")) {
|
||||
$(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
|
||||
}
|
||||
if (el.id) {
|
||||
el.id = el.id.replace(id_regex, replacement);
|
||||
}
|
||||
if (el.name) {
|
||||
el.name = el.name.replace(id_regex, replacement);
|
||||
}
|
||||
}
|
||||
|
||||
// Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0
|
||||
function deleteForm(prefix, btn) {
|
||||
var total = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());
|
||||
if (total >= 1){
|
||||
btn.closest('div').remove();
|
||||
var forms = $('.attachment');
|
||||
$('#id_' + prefix + '-TOTAL_FORMS').val(forms.length);
|
||||
for (var i=0, formCount=forms.length; i<formCount; i++) {
|
||||
$(forms.get(i)).find(':input').each(function() {
|
||||
updateElementIndex(this, prefix, i);
|
||||
});
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Credit: https://simpleit.rocks/python/django/dynamic-add-form-with-add-button-in-django-modelformset-template/
|
||||
$(document).ready(function(){
|
||||
$('#add_attachment').click(function() {
|
||||
var form_idx = $('#id_prilohy-TOTAL_FORMS').val();
|
||||
var new_form = $('#empty_form').html().replace(/__prefix__/g, form_idx);
|
||||
$('#form_set').append(new_form);
|
||||
// Newly created form has not the binding between remove button and remove function
|
||||
// We need to add it manually
|
||||
$('.remove_attachment').click(function(){
|
||||
deleteForm("prilohy",this);
|
||||
});
|
||||
$('#id_prilohy-TOTAL_FORMS').val(parseInt(form_idx) + 1);
|
||||
});
|
||||
$('.remove_attachment').click(function(){
|
||||
deleteForm("prilohy",this);
|
||||
});
|
||||
});
|
||||
|
44
seminar/templates/seminar/nahraj_reseni.html
Normal file
44
seminar/templates/seminar/nahraj_reseni.html
Normal file
|
@ -0,0 +1,44 @@
|
|||
{% 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/dynamic_formsets.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% block nadpis1a %}{% block nadpis1b %}
|
||||
Vložit řešení
|
||||
{% endblock %}{% endblock %}
|
||||
</h1>
|
||||
<form enctype="multipart/form-data" action="{% url 'seminar_nahraj_reseni' %}" method="post">
|
||||
{% csrf_token %}
|
||||
{{form}}
|
||||
{{prilohy.management_form}}
|
||||
<div id="form_set">
|
||||
{% for form in prilohy.forms %}
|
||||
<div class="attachment">
|
||||
{{form.non_field_errors}}
|
||||
{{form.errors}}
|
||||
<table class='no_error'>
|
||||
{{ form }}
|
||||
</table>
|
||||
<input type="button" value="Odebrat" class="remove_attachment" id="{{form.prefix}}-jsremove">
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<input type="button" value="Přidat přílohu" id="add_attachment">
|
||||
<div id="empty_form" style="display:none">
|
||||
<div class="attachment">
|
||||
<table class='no_error'>
|
||||
{{ prilohy.empty_form }}
|
||||
</table>
|
||||
<input type="button" value="Odebrat" class="remove_attachment" id="id_prilohy-__prefix__-jsremove">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<input type="submit" value="Odevzdat">
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
|
@ -9,7 +9,7 @@ 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
|
||||
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, TreeNode
|
||||
|
||||
from django.contrib.flatpages.models import FlatPage
|
||||
from django.contrib.sites.models import Site
|
||||
|
@ -390,17 +390,15 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori):
|
|||
co = ["téma", "záření", "stavení", "jiskření", "jelito",
|
||||
"drama", "kuře", "moře", "klání", "proudění", "čekání"]
|
||||
poc_oboru = rnd.randint(1, 2)
|
||||
poc_op = rnd.randint(1, 3)
|
||||
|
||||
rocnik_temata = []
|
||||
k = 0
|
||||
for rocnik in rocniky:
|
||||
k+=1
|
||||
n = 0
|
||||
temata = []
|
||||
cisla = rocnik_cisla[k-1]
|
||||
for ci in range(1, 3):
|
||||
n+=1
|
||||
# Věříme, že rocnik_cisla je pole polí čísel podle ročníků, tak si necháme dát vždycky jeden ročník a k němu příslušná čísla.
|
||||
for rocnik, cisla in zip(rocniky, rocnik_cisla):
|
||||
kod = 1
|
||||
letosni_temata = []
|
||||
# Do každého ročníku vymyslíme tři (zatím) témata, v každém z prvních čísel jedno
|
||||
for zacatek_tematu in range(1, 3):
|
||||
# Vygenerujeme téma
|
||||
t = Tema.objects.create(
|
||||
# atributy třídy Problem
|
||||
nazev=" ".join([rnd.choice(jake), rnd.choice(co)]),
|
||||
|
@ -408,22 +406,32 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori):
|
|||
zamereni=rnd.sample(["M", "F", "I", "O", "B"], poc_oboru),
|
||||
autor=rnd.choice(organizatori),
|
||||
garant=rnd.choice(organizatori),
|
||||
kod=str(n),
|
||||
kod=str(kod),
|
||||
# atributy třídy Téma
|
||||
tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0],
|
||||
rocnik=rocnik,
|
||||
abstrakt = "Abstrakt tematka {}".format(n)
|
||||
abstrakt = "Abstrakt tematka {}".format(kod)
|
||||
)
|
||||
konec_tematu = min(rnd.randint(ci, 7), len(cisla))
|
||||
for i in range(ci, konec_tematu+1):
|
||||
kod += 1
|
||||
|
||||
# Vymyslíme, kdy skončí
|
||||
konec_tematu = min(rnd.randint(zacatek_tematu, 7), len(cisla))
|
||||
|
||||
# Vyrobíme TemaVCisleNody pro obsah
|
||||
for i in range(zacatek_tematu, konec_tematu+1):
|
||||
node = TemaVCisleNode.objects.create(tema = t)
|
||||
# FIXME: Není to off-by-one?
|
||||
otec = cisla[i-1].cislonode
|
||||
otec_syn(otec, node)
|
||||
|
||||
t.opravovatele.set(rnd.sample(organizatori, poc_op))
|
||||
# Vymyslíme, kdo to bude opravovat
|
||||
poc_opravovatelu = rnd.randint(1, 3)
|
||||
t.opravovatele.set(rnd.sample(organizatori, poc_opravovatelu))
|
||||
|
||||
# Uložíme všechno
|
||||
t.save()
|
||||
temata.append((ci, konec_tematu, t))
|
||||
rocnik_temata.append(temata)
|
||||
letosni_temata.append((zacatek_tematu, konec_tematu, t))
|
||||
rocnik_temata.append(letosni_temata)
|
||||
return rocnik_temata
|
||||
|
||||
|
||||
|
@ -448,84 +456,103 @@ def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori)
|
|||
"netriviální aplikace diferenciálních rovnic", "zadání je vnitřně"
|
||||
"sporné", "nepopsatelně jednoduché", "pokud jste na to nepřišli,"
|
||||
"tak jste fakt hloupí"]
|
||||
k = 0
|
||||
for rocnik in rocniky:
|
||||
k+=1
|
||||
cisla = rocnik_cisla[k-1]
|
||||
temata = rocnik_temata[k-1]
|
||||
for ci in range(len(cisla)):
|
||||
print("Generuji {}-té číslo".format(ci))
|
||||
cislo = cisla[ci-1]
|
||||
mozna_tema_vcn = cislo.cislonode.first_child
|
||||
# kdybyste nad tím někdo taky přemýšleli, tak vcn == VCisleNode :)
|
||||
while mozna_tema_vcn != None:
|
||||
if type(mozna_tema_vcn) != TemaVCisleNode:
|
||||
mozna_tema_vcn = mozna_tema_vcn.succ
|
||||
# Ke každému ročníku si vezmeme příslušná čísla a témata
|
||||
for rocnik, cisla, temata in zip(rocniky, rocnik_cisla, rocnik_temata):
|
||||
# Do každého čísla nagenerujeme ke každému témátku pár úložek
|
||||
for cislo in cisla:
|
||||
print("Generuji úložky do {}-tého čísla".format(cislo.poradi))
|
||||
# Vzorák bude o dvě čísla dál
|
||||
cislo_se_vzorakem = Cislo.objects.filter(
|
||||
rocnik=rocnik,
|
||||
poradi=str(int(cislo.poradi) + 2),
|
||||
)
|
||||
# Pokud není číslo pro vzorák, tak se dá do posledního čísla (i kdyby tam mělo být zadání i řešení...)
|
||||
# Tohle sice umožňuje vygenerovat vzorák do čísla dávno po konci témátka, ale to nám pro jednoduchost nevadí.
|
||||
if len(cislo_se_vzorakem) == 0:
|
||||
cislo_se_vzorakem = cisla[-1]
|
||||
else:
|
||||
cislo_se_vzorakem = cislo_se_vzorakem.first()
|
||||
|
||||
# FIXME: Tenhle generátor dát asi někam jinam
|
||||
def potomci(node):
|
||||
if not isinstance(node, TreeNode):
|
||||
raise ValueError("Typ {} nemá potomky", type(node))
|
||||
current_child = node.first_child
|
||||
while current_child is not None:
|
||||
yield current_child
|
||||
current_child = current_child.succ
|
||||
|
||||
for mozna_tema_node in potomci(cislo.cislonode):
|
||||
if not isinstance(mozna_tema_node, TemaVCisleNode):
|
||||
continue
|
||||
else:
|
||||
tema = mozna_tema_vcn.tema
|
||||
tema_node = mozna_tema_node
|
||||
tema = tema_node.tema
|
||||
|
||||
if not temata[int(tema.kod)-1][1] >= ci+2:
|
||||
mozna_tema_vcn = mozna_tema_vcn.succ
|
||||
# Pokud už témátko skončilo, žádné úložky negenerujeme
|
||||
# FIXME: Bylo by hezčí, kdyby se čísla předávala jako Cislo a ne jako int v té trojici (start, konec, tema)
|
||||
if not temata[int(tema.kod)-1][1] >= int(cislo_se_vzorakem.poradi):
|
||||
continue
|
||||
|
||||
for i in range(1, rnd.randint(1, 4)):
|
||||
poc_op = rnd.randint(1, 4)
|
||||
poc_oboru = rnd.randint(1, 2)
|
||||
p = Uloha.objects.create(
|
||||
# Generujeme 1 až 4 úložky k tomuto témátku do tohoto čísla
|
||||
for kod in range(1, rnd.randint(1, 4)):
|
||||
u = Uloha.objects.create(
|
||||
nazev=": ".join([tema.nazev,
|
||||
"úloha {}.".format(i)]),
|
||||
"úloha {}.".format(kod)]),
|
||||
nadproblem=tema,
|
||||
stav=Problem.STAV_ZADANY,
|
||||
zamereni=tema.zamereni,
|
||||
autor=tema.autor,
|
||||
garant=tema.garant,
|
||||
kod=str(i),
|
||||
kod=str(kod),
|
||||
cislo_zadani=cislo,
|
||||
cislo_reseni=cisla[ci+2-1],
|
||||
cislo_deadline=cisla[ci+2-1],
|
||||
cislo_reseni=cislo_se_vzorakem,
|
||||
cislo_deadline=cislo_se_vzorakem,
|
||||
max_body = rnd.randint(1, 8)
|
||||
)
|
||||
|
||||
p.opravovatele.set(rnd.sample(organizatori, poc_op))
|
||||
poc_opravovatelu = rnd.randint(1, 4)
|
||||
u.opravovatele.set(rnd.sample(organizatori, poc_opravovatelu))
|
||||
|
||||
text_zadani = Text.objects.create(
|
||||
na_web = " ".join(
|
||||
[rnd.choice(sloveso),
|
||||
rnd.choice(koho),
|
||||
rnd.choice(ceho),
|
||||
rnd.choice(jmeno),
|
||||
rnd.choice(kde)]
|
||||
),
|
||||
do_cisla = " ".join(
|
||||
# Samotný obsah následně vzniklého Textu zadání
|
||||
obsah = " ".join(
|
||||
[rnd.choice(sloveso),
|
||||
rnd.choice(koho),
|
||||
rnd.choice(ceho),
|
||||
rnd.choice(jmeno),
|
||||
rnd.choice(kde)]
|
||||
)
|
||||
)
|
||||
text_zadani = Text.objects.create(
|
||||
na_web = obsah,
|
||||
do_cisla = obsah,
|
||||
)
|
||||
zad = TextNode.objects.create(text = text_zadani)
|
||||
uloha_zadani = UlohaZadaniNode.objects.create(uloha=p, first_child = zad)
|
||||
p.ulohazadaninode = uloha_zadani
|
||||
otec_syn(mozna_tema_vcn, uloha_zadani)
|
||||
# TODO dělá se podproblém takto??? TODO
|
||||
uloha_zadani = UlohaZadaniNode.objects.create(uloha=u, first_child = zad)
|
||||
u.ulohazadaninode = uloha_zadani
|
||||
|
||||
# FIXME: Tohle dává zadání vždy jako prvního potomka témátka, spec. se naskládají v opačném pořadí a nemůže mezi nimi vzniknout žádný (orgo-)text
|
||||
otec_syn(tema_node, uloha_zadani)
|
||||
|
||||
# Text vzoráku stejně
|
||||
obsah = rnd.choice(reseni)
|
||||
text_vzoraku = Text.objects.create(
|
||||
na_web = rnd.choice(reseni),
|
||||
do_cisla = rnd.choice(reseni)
|
||||
na_web = obsah,
|
||||
do_cisla = obsah,
|
||||
)
|
||||
vzorak = TextNode.objects.create(text = text_vzoraku)
|
||||
uloha_vzorak = UlohaVzorakNode.objects.create(uloha=p, first_child = vzorak)
|
||||
p.UlohaVzorakNode = uloha_vzorak
|
||||
res_tema_vcn = cisla[ci+2-1].cislonode.first_child
|
||||
while res_tema_vcn.tema != tema:
|
||||
res_tema_vcn = res_tema_vcn.succ
|
||||
otec_syn(res_tema_vcn, uloha_vzorak)
|
||||
uloha_vzorak = UlohaVzorakNode.objects.create(uloha=u, first_child = vzorak)
|
||||
u.UlohaVzorakNode = uloha_vzorak
|
||||
|
||||
# Najdeme správný TemaVCisleNode pro vložení vzoráku
|
||||
res_tema_node = None;
|
||||
for node in potomci(cislo_se_vzorakem.cislonode):
|
||||
if isinstance(node, TemaVCisleNode) and node.tema == tema:
|
||||
res_tema_node = node
|
||||
if res_tema_node is None:
|
||||
raise LookupError("Nenalezen Node pro vložení vzoráku")
|
||||
# FIXME: Stejný problém jako výše: vzoráky se dají na začátek v opačném pořadí.
|
||||
otec_syn(res_tema_node, uloha_vzorak)
|
||||
|
||||
p.save()
|
||||
|
||||
mozna_tema_vcn = mozna_tema_vcn.succ
|
||||
u.save()
|
||||
return
|
||||
|
||||
def gen_novinky(rnd, organizatori):
|
||||
|
@ -604,6 +631,7 @@ def create_test_data(size = 6, rnd = None):
|
|||
rocniky = gen_rocniky(last_rocnik, size)
|
||||
|
||||
# cisla
|
||||
# rocnik_cisla je pole polí čísel (typ Cislo), vnitřní pole odpovídají jednotlivým ročníkům.
|
||||
rocnik_cisla = gen_cisla(rnd, rocniky)
|
||||
|
||||
# generování obyčejných úloh do čísel
|
||||
|
@ -611,6 +639,7 @@ def create_test_data(size = 6, rnd = None):
|
|||
|
||||
# generování témat, zatím v prvních třech číslech po jednom
|
||||
# FIXME: více témat
|
||||
# rocnik_temata je pole polí trojic (první číslo :int, poslední číslo :int, téma:Tema), přičemž každé vnitřní pole odpovídá ročníku a FIXME: je to takhle fuj a když to někdo vidí poprvé, tak je z toho smutný, protože vůbec neví, co se děje a co má čekat.
|
||||
rocnik_temata = gen_temata(rnd, rocniky, rocnik_cisla, organizatori)
|
||||
|
||||
# generování úloh k tématům ve všech číslech
|
||||
|
|
124
seminar/treelib.py
Normal file
124
seminar/treelib.py
Normal file
|
@ -0,0 +1,124 @@
|
|||
from django.core.exceptions import ObjectDoesNotExist
|
||||
# NOTE: node.prev a node.succ jsou implementovány přímo v models.TreeNode
|
||||
# TODO: Všechny tyto funkce se naivně spoléhají na to, že jako parametr dostanou nějaký TreeNode (některé možná zvládnou i None)
|
||||
|
||||
# Slouží k debugování pro rychlé získání představy o podobě podstromu pod tímto TreeNode.
|
||||
def print_tree(node,indent=0):
|
||||
# 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,node, node.id))
|
||||
if node.first_child:
|
||||
print_tree(node.first_child, indent=indent+2)
|
||||
if node.succ:
|
||||
print_tree(node.succ, indent=indent)
|
||||
|
||||
# Django je trošku hloupé, takže node.prev nevrací None, ale hází django.core.exceptions.ObjectDoesNotExist
|
||||
def safe_pred(node):
|
||||
try:
|
||||
return node.prev
|
||||
except ObjectDoesNotExist:
|
||||
return None
|
||||
|
||||
# A to samé pro .father_of_first
|
||||
def safe_father_of_first(node):
|
||||
return node.prev
|
||||
except ObjectDoesNotExist:
|
||||
return None
|
||||
|
||||
## Rodinné vztahy
|
||||
def get_parent(node):
|
||||
# Nejdřív získáme prvního potomka...
|
||||
while safe_pred(node) is not None:
|
||||
node = safe_pred(node)
|
||||
# ... a z prvního potomka umíme najít rodiče
|
||||
return safe_father_of_first(node)
|
||||
|
||||
# Obecný next: další Node v "the-right-order" pořadí (já, pak potomci, pak sousedé)
|
||||
def general_next(node):
|
||||
# Máme potomka?
|
||||
if node.first_child is not None:
|
||||
return node.first_child
|
||||
# Nemáme potomka.
|
||||
# Chceme najít následníka sebe, nebo některého (toho nejblíž příbuzného) z našich předků (tatínka, dědečka, ...)
|
||||
while node.succ is None:
|
||||
node = get_parent(node)
|
||||
if node is None:
|
||||
return None # žádný z předků nemá následníka, takže žádny vrchol nenásleduje.
|
||||
return node.succ
|
||||
|
||||
def last_brother(node):
|
||||
while node.succ is not None:
|
||||
node = node.succ
|
||||
return node
|
||||
|
||||
def general_prev(node):
|
||||
# Předchůdce je buď rekurzivně poslední potomek předchůdce, nebo náš otec.
|
||||
# Otce vyřešíme nejdřív:
|
||||
if safe_pred(node) is None:
|
||||
return safe_father_of_first(node)
|
||||
pred = safe_pred(node)
|
||||
while pred.first_child is not None:
|
||||
pred = last_brother(pred.first_child)
|
||||
# pred nyní nemá žádné potomky, takže je to poslední rekurzivní potomek původního předchůdce
|
||||
return pred
|
||||
|
||||
# Generátor bratrů
|
||||
# Generátor potomků níže spoléhá na to, že se tohle dá volat i s parametrem None.
|
||||
def all_brothers(node):
|
||||
current = node
|
||||
while current is not None:
|
||||
yield current
|
||||
current = current.succ
|
||||
|
||||
# Generátor potomků
|
||||
def all_children(node):
|
||||
brothers = all_brothers(node.first_child)
|
||||
for br in brothers:
|
||||
yield br
|
||||
|
||||
# Generátor následníků v "the-right-order"
|
||||
|
||||
## Filtrační hledání
|
||||
# Najdi dalšího bratra nějakého typu, nebo None.
|
||||
# hledá i podtřídy, i.e. get_next_brother_of_type(neco, TreeNode) je prostě succ.
|
||||
def get_next_brother_of_type(current, type):
|
||||
pass
|
||||
def get_prev_brother_of_type(current, type):
|
||||
pass
|
||||
|
||||
# Totéž pro "the-right-order" pořadí
|
||||
def get_next_node_of_type(current, type):
|
||||
pass
|
||||
def get_next_node_of_type(current, type):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
# Editace stromu:
|
||||
def create_node_after(predecessor, type, **kwargs):
|
||||
pass
|
||||
|
||||
# Vyrábí prvního syna, ostatní nalepí za (existují-li)
|
||||
def create_child(parent, type, **kwargs):
|
||||
pass
|
||||
|
||||
def create_node_before(some, arguments, but, i, dont, know, which, yet):
|
||||
pass
|
||||
# Tohle bude hell.
|
||||
|
||||
# ValueError, pokud je (aspoň) jeden parametr None
|
||||
def swap(node, other):
|
||||
pass
|
||||
|
||||
def swap_pred(node):
|
||||
pass
|
||||
def swap_succ(node):
|
||||
pass
|
||||
|
||||
# Rotace stromu
|
||||
# Dokumentace viz wiki:
|
||||
# (lower bude jednoduchá rotace, ne mega, existence jednoduché rotace mi došla až po nakreslení obrázku)
|
||||
def raise_node(node):
|
||||
pass
|
||||
def lower_node(node):
|
||||
pass
|
|
@ -109,6 +109,7 @@ urlpatterns = [
|
|||
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('autocomplete/problem/odevzdatelny',views.OdevzdatelnyProblemAutocomplete.as_view(), name='autocomplete_problem_odevzdatelny'),
|
||||
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'),
|
||||
|
@ -118,6 +119,7 @@ urlpatterns = [
|
|||
|
||||
|
||||
path('temp/add_solution', views.AddSolutionView.as_view(),name='seminar_vloz_reseni'),
|
||||
path('temp/submit_solution', views.SubmitSolutionView.as_view(),name='seminar_nahraj_reseni'),
|
||||
|
||||
path('', views.TitulniStranaView.as_view(), name='titulni_strana'),
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ import datetime
|
|||
from django.contrib.auth.decorators import user_passes_test
|
||||
from html.parser import HTMLParser
|
||||
|
||||
import seminar.models as m
|
||||
|
||||
staff_member_required = user_passes_test(lambda u: u.is_staff)
|
||||
|
||||
class FirstTagParser(HTMLParser):
|
||||
|
@ -43,7 +45,6 @@ def from_roman(rom):
|
|||
|
||||
|
||||
def seznam_problemu():
|
||||
from .models import Problem, Resitel, Rocnik, Reseni, Cislo
|
||||
problemy = []
|
||||
|
||||
# Pomocna fce k formatovani problemovych hlasek
|
||||
|
@ -65,27 +66,26 @@ def seznam_problemu():
|
|||
|
||||
# Duplicita jmen
|
||||
jmena = {}
|
||||
for r in Resitel.objects.all():
|
||||
for r in m.Resitel.objects.all():
|
||||
j = r.plne_jmeno()
|
||||
if j not in jmena:
|
||||
jmena[j] = []
|
||||
jmena[j].append(r)
|
||||
for j in jmena:
|
||||
if len(jmena[j]) > 1:
|
||||
prb(Resitel, u'Duplicitní jméno "%s"' % (j, ), jmena[j])
|
||||
prb(m.Resitel, u'Duplicitní jméno "%s"' % (j, ), jmena[j])
|
||||
|
||||
# Data maturity a narození
|
||||
for r in Resitel.objects.all():
|
||||
for r in m.Resitel.objects.all():
|
||||
if not r.rok_maturity:
|
||||
prb(Resitel, u'Neznámý rok maturity', [r])
|
||||
prb(m.Resitel, u'Neznámý rok maturity', [r])
|
||||
if r.rok_maturity and (r.rok_maturity < 1990 or r.rok_maturity > datetime.date.today().year + 10):
|
||||
prb(Resitel, u'Podezřelé datum maturity', [r])
|
||||
prb(m.Resitel, u'Podezřelé datum maturity', [r])
|
||||
if r.datum_narozeni and (r.datum_narozeni.year < 1970 or r.datum_narozeni.year > datetime.date.today().year - 12):
|
||||
prb(Resitel, u'Podezřelé datum narození', [r])
|
||||
prb(m.Resitel, u'Podezřelé datum narození', [r])
|
||||
# if not r.email:
|
||||
# prb(Resitel, u'Neznámý email', [r])
|
||||
|
||||
return problemy
|
||||
|
||||
|
||||
|
||||
|
|
2
seminar/views/__init__.py
Normal file
2
seminar/views/__init__.py
Normal file
|
@ -0,0 +1,2 @@
|
|||
from .views_all import *
|
||||
from .autocomplete import *
|
63
seminar/views/autocomplete.py
Normal file
63
seminar/views/autocomplete.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
from dal import autocomplete
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
import seminar.models as m
|
||||
from .helpers import LoginRequiredAjaxMixin
|
||||
|
||||
class SkolaAutocomplete(autocomplete.Select2QuerySetView):
|
||||
def get_queryset(self):
|
||||
# Don't forget to filter out results depending on the visitor !
|
||||
qs = m.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 ResitelAutocomplete(LoginRequiredAjaxMixin,autocomplete.Select2QuerySetView):
|
||||
def get_queryset(self):
|
||||
qs = m.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
|
||||
|
||||
class OdevzdatelnyProblemAutocomplete(autocomplete.Select2QuerySetView):
|
||||
def get_queryset(self):
|
||||
nastaveni = get_object_or_404(m.Nastaveni)
|
||||
rocnik = nastaveni.aktualni_rocnik
|
||||
temata = m.Tema.objects.filter(rocnik=rocnik, stav=m.Problem.STAV_ZADANY)
|
||||
ulohy = m.Uloha.objects.filter(cislo_deadline__rocnik = rocnik)
|
||||
ulohy.union(temata)
|
||||
qs = ulohy
|
||||
if self.q:
|
||||
qs = qs.filter(
|
||||
Q(nazev__startswith=self.q))
|
||||
return qs
|
||||
|
||||
# Ceka na autocomplete v3
|
||||
# class OrganizatorAutocomplete(autocomplete.Select2QuerySetView):
|
||||
# def get_queryset(self):
|
||||
# if not self.request.user.is_authenticated():
|
||||
# return Organizator.objects.none()
|
||||
#
|
||||
# qs = aktivniOrganizatori()
|
||||
#
|
||||
# if self.q:
|
||||
# if self.q[0] == "!":
|
||||
# qs = Organizator.objects.all()
|
||||
# query = self.q[1:]
|
||||
# else:
|
||||
# query = self.q
|
||||
# qs = qs.filter(
|
||||
# Q(prezdivka__isstartswith=query)|
|
||||
# Q(user__first_name__isstartswith=query)|
|
||||
# Q(user__last_name__isstartswith=query))
|
||||
#
|
||||
# return qs
|
8
seminar/views/helpers.py
Normal file
8
seminar/views/helpers.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from dal import autocomplete
|
||||
|
||||
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)
|
91
seminar/views/utils.py
Normal file
91
seminar/views/utils.py
Normal file
|
@ -0,0 +1,91 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import datetime
|
||||
from django.contrib.auth.decorators import user_passes_test
|
||||
from html.parser import HTMLParser
|
||||
|
||||
import seminar.models as m
|
||||
|
||||
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:
|
||||
if i not in d:
|
||||
d[i] = 0
|
||||
d[i] += 1
|
||||
return d
|
||||
|
||||
|
||||
roman_numerals = zip((1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1),
|
||||
('M', 'CM', 'D', 'CD','C', 'XC','L','XL','X','IX','V','IV','I'))
|
||||
|
||||
def roman(num):
|
||||
res = ""
|
||||
for i, n in roman_numerals:
|
||||
res += n * (num // i)
|
||||
num %= i
|
||||
return res
|
||||
|
||||
def from_roman(rom):
|
||||
if not rom:
|
||||
return 0
|
||||
for i, n in roman_numerals:
|
||||
if rom.upper().startswith(n):
|
||||
return i + from_roman(rom[len(n):])
|
||||
raise Exception('Invalid roman numeral: "%s"', rom)
|
||||
|
||||
|
||||
def seznam_problemu():
|
||||
problemy = []
|
||||
|
||||
# Pomocna fce k formatovani problemovych hlasek
|
||||
def prb(cls, msg, objs=None):
|
||||
s = u'<b>%s:</b> %s' % (cls.__name__, msg)
|
||||
if objs:
|
||||
s += u' ['
|
||||
for o in objs:
|
||||
try:
|
||||
url = o.admin_url()
|
||||
except:
|
||||
url = None
|
||||
if url:
|
||||
s += u'<a href="%s">%s</a>, ' % (url, o.pk, )
|
||||
else:
|
||||
s += u'%s, ' % (o.pk, )
|
||||
s = s[:-2] + u']'
|
||||
problemy.append(s)
|
||||
|
||||
# Duplicita jmen
|
||||
jmena = {}
|
||||
for r in m.Resitel.objects.all():
|
||||
j = r.plne_jmeno()
|
||||
if j not in jmena:
|
||||
jmena[j] = []
|
||||
jmena[j].append(r)
|
||||
for j in jmena:
|
||||
if len(jmena[j]) > 1:
|
||||
prb(m.Resitel, u'Duplicitní jméno "%s"' % (j, ), jmena[j])
|
||||
|
||||
# Data maturity a narození
|
||||
for r in m.Resitel.objects.all():
|
||||
if not r.rok_maturity:
|
||||
prb(m.Resitel, u'Neznámý rok maturity', [r])
|
||||
if r.rok_maturity and (r.rok_maturity < 1990 or r.rok_maturity > datetime.date.today().year + 10):
|
||||
prb(m.Resitel, u'Podezřelé datum maturity', [r])
|
||||
if r.datum_narozeni and (r.datum_narozeni.year < 1970 or r.datum_narozeni.year > datetime.date.today().year - 12):
|
||||
prb(m.Resitel, u'Podezřelé datum narození', [r])
|
||||
# if not r.email:
|
||||
# prb(Resitel, u'Neznámý email', [r])
|
||||
|
||||
return problemy
|
||||
|
||||
|
|
@ -9,20 +9,19 @@ from django.utils.translation import ugettext as _
|
|||
from django.http import Http404,HttpResponseBadRequest,HttpResponseRedirect
|
||||
from django.db.models import Q, Sum, Count
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
from django.views.generic.edit import FormView
|
||||
from django.views.generic.edit import FormView, CreateView
|
||||
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
|
||||
|
||||
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 seminar.models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici, Pohadka, Tema, Clanek, Osoba, Skola # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci
|
||||
#from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva
|
||||
from . import utils
|
||||
from seminar import utils
|
||||
from .unicodecsv import UnicodeWriter
|
||||
from .forms import PrihlaskaForm, LoginForm, ProfileEditForm
|
||||
from seminar.forms import PrihlaskaForm, LoginForm, ProfileEditForm
|
||||
import seminar.forms as f
|
||||
|
||||
from datetime import timedelta, date, datetime
|
||||
|
@ -1223,6 +1222,42 @@ class AddSolutionView(LoginRequiredMixin, FormView):
|
|||
form_class = f.VlozReseniForm
|
||||
success_url = '/'
|
||||
|
||||
class SubmitSolutionView(LoginRequiredMixin, CreateView):
|
||||
model = s.Reseni
|
||||
template_name = 'seminar/nahraj_reseni.html'
|
||||
form_class = f.NahrajReseniForm
|
||||
success_url = '/'
|
||||
|
||||
def get_context_data(self,**kwargs):
|
||||
data = super().get_context_data(**kwargs)
|
||||
if self.request.POST:
|
||||
data['prilohy'] = f.ReseniSPrilohamiFormSet(self.request.POST,self.request.FILES)
|
||||
else:
|
||||
data['prilohy'] = f.ReseniSPrilohamiFormSet()
|
||||
return data
|
||||
|
||||
# FIXME prepsat tak, aby form_valid se volalo jen tehdy, kdyz je form i formset validni
|
||||
# Inspirace: https://stackoverflow.com/questions/41599809/using-a-django-filefield-in-an-inline-formset
|
||||
def form_valid(self,form):
|
||||
context = self.get_context_data()
|
||||
prilohy = context['prilohy']
|
||||
if not prilohy.is_valid():
|
||||
return super().form_invalid(form)
|
||||
with transaction.atomic():
|
||||
self.object = form.save()
|
||||
self.object.resitele.add(Resitel.objects.get(osoba__user = self.request.user))
|
||||
self.object.cas_doruceni = timezone.now()
|
||||
self.object.forma = s.Reseni.FORMA_UPLOAD
|
||||
self.object.save()
|
||||
|
||||
prilohy.instance = self.object
|
||||
prilohy.save()
|
||||
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
|
||||
|
||||
|
||||
def resetPasswordView(request):
|
||||
pass
|
||||
|
||||
|
@ -1384,58 +1419,9 @@ def prihlaskaView(request):
|
|||
|
||||
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):
|
||||
# if not self.request.user.is_authenticated():
|
||||
# return Organizator.objects.none()
|
||||
#
|
||||
# qs = aktivniOrganizatori()
|
||||
#
|
||||
# if self.q:
|
||||
# if self.q[0] == "!":
|
||||
# qs = Organizator.objects.all()
|
||||
# query = self.q[1:]
|
||||
# else:
|
||||
# query = self.q
|
||||
# qs = qs.filter(
|
||||
# Q(prezdivka__isstartswith=query)|
|
||||
# Q(user__first_name__isstartswith=query)|
|
||||
# Q(user__last_name__isstartswith=query))
|
||||
#
|
||||
# return qs
|
||||
|
||||
|
||||
# FIXME: Tohle asi vlastně vůbec nepatří do aplikace 'seminar'
|
||||
class LoginView(auth_views.LoginView):
|
1
sitetree_new.json
Normal file
1
sitetree_new.json
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue