diff --git a/alertproviders/http_alert_provider.py b/alertproviders/http_alert_provider.py index 6bed26a..a4dd99a 100644 --- a/alertproviders/http_alert_provider.py +++ b/alertproviders/http_alert_provider.py @@ -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. diff --git a/core/sig_utils.py b/core/sig_utils.py index f1cb429..ff19956 100644 --- a/core/sig_utils.py +++ b/core/sig_utils.py @@ -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: - 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 + 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 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: - 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 + 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 elif sig.upper() == "WOTA": data = SEMI_STATIC_URL_DATA_CACHE.get("https://www.wota.org.uk/mapping/data/summits.json", headers=HTTP_HEADERS).json() diff --git a/server/handlers/api/addspot.py b/server/handlers/api/addspot.py index 8e7104d..1f82b92 100644 --- a/server/handlers/api/addspot.py +++ b/server/handlers/api/addspot.py @@ -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") diff --git a/server/handlers/api/alerts.py b/server/handlers/api/alerts.py index b7740da..3ca8a22 100644 --- a/server/handlers/api/alerts.py +++ b/server/handlers/api/alerts.py @@ -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") diff --git a/server/handlers/api/lookups.py b/server/handlers/api/lookups.py index 8f19f0f..42357e6 100644 --- a/server/handlers/api/lookups.py +++ b/server/handlers/api/lookups.py @@ -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") diff --git a/server/handlers/api/spots.py b/server/handlers/api/spots.py index 0bf739c..9287f50 100644 --- a/server/handlers/api/spots.py +++ b/server/handlers/api/spots.py @@ -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") diff --git a/solarconditionsproviders/http_solar_conditions_provider.py b/solarconditionsproviders/http_solar_conditions_provider.py index 36c5af6..0a8dfcd 100644 --- a/solarconditionsproviders/http_solar_conditions_provider.py +++ b/solarconditionsproviders/http_solar_conditions_provider.py @@ -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) diff --git a/spotproviders/hema.py b/spotproviders/hema.py index 3c8adfb..cad3167 100644 --- a/spotproviders/hema.py +++ b/spotproviders/hema.py @@ -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: diff --git a/spotproviders/http_spot_provider.py b/spotproviders/http_spot_provider.py index c0e2fac..39b052d 100644 --- a/spotproviders/http_spot_provider.py +++ b/spotproviders/http_spot_provider.py @@ -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. diff --git a/spotproviders/sota.py b/spotproviders/sota.py index 70e62cf..ecddf92 100644 --- a/spotproviders/sota.py +++ b/spotproviders/sota.py @@ -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 diff --git a/templates/about.html b/templates/about.html index a3b21c4..8aa094f 100644 --- a/templates/about.html +++ b/templates/about.html @@ -69,7 +69,7 @@

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.

- + {% end %} \ No newline at end of file diff --git a/templates/add_spot.html b/templates/add_spot.html index ab79781..902fa1a 100644 --- a/templates/add_spot.html +++ b/templates/add_spot.html @@ -69,8 +69,8 @@ - - + + {% end %} \ No newline at end of file diff --git a/templates/alerts.html b/templates/alerts.html index cb8ae99..2630e05 100644 --- a/templates/alerts.html +++ b/templates/alerts.html @@ -70,8 +70,8 @@ - - + + {% end %} \ No newline at end of file diff --git a/templates/bands.html b/templates/bands.html index b3e9667..17fcbce 100644 --- a/templates/bands.html +++ b/templates/bands.html @@ -76,9 +76,9 @@ - - - + + + {% end %} \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index 85cf69e..a068fb3 100644 --- a/templates/base.html +++ b/templates/base.html @@ -24,7 +24,7 @@ Spothole - + @@ -39,16 +39,22 @@ - - + + - + - - - + + + diff --git a/templates/cards/hamqth.html b/templates/cards/hamqth.html index 3742113..beaf90a 100644 --- a/templates/cards/hamqth.html +++ b/templates/cards/hamqth.html @@ -7,10 +7,10 @@
- +
- +
diff --git a/templates/cards/qrz.html b/templates/cards/qrz.html index 0f72bb6..f127229 100644 --- a/templates/cards/qrz.html +++ b/templates/cards/qrz.html @@ -7,10 +7,10 @@
- +
- +
diff --git a/templates/conditions.html b/templates/conditions.html index 8ec28eb..5c2abe4 100644 --- a/templates/conditions.html +++ b/templates/conditions.html @@ -230,8 +230,8 @@
- - + + diff --git a/templates/map.html b/templates/map.html index 96b18de..f2569a6 100644 --- a/templates/map.html +++ b/templates/map.html @@ -79,6 +79,7 @@ + @@ -93,9 +94,9 @@ - - - + + + {% end %} \ No newline at end of file diff --git a/templates/spots.html b/templates/spots.html index 4beb2c4..12d3194 100644 --- a/templates/spots.html +++ b/templates/spots.html @@ -104,9 +104,9 @@ - - - + + + {% end %} \ No newline at end of file diff --git a/templates/status.html b/templates/status.html index aa19a88..3e849cf 100644 --- a/templates/status.html +++ b/templates/status.html @@ -59,8 +59,8 @@ - - + + diff --git a/webassets/js/add-spot.js b/webassets/js/add-spot.js index 21fdf7e..5edb301 100644 --- a/webassets/js/add-spot.js +++ b/webassets/js/add-spot.js @@ -103,7 +103,11 @@ function addSpot() { // Show an "add spot" error. function showAddSpotError(text) { - $("#result-bad").html(""); + var div = $(""); + div.append(" "); + div.append(document.createTextNode(text)); + div.append(""); + $("#result-bad").empty().append(div); } // Force callsign and mode capitalisation diff --git a/webassets/js/alerts.js b/webassets/js/alerts.js index d73c6f2..362e030 100644 --- a/webassets/js/alerts.js +++ b/webassets/js/alerts.js @@ -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["sig_refs"][i]["id"]}` + items[i] = `${escapeHtml(a["sig_refs"][i]["id"])}` } else { - items[i] = `${a["sig_refs"][i]["id"]}` + items[i] = `${escapeHtml(a["sig_refs"][i]["id"])}` } } sig_refs = items.join(", "); diff --git a/webassets/js/map.js b/webassets/js/map.js index 9db5466..55126fd 100644 --- a/webassets/js/map.js +++ b/webassets/js/map.js @@ -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); diff --git a/webassets/js/spots.js b/webassets/js/spots.js index 85e4068..ee84f46 100644 --- a/webassets/js/spots.js +++ b/webassets/js/spots.js @@ -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] = `${s["sig_refs"][i]["id"]}` + items[i] = `${escapeHtml(s["sig_refs"][i]["id"])}` } else { - items[i] = `${s["sig_refs"][i]["id"]}` + items[i] = `${escapeHtml(s["sig_refs"][i]["id"])}` } } sig_refs = items.join(", ");