Coverage for src/lcdoc/call_flows/call_flow_charting.py: 5.26%

Shortcuts on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

86 statements  

1import json lp|features/lp/python/call_flow_logging/index.mdpytest

2 

3from lcdoc.tools import project, write_file lp|features/lp/python/call_flow_logging/index.mdpytest

4 

5from lcdoc.call_flows import markdown lp|features/lp/python/call_flow_logging/index.mdpytest

6from lcdoc.call_flows.viz_sequence import Plant lp|features/lp/python/call_flow_logging/index.mdpytest

7 

8vizs = {'plantuml': Plant} # , 'mermaid': Mermaid} lp|features/lp/python/call_flow_logging/index.mdpytest

9 

10 

11def make_call_flow_chart(flow, d_dest, fn=None, viz_name='plantuml', ILS=None): lp|features/lp/python/call_flow_logging/index.mdpytest

12 """ 

13 - flow from original ILS.call_chain (list of calls, one req, one resp) 

14 - reformatted for [ [{<call n>}, <input>, <output>], ...] by arrange_call_and_write_infos 

15 

16 call: traced frame (frame incl. meta infos as traced) 

17 

18 ILS: Reference to the call_flow_logging.ILS (all the recorded state is there) 

19 """ 

20 # breakpoint() # FIXME BREAKPOINT 

21 fn = 'call_seq' if fn is None else fn 

22 r = [] 

23 if not flow: 

24 print('no flow') 

25 return 

26 r_add = r.append 

27 viz = vizs[viz_name] 

28 d_root = project.root() 

29 # flow.insert( 

30 # 0, [{'counter': 0, 'name': 'intent', 'pth': ('outside',)}, 'null', 'null'] 

31 # ) 

32 last_thread = [0] 

33 

34 def add_thread(call, have=set(), r_add=r_add, last=last_thread): 

35 thread = call['thread_req'] 

36 if thread in have: 

37 return 

38 have.add(thread) 

39 last[0] = thread 

40 r_add(viz.participant(thread, thread)) 

41 

42 # cc is created in biz_func_wrapped_for_call_flow_logging (typ, nr, data) 

43 # flow.insert(0, [{'thread_req': 't'}]) 

44 [add_thread(c[0]) for c in flow] 

45 # flow.pop(0) 

46 

47 def args_str_and_tooltip(c, viz=viz): 

48 if c == 'null': 

49 return '-', '(None)' 

50 try: 

51 args = json.loads(c) 

52 except Exception as ex: 

53 return c.replace('\n', ','), c 

54 s = ['%s:%s' % (str(k), str(v)) for k, v in args.items()] 

55 t = '\n'.join(s) 

56 s = ','.join(s) 

57 if not s.strip(): 

58 return 'null', 'null' 

59 return s, t 

60 

61 def trim(s, distance, viz=viz, chars_between_adjancent_boxes=20): 

62 """The displayed call args in the links - should be not too long to not 

63 widen the chart yet more""" 

64 d = (abs(distance) + 1) * chars_between_adjancent_boxes 

65 s = s if len(s) < d else s[: d - 2] + '..' 

66 # s = viz.ls.join(s.splitlines()) 

67 for k in '{', '[', '}', ']': 

68 s = s.replace(k, ' ') 

69 return s 

70 

71 def href(s, tooltip, nr, typ, viz=viz, max_tooltip_len=3000): 

72 """s the function name, tooltip is the params""" 

73 if not s: 

74 return '' 

75 # [[http://plantuml.com/start{Tooltip for message} some link]] 

76 s = s.replace('\n', ' ') # text over arrows should be not multline 

77 # s = s.split('(', 1)[0] # .replace('(', '_').replace(')', '_') 

78 t = tooltip.replace('\n', viz.ls) 

79 t = t[:max_tooltip_len] # bigger? he should click then 

80 t = t.replace('<', ' ').replace('>', '') 

81 t = t.replace('{', '') 

82 t = t.replace('}', '') 

83 r = '%s.json?%s{%s} %s' % (nr, typ, t, s) 

84 # r = '%s.json?%s{%s} %s' % (nr, typ, 'a', s) 

85 # r = 'javascript:onmouseover(x=>alert("foo")){foo} foo' 

86 r = r.replace(']]', '\\]\\]') 

87 return '[[%s]]' % r 

88 

89 frm = [] # last req sender, the stack of active functions 

90 # i = [thread_name(f[0]) for f in flow] 

91 # breakpoint() # FIXME BREAKPOINT 

92 t0 = flow[0][0]['t0'] 

93 tn = flow[-1][0]['t0'] + flow[-1][0].get('dt', 0) 

94 DT = tn - t0 

95 for rnd in range(-1, 6): 

96 j = 1 * round((DT / 10.0), rnd) 

97 if j != 0: 

98 break 

99 dt = j 

100 time_ = 0 

101 while flow: 

102 call, inp, output = flow.pop(0) 

103 t0 = call['t0'] 

104 thread = call['thread_req'] 

105 if call.get('fn_mod') == 'note': 

106 msg = call['name'].replace('\n', viz.ls) 

107 r_add('note over %s: %s' % (thread, msg)) 

108 continue 

109 nr = call['counter'] 

110 n = call['name'] 

111 if inp: 

112 if t0 > time_: 

113 # r_add('d <-> t: %s' viz.req('d', 't', what=str(round(time_, rnd)))) 

114 #% (last_thread[0], round(time_, rnd)) 

115 r_add('[-[#888888]-> MainThread: %s' % round(time_, rnd)) 

116 time_ += dt 

117 n_with_url = href(n, str(inp), nr, 'req') 

118 r_add('%s<-[#888888]-]: %s' % (thread, n_with_url)) 

119 # r_add('note left of %s: %s' % (thread, n)) 

120 # r_add('[-[#000033]-> %s: %s' % (thread, '')) 

121 r_add('activate %s' % thread) 

122 

123 # if frm: 

124 # nfrm = frm[-1] 

125 # # breakpoint() # FIXME BREAKPOINT 

126 # # ff = call.get('from_frame') 

127 # # if ff: 

128 # # nfrm = particips_shorts_by_code[ff.f_code] 

129 # # if '"return msg.reconfigure ? [msg, null] : [null, msg]"' in str(inp): 

130 # # breakpoint() # FIXME BREAKPOINT 

131 # s, t = args_str_and_tooltip(inp) 

132 # s1 = trim(s, distance=(nfrm - n)) 

133 # inp = href(s1, t, nr, 'req') 

134 # # add the arrow form the calling function to us, with the req params: 

135 # r_add(viz.req(nfrm, n, what=inp)) 

136 # add(viz.activate(n)) 

137 # frm.append(n) 

138 else: 

139 r_add('deactivate %s' % thread) 

140 # continue 

141 # assert inp == None 

142 # # when a source produces like an observer.on_next loop, the function has 

143 # # an entry but never exits. Then only an arrow goes out of the func: 

144 # while frm[-1] != n: 

145 # frm.pop() 

146 # frm.pop() 

147 # w = None if output == 'null' else output 

148 # if not frm: 

149 # break 

150 # if w: 

151 # s = t = w 

152 # w = trim(w, distance=(n - frm[-1])) 

153 # else: 

154 # w = 'null' 

155 # # add the arrow back to the calling function, with the response 

156 # r_add(viz.rsp(n, frm[-1], what=href(w, t, nr, 'resp') or '')) 

157 # # add(viz.deactivate(n)) 

158 

159 fn = '%s/%s/call_flow.%s' % (d_dest, fn, viz.ext) 

160 r = viz.wrap(r) # e.g. @startuml, and concat to string 

161 write_file(fn, r) 

162 return fn 

163 

164 # loc = S.d_root + '/build/autodocs/' 

165 # m = { 

166 # 'fn': fn.split(dirname(S.fn_doc), 1)[1].rsplit('.', 1)[0], 

167 # 'loc': len(ILS.d_doc.split(loc, 1)[1].split('/')) * '../', 

168 # } 

169 # if viz == Plant: 

170 # # v = '[![](.%(fn)s.svg)](%(loc)scall_sequence.html?src=%(fn)s)' % m 

171 # v = '[![](.%(fn)s.svg)](?src=%(fn)s&sequence_details=true)' % m 

172 # v = T.closed_admon('Call Sequence', v, 'note') 

173 # elif viz == Mermaid: 

174 # v = '{!%(fn)s.plantuml!}' % m 

175 # add_doc('\n%s\n' % v)