X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=csit.infra.dash%2Fapp%2Fcdash%2Futils%2Futils.py;h=d9347b1c131e7e24f85f3070c7831404e87bc346;hb=5edaac881f42de498d92efca3fbbfab8e2e47e71;hp=58bdb05cdbad670c5b1e6b8bcb6cacf156d731da;hpb=a5836196e06db97aa369efdd3b160104eb6ae1f8;p=csit.git diff --git a/csit.infra.dash/app/cdash/utils/utils.py b/csit.infra.dash/app/cdash/utils/utils.py index 58bdb05cdb..d9347b1c13 100644 --- a/csit.infra.dash/app/cdash/utils/utils.py +++ b/csit.infra.dash/app/cdash/utils/utils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2022 Cisco and/or its affiliates. +# Copyright (c) 2023 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -15,8 +15,13 @@ """ import pandas as pd +import plotly.graph_objects as go import dash_bootstrap_components as dbc +import hdrh.histogram +import hdrh.codec + +from math import sqrt from numpy import isnan from dash import dcc from datetime import datetime @@ -346,29 +351,172 @@ def set_job_params(df: pd.DataFrame, job: str) -> dict: } -def get_list_group_items(tests: list) -> list: - """Generate list of ListGroupItems with checkboxes with selected tests. - - :param tests: List of tests to be displayed in the ListGroup. - :type tests: list - :returns: List of ListGroupItems with checkboxes with selected tests. +def get_list_group_items( + items: list, + type: str, + colorize: bool=True, + add_index: bool=False + ) -> list: + """Generate list of ListGroupItems with checkboxes with selected items. + + :param items: List of items to be displayed in the ListGroup. + :param type: The type part of an element ID. + :param colorize: If True, the color of labels is set, otherwise the default + color is used. + :param add_index: Add index to the list items. + :type items: list + :type type: str + :type colorize: bool + :type add_index: bool + :returns: List of ListGroupItems with checkboxes with selected items. :rtype: list """ - return [ - dbc.ListGroupItem( - children=[ - dbc.Checkbox( - id={"type": "sel-cl", "index": i}, - label=l["id"], - value=False, - label_class_name="m-0 p-0", - label_style={ - "font-size": ".875em", - "color": get_color(i) - }, - class_name="info" - ) - ], - class_name="p-0" - ) for i, l in enumerate(tests) - ] + + children = list() + for i, l in enumerate(items): + idx = f"{i + 1}. " if add_index else str() + label = f"{idx}{l['id']}" if isinstance(l, dict) else f"{idx}{l}" + children.append( + dbc.ListGroupItem( + children=[ + dbc.Checkbox( + id={"type": type, "index": i}, + label=label, + value=False, + label_class_name="m-0 p-0", + label_style={ + "font-size": ".875em", + "color": get_color(i) if colorize else "#55595c" + }, + class_name="info" + ) + ], + class_name="p-0" + ) + ) + + return children + + +def relative_change_stdev(mean1, mean2, std1, std2): + """Compute relative standard deviation of change of two values. + + The "1" values are the base for comparison. + Results are returned as percentage (and percentual points for stdev). + Linearized theory is used, so results are wrong for relatively large stdev. + + :param mean1: Mean of the first number. + :param mean2: Mean of the second number. + :param std1: Standard deviation estimate of the first number. + :param std2: Standard deviation estimate of the second number. + :type mean1: float + :type mean2: float + :type std1: float + :type std2: float + :returns: Relative change and its stdev. + :rtype: float + """ + mean1, mean2 = float(mean1), float(mean2) + quotient = mean2 / mean1 + first = std1 / mean1 + second = std2 / mean2 + std = quotient * sqrt(first * first + second * second) + return (quotient - 1) * 100, std * 100 + + +def get_hdrh_latencies(row: pd.Series, name: str) -> dict: + """Get the HDRH latencies from the test data. + + :param row: A row fron the data frame with test data. + :param name: The test name to be displayed as the graph title. + :type row: pandas.Series + :type name: str + :returns: Dictionary with HDRH latencies. + :rtype: dict + """ + + latencies = {"name": name} + for key in C.LAT_HDRH: + try: + latencies[key] = row[key] + except KeyError: + return None + + return latencies + + +def graph_hdrh_latency(data: dict, layout: dict) -> go.Figure: + """Generate HDR Latency histogram graphs. + + :param data: HDRH data. + :param layout: Layout of plot.ly graph. + :type data: dict + :type layout: dict + :returns: HDR latency Histogram. + :rtype: plotly.graph_objects.Figure + """ + + fig = None + + traces = list() + for idx, (lat_name, lat_hdrh) in enumerate(data.items()): + try: + decoded = hdrh.histogram.HdrHistogram.decode(lat_hdrh) + except (hdrh.codec.HdrLengthException, TypeError): + continue + previous_x = 0.0 + prev_perc = 0.0 + xaxis = list() + yaxis = list() + hovertext = list() + for item in decoded.get_recorded_iterator(): + # The real value is "percentile". + # For 100%, we cut that down to "x_perc" to avoid + # infinity. + percentile = item.percentile_level_iterated_to + x_perc = min(percentile, C.PERCENTILE_MAX) + xaxis.append(previous_x) + yaxis.append(item.value_iterated_to) + hovertext.append( + f"{C.GRAPH_LAT_HDRH_DESC[lat_name]}
" + f"Direction: {('W-E', 'E-W')[idx % 2]}
" + f"Percentile: {prev_perc:.5f}-{percentile:.5f}%
" + f"Latency: {item.value_iterated_to}uSec" + ) + next_x = 100.0 / (100.0 - x_perc) + xaxis.append(next_x) + yaxis.append(item.value_iterated_to) + hovertext.append( + f"{C.GRAPH_LAT_HDRH_DESC[lat_name]}
" + f"Direction: {('W-E', 'E-W')[idx % 2]}
" + f"Percentile: {prev_perc:.5f}-{percentile:.5f}%
" + f"Latency: {item.value_iterated_to}uSec" + ) + previous_x = next_x + prev_perc = percentile + + traces.append( + go.Scatter( + x=xaxis, + y=yaxis, + name=C.GRAPH_LAT_HDRH_DESC[lat_name], + mode="lines", + legendgroup=C.GRAPH_LAT_HDRH_DESC[lat_name], + showlegend=bool(idx % 2), + line=dict( + color=get_color(int(idx/2)), + dash="solid", + width=1 if idx % 2 else 2 + ), + hovertext=hovertext, + hoverinfo="text" + ) + ) + if traces: + fig = go.Figure() + fig.add_traces(traces) + layout_hdrh = layout.get("plot-hdrh-latency", None) + if lat_hdrh: + fig.update_layout(layout_hdrh) + + return fig