66c1f9906631ded3c604b917fbdc7e90029dd3ee
[p2pool.git] / p2pool / util / graph.py
1 from __future__ import absolute_import
2 from __future__ import division
3
4 import math
5
6 from p2pool.util import math as math2
7
8
9 class DataViewDescription(object):
10     def __init__(self, bin_count, total_width):
11         self.bin_count = bin_count
12         self.bin_width = total_width/bin_count
13
14 def _shift(x, shift, pad_item):
15     left_pad = math2.clip(shift, (0, len(x)))
16     right_pad = math2.clip(-shift, (0, len(x)))
17     return [pad_item]*left_pad + x[right_pad:-left_pad if left_pad else None] + [pad_item]*right_pad
18
19 class DataView(object):
20     def __init__(self, desc, ds_desc, last_bin_end, bins):
21         assert len(bins) == desc.bin_count
22         
23         self.desc = desc
24         self.ds_desc = ds_desc
25         self.last_bin_end = last_bin_end
26         self.bins = bins
27     
28     def _add_datum(self, t, value):
29         shift = max(0, int(math.ceil((t - self.last_bin_end)/self.desc.bin_width)))
30         self.bins = _shift(self.bins, shift, (self.ds_desc.zero_element, 0))
31         self.last_bin_end += shift*self.desc.bin_width
32         
33         bin = int(math.ceil((self.last_bin_end - self.desc.bin_width - t)/self.desc.bin_width))
34         
35         if bin >= self.desc.bin_count:
36             return
37         
38         prev_total, prev_count = self.bins[bin]
39         self.bins[bin] = self.ds_desc.add_operator(prev_total, value), prev_count + 1
40     
41     def get_data(self, t):
42         shift = max(0, int(math.ceil((t - self.last_bin_end)/self.desc.bin_width)))
43         bins = _shift(self.bins, shift, (self.ds_desc.zero_element, 0))
44         last_bin_end = self.last_bin_end + shift*self.desc.bin_width
45         
46         assert last_bin_end - self.desc.bin_width <= t <= last_bin_end
47         
48         return [(
49             (min(t, last_bin_end - self.desc.bin_width*i) + (last_bin_end - self.desc.bin_width*(i + 1)))/2, # center time
50             (self.ds_desc.mult_operator(1/count, total) if count else None) if self.ds_desc.source_is_cumulative
51                 else self.ds_desc.mult_operator(1/(min(t, last_bin_end - self.desc.bin_width*i) - (last_bin_end - self.desc.bin_width*(i + 1))), total), # value
52             min(t, last_bin_end - self.desc.bin_width*i) - (last_bin_end - self.desc.bin_width*(i + 1)), # width
53         ) for i, (total, count) in enumerate(bins)]
54
55
56 class DataStreamDescription(object):
57     def __init__(self, source_is_cumulative, dataview_descriptions, zero_element=0, add_operator=lambda x, y: x+y, mult_operator=lambda a, x: a*x):
58         self.source_is_cumulative = source_is_cumulative
59         self.dataview_descriptions = dataview_descriptions
60         self.zero_element = zero_element
61         self.add_operator = add_operator
62         self.mult_operator = mult_operator
63
64 class DataStream(object):
65     def __init__(self, desc, dataviews):
66         self.desc = desc
67         self.dataviews = dataviews
68     
69     def add_datum(self, t, value=1):
70         for dv_name, dv in self.dataviews.iteritems():
71             dv._add_datum(t, value)
72
73
74 class HistoryDatabase(object):
75     @classmethod
76     def from_obj(cls, datastream_descriptions, obj={}):
77         def get_dataview(ds_name, ds_desc, dv_name, dv_desc):
78             if ds_name in obj:
79                 ds_data = obj[ds_name]
80                 if dv_name in ds_data:
81                     dv_data = ds_data[dv_name]
82                     if dv_data['bin_width'] == dv_desc.bin_width and len(dv_data['bins']) == dv_desc.bin_count:
83                         return DataView(dv_desc, ds_desc, dv_data['last_bin_end'], dv_data['bins'])
84             return DataView(dv_desc, ds_desc, 0, dv_desc.bin_count*[(ds_desc.zero_element, 0)])
85         return cls(dict(
86             (ds_name, DataStream(ds_desc, dict(
87                 (dv_name, get_dataview(ds_name, ds_desc, dv_name, dv_desc))
88                 for dv_name, dv_desc in ds_desc.dataview_descriptions.iteritems()
89             )))
90             for ds_name, ds_desc in datastream_descriptions.iteritems()
91         ))
92     
93     def __init__(self, datastreams):
94         self.datastreams = datastreams
95     
96     def to_obj(self):
97         return dict((ds_name, dict((dv_name, dict(last_bin_end=dv.last_bin_end, bin_width=dv.desc.bin_width, bins=dv.bins))
98             for dv_name, dv in ds.dataviews.iteritems())) for ds_name, ds in self.datastreams.iteritems())