def test_OrderedSiblingsListNormal(self): """ test that the tree works if the walk is done parents last. """ graph = NestedTree() (leaf_node, end_node) = self.build_simple_tree(graph) # paint the tree. painted_list = graph.walkTree(self.all_nodes_level_function, NestedTreeNode.TREE_WALK_NORMAL) # we are only using the next function for this rest. for item in painted_list: found = graph.child_node.findItemWithColour( item[2], NestedTreeNode.TREE_WALK_NORMAL, True) self.assertIsNotNone(found) self.assertEqual(found.payload, item[1]) self.assertEqual(found.colour, item[2]) # paint the tree. painted_list = graph.walkTree(self.all_nodes_level_function, NestedTreeNode.TREE_WALK_PARENTS_FIRST) # we are only using the next function for this rest. for item in painted_list: found = graph.walkTree(self.find_colours_function, NestedTreeNode.TREE_WALK_PARENTS_FIRST, item[2]) self.assertIsNotNone(found) self.assertEqual(found.payload, item[1]) self.assertEqual(found.colour, item[2])
def test_5NodeWalk(self): """ 5 Node Walk - with insert before. This test adds three nodes in a row to see if the graph code works it also inserts two more nodes before node 2 and node 3. It then will walk the tree to see if the it finds all the nodes. """ node1 = NestedTreeNode(1) node2 = NestedTreeNode(2) node3 = NestedTreeNode(3) node4 = NestedTreeNode(4) node5 = NestedTreeNode(5) graph = NestedTree() # insert the three nodes graph.addNodeAfter(node1) node1.addNodeAfter(node2) node2.addNodeAfter(node3) # now insert the two extra nodes node2.addNodeBefore(node4) node3.addNodeBefore(node5) # we are only using the next function for this rest. count = 0 count = graph.walkTree(self.count_function) self.assertTrue(count == 5)
def test_TraceSimpleTree(self): """ Trace A Simple Tree The simple tree is used for this test. It should be a walk this tree and return the nodes in the correct order. """ graph = NestedTree() # now build the tree self.build_simple_tree(graph) # we are only using the next function for this rest. value = graph.walkTree(self.collect_function) self.assertTrue(value == list(range(1, 19)))
def __init__(self): self.name = 'Project' self.filename = None self.start_time = int((time.mktime(time.gmtime()) / (60 * 60 * 24))) * (60 * 60 * 24) # now self.days_per_week = 5 # 5 days self.minutes_per_day = 450 # 7.5 hours self.first_day_of_week = 0 # Monday self.last_task_id = 0 # task_id's start at 1 self.tasks = {} # dictionary of tasks self.plan_tree = NestedTree() # Create the node tree self.calcuateTheWeek() self.initialised = False # project plan state
def test_ParentsLasttTest(self): """ test that the tree works if the walk is done parents last. """ graph = NestedTree() self.build_simple_tree(graph) # we are only using the next function for this rest. value = graph.walkTree(self.all_nodes_level_function, NestedTreeNode.TREE_WALK_PARENTS_LAST) self.assertEqual(value, [(1, 1, 1), (1, 17, 2), (1, 18, 3), (1, 2, 4), (2, 3, 5), (2, 4, 6), (2, 9, 7), (2, 10, 8), (2, 5, 9), (3, 6, 10), (3, 7, 11), (3, 8, 12), (1, 11, 13), (2, 12, 14), (2, 13, 15), (2, 14, 16), (2, 15, 17), (2, 16, 18)])
def test_SingleChildNode(self): """ Single Child Node with Walk. This test will add a single child node. It then will walk the tree to see if the it finds all the nodes. """ node1 = NestedTreeNode(1) graph = NestedTree() # insert the child node graph.addChildNode(node1) # we are only using the next function for this rest. count = 0 count = graph.walkTree(self.count_function) self.assertTrue(count == 1)
def test_ParentsFirstTest(self): """ test that the tree works if the walk is done parents first. """ graph = NestedTree() self.build_simple_tree(graph) # we are only using the next function for this rest. value = graph.walkTree(self.all_nodes_level_function, NestedTreeNode.TREE_WALK_PARENTS_FIRST) # Has the order been returned correctly. self.assertEqual(value, [(1, 2, 1), (2, 5, 2), (3, 6, 3), (3, 7, 4), (3, 8, 5), (2, 3, 6), (2, 4, 7), (2, 9, 8), (2, 10, 9), (1, 11, 10), (2, 12, 11), (2, 13, 12), (2, 14, 13), (2, 15, 14), (2, 16, 15), (1, 1, 16), (1, 17, 17), (1, 18, 18), (0, None, 19)])
def test_addNodes(self): """ Add Nodes. This test adds three nodes in a row to see if the graph code works. This is an exploding test to see if the code works. """ node1 = NestedTreeNode(1) node2 = NestedTreeNode(2) node3 = NestedTreeNode(3) graph = NestedTree() graph.addNodeAfter(node1) node1.addNodeAfter(node2) node2.addNodeAfter(node3) self.assertTrue(graph.hasSibling()) self.assertTrue(node1.hasSibling()) self.assertTrue(node2.hasSibling())
def test_createTree(self): """ Create Object Test This is a simple test that checks that the tree object can be created correctly. This is an exploding tests, to see if the code functions.` """ graph = NestedTree() self.assertTrue(True)
def test_3NodeWalk(self): """ 3 Node Walk. This test adds three nodes in a row to see if the graph code works. It then will walk the tree to see if the it finds all the nodes. """ node1 = NestedTreeNode(1) node2 = NestedTreeNode(2) node3 = NestedTreeNode(3) graph = NestedTree() graph.addNodeAfter(node1) node1.addNodeAfter(node2) node2.addNodeAfter(node3) # we are only using the next function for this rest. count = 0 count = graph.walkTree(self.count_function) self.assertTrue(count == 3)
def test_OrderedSiblingsListFirst(self): graph = NestedTree() (leaf_node, end_node) = self.build_simple_tree(graph) leaf_node.addChildNode(NestedTreeNode(98)) leaf_node.addChildNode(NestedTreeNode(99)) leaf_node.setLeaf(True) # paint the tree - now with it's new leafy-ness painted_list = graph.walkTree(self.all_nodes_level_function, NestedTreeNode.TREE_WALK_PARENTS_FIRST) # we are only using the next function for this rest. for item in painted_list: found = graph.findItemWithColour( item[2], NestedTreeNode.TREE_WALK_PARENTS_FIRST, True) self.assertIsNotNone(found) self.assertEqual(found.payload, item[1]) self.assertEqual(found.colour, item[2]) next_node = leaf_node.next_node next_node.addChildNode(NestedTreeNode(108)) next_node.addChildNode(NestedTreeNode(109)) next_node.setLeaf(True) end_node.setLeaf(True) end_node.addChildNode(NestedTreeNode(209)) end_node.addChildNode(NestedTreeNode(210)) end_node.addChildNode(NestedTreeNode(211)) # paint the tree - now with it's new leafy-ness painted_list = graph.walkTree(self.all_nodes_level_function, NestedTreeNode.TREE_WALK_PARENTS_FIRST) # we are only using the next function for this rest. for item in painted_list: found = graph.findItemWithColour( item[2], NestedTreeNode.TREE_WALK_PARENTS_FIRST, True) self.assertIsNotNone(found) self.assertEqual(found.payload, item[1]) self.assertEqual(found.colour, item[2])
def build_tree(self, use_id=True, mark_no_walk=False): """ This function will create the following tree: {} this indicate sub-tree nodes that have been created. [root] | v [1] -> [2] -> [24] -> [40] -> [41] -> [42] -> [43] -> [44] -> [46] | | | | | v v v | {25} -> [26] -> [27] -> [28] -> [29] [45] [47] -> [48] | | | v | {30} -> [31] -> [32] -> [33] -> [34] | | | v | {35} -> [36] -> [37] -> [38] -> [39] | v {3} -> [4] -> [5] -> [12] -> [13] -> [14] -> [15] -> [22] -> [23] | | v v {6} -> [7] -> [8] {16} -> [17] -> [18] | | v v {9} -> [10] -> [11] {19} -> [20] -> [21] It should be a walk this tree and return the nodes in the correct order. If no walk is set, then nodes 3,15,34,41 and 46 a marked as no walk, so that only the nodes 16-21 and 47,48 will be skipped as they are no walkers. """ graph = NestedTree() # create the nodes nodes = [] if (use_id): for i in range(49): nodes.append(NestedTreeNode(i)) else: nodes.append(NestedTreeNode(TestPayload(True))) for i in range(1, 49): nodes.append(NestedTreeNode(TestPayload())) # [1] -> [2] -> [24] -> [40] -> [41] -> [42] -> [43] -> [44] -> [46] graph.addChildNode(nodes[1]) nodes[1].addNodeAfter(nodes[2]) nodes[2].addNodeAfter(nodes[24]) nodes[24].addNodeAfter(nodes[40]) nodes[40].addNodeAfter(nodes[41]) nodes[41].addNodeAfter(nodes[42]) nodes[42].addNodeAfter(nodes[43]) nodes[43].addNodeAfter(nodes[44]) nodes[44].addNodeAfter(nodes[46]) # [25] -> [26] -> [27] -> [28] -> [29] nodes[24].addSubTreeNode(nodes[25]) nodes[25].addNodeAfter(nodes[26]) nodes[26].addNodeAfter(nodes[27]) nodes[27].addNodeAfter(nodes[28]) nodes[28].addNodeAfter(nodes[29]) # subgraph of 24 - added from 24 # [30] -> [31] -> [32] -> [33] -> [34] nodes[24].addSubTreeNode(nodes[30]) nodes[30].addNodeAfter(nodes[31]) nodes[31].addNodeAfter(nodes[32]) nodes[32].addNodeAfter(nodes[33]) nodes[33].addNodeAfter(nodes[34]) # subtree of 24 - added from 24 # [35] -> [36] -> [37] -> [38] -> [39] nodes[24].addSubTreeNode(nodes[35]) nodes[35].addNodeAfter(nodes[36]) nodes[36].addNodeAfter(nodes[37]) nodes[37].addNodeAfter(nodes[38]) nodes[38].addNodeAfter(nodes[39]) # child of 44 - single nodes[44].addChildNode(nodes[45]) # child of 46 - pair and end tree on an up. nodes[46].addChildNode(nodes[47]) nodes[47].addNodeAfter(nodes[48]) # subtree of 2 # [3] -> [4] -> [5] -> [12] -> [13] -> [14] -> [15] -> [22] -> [23] nodes[2].addSubTreeNode(nodes[3]) nodes[3].addNodeAfter(nodes[4]) nodes[4].addNodeAfter(nodes[5]) nodes[5].addNodeAfter(nodes[12]) nodes[12].addNodeAfter(nodes[13]) nodes[13].addNodeAfter(nodes[14]) nodes[14].addNodeAfter(nodes[15]) nodes[15].addNodeAfter(nodes[22]) nodes[22].addNodeAfter(nodes[23]) # subtree of 5 # [6] -> [7] -> [8] nodes[5].addSubTreeNode(nodes[6]) nodes[6].addNodeAfter(nodes[7]) nodes[7].addNodeAfter(nodes[8]) # subtree of 5 # [9] -> [10] -> [11] nodes[5].addSubTreeNode(nodes[9]) nodes[9].addNodeAfter(nodes[10]) nodes[10].addNodeAfter(nodes[11]) # subtree of 15 # [16] -> [17] -> [18] nodes[15].addSubTreeNode(nodes[16]) nodes[16].addNodeAfter(nodes[17]) nodes[17].addNodeAfter(nodes[18]) # subtree of 15 # [19] -> [20] -> [21] nodes[15].addSubTreeNode(nodes[19]) nodes[19].addNodeAfter(nodes[20]) nodes[20].addNodeAfter(nodes[21]) # Ok, need to make a couple of nodes as no-walk nodes to test that the # short walk code works. # So 3, 15, 34, and 41 as this are all different in the tree and should prove that # the code works. if not use_id and mark_no_walk: nodes[3].payload.no_walk = True nodes[15].payload.no_walk = True nodes[34].payload.no_walk = True nodes[41].payload.no_walk = True nodes[46].payload.no_walk = True return graph
def test_ChildNodeInsertionDecending(self): """ This tests that the child node insertions work as expected. It will also check to see if they can be walked as expected. """ # test descending insertion - empty list graph = NestedTree() graph.addChildNode(NestedTreeNode(1), NestedTreeNode.INSERT_DESCENDING) graph.addChildNode(NestedTreeNode(4), NestedTreeNode.INSERT_DESCENDING) graph.addChildNode(NestedTreeNode(5), NestedTreeNode.INSERT_DESCENDING) graph.addChildNode(NestedTreeNode(6), NestedTreeNode.INSERT_DESCENDING) graph.addChildNode(NestedTreeNode(7), NestedTreeNode.INSERT_DESCENDING) graph.addChildNode(NestedTreeNode(3), NestedTreeNode.INSERT_DESCENDING) graph.addChildNode(NestedTreeNode(8), NestedTreeNode.INSERT_DESCENDING) graph.addChildNode(NestedTreeNode(2), NestedTreeNode.INSERT_DESCENDING) graph.addChildNode(NestedTreeNode(5), NestedTreeNode.INSERT_DESCENDING) self.assertEqual(graph.walkTree(self.all_nodes_function), [8, 7, 6, 5, 5, 4, 3, 2, 1], "Basic descending insert") # test normal insertion - empty list nodes = [] nodes.append(NestedTreeNode(TestPayload(True))) for i in range(1, 10): nodes.append(NestedTreeNode(TestPayload())) graph = NestedTree() graph.addChildNode(nodes[1], NestedTreeNode.INSERT_DESCENDING) graph.addChildNode(nodes[4], NestedTreeNode.INSERT_DESCENDING) graph.addChildNode(nodes[5], NestedTreeNode.INSERT_DESCENDING) graph.addChildNode(nodes[6], NestedTreeNode.INSERT_DESCENDING) graph.addChildNode(nodes[7], NestedTreeNode.INSERT_DESCENDING) graph.addChildNode(nodes[3], NestedTreeNode.INSERT_DESCENDING) graph.addChildNode(nodes[8], NestedTreeNode.INSERT_DESCENDING) graph.addChildNode(nodes[2], NestedTreeNode.INSERT_DESCENDING) graph.addChildNode(nodes[5], NestedTreeNode.INSERT_DESCENDING) nodes[7].addChildNode(nodes[9]) self.assertEqual((1, [(1, 8, 2), (1, 7, 3), (2, 9, 2), (1, 6, 1), (1, 5, 3), (1, 4, 3), (1, 3, 3), (1, 2, 3), (1, 1, 3)]), graph.walkTree(self.levels_function))
def test_OrderedSiblingsListLast(self): graph = NestedTree() (leaf_node, end_node) = self.build_simple_tree(graph) leaf_node.addChildNode(NestedTreeNode(98)) leaf_node.addChildNode(NestedTreeNode(99)) # paint the tree. painted_list = graph.walkTree(self.all_nodes_level_function, NestedTreeNode.TREE_WALK_PARENTS_LAST) for item in painted_list: found = graph.findItemWithColour( item[2], NestedTreeNode.TREE_WALK_PARENTS_LAST, True) self.assertIsNotNone(found) self.assertEqual(found.payload, item[1]) self.assertEqual(found.colour, item[2]) # corner case graphs nodes = [] for count in range(1, 6): nodes.append(NestedTreeNode(count)) graph = NestedTree() # the empty graph. found = graph.findItemWithColour(1, NestedTreeNode.TREE_WALK_NORMAL, True) self.assertIsNone(found) found = graph.findItemWithColour( 1, NestedTreeNode.TREE_WALK_PARENTS_FIRST, True) self.assertIsNone(found) found = graph.findItemWithColour(1, NestedTreeNode.TREE_WALK_PARENTS_LAST, True) self.assertIsNone(found) # single node graph.addNodeAfter(nodes[1]) # unpainted found = graph.findItemWithColour(1, NestedTreeNode.TREE_WALK_NORMAL, True) self.assertIsNone(found) found = graph.findItemWithColour( 1, NestedTreeNode.TREE_WALK_PARENTS_FIRST, True) self.assertIsNone(found) found = graph.findItemWithColour(1, NestedTreeNode.TREE_WALK_PARENTS_LAST, True) self.assertIsNone(found) # painted nodes[1].colour = 1 found = graph.findItemWithColour(1, NestedTreeNode.TREE_WALK_NORMAL, True) self.assertIsNotNone(found) found = graph.findItemWithColour( 1, NestedTreeNode.TREE_WALK_PARENTS_FIRST, True) self.assertIsNotNone(found) found = graph.findItemWithColour(1, NestedTreeNode.TREE_WALK_PARENTS_LAST, True) self.assertIsNotNone(found) # One child - only need to test painted. graph = NestedTree() graph.colour = 99 # colour the route or it won't search down. graph.addChildNode(nodes[1]) found = graph.findItemWithColour(1, NestedTreeNode.TREE_WALK_NORMAL, True) self.assertIsNotNone(found) found = graph.findItemWithColour( 1, NestedTreeNode.TREE_WALK_PARENTS_FIRST, True) self.assertIsNotNone(found) found = graph.findItemWithColour(1, NestedTreeNode.TREE_WALK_PARENTS_LAST, True) self.assertIsNotNone(found) graph.addChildNode(nodes[2]) graph.addChildNode(nodes[3]) painted_list = graph.walkTree(self.all_nodes_level_function, NestedTreeNode.TREE_WALK_NORMAL) for item in range(1, 4): found = graph.findItemWithColour(item, NestedTreeNode.TREE_WALK_NORMAL, True) self.assertIsNotNone(found) painted_list = graph.walkTree(self.all_nodes_level_function, NestedTreeNode.TREE_WALK_PARENTS_FIRST) for item in range(1, 4): found = graph.findItemWithColour( 1, NestedTreeNode.TREE_WALK_PARENTS_FIRST, True) self.assertIsNotNone(found) painted_list = graph.walkTree(self.all_nodes_level_function, NestedTreeNode.TREE_WALK_PARENTS_LAST) for item in range(1, 4): found = graph.findItemWithColour( 1, NestedTreeNode.TREE_WALK_PARENTS_FIRST, True) self.assertIsNotNone(found)
def test_ChildNodeInsertion(self): """ This tests that the child node insertions work as expected. It will also check to see if they can be walked as expected. """ # test normal insertion - empty list graph = NestedTree() graph.addChildNode(NestedTreeNode(1)) self.assertEqual(graph.walkTree(self.all_nodes_function), [1], "Basic insert failed") # test normal insertion - empty list - this is the same as above as is the default graph = NestedTree() graph.addChildNode(NestedTreeNode(1), NestedTreeNode.INSERT_END) graph.addChildNode(NestedTreeNode(2), NestedTreeNode.INSERT_END) graph.addChildNode(NestedTreeNode(3), NestedTreeNode.INSERT_END) self.assertEqual(graph.walkTree(self.all_nodes_function), [1, 2, 3], "Basic append insert") # test front insertion - empty list graph = NestedTree() graph.addChildNode(NestedTreeNode(3), NestedTreeNode.INSERT_FRONT) graph.addChildNode(NestedTreeNode(2), NestedTreeNode.INSERT_FRONT) graph.addChildNode(NestedTreeNode(1), NestedTreeNode.INSERT_FRONT) self.assertEqual(graph.walkTree(self.all_nodes_function), [1, 2, 3], "Basic in front insert") # test ascending insertion - empty list graph = NestedTree() graph.addChildNode(NestedTreeNode(8), NestedTreeNode.INSERT_ASCENDING) graph.addChildNode(NestedTreeNode(2), NestedTreeNode.INSERT_ASCENDING) graph.addChildNode(NestedTreeNode(3), NestedTreeNode.INSERT_ASCENDING) graph.addChildNode(NestedTreeNode(7), NestedTreeNode.INSERT_ASCENDING) graph.addChildNode(NestedTreeNode(6), NestedTreeNode.INSERT_ASCENDING) graph.addChildNode(NestedTreeNode(5), NestedTreeNode.INSERT_ASCENDING) graph.addChildNode(NestedTreeNode(1), NestedTreeNode.INSERT_ASCENDING) graph.addChildNode(NestedTreeNode(4), NestedTreeNode.INSERT_ASCENDING) graph.addChildNode(NestedTreeNode(5), NestedTreeNode.INSERT_ASCENDING) self.assertEqual(graph.walkTree(self.all_nodes_function), [1, 2, 3, 4, 5, 5, 6, 7, 8], "Basic in front insert")
def test_MakeTree(self): """ Make Tree The test checks that makeSubTree function works. So the following tree: [1] -> [2] -> [3] -> [7] -> [8] -> [9] -> [10] -> [11] -> [12] | {4} -> [5] -> [6] can be turned into: [1] -> [2] -> [3] | {4} -> [5] -> [6] | {7} -> [8] -> [9] | {10}-> [11] -> [12] by calling sub-tree on (3), (9) in that order. Then add a new subTree: [1] -> [2] -> [3] | {4} -> [5] -> [6] | {7} -> [8] -> [9] | | | {10}-> [11] -> [12] | {13} -> [14] -> [15] By adding a subTree to (3), this will prove it works. Also doing a next walk on (3). """ graph = NestedTree() # create the nodes nodes = [] for i in range(16): nodes.append(NestedTreeNode(i)) graph.addChildNode(nodes[1]) graph.addChildNode(nodes[2]) graph.addChildNode(nodes[3]) graph.addChildNode(nodes[7]) graph.addChildNode(nodes[8]) graph.addChildNode(nodes[9]) graph.addChildNode(nodes[10]) graph.addChildNode(nodes[11]) graph.addChildNode(nodes[12]) nodes[3].addChildNode(nodes[4]) nodes[4].addNodeAfter(nodes[5]) nodes[5].addNodeAfter(nodes[6]) self.assertEqual(graph.walkTree(self.collect_function), list(range(1, 13))) self.assertEqual(graph.walkTree(self.plain_levels_function), (2, [(1, 1), (1, 2), (1, 3), (2, 4), (2, 5), (2, 6), (1, 7), (1, 8), (1, 9), (1, 10), (1, 11), (1, 12)])) # Subtrees don't make sense unless the whole tree are subtrees. # ok, let's let makeSubTree do it's thing. # self.assertTrue(graph.makeSubTree(nodes[3])) # self.assertTrue(graph.makeSubTree(nodes[9])) # self.assertEqual(graph.walkTree(self.collect_function), range(1, 13)) # self.assertEqual(graph.walkTree(self.plain_levels_function), (5, [(1, 1), (1, 2), (1, 3), (3, 4), (3, 5), (3, 6), (3, 7), (3, 8), (3, 9), (5, 10), (5, 11), (5, 12)])) # ok, add the new sub-tree nodes[12].addSubTreeNode(nodes[13]) nodes[13].addNodeAfter(nodes[14]) nodes[14].addNodeAfter(nodes[15]) # walk the tree value = graph.walkTree(self.collect_function) # now check the ordering is correct self.assertEqual(value, list(range(1, 16))) # check the shape of the tree #self.assertFalse(nodes[3].hasSibling()) self.assertFalse(nodes[6].hasSibling()) self.assertFalse(nodes[12].hasSibling()) self.assertTrue(nodes[3].hasChild()) self.assertTrue(nodes[12].hasChild())
class ProjectPlan: # file states OPEN_READING_STATE = 1 LOAD_PROJECT_STATE = 2 LOAD_TASK_STATE = 3 """ Project Plan Class This class controls the project plan files. """ def __init__(self): self.name = 'Project' self.filename = None self.start_time = int((time.mktime(time.gmtime()) / (60 * 60 * 24))) * (60 * 60 * 24) # now self.days_per_week = 5 # 5 days self.minutes_per_day = 450 # 7.5 hours self.first_day_of_week = 0 # Monday self.last_task_id = 0 # task_id's start at 1 self.tasks = {} # dictionary of tasks self.plan_tree = NestedTree() # Create the node tree self.calcuateTheWeek() self.initialised = False # project plan state def calcuateTheWeek(self): """ Calculate the Week. This function adjusts the week arrays that are used to help with correcting the time. """ # create the week structures self.day_of_week = [0,0,0,0,0,0,0] self.days_to_week = [0,0,0,0,0,0,0] self.weekend = [0,0,0,0,0,0,0] for index in range(7): self.day_of_week[index] = ((index + 7) - self.first_day_of_week) % 7 if self.day_of_week[index] >= self.days_per_week: self.day_of_week[index] = 0 self.days_to_week[index] = (7 - index + self.first_day_of_week) % 7 self.weekend[index] = 1 def calculate_dates_function(self,last_visted_node,node,value,levels,direction,parameter): """ This is a function that is used to calculate the start and end times of the items in the tree. """ if direction == NestedTree.DIRECTION_DOWN and not node.is_sub_node: prev_payload = node.getPrevPayload() if prev_payload is None: node.payload.start = self.calculateStartTime(self.start_time) node.payload.end = self.calculateEndTimes(node.payload.start,node.payload.duration) else: node.payload.start = self.calculateStartTime(prev_payload.start) node.payload.end = self.calculateEndTimes(node.payload.start,node.payload.duration) elif direction == NestedTree.DIRECTION_NEXT: prev_payload = node.getPrevPayload() node.payload.start = self.calculateStartTime(prev_payload.end) node.payload.end = self.calculateEndTimes(node.payload.start,node.payload.duration) elif direction == NestedTree.DIRECTION_UP: prev_payload = node.getPrevPayload() # set the previous end if prev_payload.end < last_visted_node.payload.end: prev_payload.end = self.calculateStartTime(last_visted_node.payload.end) if not node.is_sub_node: # now set the current nodes start and end (and the previous end) # have stepped up a level node.payload.start = self.calculateStartTime(last_visted_node.payload.end) node.payload.end = self.calculateEndTimes(node.payload.start,node.payload.duration) return (node,1,False) def createProject(self,project_name,project_file): """ Create Project File. This function will create new project file. It will write the default settings to the file. """ if self.initialised: result = False elif project_name == None or project_name == '': result = False elif project_file == None or project_file == '': result = False else: self.name = project_name self.filename = project_file result = self.saveProject() self.initialised = result return result def reDateTree(self): """ Re-Calculate The Task Dates This function will re-calculate the dates for the items in the tree. This function should be called after any task (or group of tasks) have the duration amended, or a task has been added or removed. """ # set the start and endtimes of the tasks value = self.plan_tree.walkTree(self.calculate_dates_function) def loadProject(self,project_name,project_file): """ Load the Project Plan loadProject(<project_name>,<filename>) -> Boolean This function will load the project plan into the class. If the file does not exist then it will create an empty project plan. This function can only called once and any subsequent calls will fail. """ if self.initialised: result = False elif project_name == None or project_name == '': result = False elif project_file == None or project_file == '': result = False else: self.name = project_name self.filename = project_file try: # now open the project file proj_file = open(self.filename,'r') state = ProjectPlan.OPEN_READING_STATE result = True for line in proj_file: if line[0:1] == '[': # It's a section header, is it correct if line[1:-2] == "project": if state != ProjectPlan.OPEN_READING_STATE: self.error_string = "bad file format - [project] found in wrong place" result = False else: state = ProjectPlan.LOAD_PROJECT_STATE elif line[1:-2] == "tasks": if state != ProjectPlan.LOAD_PROJECT_STATE: self.error_string = "bad file format - [tasks] found in wrong place" result = False else: state = ProjectPlan.LOAD_TASK_STATE else: self.error_string = "bad file format - unknown [%s] found in project file" % (line[1:-2]) result = False else: if state == ProjectPlan.LOAD_PROJECT_STATE: parts = line.partition(" = ") if parts[0] == 'name': self.name = parts[2] elif parts[0] == 'starttime': self.start_time = int(parts[2]) elif parts[0] == 'daysperweek': self.days_per_week = int(parts[2]) elif parts[0] == 'minutesperday': self.minutes_per_day = int(parts[2]) elif parts[0] == 'firstdayofweek': self.first_day_of_week = int(parts[2]) elif state == ProjectPlan.LOAD_TASK_STATE: parts = line.partition(" = ") t_bits = parts[2].rstrip('\n').split(',') if len(t_bits) == 6: result = self.AddTask(int(parts[0][4:]),t_bits[0],t_bits[1],t_bits[2],t_bits[3],t_bits[4],int(t_bits[5])) else: result = False break # now update the week values self.calcuateTheWeek() # now calculate the start/end times self.reDateTree() except IOError: result = False self.initialised = result return result def AddTask(self,task_id,task_type,duration,status,name,description,follows_task): """ Add Task This function will add a task to the project plan. If the job number is None, then the function will create a new job and add it into the part of the tree specified by the parameters. """ result = False # Use the given Id or add a new one. if task_id is None: self.last_task_id = self.last_task_id + 1 task_id = self.last_task_id else: task_id = task_id if task_id > self.last_task_id: self.last_task_id = task_id if task_id not in self.tasks: self.tasks[task_id] = ProjectTask(task_id,task_type,duration,status,name,description,follows_task) if follows_task == 0 or follows_task is None: self.plan_tree.addChildNode(self.tasks[task_id].node) elif task_type == 'task': self.tasks[follows_task].node.addNodeAfter(self.tasks[task_id].node) elif task_type == 'sub_task': self.tasks[follows_task].node.addSubTreeNode(self.tasks[task_id].node) result = True return result def calculateEndTimes(self,start_time,duration): """ Calculate End Time. This function takes a task and calculates it's endtime from the duration. It will take into account the weekend and day lengths of the project to calculate this value. """ # how many weeks does the task take. weeks = (duration / self.minutes_per_day) / self.days_per_week remain_days = (duration / self.minutes_per_day) - (weeks * self.days_per_week) remains = duration - (((weeks * self.days_per_week) + remain_days) * self.minutes_per_day) # calculate end time end_time = start_time + (((((weeks * 7) + remain_days) * (60 * 24)) + remains) * 60) return end_time def calculateStartTime(self,start_time): """ Calculate Start Time. This function will take in a start time and adjust it to fit the working week. If the start time falls outside of the working day, or falls onto the weekend then the date will be moved onto the next working day. """ remains_of_the_day = start_time % (60 * 60 * 24) days = remains_of_the_day / (self.minutes_per_day * 60) # did the day rollover? start_time = start_time + (days * 60 * 60 * 24) - (days * self.minutes_per_day * 60) # in the weekend? weekend_adjustment = self.days_to_week[time.gmtime(start_time).tm_wday] * (60 * 60 * 24) start_time = start_time + weekend_adjustment return start_time def saveProject(self): """ This function saves the current project. This function saves the project header, then dumps all the tasks in the list and there connections. """ try: proj_file = open(self.filename,'w') # write the header header = [] header.append('[project]\n') ini_format = "%s = %s\n" header.append(ini_format % ('name',self.name)) header.append(ini_format % ('starttime',self.start_time)) header.append(ini_format % ('daysperweek',self.days_per_week)) header.append(ini_format % ('minutesperday',self.minutes_per_day)) header.append(ini_format % ('firstdayofweek',self.first_day_of_week)) header.append('[tasks]\n') # write the header proj_file.writelines(header) for task in self.tasks: proj_file.write(self.tasks[task].toFileString()) # write the footer and close the file proj_file.close() result = True except IOError: result = False return result def getProjectList(self,node): """ This function will generate a list of the project tasks. If the project tasks has sub-tasks then these will be listed as sub-lists. """ listing = [] for item in node.getChildren(): if item.hasChild(): children = item.getChildren() item_list = [] for c_list in children: sub_list = [] if type(c_list) == list: for child in c_list: l_list = child.getChildren() sub_list.append((child,0,l_list)) else: sub_list.append((c_list,0,c_list.getChildren())) item_list.append(sub_list) listing.append((item,0,item_list)) else: listing.append((item,0,[])) return listing