Mercurial > personal > weather-server
view weather_server/locations.py @ 31:9bc3687e1e5e
logfile: Add an index, and don't keep everything in RAM.
- Adds index BSON file, updated upon writing.
- Limits amount of data in RAM.
- Gracefully handles writes that don't update index.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Tue, 07 Jul 2020 19:51:30 -0400 |
parents | 7def5611895b |
children | 2f3473416c11 |
line wrap: on
line source
"""Manages the directory containing the various logs.""" import configparser import datetime import pathlib import typing as t import pytz from . import logfile from . import types CONFIG_FILE = 'config.ini' LOG = 'log.bson' class ConfigError(Exception): """Raised when a location can't be loaded.""" class Location: def __init__(self, root: pathlib.Path, key: str): parser = configparser.ConfigParser(interpolation=None) self.root = root self.key = key config_file = root / CONFIG_FILE try: with open(config_file, 'r') as infile: parser.read_file(infile) self.name = parser.get( 'location', 'name', fallback='Weather station') self.tz_name = parser.get('location', 'timezone', fallback='UTC') self.password = parser.get('location', 'password') self.logger = logfile.Logger( str(root / LOG), sample_field='sample_time') except (IOError, KeyError, configparser.Error): raise ConfigError("Couldn't load location info.") def record( self, entries: t.Iterable[t.Dict[str, object]], timestamp: datetime.datetime, ) -> None: for e in entries: e['ingest_time'] = timestamp self.logger.write_rows(entries) @property def entries(self) -> t.Iterable[t.Dict[str, object]]: return self.logger.cached_data def latest(self) -> t.Optional[types.Reading]: most_recent = reversed(self.logger.cached_data) for entry in most_recent: try: return types.Reading.from_dict(entry) except KeyError: pass # go to the older one. return None def timezone(self) -> datetime.tzinfo: try: return pytz.timezone(self.tz_name) except pytz.UnknownTimeZoneError: return pytz.UTC def __repr__(self) -> str: return '<Location in %r>'.format(self.root) class LocationFolder: def __init__(self, root: pathlib.Path): self.root = root # locations, mtime self.info: t.Tuple[t.Dict[str, Location], t.Optional[int]] = ({}, None) self._maybe_reload() def get(self, name: str) -> Location: self._maybe_reload() locs, _ = self.info return locs[name] def locations(self) -> t.Dict[str, Location]: return self.info[0] def _maybe_reload(self) -> None: new_mtime = self.root.stat().st_mtime_ns _, old_mtime = self.info if old_mtime == new_mtime: return locations = {} for child in self.root.iterdir(): try: locations[child.name] = Location(child, child.name) except ConfigError: pass # It's OK. Skip this. self.info = locations, new_mtime