flynt pass to provide consistency to string formatters and concatenation

This commit is contained in:
Ian Renton
2026-08-15 07:55:32 +01:00
parent bff5b79f8f
commit 7391c28cd0
72 changed files with 184 additions and 193 deletions
+4 -4
View File
@@ -24,7 +24,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("Set up query of " + self.name + " alert API every " + str(self._poll_interval) + " seconds.")
logging.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 +40,7 @@ class HTTPAlertProvider(AlertProvider):
def _poll(self):
try:
# Request data from API
logging.debug("Polling " + self.name + " alert API...")
logging.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,7 +52,7 @@ class HTTPAlertProvider(AlertProvider):
self.status = "OK"
self.last_update_time = datetime.now(pytz.UTC)
logging.debug("Received data from " + self.name + " alert API.")
logging.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.")
@@ -63,7 +63,7 @@ class HTTPAlertProvider(AlertProvider):
logging.warning(f"Timeout when accessing {self.name} alerts API.")
except Exception:
self.status = "Error"
logging.exception("Exception in HTTP JSON Alert Provider (" + self.name + ")")
logging.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)
+4 -4
View File
@@ -49,9 +49,9 @@ class NG3K(HTTPAlertProvider):
end_day = end_string.split(", ")[0].strip()
end_mon = start_mon
start_timestamp = datetime.strptime(start_year + " " + start_mon + " " + start_day, "%Y %b %d").replace(
start_timestamp = datetime.strptime(f"{start_year} {start_mon} {start_day}", "%Y %b %d").replace(
tzinfo=pytz.UTC).timestamp()
end_timestamp = datetime.strptime(end_year + " " + end_mon + " " + end_day + " 23:59",
end_timestamp = datetime.strptime(f"{end_year} {end_mon} {end_day} 23:59",
"%Y %b %d %H:%M").replace(
tzinfo=pytz.UTC).timestamp()
@@ -78,8 +78,8 @@ class NG3K(HTTPAlertProvider):
alert = Alert(source=self.name,
dx_calls=dx_calls,
dx_country=dx_country,
freqs_modes=bands + (("; " + modes) if modes != "" else ""),
comment=by + "; " + comment + "; " + qsl_info,
freqs_modes=bands + (f"; {modes}" if modes != "" else ""),
comment=f"{by}; {comment}; {qsl_info}",
start_time=start_timestamp,
end_time=end_timestamp,
is_dxpedition=True)
+2 -2
View File
@@ -43,7 +43,7 @@ class ParksNPeaks(HTTPAlertProvider):
alert = Alert(source=self.name,
source_id=source_alert["alID"],
dx_calls=[source_alert["CallSign"].upper()],
freqs_modes=source_alert["Freq"] + " " + source_alert["MODE"],
freqs_modes=f"{source_alert['Freq']} {source_alert['MODE']}",
comment=source_alert["Comments"],
sig_refs=sigrefs,
start_time=start_time,
@@ -51,7 +51,7 @@ class ParksNPeaks(HTTPAlertProvider):
# Log a warning for the developer if PnP gives us an unknown programme we've never seen before
if sig and sig not in ["POTA", "SOTA", "WWFF", "SIOTA", "ZLOTA", "KRMNPA", "SANPCPA", "LLOTA", "QRP"]:
logging.warning("PNP alert found with sig " + sig + ", developer needs to add support for this!")
logging.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
+1 -1
View File
@@ -27,7 +27,7 @@ class POTA(HTTPAlertProvider):
freqs_modes=source_alert["frequencies"],
comment=source_alert["comments"],
sig_refs=[SIGRef(id=source_alert["reference"], sig="POTA", name=source_alert["name"],
url="https://pota.app/#/park/" + source_alert["reference"])],
url=f"https://pota.app/#/park/{source_alert['reference']}")],
start_time=datetime.strptime(source_alert["startDate"] + source_alert["startTime"],
"%Y-%m-%d%H:%M").replace(tzinfo=pytz.UTC).timestamp(),
end_time=datetime.strptime(source_alert["endDate"] + source_alert["endTime"],
+1 -1
View File
@@ -33,7 +33,7 @@ class SOTA(HTTPAlertProvider):
freqs_modes=source_alert["frequency"],
comment=source_alert["comments"],
sig_refs=[
SIGRef(id=source_alert["associationCode"] + "/" + source_alert["summitCode"], sig="SOTA",
SIGRef(id=f"{source_alert['associationCode']}/{source_alert['summitCode']}", sig="SOTA",
name=summit_name, activation_score=summit_points)],
start_time=datetime.strptime(source_alert["dateActivated"],
"%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=pytz.UTC).timestamp(),
+1 -1
View File
@@ -24,7 +24,7 @@ class WWFF(HTTPAlertProvider):
alert = Alert(source=self.name,
source_id=source_alert["id"],
dx_calls=[source_alert["activator_call"].upper()],
freqs_modes=source_alert["band"] + " " + source_alert["mode"],
freqs_modes=f"{source_alert['band']} {source_alert['mode']}",
comment=source_alert["remarks"],
sig_refs=[SIGRef(id=source_alert["reference"], sig="WWFF")],
start_time=datetime.strptime(source_alert["utc_start"],
+1 -1
View File
@@ -26,7 +26,7 @@ class ClublogXML(FileDownloadCallsignDataProvider):
logging.warning(
"Clublog XML callsign data provider configured but no api key was provided, this has been disabled.")
super().__init__("Clublog XML", provider_config, self.DATA_URL + "?api=" + self._api_key,
super().__init__("Clublog XML", provider_config, f"{self.DATA_URL}?api={self._api_key}",
self.CACHE_PATH_ZIPPED, self.POLL_INTERVAL_DAYS, DATA_STORE.callsign_data_clublogxml)
def _handle_file(self, path):
@@ -22,7 +22,7 @@ class FileDownloadCallsignDataProvider(CallsignDataProvider):
self._poll_interval = poll_interval
self._thread = None
self._stop_event = Event()
self._url_data_cache = URLDataCache("callsigndata_" + name)
self._url_data_cache = URLDataCache(f"callsigndata_{name}")
if self.enabled:
self.status = "Ready"
@@ -31,7 +31,7 @@ class FileDownloadCallsignDataProvider(CallsignDataProvider):
# 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(
"Set up query of " + self.name + " callsign reference data every " + str(self._poll_interval) + " days.")
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()
@@ -48,7 +48,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("Downloading " + self.name + " callsign reference data...")
logging.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:
@@ -61,7 +61,7 @@ class FileDownloadCallsignDataProvider(CallsignDataProvider):
if ok:
self.status = "OK"
self.last_update_time = datetime.now(pytz.UTC)
logging.info("Updated callsign reference data from " + self.name)
logging.info(f"Updated callsign reference data from {self.name}")
else:
self.status = "Error"
logging.warning(f"Error updating callsign reference data from {self.name}.")
@@ -78,7 +78,7 @@ class FileDownloadCallsignDataProvider(CallsignDataProvider):
logging.warning(f"Timeout when downloading callsign reference data from {self.name}.")
except Exception:
self.status = "Error"
logging.exception("Exception in callsign reference data provider (" + self.name + ")")
logging.exception(f"Exception in callsign reference data provider ({self.name})")
self._stop_event.wait(timeout=1)
def _handle_file(self, path):
+4 -6
View File
@@ -22,11 +22,11 @@ class HamQTH(APIQueryCallsignDataProvider):
def __init__(self, provider_config):
super().__init__("HamQTH", provider_config, DATA_STORE.callsign_data_hamqth)
self._HAMQTH_BASE_URL = "https://www.hamqth.com/xml.php"
self._PRG = ("Spothole v" + SOFTWARE_VERSION + " operated by " + SERVER_OWNER_CALLSIGN).replace(" ", "_")
self._PRG = f"Spothole v{SOFTWARE_VERSION} operated by {SERVER_OWNER_CALLSIGN}".replace(" ", "_")
self._URL_DATA_CACHE = URLDataCache("hamqth")
# Separate URL cache for session key lookups. Once a session key is returned from logging in with a username
# and password, this is valid for an hour, so our cache stores this specifically for 55 minutes.
self._CREDENTIALS_CACHE = CachedSession(CACHE_DIR + "/urls/hamqth-creds",
self._CREDENTIALS_CACHE = CachedSession(f"{CACHE_DIR}/urls/hamqth-creds",
expire_after=timedelta(minutes=55))
def _perform_new_lookup(self, callsign, lookup_credentials):
@@ -44,8 +44,7 @@ class HamQTH(APIQueryCallsignDataProvider):
elif lookup_credentials.hamqth_username and lookup_credentials.hamqth_password:
try:
session_data = self._CREDENTIALS_CACHE.get(
self._HAMQTH_BASE_URL + "?u=" + urllib.parse.quote_plus(lookup_credentials.hamqth_username) +
"&p=" + urllib.parse.quote_plus(lookup_credentials.hamqth_password),
f"{self._HAMQTH_BASE_URL}?u={urllib.parse.quote_plus(lookup_credentials.hamqth_username)}&p={urllib.parse.quote_plus(lookup_credentials.hamqth_password)}",
headers=HTTP_HEADERS).content
dict_data = xmltodict.parse(session_data)
if "session_id" in dict_data["HamQTH"]["session"]:
@@ -74,8 +73,7 @@ class HamQTH(APIQueryCallsignDataProvider):
for lookup_call in calls_to_try:
try:
response = self._URL_DATA_CACHE.get(
self._HAMQTH_BASE_URL + "?id=" + session_id + "&callsign=" + urllib.parse.quote_plus(
lookup_call) + "&prg=" + self._PRG, headers=HTTP_HEADERS, timeout=10)
f"{self._HAMQTH_BASE_URL}?id={session_id}&callsign={urllib.parse.quote_plus(lookup_call)}&prg={self._PRG}", headers=HTTP_HEADERS, timeout=10)
if response.ok:
# Found data, convert it to our object and return it
data = xmltodict.parse(response.content)["HamQTH"]["search"]
+5 -6
View File
@@ -24,7 +24,7 @@ class QRZ(APIQueryCallsignDataProvider):
self._URL_DATA_CACHE = URLDataCache("qrz")
# Separate URL cache for session key lookups. Once a session key is returned from logging in with a username
# and password, this is valid for an hour, so our cache stores this specifically for 55 minutes.
self._CREDENTIALS_CACHE = CachedSession(CACHE_DIR + "/urls/qrz-creds",
self._CREDENTIALS_CACHE = CachedSession(f"{CACHE_DIR}/urls/qrz-creds",
expire_after=timedelta(minutes=55))
def _perform_new_lookup(self, callsign, lookup_credentials):
@@ -42,8 +42,7 @@ class QRZ(APIQueryCallsignDataProvider):
elif lookup_credentials.qrz_username and lookup_credentials.qrz_password:
try:
login_response = self._CREDENTIALS_CACHE.get(
self._QRZ_BASE_URL + "?username=" + urllib.parse.quote_plus(lookup_credentials.qrz_username) +
"&password=" + urllib.parse.quote_plus(lookup_credentials.qrz_password) + "&agent=spothole",
f"{self._QRZ_BASE_URL}?username={urllib.parse.quote_plus(lookup_credentials.qrz_username)}&password={urllib.parse.quote_plus(lookup_credentials.qrz_password)}&agent=spothole",
headers=HTTP_HEADERS).content
login_data = xmltodict.parse(login_response)
session = login_data.get("QRZDatabase", {}).get("Session", {})
@@ -73,7 +72,7 @@ class QRZ(APIQueryCallsignDataProvider):
for lookup_call in calls_to_try:
try:
response = self._URL_DATA_CACHE.get(
self._QRZ_BASE_URL + "?s=" + session_key + "&callsign=" + urllib.parse.quote_plus(lookup_call),
f"{self._QRZ_BASE_URL}?s={session_key}&callsign={urllib.parse.quote_plus(lookup_call)}",
headers=HTTP_HEADERS, timeout=10)
if response.ok:
qrz_response = xmltodict.parse(response.content).get("QRZDatabase", {})
@@ -129,9 +128,9 @@ class QRZ(APIQueryCallsignDataProvider):
if "fname" in data:
name = data["fname"]
if "nick" in data:
name = name + " \"" + data["nick"] + "\""
name = f"{name} \"{data['nick']}\""
if "name" in data:
name = name + " " + data["name"]
name = f"{name} {data['name']}"
# Check for sensible latitudes
lat = None
+1 -1
View File
@@ -22,7 +22,7 @@ class ARLHS(FileDownloadSIGRefDataProvider):
ref_id = row["ARLHS"]
new_data.append(SIGRef(sig=self.SIG, id=ref_id, name=row["Name"] if "Name" in row else None,
ref_type="Lighthouse",
url="https://www.cqgma.org/zinfo.php?ref=" + ref_id,
url=f"https://www.cqgma.org/zinfo.php?ref={ref_id}",
latitude=float(row["Latitude"]) if "Latitude" in row and row[
"Latitude"] != "" else None,
longitude=float(row["Longitude"]) if "Longitude" in row and row[
+1 -1
View File
@@ -28,7 +28,7 @@ class DME(LocalFileSIGRefDataProvider):
ref = SIGRef(sig=self.SIG, id=ref_id,
ref_type="Town",
name=row["NOMBRE_ACTUAL"] + ", " + row["PROVINCIA"],
name=f"{row['NOMBRE_ACTUAL']}, {row['PROVINCIA']}",
latitude=latitude,
longitude=longitude)
if latitude and longitude:
@@ -21,13 +21,13 @@ class FileDownloadSIGRefDataProvider(SIGRefDataProvider):
self._poll_interval = poll_interval
self._thread = None
self._stop_event = Event()
self._url_data_cache = URLDataCache("sigrefdata_" + sig_name)
self._url_data_cache = URLDataCache(f"sigrefdata_{sig_name}")
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(
"Set up query of " + self.sig_name + " SIG ref data every " + str(self._poll_interval) + " days.")
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()
@@ -45,7 +45,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("Downloading " + self.sig_name + " SIG ref data...")
logging.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:
@@ -57,7 +57,7 @@ class FileDownloadSIGRefDataProvider(SIGRefDataProvider):
self.status = "OK"
self.last_update_time = datetime.now(pytz.UTC)
logging.debug("Received SIG ref data for " + self.sig_name)
logging.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}.")
@@ -70,7 +70,7 @@ class FileDownloadSIGRefDataProvider(SIGRefDataProvider):
logging.warning(f"Timeout when downloading SIG ref data for {self.sig_name}.")
except Exception:
self.status = "Error"
logging.exception("Exception in HTTP SIG Ref Data Provider (" + self.sig_name + ")")
logging.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):
+1 -1
View File
@@ -21,7 +21,7 @@ class GMA(FileDownloadSIGRefDataProvider):
ref_id = row["Reference"]
new_data.append(SIGRef(sig=self.SIG, id=ref_id, name=row["Name"] if "Name" in row else None,
ref_type="Summit",
url="https://www.cqgma.org/zinfo.php?ref=" + ref_id,
url=f"https://www.cqgma.org/zinfo.php?ref={ref_id}",
latitude=float(row["Latitude"]) if "Latitude" in row and row[
"Latitude"] != "" else None,
longitude=float(row["Longitude"]) if "Longitude" in row and row[
+1 -1
View File
@@ -22,7 +22,7 @@ class ILLW(FileDownloadSIGRefDataProvider):
ref_id = row["ILLW"]
new_data.append(SIGRef(sig=self.SIG, id=ref_id, name=row["Name"] if "Name" in row else None,
ref_type="Lighthouse",
url="https://www.cqgma.org/zinfo.php?ref=" + ref_id,
url=f"https://www.cqgma.org/zinfo.php?ref={ref_id}",
latitude=float(row["Latitude"]) if "Latitude" in row and row[
"Latitude"] != "" else None,
longitude=float(row["Longitude"]) if "Longitude" in row and row[
+1 -1
View File
@@ -27,7 +27,7 @@ class LLOTA(FileDownloadSIGRefDataProvider):
new_data.append(SIGRef(sig=self.SIG, id=ref_id, name=str(ref["name"]),
ref_type="Lake",
url="https://llota.app/list/ref/" + ref_id,
url=f"https://llota.app/list/ref/{ref_id}",
grid=grid,
latitude=ll[0],
longitude=ll[1]))
@@ -14,7 +14,7 @@ class LocalFileSIGRefDataProvider(SIGRefDataProvider):
self._path = path
def start(self):
logging.debug("Loading " + self.sig_name + " SIG ref data from file.")
logging.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 +23,10 @@ class LocalFileSIGRefDataProvider(SIGRefDataProvider):
self.last_update_time = datetime.now(pytz.UTC)
else:
self.status = "Error"
logging.info("Failed to load SIG ref data for " + self.sig_name)
logging.info(f"Failed to load SIG ref data for {self.sig_name}")
except Exception:
self.status = "Error"
logging.exception("Exception in local file SIG Ref Data Provider (" + self.sig_name + ")")
logging.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."""
+1 -1
View File
@@ -21,7 +21,7 @@ class MOTA(FileDownloadSIGRefDataProvider):
ref_id = row["Reference"]
new_data.append(SIGRef(sig=self.SIG, id=ref_id, name=row["Name"] if "Name" in row else None,
ref_type="Mill",
url="https://www.cqgma.org/zinfo.php?ref=" + ref_id,
url=f"https://www.cqgma.org/zinfo.php?ref={ref_id}",
latitude=float(row["Latitude"]) if "Latitude" in row and row[
"Latitude"] != "" else None,
longitude=float(row["Longitude"]) if "Longitude" in row and row[
@@ -39,7 +39,7 @@ class ParksNPeaksKMLSIGRefDataProvider(FileDownloadSIGRefDataProvider):
ref = SIGRef(sig=self.sig_name, id=ref_id, name=placemark.name,
ref_type="Park",
url="https://parksnpeaks.org/getPark.php?actPark=" + ref_id,
url=f"https://parksnpeaks.org/getPark.php?actPark={ref_id}",
latitude=latitude,
longitude=longitude)
if latitude and longitude:
+1 -1
View File
@@ -21,7 +21,7 @@ class POTA(FileDownloadSIGRefDataProvider):
ref_id = row["reference"]
new_data.append(SIGRef(sig=self.SIG, id=ref_id, name=row["name"] if "name" in row else None,
ref_type="Park",
url="https://pota.app/#/park/" + ref_id,
url=f"https://pota.app/#/park/{ref_id}",
grid=row["grid"] if "grid" in row else None,
latitude=float(row["latitude"]) if "latitude" in row and row[
"latitude"] != "" else None,
@@ -38,7 +38,7 @@ class SIGRefDataProvider:
# with transact() batches all writes together to save making thousands of individual sqlite writes
with DATA_STORE.sigrefs.transact():
for d in new_data:
DATA_STORE.sigrefs.set(self.sig_name + ":" + d.id, d)
DATA_STORE.sigrefs.set(f"{self.sig_name}:{d.id}", d)
# For the big data sources, loading will take a few minutes. If we want to shut down the software neatly
# within the first few minutes of startup, we need a way to abort this expensive process of filling up the
+1 -1
View File
@@ -26,7 +26,7 @@ class SOTA(FileDownloadSIGRefDataProvider):
altitude = float(row["AltM"]) if "AltM" in row and row["AltM"] != "" else None
ref = SIGRef(sig=self.SIG, id=ref_id, name=row["SummitName"] if "SummitName" in row else None,
ref_type="Summit",
url="https://www.sotadata.org.uk/en/summit/" + ref_id,
url=f"https://www.sotadata.org.uk/en/summit/{ref_id}",
latitude=latitude,
longitude=longitude,
altitude=altitude,
+1 -1
View File
@@ -21,7 +21,7 @@ class Towers(FileDownloadSIGRefDataProvider):
ref_id = row["Ref"]
new_data.append(SIGRef(sig=self.SIG, id=ref_id, name=row["Nazev"] if "Nazev" in row else None,
ref_type="Tower",
url="https://wwtota.com/seznam/karta_rozhledny.php?ref=" + ref_id,
url=f"https://wwtota.com/seznam/karta_rozhledny.php?ref={ref_id}",
grid=row["Lokator"] if "Lokator" in row and row["Lokator"] != "" else None,
latitude=float(row["Lat"]) if "Lat" in row and row["Lat"] != "" else None,
longitude=float(row["Lon"]) if "Lon" in row and row["Lon"] != "" else None))
+1 -1
View File
@@ -38,7 +38,7 @@ class WCA(FileDownloadSIGRefDataProvider):
new_data.append(SIGRef(sig=self.SIG, id=ref_id, name=row["CLEAN NAME"] if "CLEAN NAME" in row else None,
ref_type="Castle",
url="https://www.cqgma.org/zinfo.php?ref=" + ref_id,
url=f"https://www.cqgma.org/zinfo.php?ref={ref_id}",
latitude=latitude,
longitude=longitude,
grid=grid))
+2 -2
View File
@@ -20,10 +20,10 @@ class WOTA(FileDownloadSIGRefDataProvider):
ref_id = feature["properties"]["wotaId"]
# Fudge WOTA URLs. Outlying fell (LDO) URLs don't match their ID numbers but require 214 to be
# added to them
url = "https://www.wota.org.uk/MM_" + ref_id
url = f"https://www.wota.org.uk/MM_{ref_id}"
if ref_id.upper().startswith("LDO-"):
number = int(ref_id.upper().replace("LDO-", ""))
url = "https://www.wota.org.uk/MM_LDO-" + str(number + 214)
url = f"https://www.wota.org.uk/MM_LDO-{number + 214!s}"
new_data.append(SIGRef(sig=self.SIG, id=ref_id, name=feature["properties"]["title"], url=url,
ref_type="Summit",
+1 -1
View File
@@ -21,7 +21,7 @@ class WWBOTA(FileDownloadSIGRefDataProvider):
ref_id = row["Reference"]
new_data.append(SIGRef(sig=self.SIG, id=ref_id, name=row["Name"] if "Name" in row else None,
ref_type="Bunker",
url="https://bunkerwiki.org/?s=" + ref_id if ref_id.startswith("B/G") else None,
url=f"https://bunkerwiki.org/?s={ref_id}" if ref_id.startswith("B/G") else None,
grid=row["Locator"] if "Locator" in row and row["Locator"] != "" else None,
latitude=float(row["Lat"]) if "Lat" in row and row["Lat"] != "" else None,
longitude=float(row["Long"]) if "Long" in row and row["Long"] != "" else None))
+1 -1
View File
@@ -21,7 +21,7 @@ class WWFF(FileDownloadSIGRefDataProvider):
ref_id = row["reference"]
new_data.append(SIGRef(sig=self.SIG, id=ref_id, name=row["name"] if "name" in row else None,
ref_type="Park",
url="https://wwff.co/directory/?showRef=" + ref_id,
url=f"https://wwff.co/directory/?showRef={ref_id}",
grid=row["iaruLocator"] if "iaruLocator" in row and row[
"iaruLocator"] != "-" else None,
latitude=float(row["latitude"]) if "latitude" in row and row[
+1 -1
View File
@@ -27,7 +27,7 @@ class ZLOTA(FileDownloadSIGRefDataProvider):
new_ref = SIGRef(sig=self.SIG, id=ref_id, name=ref["name"],
ref_type=ref["asset_type"].title(),
url="https://ontheair.nz/assets/" + ref_id.replace("/", "_"),
url=f"https://ontheair.nz/assets/{ref_id.replace('/', '_')}",
latitude=latitude,
longitude=longitude)
+2 -2
View File
@@ -72,11 +72,11 @@ class HamQSL(HTTPSolarConditionsProvider):
tz_abbr = updated_str.split()[-1]
timezone = dateutil_tz.gettz(tz_abbr)
if timezone is None:
raise ValueError("Unknown timezone abbreviation: " + tz_abbr)
raise ValueError(f"Unknown timezone abbreviation: {tz_abbr}")
dt = dateutil_parser.parse(updated_str, tzinfos={tz_abbr: timezone})
updated = dt.astimezone(pytz.UTC).timestamp()
except (ValueError, IndexError):
logging.warning("HamQSL solar conditions API returned unrecognised timestamp format: " + updated_str)
logging.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 {
@@ -23,7 +23,7 @@ class HTTPSolarConditionsProvider(SolarConditionsProvider):
def start(self):
logging.info(
"Set up query of " + self.name + " solar conditions API every " + str(self._poll_interval) + " seconds.")
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()
@@ -38,7 +38,7 @@ class HTTPSolarConditionsProvider(SolarConditionsProvider):
def _poll(self):
try:
logging.debug("Polling " + self.name + " solar conditions API...")
logging.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:
@@ -47,7 +47,7 @@ class HTTPSolarConditionsProvider(SolarConditionsProvider):
self.status = "OK"
self.last_update_time = datetime.now(pytz.UTC)
logging.debug("Received data from " + self.name + " solar conditions API.")
logging.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.")
@@ -58,7 +58,7 @@ class HTTPSolarConditionsProvider(SolarConditionsProvider):
logging.warning(f"Timeout when accessing {self.name} solar conditions API.")
except Exception:
self.status = "Error"
logging.exception("Exception in HTTP Solar Conditions Provider (" + self.name + ")")
logging.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):
@@ -96,7 +96,7 @@ class NOAA3dayForecast(HTTPSolarConditionsProvider):
header_line = lines[start_idx]
year_match = re.search(r'\b(\d{4})\b', header_line)
if not year_match:
logging.warning("NOAA K-index forecast: could not extract year from: " + header_line)
logging.warning(f"NOAA K-index forecast: could not extract year from: {header_line}")
return None
year = int(year_match.group(1))
@@ -108,7 +108,7 @@ class NOAA3dayForecast(HTTPSolarConditionsProvider):
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("NOAA K-index forecast: could not parse date headers from: " + date_header_line)
logging.warning(f"NOAA K-index forecast: could not parse date headers from: {date_header_line}")
return None
column_dates = []
+10 -10
View File
@@ -54,19 +54,19 @@ class DXCluster(SpotProvider):
while not connected and self._running:
try:
self.status = "Connecting"
logging.info("DX Cluster " + self._hostname + " connecting...")
logging.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((self._login_callsign + "\n").encode("latin-1"))
self._telnet.write(f"{self._login_callsign}\n".encode("latin-1"))
connected = True
logging.info("DX Cluster " + self._hostname + " connected.")
logging.info(f"DX Cluster {self._hostname} connected.")
except ConnectionRefusedError:
self.status = "Error"
logging.warning("Connection refused to DX cluster " + self._hostname)
logging.warning(f"Connection refused to DX cluster {self._hostname}")
sleep(300)
except Exception:
self.status = "Error"
logging.exception("Exception while connecting to DX Cluster Provider (" + self._hostname + ").")
logging.exception(f"Exception while connecting to DX Cluster Provider ({self._hostname}).")
sleep(5)
self.status = "Waiting for Data"
@@ -91,25 +91,25 @@ class DXCluster(SpotProvider):
self.status = "OK"
self.last_update_time = datetime.now(pytz.UTC)
logging.debug("Data received from DX Cluster " + self._hostname + ".")
logging.debug(f"Data received from DX Cluster {self._hostname}.")
except EOFError:
connected = False
if self._running:
self.status = "Restarting"
logging.warning("Disconnected from DX Cluster " + self._hostname + ". Reconnecting...")
logging.warning(f"Disconnected from DX Cluster {self._hostname}. Reconnecting...")
sleep(5)
else:
logging.info("DX Cluster " + self._hostname + " shutting down...")
logging.info(f"DX Cluster {self._hostname} shutting down...")
self.status = "Shutting down"
except Exception:
connected = False
if self._running:
self.status = "Error"
logging.exception("Exception in DX Cluster Provider (" + self._hostname + ")")
logging.exception(f"Exception in DX Cluster Provider ({self._hostname})")
sleep(5)
else:
logging.info("DX Cluster " + self._hostname + " shutting down...")
logging.info(f"DX Cluster {self._hostname} shutting down...")
self.status = "Shutting down"
self.status = "Disconnected"
+3 -5
View File
@@ -27,7 +27,7 @@ class GMA(HTTPSpotProvider):
logging.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__("GMA", provider_config, self.SPOTS_URL + "?key=" + self._api_key, self.POLL_INTERVAL_SEC)
super().__init__("GMA", provider_config, f"{self.SPOTS_URL}?key={self._api_key}", self.POLL_INTERVAL_SEC)
def _http_response_to_spots(self, http_response):
new_spots = []
@@ -98,8 +98,7 @@ class GMA(HTTPSpotProvider):
spot.sig_refs[0].sig = "MOTA"
spot.sig = "MOTA"
case _:
logging.warning("GMA spot found with ref type " + ref_info[
"reftype"] + ", developer needs to add support for this!")
logging.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"]
spot.sig = ref_info["reftype"]
@@ -115,8 +114,7 @@ class GMA(HTTPSpotProvider):
logging.warning(
f"GMA API returned a malformed response when looking up ref {source_spot['REF']}")
except:
logging.exception("Exception when looking up " + self.REF_INFO_URL_ROOT + source_spot[
"REF"] + ", ignoring this spot for now")
logging.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}).")
+4 -4
View File
@@ -26,7 +26,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("Set up query of " + self.name + " spot API every " + str(self._poll_interval) + " seconds.")
logging.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 +50,7 @@ class HTTPSpotProvider(SpotProvider):
def _poll(self):
try:
# Request data from API
logging.debug("Polling " + self.name + " spot API...")
logging.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,7 +62,7 @@ class HTTPSpotProvider(SpotProvider):
self.status = "OK"
self.last_update_time = datetime.now(pytz.UTC)
logging.debug("Received data from " + self.name + " spot API.")
logging.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.")
@@ -73,7 +73,7 @@ class HTTPSpotProvider(SpotProvider):
logging.warning(f"Timeout when accessing {self.name} spots API.")
except Exception:
self.status = "Error"
logging.exception("Exception in HTTP Spot Provider (" + self.name + ")")
logging.exception(f"Exception in HTTP Spot Provider ({self.name})")
self._stop_event.wait(timeout=1)
def _http_response_to_spots(self, http_response):
+2 -2
View File
@@ -62,7 +62,7 @@ class ParksNPeaks(HTTPSpotProvider):
# Log a warning for the developer if PnP gives us an unknown programme we've never seen before
if sig not in ["POTA", "SOTA", "WWFF", "SIOTA", "ZLOTA", "KRMNPA", "SANPCPA", "LLOTA"]:
logging.warning("PNP spot found with sig " + sig + ", developer needs to add support for this!")
logging.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)
@@ -91,4 +91,4 @@ class ParksNPeaks(HTTPSpotProvider):
}
response = requests.post(self.SUBMIT_URL, json=body, headers=HTTP_HEADERS, timeout=(5, 30))
if not response.ok:
raise RuntimeError("Parks N Peaks API returned " + str(response.status_code) + ": " + response.text)
raise RuntimeError(f"Parks N Peaks API returned {response.status_code!s}: {response.text}")
+1 -1
View File
@@ -63,6 +63,6 @@ class POTA(HTTPSpotProvider):
headers = {**HTTP_HEADERS, "Content-Type": "application/json"}
response = requests.post(self.SUBMIT_URL, json=body, headers=headers, timeout=(5, 30))
if not response.ok:
raise RuntimeError("POTA API returned " + str(response.status_code) + ": " + response.text)
raise RuntimeError(f"POTA API returned {response.status_code!s}: {response.text}")
else:
raise RuntimeError("Park reference is required for submitting POTA spots.")
+9 -9
View File
@@ -45,15 +45,15 @@ class RBN(SpotProvider):
while not connected and self._running:
try:
self.status = "Connecting"
logging.info("RBN port " + str(self._port) + " connecting...")
logging.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((SERVER_OWNER_CALLSIGN + "\n").encode("latin-1"))
self._telnet.write(f"{SERVER_OWNER_CALLSIGN}\n".encode("latin-1"))
connected = True
logging.info("RBN port " + str(self._port) + " connected.")
logging.info(f"RBN port {self._port!s} connected.")
except Exception:
self.status = "Error"
logging.exception("Exception while connecting to RBN (port " + str(self._port) + ").")
logging.exception(f"Exception while connecting to RBN (port {self._port!s}).")
sleep(5)
self.status = "Waiting for Data"
@@ -78,25 +78,25 @@ class RBN(SpotProvider):
self.status = "OK"
self.last_update_time = datetime.now(pytz.UTC)
logging.debug("Data received from RBN on port " + str(self._port) + ".")
logging.debug(f"Data received from RBN on port {self._port!s}.")
except EOFError:
connected = False
if self._running:
self.status = "Restarting"
logging.warning("Disconnected from RBN provider (port " + str(self._port) + "). Reconnecting...")
logging.warning(f"Disconnected from RBN provider (port {self._port!s}). Reconnecting...")
sleep(5)
else:
logging.info("RBN provider (port " + str(self._port) + ") shutting down...")
logging.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("Exception in RBN provider (port " + str(self._port) + ")")
logging.exception(f"Exception in RBN provider (port {self._port!s})")
sleep(5)
else:
logging.info("RBN provider (port " + str(self._port) + ") shutting down...")
logging.info(f"RBN provider (port {self._port!s}) shutting down...")
self.status = "Shutting down"
self.status = "Disconnected"
+2 -2
View File
@@ -104,10 +104,10 @@ class SOTA(HTTPSpotProvider):
"comments": spot.comment or "",
"type": "TEST" # todo replatce with NORMAL/QRT once testing complete
}
headers = {**HTTP_HEADERS, "Authorization": "bearer " + access_token, "id_token": id_token,
headers = {**HTTP_HEADERS, "Authorization": f"bearer {access_token}", "id_token": id_token,
"Content-Type": "application/json"}
response = requests.post(self.SUBMIT_URL, json=body, headers=headers, timeout=(5, 30))
if not response.ok:
raise RuntimeError("SOTA API returned " + str(response.status_code) + ": " + response.text)
raise RuntimeError(f"SOTA API returned {response.status_code!s}: {response.text}")
else:
raise RuntimeError("Summit reference is required for submitting SOTA spots.")
+7 -7
View File
@@ -22,7 +22,7 @@ class SSESpotProvider(SpotProvider):
self._event_source = None
def start(self):
logging.info("Set up SSE connection to " + self.name + " spot API.")
logging.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
@@ -38,12 +38,12 @@ class SSESpotProvider(SpotProvider):
event_source.close()
except Exception:
logging.exception(
"Exception closing SSE connection for " + self.name + " during stop()")
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(self.name + " SSE worker thread did not exit on time and will be killed.")
logging.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"
@@ -58,7 +58,7 @@ class SSESpotProvider(SpotProvider):
def _run(self):
while not self._stop_event.is_set():
try:
logging.debug("Connecting to " + self.name + " spot API...")
logging.debug(f"Connecting to {self.name} spot API...")
self.status = "Connecting"
with EventSource(self._url, headers=HTTP_HEADERS, latest_event_id=self._last_event_id, timeout=10,
on_open=self._on_open, on_error=self._on_error) as event_source:
@@ -76,17 +76,17 @@ class SSESpotProvider(SpotProvider):
self.status = "OK"
self.last_update_time = datetime.now(pytz.UTC)
logging.debug("Received data from " + self.name + " spot API.")
logging.debug(f"Received data from {self.name} spot API.")
except Exception:
logging.exception(
"Exception processing message from SSE Spot Provider (" + self.name + ")")
f"Exception processing message from SSE Spot Provider ({self.name})")
finally:
self._set_event_source(None)
except Exception:
self.status = "Error"
logging.exception("Exception in SSE Spot Provider (" + self.name + ")")
logging.exception(f"Exception in SSE Spot Provider ({self.name})")
else:
self.status = "Disconnected"
self._stop_event.wait(timeout=5) # Wait before trying to reconnect
+2 -2
View File
@@ -86,7 +86,7 @@ class Tiles(HTTPSpotProvider):
response = requests.post(self.SUBMIT_URL, json=body, headers=headers, timeout=(5, 30))
if not response.ok:
raise RuntimeError(
"Tiles on the Air API returned " + str(response.status_code) + ": " + response.text)
f"Tiles on the Air API returned {response.status_code!s}: {response.text}")
else:
raise RuntimeError("The Tiles on the Air API requires a mode to be set.")
else:
@@ -100,4 +100,4 @@ def strip_extra_decimal_points(s):
parts = s.split('.', 1)
if len(parts) == 1:
return s
return parts[0] + '.' + parts[1].replace('.', '')
return f"{parts[0]}.{parts[1].replace('.', '')}"
+3 -5
View File
@@ -35,11 +35,9 @@ class UKPacketNet(HTTPSpotProvider):
# First build a "full" comment combining some of the extra info
comment = listed_port["comment"] if "comment" in listed_port else ""
comment = (comment + " " + listed_port["mode"]) if "mode" in listed_port else comment
comment = (comment + " " + listed_port[
"modulation"]) if "modulation" in listed_port else comment
comment = (comment + " " + str(
listed_port["baud"]) + " baud") if "baud" in listed_port and listed_port[
comment = f"{comment} {listed_port['mode']}" if "mode" in listed_port else comment
comment = f"{comment} {listed_port['modulation']}" if "modulation" in listed_port else comment
comment = f"{comment} {listed_port['baud']!s} baud" if "baud" in listed_port and listed_port[
"baud"] > 0 else comment
# Get frequency from the comment if it's not set properly in the data structure. This is
+5 -5
View File
@@ -22,7 +22,7 @@ class WebsocketSpotProvider(SpotProvider):
self._last_event_id = None
def start(self):
logging.info("Set up websocket connection to " + self.name + " spot API.")
logging.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 +44,7 @@ class WebsocketSpotProvider(SpotProvider):
def _run(self):
while not self._stopped:
try:
logging.debug("Connecting to " + self.name + " spot API...")
logging.debug(f"Connecting to {self.name} spot API...")
self.status = "Connecting"
self._ws = create_connection(self._url, header=HTTP_HEADERS)
self.status = "Connected"
@@ -57,15 +57,15 @@ class WebsocketSpotProvider(SpotProvider):
self.status = "OK"
self.last_update_time = datetime.now(pytz.UTC)
logging.debug("Received data from " + self.name + " spot API.")
logging.debug(f"Received data from {self.name} spot API.")
except Exception:
logging.exception(
"Exception processing message from Websocket Spot Provider (" + self.name + ")")
f"Exception processing message from Websocket Spot Provider ({self.name})")
except Exception as e:
self.status = "Error"
logging.exception("Exception in Websocket Spot Provider (" + self.name + ")", e)
logging.exception(f"Exception in Websocket Spot Provider ({self.name})", e)
else:
self.status = "Disconnected"
sleep(5) # Wait before trying to reconnect
+1 -1
View File
@@ -28,7 +28,7 @@ class XOTA(WebsocketSpotProvider):
def _ws_message_to_spot(self, b):
string = b.decode("utf-8")
source_spot = json.loads(string)
ref_id = self._sig_ref_prefix + " " + source_spot["reference"]["title"]
ref_id = f"{self._sig_ref_prefix} {source_spot['reference']['title']}"
spot = Spot(source=self.name,
source_id=source_spot["id"],
dx_call=source_spot["stationCallSign"].upper(),
@@ -22,13 +22,13 @@ class FileDownloadStaticDataProvider(StaticDataProvider):
self._poll_interval = poll_interval
self._thread = None
self._stop_event = Event()
self._url_data_cache = URLDataCache("staticdata_" + name)
self._url_data_cache = URLDataCache(f"staticdata_{name}")
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(
"Set up query of " + self.name + " static reference data every " + str(self._poll_interval) + " days.")
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()
@@ -45,7 +45,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("Downloading " + self.name + " static reference data...")
logging.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:
@@ -54,7 +54,7 @@ class FileDownloadStaticDataProvider(StaticDataProvider):
if ok:
self.status = "OK"
self.last_update_time = datetime.now(pytz.UTC)
logging.info("Updated static reference data for " + self.name)
logging.info(f"Updated static reference data for {self.name}")
else:
self.status = "Error"
logging.warning(f"HTTP {http_response.status_code} when downloading static reference data for {self.name}.")
@@ -67,7 +67,7 @@ class FileDownloadStaticDataProvider(StaticDataProvider):
logging.warning(f"Timeout when downloading static reference data for {self.name}.")
except Exception:
self.status = "Error"
logging.exception("Exception in HTTP static reference data provider (" + self.name + ")")
logging.exception(f"Exception in HTTP static reference data provider ({self.name})")
self._stop_event.wait(timeout=1)
def _handle_http_response(self, http_response):
@@ -15,19 +15,19 @@ class LocalFileStaticDataProvider(StaticDataProvider):
self._stop = False
def start(self):
logging.debug("Loading " + self.name + " static reference data from file.")
logging.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("Updated static reference data for " + self.name)
logging.info(f"Updated static reference data for {self.name}")
else:
self.status = "Error"
logging.error("Failed to load data for " + self.name)
logging.error(f"Failed to load data for {self.name}")
except Exception:
self.status = "Error"
logging.exception("Exception in local file Static Data Provider (" + self.name + ")")
logging.exception(f"Exception in local file Static Data Provider ({self.name})")
def stop(self):
self._stop = True