Files
wlan-lanforge-scripts/py-scripts/lf_graph.py
2024-04-23 03:56:35 +05:30

1246 lines
48 KiB
Python
Executable File

#!/usr/bin/env python3
"""
NAME: lf_graph.py
PURPOSE:
Common Library for generating graphs for LANforge output
SETUP:
/lanforge/html-reports directory needs to be present or output generated in local file
EXAMPLE:
see: /py-scritps/lf_report_test.py for example
COPYRIGHT:
Copyright 2021 Candela Technologies Inc
License: Free to distribute and modify. LANforge systems must be licensed.
INCLUDE_IN_README
"""
import sys
import os
import importlib
import matplotlib.pyplot as plt
import numpy as np
import pdfkit
from matplotlib.colors import ListedColormap
import matplotlib.ticker as mticker
import argparse
import traceback
import logging
# TODO have scipy be part of the base install
try:
from scipy import interpolate
except Exception as x:
print("Info: scipy package not installed, Needed for smoothing linear plots 'pip install scipy' ")
traceback.print_exception(Exception, x, x.__traceback__, chain=True)
sys.path.append(os.path.join(os.path.abspath(__file__ + "../../../")))
logger = logging.getLogger(__name__)
lf_logger_config = importlib.import_module("py-scripts.lf_logger_config")
lf_csv = importlib.import_module("py-scripts.lf_csv")
lf_csv = lf_csv.lf_csv
# internal candela references included during intial phases, to be deleted
# at future date
# graph reporting classes
class lf_bar_graph:
def __init__(self, _data_set=None,
_xaxis_name="x-axis",
_yaxis_name="y-axis",
_xaxis_categories=None,
_xaxis_label=None,
_graph_title="",
_title_size=16,
_graph_image_name="image_name",
_label=None,
_color=None,
_bar_width=0.25,
_color_edge='grey',
_font_weight='bold',
_color_name=None,
_figsize=(10, 5),
_show_bar_value=False,
_xaxis_step=1,
_xticks_font=None,
_xaxis_value_location=0,
_xticks_rotation=None,
_text_font=None,
_text_rotation=None,
_grp_title="",
_legend_handles=None,
_legend_loc="best",
_legend_box=None,
_legend_ncol=1,
_legend_fontsize=None,
_dpi=96,
_enable_csv=False,
_remove_border=None,
_alignment=None
):
if _data_set is None:
_data_set = [[30.4, 55.3, 69.2, 37.1], [45.1, 67.2, 34.3, 22.4], [22.5, 45.6, 12.7, 34.8]]
if _xaxis_categories is None:
_xaxis_categories = [1, 2, 3, 4]
if _xaxis_label is None:
_xaxis_label = ["a", "b", "c", "d"]
if _label is None:
_label = ["bi-downlink", "bi-uplink", 'uplink']
if _color_name is None:
_color_name = ['lightcoral', 'darkgrey', 'r', 'g', 'b', 'y']
self.data_set = _data_set
self.xaxis_name = _xaxis_name
self.yaxis_name = _yaxis_name
self.xaxis_categories = _xaxis_categories
self.xaxis_label = _xaxis_label
self.title = _graph_title
self.title_size = _title_size
self.graph_image_name = _graph_image_name
self.label = _label
self.color = _color
self.bar_width = _bar_width
self.color_edge = _color_edge
self.font_weight = _font_weight
self.color_name = _color_name
self.figsize = _figsize
self.show_bar_value = _show_bar_value
self.xaxis_step = _xaxis_step
self.xticks_font = _xticks_font
self._xaxis_value_location = _xaxis_value_location
self.text_font = _text_font
self.text_rotation = _text_rotation
self.grp_title = _grp_title
self.enable_csv = _enable_csv
self.lf_csv = lf_csv()
self.legend_handles = _legend_handles
self.legend_loc = _legend_loc
self.legend_box = _legend_box
self.legend_ncol = _legend_ncol
self.legend_fontsize = _legend_fontsize
self.remove_border = _remove_border
self.alignment = _alignment
self.xticks_rotation = _xticks_rotation
def build_bar_graph(self):
if self.color is None:
i = 0
self.color = []
for _ in self.data_set:
self.color.append(self.color_name[i])
i = i + 1
fig_size, ax = plt.subplots(figsize=self.figsize, gridspec_kw=self.alignment)
i = 0
# to remove the borders
if self.remove_border is not None:
for border in self.remove_border:
ax.spines[border].set_color(None)
if 'left' in self.remove_border: # to remove the y-axis labeling
yaxis_visable =False
else:
yaxis_visable=True
ax.yaxis.set_visible(yaxis_visable)
def show_value(rectangles):
for rect in rectangles:
h = rect.get_height()
plt.text(rect.get_x() + rect.get_width() / 2., h, h,
ha='center', va='bottom', rotation=self.text_rotation, fontsize=self.text_font)
for _ in self.data_set:
if i > 0:
br = br1
br2 = [x + self.bar_width for x in br]
rects = plt.bar(br2, self.data_set[i], color=self.color[i], width=self.bar_width,
edgecolor=self.color_edge, label=self.label[i])
if self.show_bar_value:
show_value(rects)
br1 = br2
i = i + 1
else:
br1 = np.arange(len(self.data_set[i]))
rects = plt.bar(br1, self.data_set[i], color=self.color[i], width=self.bar_width,
edgecolor=self.color_edge, label=self.label[i])
if self.show_bar_value:
show_value(rects)
i = i + 1
plt.xlabel(self.xaxis_name, fontweight='bold', fontsize=15)
plt.ylabel(self.yaxis_name, fontweight='bold', fontsize=15)
if self.xaxis_categories[0] == 0:
plt.xticks(np.arange(0,
len(self.xaxis_categories),
step=self.xaxis_step),
fontsize=self.xticks_font,rotation=self.xticks_rotation)
else:
plt.xticks([i + self._xaxis_value_location for i in np.arange(0, len(self.data_set[0]), step=self.xaxis_step)],
self.xaxis_categories, fontsize=self.xticks_font,rotation=self.xticks_rotation)
plt.legend(
handles=self.legend_handles,
loc=self.legend_loc,
bbox_to_anchor=self.legend_box,
ncol=self.legend_ncol,
fontsize=self.legend_fontsize)
plt.suptitle(self.title, fontsize=self.title_size)
plt.title(self.grp_title)
plt.gcf()
plt.savefig("%s.png" % self.graph_image_name, dpi=96)
plt.close()
logger.debug("{}.png".format(self.graph_image_name))
if self.enable_csv:
if self.data_set is not None and self.xaxis_categories is not None:
if len(self.xaxis_categories) == len(self.data_set[0]):
self.lf_csv.columns = []
self.lf_csv.rows = []
self.lf_csv.columns.append(self.xaxis_name)
self.lf_csv.columns.extend(self.label)
self.lf_csv.rows.append(self.xaxis_categories)
self.lf_csv.rows.extend(self.data_set)
self.lf_csv.filename = f"{self.graph_image_name}.csv"
self.lf_csv.generate_csv()
else:
raise ValueError(
"Length and x-axis values and y-axis values should be same.")
else:
logger.debug("No Dataset Found")
logger.debug("{}.csv".format(self.graph_image_name))
return "%s.png" % self.graph_image_name
class lf_bar_graph_horizontal:
def __init__(self, _data_set=None,
_xaxis_name="x-axis",
_yaxis_name="y-axis",
_yaxis_categories=None,
_yaxis_label=None,
_graph_title="",
_title_size=16,
_graph_image_name="image_name",
_label=None,
_color=None,
_bar_height=0.25,
_color_edge='grey',
_font_weight='bold',
_color_name=None,
_figsize=(10, 5),
_show_bar_value=False,
_yaxis_step=1,
_yticks_font=None,
_yaxis_value_location=0,
_yticks_rotation=None,
_text_font=None,
_text_rotation=None,
_grp_title="",
_legend_handles=None,
_legend_loc="best",
_legend_box=None,
_legend_ncol=1,
_legend_fontsize=None,
_dpi=96,
_enable_csv=False,
_remove_border=None,
_alignment=None
):
if _data_set is None:
_data_set = [[30.4, 55.3, 69.2, 37.1], [45.1, 67.2, 34.3, 22.4], [22.5, 45.6, 12.7, 34.8]]
if _yaxis_categories is None:
_yaxis_categories = [1, 2, 3, 4]
if _yaxis_label is None:
_yaxis_label = ["a", "b", "c", "d"]
if _label is None:
_label = ["bi-downlink", "bi-uplink", 'uplink']
if _color_name is None:
_color_name = ['lightcoral', 'darkgrey', 'r', 'g', 'b', 'y']
self.data_set = _data_set
self.xaxis_name = _xaxis_name
self.yaxis_name = _yaxis_name
self.yaxis_categories = _yaxis_categories
self.yaxis_label = _yaxis_label
self.title = _graph_title
self.title_size = _title_size
self.graph_image_name = _graph_image_name
self.label = _label
self.color = _color
self.bar_height = _bar_height
self.color_edge = _color_edge
self.font_weight = _font_weight
self.color_name = _color_name
self.figsize = _figsize
self.show_bar_value = _show_bar_value
self.yaxis_step = _yaxis_step
self.yticks_font = _yticks_font
self._yaxis_value_location = _yaxis_value_location
self.text_font = _text_font
self.text_rotation = _text_rotation
self.grp_title = _grp_title
self.enable_csv = _enable_csv
self.lf_csv = lf_csv()
self.legend_handles = _legend_handles
self.legend_loc = _legend_loc
self.legend_box = _legend_box
self.legend_ncol = _legend_ncol
self.legend_fontsize = _legend_fontsize
self.remove_border = _remove_border
self.alignment = _alignment
self.yticks_rotation = _yticks_rotation
def build_bar_graph_horizontal(self):
if self.color is None:
i = 0
self.color = []
for _ in self.data_set:
self.color.append(self.color_name[i])
i = i + 1
fig_size, ax = plt.subplots(figsize=self.figsize, gridspec_kw=self.alignment)
i = 0
# to remove the borders
if self.remove_border is not None:
for border in self.remove_border:
ax.spines[border].set_color(None)
if 'left' in self.remove_border: # to remove the y-axis labeling
yaxis_visable =False
else:
yaxis_visable=True
ax.yaxis.set_visible(yaxis_visable)
def show_value(rectangles):
for rect in rectangles:
w = rect.get_width()
y = rect.get_y()
h = rect.get_height()
x = rect.get_x()
# adding 1 may not always work based on the x axis scale may need to be configurable
plt.text(w + 1 , rect.get_y() + rect.get_height() / 4., w,
ha='center', va='bottom', rotation=self.text_rotation, fontsize=self.text_font)
for _ in self.data_set:
if i > 0:
br = br1
br2 = [y + self.bar_height for y in br]
rects = plt.barh(br2, self.data_set[i], color=self.color[i], height=self.bar_height,
edgecolor=self.color_edge, label=self.label[i])
if self.show_bar_value:
show_value(rects)
br1 = br2
i = i + 1
else:
br1 = np.arange(len(self.data_set[i]))
rects = plt.barh(br1, self.data_set[i], color=self.color[i], height=self.bar_height,
edgecolor=self.color_edge, label=self.label[i])
if self.show_bar_value:
show_value(rects)
i = i + 1
plt.xlabel(self.xaxis_name, fontweight='bold', fontsize=15)
plt.ylabel(self.yaxis_name, fontweight='bold', fontsize=15)
if self.yaxis_categories[0] == 0:
plt.yticks(np.arange(0,
len(self.yaxis_categories),
step=self.yaxis_step),
fontsize=self.yticks_font,rotation=self.yticks_rotation)
else:
plt.yticks([i + self._yaxis_value_location for i in np.arange(0, len(self.data_set[0]), step=self.yaxis_step)],
self.yaxis_categories, fontsize=self.yticks_font,rotation=self.yticks_rotation)
plt.legend(
handles=self.legend_handles,
loc=self.legend_loc,
bbox_to_anchor=self.legend_box,
ncol=self.legend_ncol,
fontsize=self.legend_fontsize)
plt.suptitle(self.title, fontsize=self.title_size)
plt.title(self.grp_title)
plt.gcf()
plt.savefig("%s.png" % self.graph_image_name, dpi=96)
plt.close()
logger.debug("{}.png".format(self.graph_image_name))
if self.enable_csv:
if self.data_set is not None and self.yaxis_categories is not None:
if len(self.yaxis_categories) == len(self.data_set[0]):
self.lf_csv.columns = []
self.lf_csv.rows = []
self.lf_csv.columns.append(self.yaxis_name)
self.lf_csv.columns.extend(self.label)
self.lf_csv.rows.append(self.yaxis_categories)
self.lf_csv.rows.extend(self.data_set)
self.lf_csv.filename = f"{self.graph_image_name}.csv"
self.lf_csv.generate_csv()
else:
raise ValueError(
"Length and x-axis values and y-axis values should be same.")
else:
logger.debug("No Dataset Found")
logger.debug("{}.csv".format(self.graph_image_name))
return "%s.png" % self.graph_image_name
class lf_scatter_graph:
def __init__(self,
_x_data_set=None,
_y_data_set=None,
_values=None,
_xaxis_name="x-axis",
_yaxis_name="y-axis",
_label=None,
_graph_image_name="image_name1",
_color=None,
_figsize=(9, 4),
_enable_csv=True):
if _x_data_set is None:
_x_data_set = ["sta0 ", "sta1", "sta2", "sta3"]
if _y_data_set is None:
_y_data_set = [[30, 55, 69, 37]]
if _label is None:
_label = ["num1", "num2"]
self.x_data_set = _x_data_set
self.y_data_set = _y_data_set
self.xaxis_name = _xaxis_name
self.yaxis_name = _yaxis_name
self.figsize = _figsize
self.graph_image_name = _graph_image_name
self.color = _color
self.label = _label
self.values = _values
self.enable_csv = _enable_csv
self.lf_csv = lf_csv()
def build_scatter_graph(self):
if self.color is None:
self.color = [
"orchid",
"lime",
"aquamarine",
"royalblue",
"darkgray",
"maroon"]
plt.subplots(figsize=self.figsize)
if self.values is None:
plt.scatter(
self.x_data_set,
self.y_data_set[0],
color=self.color[0],
label=self.label[0])
if len(self.y_data_set) > 1:
for i in range(1, len(self.y_data_set)):
plt.scatter(
self.x_data_set,
self.y_data_set[i],
color=self.color[i],
label=self.label[i])
plt.xlabel(self.xaxis_name, fontweight='bold', fontsize=15)
plt.ylabel(self.yaxis_name, fontweight='bold', fontsize=15)
plt.gcf().autofmt_xdate()
plt.legend()
else:
colours = ListedColormap(self.color)
scatter = plt.scatter(
self.x_data_set,
self.y_data_set,
c=self.values,
cmap=colours)
plt.xlabel(self.xaxis_name, fontweight='bold', fontsize=15)
plt.ylabel(self.yaxis_name, fontweight='bold', fontsize=15)
plt.gcf().autofmt_xdate()
plt.legend(handles=scatter.legend_elements()[0], labels=self.label)
plt.savefig("%s.png" % self.graph_image_name, dpi=96)
plt.close()
logger.debug("{}.png".format(self.graph_image_name))
if self.enable_csv:
self.lf_csv.columns = self.label
self.lf_csv.rows = self.y_data_set
self.lf_csv.filename = f"{self.graph_image_name}.csv"
self.lf_csv.generate_csv()
return "%s.png" % self.graph_image_name
# have a second yaxis with line graph
class lf_bar_line_graph:
def __init__(self,
_data_set1=None,
# Note data_set2, data_set2_poly and data_set2_spline needs same size list
_data_set2=None,
_data_set2_poly=[False], # Values are True or False
_data_set2_poly_degree=[3],
_data_set2_interp1d=[False], # Values are True or False
_xaxis_name="x-axis",
_y1axis_name="y1-axis",
_y2axis_name="y2-axis",
_xaxis_categories=None,
_xaxis_label=None,
_graph_title="",
_title_size=16,
_graph_image_name="image_name",
_label1=None,
_label2=None,
_label2_poly=None,
_label2_interp1d=None,
_color1=None,
_color2=None,
_color2_poly=None,
_color2_interp1d=None,
_bar_width=0.25,
_color_edge='grey',
_font_weight='bold',
_color_name1=None,
_color_name2=None,
_marker=None,
_figsize=(10, 5),
_show_bar_value=False,
_xaxis_step=1,
_xticks_font=None,
_xaxis_value_location=0,
_text_font=None,
_text_rotation=None,
_grp_title="",
_legend_handles=None,
_legend_loc1="best",
_legend_loc2="best",
_legend_box1=None,
_legend_box2=None,
_legend_ncol=1,
_legend_fontsize=None,
_dpi=96,
_enable_csv=False):
if _data_set1 is None:
_data_set1 = [[30.4, 55.3, 69.2, 37.1], [45.1, 67.2, 34.3, 22.4], [22.5, 45.6, 12.7, 34.8]]
if _xaxis_categories is None:
_xaxis_categories = [1, 2, 3, 4]
if _xaxis_label is None:
_xaxis_label = ["a", "b", "c", "d"]
if _label1 is None:
_label1 = ["bi-downlink", "bi-uplink", 'uplink']
if _label2 is None:
_label2 = ["bi-downlink", "bi-uplink", 'uplink']
if _color_name1 is None:
_color_name1 = ['lightcoral', 'darkgrey', 'r', 'g', 'b', 'y']
if _color_name2 is None:
_color_name2 = ['lightcoral', 'darkgrey', 'r', 'g', 'b', 'y']
self.data_set1 = _data_set1
self.data_set2 = _data_set2
self.data_set2_poly = _data_set2_poly
self.data_set2_poly_degree = _data_set2_poly_degree
self.data_set2_interp1d = _data_set2_interp1d
self.xaxis_name = _xaxis_name
self.y1axis_name = _y1axis_name
self.y2axis_name = _y2axis_name
self.xaxis_categories = _xaxis_categories
self.xaxis_label = _xaxis_label
self.title = _graph_title
self.title_size = _title_size
self.graph_image_name = _graph_image_name
self.label1 = _label1
self.label2 = _label2
self.label2_poly = _label2_poly
self.label2_interp1d = _label2_interp1d
self.color1 = _color1
self.color2 = _color2
self.color2_poly = _color2_poly
self.color2_interp1d = _color2_interp1d
self.marker = _marker
self.bar_width = _bar_width
self.color_edge = _color_edge
self.font_weight = _font_weight
self.color_name1 = _color_name1
self.color_name2 = _color_name2
self.figsize = _figsize
self.show_bar_value = _show_bar_value
self.xaxis_step = _xaxis_step
self.xticks_font = _xticks_font
self._xaxis_value_location = _xaxis_value_location
self.text_font = _text_font
self.text_rotation = _text_rotation
self.grp_title = _grp_title
self.enable_csv = _enable_csv
self.lf_csv = lf_csv()
self.legend_handles = _legend_handles
self.legend_loc1 = _legend_loc1
self.legend_loc2 = _legend_loc2
self.legend_box1 = _legend_box1
self.legend_box2 = _legend_box2
self.legend_ncol = _legend_ncol
self.legend_fontsize = _legend_fontsize
def build_bar_line_graph(self):
if self.color1 is None:
i = 0
self.color1 = []
for _ in self.data_set1:
self.color1.append(self.color_name[i])
i = i + 1
fig, ax1 = plt.subplots(figsize=self.figsize)
ax2 = ax1.twinx()
i = 0
def show_value(rectangles):
for rect in rectangles:
h = rect.get_height()
ax1.text(rect.get_x() + rect.get_width() / 2., h, h,
ha='center', va='bottom', rotation=self.text_rotation, fontsize=self.text_font)
for _ in self.data_set1:
if i > 0:
br = br1
br2 = [x + self.bar_width for x in br]
rects = ax1.bar(br2, self.data_set1[i], color=self.color1[i], width=self.bar_width,
edgecolor=self.color_edge, label=self.label1[i])
if self.show_bar_value:
show_value(rects)
br1 = br2
i = i + 1
else:
br1 = np.arange(len(self.data_set1[i]))
rects = ax1.bar(br1, self.data_set1[i], color=self.color1[i], width=self.bar_width,
edgecolor=self.color_edge, label=self.label1[i])
if self.show_bar_value:
show_value(rects)
i = i + 1
ax1.set_xlabel(self.xaxis_name, fontweight='bold', fontsize=15)
ax1.set_ylabel(self.y1axis_name, fontweight='bold', fontsize=15)
if self.xaxis_categories[0] == 0:
xsteps = plt.xticks(np.arange(0,
len(self.xaxis_categories),
step=self.xaxis_step),
fontsize=self.xticks_font)
else:
xsteps = plt.xticks([i + self._xaxis_value_location for i in np.arange(0, len(self.data_set1[0]), step=self.xaxis_step)],
self.xaxis_categories, fontsize=self.xticks_font)
ax1.legend(
handles=self.legend_handles,
loc=self.legend_loc1,
bbox_to_anchor=self.legend_box1,
ncol=self.legend_ncol,
fontsize=self.legend_fontsize)
# overlay line graph
def show_value2(data):
for item, value in enumerate(data):
ax2.text(item, value, "{value}".format(value=value), ha='center',rotation=self.text_rotation, fontsize=self.text_font)
i = 0
for _ in self.data_set2:
br1 = np.arange(len(self.data_set2[i]))
ax2.plot(
br1,
self.data_set2[i],
color=self.color2[i],
label=self.label2[i],
marker=self.marker[i])
show_value2(self.data_set2[i])
# do polynomial smoothing
if self.data_set2_poly[i]:
poly = np.polyfit(br1,self.data_set2[i],self.data_set2_poly_degree[i])
poly_y = np.poly1d(poly)(br1)
ax2.plot(
br1,
poly_y,
color=self.color2_poly[i],
label=self.label2_poly[i]
)
if self.data_set2_interp1d[i]:
cubic_interpolation_model = interpolate.interp1d(br1, self.data_set2[i],kind="cubic")
x_sm = np.array(br1)
x_smooth = np.linspace(x_sm.min(), x_sm.max(), 500)
y_smooth = cubic_interpolation_model(x_smooth)
ax2.plot(
x_smooth,
y_smooth,
color=self.color2_interp1d[i],
label=self.label2_interp1d[i]
)
i += 1
ax2.set_xlabel(self.xaxis_name, fontweight='bold', fontsize=15)
ax2.set_ylabel(self.y2axis_name, fontweight='bold', fontsize=15)
ax2.tick_params(axis = 'y', labelcolor = 'orange')
ax2.legend(
handles=self.legend_handles,
loc=self.legend_loc2,
bbox_to_anchor=self.legend_box2,
ncol=self.legend_ncol,
fontsize=self.legend_fontsize)
plt.suptitle(self.title, fontsize=self.title_size)
plt.title(self.grp_title)
plt.gcf()
plt.savefig("%s.png" % self.graph_image_name, dpi=96)
plt.close()
logger.debug("{}.png".format(self.graph_image_name))
# TODO work though this for two axis
if self.enable_csv:
if self.data_set is not None and self.xaxis_categories is not None:
if len(self.xaxis_categories) == len(self.data_set[0]):
self.lf_csv.columns = []
self.lf_csv.rows = []
self.lf_csv.columns.append(self.xaxis_name)
self.lf_csv.columns.extend(self.label)
self.lf_csv.rows.append(self.xaxis_categories)
self.lf_csv.rows.extend(self.data_set)
self.lf_csv.filename = f"{self.graph_image_name}.csv"
self.lf_csv.generate_csv()
else:
raise ValueError(
"Length and x-axis values and y-axis values should be same.")
else:
logger.debug("No Dataset Found")
logger.debug("{}.csv".format(self.graph_image_name))
return "%s.png" % self.graph_image_name
class lf_stacked_graph:
"""
usage: This will generate a vertically stacked graph with list _data_set as well as with dictionary _data_set.
example :
For a graph with dictionary data_set
obj = lf_stacked_graph(_data_set={'FCC0':0, 'FCC1':88.4,'FCC2':77.8,'FCC3':57.8,'FCC4':90.0,'FCC95':60.4,'FCC6':33.0},
_xaxis_name="", _yaxis_name="", _enable_csv=False, _remove_border=True)
obj.build_stacked_graph()
For a graph with list data_set
obj = lf_stacked_graph(_data_set=[['FCC0', 'FCC1', 'FCC2', 'FCC3', 'FCC4', 'FCC95', 'FCC6'],
[0, 88.4, 77.8, 57.8, 90.0, 60.4, 33.0],
[100.0, 11.6, 22.2, 42.2, 10.0, 39.6, 67.0]])
obj.build_stacked_graph()
"""
def __init__(self,
_data_set=None,
_xaxis_name="Stations",
_yaxis_name="Numbers",
_label=None,
_graph_image_name="image_name2",
_color=None,
_figsize=(9, 4),
_enable_csv=True,
_width=0.79,
_bar_text_color='white',
_bar_font_weight='bold',
_bar_font_size=8,
_legend_title="Issues",
_legend_bbox=(1.13, 1.01),
_legend_loc="upper right",
_remove_border=False,
_bar_text_rotation=0,
_x_ticklabels_rotation=0):
if _data_set is None:
_data_set = [[1, 2, 3, 4], [1, 1, 1, 1], [1, 1, 1, 1]]
if _label is None:
_label = ['Success', 'Fail']
self.data_set = _data_set # [x_axis,y1_axis,y2_axis]
self.xaxis_name = _xaxis_name
self.yaxis_name = _yaxis_name
self.figsize = _figsize
self.graph_image_name = _graph_image_name
self.label = _label
self.color = _color
self.enable_csv = _enable_csv
self.lf_csv = lf_csv()
self.width = _width
self.bar_text_color = _bar_text_color
self.bar_font_weight = _bar_font_weight
self.bar_font_size = _bar_font_size
self.legend_title = _legend_title
self.legend_bbox = _legend_bbox
self.legend_loc = _legend_loc
self.remove_border = _remove_border
self.bar_text_rotation = _bar_text_rotation
self.x_ticklabels_rotation = _x_ticklabels_rotation
def build_stacked_graph(self):
fig, axes_subplot = plt.subplots(figsize=self.figsize)
if self.color is None:
self.color = [
"darkred",
"tomato",
"springgreen",
"skyblue",
"indigo",
"plum"]
if type(self.data_set) is list:
plt.bar(self.data_set[0], self.data_set[1], color=self.color[0])
plt.bar(
self.data_set[0],
self.data_set[2],
bottom=self.data_set[1],
color=self.color[1])
if len(self.data_set) > 3:
for i in range(3, len(self.data_set)):
plt.bar(self.data_set[0], self.data_set[i],
bottom=np.array(self.data_set[i - 2]) + np.array(self.data_set[i - 1]),color=self.color[i - 1])
plt.legend(self.label)
elif type(self.data_set) is dict:
lable_values = []
pass_values = []
fail_values = []
for i in self.data_set:
lable_values.append(i)
for j in self.data_set:
pass_values.append(self.data_set[j])
fail_values.append(round(float(100.0 - self.data_set[j]), 1))
width = self.width
figure_size, axes_subplot = plt.subplots(figsize=self.figsize)
# building vertical bar plot
bar_1 = plt.bar(lable_values, pass_values, width, color='green')
bar_2 = plt.bar(lable_values, fail_values, width, bottom=pass_values, color='red')
# inserting bar text
if len(list(self.data_set.keys())) > 10:
self.bar_text_rotation = 90
self.x_ticklabels_rotation = 90
for i, v in enumerate(pass_values):
if v != 0:
plt.text(i + .005, v * 0.45, "%s%s" % (v, "%"), color=self.bar_text_color,
fontweight=self.bar_font_weight,
fontsize=self.bar_font_size, ha="center", va="center", rotation=self.bar_text_rotation)
for i, v in enumerate(fail_values):
if v != 0:
plt.text(i + .005, v * 0.45 + pass_values[i], "%s%s" % (v, "%"), color=self.bar_text_color,
fontweight=self.bar_font_weight, fontsize=self.bar_font_size, ha="center", va="center" ,
rotation=self.bar_text_rotation)
plt.legend([bar_1, bar_2], self.label, title=self.legend_title, bbox_to_anchor=self.legend_bbox,
loc=self.legend_loc)
axes_subplot.set_xticks(list(self.data_set.keys()))
axes_subplot.set_xticklabels(list(self.data_set.keys()), rotation=self.x_ticklabels_rotation)
# to remove the borders
if self.remove_border:
for border in ['top', 'right', 'left', 'bottom']:
axes_subplot.spines[border].set_visible(False)
axes_subplot.yaxis.set_visible(False)
plt.xlabel(self.xaxis_name)
plt.ylabel(self.yaxis_name)
plt.savefig("%s.png" % self.graph_image_name, bbox_inches="tight", dpi=96)
plt.close()
logger.debug("{}.png".format(self.graph_image_name))
if self.enable_csv:
self.lf_csv.columns = self.label
self.lf_csv.rows = self.data_set
self.lf_csv.filename = f"{self.graph_image_name}.csv"
self.lf_csv.generate_csv()
return "%s.png" % self.graph_image_name
class lf_horizontal_stacked_graph:
def __init__(self,
_seg=2,
_yaxis_set=('A', 'B'),
_xaxis_set1=None,
_xaxis_set2=None,
_unit="%",
_xaxis_name="Stations",
_label=None,
_graph_image_name="image_name3",
_color=None,
_figsize=(9, 4),
_disable_xaxis=False,
_enable_csv=True):
if _xaxis_set1 is None:
_xaxis_set1 = [12, 0, 0, 16, 15]
if _xaxis_set2 is None:
_xaxis_set2 = [23, 34, 23, 0]
if _label is None:
_label = ['Success', 'Fail']
if _color is None:
_color = ["success", "Fail"]
self.unit = _unit
self.seg = _seg
self.xaxis_set1 = _xaxis_set1
self.xaxis_set2 = _xaxis_set2
self.yaxis_set = _yaxis_set
self.xaxis_name = _xaxis_name
self.figsize = _figsize
self.graph_image_name = _graph_image_name
self.label = _label
self.color = _color
self.disable_xaxis = _disable_xaxis
self.enable_csv = _enable_csv
self.lf_csv = lf_csv()
def build_horizontal_stacked_graph(self):
def sumzip(items):
return [sum(values) for values in zip(items)]
fig, ax = plt.subplots(figsize=self.figsize)
n = self.seg
values1 = self.xaxis_set1
values2 = self.xaxis_set2
ind = np.arange(n) + .15
width = 0.3
plt.barh(
ind,
values1,
width,
color=self.color[0],
label=self.label[0])
plt.barh(
ind,
values2,
width,
left=sumzip(values1),
color=self.color[1],
label=self.label[1])
extra_space = 0.15
ax.set_yticks(ind + width - extra_space)
ax.set_yticklabels(self.yaxis_set)
ax.yaxis.set_tick_params(length=0, labelbottom=True)
for i, v in enumerate(values1):
if v != 0:
plt.text(v * 0.45, i + .145, "%s%s" % (v, self.unit), color='white', fontweight='bold', fontsize=10,
ha='center', va='center')
for i, v in enumerate(values2):
if v != 0:
plt.text(v * 0.45 + values1[i], i + .145, "%s%s" % (v, self.unit), color='white', fontweight='bold',
fontsize=10,
ha='center', va='center')
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.legend(loc='upper right')
if self.disable_xaxis:
plt.tick_params(
axis='x',
which='both',
bottom=False,
top=False,
labelbottom=False) # disable x-axis
plt.savefig("%s.png" % self.graph_image_name, dpi=96)
plt.close()
logger.debug("{}.png".format(self.graph_image_name))
if self.enable_csv:
self.lf_csv.columns = self.label
self.lf_csv.rows = self.data_set
self.lf_csv.filename = f"{self.graph_image_name}.csv"
self.lf_csv.generate_csv()
return "%s.png" % self.graph_image_name
class lf_line_graph:
def __init__(self, _data_set=None,
_xaxis_name="x-axis",
_yaxis_name="y-axis",
_xaxis_categories=None,
_xaxis_label=None,
_graph_title="",
_title_size=16,
_graph_image_name="line_graph",
_label=None,
_font_weight='bold',
_color=None,
_figsize=(10, 5),
_xaxis_step=5,
_xticks_font=None,
_text_font=None,
_legend_handles=None,
_legend_loc="best",
_legend_box=None,
_legend_ncol=1,
_legend_fontsize=None,
_marker=None,
_dpi=96,
_grid=True,
_enable_csv=False,
_reverse_x=False,
_reverse_y=False):
if _data_set is None:
_data_set = [[30.4, 55.3, 69.2, 37.1, 44.0], [45.1, 67.2, 34.3, 22.4, 37.6], [22.5, 45.6, 12.7, 34.8, 22.5]]
if _xaxis_categories is None:
_xaxis_categories = [1, 2, 3, 4, 5]
if _xaxis_label is None:
_xaxis_label = ["a", "b", "c", "d", "e"]
if _label is None:
_label = ["bi-downlink", "bi-uplink", 'uplink']
if _color is None:
_color = ['forestgreen', 'c', 'r', 'g', 'b', 'p']
if _marker is None:
_marker = ['s', 'o', 'v'] # available markers = '.', 'o', 'v', '<', 's', '*', 'p', 'P'
self.grid = _grid
self.data_set = _data_set
self.xaxis_name = _xaxis_name
self.yaxis_name = _yaxis_name
self.xaxis_categories = _xaxis_categories
self.xaxis_label = _xaxis_label
self.grp_title = _graph_title
self.title_size = _title_size
self.graph_image_name = _graph_image_name
self.label = _label
self.color = _color
self.font_weight = _font_weight
self.figsize = _figsize
self.xaxis_step = _xaxis_step
self.xticks_font = _xticks_font
self.text_font = _text_font
self.marker = _marker
self.enable_csv = _enable_csv
self.lf_csv = lf_csv()
self.legend_handles = _legend_handles
self.legend_loc = _legend_loc
self.legend_box = _legend_box
self.legend_ncol = _legend_ncol
self.legend_fontsize = _legend_fontsize
self.reverse_x = _reverse_x
self.reverse_y = _reverse_y
def build_line_graph(self):
plt.subplots(figsize=self.figsize)
i = 0
for data in self.data_set:
plt.plot(
self.xaxis_categories,
data,
color=self.color[i],
label=self.label[i],
marker=self.marker[i])
i += 1
plt.xlabel(self.xaxis_name, fontweight='bold', fontsize=15)
plt.ylabel(self.yaxis_name, fontweight='bold', fontsize=15)
if self.grid:
plt.grid(True, linestyle=':') # available line styles = ':', '-', '--', '-.'
plt.legend(
handles=self.legend_handles,
loc=self.legend_loc,
bbox_to_anchor=self.legend_box,
ncol=self.legend_ncol,
fontsize=self.legend_fontsize)
plt.suptitle(self.grp_title, fontsize=self.title_size)
if self.reverse_y:
plt.gca().invert_yaxis()
if self.reverse_x:
plt.gca().invert_xaxis()
plt.gcf()
plt.savefig("%s.png" % self.graph_image_name, dpi=96)
plt.close()
logger.debug("{}.png".format(self.graph_image_name))
if self.enable_csv:
if self.data_set is not None:
self.lf_csv.columns = self.label
self.lf_csv.rows = self.data_set
self.lf_csv.filename = f"{self.graph_image_name}.csv"
self.lf_csv.generate_csv()
else:
logger.debug("No Dataset Found")
logger.debug("{}.csv".format(self.graph_image_name))
return "%s.png" % self.graph_image_name
def main():
help_summary = '''\
This script facilitates the generation of comprehensive graphical reports. It offers a variety of graph types,
including bar graphs, horizontal bar graphs, scatter graphs, bar-line graphs, stacked graphs, horizontal stacked
graphs, and line graphs.
'''
# arguments
parser = argparse.ArgumentParser(
prog='lf_graph.py',
formatter_class=argparse.RawTextHelpFormatter,
epilog='''\
lf_graph.py : unit test in lf_graph.py for exersizing the lf_graph.py library
''',
description='''\
-----------------
NAME: lf_graph.py
PURPOSE:
Common Library for generating graphs for LANforge output
SETUP:
/lanforge/html-reports directory needs to be present or output generated in local file
EXAMPLE:
see: /py-scritps/lf_report_test.py for example
COPYWRITE
Copyright 2021 Candela Technologies Inc
License: Free to distribute and modify. LANforge systems must be licensed.
INCLUDE_IN_README
---------------------
''')
parser.add_argument(
'--mgr',
'--lfmgr',
dest='lfmgr',
help='sample argument: where LANforge GUI is running',
default='localhost')
# logging configuration
parser.add_argument(
'--debug',
help='--debug this will enable debugging in py-json method',
action='store_true')
parser.add_argument('--log_level',
default=None,
help='Set logging level: debug | info | warning | error | critical')
parser.add_argument(
"--lf_logger_config_json",
help="--lf_logger_config_json <json file> , json configuration of logger")
parser.add_argument('--help_summary', help='Show summary of what this script does', default=None,
action="store_true")
# the args parser is not really used , this is so the report is not generated when testing
# the imports with --help
args = parser.parse_args()
if args.help_summary:
print(help_summary)
exit(0)
# set up logger
logger_config = lf_logger_config.lf_logger_config()
# set the logger level to debug
if args.log_level:
logger_config.set_level(level=args.log_level)
# lf_logger_config_json will take presidence to changing debug levels
if args.lf_logger_config_json:
# logger_config.lf_logger_config_json = "lf_logger_config.json"
logger_config.lf_logger_config_json = args.lf_logger_config_json
logger_config.load_lf_logger_config()
logger.debug("LANforge manager {lfmgr}".format(lfmgr=args.lfmgr))
output_html_1 = "graph_1.html"
output_pdf_1 = "graph_1.pdf"
# test build_bar_graph with defaults
graph = lf_bar_graph()
graph_html_obj = """
<img align='center' style='padding:15;margin:5;width:1000px;' src=""" + "%s" % (graph.build_bar_graph()) + """ border='1' />
<br><br>
"""
#
test_file = open(output_html_1, "w")
test_file.write(graph_html_obj)
test_file.close()
# write to pdf
# write logic to generate pdf here
# wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb
# sudo apt install ./wkhtmltox_0.12.6-1.focal_amd64.deb
# prevent eerror Blocked access to file
options = {"enable-local-file-access": None}
pdfkit.from_file(output_html_1, output_pdf_1, options=options)
# test build_bar_graph setting values
dataset = [[45, 67, 34, 22], [22, 45, 12, 34], [30, 55, 69, 37]]
x_axis_values = [1, 2, 3, 4]
output_html_2 = "graph_2.html"
output_pdf_2 = "graph_2.pdf"
# test build_bar_graph with defaults
graph = lf_bar_graph(_data_set=dataset,
_xaxis_name="stations",
_yaxis_name="Throughput 2 (Mbps)",
_xaxis_categories=x_axis_values,
_graph_image_name="Bi-single_radio_2.4GHz",
_label=["bi-downlink", "bi-uplink", 'uplink'],
_color=None,
_color_edge='red',
_show_bar_value=True,
_text_font=7,
_text_rotation=None,
_enable_csv=True)
graph_html_obj = """
<img align='center' style='padding:15;margin:5;width:1000px;' src=""" + "%s" % (graph.build_bar_graph()) + """ border='1' />
<br><br>
"""
#
test_file = open(output_html_2, "w")
test_file.write(graph_html_obj)
test_file.close()
graph = lf_line_graph()
graph.build_line_graph()
# write to pdf
# write logic to generate pdf here
# wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb
# sudo apt install ./wkhtmltox_0.12.6-1.focal_amd64.deb
# prevent eerror Blocked access to file
options = {"enable-local-file-access": None}
pdfkit.from_file(output_html_2, output_pdf_2, options=options)
# test build_bar_graph_horizontal with defaults
dataset = [[45, 67, 34, 22, 31, 52, 60, 71, 24, 25, 45, 67, 34, 22, 31, 52, 60, 71, 24, 25], [22, 45, 12, 34, 70, 80, 14, 35, 44, 45,22, 45, 12, 34, 70, 80, 14, 35, 44, 45 ], [30, 55, 69, 37, 77, 24, 25, 77, 77, 80, 30, 55, 69, 37, 77, 24, 25, 77, 77, 80]]
y_axis_values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
# calculate the height of the y-axis .25 * number of values
y_fig_size = len(y_axis_values) * len(dataset) * .35
x_fig_size = 10
output_html_3 = "graph_3.html"
output_pdf_3 = "graph_3.pdf"
graph = lf_bar_graph_horizontal(_data_set=dataset,
_xaxis_name="Throughput 2 (Mbps)",
_yaxis_name="stations",
_yaxis_categories=y_axis_values,
_graph_image_name="Bi-single_radio_2.4GHz",
_label=["bi-downlink", "bi-uplink", 'uplink'],
_color=None,
_color_edge='red',
_figsize=(x_fig_size, y_fig_size),
_show_bar_value= True,
_text_font=6,
_text_rotation=True,
_enable_csv=True)
graph_html_obj = """
<img align='center' style='padding:15;margin:5;width:1000px;' src=""" + "%s" % (graph.build_bar_graph_horizontal()) + """ border='1' />
<br><br>
"""
#
test_file = open(output_html_3, "w")
test_file.write(graph_html_obj)
test_file.close()
# write to pdf
# write logic to generate pdf here
# wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb
# sudo apt install ./wkhtmltox_0.12.6-1.focal_amd64.deb
# prevent eerror Blocked access to file
options = {"enable-local-file-access": None}
pdfkit.from_file(output_html_3, output_pdf_3, options=options)
# Unit Test
if __name__ == "__main__":
main()