1#!/usr/bin/env python3
  2
  3"""Implementation of Processes widget."""
  4
  5
  6
  7
  8from datetime import timedelta
  9from collections import OrderedDict
 10
 11from chart.db.connection import db_connect
 12from chart.reports.widget import Widget
 13from chart.common.prettyprint import Table
 14
 15db_conn = db_connect('PROCESSES')
 16
 17
 18def compute_performance_stats(start_time, stop_time):
 19    """Calculate per-worker statistics showing number of processes executed and
 20    total time spent in execution."""
 21
 22    # procs = {}  # process id indexed to dictionary of name, job count, busy
 23    # worker name indexed to dictionary of process id
 24    # indexed to 'busy', 'processes', 'jobs'
 25    # for proc_id, in db.query('SELECT process_id FROM jobs WHERE
 26    # gen_time>=:start_time AND gen_time<:stop_time
 27
 28    procs = {}  # worker name against dictionary of process count, busy
 29    for worker, busy in db_conn.query('SELECT worker, execute_stop-execute_start '
 30                                 'FROM processes '
 31                                 'WHERE execute_start>=:start_time AND execute_stop<:stop_time',
 32                                 start_time=start_time, stop_time=stop_time):
 33
 34        proc = procs.get(worker)
 35        if proc is None:
 36            proc = {'count': 0,
 37                    'busy': timedelta()}
 38
 39            procs[worker] = proc
 40
 41        proc['count'] += 1
 42        proc['busy'] += busy
 43
 44    return procs
 45
 46
 47class Processes(Widget):
 48    """Daily digest report widget.
 49    Displays the load of each worker process. """
 50
 51    name = 'digest-processes'
 52
 53    document_options = OrderedDict([
 54            ('sensing_start', {'type': 'datetime'}),
 55            ('sensing_stop', {'type': 'datetime'})])
 56
 57    def __init__(self):
 58        super(Processes, self).__init__()
 59
 60    def html(self, document):
 61        dc = document.config
 62        html = document.html
 63
 64        t = Table(headings=('Worker', 'Processes', 'Busy'))
 65        for worker, info in compute_performance_stats(dc['sensing-start'],
 66                                                      dc['sensing-stop']).items():
 67            t.append((worker, info['count'], info['busy']))
 68
 69        t.write_html(html)
 70
 71
 72def main():
 73    """Command line entry point."""
 74    import io
 75    from chart.common.args import ArgumentParser
 76    parser = ArgumentParser()
 77
 78    parser.add_argument('--start',
 79                        type=ArgumentParser.start_time,
 80                        metavar='TIME',
 81                        help='Begin search at TIME.')
 82    parser.add_argument('--stop',
 83                        type=ArgumentParser.stop_time,
 84                        metavar='TIME',
 85                        help='End search at TIME.')
 86    args = parser.parse_args()
 87
 88    widget = Processes()
 89
 90    class Document:
 91        """Fake document object"""
 92        def __init__(self):
 93            self.config = None
 94            self.html = io.StringIO()
 95
 96    document = Document()
 97    document.config = {'sensing_start': args.start,
 98                       'sensing_stop': args.stop}
 99
100    widget.html(document)
101
102    def html_to_console(html):
103        """Convert HTML to console text"""
104        return html.replace(
105            '<br>', '\n').replace(
106            '&nbsp;', ' ').replace(
107                '<h2>', '').replace(
108                '</h2>', '\n')
109
110    print(html_to_console(document.html.getvalue()))
111
112if __name__ == '__main__':
113    main()