Overnight bug fixes

This commit is contained in:
Ian Renton
2026-08-18 17:56:31 +01:00
parent 38bc03a603
commit 7f1a5ae3be
12 changed files with 152 additions and 130 deletions
+54 -47
View File
@@ -1,6 +1,7 @@
import logging
from datetime import datetime
from typing import cast
from xml.parsers.expat import ExpatError
import pytz
from rss_parser import Parser as RSSParser
@@ -25,56 +26,62 @@ class WOTA(HTTPAlertProvider):
def _http_response_to_alerts(self, http_response):
new_alerts = []
rss = cast(RSS, RSSParser.parse(http_response.content.decode("utf-8-sig")))
# Iterate through source data
for source_alert in rss.channel.items:
# Reject GUID missing or zero
if (
not source_alert.guid
or not source_alert.guid.content
or source_alert.guid.content == "http://www.wota.org.uk/alerts/0"
):
continue
# Pick apart the title
dx_call = None
ref = None
ref_name = None
try:
title_split = source_alert.title.split(" on ")
dx_call = title_split[0]
if len(title_split) > 1:
ref_split = title_split[1].split(" - ")
ref = str(ref_split[0])
if len(ref_split) > 1:
ref_name = str(ref_split[1])
except Exception:
logger.warning(f"Could not parse WOTA alert title: {source_alert.description}")
try:
rss = cast(RSS, RSSParser.parse(http_response.content.decode("utf-8-sig")))
# Iterate through source data
for source_alert in rss.channel.items:
# Reject GUID missing or zero
if (
not source_alert.guid
or not source_alert.guid.content
or source_alert.guid.content == "http://www.wota.org.uk/alerts/0"
):
continue
# Pick apart the description
comment = None
freqs_modes = None
try:
desc_split = source_alert.description.split(". ")
freqs_modes = desc_split[0].replace("Frequencies/modes:", "").strip()
if len(desc_split) > 1:
comment = desc_split[1].strip()
except Exception:
logger.warning(f"Could not parse WOTA alert description: {source_alert.description}")
# Pick apart the title
dx_call = None
ref = None
ref_name = None
try:
title_split = source_alert.title.split(" on ")
dx_call = title_split[0]
if len(title_split) > 1:
ref_split = title_split[1].split(" - ")
ref = str(ref_split[0])
if len(ref_split) > 1:
ref_name = str(ref_split[1])
except Exception:
logger.warning(f"Could not parse WOTA alert title: {source_alert.description}")
time = datetime.strptime(source_alert.pub_date.content, self.RSS_DATE_TIME_FORMAT).astimezone(pytz.UTC)
# Pick apart the description
comment = None
freqs_modes = None
try:
desc_split = source_alert.description.split(". ")
freqs_modes = desc_split[0].replace("Frequencies/modes:", "").strip()
if len(desc_split) > 1:
comment = desc_split[1].strip()
except Exception:
logger.warning(f"Could not parse WOTA alert description: {source_alert.description}")
# Convert to our alert format
alert = Alert(
source=self.name,
source_id=source_alert.guid.content,
dx_calls=[dx_call],
freqs_modes=freqs_modes,
comment=comment,
sig_refs=[SIGRef(id=ref, sig="WOTA", name=ref_name)] if ref else [],
start_time=time.timestamp(),
)
time = datetime.strptime(source_alert.pub_date.content, self.RSS_DATE_TIME_FORMAT).astimezone(pytz.UTC)
# Convert to our alert format
alert = Alert(
source=self.name,
source_id=source_alert.guid.content,
dx_calls=[dx_call],
freqs_modes=freqs_modes,
comment=comment,
sig_refs=[SIGRef(id=ref, sig="WOTA", name=ref_name)] if ref else [],
start_time=time.timestamp(),
)
# Add to our list.
new_alerts.append(alert)
except ExpatError:
logger.warning("WOTA alert RSS feed was fetched but was invalid")
# Add to our list.
new_alerts.append(alert)
return new_alerts
+4 -4
View File
@@ -13,10 +13,10 @@ from providers.solarconditions.solar_conditions_provider import SolarConditionsP
logger = logging.getLogger(__name__)
# Each station gets polled roughly once every hour (3600 seconds). Note that to avoid a burst of requests to the server
# every hour, the requests for data from each station are spaced out throughout the hour, leading to one request being
# sent every 1-2 minutes.
POLL_INTERVAL = 3600
# Each station gets polled roughly once every two hours (7200 seconds). Note that to avoid a burst of requests to the
# server every two hours, the requests for data from each station are spaced out throughout the period, leading to one
# request being sent every 3-4 minutes.
POLL_INTERVAL = 7200
# To avoid looking up all stations in the GIRO system and working out which ones are providing live data, this has been
# manually determined and a CSV provided of all the stations that we can query for live data.
STATIONS_INDEX = "datafiles/didbase-stations.csv"
+10 -1
View File
@@ -1,3 +1,4 @@
import logging
from datetime import datetime
import requests
@@ -7,6 +8,8 @@ from data.sig_ref import SIGRef
from data.spot import Spot
from providers.spot.http_spot_provider import HTTPSpotProvider
logger = logging.getLogger(__name__)
class Tiles(HTTPSpotProvider):
"""Spot provider for Tiles on the Air"""
@@ -39,13 +42,19 @@ class Tiles(HTTPSpotProvider):
# Iterate through source data
for source_spot in http_response.json()["spots"]:
# Convert to our spot format
freq = None
try:
freq = float(strip_extra_decimal_points(source_spot["frequency"])) * 1000000
except ValueError:
logger.debug(f"Malformed frequency received from Tiles API: {source_spot['frequency']}")
spot = Spot(
source=self.name,
source_id=source_spot["id"],
dx_call=source_spot["call_sign"].upper(),
# No separate spotter callsign, assume all spots are self-spots
de_call=source_spot["call_sign"].upper(),
freq=float(strip_extra_decimal_points(source_spot["frequency"])) * 1000000,
freq=freq,
mode=source_spot["mode"].upper(),
comment=source_spot["notes"],
sig="Tiles",
+69 -63
View File
@@ -2,6 +2,7 @@ import logging
import re
from datetime import datetime
from typing import cast
from xml.parsers.expat import ExpatError
import pytz
from rss_parser import Parser
@@ -26,73 +27,78 @@ class WOTA(HTTPSpotProvider):
def _http_response_to_spots(self, http_response):
new_spots = []
rss = cast(RSS, Parser.parse(http_response.content.decode("utf-8-sig")))
# Iterate through source data
for source_spot in rss.channel.items:
try:
# Reject GUID missing or zero
if (
not source_spot.guid
or not source_spot.guid.content
or source_spot.guid.content == "http://www.wota.org.uk/spots/0"
):
continue
# Pick apart the title
dx_call = None
ref = None
ref_name = None
try:
rss = cast(RSS, Parser.parse(http_response.content.decode("utf-8-sig")))
# Iterate through source data
for source_spot in rss.channel.items:
try:
title_split = source_spot.title.split(" on ")
dx_call = title_split[0]
if len(title_split) > 1:
ref_split = title_split[1].split(" - ")
ref = str(ref_split[0])
if len(ref_split) > 1:
ref_name = str(ref_split[1])
# Reject GUID missing or zero
if (
not source_spot.guid
or not source_spot.guid.content
or source_spot.guid.content == "http://www.wota.org.uk/spots/0"
):
continue
# Pick apart the title
dx_call = None
ref = None
ref_name = None
try:
title_split = source_spot.title.split(" on ")
dx_call = title_split[0]
if len(title_split) > 1:
ref_split = title_split[1].split(" - ")
ref = str(ref_split[0])
if len(ref_split) > 1:
ref_name = str(ref_split[1])
except Exception:
logger.warning(f"Could not parse WOTA spot title: {source_spot.title}")
# Pick apart the description
freq_hz = None
mode = None
comment = None
spotter = None
try:
desc_split = source_spot.description.split(". ")
freq_mode = desc_split[0].replace("Frequencies/modes:", "").strip()
if freq_mode and freq_mode != "-":
freq_mode_split = re.split(r"[\-\s]+", freq_mode)
freq_hz = float(freq_mode_split[0].replace("'", ".")) * 1000000
if len(freq_mode_split) > 1:
mode = freq_mode_split[1].upper()
if len(desc_split) > 1:
comment = desc_split[1].strip()
if len(desc_split) > 2:
spotter = desc_split[2].replace("Spotted by ", "").replace(".", "").upper().strip()
except Exception:
logger.warning(f"Could not parse WOTA spot description: {source_spot.description}")
time = datetime.strptime(source_spot.pub_date.content, self.RSS_DATE_TIME_FORMAT).astimezone(pytz.UTC)
# Convert to our spot format
spot = Spot(
source=self.name,
source_id=source_spot.guid.content,
dx_call=dx_call,
de_call=spotter,
freq=freq_hz,
mode=mode,
comment=comment,
sig="WOTA",
sig_refs=[SIGRef(id=ref, sig="WOTA", name=ref_name)] if ref else [],
time=time.timestamp(),
)
new_spots.append(spot)
except Exception:
logger.warning(f"Could not parse WOTA spot title: {source_spot.title}")
logger.exception("Exception parsing WOTA spot")
# Pick apart the description
freq_hz = None
mode = None
comment = None
spotter = None
try:
desc_split = source_spot.description.split(". ")
freq_mode = desc_split[0].replace("Frequencies/modes:", "").strip()
if freq_mode and freq_mode != "-":
freq_mode_split = re.split(r"[\-\s]+", freq_mode)
freq_hz = float(freq_mode_split[0].replace("'", ".")) * 1000000
if len(freq_mode_split) > 1:
mode = freq_mode_split[1].upper()
except ExpatError:
logger.warning("WOTA spot RSS feed was fetched but was invalid")
if len(desc_split) > 1:
comment = desc_split[1].strip()
if len(desc_split) > 2:
spotter = desc_split[2].replace("Spotted by ", "").replace(".", "").upper().strip()
except Exception:
logger.warning(f"Could not parse WOTA spot description: {source_spot.description}")
time = datetime.strptime(source_spot.pub_date.content, self.RSS_DATE_TIME_FORMAT).astimezone(pytz.UTC)
# Convert to our spot format
spot = Spot(
source=self.name,
source_id=source_spot.guid.content,
dx_call=dx_call,
de_call=spotter,
freq=freq_hz,
mode=mode,
comment=comment,
sig="WOTA",
sig_refs=[SIGRef(id=ref, sig="WOTA", name=ref_name)] if ref else [],
time=time.timestamp(),
)
new_spots.append(spot)
except Exception:
logger.exception("Exception parsing WOTA spot")
return new_spots
def can_submit_spot(self, sig):
+1 -1
View File
@@ -76,7 +76,7 @@
</div>
<script src="/static/js/add-spot.js?v=1786980624"></script>
<script src="/static/js/add-spot.js?v=1787072191"></script>
<script>$(document).ready(function () {
$("#nav-link-add-spot").addClass("active");
}); <!-- highlight active page in nav --></script>
+1 -1
View File
@@ -84,7 +84,7 @@
</div>
<script src="/static/js/alerts.js?v=1786980625"></script>
<script src="/static/js/alerts.js?v=1787072191"></script>
<script>$(document).ready(function () {
$("#nav-link-alerts").addClass("active");
}); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -76,8 +76,8 @@
</div>
<script src="/static/js/spotsbandsandmap.js?v=1786980624"></script>
<script src="/static/js/bands.js?v=1786980624"></script>
<script src="/static/js/spotsbandsandmap.js?v=1787072191"></script>
<script src="/static/js/bands.js?v=1787072191"></script>
<script>$(document).ready(function () {
$("#nav-link-bands").addClass("active");
}); <!-- highlight active page in nav --></script>
+5 -5
View File
@@ -1,6 +1,6 @@
{% extends "skeleton.html" %}
{% block head_extra %}
<link rel="stylesheet" href="/static/css/style.css?v=1786980624" type="text/css">
<link rel="stylesheet" href="/static/css/style.css?v=1787072191" type="text/css">
<link href="/static/vendor/css/bootstrap-5.3.8.min.css" rel="stylesheet">
<link href="/static/vendor/css/fontawesome-6.7.2.min.css" rel="stylesheet">
<link href="/static/vendor/css/solid-6.7.2.min.css" rel="stylesheet">
@@ -15,10 +15,10 @@
window.fetchEventSource = fetchEventSource;
</script>
<script src="/static/js/utils.js?v=1786980624"></script>
<script src="/static/js/ui-ham.js?v=1786980624"></script>
<script src="/static/js/geo.js?v=1786980624"></script>
<script src="/static/js/common.js?v=1786980624"></script>
<script src="/static/js/utils.js?v=1787072191"></script>
<script src="/static/js/ui-ham.js?v=1787072191"></script>
<script src="/static/js/geo.js?v=1787072191"></script>
<script src="/static/js/common.js?v=1787072191"></script>
{% end %}
{% block body %}
<div class="container">
+1 -1
View File
@@ -284,7 +284,7 @@
</div>
<script src="/static/vendor/js/chart-4.4.9.umd.min.js"></script>
<script src="/static/js/conditions.js?v=1786980624"></script>
<script src="/static/js/conditions.js?v=1787072191"></script>
<script>$(document).ready(function () {
$("#nav-link-conditions").addClass("active");
}); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -109,8 +109,8 @@
<script src="/static/vendor/js/leaflet-cqzones.js"></script>
<script src="/static/vendor/js/leaflet-workedallbritainireland.js" type="module"></script>
<script src="/static/js/spotsbandsandmap.js?v=1786980625"></script>
<script src="/static/js/map.js?v=1786980625"></script>
<script src="/static/js/spotsbandsandmap.js?v=1787072191"></script>
<script src="/static/js/map.js?v=1787072191"></script>
<script>$(document).ready(function () {
$("#nav-link-map").addClass("active");
}); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -113,8 +113,8 @@
</div>
<script src="/static/js/spotsbandsandmap.js?v=1786980624"></script>
<script src="/static/js/spots.js?v=1786980624"></script>
<script src="/static/js/spotsbandsandmap.js?v=1787072191"></script>
<script src="/static/js/spots.js?v=1787072191"></script>
<script>$(document).ready(function () {
$("#nav-link-spots").addClass("active");
}); <!-- highlight active page in nav --></script>
+1 -1
View File
@@ -86,7 +86,7 @@
</div>
</div>
<script src="/static/js/status.js?v=1786980624"></script>
<script src="/static/js/status.js?v=1787072191"></script>
<script>
$(document).ready(function () {
$("#nav-link-status").addClass("active");