From 8f1101df131a4d3e03b377738507d88b745831c0 Mon Sep 17 00:00:00 2001 From: "Yiting.Li" Date: Tue, 22 Dec 2015 17:11:12 -0800 Subject: Upload the contribution of vstf as bottleneck network framework. End to End Performance test JIRA:BOTTLENECK-29 Change-Id: Ib2c553c8b60d6cda9e7a7b52b737c9139f706ebd Signed-off-by: Yiting.Li --- vstf/vstf/controller/reporters/report/__init__.py | 14 + .../controller/reporters/report/data_factory.py | 494 +++++++++++++ .../controller/reporters/report/html/__init__.py | 14 + .../controller/reporters/report/html/html_base.py | 42 ++ .../controller/reporters/report/html/html_text.py | 68 ++ .../reporters/report/html/htmlcreator.py | 117 +++ .../controller/reporters/report/pdf/__init__.py | 14 + .../controller/reporters/report/pdf/element.py | 781 +++++++++++++++++++++ .../controller/reporters/report/pdf/pdfcreator.py | 446 ++++++++++++ .../controller/reporters/report/pdf/pdftemplate.py | 107 +++ vstf/vstf/controller/reporters/report/pdf/story.py | 191 +++++ .../vstf/controller/reporters/report/pdf/styles.py | 198 ++++++ .../reporters/report/provider/__init__.py | 14 + .../reporters/report/provider/html_provider.py | 63 ++ 14 files changed, 2563 insertions(+) create mode 100755 vstf/vstf/controller/reporters/report/__init__.py create mode 100755 vstf/vstf/controller/reporters/report/data_factory.py create mode 100755 vstf/vstf/controller/reporters/report/html/__init__.py create mode 100755 vstf/vstf/controller/reporters/report/html/html_base.py create mode 100755 vstf/vstf/controller/reporters/report/html/html_text.py create mode 100755 vstf/vstf/controller/reporters/report/html/htmlcreator.py create mode 100755 vstf/vstf/controller/reporters/report/pdf/__init__.py create mode 100755 vstf/vstf/controller/reporters/report/pdf/element.py create mode 100755 vstf/vstf/controller/reporters/report/pdf/pdfcreator.py create mode 100755 vstf/vstf/controller/reporters/report/pdf/pdftemplate.py create mode 100755 vstf/vstf/controller/reporters/report/pdf/story.py create mode 100755 vstf/vstf/controller/reporters/report/pdf/styles.py create mode 100755 vstf/vstf/controller/reporters/report/provider/__init__.py create mode 100755 vstf/vstf/controller/reporters/report/provider/html_provider.py (limited to 'vstf/vstf/controller/reporters/report') diff --git a/vstf/vstf/controller/reporters/report/__init__.py b/vstf/vstf/controller/reporters/report/__init__.py new file mode 100755 index 00000000..89dcd4e2 --- /dev/null +++ b/vstf/vstf/controller/reporters/report/__init__.py @@ -0,0 +1,14 @@ +# Copyright Huawei Technologies Co., Ltd. 1998-2015. +# All Rights Reserved. +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an AS IS BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. diff --git a/vstf/vstf/controller/reporters/report/data_factory.py b/vstf/vstf/controller/reporters/report/data_factory.py new file mode 100755 index 00000000..39c534b6 --- /dev/null +++ b/vstf/vstf/controller/reporters/report/data_factory.py @@ -0,0 +1,494 @@ +#!/usr/bin/python +# -*- coding: utf8 -*- +# author: wly +# date: 2015-07-29 +# see license for license details +__version__ = ''' ''' + +from vstf.controller.database.dbinterface import DbManage + + +class DataProvider(object): + def __init__(self, taskid, dbase): + self._dbase = dbase + self._taskid = taskid + + +class CommonData(DataProvider): + def get_components(self): + result = [] + query = self._dbase.query_exten_info(self._taskid) + print "CommonData", query + for item in query: + if item[2]: + context = "%s:%s(%s)" % (item[0], item[1], item[2]) + else: + context = "%s:%s" % (item[0], item[1]) + result.append(context) + return result + + def get_software(self): + result = [ + " Host OS: ubuntu 14.04.2", + " Guest OS: ubuntu 12.04.4" + ] + return result + + def get_hardware(self): + result = [ + " Server: Dell R920", + " CPU: E7-8893/2P/3.4GHz/10-Cores/37.5M-L3C", + " MEM: 128G", + " NIC: Intel 82599" + ] + return result + + def get_taskname(self): + return self._dbase.query_taskname(self._taskid) + + def get_gitinfo_tabledata(self): + result = [] + return result + + def get_profileparameters_tabledData(self): + result = [ + ] + return result + + def get_testingoptions_tabledata(self): + result = [ + ] + return result + + def get_systeminfo_tabledata(self): + result = [ + ] + return result + + def get_systeminfo(self): + systable = [ + ['host', 'Server', 'CPU', 'MEM', 'NIC', 'OS'], + ] + query = self._dbase.query_task_host_list(self._taskid) + query = map(lambda x: list(x), query) + # rows = len(query) + # cols = len(zip(*query)) + # for i in range(rows): + # for j in range(cols): + # query[i][j] = query[i][j].replace('\",','\"\n') + systable += query + systable = map(lambda x: list(x), zip(*systable)) + return systable + + def get_introduct_tabledata(self): + result = [ + ["Type", "Case", "Name", "Direction", "Configure"] + ] + query = self._dbase.query_caseinfo() + result += map(lambda x: list(x), query) + return result + + def get_scenariolist(self): + query = self._dbase.query_scenariolist(self._taskid) + result = map(lambda x: list(x), zip(*query)) + if result: + return result[0] + else: + return result + + def is_scenario_start(self): + scenarioList = self.get_scenariolist() + print "scenarioList: ", scenarioList + if scenarioList: + return True + return False + + def get_contact(self): + result = [ + "Name: xxx", + "ID: xxxxxxxx", + "Email: xxxx@xxx.com" + ] + return result + + def get_casename(self, case): + return self._dbase.query_casename(case) + + def get_casefigure(self, case, tools): + return self._dbase.query_casefigure(case, tools) + + +class ScenarioData(DataProvider): + def __init__(self, taskid, dbase, scenario): + print "ScenarioData in" + DataProvider.__init__(self, taskid, dbase) + self._scenario = scenario + + def get_covertitle(self): + result = [ + "", + "", + "Elastic Virtual Switching Performance " + "Test Report", + "Scenario %s" % (self._scenario) + ] + return result + + def get_test(self): + result = [ + "Scenario: %s" % (self._scenario), + "Configuration: without VLAN", + ] + return result + + def get_test_tools(self, case): + query = self._dbase.query_casetools(self._taskid, case) + result = map(lambda x: list(x), query) + if result: + return result[0][0] + else: + return result + + def get_caselist(self): + query = self._dbase.query_caselist(self._taskid, self._scenario) + result = map(lambda x: list(x), zip(*query)) + if result: + return result[0] + else: + return result + + def is_provider_start(self, case, provider): + count = self._dbase.query_case_provider_count(self._taskid, case, provider) + if count: + return True + return False + + def is_type_provider_start(self, case, provider, ptype): + count = self._dbase.query_case_type_provider_count(self._taskid, case, provider, ptype) + if count: + return True + return False + + def is_type_start(self, case, ptype): + count = self._dbase.query_case_type_count(self._taskid, case, ptype) + if count: + return True + return False + + def is_throughput_start(self, case): + test_type = "throughput" + return self.is_type_start(case, test_type) + + def is_frameloss_start(self, case): + test_type = "frameloss" + return self.is_type_start(case, test_type) + + def is_latency_start(self, case): + test_type = "latency" + return self.is_type_start(case, test_type) + + def get_summary_throughput_data(self, case, provider): + test_type = "throughput" + return self.get_summary_tabledata(case, provider, test_type) + + def get_summary_frameLoss_data(self, case, provider): + test_type = "frameloss" + return self.get_summary_tabledata(case, provider, test_type) + + def get_summary_tabledata(self, case, provider, test_type, table_type='pdf'): + table_head = [] + table_body = [] + type_title = { + "frameloss": "Load", + "throughput": "Load" + } + tools = self.get_test_tools(case) + if "spirent" in tools: + table_body = self._dbase.query_summary_table(self._taskid, case, provider, test_type) + if 'pdf' == table_type: + table_head = [ + ["FrameSize (byte)", test_type, "", "", "", "Latency(uSec)", "", ""], + ["", " Mpps ", " " + type_title[test_type] + " (%) ", "CPU Used (%)", " Mpps/Ghz ", + " Min ", " Max ", " Avg "] + ] + else: + table_head = [ + ["FrameSize (byte)", " Mpps ", " " + type_title[test_type] + " (%) ", "CPU Used (%)", + " Mpps/Ghz ", "MinLatency(uSec)", "MaxLatency(uSec)", "AvgLatency(uSec)"], + ] + else: + table_body = self._dbase.query_summary_simpletable(self._taskid, case, provider, test_type) + if 'pdf' == table_type: + table_head = [ + ["FrameSize (byte)", test_type, "", "", "", "Latency(uSec)"], + ["", " Mpps ", " " + type_title[test_type] + " (%)", "CPU Used (%)", " Mpps/Ghz ", + " Avg "] + ] + else: + table_head = [ + ["FrameSize (byte)", " Mpps ", " " + type_title[test_type] + " (%) ", "CPU Used (%)", + " Mpps/Ghz ", "AvgLatency(uSec)"], + ] + return table_head + table_body + + def get_tabledata(self, case, test_type, item): + type_dict = { + "FrameSize": "FrameSize (byte)", + "fastlink": "fastlink", + "l2switch": "l2switch", + "rdp": "kernel rdp", + "line": "line speed" + } + item_dict = { + "Percent": " ", + "Mpps": " ", + "Avg": " ", + } + provider_list = ["fastlink", "rdp", "l2switch"] + table = [] + line_speed = 20.0 if case in ["Tn-2v", "Tn-2"] else 10.0 + + for provider in provider_list: + if self.is_provider_start(case, provider): + if item == 'Percent': + query = self._dbase.query_load(self._taskid, case, provider, test_type) + elif item == 'Mpps': + query = self._dbase.query_bandwidth(self._taskid, case, provider, test_type) + else: + query = self._dbase.query_avglatency(self._taskid, case, provider, test_type) + query = map(lambda x: list(x), zip(*query)) + if query: + table_head = [[type_dict["FrameSize"]] + map(lambda x: " %4d " % (x), query[0])] + if item == "Avg": + data = map(lambda x: item_dict[item] + "%.1f" % (x) + item_dict[item], query[1]) + else: + data = map(lambda x: item_dict[item] + "%.2f" % (x) + item_dict[item], query[1]) + if item == "Mpps": + line_table = map(lambda x: "%.2f" % (line_speed * 1000 / (8 * (x + 20))), query[0]) + table.append([type_dict[provider]] + data) + if table: + if item == "Mpps": + table.append([type_dict["line"]] + line_table) + table = table_head + table + return table + + def get_frameloss_tabledata(self, case, test_type): + item = "Percent" + table = self.get_tabledata(case, test_type, item) + return table + + def get_frameloss_chartdata(self, case, test_type): + result = self.get_frameloss_tabledata(case, test_type) + result = map(list, zip(*result)) + return result + + def get_framerate_tabledata(self, case, test_type): + item = "Mpps" + table = self.get_tabledata(case, test_type, item) + return table + + def get_framerate_chartdata(self, case, test_type): + result = self.get_framerate_tabledata(case, test_type) + result = map(list, zip(*result)) + return result + + def get_latency_tabledata(self, case): + test_type = "latency" + item = "Avg" + table = self.get_tabledata(case, test_type, item) + return table + + def get_latency_chartdata(self, case): + result = self.get_latency_tabledata(case) + result = map(list, zip(*result)) + return result + + def get_latency_bardata(self, case): + table_data = self.get_latency_tabledata(case) + result = [] + if table_data: + ytitle = "Average Latency (uSec)" + category_names = map(lambda x: "FS:%4d" % int(float(x)) + "LOAD:50", table_data[0][1:]) + bar_ = map(lambda x: x[0], table_data[1:]) + data = map(lambda x: x[1:], table_data[1:]) + result = [ytitle, category_names, bar_, data] + return result + + def get_bardata(self, case, provider, test_type): + if test_type == "latency": + query = self._dbase.query_avglatency(self._taskid, case, provider, test_type) + item = "Avg" + else: + query = self._dbase.query_load(self._taskid, case, provider, test_type) + item = "Percent" + + title_dict = { + "Avg": "Latency (uSec)", + "Percent": test_type + " (%)" + } + name_dict = { + "Avg": " LOAD:50", + "Percent": " OF:100 " + } + color_dict = { + "Avg": "latency", + "Percent": "loss" + } + ytitle = title_dict[item] + query = map(lambda x: list(x), zip(*query)) + result = [] + if query: + category_names = map(lambda x: "FS:%4d" % x + name_dict[item], query[0]) + data = query[1:] + bar_ = [color_dict[item]] + result = [ytitle, category_names, bar_, data] + return result + + +class TaskData(object): + def __init__(self, taskid, dbase): + self.__common = CommonData(taskid, dbase) + scenario_list = self.__common.get_scenariolist() + scenario_dic = {} + for scenario in scenario_list: + scenario_dic[scenario] = ScenarioData(taskid, dbase, scenario) + self.__dict__.update(scenario_dic) + + @property + def common(self): + return self.__common + + +class HistoryData(DataProvider): + def get_data(self, task_list, case, provider, ttype, item): + """ + @provider in ["fastlink", "rdp", "l2switch"] + @ttype in ["throughput", "frameloss", "latency"] + @item in ["avg", "ratep", "load"] + """ + table = [] + table_head = [] + datas = [] + sizes = [] + for taskid in task_list: + if item == 'ratep': + query = self._dbase.query_bandwidth(taskid, case, provider, ttype) + else: + query = self._dbase.query_avglatency(taskid, case, provider, ttype) + + if query: + data = {} + for size, value in query: + data[size] = value + sizes.extend(data.keys()) + sizes = {}.fromkeys(sizes).keys() + sizes.sort() + datas.append({taskid: data}) + + result = [] + for data in datas: + print data + taskid = data.keys()[0] + data_th = self._dbase.query_taskdate(taskid) + testdata = data[taskid] + item = [data_th] + for size in sizes: + item.append(str(testdata.get(size, ''))) + result.append(item) + + if result: + head_th = "FrameSize (byte)" + table_head = [[head_th] + map(lambda x: " %4d " % (x), sizes)] + table = table_head + result + + return table + + def get_tasklist(self, count=5): + task_list = [] + query = self._dbase.query_tasklist() + if query: + for item in query: + if item.TaskID <= self._taskid: + task_list.append(item.TaskID) + + task_list = task_list[-count:] + return task_list + + def get_history_info(self, case): + providers = ["fastlink", "rdp", "l2switch"] + provider_dict = {"fastlink": "Fast Link ", "l2switch": "L2Switch ", "rdp": "Kernel RDP "} + ttype_dict = { + "throughput": "Throughput Testing ", + "frameloss": "Frame Loss Testing ", + "latency": "Latency Testing " + } + + items_dict = { + "ratep": "RX Frame Rate(Mpps) ", + "avg": "Average Latency (uSec) " + } + + task_list = self.get_tasklist() + result = [] + + ttypes = ["throughput", "frameloss", "latency"] + for ttype in ttypes: + content = {} + if ttype == "latency": + item = "avg" + else: + item = "ratep" + + for provider in providers: + table_data = self.get_data(task_list, case, provider, ttype, item) + if table_data: + data = { + "title": provider_dict[provider] + items_dict[item], + "data": table_data + } + content["title"] = ttype_dict[ttype] + content.setdefault("data", []) + content["data"].append(data) + if content: + result.append(content) + print "xxxxxxxxxxxxxx" + print result + print "xxxxxxxxxxxxxx" + return result + + +def unit_test(): + dbase = DbManage() + taskid = dbase.get_last_taskid() + hdata = HistoryData(taskid, dbase) + task_list = hdata.get_tasklist() + + cdata = CommonData(taskid, dbase) + scenario_list = cdata.get_scenariolist() + print scenario_list + + scenario = "Tn" + sdata = ScenarioData(taskid, dbase, scenario) + + case_list = sdata.get_caselist() + print case_list + + case = "Tn-1" + + providers = ["fastlink", "rdp", "l2switch"] + ttypes = ["throughput", "frameloss"] + items = ["ratep", "load"] + + for provider in providers: + for ttype in ttypes: + for item in items: + print provider + print ttype + print item + print hdata.get_data(task_list, case, provider, ttype, item) + + hdata.get_history_info(case) + + +if __name__ == '__main__': + unit_test() diff --git a/vstf/vstf/controller/reporters/report/html/__init__.py b/vstf/vstf/controller/reporters/report/html/__init__.py new file mode 100755 index 00000000..89dcd4e2 --- /dev/null +++ b/vstf/vstf/controller/reporters/report/html/__init__.py @@ -0,0 +1,14 @@ +# Copyright Huawei Technologies Co., Ltd. 1998-2015. +# All Rights Reserved. +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an AS IS BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. diff --git a/vstf/vstf/controller/reporters/report/html/html_base.py b/vstf/vstf/controller/reporters/report/html/html_base.py new file mode 100755 index 00000000..270ef394 --- /dev/null +++ b/vstf/vstf/controller/reporters/report/html/html_base.py @@ -0,0 +1,42 @@ +#!/usr/bin/python +# -*- coding: utf8 -*- +# author: wly +# date: 2015-09.25 +# see license for license details +__version__ = ''' ''' + +import os +from vstf.common.pyhtml import * + + +class HtmlBase(object): + def __init__(self, provider, ofile='text.html'): + self._page = PyHtml('HtmlBase Text') + self._ofile = ofile + self._provider = provider + self._chapter = 1 + + def save(self): + if self._ofile: + os.system('rm -rf %s' % self._ofile) + self._page.output(self._ofile) + + def as_string(self): + return self._page.as_string() + + def add_table(self, data): + self._page.add_table(data) + + def add_style(self): + style = self._provider.get_style() + self._page.add_style(style) + + def create(self, is_save=True): + self.add_style() + self.create_story() + if is_save: + self.save() + return self.as_string() + + def create_story(self): + raise NotImplementedError("abstract HtmlBase") diff --git a/vstf/vstf/controller/reporters/report/html/html_text.py b/vstf/vstf/controller/reporters/report/html/html_text.py new file mode 100755 index 00000000..86505b8f --- /dev/null +++ b/vstf/vstf/controller/reporters/report/html/html_text.py @@ -0,0 +1,68 @@ +#!/usr/bin/python +# -*- coding: utf8 -*- +# author: wly +# date: 2015-09-24 +# see license for license details +__version__ = ''' ''' + +import logging + +LOG = logging.getLogger(__name__) +import vstf.common.constants as cst +from vstf.controller.reporters.report.html.html_base import * + + +class HtmlCreator(HtmlBase): + def add_subject(self): + title = self._provider.get_subject() + self._page << H1(title) + + def add_ovs(self): + title = "%s %s" % (self._chapter, self._provider.get_ovs_title()) + self._page << H2(title) + data = self._provider.get_ovs_table() + self.add_table(data) + self._chapter += 1 + + def add_result(self): + title = "%s %s" % (self._chapter, self._provider.get_result_title()) + self._page << H2(title) + + section = 1 + for ttype in cst.TTYPES: + data = self._provider.get_result_table(ttype) + if data: + title = "%s.%s %s" % (self._chapter, section, ttype.title()) + self._page << H3(title) + self.add_table(data) + section += 1 + self._chapter += 1 + + def create_story(self): + self.add_subject() + self.add_ovs() + self.add_result() + + +def unit_test(): + from vstf.common.log import setup_logging + setup_logging(level=logging.DEBUG, log_file="/var/log/html-test.log", clevel=logging.INFO) + + from vstf.controller.settings.html_settings import HtmlSettings + from vstf.controller.settings.data_settings import DataSettings + + html_settings = HtmlSettings() + LOG.info(html_settings.settings) + data_settings = DataSettings() + LOG.info(data_settings.settings) + + from vstf.controller.reporters.report.provider.html_provider import HtmlProvider + provider = HtmlProvider(data_settings.settings, html_settings.settings) + html = HtmlCreator(provider) + + result = html.create() + print result + + +if __name__ == '__main__': + unit_test() diff --git a/vstf/vstf/controller/reporters/report/html/htmlcreator.py b/vstf/vstf/controller/reporters/report/html/htmlcreator.py new file mode 100755 index 00000000..e6c75caf --- /dev/null +++ b/vstf/vstf/controller/reporters/report/html/htmlcreator.py @@ -0,0 +1,117 @@ +#!/usr/bin/python +# -*- coding: utf8 -*- +# author: wly +# date: 2015-08-04 +# see license for license details +__version__ = ''' ''' + +import logging + +from vstf.controller.reporters.report.data_factory import TaskData +from vstf.controller.database.dbinterface import DbManage +from vstf.controller.reporters.report.html.html_base import * + +LOG = logging.getLogger(__name__) + + +class HtmlvSwitchCreator(HtmlBase): + def __init__(self, task_data, provider, ofile='creator.html'): + HtmlBase.__init__(self, provider, ofile) + self._task = task_data + self._table_type = 'html' + + def create_story(self): + self.add_subject() + self.add_gitinfo() + self.add_envinfo() + self.add_scenarios() + + def add_subject(self): + job_name = "JOB_NAME: " + self._task.common.get_taskname() + self._page << H2(job_name) + + def add_gitinfo(self): + self._page << H2("Trigger and Repository Info") + + git_table = self._task.common.get_gitinfo_tabledata() + if git_table: + self.add_table(git_table) + + def add_envinfo(self): + self._page << H2("System Environment Information") + env_table = self._task.common.get_systeminfo() + LOG.info(env_table) + if env_table: + self.add_table(env_table) + + def add_scenarios(self): + scenario_list = self._task.common.get_scenariolist() + self._page << H2("Scenario List: " + ', '.join(scenario_list)) + for scenario in scenario_list: + self._page << H2("Scenario: " + scenario) + data = getattr(self._task, scenario) + self.add_scenario(data) + + def add_scenario(self, scenario_data): + case_list = scenario_data.get_caselist() + for case in case_list: + self.add_case(scenario_data, case) + + def add_case(self, scenario_data, case): + case_name = self._task.common.get_casename(case) + title = "Case : %s (%s)" % (case, case_name) + self._page << H2(title) + + provider_list = ["fastlink", "rdp", "l2switch"] + provider_dict = {"fastlink": "Fast Link", "l2switch": "L2Switch", "rdp": "Kernel RDP"} + + for provider in provider_list: + if scenario_data.is_provider_start(case, provider): + title = " %s (%s_%s)" % (provider_dict[provider], case_name, provider) + self._page << H3(title) + test_types = ["throughput", "frameloss"] + for test_type in test_types: + if scenario_data.is_type_provider_start(case, provider, test_type): + self.add_casedata(scenario_data, case, provider, test_type) + + if scenario_data.is_latency_start(case): + self.add_latency_result(scenario_data, case) + + def add_casedata(self, scenario_data, case, provider, test_type): + table_content = scenario_data.get_summary_tabledata(case, provider, test_type, self._table_type) + if table_content: + title = "Test type:%s" % (test_type) + self._page << H4(title) + self.add_table(table_content) + + def add_latency_result(self, scenario_data, case): + title = "Average Latency Summary" + table_content = scenario_data.get_latency_tabledata(case) + if table_content: + self._page << H2(title) + self.add_table(table_content) + + +def unit_test(): + from vstf.common.log import setup_logging + setup_logging(level=logging.DEBUG, log_file="/var/log/html-creator.log", clevel=logging.INFO) + + dbase = DbManage() + taskid = dbase.get_last_taskid() + task_data = TaskData(taskid, dbase) + + from vstf.controller.settings.html_settings import HtmlSettings + from vstf.controller.reporters.report.provider.html_provider import StyleProvider + + html_settings = HtmlSettings() + LOG.info(html_settings.settings) + + provider = StyleProvider(html_settings.settings) + html = HtmlvSwitchCreator(task_data, provider) + + result = html.create(True) + print result + + +if __name__ == '__main__': + unit_test() diff --git a/vstf/vstf/controller/reporters/report/pdf/__init__.py b/vstf/vstf/controller/reporters/report/pdf/__init__.py new file mode 100755 index 00000000..89dcd4e2 --- /dev/null +++ b/vstf/vstf/controller/reporters/report/pdf/__init__.py @@ -0,0 +1,14 @@ +# Copyright Huawei Technologies Co., Ltd. 1998-2015. +# All Rights Reserved. +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an AS IS BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. diff --git a/vstf/vstf/controller/reporters/report/pdf/element.py b/vstf/vstf/controller/reporters/report/pdf/element.py new file mode 100755 index 00000000..2528f2c5 --- /dev/null +++ b/vstf/vstf/controller/reporters/report/pdf/element.py @@ -0,0 +1,781 @@ +#!/usr/bin/python +# -*- coding: utf8 -*- +# author: wly +# date: 2015-05-04 +# see license for license details +__version__ = ''' ''' +__doc__ = """ +it contains the base element for pdf +eImage is used to draw picture on the pdf document +eDataTable is used to draw table on the pdf document +eGraphicsTable is used to draw plot on the pdf document +eParagraph is used to draw text on the pdf document +""" +from reportlab.platypus import Image, Table +from reportlab.graphics.shapes import Drawing +from reportlab.graphics.charts.lineplots import LinePlot +from reportlab.graphics.charts.linecharts import HorizontalLineChart +from reportlab.platypus.paragraph import Paragraph +from reportlab.graphics.widgets.markers import makeMarker +from reportlab.graphics.charts.legends import Legend +from reportlab.graphics.charts.textlabels import Label +from reportlab.graphics.charts.axes import XValueAxis +from reportlab.graphics.shapes import Group +from reportlab.graphics.charts.barcharts import VerticalBarChart +from vstf.controller.reporters.report.pdf.styles import * + + +class eImage(Image): + """ an image(digital picture)which contains the function of auto zoom picture """ + + def __init__(self, filename, width=None, height=None, kind='direct', mask="auto", lazy=1, hAlign='CENTRE', + vAlign='BOTTOM'): + Image.__init__(self, filename, None, None, kind, mask, lazy) + print height, width + print self.drawHeight, self.drawWidth + if self.drawWidth * height > self.drawHeight * width: + self.drawHeight = width * self.drawHeight / self.drawWidth + self.drawWidth = width + else: + self.drawWidth = height * self.drawWidth / self.drawHeight + self.drawHeight = height + self.hAlign = hAlign + self.vAlign = vAlign + print self.drawHeight, self.drawWidth + + +class eTable(object): + """ an abstract table class, which is contains the base functions to create table """ + + def __init__(self, data, style=TableStyle(name="default")): + self._tablestyle = style + self._table = [] + self._spin = False + self._colWidths = None + self._data = self.analysisData(data) + if self._data: + self.create() + + def analysisData(self, data): + raise NotImplementedError("abstract eTable") + + def create(self): + self._table = Table(self._data, style=self._style, splitByRow=1) + self._table.hAlign = self._tablestyle.table_hAlign + self._table.vAlign = self._tablestyle.table_vAlign + self._table.colWidths = self._tablestyle.table_colWidths + if self._spin or self._colWidths: + self._table.colWidths = self._colWidths + self._table.rowHeights = self._tablestyle.table_rowHeights + + @property + def table(self): + return self._table + + +class eCommonTable(eTable): + def analysisData(self, data): + self._style = [ + ('ALIGN', (0, 0), (-1, -1), 'CENTER'), + ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), + ('GRID', (0, 0), (-1, -1), 0.5, colors.grey), + ('BOX', (0, 0), (-1, -1), 1.2, colors.black) + ] + return data + + +class eConfigTable(eTable): + def analysisData(self, data): + self._style = [ + ('ALIGN', (0, 0), (-1, -1), 'CENTER'), + ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), + ('GRID', (0, 0), (-1, -1), 0.5, colors.grey), + ('BOX', (0, 0), (-1, -1), 1, colors.black), + ('SPAN', (2, 0), (3, 0)), + ('SPAN', (2, 1), (3, 1)), + ('SPAN', (2, 8), (3, 8)), + ('SPAN', (2, 9), (3, 9)), + ('SPAN', (2, 10), (3, 10)), + ('SPAN', (0, 0), (0, 7)), + ('SPAN', (0, 8), (0, 10)), + ('SPAN', (0, 11), (0, 19)), + ('SPAN', (1, 2), (1, 6)), + ('SPAN', (1, 12), (1, 13)), + ('SPAN', (1, 14), (1, 16)), + ('SPAN', (1, 17), (1, 19)), + ('SPAN', (2, 3), (2, 6)) + ] + return data + + +class eSummaryTable(eTable): + def analysisData(self, data): + self._style = [ + ('ALIGN', (0, 0), (-1, -1), 'CENTER'), + ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), + ('GRID', (0, 0), (-1, -1), 0.5, colors.grey), + ('BOX', (0, 0), (-1, -1), 1, colors.black), + ('SPAN', (0, 0), (0, 1)), + ('SPAN', (1, 0), (4, 0)), + ('SPAN', (5, 0), (-1, 0)) + ] + return data + + +class eGitInfoTable(eTable): + def analysisData(self, data): + self._style = [ + ('ALIGN', (0, 0), (-1, -1), 'CENTER'), + ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), + ('GRID', (0, 0), (-1, -1), 0.5, colors.grey), + ('BOX', (0, 0), (-1, -1), 1, colors.black), + ('SPAN', (0, 0), (0, 2)), + ('SPAN', (0, 3), (0, 5)), + ('SPAN', (0, 6), (0, 8)) + ] + return data + + +class eScenarioTable(eTable): + def analysisData(self, data): + self._style = [ + ('ALIGN', (0, 0), (-1, -1), 'CENTER'), + ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), + ('GRID', (0, 0), (-1, -1), 0.5, colors.grey), + ('BOX', (0, 0), (-1, -1), 1, colors.black), + ('ALIGN', (2, 1), (-1, -1), 'LEFT'), + ('SPAN', (0, 1), (0, 6)), + ('SPAN', (0, 7), (0, 12)), + ('SPAN', (0, 13), (0, 16)), + ('SPAN', (0, 17), (0, 20)) + ] + return data + + +class eOptionsTable(eTable): + def analysisData(self, data): + self._style = [ + ('ALIGN', (0, 0), (-1, -1), 'CENTER'), + ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), + ('GRID', (0, 0), (-1, -1), 0.5, colors.grey), + ('BOX', (0, 0), (-1, -1), 1, colors.black), + ('SPAN', (2, 0), (4, 0)), + ('SPAN', (2, 1), (4, 1)), + ('SPAN', (0, 0), (0, -1)), + ('SPAN', (1, 2), (1, 16)), + ('SPAN', (1, 17), (1, 19)), + ('SPAN', (1, 20), (1, 22)), + ('SPAN', (1, 23), (1, 24)), + ('SPAN', (2, 2), (2, 4)), + ('SPAN', (2, 5), (2, 12)), + ('SPAN', (2, 13), (2, 16)), + ('SPAN', (2, 17), (2, 19)), + ('SPAN', (2, 20), (2, 22)), + ('SPAN', (2, 23), (2, 24)) + ] + return data + + +class eProfileTable(eTable): + def analysisData(self, data): + self._style = [ + ('ALIGN', (0, 0), (-1, -1), 'CENTER'), + ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), + ('GRID', (0, 0), (-1, -1), 0.5, colors.grey), + ('BOX', (0, 0), (-1, -1), 1, colors.black), + ('SPAN', (0, 1), (0, -1)), + ('SPAN', (1, 0), (2, 0)), + ] + return data + + +class eDataTable(eTable): + def analysisData(self, data): + result = data + self._style = [ + ('ALIGN', (0, 0), (-1, -1), 'CENTER'), + ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), + ('LEADING', (0, 0), (-1, -1), 18), + ('GRID', (0, 0), (-1, -1), 0.5, colors.grey), + ('BOX', (0, 0), (-1, -1), 1, colors.black), + ('LINEBEFORE', (1, 0), (1, -1), 0.8, colors.black), + # ('LINEBEFORE', (3, 0), (3, -1), 1, colors.black), + # ('LINEBEFORE', (5, 0), (5, -1), 1, colors.black), + ('LINEBELOW', (0, 0), (-1, 0), 0.8, colors.black), + # ('SPAN', (0, 0), (0, 1)), + # ('SPAN', (1, 0), (2, 0)), + # ('SPAN', (3, 0), (4, 0)) + ] + if self._spin is True: + print "start spin" + result = map(list, zip(*result)) + style = [] + for value in self._style: + value = list(value) + value[1] = (value[1][1], value[1][0]) + value[2] = (value[2][1], value[2][0]) + if value[0] == 'LINEBELOW': + value[0] = 'LINEAFTER' + elif value[0] == 'LINEBEFORE': + value[0] = 'LINEABOVE' + value = tuple(value) + style.append(value) + self._style = style + return result + + +class eGraphicsTable(eTable): + def analysisData(self, data): + self._style = [ + ('ALIGN', (0, 0), (-1, -1), 'CENTER'), + ('VALIGN', (0, 0), (-1, -1), 'MIDDLE') + ] + return data + + +class noScaleXValueAxis(XValueAxis): + def __init__(self): + XValueAxis.__init__(self) + + def makeTickLabels(self): + g = Group() + if not self.visibleLabels: return g + + f = self._labelTextFormat # perhaps someone already set it + if f is None: + f = self.labelTextFormat or (self._allIntTicks() and '%.0f' or str) + elif f is str and self._allIntTicks(): + f = '%.0f' + elif hasattr(f, 'calcPlaces'): + f.calcPlaces(self._tickValues) + post = self.labelTextPostFormat + scl = self.labelTextScale + pos = [self._x, self._y] + d = self._dataIndex + pos[1 - d] = self._labelAxisPos() + labels = self.labels + if self.skipEndL != 'none': + if self.isXAxis: + sk = self._x + else: + sk = self._y + if self.skipEndL == 'start': + sk = [sk] + else: + sk = [sk, sk + self._length] + if self.skipEndL == 'end': + del sk[0] + else: + sk = [] + + nticks = len(self._tickValues) + nticks1 = nticks - 1 + for i, tick in enumerate(self._tickValues): + label = i - nticks + if label in labels: + label = labels[label] + else: + label = labels[i] + if f and label.visible: + v = self.scale(i) + if sk: + for skv in sk: + if abs(skv - v) < 1e-6: + v = None + break + if v is not None: + if scl is not None: + t = tick * scl + else: + t = tick + if isinstance(f, str): + txt = f % t + elif isSeq(f): + # it's a list, use as many items as we get + if i < len(f): + txt = f[i] + else: + txt = '' + elif hasattr(f, '__call__'): + if isinstance(f, TickLabeller): + txt = f(self, t) + else: + txt = f(t) + else: + raise ValueError('Invalid labelTextFormat %s' % f) + if post: txt = post % txt + pos[d] = v + label.setOrigin(*pos) + label.setText(txt) + + # special property to ensure a label doesn't project beyond the bounds of an x-axis + if self.keepTickLabelsInside: + if isinstance(self, XValueAxis): # not done yet for y axes + a_x = self._x + if not i: # first one + x0, y0, x1, y1 = label.getBounds() + if x0 < a_x: + label = label.clone(dx=label.dx + a_x - x0) + if i == nticks1: # final one + a_x1 = a_x + self._length + x0, y0, x1, y1 = label.getBounds() + if x1 > a_x1: + label = label.clone(dx=label.dx - x1 + a_x1) + g.add(label) + + return g + + def ___calcScaleFactor(self): + """Calculate the axis' scale factor. + This should be called only *after* the axis' range is set. + Returns a number. + """ + self._scaleFactor = self._length / (len(self._tickValues) + 1) + return self._scaleFactor + + def scale(self, value): + """Converts a numeric value to a plotarea position. + The chart first configures the axis, then asks it to + """ + assert self._configured, "Axis cannot scale numbers before it is configured" + if value is None: value = 0 + # this could be made more efficient by moving the definition of org and sf into the configuration + org = (self._x, self._y)[self._dataIndex] + sf = self._length / (len(self._tickValues) + 1) + if self.reverseDirection: + sf = -sf + org += self._length + return org + sf * (value + 1) + + +class noScaleLinePlot(LinePlot): + def __init__(self): + LinePlot.__init__(self) + self.xValueAxis = noScaleXValueAxis() + + def calcPositions(self): + """Works out where they go. + + Sets an attribute _positions which is a list of + lists of (x, y) matching the data. + """ + self._seriesCount = len(self.data) + self._rowLength = max(map(len, self.data)) + + self._positions = [] + for rowNo in range(len(self.data)): + line = [] + len_row = len(self.data[rowNo]) + for colNo in range(len_row): + datum = self.data[rowNo][colNo] # x, y value + x = self.x + self.width / (len_row + 1) * (colNo + 1) + self.xValueAxis.labels[colNo].x = self.x + self.width / (len_row + 1) * (colNo + 1) + y = self.yValueAxis.scale(datum[1]) + # print self.width, " ", x + line.append((x, y)) + self._positions.append(line) + + +# def _innerDrawLabel(self, rowNo, colNo, x, y): +# return None +class eLinePlot(object): + def __init__(self, data, style): + self._lpstyle = style + self._linename = data[0] + self._data = self.analysisData(data[1:]) + if self._data: + self.create() + + @property + def draw(self): + return self._draw + + def analysisData(self, data): + columns = len(data) + # print data + data = map(list, zip(*data)) + rows = len(data) + + for i in range(rows): + for j in range(columns): + data[i][j] = float(data[i][j]) + self._linename = self._linename[1:] + """ + delcnt = 0 + delrows = [] + for i in range(columns): + delrows.append(0.0) + del_line = [self._linename[0]] + for i in range(rows): + for j in range(columns): + data[i][j] = float(data[i][j]) + if data[i] == delrows: + delcnt += 1 + del_line.append(self._linename[i]) + for i in range(delcnt): + data.remove(delrows) + for name in del_line: + self._linename.remove(name) + + rows = len(data) + """ + # print rows + # print data + xvalueSteps = data[0] + xvalueMin = data[0][0] + xvalueMax = data[0][0] + yvalueMin = data[1][0] + yvalueMax = data[1][0] + yvalueSteps = [] + result = [] + for j in range(columns): + if xvalueMin > data[0][j]: + xvalueMin = data[0][j] + if xvalueMax < data[0][j]: + xvalueMax = data[0][j] + + for i in range(rows - 1): + lst = [] + for j in range(columns): + lst.append((data[0][j], data[i + 1][j])) + if yvalueMin > data[i + 1][j]: + yvalueMin = data[i + 1][j] + if yvalueMax < data[i + 1][j]: + yvalueMax = data[i + 1][j] + yvalueSteps.append(int(data[i + 1][j] * 2.5) / 2.5) + result.append(tuple(lst)) + xvalueMin = int(xvalueMin) / 100 * 100 + xvalueMax = int(xvalueMax) / 100 * 100 + 200 + yvalueMin = int(yvalueMin) * 1.0 - 1 + if yvalueMin < 0: + yvalueMin = 0.0 + yvalueMax = int(yvalueMax) + 2.0 + yvalueSteps.append(yvalueMin) + yvalueSteps.append(yvalueMax) + yvalueSteps = {}.fromkeys(yvalueSteps).keys() + + self._xvalue = (xvalueMin, xvalueMax, xvalueSteps) + self._yvalue = (yvalueMin, yvalueMax, yvalueSteps) + print result + return result + + def create(self): + lpw = self._lpstyle.width + lph = self._lpstyle.height + draw = Drawing(lpw, lph) + line_cnts = len(self._linename) + # lp = noScaleLinePlot() + lp = LinePlot() + lg_line = (line_cnts + 3) / 4 + lp.x = self._lpstyle.left + lp.y = self._lpstyle.bottom + + lp.height = lph - self._lpstyle.bottom * (lg_line + 1.5) + lp.width = lpw - lp.x * 2 + lp.data = self._data + lp.joinedLines = 1 + lp.strokeWidth = self._lpstyle.strokeWidth + line_cnts = len(self._data) + sytle_cnts = len(self._lpstyle.linestyle) + color_paris = [] + for i in range(line_cnts): + styleIndex = i % sytle_cnts + lp.lines[i].strokeColor = self._lpstyle.linestyle[styleIndex][0] + lp.lines[i].symbol = makeMarker(self._lpstyle.linestyle[styleIndex][1]) + lp.lines[i].strokeWidth = self._lpstyle.linestyle[styleIndex][2] + color_paris.append((self._lpstyle.linestyle[styleIndex][0], self._linename[i])) + # lp.lineLabels[i].strokeColor = self._lpstyle.linestyle[styleIndex][0] + + lp.lineLabelFormat = self._lpstyle.format[0] + + lp.strokeColor = self._lpstyle.strokeColor + + lp.xValueAxis.valueMin, lp.xValueAxis.valueMax, lp.xValueAxis.valueSteps = self._xvalue + # valueMin, valueMax, xvalueSteps = self._xvalue + # lp.xValueAxis.valueStep = (lp.xValueAxis.valueMax - lp.xValueAxis.valueMin)/len(xvalueSteps) + # lp.xValueAxis.valueSteps = map(lambda x: str(x), xvalueSteps) + + lp.yValueAxis.valueMin, lp.yValueAxis.valueMax, lp.yValueAxis.valueSteps = self._yvalue + + + + # lp.xValueAxis.forceZero = 0 + # lp.xValueAxis.avoidBoundFrac = 1 + # lp.xValueAxis.tickDown = 3 + # lp.xValueAxis.visibleGrid = 1 + # lp.xValueAxis.categoryNames = '64 256 512 1400 1500 4096'.split(' ') + + lp.xValueAxis.labelTextFormat = self._lpstyle.format[1] + lp.yValueAxis.labelTextFormat = self._lpstyle.format[2] + + delsize = int(lp.xValueAxis.valueMax / 2000) + lp.xValueAxis.labels.fontSize = self._lpstyle.labelsfont + lp.xValueAxis.labels.angle = 25 + + lp.yValueAxis.labels.fontSize = self._lpstyle.labelsfont + lp.lineLabels.fontSize = self._lpstyle.labelsfont - delsize + draw.add(lp) + + lg = Legend() + lg.colorNamePairs = color_paris + lg.fontName = 'Helvetica' + lg.fontSize = 7 + + lg.x = self._lpstyle.left * 3 + lg.y = self._lpstyle.bottom * (1 + lg_line) + lp.height + + lg.dxTextSpace = 5 + lg.dy = 5 + lg.dx = 20 + lg.deltax = 60 + lg.deltay = 0 + lg.columnMaximum = 1 + lg.alignment = 'right' + draw.add(lg) + self._draw = draw + + +class eHorizontalLineChart(object): + def __init__(self, data, style): + self._lcstyle = style + if len(data) < 1: + return + self._linename = data[0] + self._data = self.analysisData(data[1:]) + if self._data: + self.create() + + @property + def draw(self): + return self._draw + + def analysisData(self, data): + columns = len(data) + data = map(list, zip(*data)) + self._catNames = data[0] + self._linename = self._linename[1:] + data = data[1:] + rows = len(data) + + yvalueMin = float(data[0][0]) + yvalueMax = float(data[0][0]) + yvalueSteps = [] + result = [] + + for rowNo in range(rows): + for columnNo in range(columns): + data[rowNo][columnNo] = float(data[rowNo][columnNo]) + if yvalueMin > data[rowNo][columnNo]: + yvalueMin = data[rowNo][columnNo] + if yvalueMax < data[rowNo][columnNo]: + yvalueMax = data[rowNo][columnNo] + yvalueSteps.append(int(data[rowNo][columnNo] * 1.0) / 1.0) + result.append(tuple(data[rowNo])) + + yvalueMin = int(yvalueMin) * 1.0 - 1 + if yvalueMin < 0: + yvalueMin = 0.0 + yvalueMax = int(yvalueMax) + 2.0 + yvalueSteps.append(yvalueMin) + yvalueSteps.append(yvalueMax) + yvalueSteps = {}.fromkeys(yvalueSteps).keys() + + self._value = (yvalueMin, yvalueMax, yvalueSteps) + print result + return result + + def create(self): + dw = self._lcstyle.width + dh = self._lcstyle.height + draw = Drawing(dw, dh) + + lc = HorizontalLineChart() + line_cnts = len(self._linename) + + lg_line = (line_cnts + 3) / 4 + lc.height = dh - self._lcstyle.bottom * (lg_line + 1.5) + lc.width = dw - lc.x * 2 + lc.x = self._lcstyle.left + lc.y = self._lcstyle.bottom + + lc.data = self._data + + lc.strokeColor = self._lcstyle.strokeColor + lc.strokeWidth = self._lcstyle.strokeWidth + lc.useAbsolute = 1 + lc.groupSpacing = lc.width * 2.0 / len(self._catNames) + lc.joinedLines = 1 + lc.lineLabelFormat = self._lcstyle.format[0] + + lc.valueAxis.valueMin, lc.valueAxis.valueMax, lc.valueAxis.valueSteps = self._value + lc.valueAxis.labelTextFormat = self._lcstyle.format[1] + lc.valueAxis.labels.fontSize = self._lcstyle.labelsfont + + lc.categoryAxis.categoryNames = self._catNames + lc.categoryAxis.labels.boxAnchor = 'ne' + lc.categoryAxis.labels.dx = lc.width / 2.0 / len(self._catNames) + lc.categoryAxis.labels.dy = -6 + lc.categoryAxis.labels.angle = 10 + lc.categoryAxis.labels.fontSize = self._lcstyle.labelsfont + # lc.categoryAxis.visibleGrid = 1 + # lc.categoryAxis.tickUp = 100 + # lc.categoryAxis.tickDown = 50 + # lc.categoryAxis.gridEnd = dh + sytle_cnts = len(self._lcstyle.linestyle) + color_paris = [] + for i in range(line_cnts): + styleIndex = i % sytle_cnts + lc.lines[i].strokeColor = self._lcstyle.linestyle[styleIndex][0] + lc.lines[i].symbol = makeMarker(self._lcstyle.linestyle[styleIndex][1]) + lc.lines[i].strokeWidth = self._lcstyle.linestyle[styleIndex][2] + color_paris.append((self._lcstyle.linestyle[styleIndex][0], self._linename[i])) + + lc.lineLabels.fontSize = self._lcstyle.labelsfont - 2 + + draw.add(lc) + + lg = Legend() + lg.colorNamePairs = color_paris + lg.fontName = 'Helvetica' + lg.fontSize = 7 + # lg.x = dw /2 + # lg.y = self._lcstyle.bottom *(1.5 + lg_line) + + lg.x = self._lcstyle.left * 3 + lg.y = self._lcstyle.bottom * (1 + lg_line) + lc.height + + lg.dxTextSpace = 5 + lg.dy = 5 + lg.dx = 20 + lg.deltax = 60 + lg.deltay = 0 + lg.columnMaximum = 1 + lg.alignment = 'right' + draw.add(lg) + self._draw = draw + + +class eBarChartColumn(object): + def __init__(self, data, style): + self._bcstyle = style + if len(data) < 4: + return + self._data = self.analysisData(data) + if self._data: + self.create() + + @property + def draw(self): + return self._draw + + def analysisData(self, data): + self._ytitle = data[0] + self._name = data[1] + self._bar = data[2] + bar_data = data[3] + result = [] + for bar in bar_data: + bar = map(lambda x: float(x), bar) + result.append(tuple(bar)) + return result + + def create(self): + dw = self._bcstyle.width + dh = self._bcstyle.height + draw = Drawing(dw, dh) + + bc = VerticalBarChart() + bar_cnt = len(self._bar) + lg_line = (bar_cnt + 3) / 4 + + bc.width = dw - self._bcstyle.left - self._bcstyle.right + bc.height = dh - self._bcstyle.top - self._bcstyle.bottom + if bar_cnt > 1: + bc.height -= lg_line * 15 + + bc.x = self._bcstyle.left + bc.y = self._bcstyle.bottom + color_paris = [] + for i in range(bar_cnt): + bc.bars[i].fillColor = self._bcstyle.pillarstyle[self._bar[i]][0] + color_paris.append((self._bcstyle.pillarstyle[self._bar[i]][0], self._bar[i])) + + bc.fillColor = self._bcstyle.background + bc.barLabels.fontName = 'Helvetica' + bc.barLabelFormat = self._bcstyle.pillarstyle[self._bar[0]][1] + bc.barLabels.fontSize = self._bcstyle.labelsfont + bc.barLabels.dy = self._bcstyle.labelsfont + bc.valueAxis.labels.fontName = 'Helvetica' + bc.valueAxis.labels.fontSize = self._bcstyle.labelsfont + bc.valueAxis.forceZero = 1 + bc.valueAxis.valueMin = 0 + + bc.data = self._data + bc.barSpacing = self._bcstyle.barSpacing + bc.groupSpacing = self._bcstyle.groupSpacing / bar_cnt + bc.valueAxis.avoidBoundFrac = 1 + bc.valueAxis.gridEnd = dw - self._bcstyle.right + bc.valueAxis.tickLeft = self._bcstyle.tick + bc.valueAxis.visibleGrid = 1 + bc.categoryAxis.categoryNames = self._name + bc.categoryAxis.tickDown = self._bcstyle.tick + bc.categoryAxis.labels.fontName = 'Helvetica' + bc.categoryAxis.labels.fontSize = self._bcstyle.labelsfont + bc.categoryAxis.labels.dy = -27 + bc.categoryAxis.labels.angle = -90 + draw.add(bc) + lb = Label() + lb.fontName = 'Helvetica' + lb.fontSize = 7 + lb.x = 12 + lb.y = 80 + lb.angle = 90 + lb.textAnchor = 'middle' + lb.maxWidth = 100 + lb.height = 20 + lb._text = self._ytitle + draw.add(lb) + if bar_cnt > 1: + lg = Legend() + lg.colorNamePairs = color_paris + lg.fontName = 'Helvetica' + lg.fontSize = 7 + + lg.x = self._bcstyle.left + bc.width / (bar_cnt + 1) + lg.y = dh - self._bcstyle.top - lg_line * 5 + + lg.dxTextSpace = 5 + lg.dy = 5 + lg.dx = 25 + lg.deltax = 80 + lg.deltay = 0 + lg.columnMaximum = 1 + lg.alignment = 'right' + draw.add(lg) + + self._draw = draw + + +class eParagraph(object): + def __init__(self, data, style): + self._pstyle = style + self._data = self.analysisData(data) + self.create() + + def analysisData(self, data): + result = "" + for dstr in data: + if self._pstyle.name == 'ps_body': + # dstr = "" + dstr + "
" + dstr = dstr + "
" + else: + dstr = dstr + "
" + result += dstr + return result + + def create(self): + self._para = Paragraph(self._data, self._pstyle) + + @property + def para(self): + return self._para diff --git a/vstf/vstf/controller/reporters/report/pdf/pdfcreator.py b/vstf/vstf/controller/reporters/report/pdf/pdfcreator.py new file mode 100755 index 00000000..50b3bc65 --- /dev/null +++ b/vstf/vstf/controller/reporters/report/pdf/pdfcreator.py @@ -0,0 +1,446 @@ +#!/usr/bin/python +# -*- coding: utf8 -*- +# author: wly +# date: 2015-05-29 +# see license for license details +__version__ = ''' ''' + +import os + +from vstf.controller.reporters.report.pdf.styles import TemplateStyle +from vstf.controller.reporters.report.pdf.pdftemplate import PdfVswitch +from vstf.controller.reporters.report.pdf.story import TitleStory, SpaceStory, ImageStory, LineChartStory, \ + LinePlotStory, uTableStory, Story, TableOfContentsStory, PageBreakStory, ParagraphStory, BarChartStory, cTableStory +from vstf.controller.reporters.report.data_factory import CommonData, ScenarioData, HistoryData +from vstf.controller.database.dbinterface import DbManage +import vstf.controller + + +class LetterOrder(object): + def __init__(self): + self.lettertable = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + self._cur = 0 + self._len = len(self.lettertable) + + def get(self): + return self.lettertable[self._cur] + + def pre(self): + self._cur = (self._cur + self._len - 1) % self._len + + def next(self): + self._cur = (self._cur + 1) % self._len + + +class PdfBase(object): + def __init__(self): + self._case = '' + self._ofile = '' + self._title = [] + self._story = [] + self._rootdir = os.path.dirname(vstf.controller.__file__) + '/' + self._pdf = None + + def create_pdf(self): + style = TemplateStyle(name='default') + title = self._title + logo = [self._rootdir + "res/logo.jpg"] + header = [''] + footer = [""] + note = ['', ''] + output = [self._ofile] + self._pdf = PdfFrameLoss(style, title, logo, header, footer, output, note) + + def save_pdf(self): + self._pdf.generate(self._story) + + def add_coverpage(self): + story = Story() + story = PageBreakStory(story) + self._story += story.storylist + + def create_story(self): + raise NotImplementedError("abstract PdfBase") + + def create(self): + self.create_pdf() + self.create_story() + self.save_pdf() + + +class PdfvSwitchCreator(PdfBase): + def __init__(self, ofile, common_data, scenario_data, history_data): + PdfBase.__init__(self) + self._common = common_data + self._result = scenario_data + self._history = history_data + self._ofile = ofile + self._chapterid = 0 + self._appendixid = LetterOrder() + + def create_pdf(self): + style = TemplateStyle(name='default') + title = self._result.get_covertitle() + logo = [self._rootdir + "res/logo.jpg"] + header = [''] + footer = [""] + note = ['', ''] + output = [self._ofile] + self._pdf = PdfVswitch(style, title, logo, header, footer, output, note) + + def get_chapterid(self): + self._chapterid = self._chapterid + 1 + return self._chapterid + + def create_story(self): + self.add_coverpage() + self.add_table_of_contents() + # self.add_contact() + # self.add_overview() + self.add_scenario() + # self.add_info() + # self.add_appendix() + self.add_historys() + + def add_info(self): + self.add_systeminfo() + self.add_gitinfo() + self.add_profile_parameters() + self.add_testing_options() + + def add_contact(self): + story = Story() + story = SpaceStory(story) + title = ["", "", "", "Reporter"] + body = self._common.get_contact() + story = TitleStory(story, data=title, style=7) + story = ParagraphStory(story, data=body) + self._story += story.storylist + + def add_table_of_contents(self): + story = Story() + story = TableOfContentsStory(story) + self._story += story.storylist + + def add_overview(self): + story = Story() + story = PageBreakStory(story) + + chapterid = self.get_chapterid() + title = ["%d.Overview" % (chapterid)] + body = [""] + story = TitleStory(story, data=title, style=1) + story = ParagraphStory(story, data=body) + + sectionid = 1 + title = ["%d.%d Components under Test" % (chapterid, sectionid)] + body = self._common.get_components() + story = TitleStory(story, data=title, style=2) + story = ParagraphStory(story, data=body) + + sectionid = sectionid + 1 + title = ["%d.%d Test" % (chapterid, sectionid)] + body = self._result.get_test() + story = TitleStory(story, data=title, style=2) + story = ParagraphStory(story, data=body) + + sectionid = sectionid + 1 + title = ["%d.%d Configuration" % (chapterid, sectionid)] + story = TitleStory(story, data=title, style=2) + + title = ["Software"] + body = self._common.get_software() + story = TitleStory(story, data=title, style=6) + story = ParagraphStory(story, data=body) + + title = ["Hardware"] + body = self._common.get_hardware() + story = TitleStory(story, data=title, style=6) + story = ParagraphStory(story, data=body) + self._story += story.storylist + + def add_scenario(self): + case_list = self._result.get_caselist() + for case in case_list: + self.add_case(case) + + def add_case(self, case): + story = Story() + chapterid = self.get_chapterid() + + title = ["%d. Case : %s (%s)" % (chapterid, case, self._common.get_casename(case))] + + tools = self._result.get_test_tools(case) + pic = self._common.get_casefigure(case, tools) + print pic + + story = TitleStory(story, data=title, style=1) + story = SpaceStory(story) + story = ImageStory(story, data=[self._rootdir + pic]) + story = SpaceStory(story) + + sectionid = 1 + story = self.add_summary(story, chapterid, sectionid, case) + story = SpaceStory(story) + + if self._result.is_throughput_start(case): + sectionid = sectionid + 1 + story = self.add_throughput_result(story, chapterid, sectionid, case) + + if self._result.is_frameloss_start(case): + sectionid = sectionid + 1 + story = self.add_frameloss_result(story, chapterid, sectionid, case) + + if self._result.is_latency_start(case): + sectionid = sectionid + 1 + story = self.add_latency_result(story, chapterid, sectionid, case) + + story = SpaceStory(story) + story = SpaceStory(story) + self._story += story.storylist + + def add_summary(self, story, chapterid, sectionid, case): + title = ["%d.%d Summary" % (chapterid, sectionid)] + story = TitleStory(story, data=title, style=2) + provider_list = ["fastlink", "rdp", "l2switch"] + provider_dict = {"fastlink": "Fast Link", "l2switch": "L2Switch", "rdp": "Kernel RDP"} + unitid = 1 + case_name = self._common.get_casename(case) + for provider in provider_list: + if self._result.is_provider_start(case, provider): + title = ["%d.%d.%d %s (%s_%s)" % ( + chapterid, sectionid, unitid, provider_dict[provider], case_name, provider)] + unitid = unitid + 1 + story = TitleStory(story, data=title, style=6) + test_types = ["throughput", "frameloss"] + for test_type in test_types: + if self._result.is_type_provider_start(case, provider, test_type): + story = self.add_summary_type(story, case, provider, test_type) + return story + + def add_summary_type(self, story, case, provider, test_type): + bar_list = [test_type, "latency"] + for item in bar_list: + bar_data = self._result.get_bardata(case, provider, item) + story = SpaceStory(story) + story = BarChartStory(story, data=bar_data) + + table_content = self._result.get_summary_tabledata(case, provider, test_type) + story = SpaceStory(story) + story = cTableStory(story, data=table_content, style=3) + story = SpaceStory(story) + return story + + def add_throughput_result(self, story, chapterid, sectionid, case): + title = ["%d.%d Throughput " % (chapterid, sectionid)] + story = TitleStory(story, data=title, style=2) + unitid = 1 + title = ["%d.%d.%d Summary" % (chapterid, sectionid, unitid)] + story = TitleStory(story, data=title, style=6) + + test_type = "throughput" + unit = 'RX Frame Rate' + chart_data = self._result.get_frameloss_chartdata(case, test_type) + table_data = self._result.get_frameloss_tabledata(case, test_type) + title = [unit + ' (%)'] + story = TitleStory(story, data=title, style=6) + # story = SpaceStory(story) + # story = LinePlotStory(story, data=chart_data) + story = SpaceStory(story) + story = uTableStory(story, data=table_data) + story = SpaceStory(story) + + unit = 'Frame Loss Rate' + title = [unit + ' (Mpps)'] + + chart_data = self._result.get_framerate_chartdata(case, test_type) + table_data = self._result.get_framerate_tabledata(case, test_type) + story = TitleStory(story, data=title, style=6) + story = SpaceStory(story) + story = LinePlotStory(story, data=chart_data) + story = SpaceStory(story) + story = uTableStory(story, data=table_data) + story = SpaceStory(story) + return story + + def add_frameloss_result(self, story, chapterid, sectionid, case): + title = ["%d.%d Frame Loss Rate " % (chapterid, sectionid)] + story = TitleStory(story, data=title, style=2) + unitid = 1 + title = ["%d.%d.%d Summary" % (chapterid, sectionid, unitid)] + story = TitleStory(story, data=title, style=6) + + test_type = "frameloss" + unit = 'RX Frame Rate' + chart_data = self._result.get_frameloss_chartdata(case, test_type) + table_data = self._result.get_frameloss_tabledata(case, test_type) + title = [unit + ' (%)'] + story = TitleStory(story, data=title, style=6) + # story = SpaceStory(story) + # story = LineChartStory(story, data=chart_data) + story = SpaceStory(story) + story = uTableStory(story, data=table_data) + story = SpaceStory(story) + + unit = 'Frame Loss Rate' + title = [unit + ' (Mpps)'] + + chart_data = self._result.get_framerate_chartdata(case, test_type) + table_data = self._result.get_framerate_tabledata(case, test_type) + story = TitleStory(story, data=title, style=6) + story = SpaceStory(story) + story = LineChartStory(story, data=chart_data) + story = SpaceStory(story) + story = uTableStory(story, data=table_data) + story = SpaceStory(story) + return story + + def add_latency_result(self, story, chapterid, sectionid, case): + title = ["%d.%d Latency " % (chapterid, sectionid)] + story = TitleStory(story, data=title, style=2) + unitid = 1 + title = ["%d.%d.%d Summary" % (chapterid, sectionid, unitid)] + story = TitleStory(story, data=title, style=6) + + unit = 'Average Latency' + title = [unit + ' (uSec)'] + # chart_data = self._result.get_latency_chartdata(case) + bar_data = self._result.get_latency_bardata(case) + table_data = self._result.get_latency_tabledata(case) + story = TitleStory(story, data=title, style=6) + story = SpaceStory(story) + # story = LineChartStory(story, data=chart_data) + story = BarChartStory(story, data=bar_data) + + story = SpaceStory(story) + story = uTableStory(story, data=table_data) + story = SpaceStory(story) + return story + + def add_systeminfo(self): + story = Story() + chapterid = self.get_chapterid() + story = SpaceStory(story) + title = ["%d. System Information " % (chapterid)] + story = PageBreakStory(story) + story = TitleStory(story, data=title, style=1) + table_content = self._common.get_systeminfo_tabledata() + story = SpaceStory(story) + story = cTableStory(story, data=table_content, style=0) + story = SpaceStory(story) + self._story += story.storylist + + def add_gitinfo(self): + story = Story() + chapterid = self.get_chapterid() + title = ["%d. Git Repository Information " % (chapterid)] + story = TitleStory(story, data=title, style=1) + + table_content = self._common.get_gitinfo_tabledata() + if table_content: + story = SpaceStory(story) + story = cTableStory(story, data=table_content, style=5) + story = SpaceStory(story) + self._story += story.storylist + + def add_testing_options(self): + story = Story() + chapterid = self.get_chapterid() + story = SpaceStory(story) + title = ["%d. Testing Options" % (chapterid)] + + story = TitleStory(story, data=title, style=1) + table_content = self._common.get_testingoptions_tabledata() + story = SpaceStory(story) + story = cTableStory(story, data=table_content, style=1) + story = SpaceStory(story) + self._story += story.storylist + + def add_profile_parameters(self): + story = Story() + chapterid = self.get_chapterid() + story = PageBreakStory(story) + title = ["%d. " % (chapterid)] + story = TitleStory(story, data=title, style=1) + table_content = self._common.get_profileparameters_tabledData() + story = SpaceStory(story) + story = cTableStory(story, data=table_content, style=2) + story = SpaceStory(story) + self._story += story.storylist + + def add_appendix(self): + story = Story() + story = PageBreakStory(story) + + title = ["Appendix %s: vSwitching Testing Methodology" % (self._appendixid.get())] + self._appendixid.next() + story = TitleStory(story, data=title, style=1) + filename = "res/Traffic-types.jpg" + story = SpaceStory(story) + story = ImageStory(story, data=[self._rootdir + filename]) + # story = SpaceStory(story) + + title = ["Traffic Patterns: "] + story = TitleStory(story, data=title, style=6) + + body = [ + "Ti - South North Traffic", + "Tu - East Eest Traffic", + "Tn - Physical host or VM loop back", + "Tnv - Virtual Machine loop back", + ] + story = ParagraphStory(story, data=body) + + title = ["Performance Testing Coverage (version 0.1):"] + story = TitleStory(story, data=title, style=6) + + table_content = self._common.get_introduct_tabledata() + story = SpaceStory(story) + story = cTableStory(story, data=table_content, style=4) + self._story += story.storylist + + def add_historys(self): + case_list = self._result.get_caselist() + for case in case_list: + history = self._history.get_history_info(case) + if history: + self.add_history(case, history) + + def add_history(self, case, history): + story = Story() + story = PageBreakStory(story) + + title = ["Appendix %s : %s History Records" % (self._appendixid.get(), case)] + story = TitleStory(story, data=title, style=1) + + for i in range(len(history)): + title = ["%s.%s %s" % (self._appendixid.get(), i, history[i]["title"])] + story = TitleStory(story, data=title, style=2) + + section = history[i]["data"] + for unit in section: + title = [unit['title']] + story = TitleStory(story, data=title, style=6) + content = unit['data'] + story = uTableStory(story, data=content) + + self._appendixid.next() + self._story += story.storylist + + +def main(): + dbase = DbManage() + taskid = dbase.get_last_taskid() + common_data = CommonData(taskid, dbase) + scenario_list = common_data.get_scenariolist() + history_data = HistoryData(taskid, dbase) + for scenario in scenario_list: + out_file = "vstf_report_%s.pdf" % (scenario) + scenario_data = ScenarioData(taskid, dbase, scenario) + reporter = PdfvSwitchCreator(out_file, common_data, scenario_data, history_data) + if reporter: + reporter.create() + + +if __name__ == '__main__': + main() diff --git a/vstf/vstf/controller/reporters/report/pdf/pdftemplate.py b/vstf/vstf/controller/reporters/report/pdf/pdftemplate.py new file mode 100755 index 00000000..819a5c57 --- /dev/null +++ b/vstf/vstf/controller/reporters/report/pdf/pdftemplate.py @@ -0,0 +1,107 @@ +#!/usr/bin/python +# -*- coding: utf8 -*- +import time + +from reportlab.platypus.doctemplate import SimpleDocTemplate +from reportlab.platypus import PageBreak +from vstf.controller.reporters.report.pdf.styles import TemplateStyle, ps_head_lv1, ps_head_lv2, ps_head_lv3 + + +class MyDocTemplate(SimpleDocTemplate): + def __init__(self, filename, **kw): + self.allowSplitting = 0 + SimpleDocTemplate.__init__(self, filename, **kw) + + def afterFlowable(self, flowable): + """Registers TOC entries.""" + if flowable.__class__.__name__ == 'Paragraph': + text = flowable.getPlainText() + style = flowable.style.name + if style == ps_head_lv1.name: + self.notify('TOCEntry', (0, text, self.page - 1)) + elif style == ps_head_lv2.name: + self.notify('TOCEntry', (1, text, self.page - 1)) + elif style == ps_head_lv3.name: + self.notify('TOCEntry', (2, text, self.page - 1)) + + +class PdfTemplate: + def __init__(self, style, title, logo, header, footer, output, note=None): + self._style = style + self._title = title + self._logo = logo[0] + self._header = header[0] + self._footer = footer + self._output = output[0] + self._note = note + info = " Generated on %s " % time.strftime('%Y/%m/%d %H:%M:%S', time.localtime()) + self._note[0] += info + + def myFirstPage(self, canvas, doc): + raise NotImplementedError("abstract StoryDecorator") + + def myLaterPages(self, canvas, doc): + raise NotImplementedError("abstract StoryDecorator") + + def generate(self, story): + sizes = (self._style.page_wight, self._style.page_height) + doc = MyDocTemplate(self._output, pagesize=sizes) + # doc.build(story, onFirstPage=self.myFirstPage, onLaterPages=self.myLaterPages) + doc.multiBuild(story, onFirstPage=self.myFirstPage, onLaterPages=self.myLaterPages) + + +class PdfVswitch(PdfTemplate): + def myFirstPage(self, canvas, doc): + canvas.saveState() + title_lines = len(self._title) + line_size = [self._style.title_size] * title_lines + line_size.append(0) + + canvas.drawImage(self._logo, + (self._style.page_wight - self._style.logo_width) / 2.0, + self._style.page_height / 2.0 + (1 + self._style.title_leading) * reduce(lambda x, y: x + y, + line_size), + self._style.logo_width, + self._style.logo_height + ) + for i in range(title_lines): + canvas.setFont(self._style.title_font, line_size[i]) + canvas.drawCentredString(self._style.page_wight / 2.0, + self._style.page_height / 2.0 + (1 + self._style.title_leading) * reduce( + lambda x, y: x + y, line_size[i + 1:]), + self._title[i] + ) + size = self._style.body_size + canvas.setFont(self._style.body_font, size) + note_line = len(self._note) + + for i in range(note_line): + print self._note[i] + canvas.drawCentredString(self._style.page_wight / 2.0, + self._style.page_height / 5.0 + (1 + self._style.body_leading) * size * ( + note_line - i - 1), + self._note[i] + ) + size = self._style.body_size - 2 + canvas.setFont(self._style.body_font, size) + canvas.drawCentredString(self._style.page_wight / 2.0, + self._style.page_bottom / 2.0 + (1 + self._style.body_leading) * size, + self._footer[0]) + canvas.restoreState() + + def myLaterPages(self, canvas, doc): + canvas.saveState() + canvas.setLineWidth(self._style.line_width) + canvas.line(self._style.page_left, + self._style.page_height - self._style.page_top, + self._style.page_wight - self._style.page_right, + self._style.page_height - self._style.page_top + ) + size = self._style.body_size - 2 + canvas.setFont(self._style.body_font, size) + canvas.drawCentredString(self._style.page_wight / 2.0, + self._style.page_bottom - 24, + "%s%s Page %2d " % (self._footer[0], " " * 8, doc.page - 1) + ) + canvas.restoreState() + diff --git a/vstf/vstf/controller/reporters/report/pdf/story.py b/vstf/vstf/controller/reporters/report/pdf/story.py new file mode 100755 index 00000000..3e56e185 --- /dev/null +++ b/vstf/vstf/controller/reporters/report/pdf/story.py @@ -0,0 +1,191 @@ +#!/usr/bin/python +# -*- coding: utf8 -*- +__doc__ = """ +Story Decorator contains ImageStory, HeaderStory, PageBreakStory, +TableStory, LinePlotStory, TitleStory, ParagraphStory +""" +import sys +import os +from reportlab.platypus import PageBreak +from reportlab.lib import colors +from reportlab.platypus.tableofcontents import TableOfContents +from styles import * +from element import * + + +class Story(object): + def __init__(self): + self._storylist = [] + + @property + def storylist(self): + return self._storylist + + +class StoryDecorator(Story): + def __init__(self, story, data=None, style=None): + self._story = story + self._data = data + self._style = style + print self._data + self.new_story() + + # print self._story.storylist + @property + def storylist(self): + return self._story.storylist + + def new_story(self): + raise NotImplementedError("abstract StoryDecorator") + + +class ImageStory(StoryDecorator): + def new_story(self): + print "Image Story" + for filename in self._data: + if os.path.exists(filename) == False: + print "not find %s" % filename + continue + if 'Traffic-types' in filename: + style = is_traffic + image_height = style.image_height + image_width = style.image_width + image_hAlign = style.image_hAlign + image_vAlign = style.image_vAlign + self._story.storylist.append( + eImage(filename, image_width, image_height, hAlign=image_hAlign, vAlign=image_vAlign)) + else: + style = is_default + image_height = style.image_height + image_width = style.image_width + image_hAlign = style.image_hAlign + image_vAlign = style.image_vAlign + # self._story.storylist.append(eGraphicsTable([[' ' * 5, eImage(filename, image_width, image_height, hAlign=image_hAlign, vAlign=image_vAlign)]], ts_left).table) + self._story.storylist.append( + eImage(filename, image_width, image_height, hAlign=image_hAlign, vAlign=image_vAlign)) + + +class HeaderStory(StoryDecorator): + def new_story(self): + print "header story" + self._story.storylist.append(PageBreak()) + + +class PageBreakStory(StoryDecorator): + def new_story(self): + print "PageBreak story" + self._story.storylist.append(PageBreak()) + + +class TableOfContentsStory(StoryDecorator): + def new_story(self): + print "TableOfContents story" + self._data = [" ", " ", "Table Of Contents", ""] + style = ps_head_lv4 + self._story.storylist.append(eParagraph(self._data, style).para) + toc = TableOfContents() + toc.levelStyles = [ps_head_lv7, ps_head_lv8, ps_head_lv9] + self._story.storylist.append(toc) + + +class uTableStory(StoryDecorator): + def new_story(self): + print "utable story" + style = ts_left + if not self._data: + print "data error " + return + self._story.storylist.append(eCommonTable(self._data, style).table) + + +class TableStory(StoryDecorator): + def new_story(self): + print "table story" + style = ts_default + self._story.storylist.append(eDataTable(self._data, style).table) + + +class SpaceStory(StoryDecorator): + def new_story(self): + style = ps_space + self._story.storylist.append(eParagraph([" ", " "], style).para) + + +class cTableStory(StoryDecorator): + def new_story(self): + print "table story" + style = ts_default + if self._style == 0: + self._story.storylist.append(eConfigTable(self._data, style).table) + elif self._style == 1: + self._story.storylist.append(eOptionsTable(self._data, style).table) + elif self._style == 2: + self._story.storylist.append(eProfileTable(self._data, style).table) + elif self._style == 3: + self._story.storylist.append(eSummaryTable(self._data, style).table) + elif self._style == 4: + self._story.storylist.append(eScenarioTable(self._data, style).table) + elif self._style == 5: + self._story.storylist.append(eGitInfoTable(self._data, style).table) + + +class LinePlotStory(StoryDecorator): + def new_story(self): + print "LinePlot" + style = lps_default + if not self._data: + print "data error " + return + data = eGraphicsTable([[eLinePlot(self._data, style).draw]]).table + if data: + self._story.storylist.append(data) + + +class LineChartStory(StoryDecorator): + def new_story(self): + print "LineChartStory: " + style = lcs_default + if not self._data: + print "data error " + return + data = eGraphicsTable([[eHorizontalLineChart(self._data, style).draw]]).table + if data: + self._story.storylist.append(data) + + +class BarChartStory(StoryDecorator): + def new_story(self): + print "BarChartStory: " + style = bcs_default + if not self._data: + print "data error " + return + + data = eGraphicsTable([[eBarChartColumn(self._data, style).draw]]).table + if data: + self._story.storylist.append(data) + + +class ParagraphStory(StoryDecorator): + def new_story(self): + print "Paragraph Story" + style = ps_body + if not self._data: + print "data error " + return + data = eParagraph(self._data, style).para + if data: + self._story.storylist.append(data) + + +class TitleStory(StoryDecorator): + def new_story(self): + print "Paragraph Story" + if self._style - 1 in range(9): + style = eval("ps_head_lv" + "%d" % self._style) + else: + style = ps_body + # print style + # print self._data + + self._story.storylist.append(eParagraph(self._data, style).para) diff --git a/vstf/vstf/controller/reporters/report/pdf/styles.py b/vstf/vstf/controller/reporters/report/pdf/styles.py new file mode 100755 index 00000000..d54ee8ab --- /dev/null +++ b/vstf/vstf/controller/reporters/report/pdf/styles.py @@ -0,0 +1,198 @@ +#!/usr/bin/python +# -*- coding: utf8 -*- +from reportlab.lib.styles import PropertySet +from reportlab.lib.pagesizes import A4 +from reportlab.lib import colors +from reportlab.lib.styles import ParagraphStyle +from reportlab.lib.enums import TA_LEFT + + +class TemplateStyle(PropertySet): + defaults = dict( + page_height=A4[1], + page_wight=A4[0], + page_left=78, + page_top=60, + page_bottom=70, + page_right=78, + title_size=16, + title_leading=1.25, + title_font='Courier-Bold', + body_size=10, + body_leading=0.8, + body_font='Courier', + line_width=1, + logo_width=131.2, + logo_height=127.7 + ) + + +class ImageStyle(PropertySet): + defaults = dict( + image_height=165, + image_width=175, + image_hAlign='CENTRE', # LEFT,CENTRE or RIGHT + image_vAlign='MIDDLE' # BOTTOM,MIDDLE or TOP + ) + + +class TableStyle(PropertySet): + defaults = dict( + table_hAlign='CENTRE', # LEFT,CENTRE or RIGHT + table_vAlign='MIDDLE', # BOTTOM,MIDDLE or TOP + table_colWidths=None, + table_rowHeights=None + ) + + +class LinePlotStyle(PropertySet): + defaults = dict( + width=430, + height=400, + left=30, + bottom=20, + strokeColor=colors.black, + strokeWidth=1, + format=('%4.2f', '%4.0f', '%3.1f'), + labelsfont=7, + linestyle=[ + (colors.red, 'Circle', 1.5), + (colors.blue, 'Diamond', 1.5), + (colors.gold, 'Square', 1.5), + (colors.green, 'Triangle', 1.5), + (colors.pink, 'FilledCircle', 1.5), + (colors.lightblue, 'FilledDiamond', 1.5), + (colors.lightgreen, 'FilledTriangle', 1.5) + ] + ) + + +class LineChartStyle(PropertySet): + defaults = dict( + width=430, + height=400, + left=30, + bottom=20, + strokeColor=colors.lightgrey, + strokeWidth=1, + format=('%4.2f', '%3.1f'), + labelsfont=8, + linestyle=[ + (colors.red, 'Circle', 1.5), + (colors.blue, 'Diamond', 1.5), + (colors.gold, 'Square', 1.5), + (colors.green, 'Triangle', 1.5), + (colors.pink, 'FilledCircle', 1.5), + (colors.lightblue, 'FilledDiamond', 1.5), + (colors.lightgreen, 'FilledTriangle', 1.5) + ] + ) + + +class BarChartStyle(PropertySet): + defaults = dict( + width=430, + height=135, + left=30, + bottom=50, + top=0, + right=30, + groupSpacing=32, + barSpacing=4, + tick=3, + strokeColor=colors.lightgrey, + strokeWidth=1, + pillarstyle={ + "loss": (colors.lightgreen, '%4.2f'), + "latency": (colors.indianred, '%4.1f'), + "fastlink": (colors.pink, '%4.1f'), + "l2switch": (colors.lightblue, '%4.1f'), + "kernel rdp": (colors.lightgreen, '%4.1f'), + }, + background=colors.lightgrey, + labelsfont=6, + ) + + +ts_left = TableStyle( + name='left', + table_hAlign='LEFT', # LEFT,CENTRE or RIGHT + table_vAlign='BOTTOM', # BOTTOM,MIDDLE or TOP + table_colWidths=None, + table_rowHeights=None +) + +is_default = ImageStyle(name='default') +is_traffic = ImageStyle(name='traffic', + image_height=150, + image_width=360, + image_hAlign='CENTRE') + +ts_default = TableStyle(name='default') +lps_default = LinePlotStyle(name='default') +lcs_default = LineChartStyle(name='default') +bcs_default = BarChartStyle(name='default') +ps_head_lv1 = ParagraphStyle(name='ps_head_lv1', + fontName='Courier-Bold', + alignment=TA_LEFT, # TA_CENTRE, + fontSize=13, + leading=22, + leftIndent=0) + +ps_head_lv2 = ParagraphStyle(name='ps_head_lv2', + fontName='Courier', + fontSize=12, + leading=20, + leftIndent=16) + +ps_head_lv3 = ParagraphStyle(name='ps_head_lv3', + fontSize=11, + fontName='Courier', + leading=20, + leftIndent=16) + +ps_head_lv4 = ParagraphStyle(name='ps_head_lv4', + fontSize=13, + fontName='Courier-Bold', + leading=22, + leftIndent=0) + +ps_head_lv5 = ParagraphStyle(name='ps_head_lv5', + fontSize=12, + fontName='Courier', + leading=20, + leftIndent=16) + +ps_head_lv6 = ParagraphStyle(name='ps_head_lv6', + fontSize=11, + fontName='Courier', + leading=20, + leftIndent=16) + +ps_head_lv7 = ParagraphStyle(name='ps_head_lv7', + fontSize=11, + fontName='Courier', + leading=18, + leftIndent=0) + +ps_head_lv8 = ParagraphStyle(name='ps_head_lv8', + fontSize=11, + fontName='Courier', + leading=18, + leftIndent=16) + +ps_head_lv9 = ParagraphStyle(name='ps_head_lv9', + fontSize=11, + fontName='Courier', + leading=18, + leftIndent=32) + +ps_body = ParagraphStyle(name='ps_body', + fontSize=11, + fontName='Courier', + leading=18, + leftIndent=32) + +ps_space = ParagraphStyle(name='ps_space', + fontSize=5, + leading=5) diff --git a/vstf/vstf/controller/reporters/report/provider/__init__.py b/vstf/vstf/controller/reporters/report/provider/__init__.py new file mode 100755 index 00000000..89dcd4e2 --- /dev/null +++ b/vstf/vstf/controller/reporters/report/provider/__init__.py @@ -0,0 +1,14 @@ +# Copyright Huawei Technologies Co., Ltd. 1998-2015. +# All Rights Reserved. +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an AS IS BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. diff --git a/vstf/vstf/controller/reporters/report/provider/html_provider.py b/vstf/vstf/controller/reporters/report/provider/html_provider.py new file mode 100755 index 00000000..b0b07432 --- /dev/null +++ b/vstf/vstf/controller/reporters/report/provider/html_provider.py @@ -0,0 +1,63 @@ +#!/usr/bin/python +# -*- coding: utf8 -*- +# author: wly +# date: 2015-09-25 +# see license for license details +__version__ = ''' ''' +import logging + +LOG = logging.getLogger(__name__) +from vstf.controller.settings.html_settings import HtmlSettings +from vstf.controller.settings.data_settings import DataSettings + + +class HtmlProvider(object): + def __init__(self, content, style): + self._content = content + self._style = style + + def get_style(self): + return self._style["style"] + + def get_subject(self): + return self._content["subject"] + + def get_ovs_title(self): + return self._content["ovs"]["title"] + + def get_ovs_table(self): + return map(lambda x: list(x), self._content["ovs"]["content"].items()) + + def get_result_title(self): + return self._content["result"]["title"] + + def get_result_table(self, ttype): + result = [] + content = self._content["result"]["content"] + if ttype in content: + result.append(content[ttype]["columns"]) + result.extend(content[ttype]["data"]) + + result = map(lambda x: list(x), zip(*result)) + return result + + +class StyleProvider(object): + def __init__(self, style): + self._style = style + + def get_style(self): + return self._style["style"] + + +def unit_test(): + from vstf.common.log import setup_logging + setup_logging(level=logging.DEBUG, log_file="/var/log/html-provder.log", clevel=logging.INFO) + + html_settings = HtmlSettings() + LOG.info(html_settings.settings) + data_settings = DataSettings() + LOG.info(data_settings.settings) + + hprovider = HtmlProvider(data_settings.settings, html_settings.settings) + sprovider = StyleProvider(html_settings.settings) -- cgit 1.2.3-korg