From 7391c28cd0f8aaf3e7be5dd90c1259345b2ef20d Mon Sep 17 00:00:00 2001 From: Ian Renton Date: Sat, 15 Aug 2026 07:55:32 +0100 Subject: [PATCH] flynt pass to provide consistency to string formatters and concatenation --- core/config.py | 2 +- core/constants.py | 4 +-- core/data_store.py | 22 ++++++++-------- core/geo_utils.py | 2 +- core/sig_lookup_helper.py | 6 ++--- core/sig_utils.py | 2 +- core/url_data_cache.py | 2 +- core/utils.py | 2 +- data/spot.py | 2 +- providers/alert/http_alert_provider.py | 8 +++--- providers/alert/ng3k.py | 8 +++--- providers/alert/parksnpeaks.py | 4 +-- providers/alert/pota.py | 2 +- providers/alert/sota.py | 2 +- providers/alert/wwff.py | 2 +- providers/callsigndata/clublogxml.py | 2 +- .../file_download_callsign_data_provider.py | 10 +++---- providers/callsigndata/hamqth.py | 10 +++---- providers/callsigndata/qrz.py | 11 ++++---- providers/sigrefdata/arlhs.py | 2 +- providers/sigrefdata/dme.py | 2 +- .../file_download_sig_ref_data_provider.py | 10 +++---- providers/sigrefdata/gma.py | 2 +- providers/sigrefdata/illw.py | 2 +- providers/sigrefdata/llota.py | 2 +- .../local_file_sig_ref_data_provider.py | 6 ++--- providers/sigrefdata/mota.py | 2 +- .../pnp_kml_sig_ref_data_provider.py | 2 +- providers/sigrefdata/pota.py | 2 +- providers/sigrefdata/sig_ref_data_provider.py | 2 +- providers/sigrefdata/sota.py | 2 +- providers/sigrefdata/towers.py | 2 +- providers/sigrefdata/wca.py | 2 +- providers/sigrefdata/wota.py | 4 +-- providers/sigrefdata/wwbota.py | 2 +- providers/sigrefdata/wwff.py | 2 +- providers/sigrefdata/zlota.py | 2 +- providers/solarconditions/hamqsl.py | 4 +-- .../http_solar_conditions_provider.py | 8 +++--- providers/solarconditions/noaa3dayforecast.py | 4 +-- providers/spot/dxcluster.py | 20 +++++++------- providers/spot/gma.py | 8 +++--- providers/spot/http_spot_provider.py | 8 +++--- providers/spot/parksnpeaks.py | 4 +-- providers/spot/pota.py | 2 +- providers/spot/rbn.py | 18 ++++++------- providers/spot/sota.py | 4 +-- providers/spot/sse_spot_provider.py | 14 +++++----- providers/spot/tiles.py | 4 +-- providers/spot/ukpacketnet.py | 8 +++--- providers/spot/websocket_spot_provider.py | 10 +++---- providers/spot/xota.py | 2 +- .../file_download_static_data_provider.py | 10 +++---- .../local_file_static_data_provider.py | 8 +++--- server/handlers/api/addspot.py | 26 +++++++++---------- server/handlers/api/alerts.py | 2 +- server/handlers/api/lookups.py | 6 ++--- server/handlers/api/spots.py | 2 +- server/handlers/api/v1_addspot.py | 10 +++---- server/handlers/api/v1_compatability.py | 2 +- server/handlers/pagetemplate.py | 2 +- server/webserver.py | 4 +-- spothole.py | 2 +- static/img/flags/generate.py | 2 +- templates/add_spot.html | 2 +- templates/alerts.html | 2 +- templates/bands.html | 4 +-- templates/base.html | 10 +++---- templates/conditions.html | 2 +- templates/map.html | 4 +-- templates/spots.html | 4 +-- templates/status.html | 2 +- 72 files changed, 184 insertions(+), 193 deletions(-) diff --git a/core/config.py b/core/config.py index 54a1758..d1f6984 100644 --- a/core/config.py +++ b/core/config.py @@ -40,6 +40,6 @@ def create_provider_from_config(package, config_providers_entry): package to look for it in, as there are several types of provider. e.g. package "providers.spot", where the config entry is for a POTA spot provider.""" - module = importlib.import_module(package + "." + config_providers_entry["class"].lower()) + module = importlib.import_module(f"{package}.{config_providers_entry['class'].lower()}") provider_class = getattr(module, config_providers_entry["class"]) return provider_class(config_providers_entry) diff --git a/core/constants.py b/core/constants.py index a4c12a0..bf341f9 100644 --- a/core/constants.py +++ b/core/constants.py @@ -6,8 +6,8 @@ from data.sig import SIG SOFTWARE_VERSION = "2.0-pre" # HTTP headers used for spot providers that use HTTP -HTTP_HEADERS = {"User-Agent": "Spothole v" + SOFTWARE_VERSION + " (operated by " + SERVER_OWNER_CALLSIGN + ")"} -HAMQTH_PRG = ("Spothole v" + SOFTWARE_VERSION + " operated by " + SERVER_OWNER_CALLSIGN).replace(" ", "_") +HTTP_HEADERS = {"User-Agent": f"Spothole v{SOFTWARE_VERSION} (operated by {SERVER_OWNER_CALLSIGN})"} +HAMQTH_PRG = f"Spothole v{SOFTWARE_VERSION} operated by {SERVER_OWNER_CALLSIGN}".replace(" ", "_") # Special Interest Groups SIGS = [ diff --git a/core/data_store.py b/core/data_store.py index 97ae973..03dc6bc 100644 --- a/core/data_store.py +++ b/core/data_store.py @@ -46,34 +46,34 @@ class DataStore: # Standard disk cache for solar data and status data, but each cache contains only a single object which we # expose to the wider application - self._solar = diskcache.Cache(CACHE_DIR + "solar") + self._solar = diskcache.Cache(f"{CACHE_DIR}solar") if "solar_conditions" not in self._solar: self._solar.add("solar_conditions", SolarConditions()) self.solar_conditions = self._solar.get("solar_conditions") - self._status = diskcache.Cache(CACHE_DIR + "status") + self._status = diskcache.Cache(f"{CACHE_DIR}status") if "status_data" not in self._status: self._status.add("status_data", {}) self.status_data = self._status.get("status_data") # Standard disk cache for static reference and SIG ref data. Separate provider threads will repopulate these on # a regular basis but there's no need for a TTL since old data is better than no data. - self.dxcc_data = diskcache.Cache(CACHE_DIR + "dxcc_data") + self.dxcc_data = diskcache.Cache(f"{CACHE_DIR}dxcc_data") self.regenerate_call_regex_to_dxcc_entity_map() # For SIG reference data specifically, we need to key on both SIG *and* reference, and trying to do two layers # of dict in diskcache absolutely destroys performance with unpickling huge dicts, so we have an ugly "SIG:ref" # syntax for keys to keep it a single level. - self.sigrefs = diskcache.Cache(CACHE_DIR + "sigrefs") + self.sigrefs = diskcache.Cache(f"{CACHE_DIR}sigrefs") logging.info(f"Loaded data for %d SIG references.", len(self.sigrefs)) # Standard disk cache for callsign data. This data does have a TTL to trigger an occasional re-lookup. # Old data *is* better than no data, but we can't have a background thread re-looking-up every callsign # we've seen, so we rely on them timing out and this triggering another lookup. - self.callsign_data_countryfiles = diskcache.Cache(CACHE_DIR + "callsign_data_countryfiles") - self.callsign_data_clublogxml = diskcache.Cache(CACHE_DIR + "callsign_data_clublogxml") - self.callsign_data_clublogapi = diskcache.Cache(CACHE_DIR + "callsign_data_clublogapi") - self.callsign_data_qrz = diskcache.Cache(CACHE_DIR + "callsign_data_qrz") - self.callsign_data_hamqth = diskcache.Cache(CACHE_DIR + "callsign_data_hamqth") + self.callsign_data_countryfiles = diskcache.Cache(f"{CACHE_DIR}callsign_data_countryfiles") + self.callsign_data_clublogxml = diskcache.Cache(f"{CACHE_DIR}callsign_data_clublogxml") + self.callsign_data_clublogapi = diskcache.Cache(f"{CACHE_DIR}callsign_data_clublogapi") + self.callsign_data_qrz = diskcache.Cache(f"{CACHE_DIR}callsign_data_qrz") + self.callsign_data_hamqth = diskcache.Cache(f"{CACHE_DIR}callsign_data_hamqth") unique_keys = set() for c in [self.callsign_data_countryfiles, self.callsign_data_clublogxml, self.callsign_data_clublogapi, self.callsign_data_qrz, self.callsign_data_hamqth]: @@ -84,12 +84,12 @@ class DataStore: # specifically load these caches *last* so that any sigref and callsign data is already loaded from disk cache # before the spots and alerts are live in the system. self.spots = LiveDataCache(maxsize=self._MAX_SPOT_COUNT, ttl=MAX_SPOT_AGE, - snapshot_dir=CACHE_DIR + "spots", + snapshot_dir=f"{CACHE_DIR}spots", snapshot_interval_sec=self._SPOT_ALERT_SNAPSHOT_INTERVAL_SEC) logging.info(f"Loaded %d spots from a previous run.", len(self.spots.keys())) self.alerts = LiveDataCache(maxsize=self._MAX_ALERT_COUNT, ttl=MAX_ALERT_AGE, - snapshot_dir=CACHE_DIR + "alerts", + snapshot_dir=f"{CACHE_DIR}alerts", snapshot_interval_sec=self._SPOT_ALERT_SNAPSHOT_INTERVAL_SEC) logging.info(f"Loaded %d alerts from a previous run.", len(self.alerts.keys())) diff --git a/core/geo_utils.py b/core/geo_utils.py index 05d1050..ff671a7 100644 --- a/core/geo_utils.py +++ b/core/geo_utils.py @@ -172,7 +172,7 @@ def wab_wai_square_to_lat_lon(ref): elif re.match(r"^W[AV][0-9]{2}$", ref): return utm_grid_square_to_lat_lon(ref) else: - logging.warning("Invalid WAB/WAI square: " + ref) + logging.warning(f"Invalid WAB/WAI square: {ref}") return None diff --git a/core/sig_lookup_helper.py b/core/sig_lookup_helper.py index 1d96cac..6cb0f96 100644 --- a/core/sig_lookup_helper.py +++ b/core/sig_lookup_helper.py @@ -68,14 +68,14 @@ def get_sig_ref_info(sig, ref_id): if not sig_ref.name: sig_ref.name = sig_ref.id if sig_ref.name: - sig_ref.url = "https://www.beachesontheair.com/beaches/" + sig_ref.name.lower().replace(" ", "-") + sig_ref.url = f"https://www.beachesontheair.com/beaches/{sig_ref.name.lower().replace(' ', '-')}" return sig_ref ### ACTUAL LOOKUP ### # # OK, this is something we have to look up. Now check to see if our data store contains reference data and use # that. - key = sig + ":" + ref_id + key = f"{sig}:{ref_id}" lookup_data = DATA_STORE.sigrefs.get(key) if key in DATA_STORE.sigrefs else None if lookup_data: return lookup_data @@ -86,7 +86,7 @@ def get_sig_ref_info(sig, ref_id): logging.debug("%s database did not contain data for ref %s", sig, ref_id) except Exception: - logging.exception("Exception when looking up sig_ref info for " + sig + " ref " + ref_id) + logging.exception(f"Exception when looking up sig_ref info for {sig} ref {ref_id}") return sig_ref diff --git a/core/sig_utils.py b/core/sig_utils.py index 100edc1..f7b5f40 100644 --- a/core/sig_utils.py +++ b/core/sig_utils.py @@ -21,4 +21,4 @@ def get_sig_name_from_comment_name(sig): # Regex matching any SIG's "comment name", i.e. how it may be referred to in spot comments -ANY_SIG_REGEX = r"(" + r"|".join(n for s in SIGS for n in s.comment_names) + r")" +ANY_SIG_REGEX = rf"({'|'.join((n for s in SIGS for n in s.comment_names))})" diff --git a/core/url_data_cache.py b/core/url_data_cache.py index d93dbcb..9cfa8c8 100644 --- a/core/url_data_cache.py +++ b/core/url_data_cache.py @@ -17,7 +17,7 @@ class URLDataCache(CachedSession): _lock = threading.Lock() def __init__(self, name): - super().__init__(CACHE_DIR + "urls/" + name, expire_after=timedelta(days=1), + super().__init__(f"{CACHE_DIR}urls/{name}", expire_after=timedelta(days=1), allowable_codes=(200, 400, 401, 403, 404)) def get(self, *args, **kwargs): diff --git a/core/utils.py b/core/utils.py index bd6db9c..c70565f 100644 --- a/core/utils.py +++ b/core/utils.py @@ -39,7 +39,7 @@ def infer_mode_type_from_mode(mode): return "DATA" else: if mode.upper() != "OTHER": - logging.warning("Found an unrecognised mode: " + mode + ". Developer should categorise this.") + logging.warning(f"Found an unrecognised mode: {mode}. Developer should categorise this.") return None diff --git a/data/spot.py b/data/spot.py index 322d0fc..a0d1c66 100644 --- a/data/spot.py +++ b/data/spot.py @@ -388,7 +388,7 @@ class Spot: if self.sig_refs and len(self.sig_refs) > 0: qth = self.sig_refs[0].id if self.sig_refs[0].name: - qth += " " + self.sig_refs[0].name + qth += f" {self.sig_refs[0].name}" self.dx_qth = qth else: self.dx_qth = dx_call_info.qth diff --git a/providers/alert/http_alert_provider.py b/providers/alert/http_alert_provider.py index 1994a60..0b8c237 100644 --- a/providers/alert/http_alert_provider.py +++ b/providers/alert/http_alert_provider.py @@ -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) diff --git a/providers/alert/ng3k.py b/providers/alert/ng3k.py index dc08422..672234f 100644 --- a/providers/alert/ng3k.py +++ b/providers/alert/ng3k.py @@ -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) diff --git a/providers/alert/parksnpeaks.py b/providers/alert/parksnpeaks.py index dea7b04..0489772 100644 --- a/providers/alert/parksnpeaks.py +++ b/providers/alert/parksnpeaks.py @@ -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 diff --git a/providers/alert/pota.py b/providers/alert/pota.py index 243bcfe..aefa9a9 100644 --- a/providers/alert/pota.py +++ b/providers/alert/pota.py @@ -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"], diff --git a/providers/alert/sota.py b/providers/alert/sota.py index 25b97b6..6254499 100644 --- a/providers/alert/sota.py +++ b/providers/alert/sota.py @@ -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(), diff --git a/providers/alert/wwff.py b/providers/alert/wwff.py index 92207bf..a461d06 100644 --- a/providers/alert/wwff.py +++ b/providers/alert/wwff.py @@ -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"], diff --git a/providers/callsigndata/clublogxml.py b/providers/callsigndata/clublogxml.py index 41506b3..9393b04 100644 --- a/providers/callsigndata/clublogxml.py +++ b/providers/callsigndata/clublogxml.py @@ -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): diff --git a/providers/callsigndata/file_download_callsign_data_provider.py b/providers/callsigndata/file_download_callsign_data_provider.py index 4145147..999a4bb 100644 --- a/providers/callsigndata/file_download_callsign_data_provider.py +++ b/providers/callsigndata/file_download_callsign_data_provider.py @@ -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): diff --git a/providers/callsigndata/hamqth.py b/providers/callsigndata/hamqth.py index 36065fd..8612936 100644 --- a/providers/callsigndata/hamqth.py +++ b/providers/callsigndata/hamqth.py @@ -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"] diff --git a/providers/callsigndata/qrz.py b/providers/callsigndata/qrz.py index 0c46b80..dbcd5b1 100644 --- a/providers/callsigndata/qrz.py +++ b/providers/callsigndata/qrz.py @@ -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 diff --git a/providers/sigrefdata/arlhs.py b/providers/sigrefdata/arlhs.py index f61e8ec..0661820 100644 --- a/providers/sigrefdata/arlhs.py +++ b/providers/sigrefdata/arlhs.py @@ -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[ diff --git a/providers/sigrefdata/dme.py b/providers/sigrefdata/dme.py index 3756167..db61c4d 100644 --- a/providers/sigrefdata/dme.py +++ b/providers/sigrefdata/dme.py @@ -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: diff --git a/providers/sigrefdata/file_download_sig_ref_data_provider.py b/providers/sigrefdata/file_download_sig_ref_data_provider.py index 55653d9..cc7188d 100644 --- a/providers/sigrefdata/file_download_sig_ref_data_provider.py +++ b/providers/sigrefdata/file_download_sig_ref_data_provider.py @@ -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): diff --git a/providers/sigrefdata/gma.py b/providers/sigrefdata/gma.py index 2d817a5..88336a3 100644 --- a/providers/sigrefdata/gma.py +++ b/providers/sigrefdata/gma.py @@ -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[ diff --git a/providers/sigrefdata/illw.py b/providers/sigrefdata/illw.py index 3a182d0..25c4556 100644 --- a/providers/sigrefdata/illw.py +++ b/providers/sigrefdata/illw.py @@ -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[ diff --git a/providers/sigrefdata/llota.py b/providers/sigrefdata/llota.py index b274bf4..277eb91 100644 --- a/providers/sigrefdata/llota.py +++ b/providers/sigrefdata/llota.py @@ -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])) diff --git a/providers/sigrefdata/local_file_sig_ref_data_provider.py b/providers/sigrefdata/local_file_sig_ref_data_provider.py index 15f7e40..146e8a6 100644 --- a/providers/sigrefdata/local_file_sig_ref_data_provider.py +++ b/providers/sigrefdata/local_file_sig_ref_data_provider.py @@ -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.""" diff --git a/providers/sigrefdata/mota.py b/providers/sigrefdata/mota.py index 364ce7c..82a2db4 100644 --- a/providers/sigrefdata/mota.py +++ b/providers/sigrefdata/mota.py @@ -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[ diff --git a/providers/sigrefdata/pnp_kml_sig_ref_data_provider.py b/providers/sigrefdata/pnp_kml_sig_ref_data_provider.py index 4ba8d26..0d9d62e 100644 --- a/providers/sigrefdata/pnp_kml_sig_ref_data_provider.py +++ b/providers/sigrefdata/pnp_kml_sig_ref_data_provider.py @@ -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: diff --git a/providers/sigrefdata/pota.py b/providers/sigrefdata/pota.py index 656cc82..c445af7 100644 --- a/providers/sigrefdata/pota.py +++ b/providers/sigrefdata/pota.py @@ -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, diff --git a/providers/sigrefdata/sig_ref_data_provider.py b/providers/sigrefdata/sig_ref_data_provider.py index 75c752f..6809777 100644 --- a/providers/sigrefdata/sig_ref_data_provider.py +++ b/providers/sigrefdata/sig_ref_data_provider.py @@ -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 diff --git a/providers/sigrefdata/sota.py b/providers/sigrefdata/sota.py index 8a04bec..71a520e 100644 --- a/providers/sigrefdata/sota.py +++ b/providers/sigrefdata/sota.py @@ -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, diff --git a/providers/sigrefdata/towers.py b/providers/sigrefdata/towers.py index 493081e..1bb214f 100644 --- a/providers/sigrefdata/towers.py +++ b/providers/sigrefdata/towers.py @@ -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)) diff --git a/providers/sigrefdata/wca.py b/providers/sigrefdata/wca.py index 1bba095..71e1d5d 100644 --- a/providers/sigrefdata/wca.py +++ b/providers/sigrefdata/wca.py @@ -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)) diff --git a/providers/sigrefdata/wota.py b/providers/sigrefdata/wota.py index f713136..211a362 100644 --- a/providers/sigrefdata/wota.py +++ b/providers/sigrefdata/wota.py @@ -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", diff --git a/providers/sigrefdata/wwbota.py b/providers/sigrefdata/wwbota.py index 63fa00c..955aee7 100644 --- a/providers/sigrefdata/wwbota.py +++ b/providers/sigrefdata/wwbota.py @@ -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)) diff --git a/providers/sigrefdata/wwff.py b/providers/sigrefdata/wwff.py index bf72f44..468a093 100644 --- a/providers/sigrefdata/wwff.py +++ b/providers/sigrefdata/wwff.py @@ -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[ diff --git a/providers/sigrefdata/zlota.py b/providers/sigrefdata/zlota.py index 02ce7c2..1c85502 100644 --- a/providers/sigrefdata/zlota.py +++ b/providers/sigrefdata/zlota.py @@ -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) diff --git a/providers/solarconditions/hamqsl.py b/providers/solarconditions/hamqsl.py index ac5f452..541488c 100644 --- a/providers/solarconditions/hamqsl.py +++ b/providers/solarconditions/hamqsl.py @@ -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 { diff --git a/providers/solarconditions/http_solar_conditions_provider.py b/providers/solarconditions/http_solar_conditions_provider.py index 342e459..6b92200 100644 --- a/providers/solarconditions/http_solar_conditions_provider.py +++ b/providers/solarconditions/http_solar_conditions_provider.py @@ -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): diff --git a/providers/solarconditions/noaa3dayforecast.py b/providers/solarconditions/noaa3dayforecast.py index a728bb7..75eb642 100644 --- a/providers/solarconditions/noaa3dayforecast.py +++ b/providers/solarconditions/noaa3dayforecast.py @@ -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 = [] diff --git a/providers/spot/dxcluster.py b/providers/spot/dxcluster.py index 28248e9..3d84e15 100644 --- a/providers/spot/dxcluster.py +++ b/providers/spot/dxcluster.py @@ -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" diff --git a/providers/spot/gma.py b/providers/spot/gma.py index 5df218f..9754ea7 100644 --- a/providers/spot/gma.py +++ b/providers/spot/gma.py @@ -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}).") diff --git a/providers/spot/http_spot_provider.py b/providers/spot/http_spot_provider.py index 1d34a61..3a73ddd 100644 --- a/providers/spot/http_spot_provider.py +++ b/providers/spot/http_spot_provider.py @@ -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): diff --git a/providers/spot/parksnpeaks.py b/providers/spot/parksnpeaks.py index 391422c..de1e775 100644 --- a/providers/spot/parksnpeaks.py +++ b/providers/spot/parksnpeaks.py @@ -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}") diff --git a/providers/spot/pota.py b/providers/spot/pota.py index 03397c0..ea69f96 100644 --- a/providers/spot/pota.py +++ b/providers/spot/pota.py @@ -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.") diff --git a/providers/spot/rbn.py b/providers/spot/rbn.py index 6894d25..e00b1f2 100644 --- a/providers/spot/rbn.py +++ b/providers/spot/rbn.py @@ -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" diff --git a/providers/spot/sota.py b/providers/spot/sota.py index 0b9a7d8..fe235da 100644 --- a/providers/spot/sota.py +++ b/providers/spot/sota.py @@ -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.") diff --git a/providers/spot/sse_spot_provider.py b/providers/spot/sse_spot_provider.py index 4ed48ad..6a3e59e 100644 --- a/providers/spot/sse_spot_provider.py +++ b/providers/spot/sse_spot_provider.py @@ -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 diff --git a/providers/spot/tiles.py b/providers/spot/tiles.py index 15eeb5d..de73eb6 100644 --- a/providers/spot/tiles.py +++ b/providers/spot/tiles.py @@ -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('.', '')}" diff --git a/providers/spot/ukpacketnet.py b/providers/spot/ukpacketnet.py index 9f21e78..fcb63f6 100644 --- a/providers/spot/ukpacketnet.py +++ b/providers/spot/ukpacketnet.py @@ -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 diff --git a/providers/spot/websocket_spot_provider.py b/providers/spot/websocket_spot_provider.py index d0362b9..25416af 100644 --- a/providers/spot/websocket_spot_provider.py +++ b/providers/spot/websocket_spot_provider.py @@ -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 diff --git a/providers/spot/xota.py b/providers/spot/xota.py index 20496e2..5cb9d6d 100644 --- a/providers/spot/xota.py +++ b/providers/spot/xota.py @@ -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(), diff --git a/providers/staticdata/file_download_static_data_provider.py b/providers/staticdata/file_download_static_data_provider.py index d4cd403..0b1726e 100644 --- a/providers/staticdata/file_download_static_data_provider.py +++ b/providers/staticdata/file_download_static_data_provider.py @@ -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): diff --git a/providers/staticdata/local_file_static_data_provider.py b/providers/staticdata/local_file_static_data_provider.py index f21fdd3..f01a3c4 100644 --- a/providers/staticdata/local_file_static_data_provider.py +++ b/providers/staticdata/local_file_static_data_provider.py @@ -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 diff --git a/server/handlers/api/addspot.py b/server/handlers/api/addspot.py index ff9ca3b..8d7ba7d 100644 --- a/server/handlers/api/addspot.py +++ b/server/handlers/api/addspot.py @@ -119,13 +119,13 @@ class APISpotHandler(tornado.web.RequestHandler): # Reject invalid-looking callsigns if not re.match(r"^[A-Za-z0-9/\-]*$", spot.dx_call): self.set_status(422) - self.write(safe_json_dumps("Error - '" + spot.dx_call + "' does not look like a valid callsign.")) + self.write(safe_json_dumps(f"Error - '{spot.dx_call}' does not look like a valid callsign.")) self.set_header("Cache-Control", "no-store") self.set_header("Content-Type", "application/json") return if not re.match(r"^[A-Za-z0-9/\-]*$", spot.de_call): self.set_status(422) - self.write(safe_json_dumps("Error - '" + spot.de_call + "' does not look like a valid callsign.")) + self.write(safe_json_dumps(f"Error - '{spot.de_call}' does not look like a valid callsign.")) self.set_header("Cache-Control", "no-store") self.set_header("Content-Type", "application/json") return @@ -134,7 +134,7 @@ class APISpotHandler(tornado.web.RequestHandler): if infer_band_from_freq(spot.freq) == UNKNOWN_BAND: self.set_status(422) self.write( - safe_json_dumps("Error - Frequency of " + str(spot.freq / 1000.0) + "kHz is not in a known band.")) + safe_json_dumps(f"Error - Frequency of {spot.freq / 1000.0!s}kHz is not in a known band.")) self.set_header("Cache-Control", "no-store") self.set_header("Content-Type", "application/json") return @@ -145,7 +145,7 @@ class APISpotHandler(tornado.web.RequestHandler): spot.dx_grid.upper()): self.set_status(422) self.write( - safe_json_dumps("Error - '" + spot.dx_grid + "' does not look like a valid Maidenhead grid.")) + safe_json_dumps(f"Error - '{spot.dx_grid}' does not look like a valid Maidenhead grid.")) self.set_header("Cache-Control", "no-store") self.set_header("Content-Type", "application/json") return @@ -155,7 +155,7 @@ class APISpotHandler(tornado.web.RequestHandler): spot.sig) and not re.match(get_ref_regex_for_sig(spot.sig), spot.sig_refs[0].id): self.set_status(422) self.write(safe_json_dumps( - "Error - '" + spot.sig_refs[0].id + "' does not look like a valid reference for " + spot.sig + ".")) + f"Error - '{spot.sig_refs[0].id}' does not look like a valid reference for {spot.sig}.")) self.set_header("Cache-Control", "no-store") self.set_header("Content-Type", "application/json") return @@ -208,13 +208,11 @@ class APISpotHandler(tornado.web.RequestHandler): threading.Timer(1.0, provider.force_poll).start() except NotImplementedError as e: upstream_warning = str(e) - except Exception as e: - logging.warning("Failed to submit spot upstream to " + upstream_provider_name + ": " + str(e)) - upstream_warning = "Spot was saved locally but upstream submission to " + upstream_provider_name + " failed: " + str( - e) + except Exception: + logging.exception(f"Failed to submit spot upstream to {upstream_provider_name}") + upstream_warning = f"Spot was saved locally but upstream submission to {upstream_provider_name} failed." else: - upstream_warning = "No enabled provider named '" + upstream_provider_name + "' supports upstream submission for " + ( - spot.sig if spot.sig else "") + " spots." + upstream_warning = f"No enabled provider named '{upstream_provider_name}' supports upstream submission for {spot.sig if spot.sig else ''} spots." # If we successfully submitted the spot upstream, don't add it direct to Spothole, otherwise it will be a # duplicate with what immediately comes back from the API. But if we weren't asked to send it upstream, or @@ -224,7 +222,7 @@ class APISpotHandler(tornado.web.RequestHandler): self._spots.set(spot.id, spot) if upstream_warning: - self.write(safe_json_dumps("Warning - " + upstream_warning)) + self.write(safe_json_dumps(f"Warning - {upstream_warning}")) self.set_status(201) else: self.write(safe_json_dumps("OK")) @@ -256,6 +254,6 @@ class APISpotHandler(tornado.web.RequestHandler): data={"secret": RECAPTCHA_SECRET_KEY, "response": token}, timeout=(5, 10)) return response.ok and response.json().get("success", False) - except Exception as e: - logging.warning("reCAPTCHA verification request failed: " + str(e)) + except Exception: + logging.exception(f"reCAPTCHA verification request failed") return False diff --git a/server/handlers/api/alerts.py b/server/handlers/api/alerts.py index 756d853..40524f1 100644 --- a/server/handlers/api/alerts.py +++ b/server/handlers/api/alerts.py @@ -55,7 +55,7 @@ class APIAlertsHandler(tornado.web.RequestHandler): self.write(safe_json_dumps(data)) self.set_status(200) except ValueError as e: - self.write(safe_json_dumps("Bad request - " + str(e))) + self.write(safe_json_dumps(f"Bad request - {e!s}")) self.set_status(400) except Exception: logging.exception("Exception when handling client request to alerts API") diff --git a/server/handlers/api/lookups.py b/server/handlers/api/lookups.py index 55add60..89cac0d 100644 --- a/server/handlers/api/lookups.py +++ b/server/handlers/api/lookups.py @@ -50,7 +50,7 @@ class APILookupCallHandler(tornado.web.RequestHandler): self.write(safe_json_dumps(callsign_data)) else: - self.write(safe_json_dumps("Error - '" + call + "' does not look like a valid callsign.")) + self.write(safe_json_dumps(f"Error - '{call}' does not look like a valid callsign.")) self.set_status(422) else: self.write(safe_json_dumps("Error - call must be provided")) @@ -99,10 +99,10 @@ class APILookupSIGRefHandler(tornado.web.RequestHandler): else: self.write(safe_json_dumps( - "Error - '" + ref_id + "' does not look like a valid reference ID for " + sig + ".")) + f"Error - '{ref_id}' does not look like a valid reference ID for {sig}.")) self.set_status(422) else: - self.write(safe_json_dumps("Error - sig '" + sig + "' is not known.")) + self.write(safe_json_dumps(f"Error - sig '{sig}' is not known.")) self.set_status(422) else: self.write(safe_json_dumps("Error - sig and id must be provided")) diff --git a/server/handlers/api/spots.py b/server/handlers/api/spots.py index 817faff..b01cb67 100644 --- a/server/handlers/api/spots.py +++ b/server/handlers/api/spots.py @@ -55,7 +55,7 @@ class APISpotsHandler(tornado.web.RequestHandler): self.write(safe_json_dumps(data)) self.set_status(200) except ValueError as e: - self.write(safe_json_dumps("Bad request - " + str(e))) + self.write(safe_json_dumps(f"Bad request - {e!s}")) self.set_status(400) except Exception: logging.exception("Excedption when handling client request to spots API") diff --git a/server/handlers/api/v1_addspot.py b/server/handlers/api/v1_addspot.py index e7ba922..06950cf 100644 --- a/server/handlers/api/v1_addspot.py +++ b/server/handlers/api/v1_addspot.py @@ -76,13 +76,13 @@ class V1APISpotHandler(tornado.web.RequestHandler): # Reject invalid-looking callsigns if not re.match(r"^[A-Za-z0-9/\-]*$", spot.dx_call): self.set_status(422) - self.write(safe_json_dumps("Error - '" + spot.dx_call + "' does not look like a valid callsign.")) + self.write(safe_json_dumps(f"Error - '{spot.dx_call}' does not look like a valid callsign.")) self.set_header("Cache-Control", "no-store") self.set_header("Content-Type", "application/json") return if not re.match(r"^[A-Za-z0-9/\-]*$", spot.de_call): self.set_status(422) - self.write(safe_json_dumps("Error - '" + spot.de_call + "' does not look like a valid callsign.")) + self.write(safe_json_dumps(f"Error - '{spot.de_call}' does not look like a valid callsign.")) self.set_header("Cache-Control", "no-store") self.set_header("Content-Type", "application/json") return @@ -90,7 +90,7 @@ class V1APISpotHandler(tornado.web.RequestHandler): # Reject if frequency not in a known band if infer_band_from_freq(spot.freq) == UNKNOWN_BAND: self.set_status(422) - self.write(safe_json_dumps("Error - Frequency of " + str(spot.freq / 1000.0) + "kHz is not in a known band.")) + self.write(safe_json_dumps(f"Error - Frequency of {spot.freq / 1000.0!s}kHz is not in a known band.")) self.set_header("Cache-Control", "no-store") self.set_header("Content-Type", "application/json") return @@ -100,7 +100,7 @@ class V1APISpotHandler(tornado.web.RequestHandler): r"^([A-R]{2}[0-9]{2}[A-X]{2}[0-9]{2}[A-X]{2}|[A-R]{2}[0-9]{2}[A-X]{2}[0-9]{2}|[A-R]{2}[0-9]{2}[A-X]{2}|[A-R]{2}[0-9]{2})$", spot.dx_grid.upper()): self.set_status(422) - self.write(safe_json_dumps("Error - '" + spot.dx_grid + "' does not look like a valid Maidenhead grid.")) + self.write(safe_json_dumps(f"Error - '{spot.dx_grid}' does not look like a valid Maidenhead grid.")) self.set_header("Cache-Control", "no-store") self.set_header("Content-Type", "application/json") return @@ -110,7 +110,7 @@ class V1APISpotHandler(tornado.web.RequestHandler): spot.sig) and not re.match(get_ref_regex_for_sig(spot.sig), spot.sig_refs[0].id): self.set_status(422) self.write(safe_json_dumps( - "Error - '" + spot.sig_refs[0].id + "' does not look like a valid reference for " + spot.sig + ".")) + f"Error - '{spot.sig_refs[0].id}' does not look like a valid reference for {spot.sig}.")) self.set_header("Cache-Control", "no-store") self.set_header("Content-Type", "application/json") return diff --git a/server/handlers/api/v1_compatability.py b/server/handlers/api/v1_compatability.py index 5608ca8..85e0316 100644 --- a/server/handlers/api/v1_compatability.py +++ b/server/handlers/api/v1_compatability.py @@ -15,7 +15,7 @@ class V1RedirectHandler(tornado.web.RequestHandler): async def _proxy(self, path): new_url = f"{self.request.protocol}://{self.request.host}/api/v2/{path}" if self.request.query: - new_url += "?" + self.request.query + new_url += f"?{self.request.query}" client = AsyncHTTPClient() try: diff --git a/server/handlers/pagetemplate.py b/server/handlers/pagetemplate.py index bbe9deb..ff4e563 100644 --- a/server/handlers/pagetemplate.py +++ b/server/handlers/pagetemplate.py @@ -31,6 +31,6 @@ class PageTemplateHandler(tornado.web.RequestHandler): page_requests_counter.inc() # Load named template, and provide variables used in templates - self.render(self._template_name + ".html", software_version=SOFTWARE_VERSION, + self.render(f"{self._template_name}.html", software_version=SOFTWARE_VERSION, server_owner_callsign=SERVER_OWNER_CALLSIGN, allow_spotting=ALLOW_SPOTTING, web_ui_options=WEB_UI_OPTIONS, baseurl=BASE_URL, current_path=self.request.path) diff --git a/server/webserver.py b/server/webserver.py index e8b5fbc..49ecac0 100644 --- a/server/webserver.py +++ b/server/webserver.py @@ -141,8 +141,8 @@ class WebServer: log_function=request_log, debug=False) app.listen(self._port, xheaders=True) - logging.info("Web server running on port " + str(WEB_SERVER_PORT)) - logging.info("You can access your copy of Spothole at " + BASE_URL) + logging.info(f"Web server running on port {WEB_SERVER_PORT!s}") + logging.info(f"You can access your copy of Spothole at {BASE_URL}") await self._shutdown_event.wait() diff --git a/spothole.py b/spothole.py index c01a421..1701cac 100644 --- a/spothole.py +++ b/spothole.py @@ -42,7 +42,7 @@ if __name__ == '__main__': logging.info("Starting...") logging.info( - "This is Spothole version " + SOFTWARE_VERSION + ". This instance is run by " + SERVER_OWNER_CALLSIGN + ".") + f"This is Spothole version {SOFTWARE_VERSION}. This instance is run by {SERVER_OWNER_CALLSIGN}.") # Shut down gracefully on SIGINT signal.signal(signal.SIGINT, shutdown) diff --git a/static/img/flags/generate.py b/static/img/flags/generate.py index 2d90252..ba2a6e1 100644 --- a/static/img/flags/generate.py +++ b/static/img/flags/generate.py @@ -19,7 +19,7 @@ for dxcc in data["dxcc"]: draw = ImageDraw.Draw(image) draw.text((0, -10), flag, font=ImageFont.truetype("/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf", 109), embedded_color=True) - outfile = str(dxcc_id) + ".png" + outfile = f"{dxcc_id!s}.png" image.save(outfile, "PNG") image = Image.new("RGBA", (140, 110), (255, 0, 0, 0)) diff --git a/templates/add_spot.html b/templates/add_spot.html index 18815d7..70a646b 100644 --- a/templates/add_spot.html +++ b/templates/add_spot.html @@ -76,7 +76,7 @@ - + diff --git a/templates/alerts.html b/templates/alerts.html index 9ecd260..4278897 100644 --- a/templates/alerts.html +++ b/templates/alerts.html @@ -82,7 +82,7 @@ - + diff --git a/templates/bands.html b/templates/bands.html index 5314f1e..238834a 100644 --- a/templates/bands.html +++ b/templates/bands.html @@ -79,8 +79,8 @@ - - + + diff --git a/templates/base.html b/templates/base.html index de563f0..b4728ae 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,6 +1,6 @@ {% extends "skeleton.html" %} {% block head_extra %} - + @@ -15,10 +15,10 @@ window.fetchEventSource = fetchEventSource; - - - - + + + + {% end %} {% block body %}
diff --git a/templates/conditions.html b/templates/conditions.html index 8bd5db8..fe61aa8 100644 --- a/templates/conditions.html +++ b/templates/conditions.html @@ -284,7 +284,7 @@
- + diff --git a/templates/map.html b/templates/map.html index 1cce17d..414ccf9 100644 --- a/templates/map.html +++ b/templates/map.html @@ -112,8 +112,8 @@ - - + + diff --git a/templates/spots.html b/templates/spots.html index e658c32..701062e 100644 --- a/templates/spots.html +++ b/templates/spots.html @@ -118,8 +118,8 @@ - - + + diff --git a/templates/status.html b/templates/status.html index 9daac02..1628a13 100644 --- a/templates/status.html +++ b/templates/status.html @@ -86,7 +86,7 @@ - +