Merge branch 'treenode_editor' of gimli.ms.mff.cuni.cz:/akce/mam/git/mamweb into treenode_editor
This commit is contained in:
		
						commit
						4bdc03e949
					
				
					 2 changed files with 99 additions and 27 deletions
				
			
		|  | @ -28,7 +28,6 @@ from reversion import revisions as reversion | ||||||
| from seminar.utils import roman, FirstTagParser # Pro získání úryvku z TextNode | 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 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 | from polymorphic.models import PolymorphicModel | ||||||
| 
 | 
 | ||||||
|  | @ -1342,6 +1341,7 @@ class MezicisloNode(TreeNode): | ||||||
| 
 | 
 | ||||||
| 	# TODO: Využít TreeLib | 	# TODO: Využít TreeLib | ||||||
| 	def aktualizuj_nazev(self): | 	def aktualizuj_nazev(self): | ||||||
|  | 		from seminar.treelib import safe_pred | ||||||
| 		if safe_pred(self) is not None: | 		if safe_pred(self) is not None: | ||||||
| 			if (self.prev.get_real_instance_class() != CisloNode and | 			if (self.prev.get_real_instance_class() != CisloNode and | ||||||
| 				self.prev.get_real_instance_class() != MezicisloNode): | 				self.prev.get_real_instance_class() != MezicisloNode): | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ from django.db import transaction | ||||||
| # NOTE: node.prev a node.succ jsou implementovány přímo v models.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: 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: 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, 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. | # Slouží k debugování pro rychlé získání představy o podobě podstromu pod tímto TreeNode. | ||||||
| def print_tree(node,indent=0): | def print_tree(node,indent=0): | ||||||
|  | @ -15,11 +16,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 | # Django je trošku hloupé, takže node.prev nevrací None, ale hází django.core.exceptions.ObjectDoesNotExist | ||||||
| def safe_pred(node): | def safe_pred(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		return None | ||||||
| 	try: | 	try: | ||||||
| 		return node.prev | 		return node.prev | ||||||
| 	except ObjectDoesNotExist: | 	except ObjectDoesNotExist: | ||||||
| 		return None | 		return None | ||||||
| 
 | 
 | ||||||
|  | # FIXME: Proč????? | ||||||
| def safe_succ(node): | def safe_succ(node): | ||||||
| 	try: | 	try: | ||||||
| 		return node.succ | 		return node.succ | ||||||
|  | @ -27,6 +31,8 @@ def safe_succ(node): | ||||||
| 		return None | 		return None | ||||||
| 
 | 
 | ||||||
| def safe_father_of_first(node): | def safe_father_of_first(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		return None | ||||||
| 	first = first_brother(node) | 	first = first_brother(node) | ||||||
| 	try: | 	try: | ||||||
| 		return first.father_of_first | 		return first.father_of_first | ||||||
|  | @ -42,6 +48,7 @@ def first_brother(node): | ||||||
| 	return brother | 	return brother | ||||||
| 
 | 
 | ||||||
| ## Rodinné vztahy | ## Rodinné vztahy | ||||||
|  | # Tohle se teď zrovna k None chová správně, ale je potřeba na to dávat pozor | ||||||
| def get_parent(node): | def get_parent(node): | ||||||
| 	# Nejdřív získáme prvního potomka... | 	# Nejdřív získáme prvního potomka... | ||||||
| 	while safe_pred(node) is not None: | 	while safe_pred(node) is not None: | ||||||
|  | @ -50,6 +57,8 @@ def get_parent(node): | ||||||
| 	return safe_father_of_first(node) | 	return safe_father_of_first(node) | ||||||
| 
 | 
 | ||||||
| def get_last_child(node): | def get_last_child(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		return None | ||||||
| 	first = node.first_child | 	first = node.first_child | ||||||
| 	if first is None: | 	if first is None: | ||||||
| 		return None | 		return None | ||||||
|  | @ -73,6 +82,8 @@ def is_orphan(node): | ||||||
| 
 | 
 | ||||||
| # Obecný next: další Node v "the-right-order" pořadí (já, pak potomci, pak sousedé) | # Obecný next: další Node v "the-right-order" pořadí (já, pak potomci, pak sousedé) | ||||||
| def general_next(node): | def general_next(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		return None | ||||||
| 	# Máme potomka? | 	# Máme potomka? | ||||||
| 	if node.first_child is not None: | 	if node.first_child is not None: | ||||||
| 		return node.first_child | 		return node.first_child | ||||||
|  | @ -85,6 +96,8 @@ def general_next(node): | ||||||
| 	return node.succ | 	return node.succ | ||||||
| 
 | 
 | ||||||
| def last_brother(node): | def last_brother(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		return None | ||||||
| 	while node.succ is not None: | 	while node.succ is not None: | ||||||
| 		node = node.succ | 		node = node.succ | ||||||
| 	return node | 	return node | ||||||
|  | @ -92,6 +105,7 @@ def last_brother(node): | ||||||
| def general_prev(node): | def general_prev(node): | ||||||
| 	# Předchůdce je buď rekurzivně poslední potomek předchůdce, nebo náš otec. | 	# Předchůdce je buď rekurzivně poslední potomek předchůdce, nebo náš otec. | ||||||
| 	# Otce vyřešíme nejdřív: | 	# Otce vyřešíme nejdřív: | ||||||
|  | 	# Tady se ošetří node=None samo | ||||||
| 	if safe_pred(node) is None: | 	if safe_pred(node) is None: | ||||||
| 		return safe_father_of_first(node) | 		return safe_father_of_first(node) | ||||||
| 	pred = safe_pred(node) | 	pred = safe_pred(node) | ||||||
|  | @ -109,12 +123,16 @@ def me_and_right_brothers(node): | ||||||
| 		current = current.succ | 		current = current.succ | ||||||
| 
 | 
 | ||||||
| def right_brothers(node): | def right_brothers(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		return | ||||||
| 	generator = me_and_right_brothers(node.succ) | 	generator = me_and_right_brothers(node.succ) | ||||||
| 	for item in generator: | 	for item in generator: | ||||||
| 		yield item | 		yield item | ||||||
| 
 | 
 | ||||||
| # Generátor všech sourozenců (vč. sám sebe) | # Generátor všech sourozenců (vč. sám sebe) | ||||||
| def all_brothers(node): | def all_brothers(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		return | ||||||
| 	# Najdeme prvního bratra | 	# Najdeme prvního bratra | ||||||
| 	fb = first_brother(node) | 	fb = first_brother(node) | ||||||
| 	marb = me_and_right_brothers(fb) | 	marb = me_and_right_brothers(fb) | ||||||
|  | @ -122,6 +140,8 @@ def all_brothers(node): | ||||||
| 		yield cur | 		yield cur | ||||||
| 	 | 	 | ||||||
| def all_proper_brothers(node): | def all_proper_brothers(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		return | ||||||
| 	all = all_brothers(node) | 	all = all_brothers(node) | ||||||
| 	for br in all: | 	for br in all: | ||||||
| 		if br is node: | 		if br is node: | ||||||
|  | @ -130,12 +150,16 @@ def all_proper_brothers(node): | ||||||
| 
 | 
 | ||||||
| def all_children(node): | def all_children(node): | ||||||
| 	""" Generátor všech potomků zadaného Node. """ | 	""" Generátor všech potomků zadaného Node. """ | ||||||
|  | 	if node is None: | ||||||
|  | 		return | ||||||
| 	brothers = all_brothers(node.first_child) | 	brothers = all_brothers(node.first_child) | ||||||
| 	for br in brothers: | 	for br in brothers: | ||||||
| 		yield br | 		yield br | ||||||
| 
 | 
 | ||||||
| def all_children_of_type(node, type): | def all_children_of_type(node, type): | ||||||
| 	""" Generuje všechny potomky daného Node a daného typu. """ | 	""" Generuje všechny potomky daného Node a daného typu. """ | ||||||
|  | 	if node is None: | ||||||
|  | 		return | ||||||
| 	brothers = all_brothers(node.first_child) | 	brothers = all_brothers(node.first_child) | ||||||
| 	for br in brothers: | 	for br in brothers: | ||||||
| 		if isinstance(br, type): | 		if isinstance(br, type): | ||||||
|  | @ -144,6 +168,8 @@ def all_children_of_type(node, type): | ||||||
| # Generátor následníků v "the-right-order" | # Generátor následníků v "the-right-order" | ||||||
| # Bez tohoto vrcholu | # Bez tohoto vrcholu | ||||||
| def all_following(node): | def all_following(node): | ||||||
|  | 	if node is None: | ||||||
|  | 		return | ||||||
| 	current = general_next(node) | 	current = general_next(node) | ||||||
| 	while current is not None: | 	while current is not None: | ||||||
| 		yield current | 		yield current | ||||||
|  | @ -153,12 +179,16 @@ def all_following(node): | ||||||
| # Najdi dalšího bratra nějakého typu, nebo None. | # 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. | # hledá i podtřídy, i.e. get_next_brother_of_type(neco, TreeNode) je prostě succ. | ||||||
| def get_next_brother_of_type(node, type): | def get_next_brother_of_type(node, type): | ||||||
|  | 	if node is None: | ||||||
|  | 		return | ||||||
| 	for current in right_brothers(node): | 	for current in right_brothers(node): | ||||||
| 		if isinstance(current, type): | 		if isinstance(current, type): | ||||||
| 			return current | 			return current | ||||||
| 	return None | 	return None | ||||||
| 	 | 	 | ||||||
| def get_prev_brother_of_type(node, type): | 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. | 	# Na tohle není rozumný generátor, ani ho asi nechceme, prostě to implementujeme cyklem. | ||||||
| 	current = node | 	current = node | ||||||
| 	while safe_pred(current) is not None: | 	while safe_pred(current) is not None: | ||||||
|  | @ -169,6 +199,8 @@ def get_prev_brother_of_type(node, type): | ||||||
| 
 | 
 | ||||||
| # Totéž pro "the-right-order" pořadí | # Totéž pro "the-right-order" pořadí | ||||||
| def get_next_node_of_type(node, type): | def get_next_node_of_type(node, type): | ||||||
|  | 	if node is None: | ||||||
|  | 		return | ||||||
| 	for cur in all_folowing(node): | 	for cur in all_folowing(node): | ||||||
| 		if isinstance(cur, type): | 		if isinstance(cur, type): | ||||||
| 			return cur | 			return cur | ||||||
|  | @ -176,6 +208,8 @@ def get_next_node_of_type(node, type): | ||||||
| 
 | 
 | ||||||
| def get_prev_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. | 	# Na tohle není rozumný generátor, ani ho asi nechceme, prostě to implementujeme cyklem. | ||||||
|  | 	if node is None: | ||||||
|  | 		return | ||||||
| 	current = node | 	current = node | ||||||
| 	while general_prev(current) is not None: | 	while general_prev(current) is not None: | ||||||
| 		current = general_prev(current) | 		current = general_prev(current) | ||||||
|  | @ -185,9 +219,19 @@ 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: | # Editace stromu: | ||||||
| def create_node_after(predecessor, type, **kwargs): | 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 issubclass(type, TreeNode): | ||||||
|  | 		raise TreeLibError("Nový node není node!") | ||||||
| 	new_node = type.objects.create(**kwargs) | 	new_node = type.objects.create(**kwargs) | ||||||
| 	new_node.root = predecessor.root | 	new_node.root = predecessor.root | ||||||
| 	new_node.save() | 	new_node.save() | ||||||
|  | @ -200,6 +244,11 @@ def create_node_after(predecessor, type, **kwargs): | ||||||
| 
 | 
 | ||||||
| # Vyrábí prvního syna, ostatní nalepí za (existují-li) | # Vyrábí prvního syna, ostatní nalepí za (existují-li) | ||||||
| def create_child(parent, type, **kwargs): | 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 issubclass(type, TreeNode): | ||||||
|  | 		raise TreeLibError("Nový node není node!") | ||||||
| 	new_node = type.objects.create(**kwargs) | 	new_node = type.objects.create(**kwargs) | ||||||
| 	new_node.root = parent.root | 	new_node.root = parent.root | ||||||
| 	new_node.save() | 	new_node.save() | ||||||
|  | @ -213,6 +262,8 @@ def create_child(parent, type, **kwargs): | ||||||
| 	return new_node | 	return new_node | ||||||
| 
 | 
 | ||||||
| def insert_last_child(parent, 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. """ | 	""" Zadaný Node přidá jako posledního potomka otce. """ | ||||||
| 	last = get_last_child(parent) | 	last = get_last_child(parent) | ||||||
| 	if not is_orphan(node): | 	if not is_orphan(node): | ||||||
|  | @ -231,6 +282,11 @@ def insert_last_child(parent, node): | ||||||
| 		last.save() | 		last.save() | ||||||
| 
 | 
 | ||||||
| def create_node_before(successor, type, **kwargs): | 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 issubclass(type, TreeNode): | ||||||
|  | 		raise TreeLibError("Nový node není node!") | ||||||
| 	if safe_pred(successor) is not None: | 	if safe_pred(successor) is not None: | ||||||
| 		# Easy: přidáme za předchůdce | 		# Easy: přidáme za předchůdce | ||||||
| 		create_node_after(successor.prev, type, **kwargs) | 		create_node_after(successor.prev, type, **kwargs) | ||||||
|  | @ -251,11 +307,6 @@ def create_node_before(successor, type, **kwargs): | ||||||
| def swap(node, other): | def swap(node, other): | ||||||
| 	raise NotImplementedError("YAGNI (You aren't gonna need it).") | 	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 | @transaction.atomic | ||||||
| def swap_succ(node): | def swap_succ(node): | ||||||
|  | @ -297,55 +348,76 @@ def swap_pred(node): | ||||||
| 
 | 
 | ||||||
| # Rotace stromu | # Rotace stromu | ||||||
| # Dokumentace viz wiki: | # Dokumentace viz wiki: | ||||||
| # (lower bude jednoduchá rotace, ne mega, existence jednoduché rotace mi došla až po nakreslení obrázku) |  | ||||||
| def raise_node(node): | def raise_node(node): | ||||||
| 	if node is None: | 	if node is None: | ||||||
| 		raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.") | 		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) | 	# 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 | 	D = node | ||||||
| 	C = get_parent(D) | 	C = get_parent(D) | ||||||
| 	E = C.succ | 	if C is None: | ||||||
| 	subtree4_head = D.first_child | 		raise TreeLibError("Nelze povýšit vrchol, jenž nemá otce.") | ||||||
| 	subtree4_tail = last_brother(subtree4_head) | 	E = C.succ	# Může být None a ničemu to nevadí | ||||||
| 	subtree3P_head = D.succ | 	subtree4_head = D.first_child	# Může být None, ale pak se musí z 3P udělat přímo potomek D | ||||||
| 	subtree3L_head = C.first_child | 	subtree4_tail = last_brother(subtree4_head)	# Měl by být None právě když je sub4_head=None | ||||||
| 	subtree3L_tail = safe_pred(D) | 	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... | 	# Prostor pro motlitbu... | ||||||
| 	pass | 	pass | ||||||
| 	 | 	 | ||||||
| 	# Amen. | 	# 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() | 	C.save() | ||||||
| 	D.succ = E | 	D.succ = E	# Nespadne | ||||||
| 	D.save() | 	D.save() | ||||||
|  | 
 | ||||||
|  | 	if subtree3L_tail is not None: | ||||||
| 		subtree3L_tail.succ = None | 		subtree3L_tail.succ = None | ||||||
| 		subtree3L_tail.save() | 		subtree3L_tail.save() | ||||||
| 	subtree4_tail.succ = subtree3P.head | 	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() | 		subtree4_tail.save() | ||||||
|  | 	else: | ||||||
|  | 		D.first_child = subtree3P_head | ||||||
|  | 		D.save() | ||||||
| 
 | 
 | ||||||
| 	# To by mělo být všechno... | 	# 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): | def lower_node(node): | ||||||
| 	if node is None: | 	if node is None: | ||||||
| 		raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.") | 		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) | 	# 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 | 	C = node | ||||||
| 	D = C.succ | 	D = C.succ	# Může být None a ničemu to nevadí | ||||||
| 	B = safe_pred(C) | 	B = safe_pred(C) | ||||||
| 	subtree2_head = B.first_child | 	if B is None: | ||||||
| 	subtree2_tail = last_brother(subtree2_head) | 		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... | 	# Prostor pro motlitbu... | ||||||
| 	pass | 	pass | ||||||
| 	 | 	 | ||||||
| 	# Amen. | 	# 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() | 	B.save() | ||||||
|  | 	if subtree2_tail is not None: | ||||||
| 		subtree2_tail.succ = C | 		subtree2_tail.succ = C | ||||||
| 		subtree2_tail.save() | 		subtree2_tail.save() | ||||||
|  | 	else: | ||||||
|  | 		assert subtree2_head is None | ||||||
|  | 		B.first_child = C | ||||||
|  | 		B.save() | ||||||
| 
 | 
 | ||||||
| 	# To by mělo být všechno... | 	# To by mělo být všechno... | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue