diff --git a/mamweb/settings_common.py b/mamweb/settings_common.py index 718d0a3a..fa070a86 100644 --- a/mamweb/settings_common.py +++ b/mamweb/settings_common.py @@ -142,6 +142,7 @@ INSTALLED_APPS = ( 'vysledkovky', 'personalni', 'soustredeni', + 'treenode', # Admin upravy: diff --git a/mamweb/urls.py b/mamweb/urls.py index 3a0eb5d2..593bddde 100644 --- a/mamweb/urls.py +++ b/mamweb/urls.py @@ -6,7 +6,7 @@ from django.views.generic.base import TemplateView from django import views from django.urls import path # As per docs. -from .routers import router +from treenode.routers import router urlpatterns = [ @@ -39,6 +39,9 @@ urlpatterns = [ # Api (ma vlastni podadresare) (autocomplete apod.) path('', include('api.urls')), + # treenode (ma vlastni podadresare) + path('', include('treenode.urls')), + # Aesop (ma vlastni podadresare) path('', include('aesop.urls')), diff --git a/seminar/admin.py b/seminar/admin.py index 15e60c28..aa1310b7 100644 --- a/seminar/admin.py +++ b/seminar/admin.py @@ -12,7 +12,6 @@ from seminar.utils import hlavni_problem # Todo: reversion import seminar.models as m -import seminar.treelib as tl admin.site.register(m.Rocnik) @@ -153,88 +152,5 @@ class ResitelInline(admin.TabularInline): admin.site.register(m.Pohadka) admin.site.register(m.Obrazek) - -# 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 - - admin.site.register(m.Nastaveni, SingletonModelAdmin) admin.site.register(m.Novinky) diff --git a/seminar/migrations/0001_squashed_0098_auto_20210906_0305.py b/seminar/migrations/0001_squashed_0098_auto_20210906_0305.py index aedd460c..a95f9c1b 100644 --- a/seminar/migrations/0001_squashed_0098_auto_20210906_0305.py +++ b/seminar/migrations/0001_squashed_0098_auto_20210906_0305.py @@ -12,7 +12,7 @@ import taggit.managers from datetime import date from django.db.models import Q -from seminar.treelib import get_parent +from treenode.treelib import get_parent import datetime as dt diff --git a/seminar/migrations/0084_clanek_cislo.py b/seminar/migrations/0084_clanek_cislo.py index 7a211fa6..ffc6a29d 100644 --- a/seminar/migrations/0084_clanek_cislo.py +++ b/seminar/migrations/0084_clanek_cislo.py @@ -2,7 +2,7 @@ from django.db import migrations, models import django.db.models.deletion -from seminar.treelib import get_parent +from treenode.treelib import get_parent import logging logger = logging.getLogger(__name__) diff --git a/seminar/models/models_all.py b/seminar/models/models_all.py index 64dde9a1..032139ee 100644 --- a/seminar/models/models_all.py +++ b/seminar/models/models_all.py @@ -25,7 +25,7 @@ from reversion import revisions as reversion from seminar.utils import roman, FirstTagParser # Pro získání úryvku z TextNode from seminar.utils import hlavni_problem -from seminar import treelib +from treenode import treelib from unidecode import unidecode # Používám pro získání ID odkazu (ještě je to někde po někom zakomentované) @@ -728,7 +728,7 @@ class MezicisloNode(TreeNode): # TODO: Využít TreeLib def aktualizuj_nazev(self): - from seminar.treelib import safe_pred + from treenode.treelib import safe_pred if safe_pred(self) is not None: if (self.prev.get_real_instance_class() != CisloNode and self.prev.get_real_instance_class() != MezicisloNode): diff --git a/seminar/testutils.py b/seminar/testutils.py index 019f66ac..a66dea4b 100644 --- a/seminar/testutils.py +++ b/seminar/testutils.py @@ -17,7 +17,7 @@ import seminar.models as m from django.contrib.flatpages.models import FlatPage from django.contrib.sites.models import Site -from seminar.treelib import all_children, insert_last_child, all_children_of_type, create_node_after +from treenode.treelib import all_children, insert_last_child, all_children_of_type, create_node_after User = django.contrib.auth.get_user_model() @@ -752,8 +752,8 @@ def gen_clanek(rnd, organizatori, resitele): # Bude to celý text reseni.text_cely = reseninode reseni.save() - - from seminar.treelib import insert_last_child, create_child + + from treenode.treelib import insert_last_child, create_child insert_last_child(cislonode, reseninode) # Vyrobíme nějaký obsah diff --git a/seminar/urls.py b/seminar/urls.py index 8f6a04e5..182bbe7e 100644 --- a/seminar/urls.py +++ b/seminar/urls.py @@ -17,16 +17,6 @@ urlpatterns = [ path('rocnik//', views.RocnikView.as_view(), name='seminar_rocnik'), path('cislo/./', views.CisloView.as_view(), name='seminar_cislo'), path('problem//', views.problemView, name='seminar_problem'), - #path('treenode//', views.TreeNodeView.as_view(), name='seminar_treenode'), - #path('treenode//json/', views.TreeNodeJSONView.as_view(), name='seminar_treenode_json'), - #path('treenode/text//', views.TextWebView.as_view(), name='seminar_textnode_web'), - #path('treenode/editor/pridat////', views.TreeNodePridatView.as_view(), name='treenode_pridat'), - #path('treenode/editor/smazat//', views.TreeNodeSmazatView.as_view(), name='treenode_smazat'), - #path('treenode/editor/odvesitpryc//', views.TreeNodeOdvesitPrycView.as_view(), name='treenode_odvesitpryc'), - #path('treenode/editor/podvesit///', views.TreeNodePodvesitView.as_view(), name='treenode_podvesit'), - #path('treenode/editor/prohodit//', views.TreeNodeProhoditView.as_view(), name='treenode_prohodit'), - #path('treenode/sirotcinec/', views.SirotcinecView.as_view(), name='seminar_treenode_sirotcinec'), - #path('problem/(?P\d+)/(?P\d+)/', views.PrispevekView.as_view(), name='seminar_problem_prispevek'), # Zadani # path('aktualni/zadani/', views.AktualniZadaniView.as_view(), name='seminar_aktualni_zadani'), # Dočasně ad-hoc jednoduchá věc. @@ -76,9 +66,6 @@ urlpatterns = [ org_required(views.OdmenyView.as_view()), name="seminar_archiv_odmeny"), - re_path(r'^temp/vue/.*$',views.VueTestView.as_view(),name='vue_test_view'), - path('temp/image_upload/', views.NahrajObrazekKTreeNoduView.as_view()), - path('', views.TitulniStranaView.as_view(), name='titulni_strana'), path('jak-resit/', views.JakResitView.as_view(), name='jak_resit'), ] diff --git a/seminar/utils.py b/seminar/utils.py index 6d0a0b72..bb4cdfe7 100644 --- a/seminar/utils.py +++ b/seminar/utils.py @@ -20,7 +20,7 @@ from enum import auto import logging import seminar.models as m -import seminar.treelib as t +import treenode.treelib as t logger = logging.getLogger(__name__) diff --git a/seminar/views/__init__.py b/seminar/views/__init__.py index 22c60734..8db4424b 100644 --- a/seminar/views/__init__.py +++ b/seminar/views/__init__.py @@ -1,5 +1,4 @@ from .views_all import * -from .views_rest import * # Dočsasné views from .docasne import * diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 8dc8c0ee..fe0d42de 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -1,25 +1,22 @@ -from django.forms import model_to_dict -from django.shortcuts import get_object_or_404, render, redirect -from django.http import HttpResponse, JsonResponse +from django.shortcuts import get_object_or_404, render +from django.http import HttpResponse from django.urls import reverse from django.core.exceptions import ObjectDoesNotExist from django.views import generic from django.utils.translation import ugettext as _ from django.http import Http404 from django.db.models import Q, Sum, Count -from django.views.generic.edit import CreateView from django.views.generic.base import RedirectView -from django.contrib.auth.mixins import LoginRequiredMixin from django.core.exceptions import PermissionDenied import seminar.models as s import seminar.models as m from seminar.models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Organizator, Resitel, Novinky, Tema, Clanek # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci #from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva -from seminar import utils, treelib -import seminar.forms as f -import seminar.templatetags.treenodes as tnltt -import seminar.views.views_rest as vr +from seminar import utils +from treenode import treelib +import treenode.templatetags as tnltt +import treenode.serializers as vr from vysledkovky.utils import body_resitelu from vysledkovky.views import vysledkovka_rocniku, vysledkovka_cisla @@ -209,141 +206,6 @@ class TNLData(object): def __repr__(self): return("TNL({})".format(self.node)) -class TreeNodeView(generic.DetailView): - model = s.TreeNode - template_name = 'seminar/treenode.html' - - def get_context_data(self,**kwargs): - context = super().get_context_data(**kwargs) - context['tnldata'] = 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 = 'seminar/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')) - # FIXME: Pozor, níž je ještě jeden ProblemView! #class ProblemView(generic.DetailView): @@ -916,40 +778,6 @@ def StavDatabazeView(request): 'jmena_zen': utils.histogram([r.osoba.jmeno for r in zeny]), }) -### Formulare - -# pro přidání políčka do formuláře je potřeba -# - mít v modelu tu položku, kterou chci upravovat -# - přidat do views (prihlaskaView, resitelEditView) -# - přidat do forms -# - includovat do html - - -class VueTestView(generic.TemplateView): - template_name = 'seminar/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}) - # Interní, nemá se nikdy objevit v urls (jinak to účastníci vytrolí) def formularOKView(request, text=''): diff --git a/treenode/__init__.py b/treenode/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/treenode/admin.py b/treenode/admin.py new file mode 100644 index 00000000..d2ff4409 --- /dev/null +++ b/treenode/admin.py @@ -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 + + diff --git a/treenode/apps.py b/treenode/apps.py new file mode 100644 index 00000000..95139dc6 --- /dev/null +++ b/treenode/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class TreenodeConfig(AppConfig): + name = 'treenode' diff --git a/seminar/forms.py b/treenode/forms.py similarity index 100% rename from seminar/forms.py rename to treenode/forms.py diff --git a/treenode/migrations/__init__.py b/treenode/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/seminar/permissions.py b/treenode/permissions.py similarity index 100% rename from seminar/permissions.py rename to treenode/permissions.py diff --git a/mamweb/routers.py b/treenode/routers.py similarity index 94% rename from mamweb/routers.py rename to treenode/routers.py index 003dd5a6..9a7aba58 100644 --- a/mamweb/routers.py +++ b/treenode/routers.py @@ -1,5 +1,5 @@ from rest_framework import routers -from seminar import viewsets as vs +from treenode import viewsets as vs router = routers.DefaultRouter() diff --git a/seminar/views/views_rest.py b/treenode/serializers.py similarity index 99% rename from seminar/views/views_rest.py rename to treenode/serializers.py index ef49b3cc..eedb03b1 100644 --- a/seminar/views/views_rest.py +++ b/treenode/serializers.py @@ -2,7 +2,7 @@ from rest_framework import serializers from rest_polymorphic.serializers import PolymorphicSerializer import seminar.models as m -from seminar import treelib +from treenode import treelib DEFAULT_NODE_DEPTH = 2 diff --git a/seminar/static/seminar/treenode_editor.js b/treenode/static/treenode/treenode_editor.js similarity index 100% rename from seminar/static/seminar/treenode_editor.js rename to treenode/static/treenode/treenode_editor.js diff --git a/seminar/templates/seminar/orphanage.html b/treenode/templates/treenode/orphanage.html similarity index 100% rename from seminar/templates/seminar/orphanage.html rename to treenode/templates/treenode/orphanage.html diff --git a/seminar/templates/seminar/treenode.html b/treenode/templates/treenode/treenode.html similarity index 100% rename from seminar/templates/seminar/treenode.html rename to treenode/templates/treenode/treenode.html diff --git a/seminar/templates/seminar/treenode_add_stub.html b/treenode/templates/treenode/treenode_add_stub.html similarity index 100% rename from seminar/templates/seminar/treenode_add_stub.html rename to treenode/templates/treenode/treenode_add_stub.html diff --git a/seminar/templates/seminar/treenode_name.html b/treenode/templates/treenode/treenode_name.html similarity index 100% rename from seminar/templates/seminar/treenode_name.html rename to treenode/templates/treenode/treenode_name.html diff --git a/seminar/templates/seminar/treenode_recursive.html b/treenode/templates/treenode/treenode_recursive.html similarity index 100% rename from seminar/templates/seminar/treenode_recursive.html rename to treenode/templates/treenode/treenode_recursive.html diff --git a/seminar/templates/seminar/vuetest.html b/treenode/templates/treenode/vuetest.html similarity index 100% rename from seminar/templates/seminar/vuetest.html rename to treenode/templates/treenode/vuetest.html diff --git a/seminar/templatetags/treenodes.py b/treenode/templatetags.py similarity index 98% rename from seminar/templatetags/treenodes.py rename to treenode/templatetags.py index d3da23ce..e5efe701 100644 --- a/seminar/templatetags/treenodes.py +++ b/treenode/templatetags.py @@ -17,7 +17,7 @@ def nodeType(value): if isinstance(value,UlohaZadaniNode): return "Zadání úlohy" if isinstance(value,PohadkaNode): return "Pohádka" -### NASLEDUJICI FUNKCE SE POUZIVAJI VE views_all.py V SEKCI PRIPRAVJICI TNLData +### NASLEDUJICI FUNKCE SE POUZIVAJI VE views.py V SEKCI PRIPRAVJICI TNLData ### NEMAZAT, PRESUNOUT S TNLDaty NEKAM BOKEM @register.filter diff --git a/seminar/tests_treelib.py b/treenode/tests.py similarity index 98% rename from seminar/tests_treelib.py rename to treenode/tests.py index 3245d0a6..32a77196 100644 --- a/seminar/tests_treelib.py +++ b/treenode/tests.py @@ -1,5 +1,5 @@ from django.test import TestCase -import seminar.treelib as tl +import treenode.treelib as tl import seminar.models as m class SimpleTreeLibTests(TestCase): diff --git a/seminar/treelib.py b/treenode/treelib.py similarity index 100% rename from seminar/treelib.py rename to treenode/treelib.py diff --git a/treenode/urls.py b/treenode/urls.py new file mode 100644 index 00000000..60dc88ad --- /dev/null +++ b/treenode/urls.py @@ -0,0 +1,18 @@ +from django.urls import path, re_path +from . import views + +urlpatterns = [ + #path('treenode//', views.TreeNodeView.as_view(), name='seminar_treenode'), + #path('treenode//json/', views.TreeNodeJSONView.as_view(), name='seminar_treenode_json'), + #path('treenode/text//', views.TextWebView.as_view(), name='seminar_textnode_web'), + #path('treenode/editor/pridat////', views.TreeNodePridatView.as_view(), name='treenode_pridat'), + #path('treenode/editor/smazat//', views.TreeNodeSmazatView.as_view(), name='treenode_smazat'), + #path('treenode/editor/odvesitpryc//', views.TreeNodeOdvesitPrycView.as_view(), name='treenode_odvesitpryc'), + #path('treenode/editor/podvesit///', views.TreeNodePodvesitView.as_view(), name='treenode_podvesit'), + #path('treenode/editor/prohodit//', views.TreeNodeProhoditView.as_view(), name='treenode_prohodit'), + #path('treenode/sirotcinec/', views.SirotcinecView.as_view(), name='seminar_treenode_sirotcinec'), + #path('problem/(?P\d+)/(?P\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()), +] diff --git a/treenode/views.py b/treenode/views.py new file mode 100644 index 00000000..2c300263 --- /dev/null +++ b/treenode/views.py @@ -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}) diff --git a/seminar/viewsets.py b/treenode/viewsets.py similarity index 98% rename from seminar/viewsets.py rename to treenode/viewsets.py index 7e2ea63a..16dce6d6 100644 --- a/seminar/viewsets.py +++ b/treenode/viewsets.py @@ -3,10 +3,10 @@ from rest_framework import status from rest_framework.response import Response from django.core.exceptions import PermissionDenied from rest_framework.permissions import BasePermission, AllowAny -from . import models as m -from . import views +from seminar import models as m +import treenode.serializers as views -from seminar.permissions import AllowWrite +from treenode.permissions import AllowWrite class PermissionMixin(object): """ Redefines get_permissions so that only organizers can make changes. """