summaryrefslogtreecommitdiffstats
path: root/vstf/vstf/controller/reporters/report/pdf
diff options
context:
space:
mode:
Diffstat (limited to 'vstf/vstf/controller/reporters/report/pdf')
-rwxr-xr-xvstf/vstf/controller/reporters/report/pdf/__init__.py14
-rwxr-xr-xvstf/vstf/controller/reporters/report/pdf/element.py781
-rwxr-xr-xvstf/vstf/controller/reporters/report/pdf/pdfcreator.py446
-rwxr-xr-xvstf/vstf/controller/reporters/report/pdf/pdftemplate.py107
-rwxr-xr-xvstf/vstf/controller/reporters/report/pdf/story.py191
-rwxr-xr-xvstf/vstf/controller/reporters/report/pdf/styles.py198
6 files changed, 1737 insertions, 0 deletions
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 = "<i>" + dstr + "</i><br/>"
+ dstr = dstr + "<br/>"
+ else:
+ dstr = dstr + "<br/>"
+ 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 = ["<b>Appendix %s: vSwitching Testing Methodology</b>" % (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 = [
+ "<b>Ti</b> - South North Traffic",
+ "<b>Tu</b> - East Eest Traffic",
+ "<b>Tn</b> - Physical host or VM loop back",
+ "<b>Tnv</b> - Virtual Machine loop back",
+ ]
+ story = ParagraphStory(story, data=body)
+
+ title = ["<b>Performance Testing Coverage </b> (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 = ["<b>Appendix %s : %s History Records</b>" % (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)