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
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
1import json lp|features/lp/python/call_flow_logging/index.mdpytest
3from lcdoc.tools import project, write_file lp|features/lp/python/call_flow_logging/index.mdpytest
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
8vizs = {'plantuml': Plant} # , 'mermaid': Mermaid} lp|features/lp/python/call_flow_logging/index.mdpytest
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
16 call: traced frame (frame incl. meta infos as traced)
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]
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))
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)
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
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
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
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)
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))
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
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)