mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-09-20 14:27:42 +00:00
Replace root logger calls with module-specific loggers
This commit is contained in:
@@ -9,6 +9,8 @@ from requests.exceptions import ConnectionError, ConnectTimeout, ReadTimeout
|
||||
from core.constants import HTTP_HEADERS
|
||||
from providers.alert.alert_provider import AlertProvider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HTTPAlertProvider(AlertProvider):
|
||||
"""Generic alert provider class for providers that request data via HTTP(S). Just for convenience to avoid code
|
||||
@@ -24,7 +26,7 @@ class HTTPAlertProvider(AlertProvider):
|
||||
def start(self):
|
||||
# Fire off the polling thread. It will poll immediately on startup, then sleep for poll_interval between
|
||||
# subsequent polls, so start() returns immediately and the application can continue starting.
|
||||
logging.info(f"Set up query of {self.name} alert API every {self._poll_interval!s} seconds.")
|
||||
logger.info(f"Set up query of {self.name} alert API every {self._poll_interval!s} seconds.")
|
||||
self._thread = Thread(target=self._run, name=f"HTTPAlertProvider-{self.name}")
|
||||
self._thread.start()
|
||||
|
||||
@@ -40,7 +42,7 @@ class HTTPAlertProvider(AlertProvider):
|
||||
def _poll(self):
|
||||
try:
|
||||
# Request data from API
|
||||
logging.debug(f"Polling {self.name} alert API...")
|
||||
logger.debug(f"Polling {self.name} alert API...")
|
||||
http_response = requests.get(self._url, headers=HTTP_HEADERS, timeout=(5, 30))
|
||||
# Check response code was good
|
||||
if http_response.ok:
|
||||
@@ -52,18 +54,18 @@ class HTTPAlertProvider(AlertProvider):
|
||||
|
||||
self.status = "OK"
|
||||
self.last_update_time = datetime.now(pytz.UTC)
|
||||
logging.debug(f"Received data from {self.name} alert API.")
|
||||
logger.debug(f"Received data from {self.name} alert API.")
|
||||
else:
|
||||
self.status = "Error"
|
||||
logging.warning(f"HTTP {http_response.status_code} when calling {self.name} alerts API.")
|
||||
logger.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.")
|
||||
logger.warning(f"Connection error when accessing {self.name} alerts API.")
|
||||
except (ConnectTimeout, ReadTimeout):
|
||||
logging.warning(f"Timeout when accessing {self.name} alerts API.")
|
||||
logger.warning(f"Timeout when accessing {self.name} alerts API.")
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception(f"Exception in HTTP JSON Alert Provider ({self.name})")
|
||||
logger.exception(f"Exception in HTTP JSON Alert Provider ({self.name})")
|
||||
# Brief pause on error before the next poll, but still respond promptly to stop()
|
||||
self._stop_event.wait(timeout=1)
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ from data.alert import Alert
|
||||
from data.sig_ref import SIGRef
|
||||
from providers.alert.http_alert_provider import HTTPAlertProvider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ParksNPeaks(HTTPAlertProvider):
|
||||
"""Alert provider for Parks n Peaks"""
|
||||
@@ -64,7 +66,7 @@ class ParksNPeaks(HTTPAlertProvider):
|
||||
"LLOTA",
|
||||
"QRP",
|
||||
]:
|
||||
logging.warning(f"PNP alert found with sig {sig}, developer needs to add support for this!")
|
||||
logger.warning(f"PNP alert found with sig {sig}, developer needs to add support for this!")
|
||||
|
||||
# If this is POTA, SOTA or WWFF data we already have it through other means, so ignore. Otherwise, add to
|
||||
# the alert list. Note that while ZLOTA has its own spots API, it doesn't have its own alerts API. So that
|
||||
|
||||
@@ -11,6 +11,8 @@ from providers.callsigndata.api_query_callsign_data_provider import (
|
||||
APIQueryCallsignDataProvider,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ClublogAPI(APIQueryCallsignDataProvider):
|
||||
"""Callsign data provider for Clublog's API."""
|
||||
@@ -25,7 +27,7 @@ class ClublogAPI(APIQueryCallsignDataProvider):
|
||||
self._callinfo = Callinfo(lookuplib)
|
||||
else:
|
||||
provider_config["enabled"] = False
|
||||
logging.warning(
|
||||
logger.warning(
|
||||
"Clublog XML callsign data provider configured but no api key was provided, this has been disabled."
|
||||
)
|
||||
|
||||
@@ -45,6 +47,6 @@ class ClublogAPI(APIQueryCallsignDataProvider):
|
||||
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception("Exception when looking up data from Clublog API")
|
||||
logger.exception("Exception when looking up data from Clublog API")
|
||||
|
||||
return callsign_data
|
||||
|
||||
@@ -10,6 +10,8 @@ from providers.callsigndata.file_download_callsign_data_provider import (
|
||||
FileDownloadCallsignDataProvider,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ClublogXML(FileDownloadCallsignDataProvider):
|
||||
"""Callsign data provider for ClubLog's Country File, which provides basic callsign to DXCC entity mapping."""
|
||||
@@ -25,7 +27,7 @@ class ClublogXML(FileDownloadCallsignDataProvider):
|
||||
self._api_key = provider_config.get("api_key", "")
|
||||
if self._api_key == "":
|
||||
provider_config["enabled"] = False
|
||||
logging.warning(
|
||||
logger.warning(
|
||||
"Clublog XML callsign data provider configured but no api key was provided, this has been disabled."
|
||||
)
|
||||
|
||||
@@ -55,7 +57,7 @@ class ClublogXML(FileDownloadCallsignDataProvider):
|
||||
return True
|
||||
|
||||
except Exception:
|
||||
logging.exception("Exception when loading Clublog XML.")
|
||||
logger.exception("Exception when loading Clublog XML.")
|
||||
return False
|
||||
|
||||
def _perform_new_lookup(self, callsign, lookup_credentials):
|
||||
@@ -71,6 +73,6 @@ class ClublogXML(FileDownloadCallsignDataProvider):
|
||||
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception("Exception when looking up data from Clublog XML data")
|
||||
logger.exception("Exception when looking up data from Clublog XML data")
|
||||
|
||||
return callsign_data
|
||||
|
||||
@@ -9,6 +9,8 @@ from providers.callsigndata.file_download_callsign_data_provider import (
|
||||
FileDownloadCallsignDataProvider,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CountryFiles(FileDownloadCallsignDataProvider):
|
||||
"""Callsign data provider for Country-files.com, which provides basic callsign to DXCC entity mapping."""
|
||||
@@ -35,7 +37,7 @@ class CountryFiles(FileDownloadCallsignDataProvider):
|
||||
return True
|
||||
|
||||
except Exception:
|
||||
logging.exception("Exception when loading Country Files cty.plist.")
|
||||
logger.exception("Exception when loading Country Files cty.plist.")
|
||||
return False
|
||||
|
||||
def _perform_new_lookup(self, callsign, lookup_credentials):
|
||||
@@ -51,6 +53,6 @@ class CountryFiles(FileDownloadCallsignDataProvider):
|
||||
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception("Exception when looking up data from Country file")
|
||||
logger.exception("Exception when looking up data from Country file")
|
||||
|
||||
return callsign_data
|
||||
|
||||
@@ -10,6 +10,8 @@ from core.constants import HTTP_HEADERS
|
||||
from core.url_data_cache import URLDataCache
|
||||
from providers.callsigndata.callsign_data_provider import CallsignDataProvider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FileDownloadCallsignDataProvider(CallsignDataProvider):
|
||||
"""Generic callsign data provider class for providers that fetch their data from the web by downloading a file."""
|
||||
@@ -30,7 +32,7 @@ class FileDownloadCallsignDataProvider(CallsignDataProvider):
|
||||
def start(self):
|
||||
# Fire off the polling thread. It will poll immediately on startup, then sleep for poll_interval between
|
||||
# subsequent polls, so start() returns immediately and the application can continue starting.
|
||||
logging.info(f"Set up query of {self.name} callsign reference data every {self._poll_interval!s} days.")
|
||||
logger.info(f"Set up query of {self.name} callsign reference data every {self._poll_interval!s} days.")
|
||||
self._thread = Thread(target=self._run, name=f"FileDownloadCallsignDataProvider-{self.name}")
|
||||
self._thread.start()
|
||||
|
||||
@@ -47,7 +49,7 @@ class FileDownloadCallsignDataProvider(CallsignDataProvider):
|
||||
try:
|
||||
# Request the file. Use the data cache (with a TTL of 1 day) here, not as the main mechanism for
|
||||
# caching, but just so continual restarts of the software during testing don't hammer the servers.
|
||||
logging.debug(f"Downloading {self.name} callsign reference data...")
|
||||
logger.debug(f"Downloading {self.name} callsign reference data...")
|
||||
http_response = self._url_data_cache.get(self._url, headers=HTTP_HEADERS)
|
||||
# Check response code was good
|
||||
if http_response.ok:
|
||||
@@ -60,26 +62,26 @@ class FileDownloadCallsignDataProvider(CallsignDataProvider):
|
||||
if ok:
|
||||
self.status = "OK"
|
||||
self.last_update_time = datetime.now(pytz.UTC)
|
||||
logging.info(f"Updated callsign reference data from {self.name}")
|
||||
logger.info(f"Updated callsign reference data from {self.name}")
|
||||
else:
|
||||
self.status = "Error"
|
||||
logging.warning(f"Error updating callsign reference data from {self.name}.")
|
||||
logger.warning(f"Error updating callsign reference data from {self.name}.")
|
||||
|
||||
else:
|
||||
self.status = "Error"
|
||||
logging.warning(
|
||||
logger.warning(
|
||||
f"HTTP {http_response.status_code} when downloading callsign reference data from {self.name}."
|
||||
)
|
||||
|
||||
except ConnectionError:
|
||||
self.status = "Error"
|
||||
logging.warning(f"Connection error when downloading callsign reference data from {self.name}.")
|
||||
logger.warning(f"Connection error when downloading callsign reference data from {self.name}.")
|
||||
except (ConnectTimeout, ReadTimeout):
|
||||
self.status = "Error"
|
||||
logging.warning(f"Timeout when downloading callsign reference data from {self.name}.")
|
||||
logger.warning(f"Timeout when downloading callsign reference data from {self.name}.")
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception(f"Exception in callsign reference data provider ({self.name})")
|
||||
logger.exception(f"Exception in callsign reference data provider ({self.name})")
|
||||
self._stop_event.wait(timeout=1)
|
||||
|
||||
def _handle_file(self, path):
|
||||
|
||||
@@ -17,6 +17,8 @@ from providers.callsigndata.api_query_callsign_data_provider import (
|
||||
APIQueryCallsignDataProvider,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HamQTH(APIQueryCallsignDataProvider):
|
||||
"""Callsign data provider for HamQTH."""
|
||||
@@ -55,10 +57,10 @@ class HamQTH(APIQueryCallsignDataProvider):
|
||||
session_id = str(dict_data["HamQTH"]["session"]["session_id"])
|
||||
else:
|
||||
# Log this failure at debug level only, not our problem if user entered the wrong password.
|
||||
logging.debug("HamQTH login details incorrect, failed to look up with HamQTH.")
|
||||
logger.debug("HamQTH login details incorrect, failed to look up with HamQTH.")
|
||||
return None
|
||||
except Exception:
|
||||
logging.error("Exception when getting HamQTH session key")
|
||||
logger.error("Exception when getting HamQTH session key")
|
||||
return None
|
||||
|
||||
if not session_id:
|
||||
@@ -71,7 +73,7 @@ class HamQTH(APIQueryCallsignDataProvider):
|
||||
if home_call != callsign:
|
||||
calls_to_try.append(home_call)
|
||||
except ValueError:
|
||||
logging.debug(f"Could not look up home call for callsign {callsign}")
|
||||
logger.debug(f"Could not look up home call for callsign {callsign}")
|
||||
|
||||
# Try looking up each call using the API
|
||||
for lookup_call in calls_to_try:
|
||||
@@ -90,18 +92,18 @@ class HamQTH(APIQueryCallsignDataProvider):
|
||||
return self.hamqth_response_to_callsign(callsign, data)
|
||||
|
||||
elif not response.from_cache:
|
||||
logging.warning(f"HTTP {response.status_code} looking up callsign {lookup_call} using HamQTH")
|
||||
logger.warning(f"HTTP {response.status_code} looking up callsign {lookup_call} using HamQTH")
|
||||
|
||||
except (KeyError, ValueError):
|
||||
continue
|
||||
except ConnectionError:
|
||||
logging.warning(f"Connection error when looking up callsign {lookup_call} using HamQTH")
|
||||
logger.warning(f"Connection error when looking up callsign {lookup_call} using HamQTH")
|
||||
continue
|
||||
except (ConnectTimeout, ReadTimeout):
|
||||
logging.warning(f"Timeout when looking up callsign {lookup_call} using HamQTH")
|
||||
logger.warning(f"Timeout when looking up callsign {lookup_call} using HamQTH")
|
||||
continue
|
||||
except Exception:
|
||||
logging.exception(f"Exception when looking up callsign {lookup_call} using HamQTH")
|
||||
logger.exception(f"Exception when looking up callsign {lookup_call} using HamQTH")
|
||||
continue
|
||||
|
||||
# Not found in HamQTH; return a Callsign object with no data so we cache that and don't keep retrying
|
||||
@@ -109,7 +111,7 @@ class HamQTH(APIQueryCallsignDataProvider):
|
||||
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception("Exception when looking up data from HamQTH")
|
||||
logger.exception("Exception when looking up data from HamQTH")
|
||||
# Return None, this won't be cached so we will be asked to query data again for this call next time.
|
||||
return None
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ from providers.callsigndata.api_query_callsign_data_provider import (
|
||||
APIQueryCallsignDataProvider,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class QRZ(APIQueryCallsignDataProvider):
|
||||
"""Callsign data provider for QRZ.com."""
|
||||
@@ -53,10 +55,10 @@ class QRZ(APIQueryCallsignDataProvider):
|
||||
session_key = str(session["Key"])
|
||||
else:
|
||||
# Log this failure at debug level only, not our problem if user entered the wrong password.
|
||||
logging.debug("QRZ.com login details incorrect, failed to look up with QRZ.")
|
||||
logger.debug("QRZ.com login details incorrect, failed to look up with QRZ.")
|
||||
return None
|
||||
except Exception:
|
||||
logging.error("Exception when getting QRZ.com session key")
|
||||
logger.error("Exception when getting QRZ.com session key")
|
||||
return None
|
||||
|
||||
if not session_key:
|
||||
@@ -69,7 +71,7 @@ class QRZ(APIQueryCallsignDataProvider):
|
||||
if home_call != callsign:
|
||||
calls_to_try.append(home_call)
|
||||
except ValueError:
|
||||
logging.debug(f"Could not look up home call for callsign {callsign}")
|
||||
logger.debug(f"Could not look up home call for callsign {callsign}")
|
||||
|
||||
# Try looking up each call using the API
|
||||
for lookup_call in calls_to_try:
|
||||
@@ -93,25 +95,25 @@ class QRZ(APIQueryCallsignDataProvider):
|
||||
elif "Session" in qrz_response and "Error" in qrz_response.get("Session"):
|
||||
# Errors here are normally just "callsign not in database", no need to log that ourselves
|
||||
# above debug level.
|
||||
logging.debug(
|
||||
logger.debug(
|
||||
f"QRZ returned an error looking up callsign {lookup_call}: {qrz_response.get('Session').get('Error')}"
|
||||
)
|
||||
|
||||
elif not response.from_cache:
|
||||
logging.warning(f"QRZ returned a malformed response looking up callsign {lookup_call}")
|
||||
logger.warning(f"QRZ returned a malformed response looking up callsign {lookup_call}")
|
||||
elif not response.from_cache:
|
||||
logging.warning(f"HTTP {response.status_code} looking up callsign {lookup_call} using QRZ")
|
||||
logger.warning(f"HTTP {response.status_code} looking up callsign {lookup_call} using QRZ")
|
||||
|
||||
except (KeyError, ValueError):
|
||||
continue
|
||||
except ConnectionError:
|
||||
logging.warning(f"Connection error when looking up callsign {lookup_call} using QRZ")
|
||||
logger.warning(f"Connection error when looking up callsign {lookup_call} using QRZ")
|
||||
continue
|
||||
except (ConnectTimeout, ReadTimeout):
|
||||
logging.warning(f"Timeout when looking up callsign {lookup_call} using QRZ.")
|
||||
logger.warning(f"Timeout when looking up callsign {lookup_call} using QRZ.")
|
||||
continue
|
||||
except Exception:
|
||||
logging.exception(f"Exception when looking up callsign {lookup_call} using QRZ")
|
||||
logger.exception(f"Exception when looking up callsign {lookup_call} using QRZ")
|
||||
continue
|
||||
|
||||
# Not found in QRZ; return a Callsign object with no data so we cache that and don't keep retrying
|
||||
@@ -119,7 +121,7 @@ class QRZ(APIQueryCallsignDataProvider):
|
||||
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception("Exception when looking up data from QRZ.com")
|
||||
logger.exception("Exception when looking up data from QRZ.com")
|
||||
# Return None, this won't be cached so we will be asked to query data again for this call next time.
|
||||
return None
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ from core.constants import HTTP_HEADERS
|
||||
from core.url_data_cache import URLDataCache
|
||||
from providers.sigrefdata.sig_ref_data_provider import SIGRefDataProvider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FileDownloadSIGRefDataProvider(SIGRefDataProvider):
|
||||
"""Generic SIG ref data provider class for providers that fetch their data from the web by downloading a file."""
|
||||
@@ -26,7 +28,7 @@ class FileDownloadSIGRefDataProvider(SIGRefDataProvider):
|
||||
def start(self):
|
||||
# Fire off the polling thread. It will poll immediately on startup, then sleep for poll_interval between
|
||||
# subsequent polls, so start() returns immediately and the application can continue starting.
|
||||
logging.info(f"Set up query of {self.sig_name} SIG ref data every {self._poll_interval!s} days.")
|
||||
logger.info(f"Set up query of {self.sig_name} SIG ref data every {self._poll_interval!s} days.")
|
||||
self._thread = Thread(target=self._run, name=f"FileDownloadSIGRefDataProvider-{self.sig_name}")
|
||||
self._thread.start()
|
||||
|
||||
@@ -44,7 +46,7 @@ class FileDownloadSIGRefDataProvider(SIGRefDataProvider):
|
||||
try:
|
||||
# Request data from API. Use the data cache (with a TTL of 1 day) here, not as the main mechanism for
|
||||
# caching, but just so continual restarts of the software during testing don't hammer the servers.
|
||||
logging.debug(f"Downloading {self.sig_name} SIG ref data...")
|
||||
logger.debug(f"Downloading {self.sig_name} SIG ref data...")
|
||||
http_response = self._url_data_cache.get(self._url, headers=HTTP_HEADERS)
|
||||
# Check response code was good
|
||||
if http_response.ok:
|
||||
@@ -56,20 +58,20 @@ class FileDownloadSIGRefDataProvider(SIGRefDataProvider):
|
||||
|
||||
self.status = "OK"
|
||||
self.last_update_time = datetime.now(pytz.UTC)
|
||||
logging.debug(f"Received SIG ref data for {self.sig_name}")
|
||||
logger.debug(f"Received SIG ref data for {self.sig_name}")
|
||||
else:
|
||||
self.status = "Error"
|
||||
logging.warning(f"HTTP {http_response.status_code} when downloading SIG ref data for {self.sig_name}.")
|
||||
logger.warning(f"HTTP {http_response.status_code} when downloading SIG ref data for {self.sig_name}.")
|
||||
|
||||
except ConnectionError:
|
||||
self.status = "Error"
|
||||
logging.warning(f"Connection error when downloading SIG ref data for {self.sig_name}.")
|
||||
logger.warning(f"Connection error when downloading SIG ref data for {self.sig_name}.")
|
||||
except (ConnectTimeout, ReadTimeout):
|
||||
self.status = "Error"
|
||||
logging.warning(f"Timeout when downloading SIG ref data for {self.sig_name}.")
|
||||
logger.warning(f"Timeout when downloading SIG ref data for {self.sig_name}.")
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception(f"Exception in HTTP SIG Ref Data Provider ({self.sig_name})")
|
||||
logger.exception(f"Exception in HTTP SIG Ref Data Provider ({self.sig_name})")
|
||||
self._stop_event.wait(timeout=1)
|
||||
|
||||
def _http_response_to_data(self, http_response):
|
||||
|
||||
@@ -8,6 +8,8 @@ from providers.sigrefdata.file_download_sig_ref_data_provider import (
|
||||
FileDownloadSIGRefDataProvider,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IOTA(FileDownloadSIGRefDataProvider):
|
||||
"""SIG ref data provider for Islands on the Air"""
|
||||
@@ -31,7 +33,7 @@ class IOTA(FileDownloadSIGRefDataProvider):
|
||||
try:
|
||||
grid = latlong_to_locator(latitude, longitude, 6)
|
||||
except ValueError:
|
||||
logging.debug(
|
||||
logger.debug(
|
||||
"Error converting lat/lon to locator for an IOTA reference %f %f",
|
||||
latitude,
|
||||
longitude,
|
||||
|
||||
@@ -5,6 +5,8 @@ import pytz
|
||||
|
||||
from providers.sigrefdata.sig_ref_data_provider import SIGRefDataProvider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LocalFileSIGRefDataProvider(SIGRefDataProvider):
|
||||
"""Generic SIG ref data provider class for providers that fetch their data from a local file on startup."""
|
||||
@@ -14,7 +16,7 @@ class LocalFileSIGRefDataProvider(SIGRefDataProvider):
|
||||
self._path = path
|
||||
|
||||
def start(self):
|
||||
logging.debug(f"Loading {self.sig_name} SIG ref data from file.")
|
||||
logger.debug(f"Loading {self.sig_name} SIG ref data from file.")
|
||||
try:
|
||||
new_data = self._file_to_data(self._path)
|
||||
if new_data:
|
||||
@@ -23,10 +25,10 @@ class LocalFileSIGRefDataProvider(SIGRefDataProvider):
|
||||
self.last_update_time = datetime.now(pytz.UTC)
|
||||
else:
|
||||
self.status = "Error"
|
||||
logging.info(f"Failed to load SIG ref data for {self.sig_name}")
|
||||
logger.info(f"Failed to load SIG ref data for {self.sig_name}")
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception(f"Exception in local file SIG Ref Data Provider ({self.sig_name})")
|
||||
logger.exception(f"Exception in local file SIG Ref Data Provider ({self.sig_name})")
|
||||
|
||||
def _file_to_data(self, path):
|
||||
"""Load a file on the given path and turn it into SIG Ref data."""
|
||||
|
||||
@@ -5,6 +5,8 @@ import pytz
|
||||
|
||||
from core.data_store import DATA_STORE
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SIGRefDataProvider:
|
||||
"""Generic SIG reference data provider class. Subclasses of this query the individual URLs or files for data."""
|
||||
@@ -45,4 +47,4 @@ class SIGRefDataProvider:
|
||||
break
|
||||
|
||||
self.reference_count = len(new_data)
|
||||
logging.info(f"Loaded {self.reference_count} references for {self.sig_name} into the data store.")
|
||||
logger.info(f"Loaded {self.reference_count} references for {self.sig_name} into the data store.")
|
||||
|
||||
@@ -9,6 +9,8 @@ from providers.sigrefdata.file_download_sig_ref_data_provider import (
|
||||
FileDownloadSIGRefDataProvider,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WCA(FileDownloadSIGRefDataProvider):
|
||||
"""SIG ref data provider for World Castles Award"""
|
||||
@@ -36,7 +38,7 @@ class WCA(FileDownloadSIGRefDataProvider):
|
||||
longitude = float(split[1])
|
||||
grid = latlong_to_locator(latitude, longitude)
|
||||
except ValueError:
|
||||
logging.debug(f"Encountered dodgy formatting in WCA CSV, skipping location data for {ref_id}")
|
||||
logger.debug(f"Encountered dodgy formatting in WCA CSV, skipping location data for {ref_id}")
|
||||
|
||||
new_data.append(
|
||||
SIGRef(
|
||||
|
||||
@@ -11,6 +11,8 @@ from core.constants import HTTP_HEADERS
|
||||
from providers.solarconditions.ionosonde_utils import compute_band_states
|
||||
from providers.solarconditions.solar_conditions_provider import SolarConditionsProvider
|
||||
|
||||
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.
|
||||
@@ -64,7 +66,7 @@ class GIROIonosonde(SolarConditionsProvider):
|
||||
return stations
|
||||
|
||||
def start(self):
|
||||
logging.info(f"Set up query of GIRO ionosonde data API every {POLL_INTERVAL} seconds.")
|
||||
logger.info(f"Set up query of GIRO ionosonde data API every {POLL_INTERVAL} seconds.")
|
||||
self._thread = Thread(target=self._run, name="GIROIonosondeDataProvider")
|
||||
self._thread.start()
|
||||
|
||||
@@ -86,7 +88,7 @@ class GIROIonosonde(SolarConditionsProvider):
|
||||
ursi = station["ursi"]
|
||||
name = station["name"]
|
||||
try:
|
||||
logging.debug(f"Polling GIRO ionosonde data for {ursi} ({name})...")
|
||||
logger.debug(f"Polling GIRO ionosonde data for {ursi} ({name})...")
|
||||
now = datetime.now(timezone.utc)
|
||||
from_time = now - timedelta(hours=HISTORY_HOURS)
|
||||
cutoff_ts = from_time.timestamp()
|
||||
@@ -127,11 +129,11 @@ class GIROIonosonde(SolarConditionsProvider):
|
||||
self.update_data({"ionosonde_data": ionosonde_data})
|
||||
self.status = "OK"
|
||||
self.last_update_time = datetime.now(pytz.UTC)
|
||||
logging.debug(f"Updated ionosonde data for {ursi} ({name}).")
|
||||
logger.debug(f"Updated ionosonde data for {ursi} ({name}).")
|
||||
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception(f"Exception fetching GIRO ionosonde data for {ursi} ({name})")
|
||||
logger.exception(f"Exception fetching GIRO ionosonde data for {ursi} ({name})")
|
||||
|
||||
def _fetch_station_data(self, ursi, from_time, to_time):
|
||||
"""Fetch foF2, MUF and LUF readings for a station. Returns (fof2_dict, muf_dict, luf_dict) keyed by UNIX timestamp."""
|
||||
@@ -142,14 +144,14 @@ class GIROIonosonde(SolarConditionsProvider):
|
||||
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.")
|
||||
logger.warning(f"HTTP {http_response.status_code} when calling Giro ionosonde API.")
|
||||
return None, None, None
|
||||
return self._parse_all(http_response.text)
|
||||
except (ConnectTimeout, ReadTimeout):
|
||||
logging.warning("Timeout when accessing Giro ionosonde API.")
|
||||
logger.warning("Timeout when accessing Giro ionosonde API.")
|
||||
return None, None, None
|
||||
except ConnectionError:
|
||||
logging.warning("Connection error when accessing Giro ionosonde API.")
|
||||
logger.warning("Connection error when accessing Giro ionosonde API.")
|
||||
return None, None, None
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -9,6 +9,8 @@ from providers.solarconditions.http_solar_conditions_provider import (
|
||||
HTTPSolarConditionsProvider,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
POLL_INTERVAL = 3600 # 1 hour
|
||||
URL = "https://www.hamqsl.com/solarxml.php"
|
||||
|
||||
@@ -24,14 +26,14 @@ class HamQSL(HTTPSolarConditionsProvider):
|
||||
root = ElementTree.fromstring(http_response.text)
|
||||
sd = root.find("solardata")
|
||||
if sd is None:
|
||||
logging.warning("HamQSL solar conditions API returned unexpected XML structure")
|
||||
logger.warning("HamQSL solar conditions API returned unexpected XML structure")
|
||||
return None
|
||||
|
||||
# Some error checking functions in case the data is janky.
|
||||
|
||||
def text(tag, default=None):
|
||||
if sd is None:
|
||||
logging.warning("HamQSL solar conditions API returned unexpected XML structure")
|
||||
logger.warning("HamQSL solar conditions API returned unexpected XML structure")
|
||||
return default
|
||||
el = sd.find(tag)
|
||||
return el.text.strip() if el is not None and el.text else default
|
||||
@@ -79,7 +81,7 @@ class HamQSL(HTTPSolarConditionsProvider):
|
||||
dt = dateutil_parser.parse(updated_str, tzinfos={tz_abbr: timezone})
|
||||
updated = dt.astimezone(pytz.UTC).timestamp()
|
||||
except (ValueError, IndexError):
|
||||
logging.warning(f"HamQSL solar conditions API returned unrecognised timestamp format: {updated_str}")
|
||||
logger.warning(f"HamQSL solar conditions API returned unrecognised timestamp format: {updated_str}")
|
||||
|
||||
# Return the data ready to be put into the solar conditions object.
|
||||
return {
|
||||
|
||||
@@ -9,6 +9,8 @@ from requests.exceptions import ConnectionError, ConnectTimeout, ReadTimeout
|
||||
from core.constants import HTTP_HEADERS
|
||||
from providers.solarconditions.solar_conditions_provider import SolarConditionsProvider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HTTPSolarConditionsProvider(SolarConditionsProvider):
|
||||
"""Generic solar conditions provider for providers that request data via HTTP(S). Subclasses implement
|
||||
@@ -22,7 +24,7 @@ class HTTPSolarConditionsProvider(SolarConditionsProvider):
|
||||
self._stop_event = Event()
|
||||
|
||||
def start(self):
|
||||
logging.info(f"Set up query of {self.name} solar conditions API every {self._poll_interval!s} seconds.")
|
||||
logger.info(f"Set up query of {self.name} solar conditions API every {self._poll_interval!s} seconds.")
|
||||
self._thread = Thread(target=self._run, name=f"HTTPSolarConditionsProvider-{self.name}")
|
||||
self._thread.start()
|
||||
|
||||
@@ -37,7 +39,7 @@ class HTTPSolarConditionsProvider(SolarConditionsProvider):
|
||||
|
||||
def _poll(self):
|
||||
try:
|
||||
logging.debug(f"Polling {self.name} solar conditions API...")
|
||||
logger.debug(f"Polling {self.name} solar conditions API...")
|
||||
http_response = requests.get(self._url, headers=HTTP_HEADERS, timeout=(5, 30))
|
||||
# Check response code was good
|
||||
if http_response.ok:
|
||||
@@ -46,18 +48,18 @@ class HTTPSolarConditionsProvider(SolarConditionsProvider):
|
||||
|
||||
self.status = "OK"
|
||||
self.last_update_time = datetime.now(pytz.UTC)
|
||||
logging.debug(f"Received data from {self.name} solar conditions API.")
|
||||
logger.debug(f"Received data from {self.name} solar conditions API.")
|
||||
else:
|
||||
self.status = "Error"
|
||||
logging.warning(f"HTTP {http_response.status_code} when calling {self.name} solar conditions API.")
|
||||
logger.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.")
|
||||
logger.warning(f"Connection error when accessing {self.name} solar conditions API.")
|
||||
except (ConnectTimeout, ReadTimeout):
|
||||
logging.warning(f"Timeout when accessing {self.name} solar conditions API.")
|
||||
logger.warning(f"Timeout when accessing {self.name} solar conditions API.")
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception(f"Exception in HTTP Solar Conditions Provider ({self.name})")
|
||||
logger.exception(f"Exception in HTTP Solar Conditions Provider ({self.name})")
|
||||
self._stop_event.wait(timeout=1)
|
||||
|
||||
def _http_response_to_solar_conditions(self, http_response):
|
||||
|
||||
@@ -10,6 +10,8 @@ from core.constants import HTTP_HEADERS
|
||||
from providers.solarconditions.ionosonde_utils import compute_band_states
|
||||
from providers.solarconditions.solar_conditions_provider import SolarConditionsProvider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
POLL_INTERVAL = 900 # 15 minutes
|
||||
KC2G_URL = "https://prop.kc2g.com/api/stations.json"
|
||||
HISTORY_HOURS = 24
|
||||
@@ -29,7 +31,7 @@ class KC2GProp(SolarConditionsProvider):
|
||||
self._stop_event = Event()
|
||||
|
||||
def start(self):
|
||||
logging.info(f"Set up query of KC2G ionosonde data API every {POLL_INTERVAL} seconds.")
|
||||
logger.info(f"Set up query of KC2G ionosonde data API every {POLL_INTERVAL} seconds.")
|
||||
self._thread = Thread(target=self._run, name="KC2GPropProvider")
|
||||
self._thread.start()
|
||||
|
||||
@@ -44,10 +46,10 @@ class KC2GProp(SolarConditionsProvider):
|
||||
|
||||
def _poll(self):
|
||||
try:
|
||||
logging.debug("Polling KC2G ionosonde data...")
|
||||
logger.debug("Polling KC2G ionosonde data...")
|
||||
http_response = requests.get(KC2G_URL, headers=HTTP_HEADERS, timeout=(5, 30))
|
||||
if not http_response.ok:
|
||||
logging.warning(f"HTTP {http_response.status_code} when calling KG2G ionosonde API.")
|
||||
logger.warning(f"HTTP {http_response.status_code} when calling KG2G ionosonde API.")
|
||||
return
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
@@ -114,13 +116,13 @@ class KC2GProp(SolarConditionsProvider):
|
||||
self.update_data({"ionosonde_data": ionosonde_data})
|
||||
self.status = "OK"
|
||||
self.last_update_time = datetime.now(pytz.UTC)
|
||||
logging.debug(f"Updated KC2G ionosonde data for {updated_count} stations.")
|
||||
logger.debug(f"Updated KC2G ionosonde data for {updated_count} stations.")
|
||||
|
||||
except ConnectionError:
|
||||
logging.warning("Connection error when accessing KC2G ionosonde API.")
|
||||
logger.warning("Connection error when accessing KC2G ionosonde API.")
|
||||
except (ConnectTimeout, ReadTimeout):
|
||||
logging.warning("Timeout when accessing KC2G ionosonde API.")
|
||||
logger.warning("Timeout when accessing KC2G ionosonde API.")
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception("Exception in KC2G ionosonde data provider")
|
||||
logger.exception("Exception in KC2G ionosonde data provider")
|
||||
self._stop_event.wait(timeout=1)
|
||||
|
||||
@@ -6,6 +6,8 @@ from providers.solarconditions.http_solar_conditions_provider import (
|
||||
HTTPSolarConditionsProvider,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
POLL_INTERVAL = 10800 # Every 3 hours
|
||||
URL = "https://services.swpc.noaa.gov/text/3-day-forecast.txt"
|
||||
|
||||
@@ -28,7 +30,7 @@ class NOAA3dayForecast(HTTPSolarConditionsProvider):
|
||||
start_idx = i
|
||||
break
|
||||
if start_idx is None:
|
||||
logging.warning(f"NOAA 3-day forecast: could not find '{section_header}' section")
|
||||
logger.warning(f"NOAA 3-day forecast: could not find '{section_header}' section")
|
||||
return None
|
||||
|
||||
# Find the date header line by scanning the next few lines for month & day patterns
|
||||
@@ -38,11 +40,11 @@ class NOAA3dayForecast(HTTPSolarConditionsProvider):
|
||||
date_header_idx = j
|
||||
break
|
||||
if date_header_idx is None:
|
||||
logging.warning(f"NOAA 3-day forecast: could not find date header after '{section_header}'")
|
||||
logger.warning(f"NOAA 3-day forecast: could not find date header after '{section_header}'")
|
||||
return None
|
||||
date_matches = re.findall(r"([A-Za-z]{3})\s+(\d{2})", lines[date_header_idx])
|
||||
if not date_matches:
|
||||
logging.warning(f"NOAA 3-day forecast: no dates in header: {lines[date_header_idx]}")
|
||||
logger.warning(f"NOAA 3-day forecast: no dates in header: {lines[date_header_idx]}")
|
||||
return None
|
||||
|
||||
# Figure out the date based on the line found
|
||||
@@ -52,7 +54,7 @@ class NOAA3dayForecast(HTTPSolarConditionsProvider):
|
||||
dt = datetime.strptime(f"{day_str} {month_str} {year}", "%d %b %Y").replace(tzinfo=timezone.utc)
|
||||
column_timestamps.append(dt.timestamp())
|
||||
except ValueError:
|
||||
logging.warning(f"NOAA 3-day forecast: could not parse date: {month_str} {day_str} {year}")
|
||||
logger.warning(f"NOAA 3-day forecast: could not parse date: {month_str} {day_str} {year}")
|
||||
return None
|
||||
|
||||
# Parse data rows. Each non-empty line should have a text label followed by percentage values
|
||||
@@ -91,26 +93,26 @@ class NOAA3dayForecast(HTTPSolarConditionsProvider):
|
||||
start_idx = i
|
||||
break
|
||||
if start_idx is None:
|
||||
logging.warning("NOAA K-index forecast: could not find 'NOAA Kp index breakdown' section")
|
||||
logger.warning("NOAA K-index forecast: could not find 'NOAA Kp index breakdown' section")
|
||||
return None
|
||||
|
||||
# Extract the year from the header line, e.g. "NOAA Kp index breakdown Apr 2-Apr 4, 2026"
|
||||
header_line = lines[start_idx]
|
||||
year_match = re.search(r"\b(\d{4})\b", header_line)
|
||||
if not year_match:
|
||||
logging.warning(f"NOAA K-index forecast: could not extract year from: {header_line}")
|
||||
logger.warning(f"NOAA K-index forecast: could not extract year from: {header_line}")
|
||||
return None
|
||||
year = int(year_match.group(1))
|
||||
|
||||
# Parse the column date headers on the next line, e.g. " Apr 02 Apr 03 Apr 04"
|
||||
if start_idx + 1 >= len(lines):
|
||||
logging.warning("NOAA K-index forecast: missing date header line")
|
||||
logger.warning("NOAA K-index forecast: missing date header line")
|
||||
return None
|
||||
|
||||
date_header_line = lines[start_idx + 2]
|
||||
date_matches = re.findall(r"([A-Za-z]{3})\s+(\d{2})", date_header_line)
|
||||
if not date_matches:
|
||||
logging.warning(f"NOAA K-index forecast: could not parse date headers from: {date_header_line}")
|
||||
logger.warning(f"NOAA K-index forecast: could not parse date headers from: {date_header_line}")
|
||||
return None
|
||||
|
||||
column_dates = []
|
||||
@@ -118,7 +120,7 @@ class NOAA3dayForecast(HTTPSolarConditionsProvider):
|
||||
try:
|
||||
column_dates.append(datetime.strptime(f"{day_str} {month_str} {year}", "%d %b %Y").date())
|
||||
except ValueError:
|
||||
logging.warning(f"NOAA K-index forecast: could not parse date: {month_str} {day_str} {year}")
|
||||
logger.warning(f"NOAA K-index forecast: could not parse date: {month_str} {day_str} {year}")
|
||||
return None
|
||||
|
||||
# Parse each data row, e.g. "00-03UT 2.00 3.00 2.00"
|
||||
@@ -159,7 +161,7 @@ class NOAA3dayForecast(HTTPSolarConditionsProvider):
|
||||
k_index_forecast[key] = kp
|
||||
|
||||
if not k_index_forecast:
|
||||
logging.warning("NOAA K-index forecast: no data rows parsed")
|
||||
logger.warning("NOAA K-index forecast: no data rows parsed")
|
||||
return None
|
||||
|
||||
# Parse Solar Radiation Storm Forecast (single row: "S1 or greater")
|
||||
|
||||
@@ -9,6 +9,8 @@ from core.config import SERVER_OWNER_CALLSIGN
|
||||
from data.spot import Spot
|
||||
from providers.spot.spot_provider import SpotProvider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class APRSIS(SpotProvider):
|
||||
"""Spot provider for the APRS-IS."""
|
||||
@@ -25,10 +27,10 @@ class APRSIS(SpotProvider):
|
||||
def _connect(self):
|
||||
self._aprsis = aprslib.IS(SERVER_OWNER_CALLSIGN)
|
||||
self.status = "Connecting"
|
||||
logging.info("APRS-IS connecting...")
|
||||
logger.info("APRS-IS connecting...")
|
||||
self._aprsis.connect()
|
||||
self._aprsis.consumer(self._handle)
|
||||
logging.info("APRS-IS connected.")
|
||||
logger.info("APRS-IS connected.")
|
||||
|
||||
def stop(self):
|
||||
self.status = "Shutting down"
|
||||
@@ -60,4 +62,4 @@ class APRSIS(SpotProvider):
|
||||
|
||||
self.status = "OK"
|
||||
self.last_update_time = datetime.now(pytz.UTC)
|
||||
logging.debug("Data received from APRS-IS.")
|
||||
logger.debug("Data received from APRS-IS.")
|
||||
|
||||
@@ -11,6 +11,8 @@ from core.config import SERVER_OWNER_CALLSIGN
|
||||
from data.spot import Spot
|
||||
from providers.spot.spot_provider import SpotProvider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DXCluster(SpotProvider):
|
||||
"""Spot provider for a DX Cluster. Hostname, port, login_prompt, login_callsign and allow_rbn_spots are provided in config.
|
||||
@@ -59,19 +61,19 @@ class DXCluster(SpotProvider):
|
||||
while not connected and self._running:
|
||||
try:
|
||||
self.status = "Connecting"
|
||||
logging.info(f"DX Cluster {self._hostname} connecting...")
|
||||
logger.info(f"DX Cluster {self._hostname} connecting...")
|
||||
self._telnet = telnetlib3.Telnet(self._hostname, self._port)
|
||||
self._telnet.read_until(self._login_prompt.encode("latin-1"))
|
||||
self._telnet.write(f"{self._login_callsign}\n".encode("latin-1"))
|
||||
connected = True
|
||||
logging.info(f"DX Cluster {self._hostname} connected.")
|
||||
logger.info(f"DX Cluster {self._hostname} connected.")
|
||||
except ConnectionRefusedError:
|
||||
self.status = "Error"
|
||||
logging.warning(f"Connection refused to DX cluster {self._hostname}")
|
||||
logger.warning(f"Connection refused to DX cluster {self._hostname}")
|
||||
sleep(300)
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception(f"Exception while connecting to DX Cluster Provider ({self._hostname}).")
|
||||
logger.exception(f"Exception while connecting to DX Cluster Provider ({self._hostname}).")
|
||||
sleep(5)
|
||||
|
||||
self.status = "Waiting for Data"
|
||||
@@ -101,25 +103,25 @@ class DXCluster(SpotProvider):
|
||||
|
||||
self.status = "OK"
|
||||
self.last_update_time = datetime.now(pytz.UTC)
|
||||
logging.debug(f"Data received from DX Cluster {self._hostname}.")
|
||||
logger.debug(f"Data received from DX Cluster {self._hostname}.")
|
||||
|
||||
except EOFError:
|
||||
connected = False
|
||||
if self._running:
|
||||
self.status = "Restarting"
|
||||
logging.warning(f"Disconnected from DX Cluster {self._hostname}. Reconnecting...")
|
||||
logger.warning(f"Disconnected from DX Cluster {self._hostname}. Reconnecting...")
|
||||
sleep(5)
|
||||
else:
|
||||
logging.info(f"DX Cluster {self._hostname} shutting down...")
|
||||
logger.info(f"DX Cluster {self._hostname} shutting down...")
|
||||
self.status = "Shutting down"
|
||||
except Exception:
|
||||
connected = False
|
||||
if self._running:
|
||||
self.status = "Error"
|
||||
logging.exception(f"Exception in DX Cluster Provider ({self._hostname})")
|
||||
logger.exception(f"Exception in DX Cluster Provider ({self._hostname})")
|
||||
sleep(5)
|
||||
else:
|
||||
logging.info(f"DX Cluster {self._hostname} shutting down...")
|
||||
logger.info(f"DX Cluster {self._hostname} shutting down...")
|
||||
self.status = "Shutting down"
|
||||
|
||||
self.status = "Disconnected"
|
||||
|
||||
@@ -9,6 +9,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 GMA(HTTPSpotProvider):
|
||||
"""Spot provider for General Mountain Activity"""
|
||||
@@ -24,7 +26,7 @@ class GMA(HTTPSpotProvider):
|
||||
self._api_key = provider_config.get("api_key", "")
|
||||
if self._api_key == "":
|
||||
provider_config["enabled"] = False
|
||||
logging.warning("GMA spot provider configured but no api key was provided, this API will not be queried.")
|
||||
logger.warning("GMA spot provider configured but no api key was provided, this API will not be queried.")
|
||||
self._url_data_cache = URLDataCache("GMA")
|
||||
|
||||
super().__init__(
|
||||
@@ -127,7 +129,7 @@ class GMA(HTTPSpotProvider):
|
||||
spot.sig_refs[0].sig = "MOTA"
|
||||
spot.sig = "MOTA"
|
||||
case _:
|
||||
logging.warning(
|
||||
logger.warning(
|
||||
f"GMA spot found with ref type {ref_info['reftype']}, developer needs to add support for this!"
|
||||
)
|
||||
spot.sig_refs[0].sig = ref_info["reftype"]
|
||||
@@ -139,19 +141,19 @@ class GMA(HTTPSpotProvider):
|
||||
|
||||
elif not ref_response.from_cache:
|
||||
if not ref_response.ok:
|
||||
logging.warning(
|
||||
logger.warning(
|
||||
f"HTTP {ref_response.status_code} when looking up GMA ref {source_spot['REF']}"
|
||||
)
|
||||
else:
|
||||
logging.warning(
|
||||
logger.warning(
|
||||
f"GMA API returned a malformed response when looking up ref {source_spot['REF']}"
|
||||
)
|
||||
except:
|
||||
logging.exception(
|
||||
logger.exception(
|
||||
f"Exception when looking up {self.REF_INFO_URL_ROOT}{source_spot['REF']}, ignoring this spot for now"
|
||||
)
|
||||
else:
|
||||
logging.warning(f"The GMA API returned an unexpected response (HTTP {http_response.status_code}).")
|
||||
logger.warning(f"The GMA API returned an unexpected response (HTTP {http_response.status_code}).")
|
||||
|
||||
return new_spots
|
||||
|
||||
|
||||
@@ -11,6 +11,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 HEMA(HTTPSpotProvider):
|
||||
"""Spot provider for HuMPs Excluding Marilyns Award"""
|
||||
@@ -80,9 +82,9 @@ class HEMA(HTTPSpotProvider):
|
||||
# code will do that for us.
|
||||
new_spots.append(spot)
|
||||
except (ConnectTimeout, ReadTimeout):
|
||||
logging.warning("Timeout when accessing HEMA spots API.")
|
||||
logger.warning("Timeout when accessing HEMA spots API.")
|
||||
except ConnectionError:
|
||||
logging.warning("Connection error when accessing HEMA spots API.")
|
||||
logger.warning("Connection error when accessing HEMA spots API.")
|
||||
return new_spots
|
||||
|
||||
def can_submit_spot(self, sig):
|
||||
|
||||
@@ -10,6 +10,8 @@ from requests.exceptions import ConnectionError, ConnectTimeout
|
||||
from core.constants import HTTP_HEADERS
|
||||
from providers.spot.spot_provider import SpotProvider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HTTPSpotProvider(SpotProvider):
|
||||
"""Generic spot provider class for providers that request data via HTTP(S). Just for convenience to avoid code
|
||||
@@ -26,7 +28,7 @@ class HTTPSpotProvider(SpotProvider):
|
||||
def start(self):
|
||||
# Fire off the polling thread. It will poll immediately on startup, then sleep for poll_interval between
|
||||
# subsequent polls, so start() returns immediately and the application can continue starting.
|
||||
logging.info(f"Set up query of {self.name} spot API every {self._poll_interval!s} seconds.")
|
||||
logger.info(f"Set up query of {self.name} spot API every {self._poll_interval!s} seconds.")
|
||||
self._thread = Thread(target=self._run, name=f"HTTPSpotProvider-{self.name}")
|
||||
self._thread.start()
|
||||
|
||||
@@ -50,7 +52,7 @@ class HTTPSpotProvider(SpotProvider):
|
||||
def _poll(self):
|
||||
try:
|
||||
# Request data from API
|
||||
logging.debug(f"Polling {self.name} spot API...")
|
||||
logger.debug(f"Polling {self.name} spot API...")
|
||||
http_response = requests.get(self._url, headers=HTTP_HEADERS, timeout=(5, 30))
|
||||
# Check response code was good
|
||||
if http_response.ok:
|
||||
@@ -62,18 +64,18 @@ class HTTPSpotProvider(SpotProvider):
|
||||
|
||||
self.status = "OK"
|
||||
self.last_update_time = datetime.now(pytz.UTC)
|
||||
logging.debug(f"Received data from {self.name} spot API.")
|
||||
logger.debug(f"Received data from {self.name} spot API.")
|
||||
else:
|
||||
self.status = "Error"
|
||||
logging.warning(f"HTTP {http_response.status_code} when calling {self.name} spot API.")
|
||||
logger.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.")
|
||||
logger.warning(f"Connection error when accessing {self.name} spots API.")
|
||||
except (ConnectTimeout, ReadTimeout):
|
||||
logging.warning(f"Timeout when accessing {self.name} spots API.")
|
||||
logger.warning(f"Timeout when accessing {self.name} spots API.")
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception(f"Exception in HTTP Spot Provider ({self.name})")
|
||||
logger.exception(f"Exception in HTTP Spot Provider ({self.name})")
|
||||
self._stop_event.wait(timeout=1)
|
||||
|
||||
def _http_response_to_spots(self, http_response):
|
||||
|
||||
@@ -10,6 +10,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 ParksNPeaks(HTTPSpotProvider):
|
||||
"""Spot provider for Parks n Peaks"""
|
||||
@@ -91,7 +93,7 @@ class ParksNPeaks(HTTPSpotProvider):
|
||||
"SANPCPA",
|
||||
"LLOTA",
|
||||
]:
|
||||
logging.warning(f"PNP spot found with sig {sig}, developer needs to add support for this!")
|
||||
logger.warning(f"PNP spot found with sig {sig}, developer needs to add support for this!")
|
||||
|
||||
# Add new spot to the list
|
||||
new_spots.append(spot)
|
||||
|
||||
+10
-8
@@ -11,6 +11,8 @@ from core.config import SERVER_OWNER_CALLSIGN
|
||||
from data.spot import Spot
|
||||
from providers.spot.spot_provider import SpotProvider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RBN(SpotProvider):
|
||||
"""Spot provider for the Reverse Beacon Network. Connects to a single port, if you want both CW/RTTY (port 7000) and FT8
|
||||
@@ -46,15 +48,15 @@ class RBN(SpotProvider):
|
||||
while not connected and self._running:
|
||||
try:
|
||||
self.status = "Connecting"
|
||||
logging.info(f"RBN port {self._port!s} connecting...")
|
||||
logger.info(f"RBN port {self._port!s} connecting...")
|
||||
self._telnet = telnetlib3.Telnet("telnet.reversebeacon.net", self._port)
|
||||
self._telnet.read_until("Please enter your call: ".encode("latin-1"))
|
||||
self._telnet.write(f"{SERVER_OWNER_CALLSIGN}\n".encode("latin-1"))
|
||||
connected = True
|
||||
logging.info(f"RBN port {self._port!s} connected.")
|
||||
logger.info(f"RBN port {self._port!s} connected.")
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception(f"Exception while connecting to RBN (port {self._port!s}).")
|
||||
logger.exception(f"Exception while connecting to RBN (port {self._port!s}).")
|
||||
sleep(5)
|
||||
|
||||
self.status = "Waiting for Data"
|
||||
@@ -84,25 +86,25 @@ class RBN(SpotProvider):
|
||||
|
||||
self.status = "OK"
|
||||
self.last_update_time = datetime.now(pytz.UTC)
|
||||
logging.debug(f"Data received from RBN on port {self._port!s}.")
|
||||
logger.debug(f"Data received from RBN on port {self._port!s}.")
|
||||
|
||||
except EOFError:
|
||||
connected = False
|
||||
if self._running:
|
||||
self.status = "Restarting"
|
||||
logging.warning(f"Disconnected from RBN provider (port {self._port!s}). Reconnecting...")
|
||||
logger.warning(f"Disconnected from RBN provider (port {self._port!s}). Reconnecting...")
|
||||
sleep(5)
|
||||
else:
|
||||
logging.info(f"RBN provider (port {self._port!s}) shutting down...")
|
||||
logger.info(f"RBN provider (port {self._port!s}) shutting down...")
|
||||
self.status = "Shutting down"
|
||||
except Exception:
|
||||
connected = False
|
||||
if self._running:
|
||||
self.status = "Error"
|
||||
logging.exception(f"Exception in RBN provider (port {self._port!s})")
|
||||
logger.exception(f"Exception in RBN provider (port {self._port!s})")
|
||||
sleep(5)
|
||||
else:
|
||||
logging.info(f"RBN provider (port {self._port!s}) shutting down...")
|
||||
logger.info(f"RBN provider (port {self._port!s}) shutting down...")
|
||||
self.status = "Shutting down"
|
||||
|
||||
self.status = "Disconnected"
|
||||
|
||||
@@ -9,6 +9,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 SOTA(HTTPSpotProvider):
|
||||
"""Spot provider for Summits on the Air"""
|
||||
@@ -73,9 +75,9 @@ class SOTA(HTTPSpotProvider):
|
||||
# that for us.
|
||||
new_spots.append(spot)
|
||||
except ConnectionError:
|
||||
logging.warning("Connection error when accessing SOTA spots API")
|
||||
logger.warning("Connection error when accessing SOTA spots API")
|
||||
except (ConnectTimeout, ReadTimeout):
|
||||
logging.warning("Timeout when accessing SOTA spots API.")
|
||||
logger.warning("Timeout when accessing SOTA spots API.")
|
||||
return new_spots
|
||||
|
||||
def can_submit_spot(self, sig):
|
||||
|
||||
@@ -8,6 +8,8 @@ from requests_sse import EventSource
|
||||
from core.constants import HTTP_HEADERS
|
||||
from providers.spot.spot_provider import SpotProvider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SSESpotProvider(SpotProvider):
|
||||
"""Spot provider using Server-Sent Events."""
|
||||
@@ -22,7 +24,7 @@ class SSESpotProvider(SpotProvider):
|
||||
self._event_source = None
|
||||
|
||||
def start(self):
|
||||
logging.info(f"Set up SSE connection to {self.name} spot API.")
|
||||
logger.info(f"Set up SSE connection to {self.name} spot API.")
|
||||
self._stop_event.clear()
|
||||
self._thread = Thread(target=self._run, name=f"SSESpotProvider-{self.name}")
|
||||
self._thread.daemon = True
|
||||
@@ -37,12 +39,12 @@ class SSESpotProvider(SpotProvider):
|
||||
try:
|
||||
event_source.close()
|
||||
except Exception:
|
||||
logging.exception(f"Exception closing SSE connection for {self.name} during stop()")
|
||||
logger.exception(f"Exception closing SSE connection for {self.name} during stop()")
|
||||
|
||||
if self._thread:
|
||||
self._thread.join(timeout=15)
|
||||
if self._thread.is_alive():
|
||||
logging.warning(f"{self.name} SSE worker thread did not exit on time and will be killed.")
|
||||
logger.warning(f"{self.name} SSE worker thread did not exit on time and will be killed.")
|
||||
|
||||
def _on_open(self):
|
||||
self.status = "Waiting for Data"
|
||||
@@ -57,7 +59,7 @@ class SSESpotProvider(SpotProvider):
|
||||
def _run(self):
|
||||
while not self._stop_event.is_set():
|
||||
try:
|
||||
logging.debug(f"Connecting to {self.name} spot API...")
|
||||
logger.debug(f"Connecting to {self.name} spot API...")
|
||||
self.status = "Connecting"
|
||||
with EventSource(
|
||||
self._url,
|
||||
@@ -81,10 +83,10 @@ class SSESpotProvider(SpotProvider):
|
||||
|
||||
self.status = "OK"
|
||||
self.last_update_time = datetime.now(pytz.UTC)
|
||||
logging.debug(f"Received data from {self.name} spot API.")
|
||||
logger.debug(f"Received data from {self.name} spot API.")
|
||||
|
||||
except Exception:
|
||||
logging.exception(
|
||||
logger.exception(
|
||||
f"Exception processing message from SSE Spot Provider ({self.name})"
|
||||
)
|
||||
finally:
|
||||
@@ -92,7 +94,7 @@ class SSESpotProvider(SpotProvider):
|
||||
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception(f"Exception in SSE Spot Provider ({self.name})")
|
||||
logger.exception(f"Exception in SSE Spot Provider ({self.name})")
|
||||
else:
|
||||
self.status = "Disconnected"
|
||||
self._stop_event.wait(timeout=5) # Wait before trying to reconnect
|
||||
|
||||
@@ -9,6 +9,8 @@ from websocket import create_connection
|
||||
from core.constants import HTTP_HEADERS
|
||||
from providers.spot.spot_provider import SpotProvider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WebsocketSpotProvider(SpotProvider):
|
||||
"""Spot provider using websockets."""
|
||||
@@ -22,7 +24,7 @@ class WebsocketSpotProvider(SpotProvider):
|
||||
self._last_event_id = None
|
||||
|
||||
def start(self):
|
||||
logging.info(f"Set up websocket connection to {self.name} spot API.")
|
||||
logger.info(f"Set up websocket connection to {self.name} spot API.")
|
||||
self._stopped = False
|
||||
self._thread = Thread(target=self._run, name=f"WebsocketSpotProvider-{self.name}")
|
||||
self._thread.daemon = True
|
||||
@@ -44,7 +46,7 @@ class WebsocketSpotProvider(SpotProvider):
|
||||
def _run(self):
|
||||
while not self._stopped:
|
||||
try:
|
||||
logging.debug(f"Connecting to {self.name} spot API...")
|
||||
logger.debug(f"Connecting to {self.name} spot API...")
|
||||
self.status = "Connecting"
|
||||
self._ws = create_connection(self._url, header=HTTP_HEADERS)
|
||||
self.status = "Connected"
|
||||
@@ -57,14 +59,14 @@ class WebsocketSpotProvider(SpotProvider):
|
||||
|
||||
self.status = "OK"
|
||||
self.last_update_time = datetime.now(pytz.UTC)
|
||||
logging.debug(f"Received data from {self.name} spot API.")
|
||||
logger.debug(f"Received data from {self.name} spot API.")
|
||||
|
||||
except Exception:
|
||||
logging.exception(f"Exception processing message from Websocket Spot Provider ({self.name})")
|
||||
logger.exception(f"Exception processing message from Websocket Spot Provider ({self.name})")
|
||||
|
||||
except Exception as e:
|
||||
self.status = "Error"
|
||||
logging.exception(f"Exception in Websocket Spot Provider ({self.name})", e)
|
||||
logger.exception(f"Exception in Websocket Spot Provider ({self.name})", e)
|
||||
else:
|
||||
self.status = "Disconnected"
|
||||
sleep(5) # Wait before trying to reconnect
|
||||
|
||||
@@ -11,6 +11,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 WOTA(HTTPSpotProvider):
|
||||
"""Spot provider for Wainwrights on the Air"""
|
||||
@@ -82,7 +84,7 @@ class WOTA(HTTPSpotProvider):
|
||||
|
||||
new_spots.append(spot)
|
||||
except Exception as e:
|
||||
logging.error("Exception parsing WOTA spot", e)
|
||||
logger.error("Exception parsing WOTA spot", e)
|
||||
return new_spots
|
||||
|
||||
def can_submit_spot(self, sig):
|
||||
|
||||
@@ -9,6 +9,8 @@ from providers.staticdata.local_file_static_data_provider import (
|
||||
LocalFileStaticDataProvider,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CQZoneData(LocalFileStaticDataProvider):
|
||||
"""Static data provider for CQ zone geodata."""
|
||||
@@ -34,5 +36,5 @@ class CQZoneData(LocalFileStaticDataProvider):
|
||||
return True
|
||||
|
||||
except Exception:
|
||||
logging.exception("Exception when loading CQ zone data.")
|
||||
logger.exception("Exception when loading CQ zone data.")
|
||||
return False
|
||||
|
||||
@@ -10,6 +10,8 @@ from core.constants import HTTP_HEADERS
|
||||
from core.url_data_cache import URLDataCache
|
||||
from providers.staticdata.static_data_provider import StaticDataProvider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FileDownloadStaticDataProvider(StaticDataProvider):
|
||||
"""Generic static reference data provider class for providers that fetch their data from the web by downloading a
|
||||
@@ -27,7 +29,7 @@ class FileDownloadStaticDataProvider(StaticDataProvider):
|
||||
def start(self):
|
||||
# Fire off the polling thread. It will poll immediately on startup, then sleep for poll_interval between
|
||||
# subsequent polls, so start() returns immediately and the application can continue starting.
|
||||
logging.info(f"Set up query of {self.name} static reference data every {self._poll_interval!s} days.")
|
||||
logger.info(f"Set up query of {self.name} static reference data every {self._poll_interval!s} days.")
|
||||
self._thread = Thread(target=self._run, name=f"FileDownloadStaticDataProvider-{self.name}")
|
||||
self._thread.start()
|
||||
|
||||
@@ -44,7 +46,7 @@ class FileDownloadStaticDataProvider(StaticDataProvider):
|
||||
try:
|
||||
# Request data from API. Use the data cache (with a TTL of 1 day) here, not as the main mechanism for
|
||||
# caching, but just so continual restarts of the software during testing don't hammer the servers.
|
||||
logging.debug(f"Downloading {self.name} static reference data...")
|
||||
logger.debug(f"Downloading {self.name} static reference data...")
|
||||
http_response = self._url_data_cache.get(self._url, headers=HTTP_HEADERS)
|
||||
# Check response code was good
|
||||
if http_response.ok:
|
||||
@@ -53,22 +55,22 @@ class FileDownloadStaticDataProvider(StaticDataProvider):
|
||||
if ok:
|
||||
self.status = "OK"
|
||||
self.last_update_time = datetime.now(pytz.UTC)
|
||||
logging.info(f"Updated static reference data for {self.name}")
|
||||
logger.info(f"Updated static reference data for {self.name}")
|
||||
else:
|
||||
self.status = "Error"
|
||||
logging.warning(
|
||||
logger.warning(
|
||||
f"HTTP {http_response.status_code} when downloading static reference data for {self.name}."
|
||||
)
|
||||
|
||||
except ConnectionError:
|
||||
self.status = "Error"
|
||||
logging.warning(f"Connection error when downloading static reference data for {self.name}.")
|
||||
logger.warning(f"Connection error when downloading static reference data for {self.name}.")
|
||||
except (ConnectTimeout, ReadTimeout):
|
||||
self.status = "Error"
|
||||
logging.warning(f"Timeout when downloading static reference data for {self.name}.")
|
||||
logger.warning(f"Timeout when downloading static reference data for {self.name}.")
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception(f"Exception in HTTP static reference data provider ({self.name})")
|
||||
logger.exception(f"Exception in HTTP static reference data provider ({self.name})")
|
||||
self._stop_event.wait(timeout=1)
|
||||
|
||||
def _handle_http_response(self, http_response):
|
||||
|
||||
@@ -9,6 +9,8 @@ from providers.staticdata.local_file_static_data_provider import (
|
||||
LocalFileStaticDataProvider,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ITUZoneData(LocalFileStaticDataProvider):
|
||||
"""Static data provider for ITU zone geodata."""
|
||||
@@ -34,5 +36,5 @@ class ITUZoneData(LocalFileStaticDataProvider):
|
||||
return True
|
||||
|
||||
except Exception:
|
||||
logging.exception("Exception when loading ITU zone data.")
|
||||
logger.exception("Exception when loading ITU zone data.")
|
||||
return False
|
||||
|
||||
@@ -5,6 +5,8 @@ from providers.staticdata.file_download_static_data_provider import (
|
||||
FileDownloadStaticDataProvider,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class K0SWE(FileDownloadStaticDataProvider):
|
||||
"""Static data provider for K0SWE's dxcc.json, which provides callsign regex to DXCC entity mapping, plus DXCC to
|
||||
@@ -44,5 +46,5 @@ class K0SWE(FileDownloadStaticDataProvider):
|
||||
return True
|
||||
|
||||
except Exception:
|
||||
logging.exception("Exception when loading K0SWE dxcc.json.")
|
||||
logger.exception("Exception when loading K0SWE dxcc.json.")
|
||||
return False
|
||||
|
||||
@@ -5,6 +5,8 @@ import pytz
|
||||
|
||||
from providers.staticdata.static_data_provider import StaticDataProvider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LocalFileStaticDataProvider(StaticDataProvider):
|
||||
"""Generic static reference data provider class for providers that fetch their data from a local file on startup."""
|
||||
@@ -15,19 +17,19 @@ class LocalFileStaticDataProvider(StaticDataProvider):
|
||||
self._stop = False
|
||||
|
||||
def start(self):
|
||||
logging.debug(f"Loading {self.name} static reference data from file.")
|
||||
logger.debug(f"Loading {self.name} static reference data from file.")
|
||||
try:
|
||||
ok = self._load_data(self._path)
|
||||
if ok:
|
||||
self.status = "OK"
|
||||
self.last_update_time = datetime.now(pytz.UTC)
|
||||
logging.info(f"Updated static reference data for {self.name}")
|
||||
logger.info(f"Updated static reference data for {self.name}")
|
||||
else:
|
||||
self.status = "Error"
|
||||
logging.error(f"Failed to load data for {self.name}")
|
||||
logger.error(f"Failed to load data for {self.name}")
|
||||
except Exception:
|
||||
self.status = "Error"
|
||||
logging.exception(f"Exception in local file Static Data Provider ({self.name})")
|
||||
logger.exception(f"Exception in local file Static Data Provider ({self.name})")
|
||||
|
||||
def stop(self):
|
||||
self._stop = True
|
||||
|
||||
Reference in New Issue
Block a user