diff --git a/aosstower/model.py b/aosstower/model.py index 041c2aedf53f25ee1010819e429fd50ad9b3702f..bdb51bc69af7b7db091269bd4540b45a0795695a 100644 --- a/aosstower/model.py +++ b/aosstower/model.py @@ -48,6 +48,7 @@ class WrapErrors(object): except self.exceptions as err: traceback = sys.exc_info()[2] raise ModelError, str(err), traceback + wrapped.__doc__ = fcn.__doc__ return wrapped for name in dir(cls): value = getattr(cls, name) @@ -63,11 +64,15 @@ class RrdModel(object): purposes, such as web-widgets. """ + # data interval in seconds + DATA_INTERVAL = 5 + def __init__(self, filepath): self._filepath = filepath self._averages = tuple() self._datasets = None + @property def datasets(self): """Get dataset names available in the database. """ @@ -94,15 +99,18 @@ class RrdModel(object): self._averages = tuple(sorted(averages)) return self._averages - def initialize(self, start=None): + def initialize(self, start=None, days=365): """Create a new empty RRD database. """ assert not os.path.exists(self._filepath), "DB already exists" - start = start or (datetime.utcnow() - timedelta(days=365)) + start = start or (datetime.utcnow() - timedelta(days=days)) + # normalize start to data interval secs = to_unix_timestamp(start) + secs -= secs % self.DATA_INTERVAL + rrdtool.create(self._filepath, '--start={}'.format(secs), - '--step=5', + '--step={:d}'.format(self.DATA_INTERVAL), 'DS:air_temp:GAUGE:10:-40:50', 'DS:rh:GAUGE:10:0:100', 'DS:dewpoint:GAUGE:10:0:100', @@ -114,15 +122,15 @@ class RrdModel(object): 'DS:accum_precip:GAUGE:10:0:100', 'DS:solar_flux:GAUGE:10:0:1000', 'DS:altimeter:GAUGE:10:0:100', - 'RRA:LAST:0.5:1:6307200', - 'RRA:AVERAGE:0.5:12:525600', - 'RRA:AVERAGE:0.5:60:105120', - 'RRA:AVERAGE:0.5:360:17520') + 'RRA:AVERAGE:0.5:1:6307200', + 'RRA:AVERAGE:0.5:{:d}:525600'.format(60/self.DATA_INTERVAL), + 'RRA:AVERAGE:0.5:{:d}:105120'.format(300/self.DATA_INTERVAL), + 'RRA:AVERAGE:0.5:{:d}:17520'.format(1800/self.DATA_INTERVAL)) def _format_data(self, stamp, data): """Format data for insert into RRD. """ - values = ':'.join([str(data[k]) for k in self.datasets()]) + values = ':'.join([str(data[k]) for k in self.datasets]) values = '{:d}@{}'.format(to_unix_timestamp(stamp), values) return values @@ -130,7 +138,7 @@ class RrdModel(object): """Turn a tower record into database data. """ - expected_keys = set(self.datasets()) - {'winddir_north', 'winddir_east'} + expected_keys = set(self.datasets) - {'winddir_north', 'winddir_east'} missing_keys = expected_keys - set(record.keys()) if missing_keys: raise ModelError("Missing datasets %s" % missing_keys) @@ -141,7 +149,7 @@ class RrdModel(object): data['winddir_east'] = winds[0] data['winddir_north'] = winds[1] data['wind_speed'] = winds[2] - for name in self.datasets(): + for name in self.datasets: if name in record: data[name] = record[name] return data @@ -149,10 +157,12 @@ class RrdModel(object): def add_record(self, record): """Add a single record to the database. """ - stamp = record.get_stamp() + # Normalize to data interval + utime = to_unix_timestamp(record.get_stamp()) + stamp = datetime.utcfromtimestamp(utime - utime % self.DATA_INTERVAL) data = self._record_to_data(record) rrdtool.update(self._filepath, - '--template=%s' % ':'.join(self.datasets()), + '--template=%s' % ':'.join(self.datasets), self._format_data(stamp, data)) def get_slice(self, start, end, names=None, average=5):