Partial fix for mypy issues

This commit is contained in:
Ian Renton
2026-09-20 20:42:16 +01:00
parent 93ea27510f
commit 203758fa2d
25 changed files with 244 additions and 147 deletions
+21 -19
View File
@@ -51,13 +51,13 @@ class APIAlertsHandler(tornado.web.RequestHandler):
# Fetch all alerts matching the query, then optionally enrich with online data
credentials = extract_credentials(self.request.headers)
data = get_alert_list_with_filters(self._alerts, query_params)
assert self._alerts is not None, "initialize() must be called before get()"
alerts = get_alert_list_with_filters(self._alerts, query_params)
fields = [f.strip() for f in query_params["fields"].split(",")] if "fields" in query_params else []
if credentials:
data = self._enrich(data, credentials)
alerts = self._enrich(alerts, credentials)
# Filter for only the required fields, if necessary
if fields:
data = filter_fields(data, fields)
data: list[Alert] | list[dict[str, Any]] = filter_fields(alerts, fields) if fields else alerts
self.write(safe_json_dumps(data))
self.set_status(200)
except ValueError as e:
@@ -104,6 +104,7 @@ class APIAlertsStreamHandler(tornado_eventsource.handler.EventSourceHandler):
# Register to handle new alerts arriving. The callback() method will get called with the new alert as an
# argument.
assert self._sse_alert_broadcaster is not None, "initialize() must be called before open()"
self._sse_alert_broadcaster.register(self)
except Exception:
@@ -113,6 +114,7 @@ class APIAlertsStreamHandler(tornado_eventsource.handler.EventSourceHandler):
def close(self) -> None:
"""When the user closes the socket, deregister ourselves from the alert broadcaster"""
assert self._sse_alert_broadcaster is not None, "initialize() must be called before close()"
self._sse_alert_broadcaster.unregister(self)
super().close()
@@ -121,15 +123,15 @@ class APIAlertsStreamHandler(tornado_eventsource.handler.EventSourceHandler):
try:
# If the new alert matches our param filters, send it to the client. If not, ignore it.
assert self._query_params is not None, "open() must be called before callback()"
if alert_allowed_by_query(alert, self._query_params):
# Add lookup data if we have credentials
if self._credentials:
alert = copy.deepcopy(alert)
alert.infer_missing(self._credentials)
# Filter fields returned if necessary
if self._fields:
alert = filter_fields([alert], self._fields)[0]
self.write_message(msg=safe_json_dumps(alert))
output: Alert | dict[str, Any] = filter_fields([alert], self._fields)[0] if self._fields else alert
self.write_message(msg=safe_json_dumps(output))
except Exception:
logger.exception("Exception in SSE callback, connection will be closed")
self.close()
@@ -151,7 +153,7 @@ def get_alert_list_with_filters(all_alerts: LiveDataCache, query: dict[str, str]
alerts = sorted(alerts, key=lambda alert: alert.start_time if alert and alert.start_time else 0)
alerts = list(filter(lambda alert: alert_allowed_by_query(alert, query), alerts))
if "limit" in query:
alerts = alerts[: int(query.get("limit"))]
alerts = alerts[: int(query["limit"])]
return alerts
@@ -162,11 +164,11 @@ def alert_allowed_by_query(alert: Alert, query: dict[str, str]) -> bool:
for k in query:
match k:
case "received_since":
since = datetime.fromtimestamp(float(query.get(k)), pytz.UTC)
since = datetime.fromtimestamp(float(query[k]), pytz.UTC).timestamp()
if not alert.received_time or alert.received_time <= since:
return False
case "max_duration":
max_duration = int(query.get(k))
max_duration = int(query[k])
# Check the duration if end_time is provided. If end_time is not provided, assume the activation is
# "short", i.e. it always passes this check. If dxpeditions_skip_max_duration_check is true and
# the alert is a dxpedition, or contests_skip_max_duration_check and the alert is a contest, it also
@@ -174,42 +176,42 @@ def alert_allowed_by_query(alert: Alert, query: dict[str, str]) -> bool:
if (
alert.sig == ActivityName.DXPEDITION
and "dxpeditions_skip_max_duration_check" in query
and query.get("dxpeditions_skip_max_duration_check").upper() == "TRUE"
and query["dxpeditions_skip_max_duration_check"].upper() == "TRUE"
):
continue
if (
alert.sig == ActivityName.CONTEST
and "contests_skip_max_duration_check" in query
and query.get("contests_skip_max_duration_check").upper() == "TRUE"
and query["contests_skip_max_duration_check"].upper() == "TRUE"
):
continue
if alert.end_time and alert.start_time and alert.end_time - alert.start_time > max_duration:
return False
case "source":
sources = query.get(k).split(",")
sources = query[k].split(",")
if not alert.source or alert.source not in sources:
return False
case "sig":
# If a list of activities is provided, the alert must have an activity and it must match one of them.
# The special activity "NO_SIG", when supplied in the list, matches alerts with no activity.
activities = query.get(k).split(",")
activities = query[k].split(",")
include_no_activity = "NO_SIG" in activities
if not alert.sig and not include_no_activity:
return False
if alert.sig and alert.sig not in activities:
return False
case "dx_continent":
dxconts = query.get(k).split(",")
dxconts = query[k].split(",")
if not alert.dx_continent or alert.dx_continent not in dxconts:
return False
case "dx_call_includes":
dx_call_includes = query.get(k).strip()
if not alert.dx_call or dx_call_includes.upper() not in alert.dx_call.upper():
dx_call_includes = query[k].strip()
if not alert.dx_calls or not any(dx_call_includes.upper() in c.upper() for c in alert.dx_calls if c):
return False
case "text_includes":
text_includes = query.get(k).strip()
text_includes = query[k].strip()
if (
(not alert.dx_call or text_includes.upper() not in alert.dx_call.upper())
(not alert.dx_calls or not any(text_includes.upper() in c.upper() for c in alert.dx_calls if c))
and (not alert.comment or text_includes.upper() not in alert.comment.upper())
and (not alert.freqs_modes or text_includes.upper() not in alert.freqs_modes.upper())
):