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

@author: noob
"""

from .plots_base import TimelinePlot
from common.util.decorators import Memoize
import logging


class QuantilesTimelinePlot(TimelinePlot):
    @property
    def title(self):
        return u'Quantiles'

    @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: %s', quantile)
            return None

    @property
    def graphs(self):
        return 'expect', '50', '75', '80', '85', '90', '95', '98', '99', '100'

    @property
    def graphs_settings(self):
        settings = [
            {'values': 'expect',
             'title': 'avg',
             '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

    @property
    @Memoize
    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)s)*%(compress_ratio)s as t
            from loaddb.rt_microsecond_details_buffer
            where job_id=%(job)s
            and job_date = toDate(%(job_date)s)
            and tag = ''
            and time >= toDateTime(%(start)s)
            and time <= toDateTime(%(end)s)
            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)s)*%(compress_ratio)s as t
            from loaddb.rt_quantiles_buffer
            where job_id=%(job)s
            and job_date = toDate(%(job_date)s)
            '''
        if self.job_obj.multitag and self.tag:
            sql += 'and tag in (%(cases_with_tag)s) '
        else:
            sql += '''and tag='%(tag)s'  '''
        sql += '''
            and time >= toDateTime(%(start)s) 
            and time <= toDateTime(%(end)s)
            group by t
            order by t
            )
            using t'''
        query_params = self.job_obj.basic_query_params.copy()
        query_params.update({
            'start': self.slider_start,
            'end': self.slider_end,
            'compress_ratio': self.compress_ratio,
            'cases_with_tag': ','.join(
                ["'" + case + "'" for case in self.job_obj.cases if self.tag in case.split('|')]),
            'tag': self.tag,
        })
        fetched_data = self.ch_client.select(sql, query_params=query_params)
        transponed_data = 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
        """
        sql = '''
            select data from 
            (
            select intDiv(toUInt32(time), %(compress_ratio)s)*%(compress_ratio)s as t
            from loaddb.rt_microsecond_details_buffer
            where job_id=%(job)s
            and job_date=toDate(%(job_date)s)
            and tag = ''
            and time >= toDateTime(%(start)s)
            and time <= toDateTime(%(end)s)
            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)s)*%(compress_ratio)s as t
            from loaddb.rt_microsecond_details_buffer
            where job_id=%(job)s
            and job_date=toDate(%(job_date)s)
        '''
        if self.job_obj.multitag and self.tag:
            sql += 'and tag in (%(cases_with_tag)s) '
        else:
            sql += '''and tag='%(tag)s'  '''
        sql += '''
                and time >= toDateTime(%(start)s) 
                and time <= toDateTime(%(end)s)
                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

    @property
    @Memoize
    def data(self):
        """
        dict with graphs as keys and values as arrays
        """
        self.quantiles_data.update(self.expect_data)
        logging.debug("Got data for plot %s case %s job %s %s", self.__class__.__name__, self.tag, self.job)
        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,
                        '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 = u'Response time distribution'

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

    @property
    def graphs(self):
        return u'0-0.\u2089\u2089\u2089', u'1-9', u'10-99', u'100-999', u'1000+'

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

    @property
    @Memoize
    def data(self):
        """
        dict with graphs as keys and values as arrays
        """
        data = {}
        graphs_mapping = {
            u'0-0.\u2089\u2089\u2089': 0,
            u'1-9': 1,
            u'10-99': 2,
            u'100-999': 3,
            u'1000+': 4
        }
        sql = '''
            select data from 
            (
            select intDiv(toUInt32(time), %(compress_ratio)s)*%(compress_ratio)s as t
            from loaddb.rt_microsecond_details_buffer
            where job_id=%(job)s
            and job_date = toDate(%(job_date)s)
            and tag = ''
            and time >= toDateTime(%(start)s)
            and time <= toDateTime(%(end)s)
            group by t
            order by t
            ) all left join
            (
            select 
            round(toUInt32(sum(cnt))/%(compress_ratio)s, 3) as data, 
            intDiv(toUInt32(time), %(compress_ratio)s)*%(compress_ratio)s as t
            from loaddb.rt_microsecond_histograms_buffer
            where job_id=%(job)s
            and job_date = toDate(%(job_date)s)
            '''
        if self.job_obj.multitag and self.tag:
            sql += 'and tag in (%(cases_with_tag)s) '
        else:
            sql += '''and tag='%(tag)s'  '''
        sql += '''
            and time >= toDateTime(%(start)s) 
            and time <= toDateTime(%(end)s)
            and %(length)s
            group by t
            order by t
            )
            using t'''
        query_params = self.query_params.copy()
        for length in 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)))=%s' % (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,
                            '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 = u'HTTP codes'

    @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: %s', http_code)
            return None

    @property
    @Memoize
    def graphs(self):
        sql = '''
            select distinct toUInt32(code) 
            from loaddb.proto_codes_buffer
            where job_id=%(job)s
            and job_date = toDate(%(job_date)s)
            '''
        if self.job_obj.multitag and self.tag:
            sql += 'and tag in (%(cases_with_tag)s) '
        else:
            sql += '''and tag='%(tag)s'  '''
        sql += '''
            and time >= toDateTime(%(start)s) 
            and time <= toDateTime(%(end)s)
            '''
        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

    @property
    @Memoize
    def data(self):
        """
        retrieves monitoring data from database
        """
        data = {}
        sql = '''
            select data from 
            (
            select intDiv(toUInt32(time), %(compress_ratio)s)*%(compress_ratio)s as t
            from loaddb.rt_microsecond_details_buffer
            where job_id=%(job)s
            and job_date = toDate(%(job_date)s)
            and tag = ''
            and time >= toDateTime(%(start)s)
            and time <= toDateTime(%(end)s)
            group by t
            order by t
            ) all left join
            (
            select 
            round(sum(cnt)/%(compress_ratio)s, 3) as data,
            intDiv(toUInt32(time), %(compress_ratio)s)*%(compress_ratio)s as t
            from loaddb.proto_codes_buffer
            where job_id=%(job)s
            and job_date = toDate(%(job_date)s)
            '''
        if self.job_obj.multitag and self.tag:
            sql += 'and tag in (%(cases_with_tag)s) '
        else:
            sql += '''and tag='%(tag)s'  '''
        sql += '''
            and code=%(code)s
            and time >= toDateTime(%(start)s) 
            and time <= toDateTime(%(end)s)
            group by t
            order by t
            ) 
            using t
            '''
        for code in self.graphs:
            query_params = self.query_params.copy()
            query_params.update({
                '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,
                            '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):
    @property
    def title(self):
        return u"Net codes"

    @property
    @Memoize
    def graphs(self):
        sql = '''
            select distinct toUInt32(code) 
            from loaddb.net_codes_buffer
            where job_id=%(job)s
            and job_date = toDate(%(job_date)s)
            '''
        if self.job_obj.multitag and self.tag:
            sql += 'and tag in (%(cases_with_tag)s) '
        else:
            sql += '''and tag='%(tag)s'  '''
        sql += '''
            and time >= toDateTime(%(start)s) 
            and time <= toDateTime(%(end)s)
            '''
        query_params = self.job_obj.basic_query_params.copy()
        query_params.update({
            'start': self.slider_start,
            'end': self.slider_end,
            'cases_with_tag': ','.join(
                ["'" + case + "'" for case in self.job_obj.cases if self.tag in case.split('|')]),
            'tag': self.tag,
        })
        return [c[0] for c in self.ch_client.select(sql, query_params=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

    @property
    @Memoize
    def data(self):
        """
        retrieves monitoring data from database
        """
        data = {}
        sql = '''
            select data from 
            (
            select intDiv(toUInt32(time), %(compress_ratio)s)*%(compress_ratio)s as t
            from loaddb.rt_microsecond_details_buffer
            where job_id=%(job)s
            and job_date = toDate(%(job_date)s)
            and tag = ''
            and time >= toDateTime(%(start)s)
            and time <= toDateTime(%(end)s)
            group by t
            order by t
            ) all left join
            (
            select 
            round(sum(cnt)/%(compress_ratio)s, 3) as data,
            intDiv(toUInt32(time), %(compress_ratio)s)*%(compress_ratio)s as t
            from loaddb.net_codes_buffer
            where job_id=%(job)s
            and job_date = toDate(%(job_date)s)
            '''
        if self.job_obj.multitag and self.tag:
            sql += 'and tag in (%(cases_with_tag)s) '
        else:
            sql += '''and tag='%(tag)s'  '''
        sql += '''
            and code=%(code)s
            and time >= toDateTime(%(start)s) 
            and time <= toDateTime(%(end)s)
            group by t
            order by t
            )
            using t
            '''
        for code in self.graphs:
            query_params = self.job_obj.basic_query_params.copy()
            query_params.update({
                'start': self.slider_start,
                'end': self.slider_end,
                'code': code,
                'compress_ratio': self.compress_ratio,
                'cases_with_tag': ','.join(["'" + case + "'" for case in self.job_obj.cases if
                                            self.tag in case.split('|')]),
                'tag': self.tag,
            })
            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,
                            '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):
    @property
    def title(self):
        return u'Responses count per tag distribution'

    @property
    @Memoize
    def data(self):
        """
        dict with graphs as keys and values as arrays
        """
        data = {}
        sql = '''
            select data from 
            (
            select intDiv(toUInt32(time), %(compress_ratio)s)*%(compress_ratio)s as t
            from loaddb.rt_microsecond_details_buffer
            where job_id=%(job)s
            and job_date = toDate(%(job_date)s)
            and tag = ''
            and time >= toDateTime(%(start)s)
            and time <= toDateTime(%(end)s)
            group by t
            order by t
            ) all left join
            (
            select 
            round(sum(resps)/%(compress_ratio)s, 3) as data,
            intDiv(toUInt32(time), %(compress_ratio)s)*%(compress_ratio)s as t
            from loaddb.rt_microsecond_details_buffer
            where job_id=%(job)s
            and job_date = toDate(%(job_date)s)
            and tag = '%(tag)s'
            and time >= toDateTime(%(start)s) 
            and time <= toDateTime(%(end)s)
            group by t
            order by t
            )
            using t'''
        for graph in self.graphs:
            query_params = self.job_obj.basic_query_params.copy()
            query_params.update({
                'start': self.slider_start,
                'end': self.slider_end,
                'compress_ratio': self.compress_ratio,
                'tag': graph,
            })
            data[graph] = [v[0] for v in self.ch_client.select(sql, query_params=query_params)]
        return data

    @property
    @Memoize
    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 = u'Too many tags, choose one'
            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': u'Overall',
                         '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 u'Overall',
                         '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,
                            '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 = u'Response time fractions'
    graphs = 'connect_time', 'send_time', 'latency', 'receive_time'

    @property
    @Memoize
    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)s)*%(compress_ratio)s as t
            from loaddb.rt_microsecond_details_buffer
            where job_id=%(job)s
            and job_date=toDate(%(job_date)s)
            and tag = ''
            and time >= toDateTime(%(start)s) 
            and time <= toDateTime(%(end)s)
            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)s)*%(compress_ratio)s as t
            from loaddb.rt_microsecond_details_buffer
            where job_id=%(job)s
            and job_date=toDate(%(job_date)s)
        '''
        if self.job_obj.multitag and self.tag:
            sql += 'and tag in (%(cases_with_tag)s) '
        else:
            sql += '''and tag='%(tag)s'  '''
        sql += '''
                and time >= toDateTime(%(start)s) 
                and time <= toDateTime(%(end)s)
                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: %s', 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,
                        '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
    """

    @property
    def title(self):
        return u'Threads'

    @property
    def graphs(self):
        return 'threads',

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

    @property
    @Memoize
    def data(self):
        """
        retrieves monitoring data from database
        """
        sql = '''select 
            round(avg(threads), 3)
            from loaddb.rt_microsecond_details_buffer
            where job_id=%(job)s
            and job_date = toDate(%(job_date)s)
            and tag='' 
            and time >= toDateTime(%(start)s) 
            and time <= toDateTime(%(end)s)
            group by intDiv(toUInt32(time), %(compress_ratio)s)*%(compress_ratio)s
            order by intDiv(toUInt32(time), %(compress_ratio)s)*%(compress_ratio)s;'''
        query_params = self.job_obj.basic_query_params.copy()
        query_params.update({
            'start': self.slider_start,
            'end': self.slider_end,
            'compress_ratio': self.compress_ratio,
        })
        data = {'threads': [v[0] for v in self.ch_client.select(sql, query_params=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):
    @property
    def title(self):
        return u'Response time average per tag'

    @property
    @Memoize
    def data(self):
        """
        dict with graphs as keys and values as arrays
        """
        data = {}
        sql = '''
            select data from 
            (
            select intDiv(toUInt32(time), %(compress_ratio)s)*%(compress_ratio)s as t
            from loaddb.rt_microsecond_details_buffer
            where job_id=%(job)s
            and job_date=toDate(%(job_date)s)
            and tag = ''
            and time >= toDateTime(%(start)s)
            and time <= toDateTime(%(end)s)
            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)s)*%(compress_ratio)s as t
            from loaddb.rt_microsecond_details_buffer
            where job_id=%(job)s
            and job_date=toDate(%(job_date)s)
            and tag = '%(tag)s'
            and time >= toDateTime(%(start)s) 
            and time <= toDateTime(%(end)s)
            group by t
            order by t
            )
            using t
        '''
        query_params = self.query_params.copy()
        for graph in self.graphs:
            query_params['tag'] = graph
            data[graph] = [v[0] for v in self.ch_client.select(sql, query_params=query_params)]
        return data

    @property
    @Memoize
    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 = u'Too many tags, choose one'
            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': u'Overall',
                         'color': '#333',
                         }]
        elif self.job_obj.tags and not self.tag:
            settings = [{'values': '',
                         'title': u'Overall',
                         '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 u'Overall',
                         }]
        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,
                        '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):
    @property
    def title(self):
        return u'Tank egress/igress'

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

    @property
    def graphs(self):
        return 'igress', 'egress'

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

    @property
    @Memoize
    def data(self):
        """
        retrieves monitoring data from database
        """
        sql = '''select 
            round(avg(igress), 3),
            round(avg(egress), 3)
            from loaddb.rt_microsecond_details_buffer
            where job_id=%(job)s
            and job_date = toDate(%(job_date)s)
            and tag=''
            and time >= toDateTime(%(start)s) 
            and time <= toDateTime(%(end)s)
            group by intDiv(toUInt32(time), %(compress_ratio)s)*%(compress_ratio)s
            order by intDiv(toUInt32(time), %(compress_ratio)s)*%(compress_ratio)s;'''
        query_params = self.job_obj.basic_query_params.copy()
        query_params.update({
            'start': self.slider_start,
            'end': self.slider_end,
            'compress_ratio': self.compress_ratio,
        })
        fetched_data = self.ch_client.select(sql, query_params=query_params)
        data = {'igress': [v[0] for v in fetched_data],
                'egress': [v[1] for v in fetched_data],
                }
        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,
                        '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
            },
        ]
