Bug fixes and performance improvements

This commit is contained in:
Ian Renton
2026-05-10 10:57:41 +01:00
parent 74ce486098
commit 363735a235
25 changed files with 82 additions and 66 deletions

View File

@@ -40,7 +40,7 @@ class HTTPAlertProvider(AlertProvider):
try:
# Request data from API
logging.debug("Polling " + self.name + " alert API...")
http_response = requests.get(self._url, headers=HTTP_HEADERS)
http_response = requests.get(self._url, headers=HTTP_HEADERS, timeout=(5, 30))
# Pass off to the subclass for processing
new_alerts = self._http_response_to_alerts(http_response)
# Submit the new alerts for processing. There might not be any alerts for the less popular programs.

View File

@@ -70,27 +70,24 @@ def populate_sig_ref_info(sig_ref):
elif sig.upper() == "WWFF":
wwff_csv_data = SEMI_STATIC_URL_DATA_CACHE.get("https://wwff.co/wwff-data/wwff_directory.csv",
headers=HTTP_HEADERS)
wwff_dr = csv.DictReader(wwff_csv_data.content.decode().splitlines())
for row in wwff_dr:
if row["reference"] == ref_id:
wwff_index = {row["reference"]: row for row in csv.DictReader(wwff_csv_data.content.decode().splitlines())}
row = wwff_index.get(ref_id)
if row:
sig_ref.name = row["name"] if "name" in row else None
sig_ref.url = "https://wwff.co/directory/?showRef=" + ref_id
sig_ref.grid = row["iaruLocator"] if "iaruLocator" in row and row["iaruLocator"] != "-" else None
sig_ref.latitude = float(row["latitude"]) if "latitude" in row and row["latitude"] != "-" else None
sig_ref.longitude = float(row["longitude"]) if "longitude" in row and row[
"longitude"] != "-" else None
break
sig_ref.longitude = float(row["longitude"]) if "longitude" in row and row["longitude"] != "-" else None
elif sig.upper() == "SIOTA":
siota_csv_data = SEMI_STATIC_URL_DATA_CACHE.get("https://www.silosontheair.com/data/silos.csv",
headers=HTTP_HEADERS)
siota_dr = csv.DictReader(siota_csv_data.content.decode().splitlines())
for row in siota_dr:
if row["SILO_CODE"] == ref_id:
siota_index = {row["SILO_CODE"]: row for row in csv.DictReader(siota_csv_data.content.decode().splitlines())}
row = siota_index.get(ref_id)
if row:
sig_ref.name = row["NAME"] if "NAME" in row else None
sig_ref.grid = row["LOCATOR"] if "LOCATOR" in row else None
sig_ref.latitude = float(row["LAT"]) if "LAT" in row else None
sig_ref.longitude = float(row["LNG"]) if "LNG" in row else None
break
elif sig.upper() == "WOTA":
data = SEMI_STATIC_URL_DATA_CACHE.get("https://www.wota.org.uk/mapping/data/summits.json",
headers=HTTP_HEADERS).json()

View File

@@ -41,8 +41,7 @@ class APISpotHandler(tornado.web.RequestHandler):
return
# Reject if format not json
if 'Content-Type' not in self.request.headers or self.request.headers.get(
'Content-Type') != "application/json":
if not self.request.headers.get('Content-Type', '').startswith("application/json"):
self.set_status(415)
self.write(
json.dumps("Error - request Content-Type must be application/json", default=serialize_everything))
@@ -139,7 +138,7 @@ class APISpotHandler(tornado.web.RequestHandler):
except Exception as e:
logging.error(e)
self.write(json.dumps("Error - " + str(e), default=serialize_everything))
self.write(json.dumps("Error - an internal server error occurred.", default=serialize_everything))
self.set_status(500)
self.set_header("Cache-Control", "no-store")
self.set_header("Content-Type", "application/json")

View File

@@ -58,7 +58,7 @@ class APIAlertsHandler(tornado.web.RequestHandler):
self.set_status(400)
except Exception as e:
logging.error(e)
self.write(json.dumps("Error - " + str(e), default=serialize_everything))
self.write(json.dumps("Error - an internal server error occurred.", default=serialize_everything))
self.set_status(500)
self.set_header("Cache-Control", "no-store")
self.set_header("Content-Type", "application/json")

View File

@@ -70,7 +70,7 @@ class APILookupCallHandler(tornado.web.RequestHandler):
except Exception as e:
logging.error(e)
self.write(json.dumps("Error - " + str(e), default=serialize_everything))
self.write(json.dumps("Error - an internal server error occurred.", default=serialize_everything))
self.set_status(500)
self.set_header("Cache-Control", "no-store")
@@ -119,7 +119,7 @@ class APILookupSIGRefHandler(tornado.web.RequestHandler):
except Exception as e:
logging.error(e)
self.write(json.dumps("Error - " + str(e), default=serialize_everything))
self.write(json.dumps("Error - an internal server error occurred.", default=serialize_everything))
self.set_status(500)
self.set_header("Cache-Control", "no-store")
@@ -177,7 +177,7 @@ class APILookupGridHandler(tornado.web.RequestHandler):
except Exception as e:
logging.error(e)
self.write(json.dumps("Error - " + str(e), default=serialize_everything))
self.write(json.dumps("Error - an internal server error occurred.", default=serialize_everything))
self.set_status(500)
self.set_header("Cache-Control", "no-store")

View File

@@ -58,7 +58,7 @@ class APISpotsHandler(tornado.web.RequestHandler):
self.set_status(400)
except Exception as e:
logging.error(e)
self.write(json.dumps("Error - " + str(e), default=serialize_everything))
self.write(json.dumps("Error - an internal server error occurred.", default=serialize_everything))
self.set_status(500)
self.set_header("Cache-Control", "no-store")
self.set_header("Content-Type", "application/json")

View File

@@ -38,7 +38,7 @@ class HTTPSolarConditionsProvider(SolarConditionsProvider):
def _poll(self):
try:
logging.debug("Polling " + self.name + " solar conditions API...")
http_response = requests.get(self._url, headers=HTTP_HEADERS)
http_response = requests.get(self._url, headers=HTTP_HEADERS, timeout=(5, 30))
new_data = self._http_response_to_solar_conditions(http_response)
self.update_data(new_data)

View File

@@ -35,7 +35,7 @@ class HEMA(HTTPSpotProvider):
new_spots = []
# OK, if the spot seed actually changed, now we make the real request for data.
if spot_seed_changed:
source_data = requests.get(self.SPOTS_URL, headers=HTTP_HEADERS)
source_data = requests.get(self.SPOTS_URL, headers=HTTP_HEADERS, timeout=(5, 30))
source_data_items = source_data.text.split("=")
# Iterate through source data items.
for source_spot in source_data_items:

View File

@@ -40,7 +40,7 @@ class HTTPSpotProvider(SpotProvider):
try:
# Request data from API
logging.debug("Polling " + self.name + " spot API...")
http_response = requests.get(self._url, headers=HTTP_HEADERS)
http_response = requests.get(self._url, headers=HTTP_HEADERS, timeout=(5, 30))
# Pass off to the subclass for processing
new_spots = self._http_response_to_spots(http_response)
# Submit the new spots for processing. There might not be any spots for the less popular programs.

View File

@@ -33,7 +33,7 @@ class SOTA(HTTPSpotProvider):
new_spots = []
# OK, if the epoch actually changed, now we make the real request for data.
if epoch_changed:
source_data = requests.get(self.SPOTS_URL, headers=HTTP_HEADERS).json()
source_data = requests.get(self.SPOTS_URL, headers=HTTP_HEADERS, timeout=(5, 30)).json()
# Iterate through source data
for source_spot in source_data:
# Convert to our spot format

View File

@@ -69,7 +69,7 @@
<p>This software is dedicated to the memory of Tom G1PJB, SK, a friend and colleague who sadly passed away around the time I started writing it in Autumn 2025. I was looking forward to showing it to you when it was done.</p>
</div>
<script src="/js/common.js?v=1778343015"></script>
<script src="/js/common.js?v=1778407061"></script>
<script>$(document).ready(function() { $("#nav-link-about").addClass("active"); }); <!-- highlight active page in nav --></script>
{% end %}

View File

@@ -69,8 +69,8 @@
</div>
<script src="/js/common.js?v=1778343015"></script>
<script src="/js/add-spot.js?v=1778343015"></script>
<script src="/js/common.js?v=1778407061"></script>
<script src="/js/add-spot.js?v=1778407061"></script>
<script>$(document).ready(function() { $("#nav-link-add-spot").addClass("active"); }); <!-- highlight active page in nav --></script>
{% end %}

View File

@@ -70,8 +70,8 @@
</div>
<script src="/js/common.js?v=1778343015"></script>
<script src="/js/alerts.js?v=1778343015"></script>
<script src="/js/common.js?v=1778407061"></script>
<script src="/js/alerts.js?v=1778407061"></script>
<script>$(document).ready(function() { $("#nav-link-alerts").addClass("active"); }); <!-- highlight active page in nav --></script>
{% end %}

View File

@@ -76,9 +76,9 @@
<script>
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
</script>
<script src="/js/common.js?v=1778343015"></script>
<script src="/js/spotsbandsandmap.js?v=1778343015"></script>
<script src="/js/bands.js?v=1778343015"></script>
<script src="/js/common.js?v=1778407061"></script>
<script src="/js/spotsbandsandmap.js?v=1778407061"></script>
<script src="/js/bands.js?v=1778407061"></script>
<script>$(document).ready(function() { $("#nav-link-bands").addClass("active"); }); <!-- highlight active page in nav --></script>
{% end %}

View File

@@ -24,7 +24,7 @@
<title>Spothole</title>
<link rel="stylesheet" href="/css/style.css?v=1778343015" type="text/css">
<link rel="stylesheet" href="/css/style.css?v=1778407061" type="text/css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
<link href="/fa/css/fontawesome.min.css" rel="stylesheet" />
@@ -39,16 +39,22 @@
<link rel="manifest" href="manifest.webmanifest">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"
integrity="sha384-1H217gwSVyLSIfaLxHbE7dRb3v4mYCKbpQvzx0cegeju1MVsGrX5xXxAvs/HgeFs"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js"
integrity="sha384-N1xdnJwBzqfCpEDxEeSQzv4NPVPViBQq2NLbzth3YA1pLvR9mtf+TV5g6O+KLkPY"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"
integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/tinycolor2@1.6.0/cjs/tinycolor.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/tinycolor2@1.6.0/cjs/tinycolor.min.js"
integrity="sha384-L1eE4eD41kpBIWe2I0eHy+GnEUC4RIpcvibVW2JCminuPlTl+2Bc528iPdVMg5Dn"
crossorigin="anonymous"></script>
<script src="https://misc.ianrenton.com/jsutils/utils.js?v=1778343015"></script>
<script src="https://misc.ianrenton.com/jsutils/ui-ham.js?v=1778343015"></script>
<script src="https://misc.ianrenton.com/jsutils/geo.js?v=1778343015"></script>
<script src="https://misc.ianrenton.com/jsutils/utils.js?v=1778407061"></script>
<script src="https://misc.ianrenton.com/jsutils/ui-ham.js?v=1778407061"></script>
<script src="https://misc.ianrenton.com/jsutils/geo.js?v=1778407061"></script>
</head>
<body>

View File

@@ -7,10 +7,10 @@
<label for="hamqth-enabled" class="form-check-label">Use data from HamQTH</label>
</div>
<div class="mb-2">
<input type="text" class="storeable-text form-control form-control-sm" id="hamqth-username" placeholder="Username (Callsign)" onchange="saveSettings();" autocomplete="username">
<input type="text" class="storeable-text form-control" id="hamqth-username" placeholder="Username (Callsign)" onchange="saveSettings();" autocomplete="username">
</div>
<div class="mb-2">
<input type="password" class="password-field form-control form-control-sm" id="hamqth-password" placeholder="Password" data-remember-checkbox="hamqth-remember-password" onchange="saveSettings();" autocomplete="current-password">
<input type="password" class="password-field form-control" id="hamqth-password" placeholder="Password" data-remember-checkbox="hamqth-remember-password" onchange="saveSettings();" autocomplete="current-password">
</div>
<div class="form-check">
<input type="checkbox" class="storeable-checkbox form-check-input" id="hamqth-remember-password" onchange="saveSettings();">

View File

@@ -7,10 +7,10 @@
<label for="qrz-enabled" class="form-check-label">Use data from QRZ.com</label>
</div>
<div class="mb-2">
<input type="text" class="storeable-text form-control form-control-sm" id="qrz-username" placeholder="Username (Callsign)" onchange="saveSettings();" autocomplete="username">
<input type="text" class="storeable-text form-control" id="qrz-username" placeholder="Username (Callsign)" onchange="saveSettings();" autocomplete="username">
</div>
<div class="mb-2">
<input type="password" class="password-field form-control form-control-sm" id="qrz-password" placeholder="Password" data-remember-checkbox="qrz-remember-password" onchange="saveSettings();" autocomplete="current-password">
<input type="password" class="password-field form-control" id="qrz-password" placeholder="Password" data-remember-checkbox="qrz-remember-password" onchange="saveSettings();" autocomplete="current-password">
</div>
<div class="form-check">
<input type="checkbox" class="storeable-checkbox form-check-input" id="qrz-remember-password" onchange="saveSettings();">

View File

@@ -230,8 +230,8 @@
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.9/dist/chart.umd.min.js"></script>
<script src="/js/common.js?v=1778343015"></script>
<script src="/js/conditions.js?v=1778343015"></script>
<script src="/js/common.js?v=1778407061"></script>
<script src="/js/conditions.js?v=1778407061"></script>
<script>$(document).ready(function () {
$("#nav-link-conditions").addClass("active");
}); <!-- highlight active page in nav --></script>

View File

@@ -79,6 +79,7 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet-extra-markers@1.2.2/dist/css/leaflet.extra-markers.min.css">
<script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/overlapping-marker-spiderfier-leaflet/dist/oms.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/leaflet-providers@2.0.0/leaflet-providers.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/leaflet-extra-markers@1.2.2/src/assets/js/leaflet.extra-markers.min.js" type="module"></script>
<script src="https://cdn.jsdelivr.net/npm/leaflet.geodesic"></script>
@@ -93,9 +94,9 @@
<script>
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
</script>
<script src="/js/common.js?v=1778343015"></script>
<script src="/js/spotsbandsandmap.js?v=1778343015"></script>
<script src="/js/map.js?v=1778343015"></script>
<script src="/js/common.js?v=1778407061"></script>
<script src="/js/spotsbandsandmap.js?v=1778407061"></script>
<script src="/js/map.js?v=1778407061"></script>
<script>$(document).ready(function() { $("#nav-link-map").addClass("active"); }); <!-- highlight active page in nav --></script>
{% end %}

View File

@@ -104,9 +104,9 @@
<script>
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
</script>
<script src="/js/common.js?v=1778343015"></script>
<script src="/js/spotsbandsandmap.js?v=1778343015"></script>
<script src="/js/spots.js?v=1778343015"></script>
<script src="/js/common.js?v=1778407061"></script>
<script src="/js/spotsbandsandmap.js?v=1778407061"></script>
<script src="/js/spots.js?v=1778407061"></script>
<script>$(document).ready(function() { $("#nav-link-spots").addClass("active"); }); <!-- highlight active page in nav --></script>
{% end %}

View File

@@ -59,8 +59,8 @@
</div>
</div>
<script src="/js/common.js?v=1778343015"></script>
<script src="/js/status.js?v=1778343015"></script>
<script src="/js/common.js?v=1778407061"></script>
<script src="/js/status.js?v=1778407061"></script>
<script>
$(document).ready(function() { $("#nav-link-status").addClass("active"); }); <!-- highlight active page in nav -->
</script>

View File

@@ -103,7 +103,11 @@ function addSpot() {
// Show an "add spot" error.
function showAddSpotError(text) {
$("#result-bad").html("<div class='alert alert-danger alert-dismissible fade show mb-0 mt-4' role='alert'><i class='fa-solid fa-triangle-exclamation'></i> " + text + "<button type='button' class='btn-close' data-bs-dismiss='alert' aria-label='Close'></button></div>");
var div = $("<div class='alert alert-danger alert-dismissible fade show mb-0 mt-4' role='alert'></div>");
div.append("<i class='fa-solid fa-triangle-exclamation'></i> ");
div.append(document.createTextNode(text));
div.append("<button type='button' class='btn-close' data-bs-dismiss='alert' aria-label='Close'></button>");
$("#result-bad").empty().append(div);
}
// Force callsign and mode capitalisation

View File

@@ -219,9 +219,9 @@ function addAlertRowsToTable(tbody, alerts) {
var items = []
for (var i = 0; i < a["sig_refs"].length; i++) {
if (a["sig_refs"][i]["url"] != null) {
items[i] = `<a href='${a["sig_refs"][i]["url"]}' title='${a["sig_refs"][i]["name"]}' target='_new' class='sig-ref-link'>${a["sig_refs"][i]["id"]}</a>`
items[i] = `<a href='${encodeURI(a["sig_refs"][i]["url"])}' title='${escapeHtml(a["sig_refs"][i]["name"])}' target='_new' class='sig-ref-link'>${escapeHtml(a["sig_refs"][i]["id"])}</a>`
} else {
items[i] = `${a["sig_refs"][i]["id"]}`
items[i] = `${escapeHtml(a["sig_refs"][i]["id"])}`
}
}
sig_refs = items.join(", ");

View File

@@ -15,6 +15,7 @@ const WAB_WAI_GRID_COLOR_DARK = 'rgba(60, 60, 120, 1.0)';
var backgroundTileLayer;
var markersLayer;
var geodesicsLayer;
var oms;
var terminator;
var maidenheadGrid;
var cqZones;
@@ -48,7 +49,7 @@ function buildQueryString() {
});
str = str + "max_age=" + $("#max-spot-age option:selected").val();
// Additional filters for the map view: No dupes, no QRT, only spots with good locations
str = str + "&dedupe=true&allow_qrt=false&needs_good_location=true";
str = str + "&dedupe=true&allow_qrt=false";
str = str + getCredentialQueryString();
return str;
}
@@ -58,12 +59,14 @@ function updateMap() {
// Clear existing content
markersLayer.clearLayers();
geodesicsLayer.clearLayers();
oms.clearMarkers();
// Make new markers for all spots that match the filter
spots.forEach(function (s) {
var m = L.marker([s["dx_latitude"], s["dx_longitude"]], {icon: getIcon(s)});
m.bindPopup(getTooltipText(s));
markersLayer.addLayer(m);
oms.addMarker(m);
// Create geodesics if required
if ($("#mapShowGeodesics")[0].checked && s["de_latitude"] != null && s["de_longitude"] != null) {
@@ -414,6 +417,12 @@ function setUpMap() {
markersLayer = new L.LayerGroup();
markersLayer.addTo(map);
// Set up spiderfy for overlapping markers
oms = new OverlappingMarkerSpiderfier(map, {keepSpiderfied: true});
oms.addListener('click', function(marker) {
marker.openPopup();
});
// Add geodesic layer
geodesicsLayer = new L.LayerGroup();
geodesicsLayer.addTo(map);

View File

@@ -290,9 +290,9 @@ function createNewTableRowsForSpot(s, highlightNew) {
var items = []
for (var i = 0; i < s["sig_refs"].length; i++) {
if (s["sig_refs"][i]["url"] != null) {
items[i] = `<span style="white-space: nowrap;"><a href='${s["sig_refs"][i]["url"]}' title='${s["sig_refs"][i]["name"]}' target='_new' class='sig-ref-link'>${s["sig_refs"][i]["id"]}</a></span>`
items[i] = `<span style="white-space: nowrap;"><a href='${encodeURI(s["sig_refs"][i]["url"])}' title='${escapeHtml(s["sig_refs"][i]["name"])}' target='_new' class='sig-ref-link'>${escapeHtml(s["sig_refs"][i]["id"])}</a></span>`
} else {
items[i] = `<span style="white-space: nowrap;">${s["sig_refs"][i]["id"]}</span>`
items[i] = `<span style="white-space: nowrap;">${escapeHtml(s["sig_refs"][i]["id"])}</span>`
}
}
sig_refs = items.join(", ");