From 981cbc8bf17b1070a7e53d9700a77fa4fa2760e6 Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Thu, 11 Jun 2020 18:35:49 +0000 Subject: [PATCH 1/7] =?UTF-8?q?TreeLib:=20explicitn=C4=9B=20o=C5=A1et?= =?UTF-8?q?=C5=99en=C3=A9=20node=3DNone=20v=20"z=C3=ADsk=C3=A1vac=C3=ADch"?= =?UTF-8?q?=20funkc=C3=ADch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/treelib.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/seminar/treelib.py b/seminar/treelib.py index 95fae9a9..06a710cf 100644 --- a/seminar/treelib.py +++ b/seminar/treelib.py @@ -15,11 +15,14 @@ def print_tree(node,indent=0): # Django je trošku hloupé, takže node.prev nevrací None, ale hází django.core.exceptions.ObjectDoesNotExist def safe_pred(node): + if node is None: + return None try: return node.prev except ObjectDoesNotExist: return None +# FIXME: Proč????? def safe_succ(node): try: return node.succ @@ -27,6 +30,8 @@ def safe_succ(node): return None def safe_father_of_first(node): + if node is None: + return None first = first_brother(node) try: return first.father_of_first @@ -42,6 +47,7 @@ def first_brother(node): return brother ## Rodinné vztahy +# Tohle se teď zrovna k None chová správně, ale je potřeba na to dávat pozor def get_parent(node): # Nejdřív získáme prvního potomka... while safe_pred(node) is not None: @@ -50,6 +56,8 @@ def get_parent(node): return safe_father_of_first(node) def get_last_child(node): + if node is None: + return None first = node.first_child if first is None: return None @@ -73,6 +81,8 @@ def is_orphan(node): # Obecný next: další Node v "the-right-order" pořadí (já, pak potomci, pak sousedé) def general_next(node): + if node is None: + return None # Máme potomka? if node.first_child is not None: return node.first_child @@ -85,6 +95,8 @@ def general_next(node): return node.succ def last_brother(node): + if node is None: + return None while node.succ is not None: node = node.succ return node @@ -92,6 +104,7 @@ def last_brother(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: + # Tady se ošetří node=None samo if safe_pred(node) is None: return safe_father_of_first(node) pred = safe_pred(node) @@ -109,12 +122,16 @@ def me_and_right_brothers(node): current = current.succ def right_brothers(node): + if node is None: + return 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): + if node is None: + return # Najdeme prvního bratra fb = first_brother(node) marb = me_and_right_brothers(fb) @@ -122,6 +139,8 @@ def all_brothers(node): yield cur def all_proper_brothers(node): + if node is None: + return all = all_brothers(node) for br in all: if br is node: @@ -130,12 +149,16 @@ def all_proper_brothers(node): def all_children(node): """ Generátor všech potomků zadaného Node. """ + if node is None: + return brothers = all_brothers(node.first_child) for br in brothers: yield br def all_children_of_type(node, type): """ Generuje všechny potomky daného Node a daného typu. """ + if node is None: + return brothers = all_brothers(node.first_child) for br in brothers: if isinstance(br, type): @@ -144,6 +167,8 @@ def all_children_of_type(node, type): # Generátor následníků v "the-right-order" # Bez tohoto vrcholu def all_following(node): + if node is None: + return current = general_next(node) while current is not None: yield current @@ -153,12 +178,16 @@ def all_following(node): # 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): + if node is None: + return for current in right_brothers(node): if isinstance(current, type): return current return None def get_prev_brother_of_type(node, type): + if node is None: + return # Na tohle není rozumný generátor, ani ho asi nechceme, prostě to implementujeme cyklem. current = node while safe_pred(current) is not None: @@ -169,6 +198,8 @@ def get_prev_brother_of_type(node, type): # Totéž pro "the-right-order" pořadí def get_next_node_of_type(node, type): + if node is None: + return for cur in all_folowing(node): if isinstance(cur, type): return cur @@ -176,6 +207,8 @@ def get_next_node_of_type(node, type): def get_prev_node_of_type(node, type): # Na tohle není rozumný generátor, ani ho asi nechceme, prostě to implementujeme cyklem. + if node is None: + return current = node while general_prev(current) is not None: current = general_prev(current) From 3d0adc6c9eadb37f545769edcae003d00ed7367f Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Thu, 11 Jun 2020 20:27:45 +0000 Subject: [PATCH 2/7] =?UTF-8?q?TreeLib:=20Dal=C5=A1=C3=AD=20none-checky?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zbývají rotace --- seminar/treelib.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/seminar/treelib.py b/seminar/treelib.py index 06a710cf..6efab260 100644 --- a/seminar/treelib.py +++ b/seminar/treelib.py @@ -218,9 +218,18 @@ def get_prev_node_of_type(node, type): +# 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 + # Editace stromu: def create_node_after(predecessor, type, **kwargs): + if predecessor is None: + raise TreeLibError("Nelze vyrábět sirotky! (predecessor=None)") + if not isinstance(type, TreeNode): + raise TreeLibError("Nový node není node!") new_node = type.objects.create(**kwargs) new_node.root = predecessor.root new_node.save() @@ -233,6 +242,10 @@ def create_node_after(predecessor, type, **kwargs): # Vyrábí prvního syna, ostatní nalepí za (existují-li) def create_child(parent, type, **kwargs): + if parent is None: + raise TreeLibError("Nelze vyrábět sirotky! (parent=None)") + if not isinstance(type, TreeNode): + raise TreeLibError("Nový node není node!") new_node = type.objects.create(**kwargs) new_node.root = parent.root new_node.save() @@ -246,6 +259,8 @@ def create_child(parent, type, **kwargs): return new_node def insert_last_child(parent, node): + if parent is None: + raise TreeLibError("Nelze vyrábět sirotky! (parent=None)") """ Zadaný Node přidá jako posledního potomka otce. """ last = get_last_child(parent) if not is_orphan(node): @@ -264,6 +279,10 @@ def insert_last_child(parent, node): last.save() def create_node_before(successor, type, **kwargs): + if successor is None: + raise TreeLibError("Nelze vyrábět sirotky! (successor=None)") + if not isinstance(type, TreeNode): + raise TreeLibError("Nový node není node!") if safe_pred(successor) is not None: # Easy: přidáme za předchůdce create_node_after(successor.prev, type, **kwargs) @@ -284,11 +303,6 @@ def create_node_before(successor, type, **kwargs): 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 - @transaction.atomic def swap_succ(node): From ebc5967cc58165f9e5204d31fb59706388d84c1e Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Thu, 11 Jun 2020 20:36:57 +0000 Subject: [PATCH 3/7] =?UTF-8?q?TreeLib:=20chyb=C4=9Bj=C3=ADc=C3=AD=20impor?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/treelib.py | 1 + 1 file changed, 1 insertion(+) diff --git a/seminar/treelib.py b/seminar/treelib.py index 6efab260..8784c2d9 100644 --- a/seminar/treelib.py +++ b/seminar/treelib.py @@ -1,5 +1,6 @@ from django.core.exceptions import ObjectDoesNotExist from django.db import transaction +from seminar.models import TreeNode # 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. From b6f2b94a0135b3487b0da258a601ff8f668f60b7 Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Thu, 11 Jun 2020 22:13:26 +0000 Subject: [PATCH 4/7] =?UTF-8?q?Opraven=C3=AD=20cyklick=C3=BDch=20import?= =?UTF-8?q?=C5=AF=20(pls=20don't=20kill=20me)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/models.py | 2 +- seminar/treelib.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/seminar/models.py b/seminar/models.py index c045b658..8a4090fa 100644 --- a/seminar/models.py +++ b/seminar/models.py @@ -28,7 +28,6 @@ from reversion import revisions as reversion from seminar.utils import roman, FirstTagParser # Pro získání úryvku z TextNode from unidecode import unidecode # Používám pro získání ID odkazu (ještě je to někde po někom zakomentované) -from seminar.treelib import safe_pred from polymorphic.models import PolymorphicModel @@ -1342,6 +1341,7 @@ class MezicisloNode(TreeNode): # TODO: Využít TreeLib def aktualizuj_nazev(self): + from seminar.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/treelib.py b/seminar/treelib.py index 8784c2d9..2fb53c8e 100644 --- a/seminar/treelib.py +++ b/seminar/treelib.py @@ -1,6 +1,5 @@ from django.core.exceptions import ObjectDoesNotExist from django.db import transaction -from seminar.models import TreeNode # 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. @@ -227,6 +226,7 @@ class TreeLibError(RuntimeError): # Editace stromu: def create_node_after(predecessor, type, **kwargs): + from seminar.models import TreeNode if predecessor is None: raise TreeLibError("Nelze vyrábět sirotky! (predecessor=None)") if not isinstance(type, TreeNode): @@ -243,6 +243,7 @@ def create_node_after(predecessor, type, **kwargs): # Vyrábí prvního syna, ostatní nalepí za (existují-li) def create_child(parent, type, **kwargs): + from seminar.models import TreeNode if parent is None: raise TreeLibError("Nelze vyrábět sirotky! (parent=None)") if not isinstance(type, TreeNode): @@ -280,6 +281,7 @@ def insert_last_child(parent, node): last.save() def create_node_before(successor, type, **kwargs): + from seminar.models import TreeNode if successor is None: raise TreeLibError("Nelze vyrábět sirotky! (successor=None)") if not isinstance(type, TreeNode): From 15449ae24bd7bbc22b550e8c5d4339664b7f7ac5 Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Thu, 11 Jun 2020 22:51:55 +0000 Subject: [PATCH 5/7] TreeLib: Fix type check --- seminar/treelib.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seminar/treelib.py b/seminar/treelib.py index 2fb53c8e..a17d9aee 100644 --- a/seminar/treelib.py +++ b/seminar/treelib.py @@ -229,7 +229,7 @@ def create_node_after(predecessor, type, **kwargs): from seminar.models import TreeNode if predecessor is None: raise TreeLibError("Nelze vyrábět sirotky! (predecessor=None)") - if not isinstance(type, TreeNode): + if not issubclass(type, TreeNode): raise TreeLibError("Nový node není node!") new_node = type.objects.create(**kwargs) new_node.root = predecessor.root @@ -246,7 +246,7 @@ def create_child(parent, type, **kwargs): from seminar.models import TreeNode if parent is None: raise TreeLibError("Nelze vyrábět sirotky! (parent=None)") - if not isinstance(type, TreeNode): + if not issubclass(type, TreeNode): raise TreeLibError("Nový node není node!") new_node = type.objects.create(**kwargs) new_node.root = parent.root @@ -284,7 +284,7 @@ def create_node_before(successor, type, **kwargs): from seminar.models import TreeNode if successor is None: raise TreeLibError("Nelze vyrábět sirotky! (successor=None)") - if not isinstance(type, TreeNode): + if not issubclass(type, TreeNode): raise TreeLibError("Nový node není node!") if safe_pred(successor) is not None: # Easy: přidáme za předchůdce From 7e7303a50f23b9ed010973daad88cd968b7724fc Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Thu, 11 Jun 2020 23:39:12 +0000 Subject: [PATCH 6/7] TreeLib: opraveny rotace --- seminar/treelib.py | 62 +++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/seminar/treelib.py b/seminar/treelib.py index a17d9aee..bd47da4d 100644 --- a/seminar/treelib.py +++ b/seminar/treelib.py @@ -3,6 +3,7 @@ from django.db import transaction # 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. +# TODO: Do nějakých consistency-checků přidat hledání polo-sirotků (kteří nesplňují invarianty) # Slouží k debugování pro rychlé získání představy o podobě podstromu pod tímto TreeNode. def print_tree(node,indent=0): @@ -347,55 +348,76 @@ def swap_pred(node): # 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 + # FIXME: Trochu méně naivní, nevěřím tomu, prosím otestovat 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) + if C is None: + raise TreeLibError("Nelze povýšit vrchol, jenž nemá otce.") + E = C.succ # Může být None a ničemu to nevadí + subtree4_head = D.first_child # Může být None, ale pak se musí z 3P udělat přímo potomek D + subtree4_tail = last_brother(subtree4_head) # Měl by být None právě když je sub4_head=None + subtree3P_head = D.succ # Může být None a ničemu to nevadí + subtree3L_tail = safe_pred(D) # Pokud je None, D je první syn C a C má tedy skončit bezdětný # Prostor pro motlitbu... pass # Amen. - C.succ = D + # Teď už nesmíme spadnout, protože jinak skončíme se stromem v nekonzistentním stavu + C.succ = D # Nespadne C.save() - D.succ = E + D.succ = E # Nespadne D.save() - subtree3L_tail.succ = None - subtree3L_tail.save() - subtree4_tail.succ = subtree3P.head - subtree4_tail.save() + + if subtree3L_tail is not None: + subtree3L_tail.succ = None + subtree3L_tail.save() + else: + assert C.first_child is D + C.first_child = None + C.save() + + if subtree4_tail is not None: + subtree4_tail.succ = subtree3P_head + subtree4_tail.save() + else: + D.first_child = subtree3P_head + D.save() # To by mělo být všechno... +# (lower bude jednoduchá rotace, ne mega, existence jednoduché rotace mi došla až po nakreslení obrázku) 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 + D = C.succ # Může být None a ničemu to nevadí B = safe_pred(C) - subtree2_head = B.first_child - subtree2_tail = last_brother(subtree2_head) + if B is None: + raise TreeLibError("Nelze ponížit prvního syna (není pod co)") + subtree2_head = B.first_child # Je-li None, pak se z C má stát první syn + subtree2_tail = last_brother(subtree2_head) # None iff head=None, doufám # Prostor pro motlitbu... pass # Amen. - B.succ = D + # Teď už nesmíme spadnout, protože jinak skončíme se stromem v nekonzistentním stavu + B.succ = D # Nespadne B.save() - subtree2_tail.succ = C - subtree2_tail.save() + if subtree2_tail is not None: + subtree2_tail.succ = C + subtree2_tail.save() + else: + assert subtree2_head is None + B.first_child = C + B.save() # To by mělo být všechno... From 5c11369f5138d6b63ba65f887e6d67e558dfc446 Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Thu, 11 Jun 2020 23:42:47 +0000 Subject: [PATCH 7/7] =?UTF-8?q?Zp=C5=99esn=C4=9Bn=C3=AD=20koment=C3=A1?= =?UTF-8?q?=C5=99=C5=AF=20:-)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/treelib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seminar/treelib.py b/seminar/treelib.py index bd47da4d..fe4ebce0 100644 --- a/seminar/treelib.py +++ b/seminar/treelib.py @@ -3,7 +3,7 @@ from django.db import transaction # 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. -# TODO: Do nějakých consistency-checků přidat hledání polo-sirotků (kteří nesplňují invarianty) +# TODO: Do nějakých consistency-checků přidat hledání polo-sirotků (kteří nesplňují invarianty, třeba nejsou dosažitelní a mají root, vyrábějí DAG, ...) # Slouží k debugování pro rychlé získání představy o podobě podstromu pod tímto TreeNode. def print_tree(node,indent=0): @@ -395,7 +395,7 @@ 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 + # FIXME: Trochu naivní, prosím otestovat C = node D = C.succ # Může být None a ničemu to nevadí B = safe_pred(C)