1
# -*- coding: utf-8 -*-
2
# -----------------------------------------------------------------------------
3
# Gettings Things Gnome! - a personal organizer for the GNOME desktop
4
# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau
6
# This program is free software: you can redistribute it and/or modify it under
7
# the terms of the GNU General Public License as published by the Free Software
8
# Foundation, either version 3 of the License, or (at your option) any later
11
# This program is distributed in the hope that it will be useful, but WITHOUT
12
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16
# You should have received a copy of the GNU General Public License along with
17
# this program. If not, see <http://www.gnu.org/licenses/>.
18
# -----------------------------------------------------------------------------
20
from GTG.tools.logger import Log
25
def __init__(self, root=None):
29
self.pending_relationships = []
33
self.root = TreeNode(id=self.root_id)
34
self.root.set_tree(self)
37
return "<Tree: root = '%s'>" % (str(self.root))
39
def get_node_for_path(self, path):
40
return self._node_for_path(self.root,path)
42
def get_path_for_node(self, node):
43
toreturn = self._path_for_node(node)
46
#a deleted path can be requested only once
47
def get_deleted_path(self,id):
49
print "old paths areĀ : %s" %self.old_paths
50
if self.old_paths.has_key(id):
51
toreturn = self.old_paths.pop(id)
58
def set_root(self, root):
60
self.root.set_tree(self)
62
#We add a node. By default, it's a child of the root
63
def add_node(self, node, parent_id=None):
64
#print "*************adding node %s %s" %(node, parent)
66
if self.nodes.has_key(id):
67
print "Error : A node with this id %s already exists" %id
73
parent = self.get_node(parent_id)
75
node.set_parent(parent.get_id())
78
self.root.add_child(id)
80
#build the relationships that were waiting for that node
81
for rel in list(self.pending_relationships):
83
self.new_relationship(rel[0],rel[1])
86
#this will remove a node and all his children
87
#does nothing if the node doesn't exist
88
def remove_node(self, id):
89
node = self.get_node(id)
90
path = self.get_path_for_node(node)
95
for c_id in node.get_children():
96
self.remove_node(c_id)
98
for p_id in node.get_parents():
99
par = self.get_node(p_id)
102
self.root.remove_child(id)
103
self.old_paths[id] = path
106
#create a new relationship between nodes if it doesn't already exist
107
#return False if nothing was done
108
def new_relationship(self,parent_id,child_id):
109
Log.debug("new relationship between %s and %s" %(parent_id,child_id))
110
if [parent_id,child_id] in self.pending_relationships:
111
self.pending_relationships.remove([parent_id,child_id])
113
#no relationship allowed with yourself
114
if parent_id != child_id:
115
if parent_id == 'root':
116
# Log.debug(" -> adding %s to the root" %child_id)
119
p = self.get_node(parent_id)
120
c = self.get_node(child_id)
122
#no circular relationship allowed
123
if not p.has_parent(child_id) and not c.has_child(parent_id):
124
if not p.has_child(child_id):
125
p.add_child(child_id)
127
if parent_id != 'root' and not c.has_parent(parent_id):
128
c.add_parent(parent_id)
130
#removing the root from the list of parent
131
if self.root.has_child(child_id):
132
self.root.remove_child(child_id)
134
Log.debug(" * * * * * Relationship already existing")
136
#a circular relationship was found
138
Log.debug(" * * * * * Circular relationship found : undo")
139
self.break_relationship(parent_id,child_id)
142
#at least one of the node is not loaded. Save the relation for later
144
self.break_relationship(parent_id,child_id)
146
if [parent_id,child_id] not in self.pending_relationships:
147
self.pending_relationships.append([parent_id,child_id])
151
#break an existing relationship. The child is added to the root
152
#return False if the relationship didn't exist
153
def break_relationship(self,parent_id,child_id):
155
p = self.get_node(parent_id)
156
c = self.get_node(child_id)
158
if p.has_child(child_id):
159
ret = p.remove_child(child_id)
161
if c.has_parent(parent_id):
162
c.remove_parent(parent_id)
164
#if no more parent left, adding to the root
165
if not c.has_parent():
166
self.root.add_child(child_id)
169
#Trying to make a function that bypass the weirdiness of lists
170
def get_node(self,id):
171
return self.nodes.get(id)
173
def get_all_keys(self):
174
return list(self.nodes.keys())
176
def get_all_nodes(self):
178
for k in self.nodes.keys():
179
no = self.get_node(k)
184
def has_node(self, id):
185
return (self.nodes.get(id) != None)
187
def print_tree(self):
188
self._print_from_node(self.root)
190
def visit_tree(self, pre_func=None, post_func=None):
191
if self.root.has_child():
192
for c in self.root.get_children():
193
node = self.root.get_child(c)
194
self._visit_node(node, pre_func, post_func)
196
### HELPER FUNCTION FOR TREE #################################################
198
def _node_for_path(self,node,path):
199
if path[0] < node.get_n_children():
201
return node.get_nth_child(path[0])
203
node = node.get_nth_child(path[0])
205
return self._node_for_path(node, path)
209
def _path_for_node(self, node):
211
if node == self.root:
213
elif not node.has_parent():
214
index = self.root.get_child_index(node.get_id())
215
toreturn = self._path_for_node(self.root) + (index, )
217
# no multiparent support here
218
parent_id = node.get_parent()
219
if len(node.get_parents()) >= 2:
220
print "multiple parents for task %s" %node.get_id()
221
print "you should use a filteredtree above this tree"
222
parent = self.get_node(parent_id)
224
index = parent.get_child_index(node.get_id())
225
toreturn = self._path_for_node(parent) + (index, )
228
# print "returning %s" %str(toreturn)
233
def _print_from_node(self, node, prefix=""):
234
print prefix + node.id
235
prefix = prefix + " "
237
for c in node.get_children():
238
cur_node = node.get_child(c)
239
self._print_from_node(cur_node, prefix)
241
def _visit_node(self, node, pre_func=None, post_func=None):
245
for c in node.get_children():
246
cur_node = node.get_child(c)
247
self._visit_node(cur_node, pre_func, post_func)
254
def __init__(self, id, parent=None):
259
self.pending_relationship = []
261
self.add_parent(parent)
264
return "<TreeNode: '%s'>" % (self.id)
266
def set_tree(self,tree):
268
for rel in list(self.pending_relationship):
269
self.tree.new_relationship(rel[0],rel[1])
270
self.pending_relationship.remove(rel)
279
def new_relationship(self,par,chi):
281
return self.tree.new_relationship(par,chi)
283
self.pending_relationship.append([par,chi])
284
#it's pending, we return False
285
Log.debug("** There's still no tree, relationship is pending")
291
def has_parent(self,id=None):
293
return id in self.parents
295
toreturn = len(self.parents) > 0
298
#this one return only one parent.
299
#useful for tree where we know that there is only one
300
def get_parent(self):
301
#we should throw an error if there are multiples parents
302
if len(self.parents) > 1 :
303
print "Warning: get_parent will return one random parent for task %s because there are multiple parents." %(self.get_id())
304
print "Get_parent is deprecated. Please use get_parents instead"
305
if self.has_parent():
306
return self.parents[0]
310
def get_parents(self):
312
Return a list of parent ids
314
return list(self.parents)
316
def add_parent(self, parent_id):
317
# root = self.tree.get_root()
318
# print "removing root node has parent"
319
# self.tree.break_relationship(root.get_id(),self.get_id())
320
if parent_id not in self.parents:
321
self.parents.append(parent_id)
322
toreturn = self.new_relationship(parent_id, self.get_id())
324
# Log.debug("** parent addition failed (probably already done)*")
329
#set_parent means that we remove all other parents
330
def set_parent(self,par_id):
331
is_already_parent_flag = False
333
for i in self.parents:
335
assert(self.remove_parent(i) == True)
337
is_already_parent_flag = True
338
if not is_already_parent_flag:
339
self.add_parent(par_id)
341
def remove_parent(self,id):
342
if id in self.parents:
343
self.parents.remove(id)
344
ret = self.tree.break_relationship(id,self.get_id())
351
def has_child(self,id=None):
353
return id in self.children
355
return len(self.children) != 0
357
def get_children(self):
358
return list(self.children)
360
def get_n_children(self):
361
return len(self.children)
363
def get_nth_child(self, index):
365
id = self.children[index]
366
return self.tree.get_node(id)
368
raise ValueError("Index is not in the children list")
370
def get_child(self, id):
371
if id in self.children:
372
return self.tree.get_node(id)
376
def get_child_index(self, id):
377
if id in self.children:
378
return self.children.index(id)
382
#return True if the child was added correctly. False otherwise
383
#takes the id of the child as parameter.
384
#if the child is not already in the tree, the relation is anyway "saved"
385
def add_child(self, id):
386
if id not in self.children:
387
self.children.append(id)
388
toreturn = self.new_relationship(self.get_id(),id)
389
# Log.debug("new relationship : %s" %toreturn)
391
Log.debug("%s was already in children of %s" %(id,self.get_id()))
395
def remove_child(self, id):
396
if id in self.children:
397
self.children.remove(id)
398
ret = self.tree.break_relationship(self.get_id(),id)
404
def change_id(self,newid):
407
for p in self.parents:
408
par = self.tree.get(p)
409
par.remove_child(oldid)
410
par.add_child(self.id)
411
for c in self.get_children():
413
c.remove_parent(oldid)