pre-release updates, bug fixes
diff --git a/formatters.py b/formatters.py
index 2a47649..bbbf8d2 100644
--- a/formatters.py
+++ b/formatters.py
@@ -1,71 +1,128 @@
 import itertools
-import json
-import math
+from collections import defaultdict
+
+import texttable
+
+from statistic import med_dev
+
+# [{u'__meta__': {u'raw_cfg': u'[writetest * 2]\ngroup_reporting\nnumjobs=4\nwait_for_previous\nramp_time=5\nblocksize=4k\nfilename={FILENAME}\nrw=randwrite\ndirect=1\nbuffered=0\niodepth=1\nsize=100Mb\nruntime=10\ntime_based\n\n[readtest * 2]\ngroup_reporting\nnumjobs=4\nwait_for_previous\nramp_time=5\nblocksize=4k\nfilename={FILENAME}\nrw=randread\ndirect=1\nbuffered=0\niodepth=1\nsize=100Mb\nruntime=10\ntime_based\n'},
+#   u'res': {u'readtest': {u'action': u'randread',
+#                          u'blocksize': u'4k',
+#                          u'bw_mean': [349.61, 276.54],
+#                          u'clat': [11987.16, 15235.08],
+#                          u'concurence': 4,
+#                          u'direct_io': True,
+#                          u'iops': [316, 251],
+#                          u'jobname': u'readtest',
+#                          u'lat': [11987.52, 15235.46],
+#                          u'slat': [0.0, 0.0],
+#                          u'sync': False,
+#                          u'timings': [u'10', u'5']},
+#            u'writetest': {u'action': u'randwrite',
+#                           u'blocksize': u'4k',
+#                           u'bw_mean': [72.03, 61.84],
+#                           u'clat': [113525.86, 152836.42],
+#                           u'concurence': 4,
+#                           u'direct_io': True,
+#                           u'iops': [35, 23],
+#                           u'jobname': u'writetest',
+#                           u'lat': [113526.31, 152836.89],
+#                           u'slat': [0.0, 0.0],
+#                           u'sync': False,
+#                           u'timings': [u'10', u'5']}}},
+#  {u'__meta__': {u'raw_cfg': u'[writetest * 2]\ngroup_reporting\nnumjobs=4\nwait_for_previous\nramp_time=5\nblocksize=4k\nfilename={FILENAME}\nrw=randwrite\ndirect=1\nbuffered=0\niodepth=1\nsize=100Mb\nruntime=10\ntime_based\n\n[readtest * 2]\ngroup_reporting\nnumjobs=4\nwait_for_previous\nramp_time=5\nblocksize=4k\nfilename={FILENAME}\nrw=randread\ndirect=1\nbuffered=0\niodepth=1\nsize=100Mb\nruntime=10\ntime_based\n'},
+#   u'res': {u'readtest': {u'action': u'randread',
+#                          u'blocksize': u'4k',
+#                          u'bw_mean': [287.62, 280.76],
+#                          u'clat': [15437.57, 14741.65],
+#                          u'concurence': 4,
+#                          u'direct_io': True,
+#                          u'iops': [258, 271],
+#                          u'jobname': u'readtest',
+#                          u'lat': [15437.94, 14742.04],
+#                          u'slat': [0.0, 0.0],
+#                          u'sync': False,
+#                          u'timings': [u'10', u'5']},
+#            u'writetest': {u'action': u'randwrite',
+#                           u'blocksize': u'4k',
+#                           u'bw_mean': [71.18, 61.62],
+#                           u'clat': [116382.95, 153486.81],
+#                           u'concurence': 4,
+#                           u'direct_io': True,
+#                           u'iops': [32, 22],
+#                           u'jobname': u'writetest',
+#                           u'lat': [116383.44, 153487.27],
+#                           u'slat': [0.0, 0.0],
+#                           u'sync': False,
+#                           u'timings': [u'10', u'5']}}}]
 
 
-def get_formatter(test_type):
-    if test_type == "iozone" or test_type == "fio":
-        return format_io_stat
-    elif test_type == "pgbench":
-        return format_pgbench_stat
+def get_test_descr(data):
+    rw = {"randread": "rr",
+          "randwrite": "rw",
+          "read": "sr",
+          "write": "sw"}[data["action"]]
+
+    if data["direct_io"]:
+        sync_mode = 'd'
+    elif data["sync"]:
+        sync_mode = 's'
     else:
-        raise Exception("Cannot get formatter for type %s" % test_type)
+        sync_mode = 'a'
+
+    th_count = int(data['concurence'])
+
+    return "{0}{1}{2}_th{3}".format(rw, sync_mode,
+                                    data['blocksize'], th_count)
 
 
-def format_io_stat(res):
-    if len(res) != 0:
-        bw_mean = 0.0
-        for measurement in res:
-            bw_mean += measurement["bw_mean"]
+def format_results_for_console(test_set):
+    data_for_print = []
+    tab = texttable.Texttable()
+    tab.set_deco(tab.HEADER | tab.VLINES | tab.BORDER | tab.HLINES)
+    tab.set_cols_align(["l", "r", "r", "r", "r"])
 
-        bw_mean /= len(res)
+    for test_name, data in test_set['res'].items():
+        descr = get_test_descr(data)
 
-        it = ((bw_mean - measurement["bw_mean"]) ** 2 for measurement in res)
-        bw_dev = sum(it) ** 0.5
+        iops, _ = med_dev(data['iops'])
+        bw, bwdev = med_dev(data['bw_mean'])
 
-        meta = res[0]['__meta__']
+        # 3 * sigma
+        dev_perc = int((bwdev * 300) / bw)
 
-        sync = meta['sync']
-        direct = meta['direct_io']
+        params = (descr, int(iops), int(bw), dev_perc,
+                  int(med_dev(data['lat'])[0]) // 1000)
+        data_for_print.append(params)
 
-        if sync and direct:
-            ss = "d+"
-        elif sync:
-            ss = "s"
-        elif direct:
-            ss = "d"
-        else:
-            ss = "a"
+    header = ["Description", "IOPS", "BW KBps", "Dev * 3 %", "LAT ms"]
+    tab.add_row(header)
+    tab.header = header
 
-        key = "{0} {1} {2} {3}k".format(meta['action'], ss,
-                                        meta['concurence'],
-                                        meta['blocksize'])
+    map(tab.add_row, data_for_print)
 
-        data = json.dumps({key: (int(bw_mean), int(bw_dev))})
-
-        return data
+    return tab.draw()
 
 
-def format_pgbench_stat(res):
-    """
-    Receives results in format:
-    "<num_clients> <num_transactions>: <tps>
-     <num_clients> <num_transactions>: <tps>
-     ....
-    "
-    """
-    if res:
-        data = {}
-        grouped_res = itertools.groupby(res, lambda x: x[0])
-        for key, group in grouped_res:
-            results = list(group)
-            sum_res = sum([r[1] for r in results])
-            mean = sum_res/len(results)
-            sum_sq = sum([(r[1] - mean) ** 2 for r in results])
-            if len(results) > 1:
-                dev = math.sqrt(sum_sq / (len(results) - 1))
-            else:
-                dev = 0
-            data[key] = (mean, dev)
-        return data
-
+# def format_pgbench_stat(res):
+#     """
+#     Receives results in format:
+#     "<num_clients> <num_transactions>: <tps>
+#      <num_clients> <num_transactions>: <tps>
+#      ....
+#     "
+#     """
+#     if res:
+#         data = {}
+#         grouped_res = itertools.groupby(res, lambda x: x[0])
+#         for key, group in grouped_res:
+#             results = list(group)
+#             sum_res = sum([r[1] for r in results])
+#             mean = sum_res/len(results)
+#             sum_sq = sum([(r[1] - mean) ** 2 for r in results])
+#             if len(results) > 1:
+#                 dev = (sum_sq / (len(results) - 1))
+#             else:
+#                 dev = 0
+#             data[key] = (mean, dev)
+#         return data