Improved io charts
diff --git a/chart/__init__.py b/chart/__init__.py
index 4ebfd5e..e69de29 100644
--- a/chart/__init__.py
+++ b/chart/__init__.py
@@ -1 +0,0 @@
-__author__ = 'yportnova'
diff --git a/chart/charts.py b/chart/charts.py
index ec2a86f..b53ac5a 100644
--- a/chart/charts.py
+++ b/chart/charts.py
@@ -6,10 +6,11 @@
 from GChartWrapper import Line
 from GChartWrapper import constants
 
-from config import CHARTS_IMG_PATH
+from config import cfg_dict
+CHARTS_IMG_PATH = cfg_dict['charts_img_path']
 
 
-COLORS = ["1569C7", "81D8D0", "307D7E", "5CB3FF", "blue", "indigo"]
+COLORS = ["1569C7", "81D8D0", "307D7E", "5CB3FF", "0040FF", "81DAF5"]
 constants.MARKERS += 'E'  # append E marker to available markers
 
 
@@ -19,8 +20,9 @@
     t.start()
 
 
-def render_vertical_bar(title, legend, dataset, width=700, height=400, scale_x=None,
-                        scale_y=None):
+def render_vertical_bar(title, legend, dataset, width=700, height=400,
+                        scale_x=None, scale_y=None, label_x=None,
+                        label_y=None, lines=()):
     """
     Renders vertical bar group chart
 
@@ -29,7 +31,7 @@
     :param dataset - list of values for each type (value, deviation)
         Example:
             [
-                [(10,(9,11)), (11, (3,12)), (10,(9,11))], # bar1 values
+                [(10,1), (11, 2), (10,1)], # bar1 values
                 [(30,(29,33)),(35,(33,36)), (30,(29,33))], # bar2 values
                 [(20,(19,21)),(20,(13, 24)), (20,(19,21))] # bar 3 values
             ]
@@ -68,27 +70,49 @@
         # deviations.extend(zip(*dev))
         deviations.extend(zip(*display_dev))
 
-    bar.dataset(values + deviations, series=len(values))
+    # bar.dataset(values + deviations, series=len(values))
+    bar.dataset(values + deviations + [l[0] for l in lines], series=len(values))
     bar.axes.type('xyy')
-    bar.axes.label(2, None, 'Kbps')
+    bar.axes.label(2, None, label_x)
     if scale_x:
         bar.axes.label(0, *scale_x)
 
-    max_value = (max([max(l) for l in values + deviations]))
+    max_value = (max([max(l) for l in values + deviations + [lines[1][0]]]))
     bar.axes.range(1, 0, max_value)
     bar.axes.style(1, 'N*s*')
     bar.axes.style(2, '000000', '13')
 
-    bar.scale(0, max_value)
+    bar.scale(*[0, max_value] * len(values + deviations))
 
     bar.bar('r', '.1', '1')
-    for i in range(len(legend)):
+    for i in range(1):
         bar.marker('E', '000000', '%s:%s' % ((len(values) + i*2), i),
                    '', '1:10')
-    bar.legend(*legend)
-    bar.color(*COLORS[:len(values)])
+    bar.color(*COLORS)
     bar.size(width, height)
 
+    axes_type = "xyy"
+
+    scale = [0, max_value] * len(values + deviations)
+    if lines:
+        line_n = 0
+        for data, label, axe, leg in lines:
+            bar.marker('D', COLORS[len(values) + line_n],
+                       (len(values + deviations)) + line_n, 0, 3)
+            max_val_l = max(data)
+            if axe:
+                bar.axes.type(axes_type + axe)
+                bar.axes.range(len(axes_type), 0, max_val_l)
+                bar.axes.style(len(axes_type), 'N*s*')
+                bar.axes.label(len(axes_type) + 1, None, label)
+                bar.axes.style(len(axes_type) + 1, '000000', '13')
+                axes_type += axe
+                line_n += 1
+            legend.append(leg)
+            scale += [0, max_val_l]
+
+    bar.legend(*legend)
+    bar.scale(*scale)
     img_name = hashlib.md5(str(bar)).hexdigest() + ".png"
     img_path = os.path.join(CHARTS_IMG_PATH, img_name)
     if not os.path.exists(img_path):
@@ -111,7 +135,6 @@
     line.color(*COLORS[:len(legend)])
     line.size(width, height)
 
-
     img_name = hashlib.md5(str(line)).hexdigest() + ".png"
     img_path = os.path.join(CHARTS_IMG_PATH, img_name)
     if not os.path.exists(img_path):
diff --git a/koder.yaml b/koder.yaml
index 028a975..21dea2e 100644
--- a/koder.yaml
+++ b/koder.yaml
@@ -19,3 +19,5 @@
 
 logging:
     extra_logs: 1
+charts_img_path: tmp/charts
+output_dest: results.html
\ No newline at end of file
diff --git a/report.py b/report.py
index aaaaa53..e21665d 100644
--- a/report.py
+++ b/report.py
@@ -1,7 +1,11 @@
 import argparse
 from collections import OrderedDict
+import itertools
+import math
+import re
 
 from chart import charts
+import formatters
 from utils import ssize_to_b
 
 
@@ -24,58 +28,95 @@
     return parser.parse_args(argv)
 
 
-def build_vertical_bar(results):
+def pgbench_chart_data(results):
+    """
+    Format pgbench results for chart
+    """
     data = {}
     charts_url = []
 
-    for build, results in results.items():
-        for key, value in results.results.items():
-            keys = key.split(' ')
-            if not data.get(keys[2]):
-                data[keys[2]] = {}
-            if not data[keys[2]].get(build):
-                data[keys[2]][build] = {}
-            data[keys[2]][build][
-                ' '.join([keys[0], sync_async_view[keys[1]]])] = value
+    formatted_res = formatters.format_pgbench_stat(results)
+    for key, value in formatted_res.items():
+        num_cl, num_tr = key.split(' ')
+        data.setdefault(num_cl, {}).setdefault(build, {})
+        data[keys[z]][build][
+            ' '.join(keys)] = value
 
     for name, value in data.items():
-        for op_type, operations in OPERATIONS:
-            title = "Block size: " + name
-            legend = []
-            dataset = []
+        title = name
+        legend = []
+        dataset = []
 
-            scale_x = []
+        scale_x = []
 
-            for build_id, build_results in value.items():
-                vals = []
+        for build_id, build_results in value.items():
+            vals = []
+            OD = OrderedDict
+            ordered_build_results = OD(sorted(build_results.items(),
+                                       key=lambda t: t[0]))
+            scale_x = ordered_build_results.keys()
+            for key in scale_x:
+                res = build_results.get(key)
+                if res:
+                    vals.append(res)
+            if vals:
+                dataset.append(vals)
+                legend.append(build_id)
 
-                for key in operations:
-                    res = build_results.get(key)
-                    if res:
-                        vals.append(res)
-                        scale_x.append(key)
-                if vals:
-                    dataset.append(vals)
-                    legend.append(build_id)
-
-            if dataset:
-                charts_url.append(str(charts.render_vertical_bar
-                                  (title, legend, dataset, scale_x=scale_x)))
+        if dataset:
+            charts_url.append(str(charts.render_vertical_bar
+                              (title, legend, dataset, scale_x=scale_x)))
     return charts_url
 
 
-def build_lines_chart(results):
+def build_vertical_bar(results, z=0):
+    data = {}
+    charts_url = []
+    for build, res in results:
+        formatted_res = formatters.get_formatter(build)(res)
+        for key, value in formatted_res.items():
+            keys = key.split(' ')
+            data.setdefault(keys[z], {}).setdefault(build, {})
+            data[keys[z]][build][
+                ' '.join(keys)] = value
+
+    for name, value in data.items():
+        title = name
+        legend = []
+        dataset = []
+
+        scale_x = []
+
+        for build_id, build_results in value.items():
+            vals = []
+            OD = OrderedDict
+            ordered_build_results = OD(sorted(build_results.items(),
+                                       key=lambda t: t[0]))
+            scale_x = ordered_build_results.keys()
+            for key in scale_x:
+                res = build_results.get(key)
+                if res:
+                    vals.append(res)
+            if vals:
+                dataset.append(vals)
+                legend.append(build_id)
+
+        if dataset:
+            charts_url.append(str(charts.render_vertical_bar
+                              (title, legend, dataset, scale_x=scale_x)))
+    return charts_url
+
+
+def build_lines_chart(results, z=0):
     data = {}
     charts_url = []
 
-    for build, results in results.items():
-        for key, value in results.results.items():
+    for build, res in results:
+        formatted_res = formatters.get_formatter(build)(res)
+        for key, value in formatted_res.items():
             keys = key.split(' ')
-            if not data.get(' '.join([keys[0], keys[1]])):
-                data[' '.join([keys[0], keys[1]])] = {}
-            if not data[' '.join([keys[0], keys[1]])].get(build):
-                data[' '.join([keys[0], keys[1]])][build] = {}
-            data[' '.join([keys[0], keys[1]])][build][keys[2]] = value
+            data.setdefault(key[z], {})
+            data[key[z]].setdefault(build, {})[keys[1]] = value
 
     for name, value in data.items():
         title = name
@@ -99,7 +140,7 @@
     return charts_url
 
 
-def render_html(charts_urls):
+def render_html(charts_urls, dest):
     templ = open("report.html", 'r').read()
     body = "<div><ol>%s</ol></div>"
     li = "<li><img src='%s'></li>"
@@ -107,22 +148,82 @@
     for chart in charts_urls:
         ol.append(li % chart)
     html = templ % {'body': body % '\n'.join(ol)}
-    open('results.html', 'w').write(html)
+    open(dest, 'w').write(html)
 
 
+def build_io_chart(res):
+    pass
+
+
+def render_html_results(ctx, dest):
+    charts = []
+    import ipdb;ipdb.set_trace()
+    for res in ctx.results:
+        if res[0] == "io":
+            charts.append(build_io_chart(res))
+
+    bars = build_vertical_bar(ctx.results)
+    lines = build_lines_chart(ctx.results)
+
+    render_html(bars + lines, dest)
+
 # def report(url, email=None, password=None):
 #     results = storage.recent_builds()
 #     bars = build_vertical_bar(results)
 #     lines = build_lines_chart(results)
 #
 #     render_html(bars + lines)
+#
 
-#
-# def main(argv):
-#     opts = parse_args(argv)
-#     report(opts.url)
-#     return 0
-#
-#
-# if __name__ == '__main__':
-#     exit(main(sys.argv[1:]))
+
+def calc_dev(l):
+    sum_res = sum(l)
+    mean = sum_res/len(l)
+    sum_sq = sum([(r - mean) ** 2 for r in l])
+    if len(l) > 1:
+        return math.sqrt(sum_sq / (len(l) - 1))
+    else:
+        return 0
+
+
+def main():
+    from tests.disk_test_agent import parse_output
+    out = parse_output(
+        open("results/io_scenario_check_th_count.txt").read()).next()
+    results = out['res']
+
+    charts_url = []
+    charts_data = {}
+    for test_name, test_res in results.items():
+        blocksize = test_res['blocksize']
+        op_type = "sync" if test_res['sync'] else "direct"
+        chart_name = "Block size: %s %s" % (blocksize, op_type)
+        lat = sum(test_res['lat']) / len(test_res['lat']) / 1000
+        lat_dev = calc_dev(test_res['lat'])
+        iops = sum(test_res['iops']) / len(test_res['iops'])
+        iops_dev = calc_dev(test_res['iops'])
+        bw = sum(test_res['bw_mean']) / len(test_res['bw_mean'])
+        bw_dev = calc_dev(test_res['bw_mean'])
+        conc = test_res['concurence']
+        vals = ((lat, lat_dev), (iops, iops_dev), (bw, bw_dev))
+        charts_data.setdefault(chart_name, {})[conc] = vals
+
+    for chart_name, chart_data in charts_data.items():
+        legend = ["bw"]
+        ordered_data = OrderedDict(sorted(chart_data.items(),
+                                          key=lambda t: t[0]))
+
+        lat_d, iops_d, bw_d = zip(*ordered_data.values())
+        bw_sum = [vals[2][0] * conc for conc, vals in ordered_data.items()]
+
+        chart_url = str(charts.render_vertical_bar(
+            chart_name, legend, [bw_d], label_x="KBps",
+            scale_x=ordered_data.keys(),
+            lines=[(zip(*lat_d)[0], 'msec', 'rr', 'lat'), (bw_sum, None, None, 'bw_sum')]))
+        charts_url.append(chart_url)
+        render_html(charts_url, "results.html")
+    return 0
+
+
+if __name__ == '__main__':
+    exit(main())
diff --git a/run_test.py b/run_test.py
index 1dac2e5..3cbdae2 100755
--- a/run_test.py
+++ b/run_test.py
@@ -10,6 +10,8 @@
 import collections
 
 from concurrent.futures import ThreadPoolExecutor
+import formatters
+import report
 
 import utils
 import ssh_utils
@@ -266,10 +268,14 @@
 def report_stage(cfg, ctx):
     output_dest = cfg.get('output_dest')
     if output_dest is not None:
-        with open(output_dest, "w") as fd:
-            data = {"sensor_data": ctx.sensor_data,
-                    "results": ctx.results}
-            fd.write(json.dumps(data))
+        if output_dest.endswith(".html"):
+            report.render_html_results(ctx, output_dest)
+            logger.info("Results were stored in %s" % output_dest)
+        else:
+            with open(output_dest, "w") as fd:
+                data = {"sensor_data": ctx.sensor_data,
+                        "results": ctx.results}
+                fd.write(json.dumps(data))
     else:
         print "=" * 20 + " RESULTS " + "=" * 20
         pprint.pprint(ctx.results)