Jonas Havelka
3 years ago
32 changed files with 459 additions and 292 deletions
@ -1,5 +1,4 @@ |
|||||
from .views_all import * |
from .views_all import * |
||||
from .views_rest import * |
|
||||
|
|
||||
# Dočsasné views |
# Dočsasné views |
||||
from .docasne import * |
from .docasne import * |
||||
|
@ -0,0 +1,88 @@ |
|||||
|
from django.contrib import admin |
||||
|
|
||||
|
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter |
||||
|
|
||||
|
import seminar.models as m |
||||
|
|
||||
|
# Polymorfismus pro stromy |
||||
|
# TODO: Inlines podle https://django-polymorphic.readthedocs.io/en/stable/admin.html |
||||
|
|
||||
|
@admin.register(m.TreeNode) |
||||
|
class TreeNodeAdmin(PolymorphicParentModelAdmin): |
||||
|
base_model = m.TreeNode |
||||
|
child_models = [ |
||||
|
m.RocnikNode, |
||||
|
m.CisloNode, |
||||
|
m.MezicisloNode, |
||||
|
m.TemaVCisleNode, |
||||
|
m.UlohaZadaniNode, |
||||
|
m.PohadkaNode, |
||||
|
m.UlohaVzorakNode, |
||||
|
m.TextNode, |
||||
|
m.CastNode, |
||||
|
m.OrgTextNode, |
||||
|
] |
||||
|
|
||||
|
actions = ['aktualizuj_nazvy'] |
||||
|
|
||||
|
# XXX: nejspíš je to totální DB HOG, nechcete to použít moc často. |
||||
|
def aktualizuj_nazvy(self, request, queryset): |
||||
|
newqs = queryset.get_real_instances() |
||||
|
for tn in newqs: |
||||
|
tn.aktualizuj_nazev() |
||||
|
tn.save() |
||||
|
self.message_user(request, "Názvy aktualizovány.") |
||||
|
aktualizuj_nazvy.short_description = "Aktualizuj vybraným TreeNodům názvy" |
||||
|
|
||||
|
@admin.register(m.RocnikNode) |
||||
|
class RocnikNodeAdmin(PolymorphicChildModelAdmin): |
||||
|
base_model = m.RocnikNode |
||||
|
show_in_index = True |
||||
|
|
||||
|
@admin.register(m.CisloNode) |
||||
|
class CisloNodeAdmin(PolymorphicChildModelAdmin): |
||||
|
base_model = m.CisloNode |
||||
|
show_in_index = True |
||||
|
|
||||
|
@admin.register(m.MezicisloNode) |
||||
|
class MezicisloNodeAdmin(PolymorphicChildModelAdmin): |
||||
|
base_model = m.MezicisloNode |
||||
|
show_in_index = True |
||||
|
|
||||
|
@admin.register(m.TemaVCisleNode) |
||||
|
class TemaVCisleNodeAdmin(PolymorphicChildModelAdmin): |
||||
|
base_model = m.TemaVCisleNode |
||||
|
show_in_index = True |
||||
|
|
||||
|
@admin.register(m.UlohaZadaniNode) |
||||
|
class UlohaZadaniNodeAdmin(PolymorphicChildModelAdmin): |
||||
|
base_model = m.UlohaZadaniNode |
||||
|
show_in_index = True |
||||
|
|
||||
|
@admin.register(m.PohadkaNode) |
||||
|
class PohadkaNodeAdmin(PolymorphicChildModelAdmin): |
||||
|
base_model = m.PohadkaNode |
||||
|
show_in_index = True |
||||
|
|
||||
|
@admin.register(m.UlohaVzorakNode) |
||||
|
class UlohaVzorakNodeAdmin(PolymorphicChildModelAdmin): |
||||
|
base_model = m.UlohaVzorakNode |
||||
|
show_in_index = True |
||||
|
|
||||
|
@admin.register(m.TextNode) |
||||
|
class TextNodeAdmin(PolymorphicChildModelAdmin): |
||||
|
base_model = m.TextNode |
||||
|
show_in_index = True |
||||
|
|
||||
|
@admin.register(m.CastNode) |
||||
|
class TextNodeAdmin(PolymorphicChildModelAdmin): |
||||
|
base_model = m.CastNode |
||||
|
show_in_index = True |
||||
|
fields = ('nadpis',) |
||||
|
|
||||
|
@admin.register(m.OrgTextNode) |
||||
|
class TextNodeAdmin(PolymorphicChildModelAdmin): |
||||
|
base_model = m.OrgTextNode |
||||
|
show_in_index = True |
||||
|
|
||||
|
|
@ -0,0 +1,5 @@ |
|||||
|
from django.apps import AppConfig |
||||
|
|
||||
|
|
||||
|
class TreenodeConfig(AppConfig): |
||||
|
name = 'treenode' |
@ -1,5 +1,5 @@ |
|||||
from rest_framework import routers |
from rest_framework import routers |
||||
from seminar import viewsets as vs |
from treenode import viewsets as vs |
||||
|
|
||||
router = routers.DefaultRouter() |
router = routers.DefaultRouter() |
||||
|
|
@ -1,5 +1,5 @@ |
|||||
from django.test import TestCase |
from django.test import TestCase |
||||
import seminar.treelib as tl |
import treenode.treelib as tl |
||||
import seminar.models as m |
import seminar.models as m |
||||
|
|
||||
class SimpleTreeLibTests(TestCase): |
class SimpleTreeLibTests(TestCase): |
@ -0,0 +1,18 @@ |
|||||
|
from django.urls import path, re_path |
||||
|
from . import views |
||||
|
|
||||
|
urlpatterns = [ |
||||
|
#path('treenode/<int:pk>/', views.TreeNodeView.as_view(), name='seminar_treenode'), |
||||
|
#path('treenode/<int:pk>/json/', views.TreeNodeJSONView.as_view(), name='seminar_treenode_json'), |
||||
|
#path('treenode/text/<int:pk>/', views.TextWebView.as_view(), name='seminar_textnode_web'), |
||||
|
#path('treenode/editor/pridat/<str:co>/<int:pk>/<str:kam>/', views.TreeNodePridatView.as_view(), name='treenode_pridat'), |
||||
|
#path('treenode/editor/smazat/<int:pk>/', views.TreeNodeSmazatView.as_view(), name='treenode_smazat'), |
||||
|
#path('treenode/editor/odvesitpryc/<int:pk>/', views.TreeNodeOdvesitPrycView.as_view(), name='treenode_odvesitpryc'), |
||||
|
#path('treenode/editor/podvesit/<int:pk>/<str:kam>/', views.TreeNodePodvesitView.as_view(), name='treenode_podvesit'), |
||||
|
#path('treenode/editor/prohodit/<int:pk>/', views.TreeNodeProhoditView.as_view(), name='treenode_prohodit'), |
||||
|
#path('treenode/sirotcinec/', views.SirotcinecView.as_view(), name='seminar_treenode_sirotcinec'), |
||||
|
#path('problem/(?P<pk>\d+)/(?P<prispevek>\d+)/', views.PrispevekView.as_view(), name='seminar_problem_prispevek'), |
||||
|
|
||||
|
re_path(r'^temp/vue/.*$',views.VueTestView.as_view(),name='vue_test_view'), |
||||
|
path('temp/image_upload/', views.NahrajObrazekKTreeNoduView.as_view()), |
||||
|
] |
@ -0,0 +1,322 @@ |
|||||
|
from django.forms import model_to_dict |
||||
|
from django.shortcuts import redirect |
||||
|
from django.http import JsonResponse |
||||
|
from django.views import generic |
||||
|
from django.views.generic.edit import CreateView |
||||
|
from django.contrib.auth.mixins import LoginRequiredMixin |
||||
|
from django.core.exceptions import PermissionDenied |
||||
|
|
||||
|
import seminar.models as s |
||||
|
import seminar.models as m |
||||
|
from treenode import treelib |
||||
|
import treenode.forms as f |
||||
|
import treenode.templatetags as tnltt |
||||
|
import treenode.serializers as vr |
||||
|
|
||||
|
import logging |
||||
|
|
||||
|
logger = logging.getLogger(__name__) |
||||
|
|
||||
|
|
||||
|
class TNLData(object): |
||||
|
def __init__(self,anode,parent=None, index=None): |
||||
|
self.node = anode |
||||
|
self.sernode = vr.TreeNodeSerializer(anode) |
||||
|
self.children = [] |
||||
|
self.parent = parent |
||||
|
self.tema_in_path = False |
||||
|
self.index = index |
||||
|
|
||||
|
if parent: |
||||
|
self.tema_in_path = parent.tema_in_path |
||||
|
if isinstance(anode, m.TemaVCisleNode): |
||||
|
self.tema_in_path = True |
||||
|
|
||||
|
def add_edit_options(self): |
||||
|
self.deletable = tnltt.deletable(self) |
||||
|
self.editable_siblings = tnltt.editableSiblings(self) |
||||
|
self.editable_children = tnltt.editableChildren(self) |
||||
|
self.text_only_subtree = tnltt.textOnlySubtree(self) |
||||
|
self.can_podvesit_za = tnltt.canPodvesitZa(self) |
||||
|
self.can_podvesit_pred = tnltt.canPodvesitPred(self) |
||||
|
self.appendable_children = tnltt.appendableChildren(self) |
||||
|
print("appChld",self.appendable_children) |
||||
|
if self.parent: |
||||
|
self.appendable_siblings = tnltt.appendableChildren(self.parent) |
||||
|
else: |
||||
|
self.appendable_siblings = [] |
||||
|
@classmethod |
||||
|
def public_above(cls, anode): |
||||
|
""" Returns output of verejne for closest Rocnik, Cislo or Problem above. |
||||
|
(All of them have method verejne.)""" |
||||
|
parent = anode # chceme začít už od konkrétního node včetně |
||||
|
while True: |
||||
|
rocnik = isinstance(parent, s.RocnikNode) |
||||
|
cislo = isinstance(parent, s.CisloNode) |
||||
|
uloha = (isinstance(parent, s.UlohaVzorakNode) or |
||||
|
isinstance(parent, s.UlohaZadaniNode)) |
||||
|
tema = isinstance(parent, s.TemaVCisleNode) |
||||
|
|
||||
|
if (rocnik or cislo or uloha or tema) or parent==None: |
||||
|
break |
||||
|
else: |
||||
|
parent = treelib.get_parent(parent) |
||||
|
if rocnik: |
||||
|
return parent.rocnik.verejne() |
||||
|
elif cislo: |
||||
|
return parent.cislo.verejne() |
||||
|
elif uloha: |
||||
|
return parent.uloha.verejne() |
||||
|
elif tema: |
||||
|
return parent.tema.verejne() |
||||
|
elif None: |
||||
|
print("Existuje TreeNode, který není pod číslem, ročníkem, úlohou" |
||||
|
"ani tématem. {}".format(anode)) |
||||
|
return False |
||||
|
|
||||
|
@classmethod |
||||
|
def all_public_children(cls, anode): |
||||
|
for ch in treelib.all_children(anode): |
||||
|
if TNLData.public_above(ch): |
||||
|
yield ch |
||||
|
else: |
||||
|
continue |
||||
|
|
||||
|
@classmethod |
||||
|
def from_treenode(cls, anode, user, parent=None, index=None): |
||||
|
if TNLData.public_above(anode) or user.has_perm('auth.org'): |
||||
|
out = cls(anode,parent,index) |
||||
|
else: |
||||
|
raise PermissionDenied() |
||||
|
|
||||
|
if user.has_perm('auth.org'): |
||||
|
enum_children = enumerate(treelib.all_children(anode)) |
||||
|
else: |
||||
|
enum_children = enumerate(TNLData.all_public_children(anode)) |
||||
|
|
||||
|
for (idx,ch) in enum_children: |
||||
|
outitem = cls.from_treenode(ch, user, out, idx) |
||||
|
out.children.append(outitem) |
||||
|
out.add_edit_options() |
||||
|
return out |
||||
|
|
||||
|
@classmethod |
||||
|
def from_tnldata_list(cls, tnllist): |
||||
|
"""Vyrobíme virtuální TNL, který nemá obsah, ale má za potomky všechna zadaná TNLData""" |
||||
|
result = cls(None) |
||||
|
for idx, tnl in enumerate(tnllist): |
||||
|
result.children.append(tnl) |
||||
|
tnl.parent = result |
||||
|
tnl.index = idx |
||||
|
result.add_edit_options() |
||||
|
return result |
||||
|
|
||||
|
@classmethod |
||||
|
def filter_treenode(cls, treenode, predicate): |
||||
|
tnll = cls._filter_treenode_recursive(treenode, predicate) # TreeNodeList List :-) |
||||
|
return TNLData.from_tnldata_list(tnll) |
||||
|
|
||||
|
@classmethod |
||||
|
def _filter_treenode_recursive(cls, treenode, predicate): |
||||
|
if predicate(treenode): |
||||
|
return [cls.from_treenode(treenode)] |
||||
|
else: |
||||
|
found = [] |
||||
|
for tn in treelib.all_children(treenode): |
||||
|
result = cls.filter_treenode(tn, predicate) |
||||
|
# Result by v tuhle chvíli měl být seznam TNLDat odpovídající treenodům, jež matchnuly predikát. |
||||
|
for tnl in result: |
||||
|
found.append(tnl) |
||||
|
return found |
||||
|
|
||||
|
def to_json(self): |
||||
|
#self.node = anode |
||||
|
#self.children = [] |
||||
|
#self.parent = parent |
||||
|
#self.tema_in_path = False |
||||
|
#self.index = index |
||||
|
out = {} |
||||
|
out['node'] = self.sernode.data |
||||
|
out['children'] = [n.to_json() for n in self.children] |
||||
|
out['tema_in_path'] = self.tema_in_path |
||||
|
out['index'] = self.index |
||||
|
out['deletable'] = self.deletable |
||||
|
out['editable_siblings'] = self.editable_siblings |
||||
|
out['editable_children'] = self.editable_children |
||||
|
out['text_only_subtree'] = self.text_only_subtree |
||||
|
out['can_podvesit_za'] = self.can_podvesit_za |
||||
|
out['can_podvesit_pod'] = self.can_podvesit_pred |
||||
|
out['appendable_children'] = self.appendable_children |
||||
|
out['appendable_siblings'] = self.appendable_siblings |
||||
|
|
||||
|
return out |
||||
|
|
||||
|
|
||||
|
|
||||
|
def __repr__(self): |
||||
|
return("TNL({})".format(self.node)) |
||||
|
|
||||
|
|
||||
|
class TreeNodeView(generic.DetailView): |
||||
|
model = s.TreeNode |
||||
|
template_name = 'treenode/treenode.html' |
||||
|
|
||||
|
def get_context_data(self,**kwargs): |
||||
|
context = super().get_context_data(**kwargs) |
||||
|
context['tnldata'] = TNLData.from_treenode(self.object,self.request.user) |
||||
|
return context |
||||
|
|
||||
|
|
||||
|
class TreeNodeJSONView(generic.DetailView): |
||||
|
model = s.TreeNode |
||||
|
|
||||
|
def get(self,request,*args, **kwargs): |
||||
|
self.object = self.get_object() |
||||
|
data = TNLData.from_treenode(self.object,self.request.user).to_json() |
||||
|
return JsonResponse(data) |
||||
|
|
||||
|
|
||||
|
class TreeNodePridatView(generic.View): |
||||
|
type_from_str = { |
||||
|
'rocnikNode': m.RocnikNode, |
||||
|
'cisloNode': m.CisloNode, |
||||
|
'castNode': m.CastNode, |
||||
|
'textNode': m.TextNode, |
||||
|
'temaVCisleNode': m.TemaVCisleNode, |
||||
|
'reseniNode': m.ReseniNode, |
||||
|
'ulohaZadaniNode': m.UlohaZadaniNode, |
||||
|
'ulohaVzorakNode': m.UlohaVzorakNode, |
||||
|
'pohadkaNode': m.PohadkaNode, |
||||
|
'orgText': m.OrgTextNode, |
||||
|
} |
||||
|
|
||||
|
def post(self, request, *args, **kwargs): |
||||
|
######## FIXME: ROZEPSANE, NEFUNGUJE, DOPSAT !!!!!! ########### |
||||
|
node = s.TreeNode.objects.get(pk=self.kwargs['pk']) |
||||
|
kam = self.kwargs['kam'] |
||||
|
co = self.kwargs['co'] |
||||
|
typ = self.type_from_str[co] |
||||
|
|
||||
|
raise NotImplementedError('Neni to dopsane, dopis to!') |
||||
|
|
||||
|
if kam not in ('pred','syn','za'): |
||||
|
raise ValidationError('Přidat lze pouze před nebo za node nebo jako syna') |
||||
|
|
||||
|
if co == m.TextNode: |
||||
|
new_obj = m.Text() |
||||
|
new_obj.save() |
||||
|
elif co == m.CastNode: |
||||
|
new_obj = m.CastNode() |
||||
|
new_obj.nadpis = request.POST.get('pridat-castNode-{}-{}'.format(node.id,kam)) |
||||
|
new_obj.save() |
||||
|
elif co == m.ReseniNode: |
||||
|
new_obj = m |
||||
|
pass |
||||
|
elif co == m.UlohaZadaniNode: |
||||
|
pass |
||||
|
elif co == m.UlohaReseniNode: |
||||
|
pass |
||||
|
else: |
||||
|
new_obj = None |
||||
|
|
||||
|
|
||||
|
if kam == 'pred': |
||||
|
pass |
||||
|
|
||||
|
|
||||
|
if kam == 'syn': |
||||
|
if typ == m.TextNode: |
||||
|
text_obj = m.Text() |
||||
|
text_obj.save() |
||||
|
node = treelib.create_child(node, typ, text=text_obj) |
||||
|
else: |
||||
|
node = treelib.create_child(node, typ) |
||||
|
if kam == 'za': |
||||
|
if typ == m.TextNode: |
||||
|
text_obj = m.Text() |
||||
|
text_obj.save() |
||||
|
node = treelib.create_node_after(node, typ, text=text_obj) |
||||
|
else: |
||||
|
node = treelib.create_node_after(node, typ) |
||||
|
|
||||
|
return redirect(node.get_admin_url()) |
||||
|
|
||||
|
|
||||
|
class TreeNodeSmazatView(generic.base.View): |
||||
|
def post(self, request, *args, **kwargs): |
||||
|
node = s.TreeNode.objects.get(pk=self.kwargs['pk']) |
||||
|
if node.first_child: |
||||
|
raise NotImplementedError('Mazání TreeNode se syny není zatím podporováno!') |
||||
|
treelib.disconnect_node(node) |
||||
|
node.delete() |
||||
|
return redirect(request.headers.get('referer')) |
||||
|
|
||||
|
|
||||
|
class TreeNodeOdvesitPrycView(generic.base.View): |
||||
|
def post(self, request, *args, **kwargs): |
||||
|
node = s.TreeNode.objects.get(pk=self.kwargs['pk']) |
||||
|
treelib.disconnect_node(node) |
||||
|
node.root = None |
||||
|
node.save() |
||||
|
return redirect(request.headers.get('referer')) |
||||
|
|
||||
|
|
||||
|
class TreeNodePodvesitView(generic.base.View): |
||||
|
def post(self, request, *args, **kwargs): |
||||
|
node = s.TreeNode.objects.get(pk=self.kwargs['pk']) |
||||
|
kam = self.kwargs['kam'] |
||||
|
if kam == 'pred': |
||||
|
treelib.lower_node(node) |
||||
|
elif kam == 'za': |
||||
|
raise NotImplementedError('Podvěsit za není zatím podporováno') |
||||
|
return redirect(request.headers.get('referer')) |
||||
|
|
||||
|
|
||||
|
class TreeNodeProhoditView(generic.base.View): |
||||
|
def post(self, request, *args, **kwargs): |
||||
|
node = s.TreeNode.objects.get(pk=self.kwargs['pk']) |
||||
|
treelib.swap_succ(node) |
||||
|
return redirect(request.headers.get('referer')) |
||||
|
#FIXME ve formulari predat puvodni url a vratit redirect na ni |
||||
|
|
||||
|
class SirotcinecView(generic.ListView): |
||||
|
model = s.TreeNode |
||||
|
template_name = 'treenode/orphanage.html' |
||||
|
|
||||
|
def get_queryset(self): |
||||
|
return s.TreeNode.objects.not_instance_of(s.RocnikNode).filter(root=None,prev=None,succ=None,father_of_first=None) |
||||
|
|
||||
|
# FIXME pouzit Django REST Framework |
||||
|
class TextWebView(generic.DetailView): |
||||
|
model = s.Text |
||||
|
|
||||
|
def get(self,request,*args, **kwargs): |
||||
|
self.object = self.get_object() |
||||
|
return JsonResponse(model_to_dict(self.object,exclude='do_cisla')) |
||||
|
|
||||
|
|
||||
|
class VueTestView(generic.TemplateView): |
||||
|
template_name = 'treenode/vuetest.html' |
||||
|
|
||||
|
|
||||
|
class NahrajObrazekKTreeNoduView(LoginRequiredMixin, CreateView): |
||||
|
model = s.Obrazek |
||||
|
form_class = f.NahrajObrazekKTreeNoduForm |
||||
|
|
||||
|
def get_initial(self): |
||||
|
initial = super().get_initial() |
||||
|
initial['na_web'] = self.request.FILES['upload'] |
||||
|
return initial |
||||
|
|
||||
|
|
||||
|
def form_valid(self,form): |
||||
|
print(self.request.headers) |
||||
|
print(self.request.headers['Textid']) |
||||
|
print(form.instance) |
||||
|
print(form) |
||||
|
self.object = form.save(commit=False) |
||||
|
print(self.object.na_web) |
||||
|
self.object.text = m.Text.objects.get(pk=int(self.request.headers['Textid'])) |
||||
|
self.object.save() |
||||
|
|
||||
|
return JsonResponse({"url":self.object.na_web.url}) |
Loading…
Reference in new issue