Coverage for /home/deng/Projects/ete4/hackathon/ete4/ete4/smartview/renderer/layouts/context_layouts.py: 12%

81 statements  

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

1from collections import Counter, defaultdict 

2from ..treelayout import TreeLayout 

3from ..faces import ArrowFace 

4 

5 

6__all__ = [ "LayoutGenomicContext" ] 

7 

8 

9class LayoutGenomicContext(TreeLayout): 

10 def __init__(self, name="Genomic context", nside=2, 

11 conservation_threshold=0, width=70, height=15, collapse_size=1, 

12 gene_name="name", tooltip_props=[], 

13 stroke_color="gray", stroke_width="1.5px", 

14 anchor_stroke_color="black", anchor_stroke_width="3px", 

15 non_conserved_color="#d0d0d0", collapse_conservation=0.2, 

16 collapse_by_conservation=True): 

17 

18 super().__init__(name, aligned_faces=True) 

19 

20 self.nside = nside 

21 self.conservation_threshold = conservation_threshold 

22 self.gene_name = gene_name 

23 self.tooltip_props = tooltip_props 

24 

25 self.width = width 

26 self.height = height 

27 

28 self.collapse_size = collapse_size 

29 

30 self.anchor_stroke_color = anchor_stroke_color 

31 self.anchor_stroke_width = anchor_stroke_width 

32 self.stroke_color = stroke_color 

33 self.stroke_width = stroke_width 

34 

35 self.non_conserved_color = non_conserved_color 

36 

37 self.collapse_conservation = collapse_conservation 

38 self.collapse_by_conservation = collapse_by_conservation 

39 

40 def set_tree_style(self, tree, style): 

41 super().set_tree_style(tree, style) 

42 style.collapse_size = self.collapse_size 

43 

44 def set_node_style(self, node): 

45 context = self.get_context(node) 

46 if context: 

47 for idx, gene in enumerate(context): 

48 name = gene.get("name") 

49 color = gene.get("color", self.non_conserved_color) 

50 conservation = gene.get("conservation_score") 

51 if conservation is not None\ 

52 and float(conservation) < self.conservation_threshold: 

53 color = self.non_conserved_color 

54 strand = gene.get("strand", "+") 

55 cluster = gene.get("cluster") 

56 orientation = "left" if strand == "-" else "right" 

57 text = gene.get(self.gene_name, "") 

58 if idx == self.nside: 

59 stroke_color = self.anchor_stroke_color 

60 stroke_width = self.anchor_stroke_width 

61 else: 

62 stroke_color = self.stroke_color 

63 stroke_width = self.stroke_width 

64 arrow = ArrowFace(self.width, self.height, 

65 orientation=orientation, color=color, 

66 stroke_color=stroke_color, stroke_width=stroke_width, 

67 tooltip=self.get_tooltip(gene), 

68 text=text, 

69 padding_x=2, padding_y=2) 

70 node.add_face(arrow, position="aligned", column=idx, 

71 collapsed_only=(not node.is_leaf)) 

72 

73 def get_tooltip(self, gene): 

74 if self.tooltip_props is None: 

75 return "" 

76 

77 if self.tooltip_props == []: 

78 key_props = gene.keys() 

79 else: 

80 key_props = self.tooltip_props 

81 

82 props = {} 

83 for k,v in gene.items(): 

84 if k in key_props and v and not k in ("strand", "color")\ 

85 and not k.startswith("_"): 

86 if k == "hyperlink": 

87 k = "Go to" 

88 label, url = v 

89 v = f'<a href="{url}" target="_blank">{label}</a>' 

90 props[k] = v 

91 

92 return "<br>".join(f'{k}: {v}' for k,v in props.items()) 

93 

94 def get_context(self, node): 

95 if node.is_leaf: 

96 return node.props.get("_context") 

97 

98 if not self.collapse_by_conservation: 

99 first_leaf = next(node.iter_leaves()) 

100 return first_leaf.props.get("_context") 

101 

102 # Compute conserved context by color 

103 color_context = defaultdict(list) 

104 color2genes = {} 

105 

106 for l in node: 

107 lcontext = l.props.get("_context") 

108 for pos, gene in enumerate(lcontext): 

109 color = gene["color"] 

110 color_context[pos].append(color) 

111 color2genes[color] = gene 

112 

113 ntips = len(node) 

114 context = [] 

115 for pos, colors in sorted(color_context.items()): 

116 color, n = Counter(colors).most_common(1)[0] 

117 if n / ntips >= self.collapse_conservation\ 

118 and color != self.non_conserved_color: 

119 context.append({ 

120 **color2genes[color], 

121 "vertical_conservation": n / ntips }) 

122 else: 

123 context.append({ "color": self.non_conserved_color }) 

124 

125 return context