Merge branch 'data_migrations' of gimli.ms.mff.cuni.cz:/akce/mam/git/mamweb into data_migrations
This commit is contained in:
commit
aaa84a5f00
26 changed files with 1350 additions and 392 deletions
BIN
Schema_new.dia
BIN
Schema_new.dia
Binary file not shown.
|
@ -2,6 +2,7 @@ from django import forms
|
||||||
from dal import autocomplete
|
from dal import autocomplete
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.forms.models import inlineformset_factory
|
||||||
|
|
||||||
from .models import Skola, Resitel, Osoba, Problem
|
from .models import Skola, Resitel, Osoba, Problem
|
||||||
import seminar.models as m
|
import seminar.models as m
|
||||||
|
@ -252,6 +253,25 @@ class VlozReseniForm(forms.Form):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
#self.fields['favorite_color'] = forms.ChoiceField(choices=[(color.id, color.name) for color in Resitel.objects.all()])
|
#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,
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,8 @@ class Command(BaseCommand):
|
||||||
if not options['no_migrate']:
|
if not options['no_migrate']:
|
||||||
call_command('migrate', no_input=True)
|
call_command('migrate', no_input=True)
|
||||||
self.stdout.write('Vytvarim uzivatele "admin" (heslo "admin") a pseudo-nahodna data ...')
|
self.stdout.write('Vytvarim uzivatele "admin" (heslo "admin") a pseudo-nahodna data ...')
|
||||||
create_test_data(size=8)
|
create_test_data(size=5)
|
||||||
|
# menší počet ročníků, aby se zrychlilo generování dat a bylo dost úloh
|
||||||
self.stdout.write('Vytvoreno {} uzivatelu, {} skol, {} resitelu, {} rocniku, {} cisel,'
|
self.stdout.write('Vytvoreno {} uzivatelu, {} skol, {} resitelu, {} rocniku, {} cisel,'
|
||||||
' {} problemu, {} reseni.'.format(User.objects.count(), Skola.objects.count(),
|
' {} problemu, {} reseni.'.format(User.objects.count(), Skola.objects.count(),
|
||||||
Resitel.objects.count(), Rocnik.objects.count(), Cislo.objects.count(),
|
Resitel.objects.count(), Rocnik.objects.count(), Cislo.objects.count(),
|
||||||
|
|
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'),
|
||||||
|
),
|
||||||
|
]
|
27
seminar/migrations/0078_otistenereseninode.py
Normal file
27
seminar/migrations/0078_otistenereseninode.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# Generated by Django 2.2.9 on 2020-03-18 23:59
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('seminar', '0077_auto_20200318_2146'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='OtisteneReseniNode',
|
||||||
|
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')),
|
||||||
|
('reseni', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='seminar.Reseni', verbose_name='reseni')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Otištěné řešení (Node)',
|
||||||
|
'verbose_name_plural': 'Otištěná řešení (Node)',
|
||||||
|
'db_table': 'seminar_nodes_otistene_reseni',
|
||||||
|
},
|
||||||
|
bases=('seminar.treenode',),
|
||||||
|
),
|
||||||
|
]
|
18
seminar/migrations/0079_clanek_resitelsky.py
Normal file
18
seminar/migrations/0079_clanek_resitelsky.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.2.9 on 2020-03-25 19:24
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('seminar', '0078_otistenereseninode'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='clanek',
|
||||||
|
name='resitelsky',
|
||||||
|
field=models.BooleanField(default=True, verbose_name='Jde o řešitelský článek?'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -304,8 +304,9 @@ class Resitel(SeminarModelBase):
|
||||||
return sum(h.body for h in list(vsechna_hodnoceni))
|
return sum(h.body for h in list(vsechna_hodnoceni))
|
||||||
|
|
||||||
|
|
||||||
def get_titul(self):
|
def get_titul(self, celkove_body=None):
|
||||||
"Vrati titul"
|
"Vrati titul"
|
||||||
|
if celkove_body is None:
|
||||||
celkove_body = self.vsechny_body()
|
celkove_body = self.vsechny_body()
|
||||||
|
|
||||||
if celkove_body < 10:
|
if celkove_body < 10:
|
||||||
|
@ -753,6 +754,8 @@ class Clanek(Problem):
|
||||||
cislo = models.ForeignKey(Cislo, verbose_name='číslo', blank=True, null=True,
|
cislo = models.ForeignKey(Cislo, verbose_name='číslo', blank=True, null=True,
|
||||||
on_delete=models.PROTECT)
|
on_delete=models.PROTECT)
|
||||||
|
|
||||||
|
resitelsky = models.BooleanField('Jde o řešitelský článek?', default=True)
|
||||||
|
|
||||||
# má OneToOneField s:
|
# má OneToOneField s:
|
||||||
# ClanekNode
|
# ClanekNode
|
||||||
|
|
||||||
|
@ -898,6 +901,9 @@ class Reseni(SeminarModelBase):
|
||||||
# má OneToOneField s:
|
# má OneToOneField s:
|
||||||
# Konfera
|
# Konfera
|
||||||
|
|
||||||
|
# má ForeignKey s:
|
||||||
|
# Hodnoceni
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{}({}): {}({})".format(self.resitele.first(),len(self.resitele.all()), self.problem.first() ,len(self.problem.all()))
|
return "{}({}): {}({})".format(self.resitele.first(),len(self.resitele.all()), self.problem.first() ,len(self.problem.all()))
|
||||||
# NOTE: Potenciální DB HOG (bez select_related)
|
# NOTE: Potenciální DB HOG (bez select_related)
|
||||||
|
@ -920,10 +926,10 @@ class Hodnoceni(SeminarModelBase):
|
||||||
|
|
||||||
|
|
||||||
body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body',
|
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',
|
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)
|
reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
@ -935,17 +941,16 @@ class Hodnoceni(SeminarModelBase):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## FIXME: Budeme řešit později, pokud to bude potřeba.
|
def aux_generate_filename(self, filename):
|
||||||
#def aux_generate_filename(self, filename):
|
"""Pomocná funkce generující ošetřený název souboru v adresáři s datem"""
|
||||||
# """Pomocná funkce generující ošetřený název souboru v adresáři s datem"""
|
clean = get_valid_filename(
|
||||||
# clean = get_valid_filename(
|
unidecode(filename.replace('/', '-').replace('\0', ''))
|
||||||
# unidecode(filename.replace('/', '-').replace('\0', ''))
|
)
|
||||||
# )
|
datedir = timezone.now().strftime('%Y-%m')
|
||||||
# datedir = timezone.now().strftime('%Y-%m')
|
fname = "{}_{}".format(
|
||||||
# fname = "%s_%s" % (
|
timezone.now().strftime('%Y-%m-%d-%H:%M'),
|
||||||
# timezone.now().strftime('%Y-%m-%d-%H:%M'),
|
clean)
|
||||||
# clean)
|
return os.path.join(datedir, fname)
|
||||||
# return os.path.join(datedir, fname)
|
|
||||||
|
|
||||||
# Django neumí jednoduše serializovat partial nebo třídu s __call__
|
# Django neumí jednoduše serializovat partial nebo třídu s __call__
|
||||||
# (https://docs.djangoproject.com/en/1.8/topics/migrations/),
|
# (https://docs.djangoproject.com/en/1.8/topics/migrations/),
|
||||||
|
@ -989,8 +994,11 @@ class PrilohaReseni(SeminarModelBase):
|
||||||
poznamka = models.TextField('neveřejná poznámka', blank=True,
|
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')
|
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):
|
def __str__(self):
|
||||||
return self.soubor
|
return str(self.soubor)
|
||||||
|
|
||||||
|
|
||||||
class Pohadka(SeminarModelBase):
|
class Pohadka(SeminarModelBase):
|
||||||
|
@ -1228,6 +1236,8 @@ class Obrazek(SeminarModelBase):
|
||||||
help_text = 'Černobílá verze obrázku do čísla',
|
help_text = 'Černobílá verze obrázku do čísla',
|
||||||
upload_to = 'obrazky/%Y/%m/%d/', blank=True, null=True)
|
upload_to = 'obrazky/%Y/%m/%d/', blank=True, null=True)
|
||||||
|
|
||||||
|
# TODO placement hint - chci ho tady / pred textem / za textem
|
||||||
|
|
||||||
class TreeNode(PolymorphicModel):
|
class TreeNode(PolymorphicModel):
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "seminar_nodes_treenode"
|
db_table = "seminar_nodes_treenode"
|
||||||
|
@ -1242,6 +1252,7 @@ class TreeNode(PolymorphicModel):
|
||||||
on_delete = models.SET_NULL, # Vrcholy s null kořenem jsou sirotci bez ročníku
|
on_delete = models.SET_NULL, # Vrcholy s null kořenem jsou sirotci bez ročníku
|
||||||
verbose_name="kořen stromu")
|
verbose_name="kořen stromu")
|
||||||
first_child = models.ForeignKey('TreeNode',
|
first_child = models.ForeignKey('TreeNode',
|
||||||
|
related_name='father_of_first',
|
||||||
null = True,
|
null = True,
|
||||||
blank = True,
|
blank = True,
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
|
@ -1263,15 +1274,6 @@ class TreeNode(PolymorphicModel):
|
||||||
verbose_name = "Srolovatelné",
|
verbose_name = "Srolovatelné",
|
||||||
help_text = "Bude na stránce témátka možnost tuto položku skrýt")
|
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
|
def getOdkazStr(self): # String na rozcestník
|
||||||
return self.first_child.getOdkazStr()
|
return self.first_child.getOdkazStr()
|
||||||
|
|
||||||
|
@ -1339,6 +1341,7 @@ class MezicisloNode(TreeNode):
|
||||||
verbose_name = 'Mezičíslo (Node)'
|
verbose_name = 'Mezičíslo (Node)'
|
||||||
verbose_name_plural = 'Mezičísla (Node)'
|
verbose_name_plural = 'Mezičísla (Node)'
|
||||||
|
|
||||||
|
# TODO: Využít TreeLib
|
||||||
def aktualizuj_nazev(self):
|
def aktualizuj_nazev(self):
|
||||||
if self.prev:
|
if self.prev:
|
||||||
if (self.prev.get_real_instance_class() != CisloNode and
|
if (self.prev.get_real_instance_class() != CisloNode and
|
||||||
|
@ -1469,6 +1472,34 @@ class TextNode(TreeNode):
|
||||||
def getOdkazStr(self):
|
def getOdkazStr(self):
|
||||||
return str(self.text)
|
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')
|
||||||
|
|
||||||
|
def aktualizuj_nazev(self):
|
||||||
|
self.nazev = "CastNode: "+str(self.nadpis)
|
||||||
|
|
||||||
|
def getOdkazStr(self):
|
||||||
|
return str(self.nadpis)
|
||||||
|
|
||||||
|
class OtisteneReseniNode(TreeNode):
|
||||||
|
class Meta:
|
||||||
|
db_table = 'seminar_nodes_otistene_reseni'
|
||||||
|
verbose_name = 'Otištěné řešení (Node)'
|
||||||
|
verbose_name_plural = 'Otištěná řešení (Node)'
|
||||||
|
reseni = models.ForeignKey(Reseni,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
verbose_name = 'reseni')
|
||||||
|
|
||||||
|
def aktualizuj_nazev(self):
|
||||||
|
self.nazev = "OtisteneReseniNode: "+str(self.reseni)
|
||||||
|
|
||||||
|
def getOdkazStr(self):
|
||||||
|
return str(self.reseni)
|
||||||
|
|
||||||
## FIXME: Logiku přesunout do views.
|
## FIXME: Logiku přesunout do views.
|
||||||
#class VysledkyBase(SeminarModelBase):
|
#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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
{% extends "seminar/archiv/base_cisla.html" %}
|
{% extends "seminar/archiv/base_cisla.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
<h1>
|
<h1>
|
||||||
{% block nadpis1a %}{% block nadpis1b %}
|
{% block nadpis1a %}{% block nadpis1b %}
|
||||||
Číslo {{ cislo }}
|
Číslo {{ cislo }}
|
||||||
|
@ -48,11 +49,12 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if cislo.verejna_vysledkovka %}
|
{% if cislo.verejna_vysledkovka %}
|
||||||
<h2>Výsledkovka</h2>
|
<h2>Výsledkovka ({% now "jS F Y H:i" %})</h2>
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if user.is_staff %}
|
{% if user.is_staff %}
|
||||||
<div class='mam-org-only'>
|
<div class='mam-org-only'>
|
||||||
<h2>Výsledkovka (neveřejná)</h2>
|
<h2>Výsledkovka (neveřejná, {% now "jS F Y H:i:s" %})</h2>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@ -61,26 +63,25 @@
|
||||||
<tr class='border-b'>
|
<tr class='border-b'>
|
||||||
<th class='border-r'>#
|
<th class='border-r'>#
|
||||||
<th class='border-r'>Jméno
|
<th class='border-r'>Jméno
|
||||||
{# problémy by měly být veřejné, když je veřejná výsledkovka #}
|
|
||||||
{% for p in problemy %}
|
{% for p in problemy %}
|
||||||
<th class='border-r'><a href="{{ p.verejne_url }}">{{ p.kod_v_rocniku }}</a>
|
<th class='border-r'><a href="{{ p.verejne_url }}">{{ p.kod_v_rocniku }}</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<th class='border-r'>Za číslo</sup>
|
<th class='border-r'>Za číslo</sup>
|
||||||
<th class='border-r'>Za ročník
|
<th class='border-r'>Za ročník
|
||||||
<th class='border-r'>Odjakživa
|
<th class='border-r'>Odjakživa
|
||||||
{% for rv in vysledkovka %}
|
{% for rv in radky_vysledkovky %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %}
|
<td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %}
|
||||||
<th class='border-r'>
|
<th class='border-r'>
|
||||||
{% if rv.titul %}
|
{% if rv.titul is not '' %}
|
||||||
{{ rv.titul }}<sup>MM</sup>
|
{{ rv.titul }}<sup>MM</sup>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ rv.resitel.plne_jmeno }}
|
{{ rv.resitel.osoba.plne_jmeno }}
|
||||||
{% for b in rv.body_ulohy %}
|
{% for b in rv.body_problemy_sezn %}
|
||||||
<td class='border-r'>{{ b }}
|
<td class='border-r'>{{ b }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<td class='border-r'>{{ rv.body_cislo }}
|
<td class='border-r'>{{ rv.body_cislo }}
|
||||||
<td class='border-r'><b>{{ rv.body_celkem_rocnik }}</b>
|
<td class='border-r'><b>{{ rv.body_rocnik }}</b>
|
||||||
<td class='border-r'>{{ rv.body_celkem_odjakziva }}
|
<td class='border-r'>{{ rv.body_celkem_odjakziva }}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -91,6 +92,8 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
Čas: {% now "jS F Y H:i:s" %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
||||||
|
|
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 %}
|
10
seminar/templates/seminar/treenode.html
Normal file
10
seminar/templates/seminar/treenode.html
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{% extends "seminar/archiv/base_ulohy.html" %}
|
||||||
|
|
||||||
|
{% load comments %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{%with obj=tnldata depth=1 template_name="seminar/treenode_recursive.html" %}
|
||||||
|
{%include template_name%}
|
||||||
|
{%endwith%}
|
||||||
|
{% endblock content %}
|
28
seminar/templates/seminar/treenode_recursive.html
Normal file
28
seminar/templates/seminar/treenode_recursive.html
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{% load treenodes %}
|
||||||
|
{# <b>{{depth}}</b> #}
|
||||||
|
<div>
|
||||||
|
{% if obj.node|isRocnik %}
|
||||||
|
<h{{depth}}> Ročník {{obj.node.rocnik}} </h{{depth}}>
|
||||||
|
{% elif obj.node|isCislo %}
|
||||||
|
<h{{depth}}> Číslo {{obj.node.cislo}} </h{{depth}}>
|
||||||
|
{% elif obj.node|isTemaVCisle %}
|
||||||
|
<h{{depth}}> Téma {{obj.node.tema.nazev}} </h{{depth}}>
|
||||||
|
{% elif obj.node|isUlohaZadani %}
|
||||||
|
<h{{depth}}>Úloha {{obj.node.uloha.kod_v_rocniku}} ({{obj.node.uloha.max_body}} b)</h{{depth}}>
|
||||||
|
{% elif obj.node|isUlohaVzorak %}
|
||||||
|
<h{{depth}}>Řešení: {{obj.node.uloha.kod_v_rocniku}}</h{{depth}}>
|
||||||
|
{% elif obj.node|isText %}
|
||||||
|
{{obj.node.text.na_web}}
|
||||||
|
{% else %}
|
||||||
|
Objekt jiného typu {{obj.node}}
|
||||||
|
{% endif %}
|
||||||
|
{%if obj.children %}
|
||||||
|
<div>
|
||||||
|
{%for ch in obj.children %}
|
||||||
|
{%with obj=ch depth=depth|add:"1" template_name="seminar/treenode_recursive.html" %}
|
||||||
|
{%include template_name%}
|
||||||
|
{%endwith%}
|
||||||
|
{%endfor%}
|
||||||
|
</div>
|
||||||
|
{%endif%}
|
||||||
|
</div>
|
49
seminar/templatetags/treenodes.py
Normal file
49
seminar/templatetags/treenodes.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
from django import template
|
||||||
|
import seminar.models as m
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def isRocnik(value):
|
||||||
|
return isinstance(value, m.RocnikNode)
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def isCislo(value):
|
||||||
|
return isinstance(value, m.CisloNode)
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def isCast(value):
|
||||||
|
return isinstance(value, m.CastNode)
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def isText(value):
|
||||||
|
return isinstance(value, m.TextNode)
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def isTemaVCisle(value):
|
||||||
|
return isinstance(value, m.TemaVCisleNode)
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def isKonfera(value):
|
||||||
|
return isinstance(value, m.KonferaNode)
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def isClanek(value):
|
||||||
|
return isinstance(value, m.ClanekNode)
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def isUlohaVzorak(value):
|
||||||
|
return isinstance(value, m.UlohaVzorakNode)
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def isUlohaZadani(value):
|
||||||
|
return isinstance(value, m.UlohaZadaniNode)
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def isPohadka(value):
|
||||||
|
return isinstance(value, m.PohadkaNode)
|
||||||
|
|
||||||
|
#@register.filter
|
||||||
|
#def isOtisteneReseniNode(value):
|
||||||
|
# return isinstance(value, m.OtisteneReseniNode)
|
||||||
|
|
|
@ -9,7 +9,7 @@ from django.db import transaction
|
||||||
import unidecode
|
import unidecode
|
||||||
import logging
|
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.flatpages.models import FlatPage
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
|
@ -19,6 +19,13 @@ zlinska = None # tohle bude speciální škola, které později dodáme kontaktn
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# testuje unikátnost vygenerovaného jména
|
||||||
|
def __unikatni_jmeno(osoby, jmeno, prijmeni):
|
||||||
|
for os in osoby:
|
||||||
|
if os.jmeno == jmeno and os.prijmeni == prijmeni:
|
||||||
|
return 0
|
||||||
|
else: return 1
|
||||||
|
|
||||||
def gen_osoby(rnd, size):
|
def gen_osoby(rnd, size):
|
||||||
logger.info('Generuji osoby (size={})...'.format(size))
|
logger.info('Generuji osoby (size={})...'.format(size))
|
||||||
|
|
||||||
|
@ -48,6 +55,19 @@ def gen_osoby(rnd, size):
|
||||||
pohlavi = rnd.randint(0,1)
|
pohlavi = rnd.randint(0,1)
|
||||||
jmeno = rnd.choice([jmena_m, jmena_f][pohlavi])
|
jmeno = rnd.choice([jmena_m, jmena_f][pohlavi])
|
||||||
prijmeni = rnd.choice([prijmeni_m, prijmeni_f][pohlavi])
|
prijmeni = rnd.choice([prijmeni_m, prijmeni_f][pohlavi])
|
||||||
|
pokusy = 0
|
||||||
|
max_pokusy = 120*size
|
||||||
|
while (not __unikatni_jmeno and pokusy < max_pokusy):
|
||||||
|
# pokud jméno a příjmení není unikátní, zkoušíme generovat nová
|
||||||
|
# do daného limitu (abychom se nezacyklili do nekonečna při málo jménech a příjmeních
|
||||||
|
# ze kterých se generuje)
|
||||||
|
jmeno = rnd.choice([jmena_m, jmena_f][pohlavi])
|
||||||
|
prijmeni = rnd.choice([prijmeni_m, prijmeni_f][pohlavi])
|
||||||
|
pokusy = pokusy + 1
|
||||||
|
if pokusy >= max_pokusy:
|
||||||
|
print("Chyba, na danou velikost testovacích dat příliš málo možných"
|
||||||
|
" jmen a příjmení")
|
||||||
|
exit
|
||||||
prezdivka = rnd.choice(prezdivky)
|
prezdivka = rnd.choice(prezdivky)
|
||||||
email = "@".join([unidecode.unidecode(jmeno), rnd.choice(domain)])
|
email = "@".join([unidecode.unidecode(jmeno), rnd.choice(domain)])
|
||||||
telefon = "".join([str(rnd.choice([k for k in range(10)])) for i in range(9)])
|
telefon = "".join([str(rnd.choice([k for k in range(10)])) for i in range(9)])
|
||||||
|
@ -180,11 +200,12 @@ def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size)
|
||||||
k = 0
|
k = 0
|
||||||
for rocnik in rocniky:
|
for rocnik in rocniky:
|
||||||
k+=1
|
k+=1
|
||||||
|
print("Generuji {}. číslo.".format(k))
|
||||||
cisla = rocnik_cisla[k-1]
|
cisla = rocnik_cisla[k-1]
|
||||||
for ci in range(3, len(cisla)+1): # pro všechna čísla
|
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ů
|
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))
|
poc_res = rnd.randint(round(resitele_size/8), round(resitele_size/4))
|
||||||
# dané číslo řeší něco mezi osminou a tříčtvrtinou všech řešitelů
|
# dané číslo řeší něco mezi osminou a čtvrtinou všech řešitelů
|
||||||
# (náhodná hausnumera, možno změnit)
|
# (náhodná hausnumera, možno změnit)
|
||||||
# účelem je, aby se řešení generovala z menší množiny řešitelů a tedy
|
# úč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
|
# bylo více řešení od jednoho řešitele daného čísla
|
||||||
|
@ -243,9 +264,10 @@ def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size)
|
||||||
p.save()
|
p.save()
|
||||||
|
|
||||||
# generování řešení
|
# generování řešení
|
||||||
poc_reseni = rnd.randint(size // 2, size * 2)
|
poc_reseni = rnd.randint(poc_res, poc_res * 4)
|
||||||
# generujeme náhodný počet řešení
|
# generujeme náhodný počet řešení vzhledem k počtu řešitelů čísla
|
||||||
for ri in range(poc_reseni):
|
for ri in range(poc_reseni):
|
||||||
|
#print("Generuji {}-té řešení".format(ri))
|
||||||
if rnd.randint(1, 10) == 6:
|
if rnd.randint(1, 10) == 6:
|
||||||
# cca desetina řešení od více řešitelů
|
# cca desetina řešení od více řešitelů
|
||||||
res_vyber = rnd.sample(resitele_cisla, rnd.randint(2, 5))
|
res_vyber = rnd.sample(resitele_cisla, rnd.randint(2, 5))
|
||||||
|
@ -383,17 +405,15 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori):
|
||||||
co = ["téma", "záření", "stavení", "jiskření", "jelito",
|
co = ["téma", "záření", "stavení", "jiskření", "jelito",
|
||||||
"drama", "kuře", "moře", "klání", "proudění", "čekání"]
|
"drama", "kuře", "moře", "klání", "proudění", "čekání"]
|
||||||
poc_oboru = rnd.randint(1, 2)
|
poc_oboru = rnd.randint(1, 2)
|
||||||
poc_op = rnd.randint(1, 3)
|
|
||||||
|
|
||||||
rocnik_temata = []
|
rocnik_temata = []
|
||||||
k = 0
|
# 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 in rocniky:
|
for rocnik, cisla in zip(rocniky, rocnik_cisla):
|
||||||
k+=1
|
kod = 1
|
||||||
n = 0
|
letosni_temata = []
|
||||||
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
|
||||||
cisla = rocnik_cisla[k-1]
|
for zacatek_tematu in range(1, 3):
|
||||||
for ci in range(1, 3):
|
# Vygenerujeme téma
|
||||||
n+=1
|
|
||||||
t = Tema.objects.create(
|
t = Tema.objects.create(
|
||||||
# atributy třídy Problem
|
# atributy třídy Problem
|
||||||
nazev=" ".join([rnd.choice(jake), rnd.choice(co)]),
|
nazev=" ".join([rnd.choice(jake), rnd.choice(co)]),
|
||||||
|
@ -401,22 +421,32 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori):
|
||||||
zamereni=rnd.sample(["M", "F", "I", "O", "B"], poc_oboru),
|
zamereni=rnd.sample(["M", "F", "I", "O", "B"], poc_oboru),
|
||||||
autor=rnd.choice(organizatori),
|
autor=rnd.choice(organizatori),
|
||||||
garant=rnd.choice(organizatori),
|
garant=rnd.choice(organizatori),
|
||||||
kod=str(n),
|
kod=str(kod),
|
||||||
# atributy třídy Téma
|
# atributy třídy Téma
|
||||||
tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0],
|
tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0],
|
||||||
rocnik=rocnik,
|
rocnik=rocnik,
|
||||||
abstrakt = "Abstrakt tematka {}".format(n)
|
abstrakt = "Abstrakt tematka {}".format(kod)
|
||||||
)
|
)
|
||||||
konec_tematu = min(rnd.randint(ci, 7), len(cisla))
|
kod += 1
|
||||||
for i in range(ci, konec_tematu+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)
|
node = TemaVCisleNode.objects.create(tema = t)
|
||||||
|
# FIXME: Není to off-by-one?
|
||||||
otec = cisla[i-1].cislonode
|
otec = cisla[i-1].cislonode
|
||||||
otec_syn(otec, node)
|
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()
|
t.save()
|
||||||
temata.append((ci, konec_tematu, t))
|
letosni_temata.append((zacatek_tematu, konec_tematu, t))
|
||||||
rocnik_temata.append(temata)
|
rocnik_temata.append(letosni_temata)
|
||||||
return rocnik_temata
|
return rocnik_temata
|
||||||
|
|
||||||
|
|
||||||
|
@ -441,81 +471,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ě"
|
"netriviální aplikace diferenciálních rovnic", "zadání je vnitřně"
|
||||||
"sporné", "nepopsatelně jednoduché", "pokud jste na to nepřišli,"
|
"sporné", "nepopsatelně jednoduché", "pokud jste na to nepřišli,"
|
||||||
"tak jste fakt hloupí"]
|
"tak jste fakt hloupí"]
|
||||||
k = 0
|
# Ke každému ročníku si vezmeme příslušná čísla a témata
|
||||||
for rocnik in rocniky:
|
for rocnik, cisla, temata in zip(rocniky, rocnik_cisla, rocnik_temata):
|
||||||
k+=1
|
# Do každého čísla nagenerujeme ke každému témátku pár úložek
|
||||||
cisla = rocnik_cisla[k-1]
|
for cislo in cisla:
|
||||||
temata = rocnik_temata[k-1]
|
print("Generuji úložky do {}-tého čísla".format(cislo.poradi))
|
||||||
for ci in range(len(cisla)):
|
# Vzorák bude o dvě čísla dál
|
||||||
cislo = cisla[ci-1]
|
cislo_se_vzorakem = Cislo.objects.filter(
|
||||||
mozna_tema_vcn = cislo.cislonode.first_child
|
rocnik=rocnik,
|
||||||
while mozna_tema_vcn != None:
|
poradi=str(int(cislo.poradi) + 2),
|
||||||
if type(mozna_tema_vcn) != TemaVCisleNode:
|
)
|
||||||
mozna_tema_vcn = mozna_tema_vcn.succ
|
# 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í...)
|
||||||
continue
|
# 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:
|
else:
|
||||||
tema = mozna_tema_vcn.tema
|
cislo_se_vzorakem = cislo_se_vzorakem.first()
|
||||||
|
|
||||||
if not temata[int(tema.kod)-1][1] >= ci+2:
|
# FIXME: Tenhle generátor dát asi někam jinam
|
||||||
mozna_tema_vcn = mozna_tema_vcn.succ
|
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
|
||||||
|
tema_node = mozna_tema_node
|
||||||
|
tema = tema_node.tema
|
||||||
|
|
||||||
|
# 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
|
continue
|
||||||
|
|
||||||
for i in range(1, rnd.randint(1, 4)):
|
# Generujeme 1 až 4 úložky k tomuto témátku do tohoto čísla
|
||||||
poc_op = rnd.randint(1, 4)
|
for kod in range(1, rnd.randint(1, 4)):
|
||||||
poc_oboru = rnd.randint(1, 2)
|
u = Uloha.objects.create(
|
||||||
p = Uloha.objects.create(
|
|
||||||
nazev=": ".join([tema.nazev,
|
nazev=": ".join([tema.nazev,
|
||||||
"úloha {}.".format(i)]),
|
"úloha {}.".format(kod)]),
|
||||||
nadproblem=tema,
|
nadproblem=tema,
|
||||||
stav=Problem.STAV_ZADANY,
|
stav=Problem.STAV_ZADANY,
|
||||||
zamereni=tema.zamereni,
|
zamereni=tema.zamereni,
|
||||||
autor=tema.autor,
|
autor=tema.autor,
|
||||||
garant=tema.garant,
|
garant=tema.garant,
|
||||||
kod=str(i),
|
kod=str(kod),
|
||||||
cislo_zadani=cislo,
|
cislo_zadani=cislo,
|
||||||
cislo_reseni=cisla[ci+2-1],
|
cislo_reseni=cislo_se_vzorakem,
|
||||||
cislo_deadline=cisla[ci+2-1],
|
cislo_deadline=cislo_se_vzorakem,
|
||||||
max_body = rnd.randint(1, 8)
|
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(
|
# Samotný obsah následně vzniklého Textu zadání
|
||||||
na_web = " ".join(
|
obsah = " ".join(
|
||||||
[rnd.choice(sloveso),
|
|
||||||
rnd.choice(koho),
|
|
||||||
rnd.choice(ceho),
|
|
||||||
rnd.choice(jmeno),
|
|
||||||
rnd.choice(kde)]
|
|
||||||
),
|
|
||||||
do_cisla = " ".join(
|
|
||||||
[rnd.choice(sloveso),
|
[rnd.choice(sloveso),
|
||||||
rnd.choice(koho),
|
rnd.choice(koho),
|
||||||
rnd.choice(ceho),
|
rnd.choice(ceho),
|
||||||
rnd.choice(jmeno),
|
rnd.choice(jmeno),
|
||||||
rnd.choice(kde)]
|
rnd.choice(kde)]
|
||||||
)
|
)
|
||||||
|
text_zadani = Text.objects.create(
|
||||||
|
na_web = obsah,
|
||||||
|
do_cisla = obsah,
|
||||||
)
|
)
|
||||||
zad = TextNode.objects.create(text = text_zadani)
|
zad = TextNode.objects.create(text = text_zadani)
|
||||||
uloha_zadani = UlohaZadaniNode.objects.create(uloha=p, first_child = zad)
|
uloha_zadani = UlohaZadaniNode.objects.create(uloha=u, first_child = zad)
|
||||||
p.ulohazadaninode = uloha_zadani
|
u.ulohazadaninode = uloha_zadani
|
||||||
otec_syn(mozna_tema_vcn, uloha_zadani) # TODO dělá se podproblém takto??? TODO
|
|
||||||
|
|
||||||
|
# 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(
|
text_vzoraku = Text.objects.create(
|
||||||
na_web = rnd.choice(reseni),
|
na_web = obsah,
|
||||||
do_cisla = rnd.choice(reseni)
|
do_cisla = obsah,
|
||||||
)
|
)
|
||||||
vzorak = TextNode.objects.create(text = text_vzoraku)
|
vzorak = TextNode.objects.create(text = text_vzoraku)
|
||||||
uloha_vzorak = UlohaVzorakNode.objects.create(uloha=p, first_child = vzorak)
|
uloha_vzorak = UlohaVzorakNode.objects.create(uloha=u, first_child = vzorak)
|
||||||
p.UlohaVzorakNode = uloha_vzorak
|
u.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)
|
|
||||||
|
|
||||||
p.save()
|
# 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)
|
||||||
|
|
||||||
mozna_tema_vcn = mozna_tema_vcn.succ
|
u.save()
|
||||||
return
|
return
|
||||||
|
|
||||||
def gen_novinky(rnd, organizatori):
|
def gen_novinky(rnd, organizatori):
|
||||||
|
@ -594,6 +646,7 @@ def create_test_data(size = 6, rnd = None):
|
||||||
rocniky = gen_rocniky(last_rocnik, size)
|
rocniky = gen_rocniky(last_rocnik, size)
|
||||||
|
|
||||||
# cisla
|
# 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)
|
rocnik_cisla = gen_cisla(rnd, rocniky)
|
||||||
|
|
||||||
# generování obyčejných úloh do čísel
|
# generování obyčejných úloh do čísel
|
||||||
|
@ -601,6 +654,7 @@ def create_test_data(size = 6, rnd = None):
|
||||||
|
|
||||||
# generování témat, zatím v prvních třech číslech po jednom
|
# generování témat, zatím v prvních třech číslech po jednom
|
||||||
# FIXME: více témat
|
# 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)
|
rocnik_temata = gen_temata(rnd, rocniky, rocnik_cisla, organizatori)
|
||||||
|
|
||||||
# generování úloh k tématům ve všech číslech
|
# generování úloh k tématům ve všech číslech
|
||||||
|
|
286
seminar/treelib.py
Normal file
286
seminar/treelib.py
Normal file
|
@ -0,0 +1,286 @@
|
||||||
|
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)
|
||||||
|
# TODO: Chceme, aby všechno nějak zvládlo None jako parametr.
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
def first_brother(node):
|
||||||
|
if node is None:
|
||||||
|
return None
|
||||||
|
brother = node
|
||||||
|
while safe_pred(brother) is not None:
|
||||||
|
brother = safe_pred(brother)
|
||||||
|
return brother
|
||||||
|
|
||||||
|
# A to samé pro .father_of_first
|
||||||
|
def safe_father_of_first(node):
|
||||||
|
first = first_brother(node)
|
||||||
|
try:
|
||||||
|
return first.father_of_first
|
||||||
|
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 pravých bratrů (konkrétně sebe a následujících potomků)
|
||||||
|
# Generátor potomků níže spoléhá na to, že se tohle dá volat i s parametrem None.
|
||||||
|
def me_and_right_brothers(node):
|
||||||
|
current = node
|
||||||
|
while current is not None:
|
||||||
|
yield current
|
||||||
|
current = current.succ
|
||||||
|
|
||||||
|
def right_brothers(node):
|
||||||
|
generator = me_and_right_brothers(node.succ)
|
||||||
|
for item in generator:
|
||||||
|
yield item
|
||||||
|
|
||||||
|
# Generátor všech sourozenců (vč. sám sebe)
|
||||||
|
def all_brothers(node):
|
||||||
|
# Najdeme prvního bratra
|
||||||
|
fb = first_brother(node)
|
||||||
|
marb = me_and_right_brothers(fb)
|
||||||
|
for cur in marb:
|
||||||
|
yield cur
|
||||||
|
|
||||||
|
def all_proper_brothers(node):
|
||||||
|
all = all_brothers(node)
|
||||||
|
for br in all:
|
||||||
|
if br is node:
|
||||||
|
continue
|
||||||
|
yield br
|
||||||
|
|
||||||
|
# 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"
|
||||||
|
# Bez tohoto vrcholu
|
||||||
|
def all_following(node):
|
||||||
|
current = general_next(node)
|
||||||
|
while current is not None:
|
||||||
|
yield current
|
||||||
|
current = general_next(current)
|
||||||
|
|
||||||
|
## 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(node, type):
|
||||||
|
for current in right_brothers(node):
|
||||||
|
if isinstance(current, type):
|
||||||
|
return current
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_prev_brother_of_type(node, type):
|
||||||
|
# Na tohle není rozumný generátor, ani ho asi nechceme, prostě to implementujeme cyklem.
|
||||||
|
current = node
|
||||||
|
while safe_pred(current) is not None:
|
||||||
|
current = safe_pred(current)
|
||||||
|
if isinstance(current, type):
|
||||||
|
return current
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Totéž pro "the-right-order" pořadí
|
||||||
|
def get_next_node_of_type(node, type):
|
||||||
|
for cur in all_folowing(node):
|
||||||
|
if isinstance(cur, type):
|
||||||
|
return cur
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_prev_node_of_type(node, type):
|
||||||
|
# Na tohle není rozumný generátor, ani ho asi nechceme, prostě to implementujeme cyklem.
|
||||||
|
current = node
|
||||||
|
while general_prev(current) is not None:
|
||||||
|
current = general_prev(current)
|
||||||
|
if isinstance(current, type):
|
||||||
|
return current
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Editace stromu:
|
||||||
|
def create_node_after(predecessor, type, **kwargs):
|
||||||
|
new_node = type.objects.create(**kwargs)
|
||||||
|
new_node.save()
|
||||||
|
succ = predecessor.succ
|
||||||
|
predecessor.succ = new_node
|
||||||
|
predecessor.save()
|
||||||
|
new_node.succ = succ
|
||||||
|
new_node.save()
|
||||||
|
|
||||||
|
# Vyrábí prvního syna, ostatní nalepí za (existují-li)
|
||||||
|
def create_child(parent, type, **kwargs):
|
||||||
|
new_node = type.objects.create(**kwargs)
|
||||||
|
new_node.save()
|
||||||
|
orig_child = parent.first_child
|
||||||
|
parent.first_child = new_node
|
||||||
|
parent.save()
|
||||||
|
if orig_child is not None:
|
||||||
|
# Přidáme původního prvního syna jako potomka nového vrcholu
|
||||||
|
new_node.succ = orig_child
|
||||||
|
new_node.save()
|
||||||
|
|
||||||
|
def create_node_before(successor, type, **kwargs):
|
||||||
|
if safe_pred(successor) is not None:
|
||||||
|
# Easy: přidáme za předchůdce
|
||||||
|
create_node_after(successor.prev, type, **kwargs)
|
||||||
|
# Nemáme předchůdce, jsme tedy první z bratrů. Máme otce?
|
||||||
|
if safe_father_of_first(successor) is not None:
|
||||||
|
# Ano -> Easy: vyrobíme nového potomka
|
||||||
|
# NOTE: Tohle je možná trošku abuse implementace výše, ale to nevadí moc...
|
||||||
|
create_child(successor.father_of_first, type, **kwargs)
|
||||||
|
# Teď už easy: Jsme sirotci, takže se vyrobíme a našeho následníka si přidáme jako succ
|
||||||
|
new = type.objects.create(**kwargs)
|
||||||
|
new.succ = successor
|
||||||
|
new.save()
|
||||||
|
|
||||||
|
|
||||||
|
# ValueError, pokud je (aspoň) jeden parametr None
|
||||||
|
def swap(node, other):
|
||||||
|
raise NotImplementedError("YAGNI (You aren't gonna need it).")
|
||||||
|
|
||||||
|
# Exception, kterou některé metody při špatném použití mohou házet
|
||||||
|
# Hlavní důvod je možnost informovat o selhání, aby se příslušný problém dal zobrazit na frontendu,
|
||||||
|
class TreeLibError(RuntimeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def swap_pred(node):
|
||||||
|
if node is None:
|
||||||
|
raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.")
|
||||||
|
pred = safe_pred(node)
|
||||||
|
if pred is None:
|
||||||
|
raise TreeLibError("Nelze posunout vlevo, není tam žádný další uzel.")
|
||||||
|
pre_pred = safe_pred(pred)
|
||||||
|
succ = node.succ
|
||||||
|
|
||||||
|
if pre_pred is not None:
|
||||||
|
pre_pred.succ = node
|
||||||
|
pre_pred.save()
|
||||||
|
node.succ = pred
|
||||||
|
node.save()
|
||||||
|
pred.succ = succ
|
||||||
|
pred.save()
|
||||||
|
|
||||||
|
def swap_succ(node):
|
||||||
|
if node is None:
|
||||||
|
raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.")
|
||||||
|
succ = node.succ
|
||||||
|
if succ is None:
|
||||||
|
raise TreeLibError("Nelze posunout vpravo, není tam žádný další uzel")
|
||||||
|
pred = safe_pred(node)
|
||||||
|
post_succ = succ.succ
|
||||||
|
|
||||||
|
if pred is not None:
|
||||||
|
pred.succ = succ
|
||||||
|
pred.save()
|
||||||
|
succ.succ = node
|
||||||
|
succ.save()
|
||||||
|
node.succ = post_succ
|
||||||
|
node.save()
|
||||||
|
|
||||||
|
# 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):
|
||||||
|
if node is None:
|
||||||
|
raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.")
|
||||||
|
# Pojmenování viz WIKI (as of 2020-03-19 01:33:44 GMT+1)
|
||||||
|
# FIXME: Velmi naivní, chybí error checky
|
||||||
|
D = node
|
||||||
|
C = get_parent(D)
|
||||||
|
E = C.succ
|
||||||
|
subtree4_head = D.first_child
|
||||||
|
subtree4_tail = last_brother(subtree4_head)
|
||||||
|
subtree3P_head = D.succ
|
||||||
|
subtree3L_head = C.first_child
|
||||||
|
subtree3L_tail = safe_pred(D)
|
||||||
|
|
||||||
|
# Prostor pro motlitbu...
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Amen.
|
||||||
|
C.succ = D
|
||||||
|
C.save()
|
||||||
|
D.succ = E
|
||||||
|
D.save()
|
||||||
|
subtree3L_tail.succ = None
|
||||||
|
subtree3L_tail.save()
|
||||||
|
subtree4_tail.succ = subtree3P.head
|
||||||
|
subtree4_tail.save()
|
||||||
|
|
||||||
|
# To by mělo být všechno...
|
||||||
|
|
||||||
|
def lower_node(node):
|
||||||
|
if node is None:
|
||||||
|
raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.")
|
||||||
|
# Pojmenování viz WIKI (as of 2020-03-19 01:33:44 GMT+1)
|
||||||
|
# FIXME: Velmi naivní, chybí error checky
|
||||||
|
C = node
|
||||||
|
D = C.succ
|
||||||
|
B = safe_pred(C)
|
||||||
|
subtree2_head = B.first_child
|
||||||
|
subtree2_tail = last_brother(subtree2_head)
|
||||||
|
|
||||||
|
# Prostor pro motlitbu...
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Amen.
|
||||||
|
B.succ = D
|
||||||
|
B.save()
|
||||||
|
subtree2_tail.succ = C
|
||||||
|
subtree2_tail.save()
|
||||||
|
|
||||||
|
# To by mělo být všechno...
|
|
@ -8,8 +8,8 @@ from django.contrib.auth import views as auth_views
|
||||||
staff_member_required = user_passes_test(lambda u: u.is_staff)
|
staff_member_required = user_passes_test(lambda u: u.is_staff)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('aktualni/temata/', views.TemataRozcestnikView),
|
# path('aktualni/temata/', views.TemataRozcestnikView),
|
||||||
path('<int:rocnik>/t<int:tematko>/', views.TematkoView),
|
# path('<int:rocnik>/t<int:tematko>/', views.TematkoView),
|
||||||
|
|
||||||
# REDIRECTy
|
# REDIRECTy
|
||||||
path('jak-resit/', RedirectView.as_view(url='/co-je-MaM/jak-resit/')),
|
path('jak-resit/', RedirectView.as_view(url='/co-je-MaM/jak-resit/')),
|
||||||
|
@ -25,6 +25,7 @@ urlpatterns = [
|
||||||
path('rocnik/<int:rocnik>/', views.RocnikView.as_view(), name='seminar_rocnik'),
|
path('rocnik/<int:rocnik>/', views.RocnikView.as_view(), name='seminar_rocnik'),
|
||||||
path('cislo/<int:rocnik>.<int:cislo>/', views.CisloView.as_view(), name='seminar_cislo'), # odkomentované jenom kvůli testování archivu
|
path('cislo/<int:rocnik>.<int:cislo>/', views.CisloView.as_view(), name='seminar_cislo'), # odkomentované jenom kvůli testování archivu
|
||||||
path('problem/<int:pk>/', views.ProblemView.as_view(), name='seminar_problem'),
|
path('problem/<int:pk>/', views.ProblemView.as_view(), name='seminar_problem'),
|
||||||
|
path('treenode/<int:pk>/', views.TreeNodeView.as_view(), name='seminar_treenode'),
|
||||||
#path('problem/(?P<pk>\d+)/(?P<prispevek>\d+)/', views.PrispevekView.as_view(), name='seminar_problem_prispevek'),
|
#path('problem/(?P<pk>\d+)/(?P<prispevek>\d+)/', views.PrispevekView.as_view(), name='seminar_problem_prispevek'),
|
||||||
|
|
||||||
# Soustredeni
|
# Soustredeni
|
||||||
|
@ -59,8 +60,8 @@ urlpatterns = [
|
||||||
),
|
),
|
||||||
|
|
||||||
# Zadani
|
# Zadani
|
||||||
path('zadani/aktualni/', views.AktualniZadaniView, name='seminar_aktualni_zadani'),
|
path('zadani/aktualni/', views.AktualniZadaniView.as_view(), name='seminar_aktualni_zadani'),
|
||||||
path('zadani/temata/', views.ZadaniTemataView, name='seminar_temata'),
|
# path('zadani/temata/', views.ZadaniTemataView, name='seminar_temata'),
|
||||||
#path('zadani/vysledkova-listina/', views.ZadaniAktualniVysledkovkaView, name='seminar_vysledky'),
|
#path('zadani/vysledkova-listina/', views.ZadaniAktualniVysledkovkaView, name='seminar_vysledky'),
|
||||||
path('stare-novinky/', views.StareNovinkyView.as_view(), name='stare_novinky'),
|
path('stare-novinky/', views.StareNovinkyView.as_view(), name='stare_novinky'),
|
||||||
|
|
||||||
|
@ -107,8 +108,6 @@ urlpatterns = [
|
||||||
path('auth/login/', views.LoginView.as_view(), name='login'),
|
path('auth/login/', views.LoginView.as_view(), name='login'),
|
||||||
path('auth/logout/', views.LogoutView.as_view(), name='logout'),
|
path('auth/logout/', views.LogoutView.as_view(), name='logout'),
|
||||||
path('auth/resitel/', views.ResitelView.as_view(), name='seminar_resitel'),
|
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/reset_password/', views.PasswordResetView.as_view(), name='reset_password'),
|
||||||
path('auth/change_password/', views.PasswordChangeView.as_view(), name='change_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_done/', views.PasswordResetDoneView.as_view(), name='reset_password_done'),
|
||||||
|
@ -116,8 +115,13 @@ urlpatterns = [
|
||||||
path('auth/reset_password_complete/', views.PasswordResetCompleteView.as_view(), name='reset_password_complete'),
|
path('auth/reset_password_complete/', views.PasswordResetCompleteView.as_view(), name='reset_password_complete'),
|
||||||
path('auth/resitel_edit', views.resitelEditView, name='seminar_resitel_edit'),
|
path('auth/resitel_edit', views.resitelEditView, name='seminar_resitel_edit'),
|
||||||
|
|
||||||
|
# Autocomplete
|
||||||
|
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('temp/add_solution', views.AddSolutionView.as_view(),name='seminar_vloz_reseni'),
|
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'),
|
path('', views.TitulniStranaView.as_view(), name='titulni_strana'),
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@ import datetime
|
||||||
from django.contrib.auth.decorators import user_passes_test
|
from django.contrib.auth.decorators import user_passes_test
|
||||||
from html.parser import HTMLParser
|
from html.parser import HTMLParser
|
||||||
|
|
||||||
|
import seminar.models as m
|
||||||
|
|
||||||
staff_member_required = user_passes_test(lambda u: u.is_staff)
|
staff_member_required = user_passes_test(lambda u: u.is_staff)
|
||||||
|
|
||||||
class FirstTagParser(HTMLParser):
|
class FirstTagParser(HTMLParser):
|
||||||
|
@ -43,7 +45,6 @@ def from_roman(rom):
|
||||||
|
|
||||||
|
|
||||||
def seznam_problemu():
|
def seznam_problemu():
|
||||||
from .models import Problem, Resitel, Rocnik, Reseni, Cislo
|
|
||||||
problemy = []
|
problemy = []
|
||||||
|
|
||||||
# Pomocna fce k formatovani problemovych hlasek
|
# Pomocna fce k formatovani problemovych hlasek
|
||||||
|
@ -65,27 +66,26 @@ def seznam_problemu():
|
||||||
|
|
||||||
# Duplicita jmen
|
# Duplicita jmen
|
||||||
jmena = {}
|
jmena = {}
|
||||||
for r in Resitel.objects.all():
|
for r in m.Resitel.objects.all():
|
||||||
j = r.plne_jmeno()
|
j = r.plne_jmeno()
|
||||||
if j not in jmena:
|
if j not in jmena:
|
||||||
jmena[j] = []
|
jmena[j] = []
|
||||||
jmena[j].append(r)
|
jmena[j].append(r)
|
||||||
for j in jmena:
|
for j in jmena:
|
||||||
if len(jmena[j]) > 1:
|
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í
|
# Data maturity a narození
|
||||||
for r in Resitel.objects.all():
|
for r in m.Resitel.objects.all():
|
||||||
if not r.rok_maturity:
|
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):
|
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):
|
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:
|
# if not r.email:
|
||||||
# prb(Resitel, u'Neznámý email', [r])
|
# prb(Resitel, u'Neznámý email', [r])
|
||||||
|
|
||||||
return problemy
|
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.http import Http404,HttpResponseBadRequest,HttpResponseRedirect
|
||||||
from django.db.models import Q, Sum, Count
|
from django.db.models import Q, Sum, Count
|
||||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
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 authenticate, login, get_user_model, logout
|
||||||
from django.contrib.auth import views as auth_views
|
from django.contrib.auth import views as auth_views
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from dal import autocomplete
|
|
||||||
|
|
||||||
import seminar.models as s
|
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 .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva
|
||||||
from . import utils
|
from seminar import utils,treelib
|
||||||
from .unicodecsv import UnicodeWriter
|
from .unicodecsv import UnicodeWriter
|
||||||
from .forms import PrihlaskaForm, LoginForm, ProfileEditForm
|
from seminar.forms import PrihlaskaForm, LoginForm, ProfileEditForm
|
||||||
import seminar.forms as f
|
import seminar.forms as f
|
||||||
|
|
||||||
from datetime import timedelta, date, datetime
|
from datetime import timedelta, date, datetime
|
||||||
|
@ -84,131 +83,106 @@ class ObalkovaniView(generic.ListView):
|
||||||
context['cislo'] = self.cislo
|
context['cislo'] = self.cislo
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
class TNLData(object):
|
||||||
|
def __init__(self,anode):
|
||||||
|
self.node = anode
|
||||||
|
self.children = []
|
||||||
|
|
||||||
|
|
||||||
|
def treenode_strom_na_seznamy(node):
|
||||||
|
out = TNLData(node)
|
||||||
|
for ch in treelib.all_children(node):
|
||||||
|
outitem = treenode_strom_na_seznamy(ch)
|
||||||
|
out.children.append(outitem)
|
||||||
|
return out
|
||||||
|
|
||||||
def AktualniZadaniView(request):
|
class TreeNodeView(generic.DetailView):
|
||||||
|
model = s.TreeNode
|
||||||
|
template_name = 'seminar/treenode.html'
|
||||||
|
|
||||||
|
def get_context_data(self,**kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context['tnldata'] = treenode_strom_na_seznamy(self.object)
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class AktualniZadaniView(TreeNodeView):
|
||||||
|
def get_object(self):
|
||||||
nastaveni = get_object_or_404(Nastaveni)
|
nastaveni = get_object_or_404(Nastaveni)
|
||||||
|
return nastaveni.aktualni_cislo.cislonode
|
||||||
|
|
||||||
|
def get_context_data(self,**kwargs):
|
||||||
|
nastaveni = get_object_or_404(Nastaveni)
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
verejne = nastaveni.aktualni_cislo.verejne()
|
verejne = nastaveni.aktualni_cislo.verejne()
|
||||||
problemy = Problem.objects.filter(cislo_zadani=nastaveni.aktualni_cislo).filter(stav = 'zadany')
|
context['verejne'] = verejne
|
||||||
ulohy = problemy.filter(typ = 'uloha').order_by('kod')
|
return context
|
||||||
serialy = problemy.filter(typ = 'serial').order_by('kod')
|
|
||||||
jednorazove_problemy = [ulohy, serialy]
|
|
||||||
return render(request, 'seminar/zadani/AktualniZadani.html',
|
|
||||||
{'nastaveni': nastaveni,
|
|
||||||
'jednorazove_problemy': jednorazove_problemy,
|
|
||||||
'temata': verejna_temata(nastaveni.aktualni_rocnik),
|
|
||||||
'verejne': verejne,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def ZadaniTemataView(request):
|
|
||||||
nastaveni = get_object_or_404(Nastaveni)
|
|
||||||
temata = verejna_temata(nastaveni.aktualni_rocnik)
|
|
||||||
for t in temata:
|
|
||||||
if request.user.is_staff:
|
|
||||||
t.prispevky = t.prispevek_set.filter(problem=t)
|
|
||||||
else:
|
|
||||||
t.prispevky = t.prispevek_set.filter(problem=t, zverejnit=True)
|
|
||||||
return render(request, 'seminar/zadani/Temata.html',
|
|
||||||
{
|
|
||||||
'temata': temata,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# 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 ZadaniTemataView(request):
|
||||||
|
# nastaveni = get_object_or_404(Nastaveni)
|
||||||
|
# temata = verejna_temata(nastaveni.aktualni_rocnik)
|
||||||
|
# for t in temata:
|
||||||
|
# if request.user.is_staff:
|
||||||
|
# t.prispevky = t.prispevek_set.filter(problem=t)
|
||||||
|
# else:
|
||||||
|
# t.prispevky = t.prispevek_set.filter(problem=t, zverejnit=True)
|
||||||
|
# return render(request, 'seminar/zadani/Temata.html',
|
||||||
|
# {
|
||||||
|
# 'temata': temata,
|
||||||
|
# }
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#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):
|
#def ZadaniAktualniVysledkovkaView(request):
|
||||||
# nastaveni = get_object_or_404(Nastaveni)
|
# nastaveni = get_object_or_404(Nastaveni)
|
||||||
|
@ -429,27 +403,34 @@ def sloupec_s_poradim(seznam_s_body):
|
||||||
aktualni_poradi = aktualni_poradi + velikost_skupiny
|
aktualni_poradi = aktualni_poradi + velikost_skupiny
|
||||||
return sloupec_s_poradim
|
return sloupec_s_poradim
|
||||||
|
|
||||||
# spočítá součet bodů získaných daným řešitelem za zadaný problém a všechny jeho podproblémy
|
## 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):
|
#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
|
# # sečteme body za daný problém přes všechna řešení daného problému
|
||||||
# od daného řešitele
|
# # od daného řešitele
|
||||||
reseni_resitele = problem.hodnoceni_set.filter(reseni__resitele=resitel,
|
# reseni_resitele = s.Reseni_Resitele.objects.filter(resitele=resitel)
|
||||||
cislo_body=cislo)
|
# hodnoceni_resitele = problem.hodnoceni.filter(reseni__in=reseni_resitele,
|
||||||
# XXX chyba na řádku výše - řešení může mít více řešitelů, asi chceme contains
|
# cislo_body=cislo)
|
||||||
# nebo in
|
# # XXX chyba na řádku výše - řešení může mít více řešitelů, asi chceme contains
|
||||||
for r in reseni_resitele:
|
# # nebo in
|
||||||
soucet += r.body
|
# for r in hodnoceni_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
|
||||||
|
|
||||||
# a přičteme k tomu hodnocení všech podproblémů
|
## spočítá součet všech bodů ze všech podproblémů daného problému daného řešitele
|
||||||
for p in problem.podproblem.all():
|
#def body_resitele_problemu_v_cisle(problem, resitel, cislo):
|
||||||
# i přes jméno by to měla být množina jeho podproblémů
|
# # probably FIXED: nezohledňuje číslo, do kterého se body počítají
|
||||||
soucet += __soucet_resitele_problemu(p, resitel, soucet)
|
# return __soucet_resitele_problemu(problem, resitel, cislo, 0)
|
||||||
return soucet
|
|
||||||
|
|
||||||
# spočítá součet všech bodů ze všech podproblémů daného problému daného řešitele
|
# pro daný problém vrátí jeho nejvyšší nadproblém
|
||||||
def body_resitele_problemu_v_cisle(problem, resitel, cislo):
|
def hlavni_problem(problem):
|
||||||
# probably FIXED: nezohledňuje číslo, do kterého se body počítají
|
while not(problem.nadproblem == None):
|
||||||
return __soucet_resitele_problemu(problem, resitel, cislo, 0)
|
problem = problem.nadproblem
|
||||||
|
return problem
|
||||||
|
|
||||||
# vrátí list všech problémů s body v daném čísle, které již nemají nadproblém
|
# vrátí list všech problémů s body v daném čísle, které již nemají nadproblém
|
||||||
def hlavni_problemy_cisla(cislo):
|
def hlavni_problemy_cisla(cislo):
|
||||||
|
@ -464,9 +445,7 @@ def hlavni_problemy_cisla(cislo):
|
||||||
# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém)
|
# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém)
|
||||||
hlavni_problemy = []
|
hlavni_problemy = []
|
||||||
for p in problemy:
|
for p in problemy:
|
||||||
while not(p.nadproblem == None):
|
hlavni_problemy.append(hlavni_problem(p))
|
||||||
p = p.nadproblem
|
|
||||||
hlavni_problemy.append(p)
|
|
||||||
|
|
||||||
# zunikátnění
|
# zunikátnění
|
||||||
hlavni_problemy_set = set(hlavni_problemy)
|
hlavni_problemy_set = set(hlavni_problemy)
|
||||||
|
@ -475,38 +454,83 @@ def hlavni_problemy_cisla(cislo):
|
||||||
|
|
||||||
return hlavni_problemy
|
return hlavni_problemy
|
||||||
|
|
||||||
def body_resitele_odjakziva(resitel):
|
# vrátí slovník řešitel:body obsahující počty bodů zadaných řešitelů za daný ročník
|
||||||
body = 0
|
# POZOR! Aktuálně počítá jen za posledních 10 let od zadaného ročníku
|
||||||
resitelova_hodnoceni = Hodnoceni.objects.select_related('body').all().filter(reseni_resitele=resitel)
|
def body_resitelu_odjakziva(rocnik, resitele):
|
||||||
# TODO: v radku nahore chceme _in nebo _contains
|
body_odjakziva = {}
|
||||||
for hodnoceni in resitelova_hodnoceni:
|
|
||||||
body = body + hodnoceni.body
|
for r in resitele:
|
||||||
return body
|
body_odjakziva[str(r.id)] = 0
|
||||||
|
# # Body za posledních 10 let je dobrá aproximace pro naše potřeby (výsledkovka
|
||||||
|
# # s aktivními řešiteli)
|
||||||
|
#
|
||||||
|
# body_pred_roky = []
|
||||||
|
# for i in range(0, 10):
|
||||||
|
# body_pred_roky.append(body_resitelu_za_rocnik(rocnik-i, resitele))
|
||||||
|
#
|
||||||
|
# for r in resitele:
|
||||||
|
# for i in range(0,10):
|
||||||
|
# body_odjakziva[str(r.id)] += body_pred_roky[i][str(r.id)]
|
||||||
|
|
||||||
|
|
||||||
|
# Nasledující řešení je sice správné, ale moc pomalé:
|
||||||
|
for res in Reseni.objects.prefetch_related('resitele', 'hodnoceni_set').all():
|
||||||
|
for r in res.resitele.all():
|
||||||
|
# daný řešitel nemusí být v naší podmnožině
|
||||||
|
if r not in resitele: continue
|
||||||
|
|
||||||
|
for hodn in res.hodnoceni_set.all():
|
||||||
|
pricti_body(body_odjakziva, r, hodn.body)
|
||||||
|
return body_odjakziva
|
||||||
|
|
||||||
|
# vrátí slovník řešitel:body obsahující počty bodů zadaných řešitelů za daný ročník
|
||||||
|
def body_resitelu_za_rocnik(rocnik, aktivni_resitele):
|
||||||
|
body_za_rocnik = {}
|
||||||
|
# inicializujeme na 0 pro všechny aktivní řešitele
|
||||||
|
for ar in aktivni_resitele:
|
||||||
|
body_za_rocnik[str(ar.id)] = 0
|
||||||
|
|
||||||
|
# spočítáme body řešitelům přes všechna řešení s hodnocením v daném ročníku
|
||||||
|
reseni = Reseni.objects.prefetch_related('resitele', 'hodnoceni_set').filter(hodnoceni__cislo_body__rocnik=rocnik)
|
||||||
|
for res in reseni:
|
||||||
|
for resitel in res.resitele.all():
|
||||||
|
for hodn in res.hodnoceni_set.all():
|
||||||
|
pricti_body(body_za_rocnik, resitel, hodn.body)
|
||||||
|
return body_za_rocnik
|
||||||
|
|
||||||
|
#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
|
# spočítá součet všech bodů řešitele za dané číslo
|
||||||
def body_resitele_v_cisle(resitel, cislo):
|
#def body_resitele_v_cisle(resitel, cislo):
|
||||||
hlavni_problemy = hlavni_problemy_cisla(cislo)
|
# hlavni_problemy = hlavni_problemy_cisla(cislo)
|
||||||
body_resitele = 0
|
# body_resitele = 0
|
||||||
for h in hlavni_problemy:
|
# for h in hlavni_problemy:
|
||||||
body_resitele = body_resitele + body_resitele_problemu_v_cisle(h, resitel, cislo)
|
# 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
|
# # 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?
|
# # ř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.
|
# # Tady to ale nevadí, tady se počítá součet za číslo.
|
||||||
return body_resitele
|
# return body_resitele
|
||||||
|
|
||||||
# spočítá součet všech bodů řešitele za daný rok (nebo jen do daného čísla včetně)
|
# 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):
|
#def body_resitele_v_rocniku(resitel, rocnik, do_cisla=None):
|
||||||
# pokud do_cisla=None, tak do posledního čísla v ročníku
|
# # pokud do_cisla=None, tak do posledního čísla v ročníku
|
||||||
# do_cisla je objekt Cislo
|
# # do_cisla je objekt Cislo
|
||||||
cisla = rocnik.cisla.all() # funkce vrátí pole objektů
|
# cisla = rocnik.cisla.all() # funkce vrátí pole objektů
|
||||||
# Cislo už lexikograficky setřízené, viz models
|
# # Cislo už lexikograficky setřízené, viz models
|
||||||
body = 0
|
# body = 0
|
||||||
for cislo in cisla:
|
# for cislo in cisla:
|
||||||
if cislo.poradi == do_cisla.poradi: break
|
# if cislo.poradi == do_cisla.poradi: break
|
||||||
# druhá část zaručuje, že máme výsledky do daného čísla včetně
|
# # druhá část zaručuje, že máme výsledky do daného čísla včetně
|
||||||
body = body + body_resitele_v_cisle(resitel, cislo)
|
# body = body + body_resitele_v_cisle(resitel, cislo)
|
||||||
return body
|
# return body
|
||||||
|
|
||||||
|
# TODO: předělat na nový model
|
||||||
#def vysledkovka_rocniku(rocnik, jen_verejne=True):
|
#def vysledkovka_rocniku(rocnik, jen_verejne=True):
|
||||||
# """Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve
|
# """Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve
|
||||||
# formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html"
|
# formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html"
|
||||||
|
@ -588,7 +612,8 @@ class RocnikView(generic.DetailView):
|
||||||
|
|
||||||
#context['vysledkovka'] = vysledkovka_rocniku(context["rocnik"])
|
#context['vysledkovka'] = vysledkovka_rocniku(context["rocnik"])
|
||||||
#context['vysledkovka_s_neverejnymi'] = vysledkovka_rocniku(context["rocnik"], jen_verejne=False)
|
#context['vysledkovka_s_neverejnymi'] = vysledkovka_rocniku(context["rocnik"], jen_verejne=False)
|
||||||
context['temata_v_rocniku'] = verejna_temata(context["rocnik"])
|
#context['temata_v_rocniku'] = verejna_temata(context["rocnik"])
|
||||||
|
# FIXME: opravit vylistování témat v ročníku
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
@ -612,19 +637,31 @@ class ProblemView(generic.DetailView):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class VysledkyResitele(object):
|
class RadekVysledkovky(object):
|
||||||
"""Pro daného řešitele ukládá počet bodů za jednotlivé úlohy a celkový
|
"""Obsahuje věci, které se hodí vědět při konstruování výsledkovky.
|
||||||
počet bodů za konkrétní ročník do daného čísla a za dané číslo."""
|
Umožňuje snazší práci v templatu (lepší, než seznam)."""
|
||||||
|
|
||||||
def __init__(self, resitel, cislo, rocnik):
|
def __init__(self, poradi, resitel, body_problemy_sezn,
|
||||||
|
body_cislo, body_rocnik, body_odjakziva):
|
||||||
self.resitel = resitel
|
self.resitel = resitel
|
||||||
self.cislo = cislo
|
self.body_cislo = body_cislo
|
||||||
self.body_cislo = body_resitele_v_cisle(resitel, cislo)
|
self.body_rocnik = body_rocnik
|
||||||
self.body = []
|
self.body_celkem_odjakziva = body_odjakziva
|
||||||
self.rocnik = rocnik
|
self.poradi = poradi
|
||||||
self.body_rocnik = body_resitele_v_rocniku(resitel, rocnik, cislo)
|
self.body_problemy_sezn = body_problemy_sezn
|
||||||
self.body_celkem_odjakziva = resitel.vsechny_body()
|
self.titul = resitel.get_titul(body_odjakziva)
|
||||||
self.poradi = 0
|
|
||||||
|
|
||||||
|
# přiřazuje danému řešiteli body do slovníku
|
||||||
|
def pricti_body(slovnik, resitel, body):
|
||||||
|
# testujeme na None (""), pokud je to první řešení
|
||||||
|
# daného řešitele, předěláme na 0
|
||||||
|
# (v dalším kroku přičteme reálný počet bodů),
|
||||||
|
# rozlišujeme tím mezi 0 a neodevzdaným řešením
|
||||||
|
if slovnik[str(resitel.id)] == "":
|
||||||
|
slovnik[str(resitel.id)] = 0
|
||||||
|
|
||||||
|
slovnik[str(resitel.id)] += body
|
||||||
|
|
||||||
class CisloView(generic.DetailView):
|
class CisloView(generic.DetailView):
|
||||||
model = Cislo
|
model = Cislo
|
||||||
|
@ -648,48 +685,101 @@ class CisloView(generic.DetailView):
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(CisloView, self).get_context_data(**kwargs)
|
context = super(CisloView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
## TODO upravit dle nového modelu
|
|
||||||
cislo = context['cislo']
|
cislo = context['cislo']
|
||||||
hlavni_problemy = hlavni_problemy_cisla(cislo)
|
hlavni_problemy = hlavni_problemy_cisla(cislo)
|
||||||
|
# TODO setřídit hlavní problémy čísla podle id, ať jsou ve stejném pořadí pokaždé
|
||||||
|
# pro každý hlavní problém zavedeme slovník s body za daný hlavní problém
|
||||||
|
# pro jednotlivé řešitele (slovník slovníků hlavních problémů)
|
||||||
|
hlavni_problemy_slovnik = {}
|
||||||
|
for hp in hlavni_problemy:
|
||||||
|
hlavni_problemy_slovnik[str(hp.id)] = {}
|
||||||
|
|
||||||
## TODO dostat pro tyto problémy součet v daném čísle pro daného řešitele
|
## 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
|
## TODO možná chytřeji vybírat aktivní řešitele
|
||||||
## chceme letos něco poslal
|
# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají
|
||||||
aktivni_resitele = Resitel.objects.filter(
|
# u alespoň jedné hodnoty něco jiného než NULL
|
||||||
rok_maturity__gte=cislo.rocnik.druhy_rok())
|
aktivni_resitele = list(Resitel.objects.filter(
|
||||||
|
rok_maturity__gte=cislo.rocnik.druhy_rok()))
|
||||||
# TODO: zkusit hodnoceni__rocnik...
|
# TODO: zkusit hodnoceni__rocnik...
|
||||||
#.filter(hodnoceni_set__rocnik__eq=cislo_rocnik)
|
#.filter(hodnoceni_set__rocnik__eq=cislo_rocnik)
|
||||||
radky_vysledkovky = []
|
# zakládání prázdných záznamů pro řešitele
|
||||||
|
cislobody = {}
|
||||||
for ar in aktivni_resitele:
|
for ar in aktivni_resitele:
|
||||||
# získáme výsledky řešitele - součty přes číslo a ročník
|
# řešitele převedeme na řetězec pomocí unikátního id
|
||||||
vr = VysledkyResitele(ar, cislo, cislo.rocnik)
|
cislobody[str(ar.id)] = ""
|
||||||
for hp in hlavni_problemy:
|
for hp in hlavni_problemy:
|
||||||
vr.body.append(
|
slovnik = hlavni_problemy_slovnik[str(hp.id)]
|
||||||
body_resitele_problemu_v_cisle(hp, ar, cislo))
|
slovnik[str(ar.id)] = ""
|
||||||
radky_vysledkovky.append(vr)
|
|
||||||
|
|
||||||
# setřídíme řádky výsledkovky/objekty VysledkyResitele podle bodů
|
# vezmeme všechna řešení s body do daného čísla
|
||||||
radky_vysledkovky.sort(key=lambda vr: vr.body_rocnik, reverse=True)
|
reseni_do_cisla = Reseni.objects.prefetch_related('problem', 'resitele', 'hodnoceni_set').filter(hodnoceni__cislo_body=cislo)
|
||||||
|
|
||||||
# generujeme sloupec s pořadím pomocí stejně zvané funkce
|
# projdeme všechna řešení do čísla a přičteme body každému řešiteli do celkových
|
||||||
pocty_bodu = [rv.body_rocnik for rv in radky_vysledkovky]
|
# bodů i do bodů za problém
|
||||||
sloupec_poradi = sloupec_s_poradim(pocty_bodu)
|
for reseni in reseni_do_cisla:
|
||||||
|
|
||||||
# každému řádku výsledkovky přidáme jeho pořadí
|
# řešení může řešit více problémů
|
||||||
i = 0
|
for prob in list(reseni.problem.all()):
|
||||||
for rv in radky_vysledkovky:
|
nadproblem = hlavni_problem(prob)
|
||||||
rv.poradi = sloupec_poradi[i]
|
nadproblem_slovnik = hlavni_problemy_slovnik[str(nadproblem.id)]
|
||||||
i = i + 1
|
|
||||||
|
|
||||||
|
# a více hodnocení
|
||||||
|
for hodn in list(reseni.hodnoceni_set.all()):
|
||||||
|
body = hodn.body
|
||||||
|
|
||||||
|
# a více řešitelů
|
||||||
|
for resitel in list(reseni.resitele.all()):
|
||||||
|
pricti_body(cislobody, resitel, body)
|
||||||
|
pricti_body(nadproblem_slovnik, resitel, body)
|
||||||
|
|
||||||
|
# zeptáme se na dvojice (řešitel, body) za ročník a setřídíme sestupně
|
||||||
|
resitel_rocnikbody_slov = body_resitelu_za_rocnik(cislo.rocnik, aktivni_resitele)
|
||||||
|
resitel_rocnikbody_sezn = sorted(resitel_rocnikbody_slov.items(),
|
||||||
|
key = lambda x: x[1], reverse = True)
|
||||||
|
|
||||||
|
# získáme body odjakživa
|
||||||
|
resitel_odjakzivabody_slov = body_resitelu_odjakziva(cislo.rocnik.druhy_rok(),
|
||||||
|
aktivni_resitele)
|
||||||
|
|
||||||
|
# řešitelé setřídění podle bodů za číslo sestupně
|
||||||
|
setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn]
|
||||||
|
setrizeni_resitele = [Resitel.objects.get(id=i) for i in setrizeni_resitele_id]
|
||||||
|
|
||||||
|
# vytvoříme jednotlivé sloupce výsledkovky
|
||||||
|
radky_vysledkovky = []
|
||||||
|
odjakziva_body = []
|
||||||
|
rocnik_body = []
|
||||||
|
cislo_body = []
|
||||||
|
hlavni_problemy_body = []
|
||||||
|
for ar_id in setrizeni_resitele_id:
|
||||||
|
# vytáhneme ze slovníků body pro daného řešitele
|
||||||
|
odjakziva_body.append(resitel_odjakzivabody_slov[ar_id])
|
||||||
|
rocnik_body.append(resitel_rocnikbody_slov[ar_id])
|
||||||
|
cislo_body.append(cislobody[ar_id])
|
||||||
|
problemy = []
|
||||||
|
for hp in hlavni_problemy:
|
||||||
|
problemy.append(hlavni_problemy_slovnik[str(hp.id)][ar_id])
|
||||||
|
hlavni_problemy_body.append(problemy)
|
||||||
|
print("{}: body za problémy - {}, číslobody - {}, ročníkbody - {}, odjakživabody - ".format(ar_id, problemy, cislobody[ar_id], resitel_rocnikbody_slov[ar_id]))
|
||||||
|
# pořadí určíme pomocí funkce, které dáme celkové body za ročník vzestupně
|
||||||
|
poradi = sloupec_s_poradim(rocnik_body)
|
||||||
|
|
||||||
|
radky_vysledkovky = []
|
||||||
|
for i in range(0, len(setrizeni_resitele_id)):
|
||||||
|
radek = RadekVysledkovky(poradi[i], setrizeni_resitele[i],
|
||||||
|
hlavni_problemy_body[i], cislo_body[i], rocnik_body[i],
|
||||||
|
odjakziva_body[i])
|
||||||
|
radky_vysledkovky.append(radek)
|
||||||
|
print("Přikládám {}-tý řádek.".format(i))
|
||||||
|
|
||||||
|
print("Následuje předávání do kontextu.")
|
||||||
# vytahané informace předáváme do kontextu
|
# vytahané informace předáváme do kontextu
|
||||||
context['cislo'] = cislo
|
context['cislo'] = cislo
|
||||||
context['radky_vysledkovky'] = radky_vysledkovky
|
context['radky_vysledkovky'] = radky_vysledkovky
|
||||||
context['problemy'] = hlavni_problemy
|
context['problemy'] = hlavni_problemy
|
||||||
# context['v_cisle_zadane'] = TODO
|
# context['v_cisle_zadane'] = TODO
|
||||||
# context['resene_problemy'] = resene_problemy
|
# context['resene_problemy'] = resene_problemy
|
||||||
#XXX testovat
|
print("Předávám kontext.")
|
||||||
#XXX opravit to, že se nezobrazují body za jednotlivé úlohy
|
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
# problemy = sorted(set(r.problem for r in reseni), key=lambda x:(poradi_typu[x.typ], x.kod_v_rocniku()))
|
# problemy = sorted(set(r.problem for r in reseni), key=lambda x:(poradi_typu[x.typ], x.kod_v_rocniku()))
|
||||||
|
@ -917,7 +1007,7 @@ def soustredeniUcastniciExportView(request,soustredeni):
|
||||||
class ClankyResitelView(generic.ListView):
|
class ClankyResitelView(generic.ListView):
|
||||||
model = Problem
|
model = Problem
|
||||||
template_name = 'seminar/clanky/resitelske_clanky.html'
|
template_name = 'seminar/clanky/resitelske_clanky.html'
|
||||||
queryset = Clanek.objects.filter(stav=Problem.STAV_ZADANY).select_related('cislo_zadani__rocnik').order_by('-cislo_zadani__rocnik__rocnik', 'kod')
|
queryset = Clanek.objects.filter(stav=Problem.STAV_ZADANY, resitelsky=True).select_related('cislo__rocnik').order_by('-cislo__rocnik__rocnik', 'kod')
|
||||||
|
|
||||||
# FIXME: pokud chceme orgoclanky, tak nejak zavest do modelu a podle toho odkomentovat a upravit
|
# FIXME: pokud chceme orgoclanky, tak nejak zavest do modelu a podle toho odkomentovat a upravit
|
||||||
#class ClankyOrganizatorView(generic.ListView)<F12>:
|
#class ClankyOrganizatorView(generic.ListView)<F12>:
|
||||||
|
@ -1160,6 +1250,42 @@ class AddSolutionView(LoginRequiredMixin, FormView):
|
||||||
form_class = f.VlozReseniForm
|
form_class = f.VlozReseniForm
|
||||||
success_url = '/'
|
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):
|
def resetPasswordView(request):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -1321,58 +1447,9 @@ def prihlaskaView(request):
|
||||||
|
|
||||||
return render(request, 'seminar/prihlaska.html', {'form': form})
|
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'
|
# FIXME: Tohle asi vlastně vůbec nepatří do aplikace 'seminar'
|
||||||
class LoginView(auth_views.LoginView):
|
class LoginView(auth_views.LoginView):
|
Loading…
Reference in a new issue