modelflowib 2.73__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
modeldashsidebar.py ADDED
@@ -0,0 +1,456 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Fri May 14 22:46:21 2021
4
+
5
+ @author: bruger
6
+ """
7
+
8
+ import dash
9
+ # from jupyter_dash import JupyterDash
10
+ #from jupyter_dash.comms import _send_jupyter_config_comm_request
11
+ try:
12
+ from dash import Dash, callback, html, dcc, dash_table, Input, Output, State, MATCH, ALL
13
+ import dash_bootstrap_components as dbc
14
+ from dash_interactive_graphviz import DashInteractiveGraphviz
15
+ # from dash.exceptions import PreventUpdate
16
+
17
+ # try:
18
+ # from dash import dcc
19
+ # except:
20
+ # import dash_core_components as dcc
21
+
22
+ # import dash_bootstrap_components as dbc
23
+ # try:
24
+ # from dash import html
25
+ # except:
26
+ # import dash_html_components as html
27
+
28
+ # from dash.dependencies import Input, Output, State
29
+
30
+ # from dash_interactive_graphviz import DashInteractiveGraphviz
31
+ except:
32
+ print('No Dash')
33
+
34
+ import plotly.graph_objs as go
35
+ from plotly.graph_objs.scatter.marker import Line
36
+
37
+ import webbrowser
38
+ from threading import Timer
39
+
40
+ import time
41
+
42
+ import json
43
+ import networkx as nx
44
+
45
+ from dataclasses import dataclass,field
46
+
47
+
48
+ from pathlib import Path
49
+ import pandas as pd
50
+
51
+ from modelhelp import cutout
52
+
53
+ import logging
54
+ log = logging.getLogger('werkzeug')
55
+ log.setLevel(logging.ERROR)
56
+ # log.setLevel(logging.INFO)
57
+ # log.setLevel(logging.DEBUG)
58
+
59
+
60
+ first_call = True
61
+
62
+
63
+
64
+ initial_dot_source = """
65
+ digraph {
66
+ node[style="filled"]
67
+ a ->b->d
68
+ a->c->d
69
+ }
70
+ """
71
+
72
+ sidebar_width, rest_width = "15%", "82%"
73
+ # the style arguments for the sidebar. We use position:fixed and a fixed width
74
+ SIDEBAR_STYLE = {
75
+ "position": "fixed",
76
+ "top": 0,
77
+ "left": 0,
78
+ "bottom": 0,
79
+ "width": sidebar_width,
80
+ "padding": "2rem 1rem",
81
+ "background-color": "#f8f9fa",
82
+ "overflow": "scroll",
83
+ }
84
+
85
+ # the styles for the main content position it to the right of the sidebar and
86
+ # add some padding.
87
+ CONTENT_STYLE_TOP = {
88
+ "background-color": "f8f9fa",
89
+ "width": "100%",
90
+
91
+ "margin-left": "0%",
92
+ }
93
+ CONTENT_STYLE_GRAPH = {
94
+ "background-color": "f8f9fa",
95
+ "width":"82%",
96
+
97
+ "margin-left": '0%',
98
+ }
99
+ CONTENT_STYLE_TAB = {
100
+ "background-color": "f8f9fa",
101
+ "margin-left": sidebar_width,
102
+ "width": "85%",
103
+ "height": "auto",
104
+ }
105
+
106
+
107
+ def app_setup(jupyter=False):
108
+ # print('apprun')
109
+ if jupyter:
110
+ # Timer(1,_send_jupyter_config_comm_request).start()
111
+ # JupyterDash.infer_jupyter_proxy_config()
112
+ # app = JupyterDash(__name__,external_stylesheets=[dbc.themes.BOOTSTRAP])
113
+ app = dash.Dash(__name__,external_stylesheets=[dbc.themes.BOOTSTRAP])
114
+ else:
115
+ app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
116
+ return app
117
+
118
+ def app_run(app,jupyter=False,debug=False,port=5000,inline=False):
119
+ global first_call
120
+ def open_browser(port=port):
121
+ webbrowser.open_new(f"http://localhost:{port}")
122
+
123
+ # print(f'{jupyter=} {inline=}')
124
+ if jupyter:
125
+ if inline:
126
+ xx = app.run(debug=debug,port=port,mode='inline')
127
+ else:
128
+ if first_call or 1:
129
+ Timer(1, open_browser).start()
130
+ first_call = False
131
+ # print('ko')
132
+
133
+ xx =app.run(debug=debug,port=port,jupyter_mode='external')
134
+ # print('gris')
135
+
136
+ # print(f'{xx=}')
137
+
138
+ else:
139
+ Timer(1, open_browser).start()
140
+
141
+ xx = app.run(debug=debug,port=port,mode="external")
142
+
143
+
144
+
145
+
146
+ def get_stack(df,v='Guess it',heading='Yes',pct=True,threshold=0.5,desdict = {}):
147
+ pv = cutout(df,threshold)
148
+ template = '%{y:.1f}% explained by:<br><b>%{meta}</b>:' if pct else '%{y:.2f} explained by:<br><b>%{meta}</b>:'
149
+ trace = [go.Bar(x=pv.columns.astype('str'), y=pv.loc[rowname,:], name=rowname,hovertemplate = template,
150
+ meta = desdict.get(rowname,'hest')) for rowname in pv.index]
151
+ out = { 'data': trace,
152
+ 'layout':
153
+ go.Layout(title=f'{heading}', barmode='relative',legend_itemclick='toggleothers',
154
+ yaxis = {'tickformat': ',7.0','ticksuffix':'%' if pct else ''})
155
+ }
156
+ return out
157
+
158
+ def get_no_stack(df,v='No attribution for exogenous variables ',desdict = {}):
159
+ out = {'layout':
160
+ go.Layout(title=f'{v}')
161
+ }
162
+ return out
163
+
164
+ def get_line_old(pv,v='Guess it',heading='Yes'):
165
+ trace = [go.Line(x=pv.columns.astype('str'), y=pv.loc[rowname,:], name=rowname) for rowname in pv.index]
166
+ out = { 'data': trace,
167
+ 'layout':
168
+ go.Layout(title=f'{heading}')
169
+ }
170
+ return out
171
+ def get_line(pv,v='Guess it',heading='Yes',pct=True):
172
+ trace = [go.Scatter(x=pv.columns.astype('str'),
173
+ y=pv.loc[rowname,:],
174
+ name=rowname
175
+ ) for rowname in pv.index]
176
+ out = { 'data': trace,
177
+ 'layout':
178
+ go.Layout(title=f'{heading}')
179
+ }
180
+ return out
181
+
182
+
183
+ def generate_table(dataframe, max_rows=10):
184
+ return html.Table([
185
+ html.Thead(
186
+ html.Tr([html.Th('var')]+[html.Th(col) for col in dataframe.columns])
187
+ ),
188
+ html.Tbody([
189
+ html.Tr([html.Td(dataframe.index[i])]+[html.Td(f'{dataframe.iloc[i][col]:25.2f}') for col in dataframe.columns])
190
+ for i in range(min(len(dataframe), max_rows))
191
+ ])
192
+ ])
193
+
194
+
195
+ @dataclass
196
+ class Dash_graph():
197
+
198
+ mmodel : any = None
199
+ pre_var : str =''
200
+ filter: float = 0
201
+ up : int = 1
202
+ down : int = 0
203
+ time_att : bool = False
204
+ attshow :bool = False
205
+ all : bool = False
206
+ port : int = 5001
207
+ debug : bool = False
208
+ jupyter : bool = True
209
+ show_trigger : bool = False
210
+ inline : bool =False
211
+ lag : bool = False
212
+ threshold : float =0.5
213
+ growthshow : bool = False
214
+
215
+ def __post_init__(self):
216
+ self.fokusvar = set()
217
+ # print('Still worlking on the layout of this')
218
+ self.firstsession = True
219
+ selected_var = self.pre_var if self.pre_var else sorted(self.mmodel.allvar.keys())[0]
220
+ self.outvar_state = selected_var
221
+ sidebar = html.Div(
222
+ [
223
+ html.H3("Current Variable"),
224
+ dcc.Markdown(
225
+ id='outvar_state',
226
+ children=f'{selected_var}'),
227
+
228
+ html.H3("Select a variable"),
229
+ dcc.Dropdown(id='var',value=selected_var,options=[
230
+ dict(label=v, value=v)
231
+ for v in sorted(self.mmodel.allvar.keys())],persistence = True,persistence_type='session'),
232
+ html.H3("Tree walking"),
233
+ dbc.Row([
234
+ html.H5("Up"),
235
+ dcc.Dropdown(id="up",value=self.up,options=[
236
+ dict(label=engine, value=engine)
237
+ for engine in list(range(10))],persistence = True,persistence_type='session'),
238
+
239
+ html.H5("Down"),
240
+ dcc.Dropdown(id="down",value=self.down,options=[
241
+ dict(label=engine, value=engine)
242
+ for engine in list(range(10))],persistence = True,persistence_type='session'),
243
+ ]),
244
+ dbc.Tooltip('Looking at preceding variabels up to this level',
245
+ target = 'up'),
246
+ dbc.Tooltip('Looking at dependent variabels down to this level',
247
+ target = 'down'),
248
+
249
+ html.H3("Graph filter%"),
250
+ dcc.Dropdown(id="filter",value=self.filter,options=[
251
+ dict(label=t, value=t)
252
+ for t in list(range(0,100,10))],persistence = True,persistence_type='session'),
253
+ dbc.Tooltip('All branches contribution less are pruned from the graph',
254
+ target = 'filter'),
255
+
256
+ html.H3("Graph orientation"),
257
+ dcc.RadioItems(id='orient',
258
+ options=[
259
+ {'label': 'Vertical', 'value':'v'},
260
+
261
+ {'label': 'Horisontal', 'value': 'h'},
262
+ ],
263
+ value='v',labelStyle={'display': 'block'},persistence = True,persistence_type='session' ),
264
+
265
+ html.H3("Node Display"),
266
+ dcc.RadioItems(id='node',
267
+ options=[
268
+ {'label': 'Name', 'value':'name'},
269
+ {'label': '+values', 'value':'all'},
270
+
271
+ {'label': '+Attribution', 'value': 'attshow'},
272
+ ],persistence = True, persistence_type='session',
273
+ value='all' if self.all else ('attshow' if self.attshow else 'name') ,labelStyle={'display': 'block'}),
274
+
275
+ html.H3("Behavior when clicking on node"),
276
+ dcc.RadioItems(id='onclick',
277
+ options=[
278
+ {'label': 'Center', 'value':'c'},
279
+ {'label': 'Display', 'value': 'd'},
280
+ ],
281
+ value='c',persistence = True,persistence_type='session'),
282
+
283
+
284
+ ],
285
+ style=SIDEBAR_STYLE
286
+ )
287
+ # breakpoint()
288
+ outvar= selected_var
289
+ tab0 = html.Div([
290
+ dbc.Tabs(id="tabs", children=[
291
+ dbc.Tab(id='Graph',label='Graph',children= [DashInteractiveGraphviz(id="gv" , style=CONTENT_STYLE_GRAPH,
292
+
293
+ dot_source = self.mmodel.draw(selected_var,up=self.up,down=self.down,showatt=False,lag=self.lag,
294
+ debug=0,dot=True,HR=False,filter = self.filter,
295
+ all=False,attshow=False))],
296
+ style=CONTENT_STYLE_TOP , ),
297
+
298
+ dbc.Tab(id='Chart',label='Chart',
299
+ children = [dcc.Graph(id='chart',
300
+ figure=get_line(self.mmodel.value_dic[selected_var].iloc[:2,:],selected_var,f'The values for {selected_var}'))
301
+ , dcc.Graph(id='chart_dif',
302
+ figure=get_line(self.mmodel.value_dic[selected_var].iloc[[2],:],selected_var,f'The impact for {selected_var}'))
303
+ ],
304
+ style=CONTENT_STYLE_TOP),
305
+
306
+ dbc.Tab(id='Attribution',label='Attribution',
307
+ children = [dcc.Graph(id='att_pct',
308
+ figure = (get_stack(self.mmodel.att_dic[outvar],outvar,f'Attribution of the impact - pct. for {outvar}',
309
+ threshold=self.threshold,desdict=self.mmodel.var_description)
310
+ if outvar in self.mmodel.endogene else html.H3("Graph orientation")))
311
+ , dcc.Graph(id='att_level',
312
+ figure = (get_stack(self.mmodel.att_dic_level[outvar],outvar,f'Attribution of the impact - level for {outvar}',
313
+ pct=False,threshold=self.threshold,desdict=self.mmodel.var_description) if outvar in self.mmodel.endogene else html.H3("Graph orientation")))
314
+
315
+ ],
316
+ style=CONTENT_STYLE_TOP)
317
+
318
+
319
+ ],persistence=True, persistence_type = 'session'),
320
+ ],style = CONTENT_STYLE_TAB)
321
+ # tabbed = tab0
322
+ # tabbed = dbc.Container(tab0,id='tabbed',style={"height": "100vh"},fluid=True)
323
+ tabbed = html.Div(tab0,id='tabbed',style={"height": "100vh"})
324
+ self.app = app_setup(jupyter=self.jupyter)
325
+
326
+
327
+
328
+ # app.layout = html.Div([sidebar,body2])
329
+ self.app.layout = dbc.Container([sidebar,tabbed],style={"height": "100vh","width":"100%"},fluid=True)
330
+
331
+ @self.app.callback(
332
+ [Output("gv", "dot_source"),
333
+ Output('chart','figure'), Output('chart_dif','figure'),
334
+ Output('att_pct','figure'), Output('att_level','figure'),
335
+ Output('outvar_state','children'),
336
+ Output('tabs','active_tab')],
337
+
338
+ [Input('var', "value"),
339
+ Input('gv', "selected_node"), Input('gv', "selected_edge"),
340
+ Input('up', "value"),Input('down', "value"),Input('filter', "value"),
341
+ Input('orient', "value"),
342
+ Input('onclick','value'),
343
+ Input('node','value')
344
+
345
+ ]
346
+ , State('outvar_state','children')
347
+ )
348
+ def display_output( var,
349
+ selected_node,selected_edge,
350
+ up,down,filter,
351
+ orient,
352
+ onclick,
353
+ node,
354
+ outvar_state
355
+ ):
356
+ # time.sleep(3)
357
+ ctx = dash.callback_context
358
+ # if ctx.triggered[0]['prop_id']== 'gv.selected_node' and ctx.triggered[0]['value']== None:
359
+ # raise PreventUpdate
360
+
361
+ # print(f'new trigger :{ctx.triggered=}')
362
+ # breakpoint()
363
+ if ctx.triggered:
364
+
365
+ trigger = ctx.triggered[0]['prop_id'].split('.')[0]
366
+ if self.show_trigger:
367
+ print('\nNew Trigger:')
368
+ ctx_msg = json.dumps({
369
+ 'states': ctx.states,
370
+ 'triggered': ctx.triggered,
371
+ 'inputs': ctx.inputs
372
+ }, indent=2)
373
+ print(ctx_msg)
374
+ # print(f'{outvar=},{data_show=}')
375
+ print(f'{outvar_state=}')
376
+ print(f'{self.fokusvar=} When triggerd')
377
+
378
+ update_var = True
379
+
380
+ if trigger == 'var':
381
+ outvar=var
382
+
383
+
384
+
385
+ # elif trigger == 'gv' :
386
+
387
+ # print(f'{selected_node=}')
388
+ elif selected_node:
389
+ xvar= selected_node.split('(')[0]
390
+ if onclick == 'c':
391
+ outvar = xvar if trigger == 'gv' else self.outvar_state
392
+
393
+ elif onclick == 'd' :
394
+ if trigger == 'gv':
395
+ if xvar in self.fokusvar:
396
+ self.fokusvar = self.fokusvar-{xvar}
397
+ else:
398
+ self.fokusvar = self.fokusvar | {xvar}
399
+ outvar=xvar
400
+
401
+ else:
402
+
403
+ outvar=self.outvar_state
404
+
405
+ self.fokusvar = set() if trigger in {'onclick','var'} else self.fokusvar
406
+
407
+ if onclick == 'c' or trigger == 'var' : #or outvar not in self.mmodel.value_dic.keys() : # or trigger in ['up','down','orient','filter','node'] :
408
+ dot_out = self.mmodel.draw(outvar,up=up,down=down,filter=filter,showatt=False,debug=0,
409
+ lag=self.lag,dot=True,HR=orient=='h',last=0,all = False,
410
+ attshow=False)
411
+
412
+ elif onclick == 'd' :
413
+ # print(f'{outvar=}')
414
+ if self.show_trigger:
415
+ print(f'{self.fokusvar=} on display ')
416
+
417
+ dot_out = self.mmodel.draw(self.outvar_state,up=up,down=down,filter=filter,showatt=False,debug=0,
418
+ lag=self.lag,dot=True,HR=orient=='h',fokus2=self.fokusvar,all= node == 'all',
419
+ attshow= node =='attshow',growthshow= node=='all' or node =='attshow',)
420
+
421
+ chart_out = get_line(self.mmodel.get_values(outvar).iloc[:2,:],outvar,f'The values for {outvar}:{self.mmodel.var_description.get(outvar,"")}')
422
+ chart_dif_out = get_line(self.mmodel.get_values(outvar).iloc[[2],:],outvar,f'The impact for {outvar}:{self.mmodel.var_description.get(outvar,"")}')
423
+
424
+ att_pct_out = (get_stack(self.mmodel.get_att_pct(
425
+ outvar.split('(')[0], lag=False, start='', end=''),outvar,f'Attribution of the impact - pct. for {outvar}:{self.mmodel.var_description.get(outvar,"")}'
426
+ ,threshold=self.threshold,desdict=self.mmodel.var_description)
427
+ if outvar in self.mmodel.endogene else html.H3(""))
428
+
429
+ att_level_out = (get_stack(self.mmodel.get_att_level(
430
+ outvar.split('(')[0], lag=False, start='', end=''),outvar,f'Attribution of the impact - level for {outvar}',
431
+ pct=False,threshold=self.threshold,desdict=self.mmodel.var_description) if outvar in self.mmodel.endogene else html.H3("") )
432
+
433
+ else:
434
+ return [dash.no_update, dash.no_update,dash.no_update,dash.no_update,dash.no_update, dash.no_update, dash.no_update]
435
+ if self.show_trigger:
436
+ print(f'{self.fokusvar=} before render')
437
+ self.outvar_state = outvar if onclick == 'c' or trigger == 'var' else self.outvar_state
438
+ outvar_state = self.outvar_state
439
+ return [dot_out, chart_out, chart_dif_out, att_pct_out, att_level_out, outvar_state ,dash.no_update,]
440
+
441
+ app_run(self.app,jupyter=self.jupyter,debug=self.debug,port=self.port,inline=self.inline)
442
+ #%test
443
+ if __name__ == "__main__":
444
+
445
+ #%% testing
446
+
447
+ if not 'baseline' in locals() or 0 :
448
+ from modelclass import model
449
+ madam,baseline = model.modelload('../Examples/ADAM/baseline.pcim',run=1,silent=0 )
450
+ # make a simpel experimet VAT
451
+ scenarie = baseline.copy()
452
+ scenarie.TG = scenarie.TG + 0.05
453
+ _ = madam(scenarie)
454
+
455
+
456
+ _ = Dash_graph(madam,'FY',debug = 0,all=1,filter=30,show_trigger=True,jupyter=True,up=1,port=5006)