annotate weather_server/locations.py @ 21:beb42c835c52

Make weather server handle arbitrary data: - Make logfile record arbitrary BSONs - Make server handlers OK with same - Make location type a normal class rather than attrs; have it handle its own logger. - Bump version number.
author Paul Fisher <paul@pfish.zone>
date Sat, 19 Oct 2019 18:40:48 -0400
parents f1ea183d28ba
children e229afdd447b
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
1
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
1 """Manages the directory containing the various logs."""
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
2
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
3 import configparser
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
4 import datetime
1
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
5 import pathlib
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
6 import typing as t
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
7
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
8 import pytz
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
9
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
10 from . import logfile
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
11 from . import types
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
12
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
13 CONFIG_FILE = 'config.ini'
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
14 LOG = 'log.bson'
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
15
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
16
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
17 class ConfigError(Exception):
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
18 """Raised when a location can't be loaded."""
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
19
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
20
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
21 class Location:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
22
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
23 def __init__(self, root: pathlib.Path):
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
24 parser = configparser.ConfigParser(interpolation=None)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
25 self.root = root
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
26 config_file = root / CONFIG_FILE
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
27 try:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
28 with open(config_file, 'r') as infile:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
29 parser.read_file(infile)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
30 self.location = parser.get(
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
31 'location', 'name', fallback='Weather station')
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
32 self.tz_name = parser.get('location', 'timezone', fallback='UTC')
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
33 self.password = parser.get('location', 'password')
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
34 self.logger = logfile.Logger.create(
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
35 str(root / LOG), sample_field='sample_time')
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
36 except (IOError, KeyError, configparser.Error):
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
37 raise ConfigError("Couldn't load location info.")
1
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
38
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
39 def record(
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
40 self,
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
41 entries: t.Iterable[t.Dict[str, object]],
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
42 timestamp: datetime.datetime,
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
43 ) -> None:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
44 for e in entries:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
45 e['ingest_time'] = timestamp
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
46 self.logger.write_rows(entries)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
47
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
48 @property
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
49 def entries(self) -> t.Iterable[t.Dict[str, object]]:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
50 return self.logger.data
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
51
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
52 def latest(self) -> t.Optional[types.Reading]:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
53 most_recent = reversed(self.logger.data)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
54 for entry in most_recent:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
55 try:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
56 return types.Reading.from_dict(entry)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
57 except KeyError:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
58 pass # go to the older one.
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
59 return None
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
60
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
61 def timezone(self) -> datetime.tzinfo:
1
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
62 try:
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
63 return pytz.timezone(self.tz_name)
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
64 except pytz.UnknownTimeZoneError:
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
65 return pytz.UTC
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
66
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
67 def __repr__(self) -> str:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
68 return '<Location in %r>'.format(self.root)
1
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
69
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
70
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
71 class LocationFolder:
1
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
72
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
73 def __init__(self, root: pathlib.Path):
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
74 self.root = root
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
75 # locations, mtime
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
76 self.info: t.Tuple[t.Dict[str, Location], t.Optional[int]] = ({}, None)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
77 self._maybe_reload()
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
78
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
79 def get(self, name: str) -> Location:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
80 self._maybe_reload()
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
81 locs, _ = self.info
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
82 return locs[name]
1
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
83
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
84 def _maybe_reload(self) -> None:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
85 new_mtime = self.root.stat().st_mtime_ns
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
86 _, old_mtime = self.info
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
87 if old_mtime == new_mtime:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
88 return
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
89 locations = {}
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
90 for child in self.root.iterdir():
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
91 try:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
92 locations[child.name] = Location(child)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
93 except ConfigError:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
94 pass # It's OK. Skip this.
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
95 self.info = locations, new_mtime