Skip to content

Commit

Permalink
QueryProfilerComparison - UI update (#1288)
Browse files Browse the repository at this point in the history
* Added functionality to synchronize side-by-side view

* added hour glass for waiting

* Added session parameter display
  • Loading branch information
mail4umar authored Sep 26, 2024
1 parent eb32db6 commit 1a503f0
Showing 1 changed file with 111 additions and 10 deletions.
121 changes: 111 additions & 10 deletions verticapy/performance/vertica/qprof_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ def __init__(
]
)
self.update_query_display()

self.session_param_display = []
self.update_session_param_display()
self.index_widget = widgets.IntText(
description="Index:", value=self.transactions_idx
)
Expand Down Expand Up @@ -270,6 +271,7 @@ def get_qplan_tree(self, use_javascript=True, hide_settings=False, **style_kwarg
),
"Tree style": widgets.VBox(tree_settings),
"Query text": self.query_display,
"Session Parameters": self.session_param_display,
}
query_text_index = list(accordion_items.keys()).index("Query text")
self.accordions = Visualizer._accordion(
Expand Down Expand Up @@ -328,15 +330,37 @@ def update_qplan_tree(
"""
Callback function that displays the Query Plan Tree.
"""
# Create an output widget to hold the hourglass and the tree
output = widgets.Output()
display(output)

# Show hourglass in the output before starting long-running task
with output:
output.clear_output(wait=True) # Clear any previous content
# Create the hourglass icon
hourglass_icon = widgets.HTML(
value='<i class="fa fa-hourglass-half" style="font-size:48px;color:gray;"></i>',
layout=widgets.Layout(display="flex", justify_content="center"),
)
vbox = widgets.VBox(
[hourglass_icon],
layout=widgets.Layout(justify_content="center", align_items="center"),
)
display(vbox)

# Processing the inputs and generate the tree (long running task)
metric = [
QprofUtility._get_metrics_name(i, inv=True) for i in [metric1, metric2]
]
if len(metric) == 0:
metric = ["rows"]

graph_id = "g" + str(uuid.uuid4())
self.query_select_button_selected(index)
if self.pathid_dropdown.get_child_attr("disabled"):
path_id = None

# Ensure the hourglass stays displayed during the processing of get_qplan_tree
if self.use_javascript == False:
graph = super().get_qplan_tree(
metric=metric,
Expand All @@ -353,10 +377,18 @@ def update_qplan_tree(
display_tooltip_descriptors=display_tooltip_descriptors,
**self.style_kwargs,
) # type: ignore
html_widget = widgets.HTML(value=graph.pipe(format="svg").decode("utf-8"))
box = widgets.HBox([html_widget])
box.layout.justify_content = "center"
display(box)

# After long-running task is done, update output with the result
with output:
output.clear_output(
wait=True
) # Clear the hourglass before displaying the tree
html_widget = widgets.HTML(
value=graph.pipe(format="svg").decode("utf-8")
)
box = widgets.HBox([html_widget])
box.layout.justify_content = "center"
display(box)
else:
raw = super().get_qplan_tree(
metric=metric,
Expand All @@ -374,11 +406,19 @@ def update_qplan_tree(
display_tooltip_descriptors=display_tooltip_descriptors,
**self.style_kwargs,
)
output = read_package_file("html/index.html")
output = replace_value(output, "var dotSrc = [];", f"var dotSrc = `{raw}`;")
output = replace_value(output, 'id="graph"', f'id="{graph_id}"')
output = replace_value(output, "#graph", f"#{graph_id}")
display(HTML(output))

output_html = read_package_file("html/index.html")
output_html = replace_value(
output_html, "var dotSrc = [];", f"var dotSrc = `{raw}`;"
)
output_html = replace_value(output_html, 'id="graph"', f'id="{graph_id}"')
output_html = replace_value(output_html, "#graph", f"#{graph_id}")

with output:
output.clear_output(wait=True) # Clear the hourglass
display(HTML(output_html))

# Update the header after the tree is displayed
self.qpt_header.value = (
f"<h1><b>Query Plan Tree - [query_idx: {index}]</b></h1>"
)
Expand Down Expand Up @@ -452,6 +492,7 @@ def query_select_button_selected(self, selection):
self.step_idx.value = selection
self.set_position(selection)
self.update_query_display()
self.update_session_param_display()

def refresh_clicked(self, button):
"""
Expand Down Expand Up @@ -489,6 +530,39 @@ def update_query_display(self):
<b>Key ID:</b> {self.key_id}
"""

def update_session_param_display(self):
"""
Updates the Session parameter display text widget with the current query.
"""
rows = []
dict_list = self.session_params_current
if isinstance(dict_list, dict):
dict_list = [dict_list]
for dictionary in dict_list:
for key, value in dictionary.items():
# Create a key-value pair layout
key_label = widgets.HTML(
value=f"<b>{key}:</b>",
# layout=widgets.Layout(width='200px', text_align='right')
)
value_label = widgets.HTML(
value=f"{value}",
# layout=widgets.Layout(width='200px', text_align='left')
)
# Arrange key and value side by side in a horizontal box
row = widgets.HBox(
[key_label, value_label], layout=widgets.Layout(padding="5px")
)
rows.append(row)

# Add a centered title to the widget display
title = widgets.HTML(
value="<h4 style='background-color: #f0f0f0; padding: 5px; border-radius: 5px; margin: 0; text-align: center;'>Non-default Parameters</h4>"
)

# Create a VBox for the entire display (title + key-value pairs)
self.session_param_display = widgets.VBox([title] + rows)

##########################################################################

def get_qsteps_(self):
Expand Down Expand Up @@ -760,12 +834,23 @@ def __init__(self, qprof1, qprof2):

self.query_info = self._create_query_info()

self.dual_effect = True

# Initial update of the trees
nooutput = widgets.Output()
with nooutput:
self.qprof1.get_qplan_tree()
self.qprof2.get_qplan_tree()

if self.dual_effect:
# Replace the children tuple of qprof2 with a new one that copies qprof1's first accordion child
self.qprof2.accordions.children = (
self.qprof1.accordions.children[0],
) + self.qprof2.accordions.children[1:]

# Sync the accordion selection between qprof1 and qprof2
self._sync_accordion_selection()

self.controls = self._create_controls()
self.side_by_side_ui = widgets.VBox([self.query_info, self.controls])

Expand Down Expand Up @@ -816,6 +901,22 @@ def create_interactive_controls(qprof):
]
)

def _sync_accordion_selection(self):
"""
Synchronizes the accordion selection of qprof1 and qprof2.
When an accordion is selected in qprof1, it automatically updates the selection in qprof2.
"""

def on_accordion_change(change):
"""
Callback function to update qprof2's accordion selection when qprof1's accordion selection changes.
"""
if change["name"] == "selected_index" and change["new"] is not None:
self.qprof2.accordions.selected_index = change["new"]

# Observe changes in the selected_index of qprof1's accordion
self.qprof1.accordions.observe(on_accordion_change, names="selected_index")

def _create_query_info(self):
# Get and set the layout for the query display info for both qprof1 and qprof2
q1_info = self.qprof1.query_display_info
Expand Down

0 comments on commit 1a503f0

Please sign in to comment.