Browse Source

Merge branch 'data_migrations' of gimli.ms.mff.cuni.cz:/akce/mam/git/mamweb into data_migrations

export_seznamu_prednasek
Kateřina Čížková 4 years ago
parent
commit
aaa84a5f00
  1. BIN
      Schema_new.dia
  2. 24
      seminar/forms.py
  3. 3
      seminar/management/commands/testdata.py
  4. 24
      seminar/migrations/0074_auto_20200228_1401.py
  5. 18
      seminar/migrations/0075_auto_20200228_2010.py
  6. 19
      seminar/migrations/0076_auto_20200228_2013.py
  7. 32
      seminar/migrations/0077_auto_20200318_2146.py
  8. 27
      seminar/migrations/0078_otistenereseninode.py
  9. 18
      seminar/migrations/0079_clanek_resitelsky.py
  10. 81
      seminar/models.py
  11. 49
      seminar/static/seminar/dynamic_formsets.js
  12. 31
      seminar/templates/seminar/archiv/cislo.html
  13. 44
      seminar/templates/seminar/nahraj_reseni.html
  14. 10
      seminar/templates/seminar/treenode.html
  15. 28
      seminar/templates/seminar/treenode_recursive.html
  16. 49
      seminar/templatetags/treenodes.py
  17. 214
      seminar/testutils.py
  18. 286
      seminar/treelib.py
  19. 16
      seminar/urls.py
  20. 16
      seminar/utils.py
  21. 2
      seminar/views/__init__.py
  22. 63
      seminar/views/autocomplete.py
  23. 8
      seminar/views/helpers.py
  24. 0
      seminar/views/unicodecsv.py
  25. 91
      seminar/views/utils.py
  26. 609
      seminar/views/views_all.py

BIN
Schema_new.dia

Binary file not shown.

24
seminar/forms.py

@ -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,
)

3
seminar/management/commands/testdata.py

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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?'),
),
]

81
seminar/models.py

@ -304,9 +304,10 @@ 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"
celkove_body = self.vsechny_body() if celkove_body is None:
celkove_body = self.vsechny_body()
if celkove_body < 10: if celkove_body < 10:
return '' return ''
@ -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/),
@ -988,9 +993,12 @@ 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,
@ -1262,15 +1273,6 @@ class TreeNode(PolymorphicModel):
srolovatelne = models.BooleanField(null = True, blank = True, srolovatelne = models.BooleanField(null = True, blank = True,
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

@ -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);
});
});

31
seminar/templates/seminar/archiv/cislo.html

@ -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 }}
@ -45,14 +46,15 @@
<li><a href="obalkovani">Obálkování</a></li> <li><a href="obalkovani">Obálkování</a></li>
</ul> </ul>
</div> </div>
{% 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,36 +63,37 @@
<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 %}
</table> </table>
{% endif %} {% endif %}
{% if not cislo.verejna_vysledkovka and user.is_staff %} {% if not cislo.verejna_vysledkovka and user.is_staff %}
</div> </div>
{% endif %} {% endif %}
</div> Čas: {% now "jS F Y H:i:s" %}
{% endblock content %}
</div>
{% endblock content %}

44
seminar/templates/seminar/nahraj_reseni.html

@ -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

@ -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

@ -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

@ -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)

214
seminar/testutils.py

@ -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í...)
# Tohle sice umožňuje vygenerovat vzorák do čísla dávno po konci témátka, ale to nám pro jednoduchost nevadí.
if len(cislo_se_vzorakem) == 0:
cislo_se_vzorakem = cisla[-1]
else:
cislo_se_vzorakem = cislo_se_vzorakem.first()
# FIXME: Tenhle generátor dát asi někam jinam
def potomci(node):
if not isinstance(node, TreeNode):
raise ValueError("Typ {} nemá potomky", type(node))
current_child = node.first_child
while current_child is not None:
yield current_child
current_child = current_child.succ
for mozna_tema_node in potomci(cislo.cislonode):
if not isinstance(mozna_tema_node, TemaVCisleNode):
continue continue
else: tema_node = mozna_tema_node
tema = mozna_tema_vcn.tema tema = tema_node.tema
if not temata[int(tema.kod)-1][1] >= ci+2: # Pokud už témátko skončilo, žádné úložky negenerujeme
mozna_tema_vcn = mozna_tema_vcn.succ # 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(kod)]),
"úloha {}.".format(i)]),
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(
na_web = " ".join( # Samotný obsah následně vzniklého Textu zadání
[rnd.choice(sloveso), obsah = " ".join(
rnd.choice(koho), [rnd.choice(sloveso),
rnd.choice(ceho), rnd.choice(koho),
rnd.choice(jmeno), rnd.choice(ceho),
rnd.choice(kde)] rnd.choice(jmeno),
),
do_cisla = " ".join(
[rnd.choice(sloveso),
rnd.choice(koho),
rnd.choice(ceho),
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: # Najdeme správný TemaVCisleNode pro vložení vzoráku
res_tema_vcn = res_tema_vcn.succ res_tema_node = None;
otec_syn(res_tema_vcn, uloha_vzorak) for node in potomci(cislo_se_vzorakem.cislonode):
if isinstance(node, TemaVCisleNode) and node.tema == tema:
p.save() res_tema_node = node
if res_tema_node is None:
mozna_tema_vcn = mozna_tema_vcn.succ 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)
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

@ -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...

16
seminar/urls.py

@ -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'),

16
seminar/utils.py

@ -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

@ -0,0 +1,2 @@
from .views_all import *
from .autocomplete import *

63
seminar/views/autocomplete.py

@ -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

@ -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)

0
seminar/unicodecsv.py → seminar/views/unicodecsv.py

91
seminar/views/utils.py

@ -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

609
seminar/views.py → seminar/views/views_all.py

@ -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):
nastaveni = get_object_or_404(Nastaveni) model = s.TreeNode
verejne = nastaveni.aktualni_cislo.verejne() template_name = 'seminar/treenode.html'
problemy = Problem.objects.filter(cislo_zadani=nastaveni.aktualni_cislo).filter(stav = 'zadany')
ulohy = problemy.filter(typ = 'uloha').order_by('kod')
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): def get_context_data(self,**kwargs):
nastaveni = get_object_or_404(Nastaveni) context = super().get_context_data(**kwargs)
temata = verejna_temata(nastaveni.aktualni_rocnik) context['tnldata'] = treenode_strom_na_seznamy(self.object)
for t in temata: return context
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 class AktualniZadaniView(TreeNodeView):
def vytahniZLesaSeznam(tematko, koren, pouze_zajimave=False): def get_object(self):
returnVal = [] nastaveni = get_object_or_404(Nastaveni)
return nastaveni.aktualni_cislo.cislonode
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 get_context_data(self,**kwargs):
nastaveni = get_object_or_404(Nastaveni)
context = super().get_context_data(**kwargs)
verejne = nastaveni.aktualni_cislo.verejne()
context['verejne'] = verejne
return context
#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(): # # a přičteme k tomu hodnocení všech podproblémů
# i přes jméno by to měla být množina jeho podproblémů # for p in problem.podproblem.all():
soucet += __soucet_resitele_problemu(p, resitel, soucet) # # i přes jméno by to měla být množina jeho podproblémů
return soucet # soucet += __soucet_resitele_problemu(p, resitel, soucet)
# return soucet
# spočítá součet všech bodů ze všech podproblémů daného problému daného řešitele
def body_resitele_problemu_v_cisle(problem, resitel, cislo): ## spočítá součet všech bodů ze všech podproblémů daného problému daného řešitele
# probably FIXED: nezohledňuje číslo, do kterého se body počítají #def body_resitele_problemu_v_cisle(problem, resitel, cislo):
return __soucet_resitele_problemu(problem, resitel, cislo, 0) # # probably FIXED: nezohledňuje číslo, do kterého se body počítají
# return __soucet_resitele_problemu(problem, resitel, cislo, 0)
# pro daný problém vrátí jeho nejvyšší nadproblém
def hlavni_problem(problem):
while not(problem.nadproblem == None):
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,10 +445,8 @@ 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)
hlavni_problemy = list(hlavni_problemy_set) hlavni_problemy = list(hlavni_problemy_set)
@ -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)
# vezmeme všechna řešení s body do daného čísla
# setřídíme řádky výsledkovky/objekty VysledkyResitele podle bodů reseni_do_cisla = Reseni.objects.prefetch_related('problem', 'resitele', 'hodnoceni_set').filter(hodnoceni__cislo_body=cislo)
radky_vysledkovky.sort(key=lambda vr: vr.body_rocnik, reverse=True)
# projdeme všechna řešení do čísla a přičteme body každému řešiteli do celkových
# generujeme sloupec s pořadím pomocí stejně zvané funkce # bodů i do bodů za problém
pocty_bodu = [rv.body_rocnik for rv in radky_vysledkovky] for reseni in reseni_do_cisla:
sloupec_poradi = sloupec_s_poradim(pocty_bodu)
# řešení může řešit více problémů
# každému řádku výsledkovky přidáme jeho pořadí for prob in list(reseni.problem.all()):
i = 0 nadproblem = hlavni_problem(prob)
for rv in radky_vysledkovky: nadproblem_slovnik = hlavni_problemy_slovnik[str(nadproblem.id)]
rv.poradi = sloupec_poradi[i]
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…
Cancel
Save