Skip to content

Commit 23664c7

Browse files
authored
tui user experience improvement (#805)
1 parent 000fd4f commit 23664c7

File tree

6 files changed

+99
-19
lines changed

6 files changed

+99
-19
lines changed

src/rocprof_compute_tui/utils/analyze_config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ sections:
4040
- "1. System Info"
4141
- "2. System Speed-of-Light"
4242
- "3. Memory Chart"
43+
- "4. Roofline"
4344

4445
- title: "🚧 Source Level Analysis"
4546
collapsed: true

src/rocprof_compute_tui/utils/tui_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ def process_panels_to_dataframes(
418418
# Save to CSV if requested
419419
if args.df_file_dir:
420420
save_dataframe_to_csv(df, table_id_str, table_config, args)
421-
result_structure["roofline"] = roof_plot
421+
result_structure["4. Roofline"] = roof_plot
422422
return dict(result_structure)
423423

424424

src/rocprof_compute_tui/views/main_view.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ def on_data_table_cell_selected(self, event: DataTable.CellSelected) -> None:
122122

123123
@work(thread=True)
124124
def run_analysis(self) -> None:
125+
self.dfs = {}
126+
125127
if not self.selected_path:
126128
error_msg = "No directory selected for analysis"
127129
self._update_view(error_msg, LogLevel.ERROR)
@@ -237,7 +239,7 @@ def run_analysis(self) -> None:
237239
else:
238240
self.app.call_from_thread(self.refresh_results)
239241
self.logger.info("Step 8: Analysis completed successfully")
240-
if self.dfs["roofline"]:
242+
if self.dfs.get("4. Roofline"):
241243
self.logger.info("Step 8: Roofline data available")
242244
else:
243245
self.logger.info("Step 8: Roofline data not available")

src/rocprof_compute_tui/widgets/charts.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ def __init__(self, df: pd.DataFrame, **kwargs):
290290
try:
291291
plot_str = ""
292292
try:
293-
result = self.df["roofline"]
293+
result = self.df["4. Roofline"]
294294
if result:
295295
plot_str = str(result)
296296
except:

src/rocprof_compute_tui/widgets/collapsibles.py

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def build_subsection(
147147

148148
collapsible = Collapsible(*widgets, title=title, collapsed=collapsed)
149149
elif tui_style == "roofline":
150-
if dfs["roofline"]:
150+
if dfs["4. Roofline"]:
151151
widget = RooflinePlot(dfs)
152152
collapsible = Collapsible(widget, title=title, collapsed=collapsed)
153153
else:
@@ -172,28 +172,89 @@ def build_dynamic_kernel_sections(
172172
) -> List[Collapsible]:
173173
children = []
174174

175+
def add_warning(message: str):
176+
children.append(Label(message, classes="warning"))
177+
178+
def validate_data_structure(data, name: str, parent_name: str = None) -> bool:
179+
if data is None:
180+
location = f"'{parent_name}' > '{name}'" if parent_name else f"'{name}'"
181+
add_warning(f"Analysis result for {location} is not available")
182+
return False
183+
184+
if not isinstance(data, dict):
185+
location = f"'{parent_name}' > '{name}'" if parent_name else f"'{name}'"
186+
add_warning(
187+
f"Analysis result for {location} is not a dictionary type: {type(data)}"
188+
)
189+
return False
190+
191+
return True
192+
193+
def create_safe_widget(subsection_name: str, data: dict, section_name: str):
194+
if not (isinstance(data, dict) and "df" in data):
195+
add_warning(
196+
f"Invalid data structure for '{subsection_name}' in section '{section_name}'"
197+
)
198+
return None
199+
200+
try:
201+
df = data["df"]
202+
tui_style = data.get("tui_style")
203+
widget = create_widget_from_data(df, tui_style)
204+
205+
if widget is None:
206+
add_warning(f"Widget creation returned None for '{subsection_name}'")
207+
return None
208+
209+
return widget
210+
except Exception as e:
211+
add_warning(f"Failed to create widget for '{subsection_name}': {str(e)}")
212+
return None
213+
214+
def create_safe_collapsible(widget, title):
215+
try:
216+
return Collapsible(widget, title=title, collapsed=True)
217+
except Exception as e:
218+
add_warning(f"Failed to create collapsible for '{title}': {str(e)}")
219+
return None
220+
175221
try:
222+
if not validate_data_structure(dfs, "analysis result"):
223+
return children
224+
176225
for section_name, subsections in dfs.items():
177226
if section_name in skip_sections:
178227
continue
179228

229+
if not validate_data_structure(subsections, section_name):
230+
continue
231+
180232
kernel_children = []
181233
for subsection_name, data in subsections.items():
182-
if isinstance(data, dict) and "df" in data:
183-
df = data["df"]
184-
tui_style = data.get("tui_style")
185-
widget = create_widget_from_data(df, tui_style)
186-
kernel_children.append(
187-
Collapsible(widget, title=subsection_name, collapsed=True)
234+
try:
235+
widget = create_safe_widget(subsection_name, data, section_name)
236+
if widget:
237+
collapsible = create_safe_collapsible(widget, subsection_name)
238+
if collapsible:
239+
kernel_children.append(collapsible)
240+
except Exception as e:
241+
add_warning(
242+
f"Error processing subsection '{subsection_name}' in section '{section_name}': {str(e)}"
188243
)
189244

190245
if kernel_children:
191-
children.append(
192-
Collapsible(*kernel_children, title=section_name, collapsed=True)
193-
)
246+
try:
247+
section_collapsible = Collapsible(
248+
*kernel_children, title=section_name, collapsed=True
249+
)
250+
children.append(section_collapsible)
251+
except Exception as e:
252+
add_warning(
253+
f"Failed to create collapsible for section '{section_name}': {str(e)}"
254+
)
194255

195256
except Exception as e:
196-
children.append(Label(f"Error in Kernel Section: {str(e)}", classes="error"))
257+
add_warning(f"Unexpected error in Kernel Section processing: {str(e)}")
197258

198259
return children
199260

tests/test_utils.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
import subprocess
4242
import tempfile
4343
from pathlib import Path
44-
from unittest import mock
4544
from types import SimpleNamespace
45+
from unittest import mock
4646

4747
import pandas as pd
4848
import pytest
@@ -4997,7 +4997,11 @@ class MockMspec:
49974997
override_binary_path.chmod(0o755)
49984998

49994999
def mock_detect_roofline(mspec):
5000-
return {"distro": "override", "path": str(override_binary_path), "rocm_ver": "0.x.x"}
5000+
return {
5001+
"distro": "override",
5002+
"path": str(override_binary_path),
5003+
"rocm_ver": "0.x.x",
5004+
}
50015005

50025006
subprocess_calls = []
50035007

@@ -5451,7 +5455,11 @@ class MockMspec:
54515455
override_binary_path.chmod(0o755)
54525456

54535457
def mock_detect_roofline(mspec):
5454-
return {"distro": "override", "path": str(override_binary_path), "rocm_ver": "0.x.x"}
5458+
return {
5459+
"distro": "override",
5460+
"path": str(override_binary_path),
5461+
"rocm_ver": "0.x.x",
5462+
}
54555463

54565464
def mock_subprocess_run(args, check=True):
54575465
raise subprocess.CalledProcessError(1, args)
@@ -5491,7 +5499,11 @@ class MockMspec:
54915499
override_binary_path.chmod(0o755)
54925500

54935501
def mock_detect_roofline(mspec):
5494-
return {"distro": "override", "path": str(override_binary_path), "rocm_ver": "0.x.x"}
5502+
return {
5503+
"distro": "override",
5504+
"path": str(override_binary_path),
5505+
"rocm_ver": "0.x.x",
5506+
}
54955507

54965508
subprocess_calls = []
54975509

@@ -5600,7 +5612,11 @@ class MockMspec:
56005612
override_binary_path.chmod(0o755)
56015613

56025614
def mock_detect_roofline(mspec):
5603-
return {"distro": "override", "path": str(override_binary_path), "rocm_ver": "0.x.x"}
5615+
return {
5616+
"distro": "override",
5617+
"path": str(override_binary_path),
5618+
"rocm_ver": "0.x.x",
5619+
}
56045620

56055621
console_log_calls = []
56065622

0 commit comments

Comments
 (0)