Coverage for /home/deng/Projects/ete4/hackathon/ete4/ete4/nexml/_nexml_tree.py: 23%

113 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2024-03-21 09:19 +0100

1import sys 

2from ._nexml import MixedContainer, FloatTree, TreeFloatEdge, TreeNode, LiteralMeta 

3from .. import PhyloTree 

4from ..parser.newick import loads 

5 

6class Children(list): 

7 def append(self, item): 

8 list.append(self, item) 

9 item.nexml_edge.source = self.node.nexml_node.id 

10 item.nexml_edge.target = item.nexml_node.id 

11 

12class NexmlTree(PhyloTree): 

13 """ 

14 Special PhyloTree object with nexml support 

15 """ 

16 

17 def __repr__(self): 

18 return "NexML ETE tree <%s>" %hex(hash(self)) 

19 

20 def _get_dist(self): 

21 return self.nexml_edge.get_length() 

22 def _set_dist(self, value): 

23 try: 

24 self.nexml_edge.set_length(value) 

25 except ValueError: 

26 raise 

27 

28 def _get_support(self): 

29 return self._nexml_support.content 

30 def _set_support(self, value): 

31 try: 

32 self._nexml_support.content = float(value) 

33 except ValueError: 

34 raise 

35 

36 def _get_name(self): 

37 return self.nexml_node.get_label() 

38 

39 def _set_name(self, value): 

40 try: 

41 self.nexml_node.set_label(value) 

42 except ValueError: 

43 raise 

44 

45 def _get_children(self): 

46 return self._children 

47 def _set_children(self, value): 

48 if isinstance(value, Children) and \ 

49 len(set([type(n)==type(self) for n in value]))<2: 

50 self._children = value 

51 else: 

52 raise ValueError("children:wrong type") 

53 

54 dist = property(fget=_get_dist, fset=_set_dist) 

55 support = property(fget=_get_support, fset=_set_support) 

56 children = property(fget=_get_children, fset=_set_children) 

57 name = property(fget=_get_name, fset=_set_name) 

58 

59 def __init__(self, newick=None, alignment=None, alg_format="fasta", \ 

60 sp_naming_function=None, parser=0): 

61 

62 self.nexml_tree = FloatTree() 

63 self.nexml_tree.set_anyAttributes_({'xsi:type': 'FloatTree'}) 

64 self.nexml_node = TreeNode() 

65 self.nexml_edge = TreeFloatEdge() 

66 self.nexml_node.id = "node_%s" %hash(self) 

67 self.nexml_edge.id = "edge_%s" %hash(self) 

68 self.nexml_project = None 

69 self._nexml_support = LiteralMeta(datatype="float", property="branch_support", content=1.0) 

70 self.nexml_edge.length = 0.0 

71 self.nexml_edge.add_meta(self._nexml_support) 

72 

73 # Initialize empty PhyloTree 

74 super(NexmlTree, self).__init__() 

75 self._children = Children() 

76 self._children.node = self 

77 

78 if alignment: 

79 self.link_to_alignment(alignment, alg_format) 

80 if newick: 

81 tree = loads(newick, parser, self.__class__) 

82 self.children = tree.children 

83 self.props = tree.props 

84 self.set_species_naming_function(sp_naming_function) 

85 

86 def set_nexml_project(self, nexml_obj): 

87 self.nexml_project = nexml_obj 

88 

89 def build(self, node): 

90 self.nexml_tree = FloatTree() 

91 tree = self.nexml_tree 

92 tree.build(node) 

93 

94 # This detects the outgroup of the tree even if the root tag 

95 # is not set in any node 

96 rootid = set([e.source for e in tree.edge]) - set([e.target for e in tree.edge]) 

97 

98 nodeid2node = {rootid.pop(): self} 

99 for xmledge in tree.edge: 

100 child = nodeid2node.setdefault(xmledge.target, self.__class__() ) 

101 parent = nodeid2node.setdefault(xmledge.source, self.__class__() ) 

102 child.name = xmledge.target 

103 child.nexml_node.id = xmledge.target 

104 parent.name = xmledge.source 

105 parent.nexml_node.id = xmledge.source 

106 child.nexml_edge = xmledge 

107 

108 if xmledge.length is not None: 

109 child.dist = float(xmledge.length) 

110 parent.add_child(child) 

111 

112 for xmlnode in tree.node: 

113 # just a warning. I don't know if this can occur 

114 if xmlnode.id not in nodeid2node: 

115 print("Unused node", xmlnode.id, file=sys.stderr) 

116 continue 

117 

118 ete_node = nodeid2node[xmlnode.id] 

119 ete_node.nexml_node = xmlnode 

120 

121 if xmlnode.label: 

122 ete_node.name = xmlnode.label 

123 elif xmlnode.id is not None: 

124 ete_node.name = xmlnode.id 

125 

126 

127 def export(self, outfile=sys.stdout, level=0, namespace_='', name_='FloatTree', namespacedef_=''): 

128 if self.nexml_tree: 

129 info = [(n.nexml_edge, n.nexml_node) for n in self.traverse()] 

130 self.nexml_node.set_root(True) 

131 self.nexml_tree.set_edge([i[0] for i in info]) 

132 self.nexml_tree.set_node([i[1] for i in info]) 

133 self.nexml_tree.export(outfile=outfile, level=level, name_=name_, namespacedef_=namespacedef_) 

134 

135 

136 def exportChildren(self, outfile, level, namespace_='', name_='FloatTree'): 

137 sorted_nodes = [] 

138 sorted_edges = [] 

139 for n in self.traverse(): 

140 # process node 

141 node_obj = self.mixedclass_(MixedContainer.CategoryComplex, 

142 MixedContainer.TypeNone, 'node', n.nexml_node) 

143 sorted_nodes.append(node_obj) 

144 

145 # process edge 

146 if n.nexml_edge: 

147 edge_obj = self.mixedclass_(MixedContainer.CategoryComplex, 

148 MixedContainer.TypeNone, 'edge', n.nexml_edge) 

149 sorted_edges.append(edge_obj) 

150 

151 # process the nodes and edges 

152 self.tree.content_ = sorted_nodes + sorted_edges 

153 for item_ in self.tree.content_: 

154 item_.export(outfile, level, item_.name, namespace_) 

155 

156# end class AbstractTreeSub 

157NexmlNode = NexmlTree