Skip to content

Commit 55b63cc

Browse files
committed
Merge branch 'release/0.0.2'
2 parents 774364e + d485d86 commit 55b63cc

File tree

7 files changed

+153
-10
lines changed

7 files changed

+153
-10
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,5 @@ TODO
1818
TODO.txt
1919
nohup.out
2020
.cache
21+
.tox
22+
__pycache__

.travis.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,17 @@ notifications:
1414
python:
1515
- "2.7"
1616

17+
env:
18+
- TOXENV=py27
19+
- TOXENV=py33
20+
1721
install:
1822
- pip install nose coverage coveralls
1923
- pip install -r requirements.txt
24+
- pip install tox
2025

2126
script:
27+
- tox
2228
- nosetests --with-coverage --cover-package=sqlalchemy_mptt --cover-erase --with-doctest
2329

2430
after_success:

sqlalchemy_mptt/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# Copyright © 2014 uralbash <root@uralbash.ru>
66
#
77
# Distributed under terms of the MIT license.
8-
from mixins import BaseNestedSets
8+
from .mixins import BaseNestedSets
99

1010
__version__ = "0.0.1"
1111
__mixins__ = [BaseNestedSets]

sqlalchemy_mptt/events.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ def mptt_before_update(mapper, connection, instance):
173173
table.c.tree_id == instance.tree_id))
174174
.order_by(table.c.lft)
175175
).fetchall()
176-
subtree = map(lambda x: x[0], subtree)
176+
subtree = [x[0] for x in subtree]
177177

178178
""" step 0: Initialize parameters.
179179

sqlalchemy_mptt/mixins.py

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from sqlalchemy.orm import backref, relationship
1616
from sqlalchemy.orm.session import Session
1717

18-
from events import mptt_before_delete, mptt_before_insert, mptt_before_update
18+
from .events import mptt_before_delete, mptt_before_insert, mptt_before_update
1919

2020

2121
class BaseNestedSets(object):
@@ -80,7 +80,7 @@ def move_after(self, node_id):
8080
session.add(self)
8181

8282
@classmethod
83-
def get_tree(self, session, json=False, json_fields=None):
83+
def get_tree(cls, session, json=False, json_fields=None):
8484
def recursive_node_to_dict(node):
8585
result = {'node': node}
8686
if json:
@@ -93,10 +93,55 @@ def recursive_node_to_dict(node):
9393
result['children'] = children
9494
return result
9595

96-
nodes = session.query(self).filter_by(parent_id=None)\
97-
.order_by(self.tree_id).all()
96+
nodes = session.query(cls).filter_by(parent_id=None)\
97+
.order_by(cls.tree_id).all()
9898
tree = []
99-
for i, node in enumerate(nodes):
99+
for node in nodes:
100100
tree.append(recursive_node_to_dict(node))
101101

102102
return tree
103+
104+
@classmethod
105+
def rebuild_tree(cls, session, tree_id):
106+
session.query(cls).filter_by(tree_id=tree_id)\
107+
.update({cls.left: 0, cls.right: 0, cls.level: 0})
108+
top = session.query(cls).filter_by(parent_id=None)\
109+
.filter_by(tree_id=tree_id).one()
110+
top.left = left = 1
111+
top.right = right = 2
112+
top.level = level = 1
113+
114+
def reqursive(children, left, right, level):
115+
level = level + 1
116+
for i, node in enumerate(children):
117+
same_level_right = children[i-1].right
118+
left = left + 1
119+
120+
if i > 0:
121+
left = left + 1
122+
if same_level_right:
123+
left = same_level_right + 1
124+
125+
right = left + 1
126+
node.left = left
127+
node.right = right
128+
parent = node.parent
129+
130+
j = 0
131+
while parent:
132+
parent.right = right + 1 + j
133+
parent = parent.parent
134+
j += 1
135+
136+
node.level = level
137+
reqursive(node.children, left, right, level)
138+
139+
reqursive(top.children, left, right, level)
140+
141+
@classmethod
142+
def rebuild(cls, session, tree_id=None):
143+
trees = session.query(cls).filter_by(parent_id=None)
144+
if tree_id:
145+
trees = trees.filter_by(tree_id=tree_id)
146+
for tree in trees:
147+
cls.rebuild_tree(session, tree.tree_id)

sqlalchemy_mptt/tests.py

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from sqlalchemy.ext.declarative import declarative_base
1616
from sqlalchemy.orm import sessionmaker
1717

18-
from mixins import BaseNestedSets
18+
from .mixins import BaseNestedSets
1919

2020
Base = declarative_base()
2121

@@ -106,6 +106,10 @@ def add_mptt_tree(session):
106106
add_fixture(Tree, tree2, session)
107107

108108

109+
def get_obj(session, id):
110+
return session.query(Tree).filter_by(id=id).one()
111+
112+
109113
class TestTree(unittest.TestCase):
110114

111115
def setUp(self):
@@ -1029,8 +1033,11 @@ def test_move_after_between_tree(self):
10291033

10301034
def test_get_tree(self):
10311035
tree = Tree.get_tree(self.session)
1032-
self.assertEqual(str(tree),
1033-
"[{'node': <Node (1)>, 'children': [{'node': <Node (2)>, 'children': [{'node': <Node (3)>}]}, {'node': <Node (4)>, 'children': [{'node': <Node (5)>}, {'node': <Node (6)>}]}, {'node': <Node (7)>, 'children': [{'node': <Node (8)>, 'children': [{'node': <Node (9)>}]}, {'node': <Node (10)>, 'children': [{'node': <Node (11)>}]}]}]}, {'node': <Node (12)>, 'children': [{'node': <Node (13)>, 'children': [{'node': <Node (14)>}]}, {'node': <Node (15)>, 'children': [{'node': <Node (16)>}, {'node': <Node (17)>}]}, {'node': <Node (18)>, 'children': [{'node': <Node (19)>, 'children': [{'node': <Node (20)>}]}, {'node': <Node (21)>, 'children': [{'node': <Node (22)>}]}]}]}]")
1036+
1037+
def go(id):
1038+
return get_obj(self.session, id)
1039+
self.assertEqual(tree,
1040+
[{'node': go(1), 'children': [{'node': go(2), 'children': [{'node': go(3)}]}, {'node': go(4), 'children': [{'node': go(5)}, {'node': go(6)}]}, {'node': go(7), 'children': [{'node': go(8), 'children': [{'node': go(9)}]}, {'node': go(10), 'children': [{'node': go(11)}]}]}]}, {'node': go(12), 'children': [{'node': go(13), 'children': [{'node': go(14)}]}, {'node': go(15), 'children': [{'node': go(16)}, {'node': go(17)}]}, {'node': go(18), 'children': [{'node': go(19), 'children': [{'node': go(20)}]}, {'node': go(21), 'children': [{'node': go(22)}]}]}]}])
10341041

10351042
def test_get_json_tree(self):
10361043
tree = Tree.get_tree(self.session, json=True)
@@ -1043,3 +1050,78 @@ def fields(node):
10431050
tree = Tree.get_tree(self.session, json=True, json_fields=fields)
10441051
self.assertEqual(tree, [{'visible': None, 'children': [{'visible': True, 'children': [{'visible': True, 'id': 3, 'label': '<Node (3)>'}], 'id': 2, 'label': '<Node (2)>'}, {'visible': True, 'children': [{'visible': True, 'id': 5, 'label': '<Node (5)>'}, {'visible': True, 'id': 6, 'label': '<Node (6)>'}], 'id': 4, 'label': '<Node (4)>'}, {'visible': True, 'children': [{'visible': True, 'children': [{'visible': None, 'id': 9, 'label': '<Node (9)>'}], 'id': 8, 'label': '<Node (8)>'}, {'visible': None, 'children': [{'visible': None, 'id': 11, 'label': '<Node (11)>'}], 'id': 10, 'label': '<Node (10)>'}], 'id': 7, 'label': '<Node (7)>'}], 'id': 1, 'label': '<Node (1)>'}, {
10451052
'visible': None, 'children': [{'visible': None, 'children': [{'visible': None, 'id': 14, 'label': '<Node (14)>'}], 'id': 13, 'label': '<Node (13)>'}, {'visible': None, 'children': [{'visible': None, 'id': 16, 'label': '<Node (16)>'}, {'visible': None, 'id': 17, 'label': '<Node (17)>'}], 'id': 15, 'label': '<Node (15)>'}, {'visible': None, 'children': [{'visible': None, 'children': [{'visible': None, 'id': 20, 'label': '<Node (20)>'}], 'id': 19, 'label': '<Node (19)>'}, {'visible': None, 'children': [{'visible': None, 'id': 22, 'label': '<Node (22)>'}], 'id': 21, 'label': '<Node (21)>'}], 'id': 18, 'label': '<Node (18)>'}], 'id': 12, 'label': '<Node (12)>'}])
1053+
1054+
def test_rebuild(self):
1055+
""" level Nested sets tree1
1056+
1 1(1)22
1057+
_______________|___________________
1058+
| | |
1059+
2 2(2)5 6(4)11 12(7)21
1060+
| ^ ^
1061+
3 3(3)4 7(5)8 9(6)10 13(8)16 17(10)20
1062+
| |
1063+
4 14(9)15 18(11)19
1064+
1065+
level Nested sets tree2
1066+
1 1(12)22
1067+
_______________|___________________
1068+
| | |
1069+
2 2(13)5 6(15)11 12(18)21
1070+
| ^ ^
1071+
3 3(14)4 7(16)8 9(17)10 13(19)16 17(21)20
1072+
| |
1073+
4 14(20)15 18(22)19
1074+
1075+
"""
1076+
self.session.query(Tree).update({'lft': 0, 'rgt': 0, 'level': 0})
1077+
Tree.rebuild(self.session, 1)
1078+
self.assertEqual(self.result.all(),
1079+
[(1, 1, 22, 1, None, 1),
1080+
(2, 2, 5, 2, 1, 1),
1081+
(3, 3, 4, 3, 2, 1),
1082+
(4, 6, 11, 2, 1, 1),
1083+
(5, 7, 8, 3, 4, 1),
1084+
(6, 9, 10, 3, 4, 1),
1085+
(7, 12, 21, 2, 1, 1),
1086+
(8, 13, 16, 3, 7, 1),
1087+
(9, 14, 15, 4, 8, 1),
1088+
(10, 17, 20, 3, 7, 1),
1089+
(11, 18, 19, 4, 10, 1),
1090+
1091+
(12, 0, 0, 0, None, 2),
1092+
(13, 0, 0, 0, 12, 2),
1093+
(14, 0, 0, 0, 13, 2),
1094+
(15, 0, 0, 0, 12, 2),
1095+
(16, 0, 0, 0, 15, 2),
1096+
(17, 0, 0, 0, 15, 2),
1097+
(18, 0, 0, 0, 12, 2),
1098+
(19, 0, 0, 0, 18, 2),
1099+
(20, 0, 0, 0, 19, 2),
1100+
(21, 0, 0, 0, 18, 2),
1101+
(22, 0, 0, 0, 21, 2)])
1102+
1103+
Tree.rebuild(self.session)
1104+
self.assertEqual(self.result.all(),
1105+
[(1, 1, 22, 1, None, 1),
1106+
(2, 2, 5, 2, 1, 1),
1107+
(3, 3, 4, 3, 2, 1),
1108+
(4, 6, 11, 2, 1, 1),
1109+
(5, 7, 8, 3, 4, 1),
1110+
(6, 9, 10, 3, 4, 1),
1111+
(7, 12, 21, 2, 1, 1),
1112+
(8, 13, 16, 3, 7, 1),
1113+
(9, 14, 15, 4, 8, 1),
1114+
(10, 17, 20, 3, 7, 1),
1115+
(11, 18, 19, 4, 10, 1),
1116+
1117+
(12, 1, 22, 1, None, 2),
1118+
(13, 2, 5, 2, 12, 2),
1119+
(14, 3, 4, 3, 13, 2),
1120+
(15, 6, 11, 2, 12, 2),
1121+
(16, 7, 8, 3, 15, 2),
1122+
(17, 9, 10, 3, 15, 2),
1123+
(18, 12, 21, 2, 12, 2),
1124+
(19, 13, 16, 3, 18, 2),
1125+
(20, 14, 15, 4, 19, 2),
1126+
(21, 17, 20, 3, 18, 2),
1127+
(22, 18, 19, 4, 21, 2)])

tox.ini

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# content of: tox.ini , put in same dir as setup.py
2+
[tox]
3+
envlist = py27,py33
4+
[testenv]
5+
deps=
6+
-rrequirements.txt
7+
nose
8+
commands=nosetests

0 commit comments

Comments
 (0)