siliconcompiler 0.28.2__py3-none-any.whl → 0.28.4__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.
- siliconcompiler/_common.py +12 -0
- siliconcompiler/_metadata.py +1 -1
- siliconcompiler/apps/sc_dashboard.py +6 -2
- siliconcompiler/apps/sc_install.py +61 -13
- siliconcompiler/apps/sc_remote.py +1 -1
- siliconcompiler/core.py +132 -68
- siliconcompiler/fpgas/vpr_example.py +8 -0
- siliconcompiler/package.py +3 -2
- siliconcompiler/remote/client.py +41 -10
- siliconcompiler/report/__init__.py +1 -1
- siliconcompiler/report/{streamlit_report.py → dashboard/__init__.py} +56 -10
- siliconcompiler/report/dashboard/components/__init__.py +546 -0
- siliconcompiler/report/dashboard/components/flowgraph.py +114 -0
- siliconcompiler/report/dashboard/components/graph.py +208 -0
- siliconcompiler/report/dashboard/layouts/__init__.py +20 -0
- siliconcompiler/report/dashboard/layouts/_common.py +43 -0
- siliconcompiler/report/dashboard/layouts/vertical_flowgraph.py +96 -0
- siliconcompiler/report/dashboard/layouts/vertical_flowgraph_node_tab.py +117 -0
- siliconcompiler/report/dashboard/layouts/vertical_flowgraph_sac_tabs.py +110 -0
- siliconcompiler/report/dashboard/state.py +217 -0
- siliconcompiler/report/dashboard/utils/__init__.py +73 -0
- siliconcompiler/report/dashboard/utils/file_utils.py +120 -0
- siliconcompiler/report/dashboard/viewer.py +36 -0
- siliconcompiler/report/report.py +22 -4
- siliconcompiler/report/summary_table.py +1 -2
- siliconcompiler/report/utils.py +1 -2
- siliconcompiler/scheduler/__init__.py +45 -6
- siliconcompiler/schema/schema_obj.py +4 -2
- siliconcompiler/sphinx_ext/dynamicgen.py +6 -0
- siliconcompiler/tools/_common/__init__.py +44 -6
- siliconcompiler/tools/_common/asic.py +79 -23
- siliconcompiler/tools/genfasm/genfasm.py +7 -0
- siliconcompiler/tools/ghdl/convert.py +7 -0
- siliconcompiler/tools/klayout/convert_drc_db.py +60 -0
- siliconcompiler/tools/klayout/drc.py +156 -0
- siliconcompiler/tools/klayout/export.py +2 -0
- siliconcompiler/tools/klayout/klayout.py +0 -1
- siliconcompiler/tools/klayout/klayout_convert_drc_db.py +182 -0
- siliconcompiler/tools/klayout/operations.py +2 -0
- siliconcompiler/tools/klayout/screenshot.py +2 -0
- siliconcompiler/tools/klayout/show.py +4 -4
- siliconcompiler/tools/magic/drc.py +21 -0
- siliconcompiler/tools/magic/extspice.py +21 -0
- siliconcompiler/tools/magic/magic.py +29 -0
- siliconcompiler/tools/magic/sc_drc.tcl +2 -12
- siliconcompiler/tools/magic/sc_extspice.tcl +3 -15
- siliconcompiler/tools/openroad/floorplan.py +5 -0
- siliconcompiler/tools/openroad/openroad.py +56 -5
- siliconcompiler/tools/openroad/scripts/sc_apr.tcl +15 -0
- siliconcompiler/tools/openroad/scripts/sc_cts.tcl +18 -13
- siliconcompiler/tools/openroad/scripts/sc_floorplan.tcl +61 -10
- siliconcompiler/tools/openroad/scripts/sc_metrics.tcl +10 -0
- siliconcompiler/tools/openroad/scripts/sc_procs.tcl +31 -1
- siliconcompiler/tools/openroad/scripts/sc_route.tcl +8 -2
- siliconcompiler/tools/openroad/scripts/sc_screenshot.tcl +0 -5
- siliconcompiler/tools/openroad/scripts/sc_write_images.tcl +36 -6
- siliconcompiler/tools/surelog/__init__.py +12 -0
- siliconcompiler/tools/verilator/compile.py +27 -0
- siliconcompiler/tools/verilator/verilator.py +9 -0
- siliconcompiler/tools/vpr/vpr.py +18 -0
- siliconcompiler/tools/yosys/{syn_asic_fpga_shared.tcl → procs.tcl} +23 -0
- siliconcompiler/tools/yosys/sc_screenshot.tcl +104 -0
- siliconcompiler/tools/yosys/sc_syn.tcl +7 -9
- siliconcompiler/tools/yosys/screenshot.py +153 -0
- siliconcompiler/tools/yosys/syn_asic.py +3 -0
- siliconcompiler/tools/yosys/syn_asic.tcl +1 -3
- siliconcompiler/tools/yosys/syn_fpga.tcl +3 -2
- siliconcompiler/toolscripts/_tools.json +10 -5
- siliconcompiler/toolscripts/rhel8/install-chisel.sh +26 -0
- siliconcompiler/toolscripts/rhel8/install-ghdl.sh +25 -0
- siliconcompiler/toolscripts/rhel8/install-icarus.sh +40 -0
- siliconcompiler/toolscripts/rhel8/install-klayout.sh +17 -0
- siliconcompiler/toolscripts/rhel8/install-magic.sh +26 -0
- siliconcompiler/toolscripts/rhel8/install-montage.sh +5 -0
- siliconcompiler/toolscripts/rhel8/install-netgen.sh +25 -0
- siliconcompiler/toolscripts/rhel8/install-openroad.sh +31 -0
- siliconcompiler/toolscripts/rhel8/install-slang.sh +31 -0
- siliconcompiler/toolscripts/rhel8/install-surelog.sh +32 -0
- siliconcompiler/toolscripts/rhel8/install-sv2v.sh +27 -0
- siliconcompiler/toolscripts/rhel8/install-verible.sh +24 -0
- siliconcompiler/toolscripts/rhel8/install-verilator.sh +40 -0
- siliconcompiler/toolscripts/rhel8/install-xyce.sh +64 -0
- siliconcompiler/toolscripts/rhel8/install-yosys.sh +23 -0
- siliconcompiler/toolscripts/rhel9/install-chisel.sh +26 -0
- siliconcompiler/toolscripts/rhel9/install-ghdl.sh +25 -0
- siliconcompiler/toolscripts/rhel9/install-icarus.sh +40 -0
- siliconcompiler/toolscripts/rhel9/install-klayout.sh +17 -0
- siliconcompiler/toolscripts/rhel9/install-magic.sh +26 -0
- siliconcompiler/toolscripts/rhel9/install-montage.sh +5 -0
- siliconcompiler/toolscripts/rhel9/install-netgen.sh +25 -0
- siliconcompiler/toolscripts/rhel9/install-slang.sh +31 -0
- siliconcompiler/toolscripts/rhel9/install-surelog.sh +32 -0
- siliconcompiler/toolscripts/rhel9/install-sv2v.sh +27 -0
- siliconcompiler/toolscripts/rhel9/install-verible.sh +24 -0
- siliconcompiler/toolscripts/rhel9/install-verilator.sh +40 -0
- siliconcompiler/toolscripts/rhel9/install-xdm.sh +43 -0
- siliconcompiler/toolscripts/rhel9/install-xyce.sh +64 -0
- siliconcompiler/toolscripts/rhel9/install-yosys.sh +23 -0
- siliconcompiler/toolscripts/ubuntu20/install-icepack.sh +1 -1
- siliconcompiler/toolscripts/ubuntu20/install-xdm.sh +40 -0
- siliconcompiler/toolscripts/ubuntu20/install-yosys.sh +2 -2
- siliconcompiler/toolscripts/ubuntu22/install-icepack.sh +1 -1
- siliconcompiler/toolscripts/ubuntu22/install-xdm.sh +40 -0
- siliconcompiler/toolscripts/ubuntu22/install-yosys.sh +2 -2
- siliconcompiler/toolscripts/ubuntu24/install-icepack.sh +1 -1
- siliconcompiler/toolscripts/ubuntu24/install-klayout.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-xdm.sh +40 -0
- siliconcompiler/toolscripts/ubuntu24/install-yosys.sh +2 -2
- siliconcompiler/utils/__init__.py +30 -1
- siliconcompiler/utils/showtools.py +4 -0
- {siliconcompiler-0.28.2.dist-info → siliconcompiler-0.28.4.dist-info}/METADATA +22 -8
- {siliconcompiler-0.28.2.dist-info → siliconcompiler-0.28.4.dist-info}/RECORD +116 -67
- {siliconcompiler-0.28.2.dist-info → siliconcompiler-0.28.4.dist-info}/WHEEL +1 -1
- siliconcompiler/report/streamlit_viewer.py +0 -944
- {siliconcompiler-0.28.2.dist-info → siliconcompiler-0.28.4.dist-info}/LICENSE +0 -0
- {siliconcompiler-0.28.2.dist-info → siliconcompiler-0.28.4.dist-info}/entry_points.txt +0 -0
- {siliconcompiler-0.28.2.dist-info → siliconcompiler-0.28.4.dist-info}/top_level.txt +0 -0
|
@@ -1,944 +0,0 @@
|
|
|
1
|
-
import streamlit
|
|
2
|
-
from streamlit_agraph import agraph, Node, Edge, Config
|
|
3
|
-
from streamlit_tree_select import tree_select
|
|
4
|
-
import streamlit_javascript
|
|
5
|
-
from PIL import Image
|
|
6
|
-
import os
|
|
7
|
-
import argparse
|
|
8
|
-
import json
|
|
9
|
-
import pandas
|
|
10
|
-
import altair
|
|
11
|
-
import gzip
|
|
12
|
-
import base64
|
|
13
|
-
from siliconcompiler.report import report
|
|
14
|
-
from siliconcompiler import Chip, NodeStatus, utils
|
|
15
|
-
from siliconcompiler import __version__ as sc_version
|
|
16
|
-
from siliconcompiler import sc_open
|
|
17
|
-
from siliconcompiler.flowgraph import _get_flowgraph_exit_nodes, _get_flowgraph_entry_nodes
|
|
18
|
-
from siliconcompiler.tools._common import get_tool_task
|
|
19
|
-
|
|
20
|
-
# Streamlit.session_state
|
|
21
|
-
|
|
22
|
-
# key: node_from_flowgraph
|
|
23
|
-
# value: the node selected from the flowgraph or None.
|
|
24
|
-
# purpose: if the flowgraph is hidden, we still want to return the selected node.
|
|
25
|
-
|
|
26
|
-
# key: job
|
|
27
|
-
# value: the run or added chip to inspect.
|
|
28
|
-
# purpose: selecting a different job will reload the entire screen.
|
|
29
|
-
|
|
30
|
-
# key: cfg
|
|
31
|
-
# value: the chip loaded in with the -cfg flag.
|
|
32
|
-
# purpose: to keep track of the original chip (default chip) without having to reload it.
|
|
33
|
-
|
|
34
|
-
# key: transpose
|
|
35
|
-
# value: whether or not the data table is transposed.
|
|
36
|
-
# purpose: to know the state of the transpose button.
|
|
37
|
-
|
|
38
|
-
# key: selected
|
|
39
|
-
# value: a list of the selected files.
|
|
40
|
-
# purpose: know the order in which files are selected by comparing states.
|
|
41
|
-
|
|
42
|
-
# key: expanded
|
|
43
|
-
# value: a list of the expanded folders in the file viewer.
|
|
44
|
-
# purpose: know which folders are expanded after a reload.
|
|
45
|
-
|
|
46
|
-
# key: flowgraph
|
|
47
|
-
# value: if the flowgraph is shown or not shown
|
|
48
|
-
# purpose: need to keep track of the state of the flowgraph
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
# for flowgraph
|
|
52
|
-
SUCCESS_COLOR = '#8EA604' # green
|
|
53
|
-
PENDING_COLOR = '#F5BB00' # yellow, could use: #EC9F05
|
|
54
|
-
FAILURE_COLOR = '#FF4E00' # red
|
|
55
|
-
SC_LOGO_PATH = os.path.join(os.path.dirname(__file__), '..', 'data', 'logo.png')
|
|
56
|
-
SC_FONT_PATH = \
|
|
57
|
-
os.path.join(os.path.dirname(__file__), '..', 'data', 'RobotoMono', 'RobotoMono-Regular.ttf')
|
|
58
|
-
SC_ABOUT = [
|
|
59
|
-
f"SiliconCompiler {sc_version}",
|
|
60
|
-
'''A compiler framework that automates translation from source code to
|
|
61
|
-
silicon.''',
|
|
62
|
-
"https://www.siliconcompiler.com/",
|
|
63
|
-
"https://github.com/siliconcompiler/siliconcompiler/"
|
|
64
|
-
]
|
|
65
|
-
SC_MENU = {"Get help": "https://docs.siliconcompiler.com/",
|
|
66
|
-
"Report a Bug":
|
|
67
|
-
'''https://github.com/siliconcompiler/siliconcompiler/issues''',
|
|
68
|
-
"About": "\n\n".join(SC_ABOUT)}
|
|
69
|
-
|
|
70
|
-
# opened by running command in siliconcompiler/apps/sc_dashboard.py
|
|
71
|
-
parser = argparse.ArgumentParser('dashboard')
|
|
72
|
-
parser.add_argument('cfg', nargs='?')
|
|
73
|
-
args = parser.parse_args()
|
|
74
|
-
|
|
75
|
-
if not args.cfg:
|
|
76
|
-
raise ValueError('configuration not provided')
|
|
77
|
-
|
|
78
|
-
if 'job' not in streamlit.session_state:
|
|
79
|
-
with open(args.cfg, 'r') as f:
|
|
80
|
-
config = json.load(f)
|
|
81
|
-
chip = Chip(design='')
|
|
82
|
-
chip.read_manifest(config["manifest"])
|
|
83
|
-
for file_path in config['graph_chips']:
|
|
84
|
-
graph_chip = Chip(design='')
|
|
85
|
-
graph_chip.read_manifest(file_path)
|
|
86
|
-
chip.schema.cfg['history'][os.path.basename(file_path)] = graph_chip.schema.cfg
|
|
87
|
-
streamlit.set_page_config(page_title=f'{chip.design} dashboard',
|
|
88
|
-
page_icon=Image.open(SC_LOGO_PATH), layout="wide",
|
|
89
|
-
menu_items=SC_MENU)
|
|
90
|
-
streamlit.session_state['cfg'] = chip
|
|
91
|
-
streamlit.session_state['job'] = 'default'
|
|
92
|
-
selected_chip = chip
|
|
93
|
-
streamlit.session_state['transpose'] = False
|
|
94
|
-
else:
|
|
95
|
-
chip = streamlit.session_state['cfg']
|
|
96
|
-
streamlit.set_page_config(page_title=f'{chip.design} dashboard',
|
|
97
|
-
page_icon=Image.open(SC_LOGO_PATH), layout="wide",
|
|
98
|
-
menu_items=SC_MENU)
|
|
99
|
-
selected_chip = Chip(design='')
|
|
100
|
-
job = streamlit.session_state['job']
|
|
101
|
-
if job == 'default':
|
|
102
|
-
selected_chip = chip
|
|
103
|
-
else:
|
|
104
|
-
selected_chip.schema = chip.schema.history(job)
|
|
105
|
-
selected_chip.set('design', chip.design)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def _convert_filepaths(logs_and_reports):
|
|
109
|
-
"""
|
|
110
|
-
Converts the logs_and_reports found to the structure
|
|
111
|
-
required by streamlit_tree_select. Success is predicated on the order of
|
|
112
|
-
logs_and_reports outlined in report.get_files.
|
|
113
|
-
|
|
114
|
-
Args:
|
|
115
|
-
logs_and_reports (list) : A list of 3-tuples with order of a path name,
|
|
116
|
-
folder in the..., and files in the....
|
|
117
|
-
"""
|
|
118
|
-
if not logs_and_reports:
|
|
119
|
-
return []
|
|
120
|
-
|
|
121
|
-
all_files = {}
|
|
122
|
-
for path_name, folders, files in logs_and_reports:
|
|
123
|
-
all_files[path_name] = {
|
|
124
|
-
'files': list(files),
|
|
125
|
-
'folders': list(folders)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
def organize_node(base_folder):
|
|
129
|
-
nodes = []
|
|
130
|
-
|
|
131
|
-
for folder in all_files[base_folder]['folders']:
|
|
132
|
-
path = os.path.join(base_folder, folder)
|
|
133
|
-
nodes.append({
|
|
134
|
-
'value': path,
|
|
135
|
-
'label': folder,
|
|
136
|
-
'children': organize_node(path)
|
|
137
|
-
})
|
|
138
|
-
for file in all_files[base_folder]['files']:
|
|
139
|
-
nodes.append({
|
|
140
|
-
'value': os.path.join(base_folder, file),
|
|
141
|
-
'label': file
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
return nodes
|
|
145
|
-
|
|
146
|
-
starting_path_name = logs_and_reports[0][0]
|
|
147
|
-
return organize_node(starting_path_name)
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
def get_nodes_and_edges(chip, node_dependencies, successful_path,
|
|
151
|
-
successful_path_node_opacity=1, successful_path_node_width=3,
|
|
152
|
-
successful_path_edge_width=5, default_node_opacity=0.2,
|
|
153
|
-
default_node_border_width=1, default_edge_width=3):
|
|
154
|
-
"""
|
|
155
|
-
Returns the nodes and edges required to make a streamlit_agraph.
|
|
156
|
-
|
|
157
|
-
Args:
|
|
158
|
-
chip (Chip) : The chip object that contains the schema read from.
|
|
159
|
-
node_dependencies (dict) : Dictionary mapping nodes
|
|
160
|
-
(as tuples of step/index) to their input nodes.
|
|
161
|
-
successful_path (set) : Contains all the nodes that are part of the 'winning' path.
|
|
162
|
-
succesful_path_node_opacity (float) : A number between 0 and 1 (inclusive) which
|
|
163
|
-
represents the opacity for nodes on a successful path.
|
|
164
|
-
succesful_path_node_border_width (int) : A number between 0 or greater
|
|
165
|
-
which represents the width for nodes on a successful path.
|
|
166
|
-
succesful_path_edge_width (int) : A number between 0 or greater which
|
|
167
|
-
represents the width for edges on a successful path.
|
|
168
|
-
default_node_opacity (float) : A number between 0 and 1 (inclusive)
|
|
169
|
-
which represents the opacity for nodes of a node not on a successful path.
|
|
170
|
-
default_node_border_width (int) : A number between 0 or greater
|
|
171
|
-
which represents the width for nodes not on a successful path.
|
|
172
|
-
default_edge_width (int) : A number between 0 or greater which
|
|
173
|
-
represents the width for edges not on a successful path.
|
|
174
|
-
"""
|
|
175
|
-
nodes = []
|
|
176
|
-
edges = []
|
|
177
|
-
for step, index in node_dependencies:
|
|
178
|
-
node_opacity = default_node_opacity
|
|
179
|
-
node_border_width = default_node_border_width
|
|
180
|
-
if (step, index) in successful_path:
|
|
181
|
-
node_opacity = successful_path_node_opacity
|
|
182
|
-
if (step, index) in _get_flowgraph_exit_nodes(chip, chip.get('option', 'flow')) or \
|
|
183
|
-
(step, index) in _get_flowgraph_entry_nodes(chip, chip.get('option', 'flow')):
|
|
184
|
-
node_border_width = successful_path_node_width
|
|
185
|
-
node_status = chip.get('record', 'status', step=step, index=index)
|
|
186
|
-
if node_status == NodeStatus.SUCCESS:
|
|
187
|
-
node_color = SUCCESS_COLOR
|
|
188
|
-
elif node_status == NodeStatus.ERROR:
|
|
189
|
-
node_color = FAILURE_COLOR
|
|
190
|
-
else:
|
|
191
|
-
node_color = PENDING_COLOR
|
|
192
|
-
tool, task = get_tool_task(chip, step, index)
|
|
193
|
-
node_name = f'{step}{index}'
|
|
194
|
-
label = node_name + "\n" + tool + "/" + task
|
|
195
|
-
if tool == 'builtin':
|
|
196
|
-
label = node_name + "\n" + tool
|
|
197
|
-
nodes.append(Node(id=node_name, label=label, color=node_color, opacity=node_opacity,
|
|
198
|
-
borderWidth=node_border_width, shape='oval'))
|
|
199
|
-
for source_step, source_index in node_dependencies[step, index]:
|
|
200
|
-
edge_width = default_edge_width
|
|
201
|
-
if (source_step, source_index) in successful_path and \
|
|
202
|
-
(source_step, source_index) in successful_path:
|
|
203
|
-
edge_width = successful_path_edge_width
|
|
204
|
-
edges.append(Edge(source=source_step + source_index, dir='up', target=node_name,
|
|
205
|
-
width=edge_width, color='black', curve=True))
|
|
206
|
-
return nodes, edges
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
def file_viewer_module(display_file_content, chip, step, index, header_col_width=0.89):
|
|
210
|
-
"""
|
|
211
|
-
Displays the file if present. If not, displays an error message.
|
|
212
|
-
|
|
213
|
-
Args:
|
|
214
|
-
display_file_content (bool) : True if there is a file selected to display
|
|
215
|
-
header_col_width (float) : A number between 0 and 1 which is the
|
|
216
|
-
percentage of the width of the screen given to the header. The rest
|
|
217
|
-
is given to the download button.
|
|
218
|
-
"""
|
|
219
|
-
if not display_file_content:
|
|
220
|
-
streamlit.error('Select a file in the metrics tab first!')
|
|
221
|
-
return
|
|
222
|
-
path = streamlit.session_state['selected'][0]
|
|
223
|
-
# This file extension may be '.gz', if it is, it is compressed.
|
|
224
|
-
_, compressed_file_extension = os.path.splitext(path)
|
|
225
|
-
# This is the true file_extension of the file, regardless of if it is
|
|
226
|
-
# compressed or not.
|
|
227
|
-
file_extension = utils.get_file_ext(path)
|
|
228
|
-
header_col, download_col = \
|
|
229
|
-
streamlit.columns([header_col_width, 1 - header_col_width], gap='small')
|
|
230
|
-
relative_path = os.path.relpath(path, chip.getworkdir(step=step, index=index))
|
|
231
|
-
with header_col:
|
|
232
|
-
streamlit.header(relative_path)
|
|
233
|
-
with download_col:
|
|
234
|
-
streamlit.markdown(' ') # aligns download button with title
|
|
235
|
-
streamlit.download_button(label="Download file",
|
|
236
|
-
data=path,
|
|
237
|
-
file_name=relative_path)
|
|
238
|
-
|
|
239
|
-
if file_extension.lower() in ("png", "jpg"):
|
|
240
|
-
streamlit.image(path)
|
|
241
|
-
else:
|
|
242
|
-
try:
|
|
243
|
-
open_func, open_args = sc_open, []
|
|
244
|
-
if compressed_file_extension == '.gz':
|
|
245
|
-
open_func, open_args = gzip.open, ['rt']
|
|
246
|
-
|
|
247
|
-
with open_func(path, *open_args) as fid:
|
|
248
|
-
content = fid.read()
|
|
249
|
-
if file_extension.lower() == "json":
|
|
250
|
-
streamlit.json(content)
|
|
251
|
-
else:
|
|
252
|
-
streamlit.code(content, language='markdown', line_numbers=True)
|
|
253
|
-
except UnicodeDecodeError: # might be OSError, not sure yet
|
|
254
|
-
streamlit.markdown('Cannot read file')
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
def show_files(chip, step, index):
|
|
258
|
-
"""
|
|
259
|
-
Displays the logs and reports using streamlit_tree_select.
|
|
260
|
-
|
|
261
|
-
Args:
|
|
262
|
-
chip (Chip) : the chip object that contains the schema read from.
|
|
263
|
-
step (string) : step of node.
|
|
264
|
-
index (string) : index of node.
|
|
265
|
-
"""
|
|
266
|
-
streamlit.subheader(f'{step}{index} Files')
|
|
267
|
-
logs_and_reports = report.get_files(chip, step, index)
|
|
268
|
-
logs_and_reports = _convert_filepaths(logs_and_reports)
|
|
269
|
-
if logs_and_reports == []:
|
|
270
|
-
streamlit.markdown('No files to show')
|
|
271
|
-
return False
|
|
272
|
-
# kinda janky at the moment, does not always flip immediately
|
|
273
|
-
# TODO make so that selection changes on first click
|
|
274
|
-
if "selected" not in streamlit.session_state:
|
|
275
|
-
streamlit.session_state['selected'] = []
|
|
276
|
-
if "expanded" not in streamlit.session_state:
|
|
277
|
-
streamlit.session_state['expanded'] = []
|
|
278
|
-
|
|
279
|
-
selected = tree_select(logs_and_reports,
|
|
280
|
-
expand_on_click=True,
|
|
281
|
-
checked=streamlit.session_state['selected'],
|
|
282
|
-
expanded=streamlit.session_state['expanded'],
|
|
283
|
-
only_leaf_checkboxes=True)
|
|
284
|
-
# only include files in 'checked' (folders are also included when they are opened)
|
|
285
|
-
selected['checked'] = [x for x in selected['checked'] if os.path.isfile(x)]
|
|
286
|
-
if len(selected['checked']) == 0:
|
|
287
|
-
streamlit.session_state['selected'] = []
|
|
288
|
-
elif len(selected["checked"]) == 1:
|
|
289
|
-
streamlit.session_state['selected'] = [*selected["checked"]]
|
|
290
|
-
elif len(selected["checked"]) > 1:
|
|
291
|
-
newly_selected = selected["checked"][0]
|
|
292
|
-
if len(streamlit.session_state['selected']) != 0:
|
|
293
|
-
for x in selected["checked"]:
|
|
294
|
-
if x != streamlit.session_state['selected'][0]:
|
|
295
|
-
newly_selected = x
|
|
296
|
-
break
|
|
297
|
-
streamlit.session_state['selected'] = [newly_selected]
|
|
298
|
-
streamlit.session_state['expanded'] = [*selected["expanded"]]
|
|
299
|
-
streamlit.rerun()
|
|
300
|
-
if streamlit.session_state['selected'] != []:
|
|
301
|
-
return True
|
|
302
|
-
return False
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
def show_metrics_for_file(chip, step, index):
|
|
306
|
-
"""
|
|
307
|
-
Displays the metrics that are included in each file.
|
|
308
|
-
|
|
309
|
-
Args:
|
|
310
|
-
chip (Chip) : The chip object that contains the schema read from.
|
|
311
|
-
step (string) : Step of node.
|
|
312
|
-
index (string) : Index of node.
|
|
313
|
-
"""
|
|
314
|
-
if 'selected' in streamlit.session_state and \
|
|
315
|
-
len(streamlit.session_state['selected']) == 1:
|
|
316
|
-
file = streamlit.session_state['selected'][0]
|
|
317
|
-
metrics_of_file = report.get_metrics_source(chip, step, index)
|
|
318
|
-
file = os.path.relpath(file, f"/{step}/{index}")
|
|
319
|
-
if file in metrics_of_file:
|
|
320
|
-
metrics = ", ".join(metrics_of_file[file]) + "."
|
|
321
|
-
streamlit.success("This file includes the metrics of " + metrics)
|
|
322
|
-
else:
|
|
323
|
-
streamlit.warning("This file does not include any metrics.")
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
def manifest_module(chip, manifest, ui_width, max_num_of_keys_to_show=20,
|
|
327
|
-
default_toggle_width_in_percent=0.2,
|
|
328
|
-
default_toggle_width_in_pixels=200, header_col_width=0.85):
|
|
329
|
-
"""
|
|
330
|
-
Displays the manifest and a way to search through the manifest.
|
|
331
|
-
|
|
332
|
-
Args:
|
|
333
|
-
chip (Chip) : The chip object that contains the schema read from.
|
|
334
|
-
manifest (dict) : Layered dictionary containing a filtered version of the
|
|
335
|
-
chip.schema.cfg
|
|
336
|
-
ui_width (int) : The width of the screen of the web browser in pixels.
|
|
337
|
-
max_num_of_keys_to_show (int) : The maximum number of keys that the
|
|
338
|
-
manifest may have in order to be automatically expanded.
|
|
339
|
-
default_toggle_width_in_percent (float) : A number between 0 and 1
|
|
340
|
-
which is the maximum percentage of the width of the screen given to
|
|
341
|
-
the checkbox. The rest is given to the search bars.
|
|
342
|
-
default_toggle_width_in_pixels (int) : A number greater than 0 which
|
|
343
|
-
is the maximum pixel width of the screen given to the checkbox. The
|
|
344
|
-
rest is given to the search bars.
|
|
345
|
-
header_col_width (float) : A number between 0 and 1 which is the maximum
|
|
346
|
-
percentage of the width of the screen given to the header. The rest
|
|
347
|
-
is given to the download button.
|
|
348
|
-
"""
|
|
349
|
-
# TODO include toggle to expand the tree, find less redundant header name
|
|
350
|
-
header_col, download_col = streamlit.columns([header_col_width, 1 - header_col_width],
|
|
351
|
-
gap='small')
|
|
352
|
-
with header_col:
|
|
353
|
-
streamlit.header('Manifest Tree')
|
|
354
|
-
if ui_width > 0:
|
|
355
|
-
toggle_col_width_in_percent = \
|
|
356
|
-
min(default_toggle_width_in_pixels / ui_width, default_toggle_width_in_percent)
|
|
357
|
-
else:
|
|
358
|
-
toggle_col_width_in_percent = default_toggle_width_in_percent
|
|
359
|
-
search_col_width = (1 - toggle_col_width_in_percent) / 2
|
|
360
|
-
key_search_col, value_search_col, toggle_col = \
|
|
361
|
-
streamlit.columns([search_col_width, search_col_width,
|
|
362
|
-
toggle_col_width_in_percent], gap="large")
|
|
363
|
-
with toggle_col:
|
|
364
|
-
# to align the checkbox with the search bars
|
|
365
|
-
streamlit.markdown('')
|
|
366
|
-
streamlit.markdown('')
|
|
367
|
-
if streamlit.checkbox('Raw manifest',
|
|
368
|
-
help='Click here to see the JSON before it was made more readable'):
|
|
369
|
-
manifest_to_show = chip.schema.cfg
|
|
370
|
-
else:
|
|
371
|
-
manifest_to_show = manifest
|
|
372
|
-
with key_search_col:
|
|
373
|
-
key = streamlit.text_input('Search Keys', '', placeholder="Keys")
|
|
374
|
-
if key != '':
|
|
375
|
-
manifest_to_show = report.search_manifest(manifest_to_show, key_search=key)
|
|
376
|
-
with value_search_col:
|
|
377
|
-
value = streamlit.text_input('Search Values', '', placeholder="Values")
|
|
378
|
-
if value != '':
|
|
379
|
-
manifest_to_show = report.search_manifest(manifest_to_show, value_search=value)
|
|
380
|
-
with download_col:
|
|
381
|
-
streamlit.markdown(' ') # aligns download button with title
|
|
382
|
-
streamlit.download_button(label='Download manifest', file_name='manifest.json',
|
|
383
|
-
data=json.dumps(manifest_to_show, indent=4),
|
|
384
|
-
mime="application/json")
|
|
385
|
-
numOfKeys = report.get_total_manifest_key_count(manifest_to_show)
|
|
386
|
-
streamlit.json(manifest_to_show, expanded=(numOfKeys < max_num_of_keys_to_show))
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
def select_nodes(metric_dataframe, node_from_flowgraph):
|
|
390
|
-
"""
|
|
391
|
-
Displays selectbox for nodes to show in the node information panel. Since
|
|
392
|
-
both the flowgraph and selectbox show which node's information is
|
|
393
|
-
displayed, the one clicked more recently will be displayed.
|
|
394
|
-
|
|
395
|
-
Args:
|
|
396
|
-
metric_dataframe (Pandas.DataFrame) : Contains the metrics of all nodes.
|
|
397
|
-
node_from_flowgraph (string/None) : Contains a string of the node to
|
|
398
|
-
display or None if none exists.
|
|
399
|
-
"""
|
|
400
|
-
option = metric_dataframe.columns.tolist()[0]
|
|
401
|
-
with streamlit.expander("Select Node"):
|
|
402
|
-
with streamlit.form("nodes"):
|
|
403
|
-
option = streamlit.selectbox('Pick a node to inspect',
|
|
404
|
-
metric_dataframe.columns.tolist())
|
|
405
|
-
params_submitted = streamlit.form_submit_button('Apply')
|
|
406
|
-
if not params_submitted and node_from_flowgraph is not None:
|
|
407
|
-
option = node_from_flowgraph
|
|
408
|
-
streamlit.session_state['selected'] = []
|
|
409
|
-
if params_submitted:
|
|
410
|
-
streamlit.session_state['selected'] = []
|
|
411
|
-
return option
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
def metrics_dataframe_module(metric_dataframe, metric_to_metric_unit_map):
|
|
415
|
-
"""
|
|
416
|
-
Displays multi-select check box to the users which allows them to select
|
|
417
|
-
which nodes and metrics to view in the dataframe.
|
|
418
|
-
|
|
419
|
-
Args:
|
|
420
|
-
metric_dataframe (Pandas.DataFrame) : Contains the metrics of all nodes.
|
|
421
|
-
metric_to_metric_unit_map (dict) : Maps the metric to the associated metric unit.
|
|
422
|
-
"""
|
|
423
|
-
show_dataframe_header()
|
|
424
|
-
container = streamlit.container()
|
|
425
|
-
transpose = streamlit.session_state['transpose']
|
|
426
|
-
if transpose:
|
|
427
|
-
metric_dataframe = metric_dataframe.transpose()
|
|
428
|
-
metrics_list = metric_dataframe.columns.tolist()
|
|
429
|
-
node_list = metric_dataframe.index.tolist()
|
|
430
|
-
else:
|
|
431
|
-
metrics_list = metric_dataframe.index.tolist()
|
|
432
|
-
node_list = metric_dataframe.columns.tolist()
|
|
433
|
-
display_to_data = {}
|
|
434
|
-
display_options = []
|
|
435
|
-
for metric_unit in metrics_list:
|
|
436
|
-
metric = metric_to_metric_unit_map[metric_unit]
|
|
437
|
-
display_to_data[metric] = metric_unit
|
|
438
|
-
display_options.append(metric)
|
|
439
|
-
options = {'metrics': [], 'nodes': []}
|
|
440
|
-
# pick parameters
|
|
441
|
-
with streamlit.expander("Select Parameters"):
|
|
442
|
-
with streamlit.form("params"):
|
|
443
|
-
nodes = streamlit.multiselect('Pick nodes to include', node_list, [])
|
|
444
|
-
options['nodes'] = nodes
|
|
445
|
-
metrics = streamlit.multiselect('Pick metrics to include?', display_options, [])
|
|
446
|
-
options['metrics'] = []
|
|
447
|
-
for metric in metrics:
|
|
448
|
-
options['metrics'].append(display_to_data[metric])
|
|
449
|
-
streamlit.form_submit_button('Apply')
|
|
450
|
-
if not options['nodes']:
|
|
451
|
-
options['nodes'] = node_list
|
|
452
|
-
if not options['metrics']:
|
|
453
|
-
options['metrics'] = metrics_list
|
|
454
|
-
# showing the dataframe
|
|
455
|
-
# TODO By July 2024, Streamlit will let catch click events on the dataframe
|
|
456
|
-
if transpose:
|
|
457
|
-
container.dataframe((metric_dataframe.loc[options['nodes'], options['metrics']]),
|
|
458
|
-
use_container_width=True)
|
|
459
|
-
else:
|
|
460
|
-
container.dataframe((metric_dataframe.loc[options['metrics'], options['nodes']]),
|
|
461
|
-
use_container_width=True)
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
def show_dataframe_header(header_col_width=0.7):
|
|
465
|
-
"""
|
|
466
|
-
Displays the header and toggle for the dataframe. If the toggle is flipped,
|
|
467
|
-
it will update the view of the dataframe accordingly.
|
|
468
|
-
|
|
469
|
-
Args:
|
|
470
|
-
header_col_width (float) : A number between 0 and 1 which is the
|
|
471
|
-
percentage of the width of the screen given to the header. The rest
|
|
472
|
-
is given to the transpose toggle.
|
|
473
|
-
"""
|
|
474
|
-
header_col, transpose_col = streamlit.columns([header_col_width, 1 - header_col_width],
|
|
475
|
-
gap="large")
|
|
476
|
-
with header_col:
|
|
477
|
-
streamlit.header('Metrics')
|
|
478
|
-
with transpose_col:
|
|
479
|
-
streamlit.markdown('')
|
|
480
|
-
streamlit.markdown('')
|
|
481
|
-
streamlit.session_state['transpose'] = \
|
|
482
|
-
streamlit.checkbox('Transpose', help='Click here to see the table transposed')
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
def display_flowgraph_toggle(label_after, vertical_layout_collapsed=False):
|
|
486
|
-
"""
|
|
487
|
-
Displays the toggle for the flowgraph.
|
|
488
|
-
|
|
489
|
-
Args:
|
|
490
|
-
label_after (bool) : the default label for the toggle
|
|
491
|
-
vertical_layout_collapsed (bool) : If vertical_layout_collapsed,
|
|
492
|
-
there is no need to align the toggle with the header
|
|
493
|
-
"""
|
|
494
|
-
if not vertical_layout_collapsed:
|
|
495
|
-
# this horizontally aligns the toggle with the header
|
|
496
|
-
streamlit.markdown("")
|
|
497
|
-
streamlit.markdown("")
|
|
498
|
-
fg_toggle = not streamlit.checkbox('Hide flowgraph', help='Click here to hide the flowgraph')
|
|
499
|
-
streamlit.session_state['flowgraph'] = fg_toggle
|
|
500
|
-
if streamlit.session_state['flowgraph'] != label_after:
|
|
501
|
-
streamlit.rerun()
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
def show_flowgraph(chip):
|
|
505
|
-
'''
|
|
506
|
-
This function creates, displays, and returns the selected node of the flowgraph.
|
|
507
|
-
|
|
508
|
-
Args:
|
|
509
|
-
chip (Chip) : The chip object that contains the schema read from.
|
|
510
|
-
'''
|
|
511
|
-
nodes, edges = get_nodes_and_edges(chip, report.get_flowgraph_edges(chip),
|
|
512
|
-
report.get_flowgraph_path(chip))
|
|
513
|
-
config = Config(width='100%', directed=True, physics=False, hierarchical=True,
|
|
514
|
-
clickToUse=True, nodeSpacing=150, levelSeparation=100,
|
|
515
|
-
sortMethod='directed')
|
|
516
|
-
node_from_flowgraph = agraph(nodes=nodes, edges=edges, config=config)
|
|
517
|
-
return node_from_flowgraph
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
def show_title_and_runs(title_col_width=0.7):
|
|
521
|
-
"""
|
|
522
|
-
Displays the title and a selectbox that allows you to select a given run
|
|
523
|
-
to inspect.
|
|
524
|
-
|
|
525
|
-
Args:
|
|
526
|
-
title_col_width (float) : A number between 0 and 1 which is the percentage of the
|
|
527
|
-
width of the screen given to the title and logo. The rest is given to selectbox.
|
|
528
|
-
"""
|
|
529
|
-
title_col, job_select_col = \
|
|
530
|
-
streamlit.columns([title_col_width, 1 - title_col_width], gap="large")
|
|
531
|
-
with title_col:
|
|
532
|
-
streamlit.markdown(
|
|
533
|
-
'''
|
|
534
|
-
<head>
|
|
535
|
-
<style>
|
|
536
|
-
/* Define the @font-face rule */
|
|
537
|
-
@font-face {
|
|
538
|
-
font-family: 'Roboto Mono';
|
|
539
|
-
src: url(SC_FONT_PATH) format('truetype');
|
|
540
|
-
font-weight: normal;
|
|
541
|
-
font-style: normal;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
/* Styles for the logo and text */
|
|
545
|
-
.logo-container {
|
|
546
|
-
display: flex;
|
|
547
|
-
align-items: flex-start;
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
.logo-image {
|
|
551
|
-
margin-right: 10px;
|
|
552
|
-
margin-top: -10px;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
.logo-text {
|
|
556
|
-
display: flex;
|
|
557
|
-
flex-direction: column;
|
|
558
|
-
margin-top: -20px;
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
.text1 {
|
|
562
|
-
color: #F1C437; /* Yellow color */
|
|
563
|
-
font-family: 'Roboto Mono', sans-serif;
|
|
564
|
-
font-weight: 700 !important;
|
|
565
|
-
font-size: 30px !important;
|
|
566
|
-
margin-bottom: -16px;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
.text2 {
|
|
570
|
-
color: #1D4482; /* Blue color */
|
|
571
|
-
font-family: 'Roboto Mono', sans-serif;
|
|
572
|
-
font-weight: 700 !important;
|
|
573
|
-
font-size: 30px !important;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
</style>
|
|
577
|
-
</head>''',
|
|
578
|
-
unsafe_allow_html=True
|
|
579
|
-
)
|
|
580
|
-
|
|
581
|
-
streamlit.markdown(
|
|
582
|
-
f'''
|
|
583
|
-
<body>
|
|
584
|
-
<div class="logo-container">
|
|
585
|
-
<img src="data:image/png;base64,{base64.b64encode(open(SC_LOGO_PATH,
|
|
586
|
-
"rb").read()).decode()}" alt="Logo Image" class="logo-image" height="61">
|
|
587
|
-
<div class="logo-text">
|
|
588
|
-
<p class="text1">{streamlit.session_state['cfg'].design}</p>
|
|
589
|
-
<p class="text2">dashboard</p>
|
|
590
|
-
</div>
|
|
591
|
-
</div>
|
|
592
|
-
</body>
|
|
593
|
-
''',
|
|
594
|
-
unsafe_allow_html=True
|
|
595
|
-
)
|
|
596
|
-
with job_select_col:
|
|
597
|
-
all_jobs = streamlit.session_state['cfg'].getkeys('history')
|
|
598
|
-
all_jobs.insert(0, 'default')
|
|
599
|
-
job = streamlit.selectbox('pick a job', all_jobs,
|
|
600
|
-
label_visibility='collapsed')
|
|
601
|
-
previous_job = streamlit.session_state['job']
|
|
602
|
-
streamlit.session_state['job'] = job
|
|
603
|
-
if previous_job != job:
|
|
604
|
-
streamlit.rerun()
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
def show_metric_and_node_selection_for_graph(metrics, nodes, graph_number):
|
|
608
|
-
"""
|
|
609
|
-
Displays selectbox for metrics and nodes which informs the graph on what
|
|
610
|
-
to display.
|
|
611
|
-
|
|
612
|
-
Args:
|
|
613
|
-
metrics (list) : A list of metrics that are set for all chips given in chips.
|
|
614
|
-
nodes (list) : A list of nodes given in the form f'{step}{index}'
|
|
615
|
-
graph_number (int) : The number of graphs there are. Used to create
|
|
616
|
-
keys to distinguish selectboxes from each other.
|
|
617
|
-
"""
|
|
618
|
-
metric_selector_col, node_selector_col = streamlit.columns(2, gap='small')
|
|
619
|
-
with metric_selector_col:
|
|
620
|
-
with streamlit.expander('Select a Metric'):
|
|
621
|
-
with streamlit.form(f'metrics{graph_number}'):
|
|
622
|
-
selected_metric = streamlit.selectbox('Select a Metric', metrics,
|
|
623
|
-
label_visibility='collapsed',
|
|
624
|
-
key=f'metric selection {graph_number}')
|
|
625
|
-
streamlit.form_submit_button('Apply')
|
|
626
|
-
with node_selector_col:
|
|
627
|
-
with streamlit.expander('Select Nodes'):
|
|
628
|
-
with streamlit.form(f'nodes{graph_number}'):
|
|
629
|
-
selected_nodes = \
|
|
630
|
-
streamlit.multiselect('Select a Node', nodes, label_visibility='collapsed',
|
|
631
|
-
key=f'node selection {graph_number}', default=nodes[0])
|
|
632
|
-
streamlit.form_submit_button('Apply')
|
|
633
|
-
return selected_metric, selected_nodes
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
def show_graph(data, x_axis_label, y_axis_label, color_label, height=300):
|
|
637
|
-
"""
|
|
638
|
-
Displays a graph with the given "data" on the y-axis and "jobs" on the x-axis.
|
|
639
|
-
|
|
640
|
-
Args:
|
|
641
|
-
data (Pandas.DataFrame) : A dataframe containing all the graphing data.
|
|
642
|
-
x_axis_label (string) : The name of the runs column.
|
|
643
|
-
y_axis_label (string) : The name of the jobs column.
|
|
644
|
-
color_label (string) : The name of the nodes column.
|
|
645
|
-
height (int) : The height of one graph.
|
|
646
|
-
"""
|
|
647
|
-
x_axis = altair.X(x_axis_label, axis=altair.Axis(labelAngle=-75))
|
|
648
|
-
y_axis = y_axis_label
|
|
649
|
-
color = color_label
|
|
650
|
-
chart = altair.Chart(data, height=height).mark_line(point=True).encode(x=x_axis, y=y_axis,
|
|
651
|
-
color=color)
|
|
652
|
-
streamlit.altair_chart(chart, use_container_width=True, theme='streamlit')
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
def select_runs(jobs):
|
|
656
|
-
"""
|
|
657
|
-
Displays a dataframe that can be edited to select specific jobs to include
|
|
658
|
-
in the analysis.
|
|
659
|
-
|
|
660
|
-
Args:
|
|
661
|
-
jobs (list) : A list of job names.
|
|
662
|
-
"""
|
|
663
|
-
all_jobs = pandas.DataFrame({'job names': jobs, 'selected jobs': [True] * len(jobs)})
|
|
664
|
-
configuration = {'selected jobs': streamlit.column_config.CheckboxColumn('Select runs',
|
|
665
|
-
default=True)}
|
|
666
|
-
filtered_jobs = streamlit.data_editor(all_jobs, disabled=['job names'],
|
|
667
|
-
use_container_width=True, hide_index=True,
|
|
668
|
-
column_config=configuration)
|
|
669
|
-
return filtered_jobs
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
def structure_graph_data(chips, metric, selected_jobs, nodes):
|
|
673
|
-
"""
|
|
674
|
-
Displays a graph and it's corresponding metric and node selection.
|
|
675
|
-
|
|
676
|
-
Args:
|
|
677
|
-
chips (list) : A list of tuples in the form (chip, chip name) where
|
|
678
|
-
the chip name is a string.
|
|
679
|
-
metric (string) : The metric to be inspected.
|
|
680
|
-
selected_jobs (pandas.DataFrame) : A dataframe with a column called
|
|
681
|
-
'selected jobs' which identifies which jobs the user wants to see
|
|
682
|
-
and a corresponding column called 'job names'.
|
|
683
|
-
nodes (list) : A list of dictionaries with the form (step, index).
|
|
684
|
-
"""
|
|
685
|
-
x_axis_label = 'runs'
|
|
686
|
-
y_axis_label = metric
|
|
687
|
-
color_label = 'nodes'
|
|
688
|
-
if not nodes:
|
|
689
|
-
show_graph(pandas.DataFrame({x_axis_label: [], y_axis_label: [], color_label: []}),
|
|
690
|
-
x_axis_label, y_axis_label, color_label)
|
|
691
|
-
return
|
|
692
|
-
data, metric_unit = report.get_chart_data(chips, metric, nodes)
|
|
693
|
-
if metric_unit:
|
|
694
|
-
y_axis_label = f'{metric}({metric_unit})'
|
|
695
|
-
filtered_data = {x_axis_label: [], y_axis_label: [], color_label: []}
|
|
696
|
-
# filtering through data
|
|
697
|
-
for is_selected, job_name in zip(selected_jobs['selected jobs'].tolist(),
|
|
698
|
-
selected_jobs['job names'].tolist()):
|
|
699
|
-
if is_selected:
|
|
700
|
-
for step, index in data:
|
|
701
|
-
filtered_data[x_axis_label].append(job_name)
|
|
702
|
-
filtered_data[color_label].append(step + index)
|
|
703
|
-
if job_name not in data[(step, index)].keys():
|
|
704
|
-
filtered_data[y_axis_label].append(None)
|
|
705
|
-
else:
|
|
706
|
-
filtered_data[y_axis_label].append(data[(step, index)][job_name])
|
|
707
|
-
show_graph(pandas.DataFrame(filtered_data).dropna(), x_axis_label, y_axis_label, color_label)
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
def flowgraph_layout_vertical_flowgraph(chip, ui_width):
|
|
711
|
-
'''
|
|
712
|
-
This function dynamically calculates what the flowgraph's width should be.
|
|
713
|
-
This function is specific to the vertical_flowgraph layout. It returns the
|
|
714
|
-
node selected from the flowgraph and the column for the metric section and
|
|
715
|
-
node information section.
|
|
716
|
-
|
|
717
|
-
Args:
|
|
718
|
-
chip (Chip) : The chip object that contains the schema read from.
|
|
719
|
-
ui_width (int) : The width of the browser in terms of pixels.
|
|
720
|
-
'''
|
|
721
|
-
if streamlit.session_state['flowgraph']:
|
|
722
|
-
default_flowgraph_width_in_percent = 0.4
|
|
723
|
-
flowgraph_col_width_in_pixels = 520
|
|
724
|
-
if ui_width > 0:
|
|
725
|
-
flowgraph_col_width_in_percent = \
|
|
726
|
-
min(flowgraph_col_width_in_pixels / ui_width, default_flowgraph_width_in_percent)
|
|
727
|
-
else:
|
|
728
|
-
flowgraph_col_width_in_percent = default_flowgraph_width_in_percent
|
|
729
|
-
flowgraph_col, dataframe_and_node_info_col = \
|
|
730
|
-
streamlit.columns([flowgraph_col_width_in_percent,
|
|
731
|
-
1 - flowgraph_col_width_in_percent], gap="large")
|
|
732
|
-
with flowgraph_col:
|
|
733
|
-
header_col, toggle_col = streamlit.columns(2, gap="large")
|
|
734
|
-
with header_col:
|
|
735
|
-
streamlit.header('Flowgraph')
|
|
736
|
-
with toggle_col:
|
|
737
|
-
display_flowgraph_toggle(True)
|
|
738
|
-
node_from_flowgraph = show_flowgraph(chip)
|
|
739
|
-
else:
|
|
740
|
-
display_flowgraph_toggle(False)
|
|
741
|
-
dataframe_and_node_info_col = streamlit.container()
|
|
742
|
-
node_from_flowgraph = streamlit.session_state['node_from_flowgraph']
|
|
743
|
-
return node_from_flowgraph, dataframe_and_node_info_col
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
def node_metric_dataframe(node_name, metrics, height=None):
|
|
747
|
-
'''
|
|
748
|
-
Displays the node metric dataframe.
|
|
749
|
-
|
|
750
|
-
Args:
|
|
751
|
-
node_name (string) : The name of the node selected.
|
|
752
|
-
metrics (pandas.DataFrame) : The metrics for the node corresponding with
|
|
753
|
-
node_name with the Null values removed.
|
|
754
|
-
height (int or None) : The height of the dataframe. If None, streamlit
|
|
755
|
-
automatically calculates it.
|
|
756
|
-
'''
|
|
757
|
-
streamlit.subheader(f'{node_name} Metrics')
|
|
758
|
-
streamlit.dataframe(metrics, use_container_width=True, height=height)
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
def node_details_dataframe(chip, step, index, height=None):
|
|
762
|
-
'''
|
|
763
|
-
Displays the node details dataframe.
|
|
764
|
-
|
|
765
|
-
Args:
|
|
766
|
-
chip (Chip) : the chip object that contains the schema read from.
|
|
767
|
-
step (string) : step of node.
|
|
768
|
-
index (string) : index of node.
|
|
769
|
-
height (int or None) : The height of the dataframe. If None, streamlit
|
|
770
|
-
automatically calculates it.
|
|
771
|
-
'''
|
|
772
|
-
streamlit.subheader(f'{step}{index} Details')
|
|
773
|
-
nodes = {}
|
|
774
|
-
nodes[step + index] = report.get_flowgraph_nodes(chip, step, index)
|
|
775
|
-
node_reports = pandas.DataFrame.from_dict(nodes)
|
|
776
|
-
streamlit.dataframe(node_reports, use_container_width=True, height=height)
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
def design_preview_module(chip):
|
|
780
|
-
'''
|
|
781
|
-
Displays the design preview.
|
|
782
|
-
|
|
783
|
-
Args:
|
|
784
|
-
chip (Chip) : the chip object that contains the schema read from.
|
|
785
|
-
'''
|
|
786
|
-
streamlit.header('Design Preview')
|
|
787
|
-
streamlit.image(f'{chip.getworkdir()}/{chip.design}.png')
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
def make_node_to_step_index_map(metric_dataframe):
|
|
791
|
-
'''
|
|
792
|
-
Returns a map from the name of a node to the associated step, index pair.
|
|
793
|
-
|
|
794
|
-
Args:
|
|
795
|
-
metric_dataframe (pandas.DataFrame) : A dataframe full of all metrics and all
|
|
796
|
-
nodes of the selected chip
|
|
797
|
-
'''
|
|
798
|
-
node_to_step_index_map = {}
|
|
799
|
-
for step, index in metric_dataframe.columns.tolist():
|
|
800
|
-
node_to_step_index_map[step + index] = (step, index)
|
|
801
|
-
# concatenate step and index
|
|
802
|
-
metric_dataframe.columns = metric_dataframe.columns.map(lambda x: f'{x[0]}{x[1]}')
|
|
803
|
-
return node_to_step_index_map, metric_dataframe
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
def make_metric_to_metric_unit_map(metric_dataframe):
|
|
807
|
-
'''
|
|
808
|
-
Returns a map from the name of a metric to the associated metric and unit in
|
|
809
|
-
the form f'{x[0]} ({x[1]})'
|
|
810
|
-
|
|
811
|
-
Args:
|
|
812
|
-
metric_dataframe (pandas.DataFrame) : A dataframe full of all metrics and all
|
|
813
|
-
nodes of the selected chip.
|
|
814
|
-
'''
|
|
815
|
-
metric_to_metric_unit_map = {}
|
|
816
|
-
for metric, unit in metric_dataframe.index.tolist():
|
|
817
|
-
if unit != '':
|
|
818
|
-
metric_to_metric_unit_map[f'{metric} ({unit})'] = metric
|
|
819
|
-
else:
|
|
820
|
-
metric_to_metric_unit_map[metric] = metric
|
|
821
|
-
# concatenate metric and unit
|
|
822
|
-
metric_dataframe.index = metric_dataframe.index.map(lambda x: f'{x[0]} ({x[1]})'
|
|
823
|
-
if x[1] else x[0])
|
|
824
|
-
return metric_to_metric_unit_map, metric_dataframe
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
def graphs_module(metric_dataframe, node_to_step_index_map, metric_to_metric_unit_map):
|
|
828
|
-
'''
|
|
829
|
-
This displays the graph module.
|
|
830
|
-
|
|
831
|
-
Args:
|
|
832
|
-
metric_dataframe (pandas.DataFrame) : A dataframe full of all metrics and all
|
|
833
|
-
nodes of the selected chip.
|
|
834
|
-
node_to_step_index_map (dict) : Maps the node to the associated step, index pair.
|
|
835
|
-
metric_to_metric_unit_map (dict) : Maps the metric to the associated metric unit.
|
|
836
|
-
'''
|
|
837
|
-
metrics = metric_dataframe.index.map(lambda x: metric_to_metric_unit_map[x])
|
|
838
|
-
nodes = metric_dataframe.columns
|
|
839
|
-
chips = []
|
|
840
|
-
jobs = []
|
|
841
|
-
for job in streamlit.session_state['cfg'].getkeys('history'):
|
|
842
|
-
selected_chip = Chip(design='')
|
|
843
|
-
selected_chip.schema = chip.schema.history(job)
|
|
844
|
-
selected_chip.set('design', chip.design)
|
|
845
|
-
chips.append({'chip_object': selected_chip, 'chip_name': job})
|
|
846
|
-
jobs.append(job)
|
|
847
|
-
job_selector_col, graph_adder_col = streamlit.columns(2, gap='large')
|
|
848
|
-
with job_selector_col:
|
|
849
|
-
with streamlit.expander('Select Jobs'):
|
|
850
|
-
selected_jobs = select_runs(jobs)
|
|
851
|
-
with graph_adder_col:
|
|
852
|
-
graphs = streamlit.slider('pick the number of graphs you want', 1, 10,
|
|
853
|
-
1, label_visibility='collapsed')
|
|
854
|
-
graph_number = 1
|
|
855
|
-
left_graph_col, right_graph_col = streamlit.columns(2, gap='large')
|
|
856
|
-
while graph_number <= graphs:
|
|
857
|
-
if graph_number % 2 == 1:
|
|
858
|
-
graph_col = left_graph_col
|
|
859
|
-
else:
|
|
860
|
-
graph_col = right_graph_col
|
|
861
|
-
with graph_col:
|
|
862
|
-
metric, selected_nodes = \
|
|
863
|
-
show_metric_and_node_selection_for_graph(metrics, nodes, graph_number)
|
|
864
|
-
nodes_as_step_and_index = []
|
|
865
|
-
for selected_node in selected_nodes:
|
|
866
|
-
step, index = node_to_step_index_map[selected_node]
|
|
867
|
-
nodes_as_step_and_index.append((step, index))
|
|
868
|
-
structure_graph_data(chips, metric, selected_jobs, nodes_as_step_and_index)
|
|
869
|
-
if not (graph_number == graphs or graph_number == graphs - 1):
|
|
870
|
-
streamlit.divider()
|
|
871
|
-
graph_number += 1
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
def make_tabs(metric_dataframe, chip, node_to_step_index_map):
|
|
875
|
-
'''
|
|
876
|
-
Creates all the tabs. Displays the modules for the tabs that may or may not exist
|
|
877
|
-
which include the graphs tab and design preview tab. Returns the rest of the tabs.
|
|
878
|
-
|
|
879
|
-
Args:
|
|
880
|
-
metric_dataframe (pandas.DataFrame) : A dataframe full of all metrics and all
|
|
881
|
-
nodes of the selected chip
|
|
882
|
-
chip (Chip) : The chip object that contains the schema read from.
|
|
883
|
-
node_to_step_index_map (dict) : Maps the node to the associated step, index pair
|
|
884
|
-
'''
|
|
885
|
-
if 'flowgraph' not in streamlit.session_state:
|
|
886
|
-
streamlit.session_state['flowgraph'] = True
|
|
887
|
-
tabs = ["Metrics", "Manifest", "File Viewer"]
|
|
888
|
-
num_of_chips = len(streamlit.session_state['cfg'].getkeys('history'))
|
|
889
|
-
if os.path.isfile(f'{chip.getworkdir()}/{chip.design}.png') & num_of_chips > 1:
|
|
890
|
-
metrics_tab, manifest_tab, file_viewer_tab, design_preview_tab, graphs_tab = \
|
|
891
|
-
streamlit.tabs(tabs + ["Graphs", "Design Preview"])
|
|
892
|
-
with graphs_tab:
|
|
893
|
-
graphs_module(metric_dataframe, node_to_step_index_map, metric_to_metric_unit_map)
|
|
894
|
-
with design_preview_tab:
|
|
895
|
-
design_preview_module(chip)
|
|
896
|
-
elif os.path.isfile(f'{chip.getworkdir()}/{chip.design}.png'):
|
|
897
|
-
metrics_tab, manifest_tab, file_viewer_tab, design_preview_tab = \
|
|
898
|
-
streamlit.tabs(tabs + ["Design Preview"])
|
|
899
|
-
with design_preview_tab:
|
|
900
|
-
design_preview_module(chip)
|
|
901
|
-
elif num_of_chips > 1:
|
|
902
|
-
metrics_tab, manifest_tab, file_viewer_tab, graphs_tab = streamlit.tabs(tabs + ["Graphs"])
|
|
903
|
-
with graphs_tab:
|
|
904
|
-
graphs_module(metric_dataframe, node_to_step_index_map, metric_to_metric_unit_map)
|
|
905
|
-
else:
|
|
906
|
-
metrics_tab, manifest_tab, file_viewer_tab = streamlit.tabs(tabs)
|
|
907
|
-
return metrics_tab, manifest_tab, file_viewer_tab
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
if 'node_from_flowgraph' not in streamlit.session_state:
|
|
911
|
-
streamlit.session_state['node_from_flowgraph'] = None
|
|
912
|
-
# TODO find more descriptive way to describe layouts
|
|
913
|
-
layout = 'vertical_flowgraph'
|
|
914
|
-
# gathering data
|
|
915
|
-
metric_dataframe = report.make_metric_dataframe(selected_chip)
|
|
916
|
-
node_to_step_index_map, metric_dataframe = make_node_to_step_index_map(metric_dataframe)
|
|
917
|
-
metric_to_metric_unit_map, metric_dataframe = make_metric_to_metric_unit_map(metric_dataframe)
|
|
918
|
-
manifest = report.make_manifest(selected_chip)
|
|
919
|
-
if layout == 'vertical_flowgraph':
|
|
920
|
-
show_title_and_runs()
|
|
921
|
-
metrics_tab, manifest_tab, file_viewer_tab = make_tabs(metric_dataframe, selected_chip,
|
|
922
|
-
node_to_step_index_map)
|
|
923
|
-
ui_width = streamlit_javascript.st_javascript("window.innerWidth")
|
|
924
|
-
with metrics_tab:
|
|
925
|
-
node_from_flowgraph, datafram_and_node_info_col = \
|
|
926
|
-
flowgraph_layout_vertical_flowgraph(selected_chip, ui_width)
|
|
927
|
-
streamlit.session_state['node_from_flowgraph'] = node_from_flowgraph
|
|
928
|
-
with datafram_and_node_info_col:
|
|
929
|
-
metrics_dataframe_module(metric_dataframe, metric_to_metric_unit_map)
|
|
930
|
-
streamlit.header('Node Information')
|
|
931
|
-
metrics_col, records_col, logs_and_reports_col = streamlit.columns(3, gap='small')
|
|
932
|
-
selected_node = select_nodes(metric_dataframe, node_from_flowgraph)
|
|
933
|
-
step, index = node_to_step_index_map[selected_node]
|
|
934
|
-
with metrics_col:
|
|
935
|
-
node_metric_dataframe(selected_node, metric_dataframe[selected_node].dropna())
|
|
936
|
-
with records_col:
|
|
937
|
-
node_details_dataframe(selected_chip, step, index)
|
|
938
|
-
with logs_and_reports_col:
|
|
939
|
-
display_file_content = show_files(selected_chip, step, index)
|
|
940
|
-
show_metrics_for_file(selected_chip, step, index)
|
|
941
|
-
with manifest_tab:
|
|
942
|
-
manifest_module(selected_chip, manifest, ui_width)
|
|
943
|
-
with file_viewer_tab:
|
|
944
|
-
file_viewer_module(display_file_content, selected_chip, step, index)
|