# (c) cavaliba.com - app_status - common.py

import time
import re
from datetime import datetime, timedelta

from django.utils import timezone
from django.db.models import Sum

import requests

from app_home.configuration import get_configuration

from app_status.models import StatusRaw
from app_status.models import StatusSampleHour
from app_status.models import StatusSampleDay
from app_data.data import get_instances
from app_data.data import Instance


requests.packages.urllib3.disable_warnings()



# --------------------------------------------------------
# Helpers
# --------------------------------------------------------


def get_monitor(iname=None):

    instance = Instance.from_keyname(classname='status_monitor', keyname=iname)
    if not instance:
        return
    itype = instance.get_attribute_first('type')
    if itype == "HTTP":
        monitor = MonitorHTTP(instance=instance)
    else:
        monitor = Monitor(instance=instance)
    return monitor



def get_monitors():

    reply2 = []

    for instance in Instance.iterate_classname(classname='status_monitor', enabled=True):
        itype = instance.get_attribute_first('type')
        if itype == "HTTP":
            monitor = MonitorHTTP(instance=instance)
        else:
            monitor = Monitor(instance=instance)
        reply2.append(monitor)
    return reply2


def get_monitor_names():
    db_instances = get_instances(classname='status_monitor', is_enabled=True)
    return [i.keyname for i in db_instances]


def run_monitors(monitors=None):

    # list of Class MonitorXXXXX
    if not monitors:
        monitors = get_monitors()

    count = 0
    for monitor in monitors:
        monitor.run()
        if not monitor.run_done:
            continue

        count += 1
        obj = StatusRaw()
        obj.monitor = monitor.keyname
        if monitor.run_error:
            obj.status = False
        else:
            obj.status = True

        obj.data = monitor.run_error
        obj.usec = monitor.run_duration
        obj.save()

    return count

# -----------------------------------------------
# live
# -----------------------------------------------
def compute_live_status(monitors=None):

    # list of Class MonitorXXXXX
    if not monitors:
        monitors = get_monitors()

    delta = timezone.now() - timedelta(minutes=5)
    raws = StatusRaw.objects.filter(timestamp__gte=delta).order_by('-timestamp')

    for monitor in monitors:
        monitor.live_status = "NA"
        for raw in raws:
            if raw.monitor == monitor.keyname:
                if monitor.live_status == "NA":
                    if raw.status:
                        monitor.live_status = "OK"
                    else:
                        monitor.live_status = "KO"
                    break

    return monitors

# -----------------------------------------------
# hourly
# -----------------------------------------------
def compute_hourly_ratio(monitors=None):
    '''
    out:  dict {} : monitor_name => {'OK':int, 'KO':int, 'ratio':int}
    '''
    if not monitors:
        monitors = get_monitors()

    delta = timezone.now() - timedelta(minutes=60)
    raws = StatusRaw.objects.filter(timestamp__gte=delta)

    table = {}
    #   compute to buckets
    for raw in raws:
        if raw.monitor not in table:
            table[raw.monitor] = {}
            table[raw.monitor]['OK'] = 0
            table[raw.monitor]['KO'] = 0
        if raw.status:
            table[raw.monitor]['OK'] += 1
        else:
            table[raw.monitor]['KO'] += 1

    #   compute ratio
    for m, counters in table.items():
        d = table[m]['OK'] + table[m]['KO']
        if d > 0:
            table[m]['ratio'] = table[m]['OK'] / d
        else:
            table[m]['ratio'] = -1

    # ratio & pretty
    for monitor in monitors:

        monitor.hourly_ratio = -1
        monitor.hourly_ratio_pretty = 'NA'

        if monitor.keyname not in table:
            continue

        ratio = table[monitor.keyname]['ratio']

        if ratio >= 1:
            pretty = "100 %"
        elif ratio >= 0:
            pretty = f"{100*ratio:.0f} %"
        else:
            pretty = 'NA'

        monitor.hourly_ratio = ratio
        monitor.hourly_ratio_pretty = pretty

    return




def compute_hourly_bargraph(monitors=None):
    '''
    out:  []  'minute':int(0-59), 'ok':int, 'ko':int, 'ratio':float|None
    '''

    WIDTH = 15

    if not monitors:
        monitors = get_monitors()

    for monitor in monitors:

        now = timezone.now()               # django aware (UTC with TZ convert for rendering)
        now_local = datetime.now()
        now1h = now - timedelta(minutes=60)
        raws = StatusRaw.objects.filter(monitor=monitor.keyname, timestamp__gte=now1h)

        bargraph = {}
        for i in range(60):
            bargraph[i] = {}
            bargraph[i]['OK'] = 0
            bargraph[i]['KO'] = 0
            bargraph[i]['ratio'] = -1
            bargraph[i]['tooltip'] = 'NA'
            bargraph[i]['color'] = 'grey'
            # x, width

        for raw in raws:
            diff = now - raw.timestamp
            diff_seconds = diff.seconds
            minute = int(diff_seconds/60)
            if minute < 0 or minute > 59:
                continue
            if raw.status:
                bargraph[minute]['OK'] += 1
            else:
                bargraph[minute]['KO'] += 1

        # compute & pretty
        for i in range(60):

            #   ratio
            d = bargraph[i]['OK'] + bargraph[i]['KO']
            if d > 0:
                bargraph[i]['ratio'] = bargraph[i]['OK'] / d
            else:
                bargraph[i]['ratio'] = -1

            # tootlip
            tooltip_time = (now_local-timedelta(minutes=i)).strftime('%H:%M')
            if bargraph[i]['ratio'] >= 1:
                tooltip_ratio = "100 %"
            elif bargraph[i]['ratio'] >= 0:
                tooltip_ratio = f"{100*bargraph[i]['ratio']:.0f} %"
            else:
                tooltip_ratio = "NA"
            bargraph[i]['tooltip'] = f"{tooltip_time} - {tooltip_ratio}"

            #   x
            bargraph[i]['x'] = (59 - i) * WIDTH
            bargraph[i]['width'] = WIDTH - 3

            #   color
            color = 'grey'
            if bargraph[i]['ratio'] >= 1:
                color = 'green100'
            if bargraph[i]['ratio'] > 0:
                color = 'green'
            else:
                color = 'red'

            # if bargraph[i]['ratio'] >= 1:
            #     color = 'green100'
            # elif bargraph[i]['ratio'] > 0.99:
            #     color = 'green'
            # elif bargraph[i]['ratio'] > 0.98:
            #     color = 'orange'
            # elif bargraph[i]['ratio'] >= 0:
            #     color = 'red'
            bargraph[i]['color'] = color

        monitor.hourly_bargraph = bargraph

    return monitors


# -----------------------------------------------
# daily
# -----------------------------------------------
def compute_daily_ratio(monitors=None):
    '''
    out:  dict {} : monitor_name => {'OK':int, 'KO':int, 'ratio':int}
    '''
    if not monitors:
        monitors = get_monitors()

    delta = timezone.now() - timedelta(hours=24)
    samples = StatusSampleHour.objects.filter(timestamp__gte=delta)

    table = {}
    #   compute to buckets
    for sample in samples:
        if sample.monitor not in table:
            table[sample.monitor] = {}
            table[sample.monitor]['OK'] = 0
            table[sample.monitor]['KO'] = 0
        if sample.count_ok:
            table[sample.monitor]['OK'] += sample.count_ok
        if sample.count_ko:
            table[sample.monitor]['KO'] += sample.count_ko

    #   compute ratio i=hour
    for i, counters in table.items():
        d = table[i]['OK'] + table[i]['KO']
        if d > 0:
            table[i]['ratio'] = table[i]['OK'] / d
        else:
            table[i]['ratio'] = -1

    # ratio & pretty
    for monitor in monitors:

        monitor.daily_ratio = -1
        monitor.daily_ratio_pretty = 'NA'

        if monitor.keyname not in table:
            continue

        ratio = table[monitor.keyname]['ratio']

        if ratio >= 1:
            pretty = "100 %"
        elif ratio >= 0:
            pretty = f"{100*ratio:.2f} %"
        else:
            pretty = 'NA'

        monitor.daily_ratio = ratio
        monitor.daily_ratio_pretty = pretty

    return



def compute_daily_bargraph(monitors=None):

    '''
    out:  []  'hour':int(0-23), 'ok':int, 'ko':int, 'ratio':float|-1
    '''

    WIDTH = 30

    if not monitors:
        monitors = get_monitors()

    for monitor in monitors:

        now = timezone.now()               # django aware (UTC with TZ convert for rendering)
        # now24h = now - timedelta(hours=24)

        first_hour = now - \
            timedelta(minutes=now.minute) - \
            timedelta(seconds=now.second) - \
            timedelta(microseconds=now.microsecond) - \
            timedelta(hours=24)

        tmp = datetime.now()
        now_local = tmp - \
            timedelta(minutes=tmp.minute) - \
            timedelta(seconds=tmp.second) - \
            timedelta(microseconds=tmp.microsecond)

        samples = StatusSampleHour.objects.filter(monitor=monitor.keyname, timestamp__gt=first_hour)

        bargraph = {}
        for i in range(24):
            bargraph[i] = {}
            bargraph[i]['OK'] = 0
            bargraph[i]['KO'] = 0
            bargraph[i]['ratio'] = -1
            bargraph[i]['tooltip'] = 'NA'
            bargraph[i]['color'] = 'grey'
            # x, width

        for sample in samples:
            diff = first_hour - sample.timestamp
            diff_seconds = diff.seconds
            hour = int(diff_seconds/3600)

            if hour < 0 or hour > 23:
                continue
            if sample.count_ok:
                bargraph[hour]['OK'] += sample.count_ok
            if sample.count_ko:
                bargraph[hour]['KO'] += sample.count_ko

        # compute & pretty
        for i in range(24):

            #   ratio
            evtcount = bargraph[i]['OK'] + bargraph[i]['KO']
            if evtcount > 0:
                bargraph[i]['ratio'] = bargraph[i]['OK'] / evtcount
            else:
                bargraph[i]['ratio'] = -1

            # tootlip
            tooltip_time = (now_local-timedelta(hours=i)).strftime('%H:%M')
            if bargraph[i]['ratio'] >= 1:
                tooltip_ratio = "100 %"
            elif bargraph[i]['ratio'] >= 0:
                tooltip_ratio = f"{100*bargraph[i]['ratio']:.2f} %"
            else:
                tooltip_ratio = "NA"
            bargraph[i]['tooltip'] = f"{tooltip_time} - {tooltip_ratio} - {evtcount} evt"

            #   x
            bargraph[i]['x'] = (23 - i) * WIDTH
            bargraph[i]['width'] = WIDTH - 3

            #   color
            color = 'grey'
            if bargraph[i]['ratio'] >= 1:
                color = 'green100'
            elif bargraph[i]['ratio'] > 0.99:
                color = 'green'
            elif bargraph[i]['ratio'] > 0.98:
                color = 'orange'
            elif bargraph[i]['ratio'] >= 0:
                color = 'red'
            bargraph[i]['color'] = color

        monitor.daily_bargraph = bargraph

    return monitors


# -----------------------------------------------
# monthly / 31d
# -----------------------------------------------
def compute_monthly_ratio(monitors=None):
    '''
    out:  dict {} : monitor_name => {'OK':int, 'KO':int, 'ratio':int}
    '''
    if not monitors:
        monitors = get_monitors()

    delta = timezone.now() - timedelta(days=31)
    samples = StatusSampleDay.objects.filter(timestamp__gte=delta)

    table = {}
    #   compute to buckets
    for sample in samples:
        if sample.monitor not in table:
            table[sample.monitor] = {}
            table[sample.monitor]['OK'] = 0
            table[sample.monitor]['KO'] = 0
        if sample.count_ok:
            table[sample.monitor]['OK'] += sample.count_ok
        if sample.count_ko:
            table[sample.monitor]['KO'] += sample.count_ko

    #   compute ratio
    for monitor, counters in table.items():
        d = table[monitor]['OK'] + table[monitor]['KO']
        if d > 0:
            table[monitor]['ratio'] = table[monitor]['OK'] / d
        else:
            table[monitor]['ratio'] = -1

    # ratio & pretty
    for monitor in monitors:

        monitor.monthly_ratio = -1
        monitor.monthly_ratio_pretty = 'NA'

        if monitor.keyname not in table:
            continue

        ratio = table[monitor.keyname]['ratio']

        if ratio >= 1:
            pretty = "100 %"
        elif ratio >= 0:
            pretty = f"{100*ratio:.2f} %"
        else:
            pretty = 'NA'

        monitor.monthly_ratio = ratio
        monitor.monthly_ratio_pretty = pretty

    return


def compute_monthly_bargraph(monitors=None):

    '''
    out:  []  'day':int(0-30), 'ok':int, 'ko':int, 'ratio':float|-1
    '''

    WIDTH = 26

    if not monitors:
        monitors = get_monitors()

    for monitor in monitors:

        now = timezone.now()               # django aware (UTC with TZ convert for rendering)

        first_day = now - timedelta(days=31) - \
            timedelta(hours=now.hour) - \
            timedelta(minutes=now.minute) - \
            timedelta(seconds=now.second) - \
            timedelta(microseconds=now.microsecond)


        tmp = datetime.now()
        now_local = tmp - \
            timedelta(hours=tmp.hour) - \
            timedelta(minutes=tmp.minute) - \
            timedelta(seconds=tmp.second) - \
            timedelta(microseconds=tmp.microsecond)

        samples = StatusSampleDay.objects.filter(monitor=monitor.keyname, timestamp__gt=first_day)

        bargraph = {}
        for i in range(31):
            bargraph[i] = {}
            bargraph[i]['OK'] = 0
            bargraph[i]['KO'] = 0
            bargraph[i]['ratio'] = -1
            bargraph[i]['tooltip'] = 'NA'
            bargraph[i]['color'] = 'grey'
            # x, width

        for sample in samples:
            diff = sample.timestamp - first_day
            day = 31 - diff.days

            if day < 0 or day > 30:
                continue
            if sample.count_ok:
                bargraph[day]['OK'] += sample.count_ok
            if sample.count_ko:
                bargraph[day]['KO'] += sample.count_ko

        # compute & pretty
        for i in range(31):

            #   ratio
            evtcount = bargraph[i]['OK'] + bargraph[i]['KO']
            if evtcount > 0:
                bargraph[i]['ratio'] = bargraph[i]['OK'] / evtcount
            else:
                bargraph[i]['ratio'] = -1

            # tootlip
            tooltip_time = (now_local-timedelta(days=i)).strftime('%Y-%m-%d')
            if bargraph[i]['ratio'] >= 1:
                tooltip_ratio = "100 %"
            elif bargraph[i]['ratio'] >= 0:
                tooltip_ratio = f"{100*bargraph[i]['ratio']:.2f} %"
            else:
                tooltip_ratio = "NA"
            bargraph[i]['tooltip'] = f"{tooltip_time} - {tooltip_ratio} - {evtcount} evt"

            #   x
            bargraph[i]['x'] = (30 - i) * WIDTH
            bargraph[i]['width'] = WIDTH - 3

            #   color
            color = 'grey'
            if bargraph[i]['ratio'] >= 1:
                color = 'green100'
            elif bargraph[i]['ratio'] > 0.99:
                color = 'green'
            elif bargraph[i]['ratio'] > 0.98:
                color = 'orange'
            elif bargraph[i]['ratio'] >= 0:
                color = 'red'
            bargraph[i]['color'] = color

        monitor.monthly_bargraph = bargraph

    return monitors

# -----------------------------------------------
# quaterly - 90D
# -----------------------------------------------
def compute_quaterly_ratio(monitors=None):
    '''
    out:  dict {} : monitor_name => {'OK':int, 'KO':int, 'ratio':int}
    '''
    if not monitors:
        monitors = get_monitors()

    delta = timezone.now() - timedelta(days=90)
    samples = StatusSampleDay.objects.filter(timestamp__gte=delta)

    table = {}
    #   compute to buckets
    for sample in samples:
        if sample.monitor not in table:
            table[sample.monitor] = {}
            table[sample.monitor]['OK'] = 0
            table[sample.monitor]['KO'] = 0
        if sample.count_ok:
            table[sample.monitor]['OK'] += sample.count_ok
        if sample.count_ko:
            table[sample.monitor]['KO'] += sample.count_ko

    #   compute ratio
    for monitor, counters in table.items():
        d = table[monitor]['OK'] + table[monitor]['KO']
        if d > 0:
            table[monitor]['ratio'] = table[monitor]['OK'] / d
        else:
            table[monitor]['ratio'] = -1

    # ratio & pretty
    for monitor in monitors:

        monitor.quaterly_ratio = -1
        monitor.quaterly_ratio_pretty = 'NA'

        if monitor.keyname not in table:
            continue

        ratio = table[monitor.keyname]['ratio']

        if ratio >= 1:
            pretty = "100 %"
        elif ratio >= 0:
            pretty = f"{100*ratio:.2f} %"
        else:
            pretty = 'NA'

        monitor.quaterly_ratio = ratio
        monitor.quaterly_ratio_pretty = pretty

    return


def compute_quaterly_bargraph(monitors=None):

    '''
    out:  []  'day':int(0-89), 'ok':int, 'ko':int, 'ratio':float|-1
    '''

    WIDTH = 10

    if not monitors:
        monitors = get_monitors()

    for monitor in monitors:

        now = timezone.now()               # django aware (UTC with TZ convert for rendering)

        first_day = now - timedelta(days=90) - \
            timedelta(hours=now.hour) - \
            timedelta(minutes=now.minute) - \
            timedelta(seconds=now.second) - \
            timedelta(microseconds=now.microsecond)


        tmp = datetime.now()
        now_local = tmp - \
            timedelta(hours=tmp.hour) - \
            timedelta(minutes=tmp.minute) - \
            timedelta(seconds=tmp.second) - \
            timedelta(microseconds=tmp.microsecond)

        samples = StatusSampleDay.objects.filter(monitor=monitor.keyname, timestamp__gt=first_day)

        bargraph = {}
        for i in range(90):
            bargraph[i] = {}
            bargraph[i]['OK'] = 0
            bargraph[i]['KO'] = 0
            bargraph[i]['ratio'] = -1
            bargraph[i]['tooltip'] = 'NA'
            bargraph[i]['color'] = 'grey'
            # x, width

        for sample in samples:
            diff  = sample.timestamp - first_day
            day = 90 - diff.days

            if day < 0 or day > 89:
                continue
            if sample.count_ok:
                bargraph[day]['OK'] += sample.count_ok
            if sample.count_ko:
                bargraph[day]['KO'] += sample.count_ko

        # compute & pretty
        for i in range(90):

            #   ratio
            evtcount = bargraph[i]['OK'] + bargraph[i]['KO']
            if evtcount > 0:
                bargraph[i]['ratio'] = bargraph[i]['OK'] / evtcount
            else:
                bargraph[i]['ratio'] = -1

            # tootlip
            tooltip_time = (now_local-timedelta(days=i)).strftime('%Y-%m-%d')
            if bargraph[i]['ratio'] >= 1:
                tooltip_ratio = "100 %"
            elif bargraph[i]['ratio'] >= 0:
                tooltip_ratio = f"{100*bargraph[i]['ratio']:.2f} %"
            else:
                tooltip_ratio = "NA"
            bargraph[i]['tooltip'] = f"{tooltip_time} - {tooltip_ratio} - {evtcount} evt"

            #   x
            bargraph[i]['x'] = (89 - i) * WIDTH
            bargraph[i]['width'] = WIDTH - 3

            #   color
            color = 'grey'
            if bargraph[i]['ratio'] >= 1:
                color = 'green100'
            elif bargraph[i]['ratio'] > 0.99:
                color = 'green'
            elif bargraph[i]['ratio'] > 0.98:
                color = 'orange'
            elif bargraph[i]['ratio'] >= 0:
                color = 'red'
            bargraph[i]['color'] = color

        monitor.quaterly_bargraph = bargraph

    return monitors



# -----------------------------------------------
# yearly - 365D
# -----------------------------------------------
def compute_yearly_ratio(monitors=None):
    '''
    out:  dict {} : monitor_name => {'OK':int, 'KO':int, 'ratio':int}
    '''
    if not monitors:
        monitors = get_monitors()

    delta = timezone.now() - timedelta(days=365)
    samples = StatusSampleDay.objects.filter(timestamp__gte=delta)

    table = {}
    #   compute to buckets
    for sample in samples:
        if sample.monitor not in table:
            table[sample.monitor] = {}
            table[sample.monitor]['OK'] = 0
            table[sample.monitor]['KO'] = 0
        if sample.count_ok:
            table[sample.monitor]['OK'] += sample.count_ok
        if sample.count_ko:
            table[sample.monitor]['KO'] += sample.count_ko

    #   compute ratio
    for monitor, counters in table.items():
        d = table[monitor]['OK'] + table[monitor]['KO']
        if d > 0:
            table[monitor]['ratio'] = table[monitor]['OK'] / d
        else:
            table[monitor]['ratio'] = -1

    # ratio & pretty
    for monitor in monitors:

        monitor.yearly_ratio = -1
        monitor.yearly_ratio_pretty = 'NA'

        if monitor.keyname not in table:
            continue

        ratio = table[monitor.keyname]['ratio']

        if ratio >= 1:
            pretty = "100 %"
        elif ratio >= 0:
            pretty = f"{100*ratio:.2f} %"
        else:
            pretty = 'NA'

        monitor.yearly_ratio = ratio
        monitor.yearly_ratio_pretty = pretty

    return


def compute_yearly_bargraph(monitors=None):
    pass


# -----------------------------------------------
# sample RAW to HOUR
# -----------------------------------------------
def sample_hour(monitors=None, start_date=None, end_date=None):

    if not monitors:
        monitors = get_monitors()

    if not start_date:
        start_date = timezone.now() - timedelta(hours=1)

    if not end_date:
        end_date = timezone.now()

    # get first/last timestamp
    first_hour = start_date - \
        timedelta(minutes=start_date.minute) - \
        timedelta(seconds=start_date.second) - \
        timedelta(microseconds=start_date.microsecond)
    last_hour = end_date - \
        timedelta(minutes=end_date.minute) - \
        timedelta(seconds=end_date.second) - \
        timedelta(microseconds=end_date.microsecond)

    current_hour = first_hour
    done = False
    total = 0       # samples created/updated
    while not done:

        next_hour = current_hour + timedelta(hours=1)

        for monitor in monitors:

            count_ok = StatusRaw.objects.filter(
                monitor=monitor.keyname,
                timestamp__gte=current_hour,
                timestamp__lt=next_hour,
                status=True
                ).count()

            count_ko = StatusRaw.objects.filter(
                monitor=monitor.keyname,
                timestamp__gte=current_hour,
                timestamp__lt=next_hour,
                status=False
                ).count()

            # delete previous entry
            StatusSampleHour.objects.filter(
                monitor=monitor.keyname,
                timestamp__gte=current_hour,
                timestamp__lt=next_hour
                ).delete()

            # create new entry
            obj = StatusSampleHour()
            obj.timestamp = current_hour
            obj.monitor = monitor.keyname
            obj.count_ok = count_ok
            obj.count_ko = count_ko
            obj.save()

            total += 1

        current_hour += timedelta(hours=1)

        if current_hour > last_hour:
            done = True


    return total



# -----------------------------------------------
# sample HOUR to DAY
# -----------------------------------------------
def sample_day(monitors=None, start_date=None, end_date=None):

    if not monitors:
        monitors = get_monitors()

    if not start_date:
        start_date = timezone.now() - timedelta(days=1)

    if not end_date:
        end_date = timezone.now()

    # get first/last timestamp
    first_day = start_date - \
        timedelta(hours=start_date.hour) - \
        timedelta(minutes=start_date.minute) - \
        timedelta(seconds=start_date.second) - \
        timedelta(microseconds=start_date.microsecond)
    last_day = end_date - \
        timedelta(hours=end_date.hour) - \
        timedelta(minutes=end_date.minute) - \
        timedelta(seconds=end_date.second) - \
        timedelta(microseconds=end_date.microsecond)

    current_day = first_day
    done = False
    total = 0       # samples created/updated
    while not done:

        next_day = current_day + timedelta(days=1)

        for monitor in monitors:

            tmp = StatusSampleHour.objects.filter(
                monitor=monitor.keyname,
                timestamp__gte=current_day,
                timestamp__lt=next_day,
                ).aggregate(Sum("count_ok"))
            count_ok = tmp['count_ok__sum']

            tmp = StatusSampleHour.objects.filter(
                monitor=monitor.keyname,
                timestamp__gte=current_day,
                timestamp__lt=next_day,
                ).aggregate(Sum("count_ko"))
            count_ko = tmp['count_ko__sum']


            # delete previous entry
            StatusSampleDay.objects.filter(
                monitor=monitor.keyname,
                timestamp__gte=current_day,
                timestamp__lt=next_day
                ).delete()

            # create new entry
            obj = StatusSampleDay()
            obj.timestamp = current_day
            obj.monitor = monitor.keyname
            obj.count_ok = count_ok
            obj.count_ko = count_ko
            obj.save()

            total += 1

        current_day += timedelta(days=1)

        if current_day > last_day:
            done = True


    return total

# -----------------------------------------------
# cleanup_raw per monitor
# -----------------------------------------------
def cleanup_raw(monitors=None):

    delete_all = False
    if not monitors:
        monitors = get_monitors()
        delete_all = True


    keepdays = int(get_configuration("status", "STATUS_RAW_KEEP_DAYS"))


    # delete before this timestamp
    timestamp = timezone.now() - timedelta(days=keepdays)

    # catch all if no monitors provided
    if delete_all:
        StatusRaw.objects.filter(
            timestamp__lt=timestamp
            ).delete()

    else:
        for monitor in monitors:
            StatusRaw.objects.filter(
                monitor=monitor.keyname,
                timestamp__lt=timestamp
                ).delete()


# -----------------------------------------------
# cleanup_hour per monitor
# -----------------------------------------------
def cleanup_hour(monitors=None):

    delete_all = False
    if not monitors:
        monitors = get_monitors()
        delete_all = True


    keepdays = int(get_configuration("status", "STATUS_SAMPLE_HOUR_KEEP_DAYS"))

    # delete before this timestamp
    timestamp = timezone.now() - timedelta(days=keepdays)

    # catch all if no monitors provided
    if delete_all:
        StatusSampleHour.objects.filter(
            timestamp__lt=timestamp
            ).delete()

    else:
        for monitor in monitors:
            StatusSampleHour.objects.filter(
                monitor=monitor.keyname,
                timestamp__lt=timestamp
                ).delete()



# --------------------------------------------------------
# CLASS Monitor
# --------------------------------------------------------
class Monitor:

    def __init__(self, instance=None):

        self.keyname = ""
        self.displayname = ""
        self.is_enabled = True
        self.type = None

        self.app = ""
        self.timeout = 2000
        self.schedule = 5
        self.pattern = ""


        # state OK/KO/NA [DOWNTIME]
        self.live_status = "NA"
        # hourly
        self.hourly_ratio = None
        self.hourly_ratio_pretty = None
        self.hourly_bargraph = None      #  dict minute => bar ; bar = dict {x/ratio/count_ok/color/...}
        # daily
        self.daily_ratio = None
        self.daily_ratio_pretty = None
        self.daily_bargraph = None      #  dict hour => bar ; bar = dict {x/ratio/count_ok/color/...}
        # monthly
        self.monthly_ratio = None
        self.monthly_ratio_pretty = None
        self.monthly_bargraph = None      #  dict day => bar ; bar = dict {x/ratio/count_ok/color/...}
        # quaterly
        self.quaterly_ratio = None
        self.quaterly_ratio_pretty = None
        self.quaterly_bargraph = None      #  dict XXX => bar ; bar = dict {x/ratio/count_ok/color/...}
        # yearly
        self.yearly_ratio = None
        self.yearly_ratio_pretty = None
        self.yearly_bargraph = None      #  dict XXX => bar ; bar = dict {x/ratio/count_ok/color/...}

        #   run
        self.run_done = False
        self.run_error = None
        self.run_duration = None


        # existing from DB
        if not instance:
            return

        # populate with DB instance
        self.keyname = instance.keyname
        self.displayname = instance.displayname
        self.is_enabled = instance.is_enabled


        try:
            self.app = instance.get_attribute_first('app')
        except Exception:
            self.app = ""

        try:
            self.timeout = int(instance.get_attribute_first('timeout'))
        except Exception:
            self.timeout = 2000

        try:
            self.schedule = int(instance.get_attribute_first('schedule'))
        except Exception:
            self.schedule = 5

        try:
            self.target = instance.get_attribute_first('target')
        except Exception:
            self.target = ""


        try:
            self.pattern = instance.get_attribute_first('pattern')
        except Exception:
            self.pattern = ""

    def __str__(self):
        return f"{self.displayname}"


    def run(self):
        # overload in herited MonitorXXXX
        self.run_done = False
        self.run_duration = 0
        self.run_error = "not implemented"


    def update_last_state(self, second=300):
        self.last_state = "OK"


#   ------------------------------------------------------------

class MonitorHTTP(Monitor):

    def __init__(self, instance=None):
        super().__init__(instance=instance)

        self.type = "HTTP"

        self.http_code = 200
        self.http_host = ""
        self.http_ssl_verify = True
        self.http_allow_redirect = True
        #   run
        self.run_http_code = None

        try:
            self.http_code = int(instance.get_attribute_first('http_code'))
        except Exception:
            self.http_code = 200



    def run(self):
        super().run()

        self.run_done = True
        self.run_duration = 0
        self.run_error = ""


        # my_http_proxy  = c.get('http_proxy',"")
        # my_https_proxy = c.get('https_proxy',my_http_proxy)
        # # default : use env proxies
        # # if "http_proxy:noenv" remove env proxies
        # # else use specified proxies
        # proxies = {}
        # if my_http_proxy != "":
        #     proxies["http"] = my_http_proxy
        # if my_https_proxy != "":
        #     proxies["https"] = my_https_proxy
        # session = requests.Session()
        # if my_http_proxy =="noenv":
        #     session.trust_env = False
        #     proxies = {}
        # ##print("\n---\n",proxies)

        headers = {}
        if self.http_host != "":
            headers['Host'] = self.http_host


        start_time = time.time()

        try:
            resp = requests.get(self.target,
                                headers=headers,
                                timeout=int(self.timeout / 1000),
                                verify=self.http_ssl_verify,
                                # proxies=proxies,
                                allow_redirects=self.http_allow_redirect
                                # auth=(self.http_username, self.http_password),
                                )
        except Exception as e:
            self.run_error = e
            return

        # usec
        self.run_duration = int(1000000 * (time.time() - start_time))


        # http_code : resp.status_code
        self.run_http_code = int(resp.status_code)
        if self.run_http_code != self.http_code:
            self.run_error = f"bad http_code {resp.status_code}"
            return


        # check for pattern, in page content AND in header
        if self.pattern and len(self.pattern) > 0:

            fulltext = ""
            for k, v in resp.headers.items():
                line = "{}: {}\n".format(k, v)
                fulltext += line
            fulltext += resp.text

            # acceptance pattern
            if self.pattern[0] != '!':
                mysearch = re.search(self.pattern, fulltext)
                if not mysearch:
                    self.run_error = f"expected pattern not found ({self.pattern})"
                    return
            # rejection pattern
            else:
                mysearch = re.search(self.pattern[1:], fulltext)
                if mysearch:
                    self.run_error = f"reject pattern found ({self.pattern})"
                    return
