#!/usr/bin/python
import sys
import os
import yaml
import optparse

NANOSECONDS = int(1e9)

# Output stats in a format similar to siege
# see http://www.joedog.org/index/siege-home
OUTPUT_FORMAT = """Stats for type: [{type}]
Transactions:            {trans:>11} hits
Availability:            {avail:>11.2f} %
Elapsed time:            {elapsed:>11.2f} secs
Data transferred:        {data:>11.2f} MB
Response time:           {resp_time:>11.2f} secs
Transaction rate:        {trans_rate:>11.2f} trans/sec
Throughput:              {data_rate:>11.2f} MB/sec
Concurrency:             {conc:>11.2f}
Successful transactions: {trans_success:>11}
Failed transactions:     {trans_fail:>11}
Longest transaction:     {trans_long:>11.2f}
Shortest transaction:    {trans_short:>11.2f}
"""

def parse_options():
    usage = "usage: %prog [options]"
    parser = optparse.OptionParser(usage=usage)
    parser.add_option(
        "-f", "--file", dest="input", metavar="FILE",
        help="Name of input YAML file. Default uses sys.stdin")
    parser.add_option(
        "-v", "--verbose", dest="verbose", action="store_true",
        help="Enable verbose output")

    (options, args) = parser.parse_args()

    if not options.input and os.isatty(sys.stdin.fileno()):
        parser.error("option -f required if no data is provided "
                     "in stdin")

    return (options, args)

def main():
    (options, args) = parse_options()

    total     = {}
    durations = {}
    min_time  = {}
    max_time  = {}
    errors    = {}
    success   = {}

    calculate_stats(options, total, durations, min_time, max_time, errors,
                    success)
    print_results(total, durations, min_time, max_time, errors, success)

def calculate_stats(options, total, durations, min_time, max_time, errors,
                    success):
    print 'Calculating statistics...'
    
    f = sys.stdin
    if options.input:
        f = file(options.input, 'r')

    for item in yaml.safe_load_all(f):
        type_ = item.get('type')
        if type_ not in ('r', 'w'):
            continue # ignore any invalid items

        if 'error' in item:
            errors[type_] = errors.get(type_, 0) + 1
            continue # skip rest of analysis for this item
        else:
            success[type_] = success.get(type_, 0) + 1

        # parse the item
        data_size = item['chunks'][-1][0]
        duration = item['duration']
        start = item['start']
        end = start + duration / float(NANOSECONDS)

        if options.verbose:
            print "[{type}] POSIX time: {start:>18.2f} - {end:<18.2f} " \
                  "{data:>11.2f} KB".format(
                type=type_,
                start=start,
                end=end,
                data=data_size / 1024.0, # convert to KB
                )

        # update time boundaries
        prev = min_time.setdefault(type_, start)
        if start < prev:
            min_time[type_] = start
        prev = max_time.setdefault(type_, end)
        if end > prev:
            max_time[type_] = end

        # save the duration
        if type_ not in durations:
            durations[type_] = []
        durations[type_].append(duration)

        # add to running totals
        total[type_] = total.get(type_, 0) + data_size

def print_results(total, durations, min_time, max_time, errors, success):
    for type_ in total.keys():
        trans_success = success.get(type_, 0)
        trans_fail    = errors.get(type_, 0)
        trans         = trans_success + trans_fail
        avail         = trans_success * 100.0 / trans
        elapsed       = max_time[type_] - min_time[type_]
        data          = total[type_] / 1024.0 / 1024.0 # convert to MB
        resp_time     = sum(durations[type_]) / float(NANOSECONDS) / \
                        len(durations[type_])
        trans_rate    = trans / elapsed
        data_rate     = data / elapsed
        conc          = trans_rate * resp_time
        trans_long    = max(durations[type_]) / float(NANOSECONDS)
        trans_short   = min(durations[type_]) / float(NANOSECONDS)

        print OUTPUT_FORMAT.format(
            type=type_,
            trans_success=trans_success,
            trans_fail=trans_fail,
            trans=trans,
            avail=avail,
            elapsed=elapsed,
            data=data,
            resp_time=resp_time,
            trans_rate=trans_rate,
            data_rate=data_rate,
            conc=conc,
            trans_long=trans_long,
            trans_short=trans_short,
            )

if __name__ == '__main__':
    main()