Bulk convert comments above classes/functions/methods into proper docstrings

This commit is contained in:
Ian Renton
2026-02-27 14:21:35 +00:00
parent 068c732796
commit 6b18ec6f88
63 changed files with 540 additions and 349 deletions

View File

@@ -5,11 +5,12 @@ import pytz
from core.config import MAX_ALERT_AGE
# Generic alert provider class. Subclasses of this query the individual APIs for alerts.
class AlertProvider:
"""Generic alert provider class. Subclasses of this query the individual APIs for alerts."""
# Constructor
def __init__(self, provider_config):
"""Constructor"""
self.name = provider_config["name"]
self.enabled = provider_config["enabled"]
self.last_update_time = datetime.min.replace(tzinfo=pytz.UTC)
@@ -17,19 +18,22 @@ class AlertProvider:
self.alerts = None
self.web_server = None
# Set up the provider, e.g. giving it the alert list to work from
def setup(self, alerts, web_server):
"""Set up the provider, e.g. giving it the alert list to work from"""
self.alerts = alerts
self.web_server = web_server
# Start the provider. This should return immediately after spawning threads to access the remote resources
def start(self):
"""Start the provider. This should return immediately after spawning threads to access the remote resources"""
raise NotImplementedError("Subclasses must implement this method")
# Submit a batch of alerts retrieved from the provider. There is no timestamp checking like there is for spots,
# because alerts could be created at any point for any time in the future. Rely on hashcode-based id matching
# to deal with duplicates.
def submit_batch(self, alerts):
"""Submit a batch of alerts retrieved from the provider. There is no timestamp checking like there is for spots,
because alerts could be created at any point for any time in the future. Rely on hashcode-based id matching
to deal with duplicates."""
# Sort the batch so that earliest ones go in first. This helps keep the ordering correct when alerts are fired
# off to SSE listeners.
alerts = sorted(alerts, key=lambda alert: (alert.start_time if alert and alert.start_time else 0))
@@ -45,6 +49,7 @@ class AlertProvider:
if self.web_server:
self.web_server.notify_new_alert(alert)
# Stop any threads and prepare for application shutdown
def stop(self):
raise NotImplementedError("Subclasses must implement this method")
"""Stop any threads and prepare for application shutdown"""
raise NotImplementedError("Subclasses must implement this method")

View File

@@ -8,8 +8,9 @@ from data.alert import Alert
from data.sig_ref import SIGRef
# Alert provider for Beaches on the Air
class BOTA(HTTPAlertProvider):
"""Alert provider for Beaches on the Air"""
POLL_INTERVAL_SEC = 1800
ALERTS_URL = "https://www.beachesontheair.com/"
@@ -33,7 +34,7 @@ class BOTA(HTTPAlertProvider):
# Get the date, dealing with the fact we get no year so have to figure out if it's last year or next year
date_text = str(cells[2].find('span').contents[0]).strip()
date_time = datetime.strptime(date_text,"%d %b - %H:%M UTC").replace(tzinfo=pytz.UTC)
date_time = datetime.strptime(date_text, "%d %b - %H:%M UTC").replace(tzinfo=pytz.UTC)
date_time = date_time.replace(year=datetime.now(pytz.UTC).year)
# If this was more than a day ago, activation is actually next year
if date_time < datetime.now(pytz.UTC) - timedelta(days=1):

View File

@@ -9,9 +9,9 @@ from alertproviders.alert_provider import AlertProvider
from core.constants import HTTP_HEADERS
# Generic alert provider class for providers that request data via HTTP(S). Just for convenience to avoid code
# duplication. Subclasses of this query the individual APIs for data.
class HTTPAlertProvider(AlertProvider):
"""Generic alert provider class for providers that request data via HTTP(S). Just for convenience to avoid code
duplication. Subclasses of this query the individual APIs for data."""
def __init__(self, provider_config, url, poll_interval):
super().__init__(provider_config)
@@ -56,8 +56,9 @@ class HTTPAlertProvider(AlertProvider):
# Brief pause on error before the next poll, but still respond promptly to stop()
self._stop_event.wait(timeout=1)
# Convert an HTTP response returned by the API into alert data. The whole response is provided here so the subclass
# implementations can check for HTTP status codes if necessary, and handle the response as JSON, XML, text, whatever
# the API actually provides.
def http_response_to_alerts(self, http_response):
raise NotImplementedError("Subclasses must implement this method")
"""Convert an HTTP response returned by the API into alert data. The whole response is provided here so the subclass
implementations can check for HTTP status codes if necessary, and handle the response as JSON, XML, text, whatever
the API actually provides."""
raise NotImplementedError("Subclasses must implement this method")

View File

@@ -8,8 +8,9 @@ from alertproviders.http_alert_provider import HTTPAlertProvider
from data.alert import Alert
# Alert provider NG3K DXpedition list
class NG3K(HTTPAlertProvider):
"""Alert provider NG3K DXpedition list"""
POLL_INTERVAL_SEC = 1800
ALERTS_URL = "https://www.ng3k.com/adxo.xml"
AS_CALL_PATTERN = re.compile("as ([a-z0-9/]+)", re.IGNORECASE)
@@ -48,7 +49,8 @@ class NG3K(HTTPAlertProvider):
start_timestamp = datetime.strptime(start_year + " " + start_mon + " " + start_day, "%Y %b %d").replace(
tzinfo=pytz.UTC).timestamp()
end_timestamp = datetime.strptime(end_year + " " + end_mon + " " + end_day + " 23:59", "%Y %b %d %H:%M").replace(
end_timestamp = datetime.strptime(end_year + " " + end_mon + " " + end_day + " 23:59",
"%Y %b %d %H:%M").replace(
tzinfo=pytz.UTC).timestamp()
# Sometimes the DX callsign is "real", sometimes you just get a prefix with the real working callsigns being
@@ -62,7 +64,7 @@ class NG3K(HTTPAlertProvider):
dx_calls = [parts[2].upper()]
# "Calls" of TBA, TBC or TBD are not real attempts at Turkish callsigns
dx_calls = list(filter(lambda a: a != "TBA" and a != "TBC" and a != "TBD" , dx_calls))
dx_calls = list(filter(lambda a: a != "TBA" and a != "TBC" and a != "TBD", dx_calls))
dx_country = parts[1]
qsl_info = parts[3]

View File

@@ -8,8 +8,9 @@ from data.alert import Alert
from data.sig_ref import SIGRef
# Alert provider for Parks n Peaks
class ParksNPeaks(HTTPAlertProvider):
"""Alert provider for Parks n Peaks"""
POLL_INTERVAL_SEC = 1800
ALERTS_URL = "http://parksnpeaks.org/api/ALERTS/"

View File

@@ -7,8 +7,9 @@ from data.alert import Alert
from data.sig_ref import SIGRef
# Alert provider for Parks on the Air
class POTA(HTTPAlertProvider):
"""Alert provider for Parks on the Air"""
POLL_INTERVAL_SEC = 1800
ALERTS_URL = "https://api.pota.app/activation"
@@ -25,7 +26,8 @@ class POTA(HTTPAlertProvider):
dx_calls=[source_alert["activator"].upper()],
freqs_modes=source_alert["frequencies"],
comment=source_alert["comments"],
sig_refs=[SIGRef(id=source_alert["reference"], sig="POTA", name=source_alert["name"], url="https://pota.app/#/park/" + source_alert["reference"])],
sig_refs=[SIGRef(id=source_alert["reference"], sig="POTA", name=source_alert["name"],
url="https://pota.app/#/park/" + source_alert["reference"])],
start_time=datetime.strptime(source_alert["startDate"] + source_alert["startTime"],
"%Y-%m-%d%H:%M").replace(tzinfo=pytz.UTC).timestamp(),
end_time=datetime.strptime(source_alert["endDate"] + source_alert["endTime"],

View File

@@ -7,8 +7,9 @@ from data.alert import Alert
from data.sig_ref import SIGRef
# Alert provider for Summits on the Air
class SOTA(HTTPAlertProvider):
"""Alert provider for Summits on the Air"""
POLL_INTERVAL_SEC = 1800
ALERTS_URL = "https://api-db2.sota.org.uk/api/alerts/365/all/all"
@@ -31,7 +32,9 @@ class SOTA(HTTPAlertProvider):
dx_names=[source_alert["activatorName"].upper()],
freqs_modes=source_alert["frequency"],
comment=source_alert["comments"],
sig_refs=[SIGRef(id=source_alert["associationCode"] + "/" + source_alert["summitCode"], sig="SOTA", name=summit_name, activation_score=summit_points)],
sig_refs=[
SIGRef(id=source_alert["associationCode"] + "/" + source_alert["summitCode"], sig="SOTA",
name=summit_name, activation_score=summit_points)],
start_time=datetime.strptime(source_alert["dateActivated"],
"%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=pytz.UTC).timestamp(),
is_dxpedition=False)

View File

@@ -8,8 +8,9 @@ from data.alert import Alert
from data.sig_ref import SIGRef
# Alert provider for Wainwrights on the Air
class WOTA(HTTPAlertProvider):
"""Alert provider for Wainwrights on the Air"""
POLL_INTERVAL_SEC = 1800
ALERTS_URL = "https://www.wota.org.uk/alerts_rss.php"
RSS_DATE_TIME_FORMAT = "%a, %d %b %Y %H:%M:%S %z"

View File

@@ -7,8 +7,9 @@ from data.alert import Alert
from data.sig_ref import SIGRef
# Alert provider for Worldwide Flora and Fauna
class WWFF(HTTPAlertProvider):
"""Alert provider for Worldwide Flora and Fauna"""
POLL_INTERVAL_SEC = 1800
ALERTS_URL = "https://spots.wwff.co/static/agendas.json"