Mercurial > personal > weather-server
annotate weather_server/server.py @ 49:9348c3429c13 default tip
Added tag v0.2.3 for changeset 3b0d7cd9b95f
| author | Paul Fisher <paul@pfish.zone> |
|---|---|
| date | Sat, 03 May 2025 20:14:41 -0400 |
| parents | 1a69004d8e5a |
| children |
| rev | line source |
|---|---|
|
11
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
1 import datetime |
|
4
e7c8dcc5fc15
Make the weather server pip-installable and locally runnable.
Paul Fisher <paul@pfish.zone>
parents:
3
diff
changeset
|
2 import hmac |
|
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
16
diff
changeset
|
3 import pathlib |
|
5
368f732f13d0
Actually make the server runnable.
Paul Fisher <paul@pfish.zone>
parents:
4
diff
changeset
|
4 import sys |
|
4
e7c8dcc5fc15
Make the weather server pip-installable and locally runnable.
Paul Fisher <paul@pfish.zone>
parents:
3
diff
changeset
|
5 |
|
1
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
6 import bson |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
7 import flask |
|
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 common |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
10 from . import locations |
|
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 |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
13 def build_app(root_directory: str) -> flask.Flask: |
|
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
16
diff
changeset
|
14 locs = locations.LocationFolder(pathlib.Path(root_directory)) |
|
1
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
15 app = flask.Flask(__name__) |
|
8
d54155a199d8
Improve template; add stylesheet.
Paul Fisher <paul@pfish.zone>
parents:
5
diff
changeset
|
16 app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0 |
|
1
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
17 |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
18 @app.route('/favicon.ico') |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
19 def favicon(): |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
20 return flask.send_file('static/favicon.ico') |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
21 |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
22 @app.route('/') |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
23 def home(): |
|
26
7def5611895b
Add support for an index page displaying all locations.
Paul Fisher <paul@pfish.zone>
parents:
23
diff
changeset
|
24 locations = tuple(locs.locations().values()) |
|
7def5611895b
Add support for an index page displaying all locations.
Paul Fisher <paul@pfish.zone>
parents:
23
diff
changeset
|
25 return flask.render_template( |
|
7def5611895b
Add support for an index page displaying all locations.
Paul Fisher <paul@pfish.zone>
parents:
23
diff
changeset
|
26 'index.html', |
|
7def5611895b
Add support for an index page displaying all locations.
Paul Fisher <paul@pfish.zone>
parents:
23
diff
changeset
|
27 locations=locations, |
|
7def5611895b
Add support for an index page displaying all locations.
Paul Fisher <paul@pfish.zone>
parents:
23
diff
changeset
|
28 ) |
|
1
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
29 |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
30 @app.route('/_submit', methods=['POST']) |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
31 def submit(): |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
32 req = flask.request |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
33 reader = bson.decode_file_iter( |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
34 req.stream, codec_options=common.BSON_OPTIONS) |
|
3
b42c4bfe57c7
server: Use a "preamble" object in the POST to auth.
Paul Fisher <paul@pfish.zone>
parents:
2
diff
changeset
|
35 try: |
|
b42c4bfe57c7
server: Use a "preamble" object in the POST to auth.
Paul Fisher <paul@pfish.zone>
parents:
2
diff
changeset
|
36 preamble = next(reader) |
|
b42c4bfe57c7
server: Use a "preamble" object in the POST to auth.
Paul Fisher <paul@pfish.zone>
parents:
2
diff
changeset
|
37 loc_name = preamble['location'] |
|
b42c4bfe57c7
server: Use a "preamble" object in the POST to auth.
Paul Fisher <paul@pfish.zone>
parents:
2
diff
changeset
|
38 password = str(preamble['password']) |
|
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
16
diff
changeset
|
39 loc = locs.get(loc_name) |
|
3
b42c4bfe57c7
server: Use a "preamble" object in the POST to auth.
Paul Fisher <paul@pfish.zone>
parents:
2
diff
changeset
|
40 if not hmac.compare_digest(password, loc.password): |
|
b42c4bfe57c7
server: Use a "preamble" object in the POST to auth.
Paul Fisher <paul@pfish.zone>
parents:
2
diff
changeset
|
41 flask.abort(400) |
|
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
16
diff
changeset
|
42 entries = tuple(reader) |
|
3
b42c4bfe57c7
server: Use a "preamble" object in the POST to auth.
Paul Fisher <paul@pfish.zone>
parents:
2
diff
changeset
|
43 except (KeyError, bson.InvalidBSON): |
|
b42c4bfe57c7
server: Use a "preamble" object in the POST to auth.
Paul Fisher <paul@pfish.zone>
parents:
2
diff
changeset
|
44 flask.abort(400) |
|
39
b77c8e7d2742
Use zoneinfo rather than pytz.
Paul Fisher <paul@pfish.zone>
parents:
26
diff
changeset
|
45 now = datetime.datetime.now(tz=datetime.UTC) |
|
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
16
diff
changeset
|
46 loc.record(entries, now) |
|
2
cda47993a193
server: fix bugs and improve template.
Paul Fisher <paul@pfish.zone>
parents:
1
diff
changeset
|
47 return flask.jsonify({'status': 'OK'}) |
|
1
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
48 |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
49 @app.route('/<location>') |
|
11
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
50 def show(location: str): |
|
1
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
51 try: |
|
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
16
diff
changeset
|
52 loc = locs.get(location) |
|
1
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
53 except KeyError: |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
54 flask.abort(404) |
|
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
16
diff
changeset
|
55 last_reading = loc.latest() |
|
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
16
diff
changeset
|
56 if last_reading: |
|
2
cda47993a193
server: fix bugs and improve template.
Paul Fisher <paul@pfish.zone>
parents:
1
diff
changeset
|
57 tz = loc.timezone() |
|
43
1a69004d8e5a
Remove improper, broken use of ZoneInfo.localize.
Paul Fisher <paul@pfish.zone>
parents:
39
diff
changeset
|
58 date = last_reading.sample_time.astimezone(tz) |
|
1
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
59 else: |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
60 date = None |
|
39
b77c8e7d2742
Use zoneinfo rather than pytz.
Paul Fisher <paul@pfish.zone>
parents:
26
diff
changeset
|
61 now = datetime.datetime.now(tz=datetime.UTC) |
|
23
88249e451566
server: show date when last report was >12h ago.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
62 diff = (now - date) if date else None |
|
88249e451566
server: show date when last report was >12h ago.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
63 is_recent = diff and diff < datetime.timedelta(hours=12) |
|
1
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
64 return flask.render_template( |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
65 'location.html', |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
66 location=loc, |
|
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
67 last_reading=last_reading, |
|
23
88249e451566
server: show date when last report was >12h ago.
Paul Fisher <paul@pfish.zone>
parents:
21
diff
changeset
|
68 date=date, |
|
39
b77c8e7d2742
Use zoneinfo rather than pytz.
Paul Fisher <paul@pfish.zone>
parents:
26
diff
changeset
|
69 date_format='%H:%M' if is_recent else '%Y-%m-%d %H:%M') |
|
1
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
70 |
|
11
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
71 @app.route('/<location>/recent') |
|
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
72 def recent(location: str): |
|
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
73 try: |
|
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
16
diff
changeset
|
74 loc = locs.get(location) |
|
11
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
75 except KeyError: |
|
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
76 flask.abort(404) |
|
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
77 req = flask.request |
|
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
78 |
|
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
79 try: |
|
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
80 seconds = int(req.args['seconds']) |
|
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
81 except (KeyError, ValueError): |
|
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
82 flask.abort(400) |
|
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
83 |
|
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
84 start = common.utc_now() - datetime.timedelta(seconds=seconds) |
|
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
85 |
|
21
beb42c835c52
Make weather server handle arbitrary data:
Paul Fisher <paul@pfish.zone>
parents:
16
diff
changeset
|
86 readings = [r for r in loc.entries if start < r['sample_time']] |
|
11
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
87 resp = flask.Response() |
|
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
88 resp.content_type = 'application/json' |
|
16
9a609bcf0809
Port main script to TypeScript and prepare for serving it.
Paul Fisher <paul@pfish.zone>
parents:
11
diff
changeset
|
89 resp.data = common.json_dumps({ |
|
9a609bcf0809
Port main script to TypeScript and prepare for serving it.
Paul Fisher <paul@pfish.zone>
parents:
11
diff
changeset
|
90 'timezone': loc.tz_name, |
|
9a609bcf0809
Port main script to TypeScript and prepare for serving it.
Paul Fisher <paul@pfish.zone>
parents:
11
diff
changeset
|
91 'readings': readings, |
|
9a609bcf0809
Port main script to TypeScript and prepare for serving it.
Paul Fisher <paul@pfish.zone>
parents:
11
diff
changeset
|
92 }) |
|
11
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
93 return resp |
|
52ef21607b31
server: Create endpoint to get some recent readings.
Paul Fisher <paul@pfish.zone>
parents:
8
diff
changeset
|
94 |
|
1
f66df122f18d
get the skeleton of a webserver up?
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
95 return app |
|
4
e7c8dcc5fc15
Make the weather server pip-installable and locally runnable.
Paul Fisher <paul@pfish.zone>
parents:
3
diff
changeset
|
96 |
|
e7c8dcc5fc15
Make the weather server pip-installable and locally runnable.
Paul Fisher <paul@pfish.zone>
parents:
3
diff
changeset
|
97 |
|
e7c8dcc5fc15
Make the weather server pip-installable and locally runnable.
Paul Fisher <paul@pfish.zone>
parents:
3
diff
changeset
|
98 def main(argv): |
|
e7c8dcc5fc15
Make the weather server pip-installable and locally runnable.
Paul Fisher <paul@pfish.zone>
parents:
3
diff
changeset
|
99 """Main function for a simple local demo.""" |
|
5
368f732f13d0
Actually make the server runnable.
Paul Fisher <paul@pfish.zone>
parents:
4
diff
changeset
|
100 app = build_app(argv[0]) |
|
4
e7c8dcc5fc15
Make the weather server pip-installable and locally runnable.
Paul Fisher <paul@pfish.zone>
parents:
3
diff
changeset
|
101 app.run(host='0.0.0.0') |
|
e7c8dcc5fc15
Make the weather server pip-installable and locally runnable.
Paul Fisher <paul@pfish.zone>
parents:
3
diff
changeset
|
102 |
|
e7c8dcc5fc15
Make the weather server pip-installable and locally runnable.
Paul Fisher <paul@pfish.zone>
parents:
3
diff
changeset
|
103 |
|
e7c8dcc5fc15
Make the weather server pip-installable and locally runnable.
Paul Fisher <paul@pfish.zone>
parents:
3
diff
changeset
|
104 if __name__ == '__main__': |
|
e7c8dcc5fc15
Make the weather server pip-installable and locally runnable.
Paul Fisher <paul@pfish.zone>
parents:
3
diff
changeset
|
105 main(sys.argv[1:]) |
