# -*- coding: utf-8 -*-
"""
Created on Aug 8, 2013

@author: noob
"""
from common.util.meta import escape_string
from common.util.decorators import memoized_property
from .plots_base import TimelinePlot
import logging


class QuantilesTimelinePlot(TimelinePlot):
    title = 'Квантили времен ответа'
    graphs = 'expect', '50', '75', '80', '85', '90', '95', '98', '99', '100'

    @staticmethod
    def color_mapping(quantile):
        color_map = {
            'expect': '#303030',
            '50': '#f85750',
            '75': 'Coral',
            '80': 'DarkOrange',
            '85': 'Orange',
            '90': 'gold',
            '95': '#b8e13d',
            '98': '#66af5f',
            '99': '#7cb3f1',
            '100': '#b2b8c8'
        }
        try:
            color = color_map[quantile]
            return color
        except KeyError:
            logging.warning('Unexpected quantile: {}'.format(quantile))
            return None

    @property
    def graphs_settings(self):
        settings = [
            {'values': 'expect',
             'title': 'среднее',
             'disabled': True,
             'type': 'line',
             'color': self.color_mapping('expect'),
             }
        ]
        settings += [
            {'values': quantile,
             'title': quantile + '%',
             'disabled': True,
             'type': 'line',
             'color': self.color_mapping(quantile),
             } for quantile in ('100', '99')
            ]
        settings += [
            {'values': quantile,
             'title': quantile + '%',
             'color': self.color_mapping(quantile),
             } for quantile in ('98', '95', '90', '85', '80', '75', '50')
            ]

        return settings

    @memoized_property
    def quantiles_data(self):
        """
        retrieves monitoring data from database
        """
        sql = '''
            select mq50, mq75, mq80, mq85, mq90, mq95, mq98, mq99, mq100 from
            (
            select intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t
            from loaddb.rt_microsecond_details_buffer
            where job_id={job}
            and job_date=toDate({job_date})
            and tag = ''
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            group by t
            order by t
            ) all left join
            (
            select
            round(max(q50), 3) as mq50,
            round(max(q75), 3) as mq75,
            round(max(q80), 3) as mq80,
            round(max(q85), 3) as mq85,
            round(max(q90), 3) as mq90,
            round(max(q95), 3) as mq95,
            round(max(q98), 3) as mq98,
            round(max(q99), 3) as mq99,
            round(max(q100), 3) as mq100,
            intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t
            from loaddb.rt_quantiles_buffer
            where job_id={job}
            and job_date=toDate({job_date})
            '''
        if self.job_obj.multitag and self.tag:
            sql += 'and tag in ({cases_with_tag}) '
        else:
            sql += '''and tag='{tag}'  '''
        sql += '''
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            group by t
            order by t
            )
            using t'''
        fetched_data = self.ch_client.select(sql, query_params=self.query_params)
        transponed_data = list(zip(*fetched_data))
        quantiles = ('50', '75', '80', '85', '90', '95', '98', '99', '100')
        data = {quantile: transponed_data[quantiles.index(quantile)] if transponed_data else [] for quantile in
                quantiles}
        return data

    @property
    def expect_data(self):
        """
        retrieves monitoring data from database
        """
        # FIXME: Вилка
        sql = '''
            select data from
            (
            select intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t
            from loaddb.rt_microsecond_details_buffer
            where job_id={job}
            and job_date=toDate({job_date})
            and tag = ''
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            group by t
            order by t
            ) all left join
            (
            select
            round(avg(toFloat64((connect_time_sum + send_time_sum + latency_sum + receive_time_sum)/1000/resps)), 3) as data,
            intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t
            from loaddb.rt_microsecond_details_buffer
            where job_id={job}
            and job_date=toDate({job_date})
        '''
        if self.job_obj.multitag and self.tag:
            sql += 'and tag in ({cases_with_tag}) '
        else:
            sql += '''and tag='{tag}'  '''
        sql += '''
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            group by t
            order by t
            )
            using t'''
        data = {'expect': [v[0] for v in self.ch_client.select(sql, query_params=self.query_params)]}
        return data

    @memoized_property
    def data(self):
        """
        dict with graphs as keys and values as arrays
        """
        self.quantiles_data.update(self.expect_data)
        return self.quantiles_data

    @property
    def yaxis(self):
        return [
            {
                'label': self.scheme_type['type'],
                'overrides': {'gridLineWidth': 0, 'min': 0, 'allowDecimals': False},
                'position': 'right',
                'type': 'line',
                'graphs': [
                    {
                        'values': self.scheme_data,
                        'zIndex': 2,
                        'disabled': self.job_obj.uses_jmeter and self.scheme_type['type'] == 'instances',
                        'title': self.scheme_type['type'],
                        'color': self.scheme_type['color']
                    }
                ]
            },
            {
                'label': 'ms',
                'overrides': {'min': 0, 'allowDecimals': False},
                'type': 'area',
                'position': 'left',
                'graphs': self.graphs_settings
            },
        ]


class TimesDistTimelinePlot(TimelinePlot):
    title = 'Распределение времен ответа'
    graphs = '0-0.\u2089\u2089\u2089', '1-9', '10-99', '100-999', '1000+'

    @staticmethod
    def color_mapping(interval):
        color_map = {
            '0-0.\u2089\u2089\u2089': '#66af5f',  # подстрочные девятки
            '1-9': '#b8e13d',
            '10-99': '#ffd43f',  # '#e9f66e',
            '100-999': '#fca43f',
            '1000+': '#f85750',
        }
        try:
            color = color_map[interval]
            return color
        except KeyError:
            logging.warning('Unexpected interval: {}'.format(interval))
            return None

    @property
    def graphs_settings(self):
        """
        settings for highcharts series
        """
        return [{
                    'values': interval,
                    'title': interval,
                    'color': self.color_mapping(interval),
                } for interval in self.graphs]

    @memoized_property
    def data(self):
        """
        dict with graphs as keys and values as arrays
        """
        data = {}
        graphs_mapping = {
            '0-0.\u2089\u2089\u2089': 0,
            '1-9': 1,
            '10-99': 2,
            '100-999': 3,
            '1000+': 4
        }
        sql = '''
            select data from
            (
            select intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t
            from loaddb.rt_microsecond_details_buffer
            where job_id={job}
            and job_date=toDate({job_date})
            and tag = ''
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            group by t
            order by t
            ) all left join
            (
            select
            round(toUInt32(sum(cnt))/{compress_ratio}, 3) as data,
            intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t
            from loaddb.rt_microsecond_histograms_buffer
            where job_id={job}
            and job_date=toDate({job_date})
            '''
        if self.job_obj.multitag and self.tag:
            sql += 'and tag in ({cases_with_tag}) '
        else:
            sql += '''and tag='{tag}'  '''
        sql += '''
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            and {length}
            group by t
            order by t
            )
            using t'''
        query_params = self.query_params.copy()
        for length in list(graphs_mapping.keys()):
            if graphs_mapping[length] == 4:
                query_params['length'] = 'toInt8(length(toString(bin)))>=7'
            elif graphs_mapping[length] == 0:
                query_params['length'] = 'toInt8(length(toString(bin)))<=3'
            else:
                # microseconds
                query_params['length'] = 'toInt8(length(toString(bin)))={}'.format(int(graphs_mapping[length]) + 3)
            data[length] = [v[0] for v in self.ch_client.select(sql, query_params=query_params)]
        return data

    @property
    def yaxis(self):
        yaxis = []
        if self.scheme_type['type'] == 'rps':
            yaxis += [
                {
                    'stacking': 'normal',
                    'overrides': {'min': 0, 'allowDecimals': False},
                    'label': self.scheme_type['type'],
                    'position': 'right',
                    'type': 'area',
                    'graphs': [
                                  {
                                      'values': self.scheme_data,
                                      'zIndex': 2,
                                      'stacking': None,
                                      'type': 'line',
                                      'title': self.scheme_type['type'],
                                      'color': self.scheme_type['color'],
                                  }
                              ] + self.graphs_settings
                },
            ]
        elif self.scheme_type['type'] == 'instances':
            yaxis += [
                {
                    'label': self.scheme_type['type'],
                    'labels': {'format': '"{value:,f}"'},
                    'overrides': {'gridLineWidth': 0, 'min': 0, 'labels': {'enabled': True, }, 'allowDecimals': False},
                    'position': 'right',
                    'type': 'line',
                    'graphs': [
                        {
                            'values': self.scheme_data,
                            'zIndex': 2,
                            'disabled': self.job_obj.uses_jmeter,
                            'title': self.scheme_type['type'],
                            'color': self.scheme_type['color']
                        }
                    ]
                },
                {
                    'stacking': 'normal',
                    'overrides': {'min': 0},
                    'label': '',
                    'type': 'area',
                    'position': 'left',
                    'graphs': self.graphs_settings
                },
            ]
        return yaxis


class HTTPCodesTimelinePlot(TimelinePlot):
    title = 'HTTP коды'

    @staticmethod
    def color_mapping(http_code):
        color_map = {0: '#ccc',
                     2: '#66af5f',
                     3: '#60becc',
                     4: '#ffd43f',
                     5: '#ff3f3f', }
        try:
            color = color_map[http_code // 100]
            return color
        except KeyError:
            logging.warning('Unexpected http_code: {}'.format(http_code))
            return None

    @memoized_property
    def graphs(self):
        sql = '''
            select distinct toUInt32(code)
            from loaddb.proto_codes_buffer
            where job_id={job}
            and job_date=toDate({job_date})
            '''
        if self.job_obj.multitag and self.tag:
            sql += 'and tag in ({cases_with_tag}) '
        else:
            sql += '''and tag='{tag}'  '''
        sql += '''
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            '''
        return [c[0] for c in self.ch_client.select(sql, query_params=self.query_params)]

    @property
    def graphs_settings(self):
        """
        settings for highcharts series
        """
        if self.graphs == [200]:
            settings = [{'values': http_code,
                         'title': 'http_' + str(http_code),
                         'color': self.color_mapping(http_code)} for http_code in self.graphs]
        else:
            settings = [{'values': http_code,
                         'title': 'http_' + str(http_code),
                         'disabled': True,
                         'color': self.color_mapping(http_code)} for http_code in self.graphs if http_code == 200]
            settings += [{'values': http_code,
                          'title': 'http_' + str(http_code),
                          'color': self.color_mapping(http_code)} for http_code in self.graphs if http_code != 200]

        return settings

    @memoized_property
    def data(self):
        """
        retrieves monitoring data from database
        """
        data = {}
        sql = '''
            select data from
            (
            select intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t
            from loaddb.rt_microsecond_details_buffer
            where job_id={job}
            and job_date=toDate({job_date})
            and tag = ''
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            group by t
            order by t
            ) all left join
            (
            select
            round(sum(cnt)/{compress_ratio}, 3) as data,
            intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t
            from loaddb.proto_codes_buffer
            where job_id={job}
            and job_date=toDate({job_date})
            '''
        if self.job_obj.multitag and self.tag:
            sql += 'and tag in ({cases_with_tag}) '
        else:
            sql += '''and tag='{tag}'  '''
        sql += '''
            and code={code}
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            group by t
            order by t
            )
            using t
            '''
        query_params = self.query_params.copy()
        for code in self.graphs:
            query_params['code'] = code
            data[code] = [v[0] for v in self.ch_client.select(sql, query_params=query_params)]
        return data

    @property
    def yaxis(self):
        if len(self.graphs) == 1 and self.scheme_type['type'] == 'rps':
            yaxis = [{
                'stacking': 'normal',
                'overrides': {'min': 0, 'allowDecimals': False},
                'label': self.scheme_type['type'],
                'position': 'right',
                'type': 'area',
                'graphs': [
                              {
                                  'values': self.scheme_data,
                                  'zIndex': 2,
                                  'stacking': None,
                                  'type': 'line',
                                  'title': self.scheme_type['type'],
                                  'color': self.scheme_type['color']
                              }
                          ] + self.graphs_settings
            },
            ]
        elif self.scheme_type['type'] == 'instances':
            yaxis = [
                {
                    'label': self.scheme_type['type'],
                    'overrides': {'gridLineWidth': 0, 'min': 0, 'labels': {'enabled': True}, 'allowDecimals': False},
                    'position': 'right',
                    'type': 'line',
                    'graphs': [
                        {
                            'values': self.scheme_data,
                            'zIndex': 2,
                            'disabled': self.job_obj.uses_jmeter,
                            'title': self.scheme_type['type'],
                            'color': self.scheme_type['color']
                        }
                    ]
                },
                {
                    'stacking': 'normal',
                    'overrides': {'allowDecimals': False, 'min': 0},
                    'label': '',
                    'type': 'area',
                    'position': 'left',
                    'graphs': self.graphs_settings
                },
            ]
        else:
            yaxis = [
                {
                    'label': self.scheme_type['type'],
                    'overrides': {'min': 0, 'allowDecimals': False},
                    'position': 'right',
                    'type': 'line',
                    'graphs': [
                        {
                            'values': self.scheme_data,
                            'zIndex': 2,
                            'title': self.scheme_type['type'],
                            'color': self.scheme_type['color']
                        }
                    ]
                },
                {
                    'stacking': 'normal',
                    'overrides': {'gridLineWidth': 0, 'min': 0, 'labels': {'enabled': True}, 'allowDecimals': False},
                    'label': '',
                    'type': 'area',
                    'position': 'left',
                    'graphs': self.graphs_settings
                },
            ]
        return yaxis


class NetCodesTimelinePlot(TimelinePlot):
    title = "Сетевые коды"

    @memoized_property
    def graphs(self):
        sql = '''
            select distinct toUInt32(code)
            from loaddb.net_codes_buffer
            where job_id={job}
            and job_date=toDate({job_date})
            '''
        if self.job_obj.multitag and self.tag:
            sql += 'and tag in ({cases_with_tag}) '
        else:
            sql += '''and tag='{tag}'  '''
        sql += '''
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            '''
        return [c[0] for c in self.ch_client.select(sql, query_params=self.query_params)]

    @property
    def graphs_settings(self):
        """
        settings for highcharts series
        """
        if self.graphs == [0]:
            settings = [{'values': net_code,
                         'title': 'OK',
                         'color': self.color_mapping(net_code)} for net_code in self.graphs]
        else:
            settings = [{'values': net_code,
                         'title': 'OK',
                         'disabled': True,
                         'color': self.color_mapping(net_code)} for net_code in self.graphs if not net_code]
            settings += [{'values': net_code,
                          'title': 'net_' + str(net_code),
                          'color': self.color_mapping(net_code)} for net_code in self.graphs if net_code]
        return settings

    @memoized_property
    def data(self):
        """
        retrieves monitoring data from database
        """
        data = {}
        sql = '''
            select data from
            (
            select intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t
            from loaddb.rt_microsecond_details_buffer
            where job_id={job}
            and job_date=toDate({job_date})
            and tag = ''
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            group by t
            order by t
            ) all left join
            (
            select
            round(sum(cnt)/{compress_ratio}, 3) as data,
            intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t
            from loaddb.net_codes_buffer
            where job_id={job}
            and job_date=toDate({job_date})
            '''
        if self.job_obj.multitag and self.tag:
            sql += 'and tag in ({cases_with_tag}) '
        else:
            sql += '''and tag='{tag}'  '''
        sql += '''
            and code={code}
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            group by t
            order by t
            )
            using t
            '''
        query_params = self.query_params.copy()
        for code in self.graphs:
            query_params['code'] = code
            data[code] = [v[0] for v in self.ch_client.select(sql, query_params=query_params)]
        return data

    @staticmethod
    def color_mapping(net_code):
        """

        :param net_code: int
        """
        if net_code:
            return None
        else:
            return '#66af5f'  # OK green

    @property
    def yaxis(self):
        if len(self.graphs) == 1 and self.scheme_type['type'] == 'rps':
            yaxis = [{
                'stacking': 'normal',
                'overrides': {'min': 0, 'allowDecimals': False},
                'label': self.scheme_type['type'],
                'position': 'right',
                'type': 'area',
                'graphs': [
                              {
                                  'values': self.scheme_data,
                                  'zIndex': 2,
                                  'stacking': None,
                                  'type': 'line',
                                  'title': self.scheme_type['type'],
                                  'color': self.scheme_type['color']
                              }
                          ] + self.graphs_settings
            },
            ]
        elif self.scheme_type['type'] == 'instances':
            yaxis = [
                {
                    'label': self.scheme_type['type'],
                    'overrides': {'gridLineWidth': 0, 'min': 0, 'labels': {'enabled': True}, 'allowDecimals': False},
                    'position': 'right',
                    'type': 'line',
                    'graphs': [
                        {
                            'values': self.scheme_data,
                            'zIndex': 2,
                            'disabled': self.job_obj.uses_jmeter,
                            'title': self.scheme_type['type'],
                            'color': self.scheme_type['color']
                        }
                    ]
                },
                {
                    'stacking': 'normal',
                    'overrides': {'allowDecimals': False, 'min': 0},
                    'label': '',
                    'type': 'area',
                    'position': 'left',
                    'graphs': self.graphs_settings
                },
            ]
        else:
            yaxis = [
                {
                    'label': self.scheme_type['type'],
                    'overrides': {'min': 0, 'allowDecimals': False, },
                    'position': 'right',
                    'type': 'line',
                    'graphs': [
                        {
                            'values': self.scheme_data,
                            'zIndex': 2,
                            'title': self.scheme_type['type'],
                            'color': self.scheme_type['color']
                        }
                    ]
                },
                {
                    'stacking': 'normal',
                    'overrides': {'gridLineWidth': 0, 'min': 0, 'labels': {'enabled': True}, 'allowDecimals': False, },
                    'label': '',
                    'type': 'area',
                    'position': 'left',
                    'graphs': self.graphs_settings
                },
            ]
        return yaxis


class CasesTimelinePlot(TimelinePlot):
    title = 'Количество ответов по тэгам'

    @memoized_property
    def data(self):
        """
        dict with graphs as keys and values as arrays
        """
        data = {}
        sql = '''
            select data from
            (
            select intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t
            from loaddb.rt_microsecond_details_buffer
            where job_id={job}
            and job_date=toDate({job_date})
            and tag = ''
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            group by t
            order by t
            ) all left join
            (
            select
            round(sum(resps)/{compress_ratio}, 3) as data,
            intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t
            from loaddb.rt_microsecond_details_buffer
            where job_id={job}
            and job_date=toDate({job_date})
            and tag {cases_with_tag}
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            group by t
            order by t
            )
            using t'''
        query_params = self.query_params.copy()
        for graph in self.graphs:
            if self.job_obj.multitag and graph:
                query_params['cases_with_tag'] = 'in ({}) '.format(','.join(
                    ["'{}'".format(escape_string(case)) for case in self.job_obj.cases if
                     graph in case.split('|')]))
            else:
                query_params['cases_with_tag'] = "='{}' ".format(escape_string(graph))
            data[graph] = [v[0] for v in self.ch_client.select(sql, query_params=query_params)]
        return data

    @memoized_property
    def graphs(self):
        """
        self case or job cases and overall as empty string
        """

        if len(self.job_obj.tags) > self.series_limit and not self.tag:
            self.subtitle = 'Слишком много тэгов, см. по отдельности или в таблицах.'
            return ['']
        elif self.job_obj.tags and not self.tag:
            return self.job_obj.tags
        else:
            return [self.tag]

    @property
    def graphs_settings(self):
        """
        settings for highcharts series
        """

        if len(self.job_obj.tags) > self.series_limit and not self.tag:
            settings = [{'values': '',
                         'title': 'Весь тест',
                         'type': 'area',
                         }]
        elif self.job_obj.tags and not self.tag:
            settings = [{'values': tag,
                         'title': tag,
                         'type': 'area',
                         } for tag in self.graphs if tag]
        else:
            settings = [{'values': self.tag,
                         'title': self.tag or 'Весь тест',
                         'type': 'area',
                         }]
        return settings

    @property
    def yaxis(self):
        if self.tag == '' and self.scheme_type['type'] == 'rps':
            yaxis = [{
                'stacking': 'normal',
                'overrides': {'min': 0, 'allowDecimals': False},
                'label': self.scheme_type['type'],
                'position': 'right',
                'type': 'line',
                'graphs': [
                              {
                                  'values': self.scheme_data,
                                  'zIndex': 2,
                                  'stacking': 'normal',
                                  'title': self.scheme_type['type'],
                                  'color': self.scheme_type['color']
                              }
                          ] + self.graphs_settings
            },
            ]
        else:
            yaxis = [
                {
                    'label': self.scheme_type['type'],
                    'overrides': {'gridLineWidth': 0, 'min': 0, 'labels': {'enabled': False}, 'allowDecimals': False, },
                    'position': 'right',
                    'type': 'line',
                    'graphs': [
                        {
                            'values': self.scheme_data,
                            'zIndex': 2,
                            'disabled': self.job_obj.uses_jmeter and self.scheme_type['type'] == 'instances',
                            'title': self.scheme_type['type'],
                            'color': self.scheme_type['color']
                        }
                    ]
                },
                {
                    'stacking': 'normal',
                    'overrides': {'min': 0, 'allowDecimals': False, },
                    'label': '',
                    'type': 'area',
                    'position': 'left',
                    'graphs': self.graphs_settings
                },
            ]
        return yaxis


class AvgTimesTimelinePlot(TimelinePlot):
    title = 'Средние времена'
    graphs = 'connect_time', 'send_time', 'latency', 'receive_time'

    @memoized_property
    def data(self):
        """
        retrieves monitoring data from database
        """
        data = {}
        sql = '''
            select connect_time_data, send_time_data, latency_data, receive_time_data from
            (
            select intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t
            from loaddb.rt_microsecond_details_buffer
            where job_id={job}
            and job_date=toDate({job_date})
            and tag = ''
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            group by t
            order by t
            ) all left join
            (
            select
            round(avg(connect_time_sum/1000/resps), 3) as connect_time_data,
            round(avg(send_time_sum/1000/resps), 3) as send_time_data,
            round(avg(latency_sum/1000/resps), 3) as latency_data,
            round(avg(receive_time_sum/1000/resps), 3) as receive_time_data,
            intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t
            from loaddb.rt_microsecond_details_buffer
            where job_id={job}
            and job_date=toDate({job_date})
        '''
        if self.job_obj.multitag and self.tag:
            sql += 'and tag in ({cases_with_tag}) '
        else:
            sql += '''and tag='{tag}'  '''
        sql += '''
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            group by t
            order by t
            )
            using t'''
        fetched_data = self.ch_client.select(sql, query_params=self.query_params)
        for graph in self.graphs:
            data[graph] = [v[self.graphs.index(graph)] for v in fetched_data]
        return data

    @property
    def graphs_settings(self):
        """
        settings for highcharts series
        """
        return [{'values': time,
                 'title': time,
                 'color': self.color_mapping(time)} for time in self.graphs]

    @staticmethod
    def color_mapping(time):
        color_map = {'connect_time': 'rgb(114,137,184)',
                     'send_time': 'rgb(117,193,79)',
                     'latency': 'rgb(255,94,82)',
                     'receive_time': 'rgb(255,230,132)'
                     }
        try:
            color = color_map[time]
            return color
        except KeyError:
            logging.warning('Unexpected time: {}'.format(time))
            return None

    @property
    def yaxis(self):
        return [
            {
                'label': self.scheme_type['type'],
                'overrides': {'gridLineWidth': 0, 'min': 0, 'allowDecimals': False},
                'position': 'right',
                'type': 'line',
                'graphs': [
                    {
                        'values': self.scheme_data,
                        'zIndex': 2,
                        'disabled': self.job_obj.uses_jmeter and self.scheme_type['type'] == 'instances',
                        'title': self.scheme_type['type'],
                        'color': self.scheme_type['color']
                    }
                ]
            },
            {
                'stacking': 'normal',
                'label': 'ms',
                'type': 'area',
                'position': 'left',
                'graphs': self.graphs_settings
            },
        ]


class InstancesTimelinePlot(TimelinePlot):
    """
    doesn't depend on case or multitag
    """

    title = 'Тестирующие потоки'

    @property
    def graphs(self):
        return ['threads']

    @property
    def graphs_settings(self):
        """
        settings for highcharts series
        """
        return [{'values': graph,
                 'title': 'Потоки',
                 'color': '#ff00ff'} for graph in self.graphs]

    @memoized_property
    def data(self):
        """
        retrieves monitoring data from database
        """
        sql = '''select
            round(avg(threads), 3)
            from loaddb.rt_microsecond_details_buffer
            where job_id={job}
            and job_date=toDate({job_date})
            and tag=''
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            group by intDiv(toUInt32(time), {compress_ratio})*{compress_ratio}
            order by intDiv(toUInt32(time), {compress_ratio})*{compress_ratio};'''
        data = {'threads': [v[0] for v in self.ch_client.select(sql, query_params=self.query_params)]}
        return data

    @property
    def yaxis(self):
        yaxis = [
            {
                'label': 'instances',
                'overrides': {'min': 0, 'allowDecimals': False},
                'type': 'line',
                'position': 'left',
                'graphs': self.graphs_settings
            }
        ]

        if self.scheme_type['type'] == 'rps':
            yaxis.append({
                'label': self.scheme_type['type'],
                'overrides': {'gridLineWidth': 0, 'min': 0, 'allowDecimals': False},
                'position': 'right',
                'type': 'line',
                'graphs': [
                    {
                        'values': self.scheme_data,
                        'zIndex': 2,
                        'title': self.scheme_type['type'],
                        'color': self.scheme_type['color']
                    }
                ]
            })
        return yaxis


class CasesAvgTimelinePlot(TimelinePlot):
    title = 'Среднее время ответа по тэгам'

    @memoized_property
    def data(self):
        """
        dict with graphs as keys and values as arrays
        """
        data = {}
        sql = '''
            select data from
            (
            select intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t
            from loaddb.rt_microsecond_details_buffer
            where job_id={job}
            and job_date=toDate({job_date})
            and tag = ''
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            group by t
            order by t
            ) all left join
            (
            select
            round(avg((connect_time_sum + send_time_sum + latency_sum + receive_time_sum)/1000/resps), 3) as data,
            intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t
            from loaddb.rt_microsecond_details_buffer
            where job_id={job}
            and job_date=toDate({job_date})
            and tag {cases_with_tag}
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            group by t
            order by t
            )
            using t
        '''
        query_params = self.query_params.copy()
        for graph in self.graphs:
            if self.job_obj.multitag and graph:
                query_params['cases_with_tag'] = 'in ({})'.format(','.join(
                    ["'{}'".format(escape_string(case)) for case in self.job_obj.cases if
                     graph in case.split('|')]))
            else:
                query_params['cases_with_tag'] = "='{}' ".format(escape_string(graph))
            data[graph] = [v[0] for v in self.ch_client.select(sql, query_params=query_params)]
        return data

    @memoized_property
    def graphs(self):
        """
        self case or job cases and overall as empty string
        """

        if len(self.job_obj.tags) > self.series_limit and not self.tag:
            self.subtitle = 'Слишком много тэгов, см. по отдельности или в таблицах.'
            return ['']
        elif self.job_obj.tags and not self.tag:
            return [''] + self.job_obj.tags
        else:
            return [self.tag]

    @property
    def graphs_settings(self):
        """
        settings for highcharts series
        """

        if len(self.job_obj.tags) > self.series_limit and not self.tag:
            settings = [{'values': '',
                         'title': 'Весь тест',
                         'color': '#333',
                         }]
        elif self.job_obj.tags and not self.tag:
            settings = [{'values': '',
                         'title': 'Весь тест',
                         'disabled': True,
                         'color': '#333',
                         }]
            settings += [{'values': tag,
                          'title': tag,
                          } for tag in self.graphs if tag]
        else:
            settings = [{'values': self.tag,
                         'title': self.tag or 'Весь тест',
                         }]
        return settings

    @property
    def yaxis(self):
        return [
            {
                'label': self.scheme_type['type'],
                'overrides': {'gridLineWidth': 0, 'min': 0, 'allowDecimals': False},
                'position': 'right',
                'type': 'line',
                'graphs': [
                    {
                        'values': self.scheme_data,
                        'zIndex': 2,
                        'disabled': self.job_obj.uses_jmeter and self.scheme_type['type'] == 'instances',
                        'title': self.scheme_type['type'],
                        'color': self.scheme_type['color']
                    }
                ]
            },
            {
                'label': 'ms',
                'overrides': {'min': 0, 'allowDecimals': False},
                'type': 'line',
                'position': 'left',
                'graphs': self.graphs_settings
            },
        ]


class SentReceivedPlot(TimelinePlot):
    title = 'Данные (из танка)'
    graphs = 'igress', 'egress'

    @staticmethod
    def color_mapping(graph):
        color_map = {'igress': '#008000',
                     'egress': '#ff0000'}
        try:
            color = color_map[graph]
            return color
        except KeyError:
            logging.warning('Unexpected graph: {}'.format(graph))
            return None

    @property
    def graphs_settings(self):
        """
        settings for highcharts series
        """
        return [{'values': graph,
                 'title': graph,
                 'color': self.color_mapping(graph)} for graph in self.graphs]

    @memoized_property
    def data(self):
        """
        retrieves monitoring data from database
        """
        data = {}
        sql = '''select
            round(avg({data_type}), 3)
            from loaddb.rt_microsecond_details_buffer
            where job_id={job}
            and job_date=toDate({job_date})
            and tag=''
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            group by intDiv(toUInt32(time), {compress_ratio})*{compress_ratio}
            order by intDiv(toUInt32(time), {compress_ratio})*{compress_ratio};'''
        query_params = self.query_params.copy()
        for graph in self.graphs:
            query_params['data_type'] = graph
            data[graph] = [v[0] for v in self.ch_client.select(sql, query_params=query_params)]
        return data

    @property
    def yaxis(self):
        return [
            {
                'label': self.scheme_type['type'],
                'overrides': {'gridLineWidth': 0, 'min': 0, 'allowDecimals': False},
                'position': 'right',
                'type': 'line',
                'graphs': [
                    {
                        'values': self.scheme_data,
                        'zIndex': 2,
                        'disabled': self.job_obj.uses_jmeter and self.scheme_type['type'] == 'instances',
                        'title': self.scheme_type['type'],
                        'color': self.scheme_type['color']
                    }
                ]
            },
            {
                'stacking': 'normal',
                'overrides': {'min': 0, },
                'label': 'bytes',
                'type': 'line',
                'position': 'left',
                'graphs': self.graphs_settings
            },
        ]
