diff --git a/seminar/migrations/0078_otistenereseninode.py b/seminar/migrations/0078_otistenereseninode.py new file mode 100644 index 00000000..2f426a17 --- /dev/null +++ b/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',), + ), + ] diff --git a/seminar/models.py b/seminar/models.py index 25e4130d..82b09944 100644 --- a/seminar/models.py +++ b/seminar/models.py @@ -1474,6 +1474,26 @@ class CastNode(TreeNode): 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. #class VysledkyBase(SeminarModelBase): diff --git a/seminar/templates/seminar/treenode.html b/seminar/templates/seminar/treenode.html new file mode 100644 index 00000000..0fd734ef --- /dev/null +++ b/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 %} diff --git a/seminar/templates/seminar/treenode_recursive.html b/seminar/templates/seminar/treenode_recursive.html new file mode 100644 index 00000000..0cf37d9a --- /dev/null +++ b/seminar/templates/seminar/treenode_recursive.html @@ -0,0 +1,28 @@ +{% load treenodes %} +{# {{depth}} #} +
+{% if obj.node|isRocnik %} + Ročník {{obj.node.rocnik}} +{% elif obj.node|isCislo %} + Číslo {{obj.node.cislo}} +{% elif obj.node|isTemaVCisle %} + Téma {{obj.node.tema.nazev}} +{% elif obj.node|isUlohaZadani %} +Úloha {{obj.node.uloha.kod_v_rocniku}} ({{obj.node.uloha.max_body}} b) +{% elif obj.node|isUlohaVzorak %} +Řešení: {{obj.node.uloha.kod_v_rocniku}} +{% elif obj.node|isText %} +{{obj.node.text.na_web}} +{% else %} +Objekt jiného typu {{obj.node}} +{% endif %} + {%if obj.children %} +
+ {%for ch in obj.children %} + {%with obj=ch depth=depth|add:"1" template_name="seminar/treenode_recursive.html" %} + {%include template_name%} + {%endwith%} + {%endfor%} +
+ {%endif%} +
diff --git a/seminar/templatetags/treenodes.py b/seminar/templatetags/treenodes.py new file mode 100644 index 00000000..0d60765e --- /dev/null +++ b/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) + diff --git a/seminar/treelib.py b/seminar/treelib.py index cfb49d86..26b77d1d 100644 --- a/seminar/treelib.py +++ b/seminar/treelib.py @@ -1,6 +1,7 @@ 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): @@ -18,9 +19,19 @@ def safe_pred(node): 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): - return node.prev + first = first_brother(node) + try: + return first.father_of_first except ObjectDoesNotExist: return None @@ -61,14 +72,34 @@ def general_prev(node): # pred nyní nemá žádné potomky, takže je to poslední rekurzivní potomek původního předchůdce return pred -# Generátor bratrů +# Generátor 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 all_brothers(node): +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) @@ -76,44 +107,129 @@ def all_children(node): 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(current, type): - pass -def get_prev_brother_of_type(current, type): - pass +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(current, type): - pass -def get_next_node_of_type(current, type): - pass +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): - pass + 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): - pass + 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() -def create_node_before(some, arguments, but, i, dont, know, which, yet): - pass - # Tohle bude hell. # ValueError, pokud je (aspoň) jeden parametr None def swap(node, other): + 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): - pass + 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): - pass + 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: diff --git a/seminar/urls.py b/seminar/urls.py index 0cd3f5bb..48a4b754 100644 --- a/seminar/urls.py +++ b/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) urlpatterns = [ - path('aktualni/temata/', views.TemataRozcestnikView), - path('/t/', views.TematkoView), +# path('aktualni/temata/', views.TemataRozcestnikView), +# path('/t/', views.TematkoView), # REDIRECTy path('jak-resit/', RedirectView.as_view(url='/co-je-MaM/jak-resit/')), @@ -25,6 +25,7 @@ urlpatterns = [ path('rocnik//', views.RocnikView.as_view(), name='seminar_rocnik'), path('cislo/./', views.CisloView.as_view(), name='seminar_cislo'), path('problem//', views.ProblemView.as_view(), name='seminar_problem'), + path('treenode//', views.TreeNodeView.as_view(), name='seminar_treenode'), #path('problem/(?P\d+)/(?P\d+)/', views.PrispevekView.as_view(), name='seminar_problem_prispevek'), # Soustredeni @@ -59,8 +60,8 @@ urlpatterns = [ ), # Zadani - path('zadani/aktualni/', views.AktualniZadaniView, name='seminar_aktualni_zadani'), - path('zadani/temata/', views.ZadaniTemataView, name='seminar_temata'), +# path('zadani/aktualni/', views.AktualniZadaniView, name='seminar_aktualni_zadani'), +# path('zadani/temata/', views.ZadaniTemataView, name='seminar_temata'), #path('zadani/vysledkova-listina/', views.ZadaniAktualniVysledkovkaView, name='seminar_vysledky'), path('stare-novinky/', views.StareNovinkyView.as_view(), name='stare_novinky'), @@ -107,9 +108,6 @@ urlpatterns = [ path('auth/login/', views.LoginView.as_view(), name='login'), path('auth/logout/', views.LogoutView.as_view(), name='logout'), path('auth/resitel/', views.ResitelView.as_view(), name='seminar_resitel'), - path('autocomplete/skola/',views.SkolaAutocomplete.as_view(), name='autocomplete_skola'), - path('autocomplete/resitel/',views.ResitelAutocomplete.as_view(), name='autocomplete_resitel'), - path('autocomplete/problem/odevzdatelny',views.OdevzdatelnyProblemAutocomplete.as_view(), name='autocomplete_problem_odevzdatelny'), path('auth/reset_password/', views.PasswordResetView.as_view(), name='reset_password'), path('auth/change_password/', views.PasswordChangeView.as_view(), name='change_password'), path('auth/reset_password_done/', views.PasswordResetDoneView.as_view(), name='reset_password_done'), @@ -117,6 +115,10 @@ urlpatterns = [ path('auth/reset_password_complete/', views.PasswordResetCompleteView.as_view(), name='reset_password_complete'), 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/submit_solution', views.SubmitSolutionView.as_view(),name='seminar_nahraj_reseni'), diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 3dcf1331..945d283e 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -19,7 +19,7 @@ from django.db import transaction import seminar.models as s 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 seminar import utils +from seminar import utils,treelib from .unicodecsv import UnicodeWriter from seminar.forms import PrihlaskaForm, LoginForm, ProfileEditForm import seminar.forms as f @@ -83,131 +83,110 @@ class ObalkovaniView(generic.ListView): context['cislo'] = self.cislo 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): - nastaveni = get_object_or_404(Nastaveni) - verejne = nastaveni.aktualni_cislo.verejne() - 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): - 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, - } - ) +class TreeNodeView(generic.DetailView): + model = s.TreeNode + template_name = 'seminar/treenode.html' -# 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 get_context_data(self,**kwargs): + context = super().get_context_data(**kwargs) + context['tnldata'] = treenode_strom_na_seznamy(self.object) + return context -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 AktualniZadaniView(request): +# nastaveni = get_object_or_404(Nastaveni) +# verejne = nastaveni.aktualni_cislo.verejne() +# 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): +# 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): # nastaveni = get_object_or_404(Nastaveni)