mirror of
				https://github.com/Telecominfraproject/wlan-lanforge-scripts.git
				synced 2025-10-31 18:58:01 +00:00 
			
		
		
		
	 8855c36f3e
			
		
	
	8855c36f3e
	
	
	
		
			
			lf_report.py the get_date will allow for timestamps to be added to addtional files to be stored in results test_l3_longevity.py Phase 2, add the abilty to clear and read the ap upload and download stats write the output to a file in the results. The clear takes place at the beginning of the test and reading at the end It was unclear if the ap needed to be read on each interation
		
			
				
	
	
		
			413 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			413 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env python3
 | |
| 
 | |
| '''
 | |
| NAME: lf_report.py
 | |
| 
 | |
| PURPOSE: 
 | |
| 
 | |
| This program is a helper  class for reporting results for a lanforge python script.
 | |
| The class will generate an output directory based on date and time in the /home/lanforge/html-reports/ .   
 | |
| If the reports-data is not present then the date and time directory will be created in the current directory.
 | |
| The banner and Candela Technology logo will be copied in the results directory. 
 | |
| The results directory may be over written and many of the other paramaters during construction. 
 | |
| Creating the date time directory on construction was a design choice.
 | |
| 
 | |
| EXAMPLE: 
 | |
| 
 | |
| This is a helper class, a unit test is included at the bottom of the file.  
 | |
| To test lf_report.py and lf_graph.py together use the lf_report_test.py file
 | |
| 
 | |
| LICENSE:
 | |
|     Free to distribute and modify. LANforge systems must be licensed.
 | |
|     Copyright 2021 Candela Technologies Inc
 | |
| 
 | |
| 
 | |
| INCLUDE_IN_README
 | |
| '''
 | |
| 
 | |
| import datetime
 | |
| import os
 | |
| import shutil
 | |
| 
 | |
| import pandas as pd
 | |
| import pdfkit
 | |
| 
 | |
| # internal candela references included during intial phases, to be deleted at future date
 | |
| # https://candelatech.atlassian.net/wiki/spaces/LANFORGE/pages/372703360/Scripting+Data+Collection+March+2021
 | |
| # base report class
 | |
| class lf_report():
 | |
|     def __init__(self,
 | |
|                 #_path the report directory under which the report directories will be created.
 | |
|                 _path = "/home/lanforge/html-reports",
 | |
|                 _alt_path = "",
 | |
|                 _date = "",
 | |
|                 _title="LANForge Test Run Heading",
 | |
|                 _table_title="LANForge Table Heading",
 | |
|                 _graph_title="LANForge Graph Title",
 | |
|                 _obj = "",
 | |
|                 _obj_title = "",
 | |
|                 _output_html="outfile.html",
 | |
|                 _output_pdf="outfile.pdf",
 | |
|                 _results_dir_name = "LANforge_Test_Results",
 | |
|                 _output_format = 'html',  # pass in on the write functionality, current not used
 | |
|                 _dataframe="",
 | |
|                 _path_date_time="",
 | |
|                  _custom_css='custom-example.css'):  # this is where the final report is placed.
 | |
|                 #other report paths, 
 | |
| 
 | |
|             # _path is where the directory with the data time will be created
 | |
|             if _path == "local" or _path == "here":
 | |
|                 self.path = os.path.abspath(__file__)
 | |
|                 print("path set to file path: {}".format(self.path))
 | |
|             elif _alt_path != "":
 | |
|                 self.path = _alt_path
 | |
|                 print("path set to alt path: {}".format(self.path))
 | |
|             else:
 | |
|                 self.path = _path
 | |
|                 print("path set: {}".format(self.path))
 | |
|                 
 | |
|             self.dataframe=_dataframe
 | |
|             self.text = ""
 | |
|             self.title=_title
 | |
|             self.table_title=_table_title
 | |
|             self.graph_title=_graph_title
 | |
|             self.date=_date
 | |
|             self.output_html=_output_html
 | |
|             self.path_date_time = _path_date_time
 | |
|             self.write_output_html = ""
 | |
|             self.output_pdf=_output_pdf
 | |
|             self.write_output_pdf = ""
 | |
|             self.banner_html = ""
 | |
|             self.graph_titles=""
 | |
|             self.graph_image=""
 | |
|             self.html = ""
 | |
|             self.custom_html = ""
 | |
|             self.objective = _obj
 | |
|             self.obj_title = _obj_title
 | |
|             #self.systeminfopath = ""
 | |
|             self.date_time_directory = ""
 | |
|             self.banner_directory = "artifacts"
 | |
|             self.banner_file_name = "banner.png"    # does this need to be configurable
 | |
|             self.logo_directory = "artifacts"       
 | |
|             self.logo_file_name = "CandelaLogo2-90dpi-200x90-trans.png"      # does this need to be configurable.
 | |
|             self.current_path = os.path.dirname(os.path.abspath(__file__))
 | |
|             self.custom_css = _custom_css
 | |
|             # pass in _date to allow to change after construction
 | |
|             self.set_date_time_directory(_date,_results_dir_name)
 | |
|             self.build_date_time_directory()
 | |
| 
 | |
|             self.font_file = "CenturyGothic.woff"
 | |
|             # move the banners and candela images to report path
 | |
|             self.copy_banner()
 | |
|             self.copy_css()
 | |
|             self.copy_logo()
 | |
|     
 | |
|     def copy_banner(self):
 | |
|         banner_src_file = str(self.current_path)+'/'+str(self.banner_directory)+'/'+str(self.banner_file_name)
 | |
|         banner_dst_file = str(self.path_date_time)+'/'+ str(self.banner_file_name)
 | |
|         #print("banner src_file: {}".format(banner_src_file))
 | |
|         #print("dst_file: {}".format(banner_dst_file))
 | |
|         shutil.copy(banner_src_file, banner_dst_file)
 | |
| 
 | |
|     def copy_css(self):
 | |
|         reportcss_src_file = str(self.current_path)+'/'+str(self.banner_directory)+'/report.css'
 | |
|         reportcss_dest_file = str(self.path_date_time)+'/report.css'
 | |
| 
 | |
|         customcss_src_file = str(self.current_path)+'/'+str(self.banner_directory)+'/'+str(self.custom_css)
 | |
|         customcss_dest_file = str(self.path_date_time)+'/custom.css'
 | |
| 
 | |
|         font_src_file = str(self.current_path)+'/'+str(self.banner_directory)+'/'+str(self.font_file)
 | |
|         font_dest_file = str(self.path_date_time)+'/'+str(self.font_file)
 | |
| 
 | |
|         shutil.copy(reportcss_src_file, reportcss_dest_file)
 | |
|         shutil.copy(customcss_src_file, customcss_dest_file)
 | |
|         shutil.copy(font_src_file, font_dest_file)
 | |
| 
 | |
|     def copy_logo(self):
 | |
|         logo_src_file = str(self.current_path)+'/'+str(self.logo_directory)+'/'+str(self.logo_file_name)
 | |
|         logo_dst_file = str(self.path_date_time)+'/'+ str(self.logo_file_name)
 | |
|         #print("logo_src_file: {}".format(logo_src_file))
 | |
|         #print("logo_dst_file: {}".format(logo_dst_file))
 | |
|         shutil.copy(logo_src_file, logo_dst_file)
 | |
| 
 | |
|     def move_graph_image(self,):
 | |
|         graph_src_file = str(self.graph_image)
 | |
|         graph_dst_file = str(self.path_date_time)+'/'+ str(self.graph_image)
 | |
|         print("graph_src_file: {}".format(graph_src_file))
 | |
|         print("graph_dst_file: {}".format(graph_dst_file))
 | |
|         shutil.move(graph_src_file, graph_dst_file)
 | |
| 
 | |
|     def set_path(self,_path):
 | |
|         self.path = _path
 | |
| 
 | |
|     def set_date_time_directory(self,_date,_results_dir_name):
 | |
|         self.date = _date
 | |
|         self.results_dir_name = _results_dir_name
 | |
|         if self.date != "":
 | |
|             self.date_time_directory = str(self.date) + str("_") + str(self.results_dir_name)
 | |
|         else:
 | |
|             self.date = str(datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S")).replace(':','-')
 | |
|             self.date_time_directory = self.date + str("_") + str(self.results_dir_name)
 | |
| 
 | |
|     def build_date_time_directory(self):
 | |
|         if self.date_time_directory == "":
 | |
|             self.set_date_time_directory()
 | |
|         self.path_date_time = os.path.join(self.path, self.date_time_directory)
 | |
|         print("path_date_time {}".format(self.path_date_time))
 | |
|         try:        
 | |
|             if not os.path.exists(self.path_date_time):
 | |
|                 os.mkdir(self.path_date_time)
 | |
|         except:
 | |
|             self.path_date_time = os.path.join(self.current_path, self.date_time_directory)
 | |
|             if not os.path.exists(self.path_date_time):
 | |
|                 os.mkdir(self.path_date_time)
 | |
|         print("report path : {}".format(self.path_date_time))    
 | |
| 
 | |
|     def set_text(self,_text):
 | |
|         self.text = _text
 | |
| 
 | |
|     def set_title(self,_title):
 | |
|         self.title = _title
 | |
| 
 | |
|     def set_table_title(self,_table_title):
 | |
|         self.table_title = _table_title
 | |
| 
 | |
|     def set_graph_title(self,_graph_title):
 | |
|         self.graph_title = _graph_title
 | |
| 
 | |
|     # The _date is set when class is enstanciated / created so this set_date should be used with caution, used to synchronize results
 | |
|     def set_date(self,_date):
 | |
|         self.date = _date
 | |
| 
 | |
|     def set_table_dataframe(self,_dataframe):
 | |
|         self.dataframe = _dataframe
 | |
| 
 | |
|     def set_table_dataframe_from_csv(self,_csv):
 | |
|         self.dataframe = pd.read_csv(_csv)
 | |
| 
 | |
|     def set_custom_html(self,_custom_html):
 | |
|         self.custom_html = _custom_html
 | |
| 
 | |
|     def set_obj_html(self,_obj_title, _obj ):
 | |
|         self.objective = _obj
 | |
|         self.obj_title = _obj_title
 | |
| 
 | |
|     def set_graph_image(self,_graph_image):
 | |
|         self.graph_image = _graph_image
 | |
| 
 | |
|     def get_date(self):
 | |
|         return self.date
 | |
| 
 | |
|     def get_path(self):
 | |
|         return self.path
 | |
|     # get_path_date_time, get_report_path and need to be the same
 | |
|     def get_path_date_time(self):
 | |
|         return self.path_date_time
 | |
| 
 | |
|     def get_report_path(self):
 | |
|         return self.path_date_time
 | |
| 
 | |
|     def file_add_path(self, file):
 | |
|         output_file = str(self.path_date_time)+'/'+ str(file)
 | |
|         print("output file {}".format(output_file))
 | |
|         return output_file
 | |
| 
 | |
|     def write_html(self): 
 | |
|         self.write_output_html = str(self.path_date_time)+'/'+ str(self.output_html)
 | |
|         print("write_output_html: {}".format(self.write_output_html))
 | |
|         try:
 | |
|             test_file = open(self.write_output_html, "w")
 | |
|             test_file.write(self.html)
 | |
|             test_file.close()
 | |
|         except:
 | |
|             print("write_html failed")
 | |
|         return self.write_output_html
 | |
| 
 | |
|     def write_html_with_timestamp(self): 
 | |
|         self.write_output_html = "{}/{}-{}".format(self.path_date_time,self.date,self.output_html)
 | |
|         print("write_output_html: {}".format(self.write_output_html))
 | |
|         try:
 | |
|             test_file = open(self.write_output_html, "w")
 | |
|             test_file.write(self.html)
 | |
|             test_file.close()
 | |
|         except:
 | |
|             print("write_html failed")
 | |
|         return self.write_output_html
 | |
| 
 | |
| 
 | |
|     # https://wkhtmltopdf.org/usage/wkhtmltopdf.txt
 | |
|     # page_size A4, A3, Letter, Legal
 | |
|     # orientation Portrait , Landscape
 | |
|     def write_pdf(self, _page_size = 'A4', _orientation = 'Portrait'):
 | |
|             # 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
 | |
|             
 | |
|             options = {"enable-local-file-access" : None,
 | |
|                         'orientation': _orientation,
 | |
|                         'page-size': _page_size}  # prevent error Blocked access to file
 | |
|             self.write_output_pdf = str(self.path_date_time)+'/'+ str(self.output_pdf)
 | |
|             pdfkit.from_file(self.write_output_html, self.write_output_pdf, options=options)
 | |
| 
 | |
|     # https://wkhtmltopdf.org/usage/wkhtmltopdf.txt
 | |
|     # page_size A4, A3, Letter, Legal
 | |
|     # orientation Portrait , Landscape
 | |
|     def write_pdf_with_timestamp(self, _page_size = 'A4', _orientation = 'Portrait'):
 | |
|             # 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
 | |
|             
 | |
|             options = {"enable-local-file-access" : None,
 | |
|                         'orientation': _orientation,
 | |
|                         'page-size': _page_size}  # prevent error Blocked access to file
 | |
|             self.write_output_pdf = "{}/{}-{}".format(self.path_date_time,self.date,self.output_pdf)
 | |
|             pdfkit.from_file(self.write_output_html, self.write_output_pdf, options=options)
 | |
| 
 | |
| 
 | |
|     def generate_report(self):
 | |
|         self.write_html()            
 | |
|         self.write_pdf()
 | |
| 
 | |
|     def build_all(self):
 | |
|         self.build_banner()
 | |
|         self.start_content_div()
 | |
|         self.build_table_title()
 | |
|         self.build_table()
 | |
|         self.end_content_div()
 | |
| 
 | |
|     def build_banner(self):
 | |
|         # NOTE: {{ }} are the ESCAPED curly braces
 | |
|         self.banner_html = """<!DOCTYPE html>
 | |
| <html lang='en'>
 | |
| <head>
 | |
|     <meta charset='UTF-8'>
 | |
|     <meta name='viewport' content='width=device-width, initial-scale=1' />
 | |
|     <style>
 | |
|     body {{ margin: 0; padding: 0; }}
 | |
|     </style>
 | |
|     <link rel='stylesheet' href='report.css' />
 | |
|     <link rel='stylesheet' href='custom.css' />
 | |
|     <title>{title}</title>
 | |
| </head>
 | |
| <body>
 | |
| <div id='BannerBack'>
 | |
|     <div id='Banner'>
 | |
|         <br/>
 | |
|         <img id='BannerLogo' align='right' src="CandelaLogo2-90dpi-200x90-trans.png" border='0' />
 | |
|         <div class='HeaderStyle'>
 | |
|             <h1 class='TitleFontPrint'>{title}</h1>
 | |
|             <h4 class='TitleFontPrintSub'>{date}</h4>
 | |
|         </div>
 | |
|     </div>
 | |
| </div>
 | |
| """.format(
 | |
|             title=self.title,
 | |
|             date=self.date,
 | |
|         )
 | |
|         self.html += self.banner_html
 | |
| 
 | |
|     def build_table_title(self):
 | |
|         self.table_title_html = "<h2 class='TitleFontPrint''>{title}</h2>".format(title=self.table_title)
 | |
|         self.html += self.table_title_html
 | |
| 
 | |
|     def start_content_div(self):
 | |
|         self.html += "\n<div class='contentDiv'>\n"
 | |
| 
 | |
|     def build_text(self):
 | |
|         # please do not use 'style=' tags unless you cannot override a class
 | |
|         self.text_html = """
 | |
|         <div class='HeaderStyle'>
 | |
|             <h3 class='TitleFontPrint'>{text}</h3>\n
 | |
|         </div>""".format(text=self.text)
 | |
|         self.html += self.text_html
 | |
| 
 | |
|     def build_date_time(self):
 | |
|         self.date_time = str(datetime.datetime.now().strftime("%Y-%m-%d-%H-h-%m-m-%S-s")).replace(':','-')
 | |
|         return self.date_time
 | |
| 
 | |
|     def build_path_date_time(self):
 | |
|         try: 
 | |
|             self.path_date_time = os.path.join(self.path,self.date_time)
 | |
|             os.mkdir(self.path_date_time)
 | |
|         except:
 | |
|             curr_dir_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))      
 | |
|             self.path_date_time = os.path.join(curr_dir_path,self.date_time)
 | |
|             os.mkdir(self.path_date_time)       
 | |
| 
 | |
|     def build_table(self):
 | |
|         self.dataframe_html = self.dataframe.to_html(index=False, justify='center')  # have the index be able to be passed in.
 | |
|         self.html += self.dataframe_html
 | |
| 
 | |
|     def build_custom(self):
 | |
|         self.html += self.custom_html
 | |
| 
 | |
|     def build_objective(self):
 | |
|         self.obj_html = """
 | |
|             <!-- Test Objective -->
 | |
|             <h3 align='left'>{title}</h3> 
 | |
|             <p align='left' width='900'>{objective}</p>
 | |
|             """.format(title=self.obj_title,
 | |
|                        objective=self.objective)
 | |
|         self.html += self.obj_html
 | |
| 
 | |
|     def build_graph_title(self):
 | |
|         self.table_graph_html = """
 | |
|             <div class='HeaderStyle'>
 | |
|                 <h2 class='TitleFontPrint' style='color:darkgreen;'>{title}</h2>
 | |
|             """.format(title=self.graph_title)
 | |
|         self.html += self.table_graph_html
 | |
| 
 | |
|     def build_graph(self):
 | |
|         self.graph_html_obj = """
 | |
|               <img align='center' style='padding:15px;margin:5px 5px 2em 5px;width:1000px;' src='{image}' border='1' />
 | |
|             """.format(image=self.graph_image)
 | |
|         self.html +=self.graph_html_obj
 | |
| 
 | |
|     def end_content_div(self):
 | |
|         self.html += "\n</div><!-- end contentDiv -->\n"
 | |
| 
 | |
| # Unit Test
 | |
| if __name__ == "__main__":
 | |
| 
 | |
|     # Testing: generate data frame 
 | |
|     dataframe = pd.DataFrame({
 | |
|     'product':['CT521a-264-1ac-1n','CT521a-1ac-1ax','CT522-264-1ac2-1n','CT523c-2ac2-db-10g-cu','CT523c-3ac2-db-10g-cu','CT523c-8ax-ac10g-cu','CT523c-192-2ac2-1ac-10g'],
 | |
|     'radios':[1,1,2,2,6,9,3],
 | |
|     'MIMO':['N','N','N','Y','Y','Y','Y'],
 | |
|     'stations':[200,64,200,128,384,72,192],
 | |
|     'mbps':[300,300,300,10000,10000,10000,10000]
 | |
|     })
 | |
| 
 | |
|     print(dataframe)
 | |
| 
 | |
|     # Testing: generate data frame 
 | |
|     dataframe2 = pd.DataFrame({
 | |
|      'station':[1,2,3,4,5,6,7],
 | |
|      'time_seconds':[23,78,22,19,45,22,25]
 | |
|     })
 | |
| 
 | |
|     report = lf_report()
 | |
|     report.set_title("Banner Title One")
 | |
|     report.build_banner()
 | |
| 
 | |
|     report.set_table_title("Title One")
 | |
|     report.build_table_title()
 | |
| 
 | |
|     report.set_table_dataframe(dataframe)
 | |
|     report.build_table()
 | |
| 
 | |
|     report.set_table_title("Title Two")
 | |
|     report.build_table_title()
 | |
| 
 | |
|     report.set_table_dataframe(dataframe2)
 | |
|     report.build_table()
 | |
| 
 | |
|     #report.build_all()
 | |
| 
 | |
|     html_file = report.write_html() 
 | |
|     print("returned file ")
 | |
|     print(html_file)
 | |
|     report.write_pdf()
 | |
| 
 | |
|     print("report path {}".format(report.get_path()))
 |