annotate weather_server/locations.py @ 33:beedfa8eaa3f

Add grid lines to the graph. - Adds time gridlines on the hour and 15 minutes. - Adds temperature gridlines on 10, 5, and 1 degree intervals. - Increases chart range. - Changes from rounding temperature to floor()ing it.
author Paul Fisher <paul@pfish.zone>
date Sat, 12 Jun 2021 20:22:46 +0000
parents 9bc3687e1e5e
children 2f3473416c11
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
26
7def5611895b Add support for an index page displaying all locations.
Paul Fisher <paul@pfish.zone>
parents: 24
diff changeset
23 def __init__(self, root: pathlib.Path, key: str):
21
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
26
7def5611895b Add support for an index page displaying all locations.
Paul Fisher <paul@pfish.zone>
parents: 24
diff changeset
26 self.key = key
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
27 config_file = root / CONFIG_FILE
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
28 try:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
29 with open(config_file, 'r') as infile:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
30 parser.read_file(infile)
22
e229afdd447b locations.py: store location name into the right place.
Paul Fisher <paul@pfish.zone>
parents: 21
diff changeset
31 self.name = parser.get(
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
32 'location', 'name', fallback='Weather station')
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
33 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
34 self.password = parser.get('location', 'password')
24
20c8ec56e447 logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents: 22
diff changeset
35 self.logger = logfile.Logger(
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
36 str(root / LOG), sample_field='sample_time')
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
37 except (IOError, KeyError, configparser.Error):
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
38 raise ConfigError("Couldn't load location info.")
1
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
39
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
40 def record(
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
41 self,
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
42 entries: t.Iterable[t.Dict[str, object]],
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
43 timestamp: datetime.datetime,
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
44 ) -> None:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
45 for e in entries:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
46 e['ingest_time'] = timestamp
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
47 self.logger.write_rows(entries)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
48
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
49 @property
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
50 def entries(self) -> t.Iterable[t.Dict[str, object]]:
31
9bc3687e1e5e logfile: Add an index, and don't keep everything in RAM.
Paul Fisher <paul@pfish.zone>
parents: 26
diff changeset
51 return self.logger.cached_data
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
52
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
53 def latest(self) -> t.Optional[types.Reading]:
31
9bc3687e1e5e logfile: Add an index, and don't keep everything in RAM.
Paul Fisher <paul@pfish.zone>
parents: 26
diff changeset
54 most_recent = reversed(self.logger.cached_data)
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
55 for entry in most_recent:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
56 try:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
57 return types.Reading.from_dict(entry)
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
58 except KeyError:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
59 pass # go to the older one.
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
60 return None
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
61
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
62 def timezone(self) -> datetime.tzinfo:
1
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
63 try:
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
64 return pytz.timezone(self.tz_name)
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
65 except pytz.UnknownTimeZoneError:
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
66 return pytz.UTC
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
67
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
68 def __repr__(self) -> str:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
69 return '<Location in %r>'.format(self.root)
1
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
70
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
71
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
72 class LocationFolder:
1
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
73
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
74 def __init__(self, root: pathlib.Path):
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
75 self.root = root
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
76 # locations, mtime
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
77 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
78 self._maybe_reload()
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
79
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
80 def get(self, name: str) -> Location:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
81 self._maybe_reload()
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
82 locs, _ = self.info
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
83 return locs[name]
1
f66df122f18d get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff changeset
84
26
7def5611895b Add support for an index page displaying all locations.
Paul Fisher <paul@pfish.zone>
parents: 24
diff changeset
85 def locations(self) -> t.Dict[str, Location]:
7def5611895b Add support for an index page displaying all locations.
Paul Fisher <paul@pfish.zone>
parents: 24
diff changeset
86 return self.info[0]
7def5611895b Add support for an index page displaying all locations.
Paul Fisher <paul@pfish.zone>
parents: 24
diff changeset
87
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
88 def _maybe_reload(self) -> None:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
89 new_mtime = self.root.stat().st_mtime_ns
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
90 _, old_mtime = self.info
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
91 if old_mtime == new_mtime:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
92 return
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
93 locations = {}
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
94 for child in self.root.iterdir():
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
95 try:
26
7def5611895b Add support for an index page displaying all locations.
Paul Fisher <paul@pfish.zone>
parents: 24
diff changeset
96 locations[child.name] = Location(child, child.name)
21
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
97 except ConfigError:
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
98 pass # It's OK. Skip this.
beb42c835c52 Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents: 9
diff changeset
99 self.info = locations, new_mtime