edgimar avatar edgimar committed f77fa86

- access children of TaskTree objects as list indices
- new TaskTree method: add_subtree() -- adds all the children of a subtree as
children of the current tree's root node.
- avoid "x or y" syntax in TaskTree.last(), which behaves incorrectly if
len(TaskTree()) is defined, and happens to be 0.
- concatenate_trees() function -- combine the children of two trees into one
tree.
- unit tests for concatenation and TaskTree.add_subtree()

Comments (0)

Files changed (2)

         self.notes = task_notes
         # *status* usually takes on the value 'completed' or 'needsAction'
         self.status = task_status
+        
+    def __getitem__(self, key):
+        return self.subtasks[key]
+    
+    def __setitem__(self, key, val):
+        self.subtasks[key] = val
+        
+    def __delitem__(self, key):
+        del(self.subtasks[key])
+    
+    def __len__(self):
+        return len(self.subtasks)
 
     def get(self, task_id):
         """Returns the task of given id"""
                 raise ValueError, "No element with suitable parent id"
             self.get(parent_id).add_subtask(title, task_id, None, task_notes,
                     task_status)
-
+    
+    def add_subtree(self, tree_to_add, include_root=False, root_title=None,
+            root_notes=None):
+        """Add *tree_to_add* as a subtree of this tree.
+        
+        If *include_root* is False, then the children of *tree_to_add* will be
+        added as children of this tree's root node.  Otherwise, the root node
+        of *tree_to_add* will be added as a child of this tree's root node.
+        
+        The *root_title* and *root_notes* arguments are optional, and can be
+        used to set the title and notes of *tree_to_add*'s root node when
+        *include_root* is True. 
+        
+        """
+        if not include_root:
+            self.subtasks.extend(tree_to_add.subtasks)
+        else:
+            if root_title is not None:
+                tree_to_add.title = root_title
+            if tree_to_add.title is None:
+                tree_to_add.title = ""
+                
+            if root_notes is not None:
+                tree_to_add.notes = root_notes
+            
+            self.subtasks.append(tree_to_add)
+    
     def last(self, level):
-        """Returns the last task added at a given level of the tree"""
+        """Return the last task added at a given level of the tree.
+        
+        Level 0 is the "root" node of the tree, and there is only one node at
+        this level, which contains all of the level 1 nodes (tasks/headlines).
+        
+        A TaskTree object is returned that corresponds to the last task having
+        the specified level.  This TaskTree object will have the last task as
+        the root node of the tree, and will maintain all of the node's
+        descendants.
+        
+        """
         if level == 0:
             return self
         else:
             res = None
             for subtask in self.subtasks:
-                res = subtask.last(level - 1) or res
-            if res:
+                x = subtask.last(level - 1)
+                if x is not None:
+                    res = x
+            if res is not None:
                 return res
 
     def push(self, service, list_id, parent = None, root=True):
 
 
     def __str__(self):
-        """string representation of the tree"""
+        """string representation of the tree.
+        
+        Only the root-node's children (and their descendents...) are printed,
+        not the root-node itself.
+        
+        """
         # always add a trailing "\n" because text-files normally include a "\n"
         # at the end of the last line of the file.
         return '\n'.join(self._lines(0)) + "\n"
 
+
+def concatenate_trees(t1, t2):
+    """Combine tree *t1*'s children with tree *t2*'s children.
+    
+    A tree is returned whose children include the children of *t1* and the
+    children of *t2*.  The root node of the returned tree is a dummy node
+    having no title.
+    
+    Note: children are treated as references, so modifying *t1* after creating
+    the combined tree will also modify the combined tree.
+    
+    """
+    joined_tree = TasksTree()
+    joined_tree.add_subtree(t1)
+    joined_tree.add_subtree(t2)
+    
+    return joined_tree
+
+
 def get_service():
     """
     Handle oauth's shit (copy-pasta from
 class TestMichel(unittest.TestCase):
     def setUp(self):
         pass
-    
     def test_text_to_tasktree(self):
         # text should have trailing "\n" character, like most textfiles
         org_text = textwrap.dedent("""\
             * DONE    Headline 2
             ** Headline 2.1
             """)
-
         tasktree = m.parse_text(org_text)
         self.assertEqual(str(tasktree), org_text)
 
         # a dummy headline will be added to contain the initial text
         self.assertEqual(str(tasktree), "* \n" + org_text)
 
+    def test_add_subtrees(self):
+        org_text1 = textwrap.dedent("""\
+            * Headline A1
+            * Headline A2
+            ** Headline A2.1
+            """)
+        org_text2 = textwrap.dedent("""\
+            * Headline B1
+            ** Headline B1.1
+            * Headline B2
+            """)
+        tree1 = m.parse_text(org_text1)
+        tree2 = m.parse_text(org_text2)
+        
+        # test tree concatenation
+        target_tree = m.concatenate_trees(tree1, tree2)
+        target_text = textwrap.dedent("""\
+            * Headline A1
+            * Headline A2
+            ** Headline A2.1
+            * Headline B1
+            ** Headline B1.1
+            * Headline B2
+            """)
+        self.assertEqual(str(target_tree), target_text)
+        
+        # test subtree grafting
+        # add tree2's children to first child of tree1
+        tree1[0].add_subtree(tree2)
+        target_tree = tree1
+        target_text = textwrap.dedent("""\
+            * Headline A1
+            ** Headline B1
+            *** Headline B1.1
+            ** Headline B2
+            * Headline A2
+            ** Headline A2.1
+            """)
+        self.assertEqual(str(target_tree), target_text)
+
 if __name__ == '__main__':
     unittest.main()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.