From e4003606974729bc3ad6e19f5e8d7cfcd1b8d20e Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Sat, 15 Jun 2024 21:35:18 +0100 Subject: [PATCH] Send alert emails for particular rocket launches Closes: #156 --- agenda/thespacedevs.py | 21 +++++++++++++++---- update.py | 47 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/agenda/thespacedevs.py b/agenda/thespacedevs.py index 18a5bbb..01a27e5 100644 --- a/agenda/thespacedevs.py +++ b/agenda/thespacedevs.py @@ -7,13 +7,15 @@ from datetime import datetime import requests +from .types import StrDict + Launch = dict[str, typing.Any] Summary = dict[str, typing.Any] ttl = 60 * 60 * 2 # two hours -def next_launch_api(rocket_dir: str, limit: int = 200) -> list[Launch]: +def next_launch_api_data(rocket_dir: str, limit: int = 200) -> StrDict: """Get the next upcoming launches from the API.""" now = datetime.now() filename = os.path.join(rocket_dir, now.strftime("%Y-%m-%d_%H:%M:%S.json")) @@ -21,8 +23,14 @@ def next_launch_api(rocket_dir: str, limit: int = 200) -> list[Launch]: params: dict[str, str | int] = {"limit": limit} r = requests.get(url, params=params) - data = r.json() + data: StrDict = r.json() open(filename, "w").write(r.text) + return data + + +def next_launch_api(rocket_dir: str, limit: int = 200) -> list[Launch]: + """Get the next upcoming launches from the API.""" + data = next_launch_api_data(rocket_dir, limit) return [summarize_launch(launch) for launch in data["results"]] @@ -138,7 +146,7 @@ def summarize_launch(launch: Launch) -> Summary: } -def read_cached_launches(rocket_dir: str) -> list[Summary]: +def load_cached_launches(rocket_dir: str) -> StrDict: """Read the most recent cache of launches.""" existing = [x for x in (filename_timestamp(f) for f in os.listdir(rocket_dir)) if x] @@ -146,7 +154,12 @@ def read_cached_launches(rocket_dir: str) -> list[Summary]: f = existing[0][1] filename = os.path.join(rocket_dir, f) - data = json.load(open(filename)) + return typing.cast(StrDict, json.load(open(filename))) + + +def read_cached_launches(rocket_dir: str) -> list[Summary]: + """Read cached launches.""" + data = load_cached_launches(rocket_dir) return [summarize_launch(launch) for launch in data["results"]] diff --git a/update.py b/update.py index bae4304..09a6d39 100755 --- a/update.py +++ b/update.py @@ -2,9 +2,11 @@ """Combined update script for various data sources.""" import asyncio +import json import os import smtplib import sys +import typing from datetime import date, datetime from email.message import EmailMessage from email.utils import formatdate, make_msgid @@ -19,6 +21,7 @@ import agenda.types import agenda.uk_holiday import agenda.waste_schedule from agenda import gwr +from agenda.types import StrDict from web_view import app @@ -95,15 +98,57 @@ Agenda: https://edwardbetts.com/agenda/ send_mail(config, subject, body) +def report_space_launch_change( + config: flask.config.Config, prev_launch: StrDict, cur_launch: StrDict +) -> None: + """Send mail to announce change to space launch data.""" + subject = f'Change to {cur_launch["name"]}' + + body = f""" +A space launch of interest was updated. + +Get ready for two big piles of JSON. + +Old launch data +{json.dumps(prev_launch, indent=2)} + +New launch data +{json.dumps(cur_launch, indent=2)} + """ + + send_mail(config, subject, body) + + +def get_launch_by_slug(data: StrDict, slug: str) -> StrDict: + """Find last update for space launch.""" + return {item["slug"]: typing.cast(StrDict, item) for item in data["results"]}[slug] + + def update_thespacedevs(config: flask.config.Config) -> None: """Update cache of space launch API.""" rocket_dir = os.path.join(config["DATA_DIR"], "thespacedevs") + existing_data = agenda.thespacedevs.load_cached_launches(rocket_dir) + prev_launches = { + slug: get_launch_by_slug(existing_data, slug) + for slug in config["FOLLOW_LAUNCHES"] + } + t0 = time() - rockets = agenda.thespacedevs.next_launch_api(rocket_dir) + data = agenda.thespacedevs.next_launch_api_data(rocket_dir) + cur_launches = { + slug: get_launch_by_slug(data, slug) for slug in config["FOLLOW_LAUNCHES"] + } + + for slug in config["FOLLOW_LAUNCHES"]: + prev, cur = prev_launches[slug], cur_launches[slug] + if prev["last_updated"] != cur["last_updated"]: + report_space_launch_change(config, prev, cur) + time_taken = time() - t0 if not sys.stdin.isatty(): return + rockets = [agenda.thespacedevs.summarize_launch(item) for item in data["results"]] print(len(rockets), "launches") print(f"took {time_taken:.1f} seconds")