~launchpad-p-s/sofastatistics/main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
class Nodes(object):
    """
    Nodes functionality used by Nodes and Trees
    """
    def addChild(self, child_node):
        """
        Add child node.  Set level, and parent of node.
        Returns child node
        """
        if isinstance(self, NodeTree):
            start_node = self.root_node
        else:
            start_node = self
        child_node.level = start_node.level + 1
        child_node.parent = start_node
        start_node.children.append(child_node)
        return child_node
        
    def getDepth(self):
        "Get tree depth (including root node)"
        if isinstance(self, NodeTree):
            start_node = self.root_node
        else:
            start_node = self
        max_depth = 1 # initialise
        for child_node in start_node.children:
            child_depth = child_node.getDepth()
            if (child_depth + 1) > max_depth:
                max_depth = child_depth + 1
        return max_depth

    def getTerminalNodes(self):
        "Gets list of terminal nodes"
        if isinstance(self, NodeTree):
            if not self.root_node.children:
                raise Exception, "Cannot get terminal nodes until " + \
                    "there is at least one node added to tree"                
            start_node = self.root_node
        else:
            start_node = self            
        if not start_node.children:
            return [start_node]
        else:
            term_nodes_lst = []
            children_term_nodes = \
                [child_node.getTerminalNodes() for child_node in start_node.children]
            for child_term_nodes in children_term_nodes:
                term_nodes_lst += child_term_nodes
            return term_nodes_lst
        
    def generNode(self):
        yield self
        for child_node in self.children:
            for node in child_node.generNode():
                yield node
    
class NodeTree(Nodes):
    """
    Object names follow standard tree data structure terminology of 
    root, nodes, subtrees, terminal nodes, parent, child, sibling, 
    and tree depth.
    Nodes can only have one parent.  All nodes come from root.
    """
    
    def __init__(self):
        self.root_node = Node(label="Root")
        self.root_node.level = 0

    def printChildren(self, node):
        l = []
        for child_node in node.children:
            l.append(str(child_node))
            children_str = str(self.printChildren(child_node))
            if children_str: #otherwise an empty string will get own line
                l.append(str(self.printChildren(child_node)))
        return "\n".join(l)
    
    def __str__(self):
        l = []
        l.append(str(self.root_node))
        l.append(self.printChildren(self.root_node))
        return "\n".join(l)
        
class Node(Nodes):
    """
    Optionally, has details (a dictionary) and a text label.    
    Node index is set when added to either a tree 
    or an existing node.
    Parent is set when added to a node (or left as None if added
    to a tree). Children is updated as children are added.
    """
    
    def __init__(self, dets_dic=None, label=""):
        if dets_dic:
            self.dets_dic = dets_dic
        else:
            self.dets_dic = {}
        self.level = None
        self.parent = None
        self.children=[]
        self.label = label
        
    def __str__(self):
        return self.level*2*" " + "Level: " + str(self.level) + \
            "; Label: " + self.label + \
            "; Details: " + str(self.dets_dic) + \
            "; Child labels: " + ", ".join([x.label for x in self.children])