Index: /branches/rel_apv_10_7/usr/click/webui/htdocs/new/src/hive/monitor_log/ChartController.py
===================================================================
--- /branches/rel_apv_10_7/usr/click/webui/htdocs/new/src/hive/monitor_log/ChartController.py	(revision 0)
+++ /branches/rel_apv_10_7/usr/click/webui/htdocs/new/src/hive/monitor_log/ChartController.py	(working copy)
@@ -0,0 +1,240 @@
+from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, Line, Circle, Rect, Group, String
+from reportlab.lib.colors import PCMYKColor
+from reportlab.graphics.charts.lineplots import LinePlot
+from reportlab.graphics.charts.barcharts import VerticalBarChart
+from reportlab.graphics.widgets.markers import makeMarker
+from reportlab.graphics.charts.textlabels import Label
+from reportlab.lib import colors
+from reportlab.pdfbase.pdfmetrics import stringWidth
+
+class ChartController(_DrawingEditorMixin, Drawing):
+
+    def __init__(self):
+        pass
+
+    def generate_line_plot(self, chart_data, time_list, yaxis_formatter = "", width=800, height=370, *args, **kw):
+        """
+        Generate image of line plot
+        :param dict chart_data: dict with each chart data (item_name: str, item_value: list, table_item_name: str)
+        :param list time_list: the list of time in format: "2024-10-09 14:24:33"
+        :param string yaxis_formatter: format of yaxis, special for K, M, G, T as bytes unit. the max value to compute it(if value is K or M, then format is M)
+        :param int width: width of the chart
+        :param int height: height of the chart
+        """
+        Drawing.__init__(self, width, height, *args, **kw)
+        # font
+        fontName = 'alibaba'
+        fontSize = 12
+        # chart
+        self._add(self, LinePlot(), name='chart', validate=None, desc=None)
+        self.chart.x                = 80
+        self.chart.y                = 60
+        self.chart.width            = 650
+        self.chart.height           = 250
+        # line styles
+        self.chart.lines.strokeWidth     = 1
+
+        chart_ins = []
+        legend = Group() # for group of legend in the top center place
+                         # each swatch is a circle with 2 lines and text for name
+        titles = [elem['item_name'] for elem in chart_data]
+        legend_width = []
+        for key in range(len(titles)):
+            text_width = stringWidth(titles[key], fontName, 13, 'utf8') # width of each string
+            text_width += 22 # width of left and rigth line(8*2) and circle(6)
+            legend_width.append(text_width) # width of each swatch
+        interval_width = 30 # interval between each swatch
+        interval = (len(chart_data) - 1) * interval_width # total length of interval
+        start_x = (width / 2) - (sum(legend_width) + interval) / 2 # the most left x for legend
+        for ind in range(len(chart_data)):
+            each = chart_data[ind]
+            title = each['item_name']
+
+            chart_each = []
+            if len(each['item_value']):
+                for key in range(len(each['item_value'])):
+                    chart_each.append((key, each['item_value'][key]))
+            else:
+                for ind_t in range(len(time_list)):
+                    chart_each.append((ind_t, 0))
+            chart_ins.append(chart_each)
+
+            defaultColor = self.get_color(ind)
+            self.chart.lines[ind].strokeColor = defaultColor
+            self.chart.lines[ind].name = title
+            # custom symbol that is empty circle with stroke color
+            self.chart.lines[ind].symbol = makeMarker('Circle', kind='Circle', size = 5, strokeWidth = 1, fillColor = PCMYKColor(0,0,0,0,alpha=100), strokeColor=defaultColor)
+
+            # Each legend
+            if ind > 0:
+                # start_x will be changed as the start x of the previous swatch
+                start_x = start_x + interval_width + legend_width[ind - 1]
+            # title
+            legend.add(String(start_x + 35, 354, title, fontName = fontName,fontSize = 13))
+            # circle
+            legend.add(Circle(start_x + 14, 358, 6, fillColor=colors.white, strokeColor=defaultColor))
+            # left line
+            legend.add(Line(start_x, 358, start_x + 8, 358, strokeColor=defaultColor))
+            # right line
+            legend.add(Line(start_x + 20, 358, start_x + 28, 358, strokeColor=defaultColor))
+        self._add(self, legend)
+
+        # sample data
+        self.chart.data = chart_ins
+
+        # x axis
+        self.chart.xValueAxis.labels.fontName          = fontName
+        self.chart.xValueAxis.labels.fontSize          = fontSize
+        self.chart.xValueAxis.maximumTicks          = 6
+        self.chart.xValueAxis.labels.dy = -10
+        label_list = self.get_evenly_spaced_elements(time_list, 5)
+        self.chart.xValueAxis.labelTextFormat = label_list
+        # y axis
+        self.chart.yValueAxis.labels.fontName       = fontName
+        self.chart.yValueAxis.labels.fontSize       = fontSize
+        self.chart.yValueAxis.strokeWidth           = 0.25
+        self.chart.yValueAxis.visible               = 1
+        self.chart.yValueAxis.labels.rightPadding   = 10
+        self.chart.yValueAxis.rangeRound            ='both'
+        self.chart.yValueAxis.tickLeft              = 5
+        self.chart.yValueAxis.maximumTicks          = 10
+        self.chart.yValueAxis.forceZero             = 0
+        self.chart.yValueAxis.valueMin = 0
+        if self.check_is_all_zero(chart_data):
+            self.chart.yValueAxis.labelTextFormat = ['0', '0.2', '0.4', '0.6', '0.8', '1']
+        if yaxis_formatter:
+            self.chart.yValueAxis.labelTextFormat = '%s ' + yaxis_formatter
+
+        return self
+
+    def generate_vertical_bar(self, title, data_list, chart_data, width=800, height=370, *args, **kw):
+        """
+        Generate image of vertical bar
+        :param string title: title of the chart
+        :param list data_list: list with each data(value for x axis)
+        :param list chart_data: the data for each status(value for mapping to y axis)
+        :param int width: width of the chart
+        :param int height: height of the chart
+        """
+        Drawing.__init__(self, width, height, *args, **kw)
+        fontName = 'alibaba'
+        fontSize = 13
+        default_color = PCMYKColor(0, 73, 75, 24, alpha=100)
+        self._add(self, VerticalBarChart(), name='chart', validate=None, desc=None)
+        self.chart.y                 = 60
+        self.chart.x                 = 80
+        self.chart.height            = 250
+        self.chart.width             = 650
+        self.chart.barWidth          = 20
+        self.chart.barSpacing        = 10
+        self.chart.bars.strokeWidth  = 0
+        self.chart.bars.strokeColor  = None
+        self.chart.bars[0].fillColor = default_color
+        # value axis (y axis)
+        self.chart.valueAxis.labels.fontName       = fontName
+        self.chart.valueAxis.labels.fontSize       = fontSize
+        self.chart.valueAxis.rangeRound            = 'both'
+        self.chart.valueAxis.strokeWidth           = 0.25
+        self.chart.valueAxis.forceZero             = True
+        self.chart.valueAxis.labels.dx             = -13
+        self.chart.valueAxis.valueMin              = 0
+        if self.check_is_all_zero_bar(chart_data):
+            self.chart.valueAxis.labelTextFormat  = ['0', '0.2', '0.4', '0.6', '0.8', '1']
+        # categoy axis (x axis)
+        self.chart.categoryAxis.labelAxisMode     = 'low'
+        self.chart.categoryAxis.labels.angle      = 50
+        self.chart.categoryAxis.labels.boxAnchor  = 'n'
+        self.chart.categoryAxis.labels.dy         = -11
+        self.chart.categoryAxis.labels.dx         = -14
+        self.chart.categoryAxis.labels.fillColor  = PCMYKColor(0, 0, 0, 100)
+        self.chart.categoryAxis.labels.fontName   = fontName
+        self.chart.categoryAxis.labels.fontSize   = fontSize
+        self.chart.categoryAxis.labels.textAnchor = 'middle'
+        category_list = map(str, data_list)
+        self.chart.categoryAxis.categoryNames     = list(category_list)
+        # Title
+        self._add(self, Label(), name='Title', validate=None, desc="The title at the top of the chart")
+        self.Title.fontName   = fontName
+        self.Title._text      = title
+        self.Title.fontSize   = fontSize
+        self.Title.x          = 410
+        self.Title.y          = 360
+        self.Title.textAnchor = 'middle'
+        titleBounds = self.Title.getBounds()
+        titleX = titleBounds[0]
+        # round rectangle for bar indicates the category
+        self._add(self, Rect(titleX-30, 350, 25, 15, 3, 3, fillColor=default_color, strokeColor=None))
+
+        self.chart.data = [chart_data]
+
+        return self
+
+    def get_color(self, ind):
+        """
+        Return color in list by index
+        :param int ind: index
+        """
+        color_list = [
+            PCMYKColor(0, 73, 75, 24, alpha=100), # "#c23531"
+            PCMYKColor(44, 18, 0, 67, alpha=100), # "#2f4554"
+            PCMYKColor(42, 5, 0, 34, alpha=100), # "#61a0a8"
+            PCMYKColor(0, 39, 52, 17, alpha=100), # "#d48265"
+            PCMYKColor(27, 0, 18, 38, alpha=100), # "#749f83"
+            PCMYKColor(0, 34, 83, 21, alpha=100), # "#ca8622"
+            PCMYKColor(0, 14, 19, 26, alpha=100), # "#bda29a"
+            PCMYKColor(5, 3, 0, 55, alpha=100), # "#6e7074"
+            PCMYKColor(25, 10, 0, 56, alpha=100), # "#546570"
+            PCMYKColor(7, 3, 0, 17, alpha=100), # "#c4ccd3"
+            PCMYKColor(0, 62, 53, 6, alpha=100), # "#f05b72"
+            PCMYKColor(0, 62, 35, 6, alpha=100), # "#ef5b9c"
+            PCMYKColor(0, 50, 87, 4, alpha=100), # "#f47920"
+            PCMYKColor(0, 37, 58, 44, alpha=100), # "#905a3d"
+            PCMYKColor(0, 29, 51, 2, alpha=100), # "#fab27b"
+            PCMYKColor(75, 46, 0, 33, alpha=100), # "#2a5caa"
+            PCMYKColor(54, 52, 0, 42, alpha=100), # "#444693"
+            PCMYKColor(0, 8, 58, 55, alpha=100), # "#726930"
+            PCMYKColor(15, 0, 75, 18, alpha=100), # "#b2d235"
+            PCMYKColor(17, 0, 47, 49, alpha=100), # "#6d8346"
+            PCMYKColor(0, 40, 40, 33, alpha=100), # "#ac6767"
+            PCMYKColor(81, 0, 58, 42, alpha=100), # "#1d953f"
+            PCMYKColor(35, 50, 0, 37, alpha=100), # "#6950a1"
+            PCMYKColor(4, 12, 0, 41, alpha=100), # "#918597"
+            PCMYKColor(0, 0, 4, 4, alpha=100) # "#f6f5ec"
+        ]
+        return color_list[ind]
+
+    def get_evenly_spaced_elements(self, target_list, cnt):
+        """
+        Get elements that index evenly for the count of a list
+        :param list target_list: The target list
+        :param int cnt: always get such count of elements, but index is evenly
+        """
+        length = len(target_list)
+
+        if length <= cnt:
+            return target_list
+
+        evenly = cnt - 1
+        # count index evenly
+        indices = [int(i * (length - 1) / evenly) for i in range(cnt)]
+
+        # get element in target_list
+        return [target_list[i] for i in indices]
+
+    def check_is_all_zero(self, chart_data):
+        is_not_zero = False
+        for ind in range(len(chart_data)):
+            each = chart_data[ind]
+            for value in each['item_value']:
+                if value:
+                    is_not_zero = True
+                    return not is_not_zero
+        return not is_not_zero
+
+    def check_is_all_zero_bar(self, bar_data):
+        is_not_zero = False
+        for value in bar_data:
+            if value:
+                is_not_zero = True
+                return not is_not_zero
+        return not is_not_zero
Index: /branches/rel_apv_10_7/usr/click/webui/htdocs/new/src/hive/report.py
===================================================================
--- /branches/rel_apv_10_7/usr/click/webui/htdocs/new/src/hive/report.py	(revision 38687)
+++ /branches/rel_apv_10_7/usr/click/webui/htdocs/new/src/hive/report.py	(working copy)
@@ -21,6 +21,7 @@
 from hive.monitor_log.TCPSYNDropStat import TCPSYNDropStat
 from hive.monitor_log.TCPStatistics import TCPStatistics
 from hive.monitor_log.SSLStatFacade import SSLStatFacade
+from hive.monitor_log.ChartController import ChartController
 
 TIMEFORMAT='%Y-%m-%d %X'
 chart_path = "/var/crash/statmon/"
@@ -70,19 +71,15 @@
     story.append(Paragraph(ptext, styles["alibaba"]))
     d = Draw_a_Line()
     story.append(d)
-    
-    table_header = [unicode(_('Time'))]
-    chart_ins = Line(height=370, is_animation=False)
-    for each in chart_item:
-        chart_ins.add(each['item_name'], time_list, each['item_value'], is_smooth=True,is_splitline_show=False, is_toolbox_show=False, yaxis_formatter=yaxis_name)
-        table_header.append(each['table_item_name']) if "table_item_name" in each else table_header.append(each['item_name'])
 
-    random_value = random.randint(100000, 999999)
-    report_chart_name = 'report_chart_%s.png' % random_value
-    env.render_chart_to_file(chart_ins, path=chart_path+report_chart_name)
-    im = Image(chart_path+report_chart_name, 7*inch, 3*inch)
+    chartController = ChartController()
+    d = chartController.generate_line_plot(chart_item, time_list, yaxis_name)
+    im = Image(d, 7*inch, 3*inch)
     story.append(im)
 
+    table_header = [unicode(_('Time'))]
+    for each in chart_item:
+        table_header.append(each['table_item_name']) if "table_item_name" in each else table_header.append(each['item_name'])
     data = [tuple(table_header)] + table_data
     t = generate_report_table(data, len(table_header))
     story.append(t)
@@ -96,16 +93,12 @@
     d = Draw_a_Line()
     story.append(d)
 
-    table_header = [chart_item['xaxis_name'], chart_item['yaxis_name']]
-    chart_ins = Bar(height=370, is_animation=False)
-    chart_ins.add(chart_item['xaxis_name'], xaxis_list, chart_item['item_value'], is_smooth=True,xaxis_interval=0, xaxis_rotate=xrotate,is_splitline_show=False, is_toolbox_show=False)
-    
-    random_value = random.randint(100000, 999999)
-    report_chart_name = 'report_chart_%s.png' % random_value
-    env.render_chart_to_file(chart_ins, path=chart_path+report_chart_name)
-    im = Image(chart_path+report_chart_name, 7*inch, 3*inch)
+    chartController = ChartController()
+    d = chartController.generate_vertical_bar(chart_item['xaxis_name'], xaxis_list, chart_item['item_value'])
+    im = Image(d, 7*inch, 3*inch)
     story.append(im)
 
+    table_header = [chart_item['xaxis_name'], chart_item['yaxis_name']]
     data = [tuple(table_header)] + table_data
     t = generate_report_table(data, len(table_header))
     story.append(t)
@@ -1641,9 +1634,9 @@
 def myLaterPages(canvas, doc):
     styles=getSampleStyleSheet()
     canvas.saveState()
-    p = Paragraph("<img src='%s' width=70 height=30></img>" % get_pdf_logo(), styles["Heading1"])  
+    p = Paragraph("<img src='%s' width=70 height=21></img>" % get_pdf_logo(), styles["Heading1"])
     w, h = p.wrap(doc.width, doc.bottomMargin)
-    p.drawOn(canvas, doc.leftMargin, doc.topMargin + doc.height - 0.5*cm)                                                
+    p.drawOn(canvas, doc.leftMargin, doc.topMargin + doc.height - 0.5*cm)
     #canvas.line(doc.leftMargin, doc.bottomMargin+doc.height + 0.5*cm, doc.leftMargin+doc.width, doc.bottomMargin+doc.height + 0.5*cm) 
     canvas.setFont('alibaba',10)
     canvas.drawRightString(7.5 * inch, 0.75 * inch, unicode(_("Page %s")) % (doc.page))
@@ -1671,7 +1664,7 @@
             [unicode(_("Model")), sys_info['system_model']], 
             [unicode(_("Cumulative Running Time")), sys_info['system_boot']]]
     t = generate_report_table_cover(data, 3)
-    im = Image(get_pdf_logo(), 2*inch, 0.8*inch)
+    im = Image(get_pdf_logo(), 2*inch, 0.57*inch)
     im.hAlign = 'LEFT'
     story = []
     story.append(im)
