#!/usr/bin/python3
#
# Plugin to monitor the types of requsts made to the API
#
# Uses the query log.
#
# Parameters: 
#
#       config   (required)
#       autoconf (optional - used by munin-config)
#

import re
import os
import sys
from datetime import datetime, timedelta

CONFIG="""graph_title Requests by API call
graph_args --base 1000 -l 0
graph_vlabel requests per minute
graph_category nominatim
z1.label reverse
z1.draw AREA
z1.type GAUGE
z2.label search (successful)
z2.draw STACK
z2.type GAUGE
z3.label search (no result)
z3.draw STACK
z3.type GAUGE
z4.label lookup
z4.draw STACK
z4.type GAUGE
z4.label details
z4.draw STACK
z4.type GAUGE"""

ENTRY_REGEX = re.compile(r'\[[^]]+\] (?P<dur>[0-9.]+) (?P<numres>\d+) (?P<type>[a-z]+) ')
TIME_REGEX = re.compile(r'\[(?P<t_year>\d\d\d\d)-(?P<t_month>\d\d)-(?P<t_day>\d\d) (?P<t_hour>\d\d):(?P<t_min>\d\d):(?P<t_sec>\d\d)[0-9.]*\] ')


class LogFile:
    """ A query log file, unpacked. """

    def __init__(self, filename):
        self.fd = open(filename, encoding='utf-8', errors='replace')
        self.len = os.path.getsize(filename)

    def __del__(self):
        self.fd.close()

    def seek_next(self, abstime):
        self.fd.seek(abstime)
        self.fd.readline()
        l = self.fd.readline()
        e = TIME_REGEX.match(l)
        if e is None:
            return None
        e = e.groupdict()
        return datetime(int(e['t_year']), int(e['t_month']), int(e['t_day']),
                             int(e['t_hour']), int(e['t_min']), int(e['t_sec']))

    def seek_to_date(self, target):
        # start position for binary search
        fromseek = 0
        fromdate = self.seek_next(0)
        if fromdate > target:
            return True
        # end position for binary search
        toseek = -100
        while -toseek < self.len:
            todate = self.seek_next(self.len + toseek)
            if todate is not None:
                break
            toseek -= 100
        if todate is None or todate < target:
            return False
        toseek = self.len + toseek


        while True:
            bps = (toseek - fromseek) / (todate - fromdate).total_seconds()
            newseek = fromseek + int((target - fromdate).total_seconds() * bps)
            newdate = self.seek_next(newseek)
            if newdate is None:
                return False;
            error = abs((target - newdate).total_seconds())
            if error < 1:
                return True
            if newdate > target:
                toseek = newseek
                todate = newdate
                oldfromseek = fromseek
                fromseek = toseek - error * bps
                while True:
                    if fromseek <= oldfromseek:
                        fromseek = oldfromseek
                        fromdate = self.seek_next(fromseek)
                        break
                    fromdate = self.seek_next(fromseek)
                    if fromdate < target:
                        break;
                    bps *=2
                    fromseek -= error * bps
            else:
                fromseek = newseek
                fromdate = newdate
                oldtoseek = toseek
                toseek = fromseek + error * bps
                while True:
                    if toseek > oldtoseek:
                        toseek = oldtoseek
                        todate = self.seek_next(toseek)
                        break
                    todate = self.seek_next(toseek)
                    if todate > target:
                        break
                    bps *=2
                    toseek += error * bps
            if toseek - fromseek < 500:
                return True


    def loglines(self):
        for l in self.fd:
            e = ENTRY_REGEX.match(l)
            if e is not None:
                yield e.groupdict()


if __name__ == '__main__':

    if len(sys.argv) > 1 and sys.argv[1] == 'config':
        print(CONFIG)
        sys.exit(0)

    reverse = 0
    searchy = 0
    searchn = 0
    lookup = 0
    details = 0
    if 'NOMINATIM_QUERYLOG' in os.environ:
        lf = LogFile(os.environ['NOMINATIM_QUERYLOG'])
        if lf.seek_to_date(datetime.now() - timedelta(minutes=5)):
            for l in lf.loglines():
                if l['type'] == 'reverse':
                    reverse += 1
                elif  l['type'] == 'search':
                    if l['numres'] == '0':
                        searchn += 1
                    else:
                        searchy += 1
                elif  l['type'] == 'place':
                    lookup +=1
                else:
                    details += 1


    print('z1.value', reverse/5)
    print('z2.value', searchy/5)
    print('z3.value', searchn/5)
    print('z4.value', lookup/5)
    print('z4.value', details/5)