Mercurial > personal > weather-server
annotate weather_server/locations.py @ 39:b77c8e7d2742
Use zoneinfo rather than pytz.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Tue, 01 Apr 2025 15:54:21 -0400 |
parents | 2f3473416c11 |
children |
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 |
39
b77c8e7d2742
Use zoneinfo rather than pytz.
Paul Fisher <paul@pfish.zone>
parents:
35
diff
changeset
|
7 import zoneinfo |
1
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
8 |
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
9 from . import logfile |
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
10 from . import types |
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
11 |
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
12 CONFIG_FILE = 'config.ini' |
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
13 LOG = 'log.bson' |
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
14 |
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
15 |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
9
diff
changeset
|
16 class ConfigError(Exception): |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
9
diff
changeset
|
17 """Raised when a location can't be loaded.""" |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
9
diff
changeset
|
18 |
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 class Location: |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
9
diff
changeset
|
21 |
26
7def5611895b
Add support for an index page displaying all locations.
Paul Fisher <paul@pfish.zone>
parents:
24
diff
changeset
|
22 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
|
23 parser = configparser.ConfigParser(interpolation=None) |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
9
diff
changeset
|
24 self.root = root |
26
7def5611895b
Add support for an index page displaying all locations.
Paul Fisher <paul@pfish.zone>
parents:
24
diff
changeset
|
25 self.key = key |
21
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) |
22
e229afdd447b
locations.py: store location name into the right place.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
30 self.name = parser.get( |
21
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') |
24
20c8ec56e447
logfile: Pull logfile thread out of Logger.
Paul Fisher <paul@pfish.zone>
parents:
22
diff
changeset
|
34 self.logger = logfile.Logger( |
21
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]]: |
31
9bc3687e1e5e
logfile: Add an index, and don't keep everything in RAM.
Paul Fisher <paul@pfish.zone>
parents:
26
diff
changeset
|
50 return self.logger.cached_data |
21
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]: |
31
9bc3687e1e5e
logfile: Add an index, and don't keep everything in RAM.
Paul Fisher <paul@pfish.zone>
parents:
26
diff
changeset
|
53 most_recent = reversed(self.logger.cached_data) |
21
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: |
39
b77c8e7d2742
Use zoneinfo rather than pytz.
Paul Fisher <paul@pfish.zone>
parents:
35
diff
changeset
|
63 return zoneinfo.ZoneInfo(self.tz_name) |
b77c8e7d2742
Use zoneinfo rather than pytz.
Paul Fisher <paul@pfish.zone>
parents:
35
diff
changeset
|
64 except zoneinfo.ZoneInfoNotFoundError: |
b77c8e7d2742
Use zoneinfo rather than pytz.
Paul Fisher <paul@pfish.zone>
parents:
35
diff
changeset
|
65 return datetime.UTC |
1
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: |
35
2f3473416c11
Fix format string in repr(Location).
Paul Fisher <paul@pfish.zone>
parents:
31
diff
changeset
|
68 return f'<Location in {self.root!r}>' |
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 |
26
7def5611895b
Add support for an index page displaying all locations.
Paul Fisher <paul@pfish.zone>
parents:
24
diff
changeset
|
84 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
|
85 return self.info[0] |
7def5611895b
Add support for an index page displaying all locations.
Paul Fisher <paul@pfish.zone>
parents:
24
diff
changeset
|
86 |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
9
diff
changeset
|
87 def _maybe_reload(self) -> None: |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
9
diff
changeset
|
88 new_mtime = self.root.stat().st_mtime_ns |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
9
diff
changeset
|
89 _, old_mtime = self.info |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
9
diff
changeset
|
90 if old_mtime == new_mtime: |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
9
diff
changeset
|
91 return |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
9
diff
changeset
|
92 locations = {} |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
9
diff
changeset
|
93 for child in self.root.iterdir(): |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
9
diff
changeset
|
94 try: |
26
7def5611895b
Add support for an index page displaying all locations.
Paul Fisher <paul@pfish.zone>
parents:
24
diff
changeset
|
95 locations[child.name] = Location(child, child.name) |
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
9
diff
changeset
|
96 except ConfigError: |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
9
diff
changeset
|
97 pass # It's OK. Skip this. |
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
9
diff
changeset
|
98 self.info = locations, new_mtime |