#!/usb/bin/env python
# Copyright (c) 2009, Yoan Blanc <yoan@dosimple.ch>
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of the <organization> nor the
#       names of its contributors may be used to endorse or promote products
#       derived from this software without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY <copyright holder> ''AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

__author__ = "Yoan Blanc <yoan@dosimple.ch>"
__licence__ = "BSD"


import memcache

from time import time
from wsgiref.simple_server import make_server


class LiveStats(object):

    def __init__(self, application, cache, period=3600, precision=10):
        self.application = application
        self.cache = cache
        self.period = period
        self.precision = precision

    def inc(self, key, value=1, timeout=None):
        try:
            self.cache.incr(key, value)
        except ValueError: # Not Found
            if timeout is None:
                timeout = self.period
            self.cache.add(key, value, timeout)

    def __call__(self, environ, start_response):
        environ["livestats.track"] = True
        tstart = time()

        response = [s for s in self.application(environ, start_response)]

        if environ["livestats.track"]:
            tend = time()
            ts = int((tend - tstart) * 1000)

            key = "%d" % (tend / self.precision)

            self.inc("t"+key, ts)
            self.inc("p"+key)
        return response


class MyApplication(object):

    def __init__(self, cache, period=3600, precision=10):
        self.cache = cache
        self.period = period
        self.precision = precision

    def index(self, environ, start_response):
        start_response("200 OK", [("Content-Type", "text/plain")])
        return ["You've been counted"]

    def stats(self, environ, start_response):
        key = int(time() / self.precision)
        keys = [str(key - i) for i in xrange(self.period / self.precision)]
        keys.reverse()

        vs = self.cache.get_multi(keys, key_prefix="p")
        ts = self.cache.get_multi(keys, key_prefix="t")

        vmax = max(vs.values()) / float(self.precision) if vs else 1
        tmax = max(ts.values()) / float(self.precision) if ts else 1
        
        views = []
        times = []

        for list, data, m in ((views, vs, vmax), (times, ts, tmax)):
            m = float(max(m, 1))
            for k in keys:
                if k in data:
                    list.append(int(data[k] / float(self.precision) / m * 100))
                else:
                    list.append(0)
        
        start_response("200 OK", [("Content-Type", "text/html; charset=utf-8")])
        return ["""<!DOCTYPE html>
<html>
    <meta charset=utf-8>
    <title>Live stats</title>
    <h1>Live stats</h1>
    <h2>Visits in the last hours</h2>
    <img src="http://chart.apis.google.com/chart?cht=ls&chs=1000x200&chd=t:%s|%s&chxt=x,y,r&chxr=0,-%d,0|1,0,%d|2,0,%d&chdl=Views+[req/s]|Time+[ms]&chco=ff0000,00ff00">
</html>""" % (",".join([str(v) for v in views]),
              ",".join([str(t) for t in times]),
              self.period / 60, # in minutes
              int(vmax),
              int(tmax))]


    def __call__(self, environ, start_response):
        if environ["PATH_INFO"] == "/stats":
            environ["livestats.track"] = False
            return self.stats(environ, start_response)
        else:
            return self.index(environ, start_response)


mc = memcache.Client(["127.0.0.1:11211"])
kwargs = {"period": 3600, "precision": 10}

application = LiveStats(MyApplication(mc, **kwargs), mc, **kwargs)


if __name__ == "__main__":
    server = make_server('', 8000, application)
    try:
        print "Serving from localhost on port 8000"
        server.serve_forever()
    except KeyboardInterrupt, ki:
        print "Bye bye."

