Mercurial > personal > weather-server
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 |
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 |