diff --git a/noblogsmv/state.py b/noblogsmv/state.py index 504dd6b63bf4325bc10cb1c5fd1d8442b35b4e9c..60a8ef3822b849567d472038b6254ca6fa21de2f 100644 --- a/noblogsmv/state.py +++ b/noblogsmv/state.py @@ -253,6 +253,23 @@ class StateMachineWorker(threading.Thread): return info +class RingBuffer(object): + + def __init__(self, size): + self.size = size + self.values = [None] * size + self.ptr = 0 + + def add(self, value): + self.values[self.ptr] = value + self.ptr += 1 + if self.ptr > self.size: + self.ptr = 0 + + def get_values(self): + return values[self.ptr:] + values[:self.ptr] + + class StatsThread(threading.Thread): def __init__(self, sm): @@ -285,6 +302,7 @@ class StateMachine(object): self.num_workers = num_workers self.threads = [] self.state_count = {} + self.state_count_ts = {} def load_data(self, input_stream): with transaction(self.db) as session: @@ -321,9 +339,22 @@ class StateMachine(object): with readonly_transaction(self.db) as session: return self.db.dump(session) + def get_current_state_count(self): + return self.state_count + + def get_ts_state_count(self): + return self.state_count_ts + def compute_stats(self): with readonly_transaction(self.db) as session: - self.state_count = self.db.count_by_state(session) + state_count = self.db.count_by_state(session) + state_count_ts = dict(self.state_count_ts) + for key, value in state_count.iteritems(): + if key not in state_count_ts: + state_count_ts[key] = RingBuffer(512) + state_count_ts[key].add(value) + self.state_count = state_count + self.state_count_ts = state_count_ts def run(self): input_queue = Queue.Queue() diff --git a/noblogsmv/templates/index.html b/noblogsmv/templates/index.html index 05141eac1caf8f53247dd7629f689b0ff50ad990..6616ab8b17beecd8d5b84089347b16c3e7a70f12 100644 --- a/noblogsmv/templates/index.html +++ b/noblogsmv/templates/index.html @@ -69,6 +69,10 @@ {% endfor %} </tbody> </table> + + <h4>Task states over time:</h4> + <img src="{{ state_count_graph }}"> + </div> <div class="tab-pane" id="tasks"> diff --git a/noblogsmv/webapp.py b/noblogsmv/webapp.py index 6287ad9a66615f29f8243d3af703b3358ee5ea33..fe57101f4f4e7491ea6af72bce5c7e408a1a0e95 100644 --- a/noblogsmv/webapp.py +++ b/noblogsmv/webapp.py @@ -7,6 +7,7 @@ import time import urllib2 from noblogsmv import state from flask import Flask, redirect, render_template, request, g +from pygooglechart import SimpleLineChart app = Flask(__name__) @@ -17,6 +18,35 @@ def before_request_handler(): g.sm = app.config.sm +_palette = [0xffa07a, + 0xffdab9, + 0x5f9ea0, + 0xdda0dd, + 0x6294ed, + 0x8fbc8b, + 0xf0e68c, + 0xff69b4, + 0x4ce2d3, + ] + +def _mk_stats_graph(x=450, y=270): + ts = g.sm.state_count_ts + + chart = SimpleLineChart(x, y) + labels = [] + for key, rbuf in ts.iteritems(): + values = rbuf.get_values() + chart.add_data(values) + labels.append(key) + nkeys = len(labels) + chart.add_data([0] * nkeys) + chart.set_colours(_palette[:nkeys]) + for i in xrange(nkeys - 1): + chart.add_fill_range(_palette[i], i, i+1) + + return chart.get_url() + + @app.route('/') def index(): now = time.time() @@ -26,6 +56,7 @@ def index(): return render_template('index.html', state=cur_state, state_count=state_count, + state_count_graph=_mk_stats_graph(), worker_info=worker_info, now=time.time()) diff --git a/setup.py b/setup.py index 14811eed154deb8e5945487f3e0283f7c3f0e732..fc39f7daff7c82505b5fb84eeda306c074489123 100755 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( author="ale", author_email="ale@incal.net", url="https://git.autistici.org/ai/noblogsmv", - install_requires=['Flask', 'SQLAlchemy'], + install_requires=['Flask', 'SQLAlchemy', 'pygooglechart'], setup_requires=[], zip_safe=True, packages=find_packages(),