From 374a3268744c56e87d41316f8cfb4b3a2e31de9a Mon Sep 17 00:00:00 2001 From: Ian Renton Date: Sat, 25 Jul 2026 09:02:57 +0100 Subject: [PATCH] Improve error reporting --- alertproviders/http_alert_provider.py | 2 + core/lookup_helper.py | 12 +++- core/sig_utils.py | 4 +- solarconditionsproviders/giroionosonde.py | 12 ++-- .../http_solar_conditions_provider.py | 2 + solarconditionsproviders/kc2gprop.py | 2 + spotproviders/hema.py | 60 ++++++++++--------- spotproviders/http_spot_provider.py | 2 + spotproviders/sota.py | 46 +++++++------- templates/add_spot.html | 2 +- templates/alerts.html | 2 +- templates/bands.html | 4 +- templates/base.html | 10 ++-- templates/conditions.html | 2 +- templates/map.html | 4 +- templates/spots.html | 4 +- templates/status.html | 2 +- 17 files changed, 101 insertions(+), 71 deletions(-) diff --git a/alertproviders/http_alert_provider.py b/alertproviders/http_alert_provider.py index debf791..b7c83de 100644 --- a/alertproviders/http_alert_provider.py +++ b/alertproviders/http_alert_provider.py @@ -56,6 +56,8 @@ class HTTPAlertProvider(AlertProvider): self.status = "Error" logging.warning(f"HTTP {http_response.status_code} when calling {self.name} alerts API.") + except ConnectionError: + logging.warning(f"Connection error when accessing {self.name} alerts API.") except Exception: self.status = "Error" logging.exception("Exception in HTTP JSON Alert Provider (" + self.name + ")") diff --git a/core/lookup_helper.py b/core/lookup_helper.py index 97b6060..f4f7363 100644 --- a/core/lookup_helper.py +++ b/core/lookup_helper.py @@ -153,6 +153,8 @@ class LookupHelper: logging.warning(f"HTTP {response.status_code} when downloading Country-files.com cty.plist.") return False + except ConnectionError: + logging.warning(f"Connection error when downloading Clublog cty.xml.") except Exception as e: logging.error("Exception when downloading Clublog cty.xml", e) return False @@ -175,6 +177,8 @@ class LookupHelper: logging.warning(f"HTTP {response.status_code} when downloading dxcc.json.") return False + except ConnectionError: + logging.warning(f"Connection error when downloading dxcc.json.") except Exception as e: logging.error("Exception when downloading dxcc.json", e) return False @@ -519,8 +523,10 @@ class LookupHelper: except (KeyError, ValueError): continue + except ConnectionError: + logging.warning(f"Connection error when looking up callsign %s using QRZ", lookup_call) except Exception: - logging.error("Exception when looking up QRZ data") + logging.error("Exception when looking up callsign %s using QRZ", lookup_call) return None # Not found in QRZ; cache None so we don't keep retrying @@ -578,8 +584,10 @@ class LookupHelper: except (KeyError, ValueError): continue + except ConnectionError: + logging.warning(f"Connection error when looking up callsign %s using HamQTH", lookup_call) except Exception: - logging.error("Exception when looking up HamQTH data") + logging.error("Exception when looking up callsign %s using HamQTH", lookup_call) return None # Not found in HamQTH; cache None so we don't keep retrying diff --git a/core/sig_utils.py b/core/sig_utils.py index 339bd4a..60f5f96 100644 --- a/core/sig_utils.py +++ b/core/sig_utils.py @@ -259,8 +259,10 @@ def populate_sig_ref_info(sig_ref): else: logging.warning("DME database did not contain data for ref %s", ref_id) + except ConnectionError: + logging.warning("Connection error when looking up sig_ref info for " + sig + " ref " + ref_id) except Exception: - logging.warning("Exception when looking up sig_ref info for " + sig + " ref " + ref_id, exc_info=True) + logging.error("Exception when looking up sig_ref info for " + sig + " ref " + ref_id, exc_info=True) return sig_ref diff --git a/solarconditionsproviders/giroionosonde.py b/solarconditionsproviders/giroionosonde.py index b97b1e3..380bd7e 100644 --- a/solarconditionsproviders/giroionosonde.py +++ b/solarconditionsproviders/giroionosonde.py @@ -127,11 +127,15 @@ class GIROIonosonde(SolarConditionsProvider): from_str = from_time.strftime("%Y.%m.%d+%H:%M:%S") to_str = to_time.strftime("%Y.%m.%d+%H:%M:%S") url = f"{LGDC_URL}?ursiCode={ursi}&charName=foF2,MUFD,fmin&DMUF=3000&fromDate={from_str}&toDate={to_str}" - http_response = requests.get(url, headers=HTTP_HEADERS, timeout=(5, 15)) - if not http_response.ok: - logging.warning(f"HTTP {http_response.status_code} when calling Giro ionosonde API.") + try: + http_response = requests.get(url, headers=HTTP_HEADERS, timeout=(5, 15)) + if not http_response.ok: + logging.warning(f"HTTP {http_response.status_code} when calling Giro ionosonde API.") + return None, None, None + return self._parse_all(http_response.text) + except ConnectionError: + logging.warning("Connection error when accessing Giro ionosonde API.") return None, None, None - return self._parse_all(http_response.text) @staticmethod def _parse_all(text): diff --git a/solarconditionsproviders/http_solar_conditions_provider.py b/solarconditionsproviders/http_solar_conditions_provider.py index 0662b07..fbf87fd 100644 --- a/solarconditionsproviders/http_solar_conditions_provider.py +++ b/solarconditionsproviders/http_solar_conditions_provider.py @@ -51,6 +51,8 @@ class HTTPSolarConditionsProvider(SolarConditionsProvider): self.status = "Error" logging.warning(f"HTTP {http_response.status_code} when calling {self.name} solar conditions API.") + except ConnectionError: + logging.warning(f"Connection error when accessing {self.name} solar conditions API.") except Exception: self.status = "Error" logging.exception("Exception in HTTP Solar Conditions Provider (" + self.name + ")") diff --git a/solarconditionsproviders/kc2gprop.py b/solarconditionsproviders/kc2gprop.py index 47c6500..a33a0f9 100644 --- a/solarconditionsproviders/kc2gprop.py +++ b/solarconditionsproviders/kc2gprop.py @@ -115,6 +115,8 @@ class KC2GProp(SolarConditionsProvider): self.last_update_time = datetime.now(pytz.UTC) logging.debug(f"Updated KC2G ionosonde data for {updated_count} stations.") + except ConnectionError: + logging.warning("Connection error when accessing KC2G ionosonde API.") except Exception: self.status = "Error" logging.exception("Exception in KC2G ionosonde data provider") diff --git a/spotproviders/hema.py b/spotproviders/hema.py index b2517e7..e796f4f 100644 --- a/spotproviders/hema.py +++ b/spotproviders/hema.py @@ -1,3 +1,4 @@ +import logging import re from datetime import datetime @@ -35,34 +36,37 @@ 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, timeout=(5, 30)) - source_data_items = source_data.text.split("=") - # Iterate through source data items. - for source_spot in source_data_items: - spot_items = source_spot.split(";") - # Any line with less than 9 items is not a proper spot line - if len(spot_items) >= 9: - # Fiddle with some data to extract bits we need. Freq/mode and spotter/comment come in combined fields. - freq_mode_match = re.search(self.FREQ_MODE_PATTERN, spot_items[5]) - spotter_comment_match = re.search(self.SPOTTER_COMMENT_PATTERN, spot_items[6]) - if not freq_mode_match or not spotter_comment_match: - continue + try: + 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: + spot_items = source_spot.split(";") + # Any line with less than 9 items is not a proper spot line + if len(spot_items) >= 9: + # Fiddle with some data to extract bits we need. Freq/mode and spotter/comment come in combined fields. + freq_mode_match = re.search(self.FREQ_MODE_PATTERN, spot_items[5]) + spotter_comment_match = re.search(self.SPOTTER_COMMENT_PATTERN, spot_items[6]) + if not freq_mode_match or not spotter_comment_match: + continue - # Convert to our spot format - spot = Spot(source=self.name, - dx_call=spot_items[2].upper(), - de_call=spotter_comment_match.group(1).upper(), - freq=float(freq_mode_match.group(1)) * 1000000, - mode=freq_mode_match.group(2).upper(), - comment=spotter_comment_match.group(2), - sig="HEMA", - sig_refs=[SIGRef(id=spot_items[3].upper(), sig="HEMA", name=spot_items[4])], - time=datetime.strptime(spot_items[0], "%d/%m/%Y %H:%M").replace( - tzinfo=pytz.UTC).timestamp(), - dx_latitude=float(spot_items[7]), - dx_longitude=float(spot_items[8])) + # Convert to our spot format + spot = Spot(source=self.name, + dx_call=spot_items[2].upper(), + de_call=spotter_comment_match.group(1).upper(), + freq=float(freq_mode_match.group(1)) * 1000000, + mode=freq_mode_match.group(2).upper(), + comment=spotter_comment_match.group(2), + sig="HEMA", + sig_refs=[SIGRef(id=spot_items[3].upper(), sig="HEMA", name=spot_items[4])], + time=datetime.strptime(spot_items[0], "%d/%m/%Y %H:%M").replace( + tzinfo=pytz.UTC).timestamp(), + dx_latitude=float(spot_items[7]), + dx_longitude=float(spot_items[8])) - # Add to our list. Don't worry about de-duping, removing old spots etc. at this point; other code will do - # that for us. - new_spots.append(spot) + # Add to our list. Don't worry about de-duping, removing old spots etc. at this point; other + # code will do that for us. + new_spots.append(spot) + except ConnectionError: + logging.warning("Connection error when accessing HEMA spots API.") return new_spots diff --git a/spotproviders/http_spot_provider.py b/spotproviders/http_spot_provider.py index f918a77..9885940 100644 --- a/spotproviders/http_spot_provider.py +++ b/spotproviders/http_spot_provider.py @@ -56,6 +56,8 @@ class HTTPSpotProvider(SpotProvider): self.status = "Error" logging.warning(f"HTTP {http_response.status_code} when calling {self.name} spot API.") + except ConnectionError: + logging.warning(f"Connection error when accessing {self.name} spots API.") except Exception: self.status = "Error" logging.exception("Exception in HTTP JSON Spot Provider (" + self.name + ")") diff --git a/spotproviders/sota.py b/spotproviders/sota.py index 4aff055..fee0ef2 100644 --- a/spotproviders/sota.py +++ b/spotproviders/sota.py @@ -1,3 +1,4 @@ +import logging from datetime import datetime import requests @@ -33,26 +34,29 @@ 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, timeout=(5, 30)).json() - # Iterate through source data - for source_spot in source_data: - # Convert to our spot format - spot = Spot(source=self.name, - source_id=source_spot["id"], - dx_call=source_spot["activatorCallsign"].upper(), - dx_name=source_spot["activatorName"], - de_call=source_spot["callsign"].upper(), - freq=(float(source_spot["frequency"]) * 1000000) if ( - source_spot["frequency"] is not None) else None, - # Seen SOTA spots with no frequency! - mode=source_spot["mode"].upper(), - comment=source_spot["comments"], - sig="SOTA", - sig_refs=[SIGRef(id=source_spot["summitCode"], sig="SOTA", name=source_spot["summitName"], - activation_score=source_spot["points"])], - time=datetime.fromisoformat(source_spot["timeStamp"].replace("Z", "+00:00")).timestamp()) + try: + 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 + spot = Spot(source=self.name, + source_id=source_spot["id"], + dx_call=source_spot["activatorCallsign"].upper(), + dx_name=source_spot["activatorName"], + de_call=source_spot["callsign"].upper(), + freq=(float(source_spot["frequency"]) * 1000000) if ( + source_spot["frequency"] is not None) else None, + # Seen SOTA spots with no frequency! + mode=source_spot["mode"].upper(), + comment=source_spot["comments"], + sig="SOTA", + sig_refs=[SIGRef(id=source_spot["summitCode"], sig="SOTA", name=source_spot["summitName"], + activation_score=source_spot["points"])], + time=datetime.fromisoformat(source_spot["timeStamp"].replace("Z", "+00:00")).timestamp()) - # Add to our list. Don't worry about de-duping, removing old spots etc. at this point; other code will do - # that for us. - new_spots.append(spot) + # Add to our list. Don't worry about de-duping, removing old spots etc. at this point; other code will do + # that for us. + new_spots.append(spot) + except ConnectionError: + logging.warning("Connection error when accessing SOTA spots API") return new_spots diff --git a/templates/add_spot.html b/templates/add_spot.html index 5eb90aa..75c4ec4 100644 --- a/templates/add_spot.html +++ b/templates/add_spot.html @@ -76,7 +76,7 @@ - + diff --git a/templates/alerts.html b/templates/alerts.html index 9d79c2d..6b03924 100644 --- a/templates/alerts.html +++ b/templates/alerts.html @@ -75,7 +75,7 @@ - + diff --git a/templates/bands.html b/templates/bands.html index 8ad31ad..af63fe2 100644 --- a/templates/bands.html +++ b/templates/bands.html @@ -75,8 +75,8 @@ - - + + diff --git a/templates/base.html b/templates/base.html index 3ffcb45..682fc6e 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,6 +1,6 @@ {% extends "skeleton.html" %} {% block head_extra %} - + @@ -10,10 +10,10 @@ - - - - + + + + {% end %} {% block body %}
diff --git a/templates/conditions.html b/templates/conditions.html index 2959b1c..1cdc718 100644 --- a/templates/conditions.html +++ b/templates/conditions.html @@ -284,7 +284,7 @@
- + diff --git a/templates/map.html b/templates/map.html index 0af2128..b2b8889 100644 --- a/templates/map.html +++ b/templates/map.html @@ -95,8 +95,8 @@ - - + + diff --git a/templates/spots.html b/templates/spots.html index e7f3a1d..b5c8d37 100644 --- a/templates/spots.html +++ b/templates/spots.html @@ -116,8 +116,8 @@ - - + + diff --git a/templates/status.html b/templates/status.html index 25ccc0a..ef05c15 100644 --- a/templates/status.html +++ b/templates/status.html @@ -59,7 +59,7 @@ - +