diff --git a/config-example.yml b/config-example.yml index 502e532..564ee5f 100644 --- a/config-example.yml +++ b/config-example.yml @@ -4,18 +4,18 @@ # Your callsign. Used to log into DX clusters and included in User-Agent when querying HTTP servers. Useful so if your # server goes crazy and causes other people problems, they know who to contact :) If you don't have one, you can leave # this as "N0CALL" and it shouldn't do any harm, as we're not sending anything to the various networks, only receiving. -server-owner-callsign: "N0CALL" +server_owner_callsign: "N0CALL" # Port to open the local web server on -web-server-port: 8080 +web_server_port: 8080 # Run in API-only mode? When enabled, the web UI is not served, only the API endpoints and the OpenAPI documentation # page. If you are running your own Spothole instance purely to serve client software, and not wanting visitors to # discover a full web-based cluster UI, enable this flag. -api-only-mode: false +api_only_mode: false # The base URL at which the software runs. -base-url: "http://localhost:8080" +base_url: "http://localhost:8080" # Spot providers to use. This is an example set, tailor it to your liking by commenting and uncommenting. # RBN and APRS-IS are supported but have such a high data rate, you probably don't want them enabled. @@ -23,7 +23,7 @@ base-url: "http://localhost:8080" # addresses and ports. You can duplicate them if you like, e.g. to support several DX clusters. RBN uses two ports, 7000 # for CW/RTTY and 7001 for FT8, so if you want both, you need two entries, as shown below. # Feel free to write your own provider classes! There are details in the README. -spot-providers: +spot_providers: - class: "POTA" enabled: true @@ -39,7 +39,7 @@ spot-providers: - class: "GMA" enabled: true # GMA requires an API key to fetch spots. After creating an account on cqgma.org, email support and request one. - api-key: "" + api_key: "" - class: "HEMA" enabled: true @@ -100,18 +100,18 @@ spot-providers: # This setting doesn't affect the spot provider itself, or anything in the back-end of Spothole, just the web UI. # By default spots from all enabled providers will be shown in the web UI. However, you might want RBN data to be # received by Spothole but not shown on the web UI unless the user explicitly turns it on. For that behaviour, - # set enabled to true, but enabled-by-default-in-web-ui to false. - enabled-by-default-in-web-ui: false + # set enabled to true, but enabled_by_default_in_web_ui to false. + enabled_by_default_in_web_ui: false - class: "RBN" name: "RBN FT8" enabled: false port: 7001 - enabled-by-default-in-web-ui: false + enabled_by_default_in_web_ui: false - class: "UKPacketNet" enabled: false - enabled-by-default-in-web-ui: false + enabled_by_default_in_web_ui: false - class: "XOTA" # xOTA sources have a "name" property so you can define what programme it is for, since xOTA is generic software @@ -125,25 +125,25 @@ spot-providers: # For Toilets on the Air, we prefix the SIG references (T-01 etc) with some characters that define the conference: # C3, EH or HOPE - so we can look up the correct locations in our database, because each conference starts from T-01 # but refers to a toilet in a different building (or continent!) - sig-ref-prefix: "C3" + sig_ref_prefix: "C3" - class: "XOTA" name: "EH23 TOTA" enabled: false url: "wss://eh23.totawatch.de/api/spot/live" sig: "Toilets" - sig-ref-prefix: "EH" + sig_ref_prefix: "EH" - class: "XOTA" name: "HOPE26 TOTA" enabled: false url: "wss://hope-26.totawatch.de/api/spot/live" sig: "Toilets" - sig-ref-prefix: "HOPE" + sig_ref_prefix: "HOPE" # Alert providers to use. Same setup as the spot providers list above. -alert-providers: +alert_providers: - class: "POTA" enabled: true @@ -168,7 +168,7 @@ alert-providers: # Solar condition providers to use. These poll external APIs for solar propagation data (SFI, A/K indices, band # conditions, etc.) and make it available via the /api/v2/solar endpoint. -solar-condition-providers: +solar_condition_providers: - class: "HamQSL" enabled: true @@ -184,7 +184,7 @@ solar-condition-providers: # Static reference data providers to use. These allow Spothole to download data such as mapping between callsign # prefixes and DXCC entities. -static-data-providers: +static_data_providers: - class: "K0SWE" enabled: true @@ -197,7 +197,7 @@ static-data-providers: # SIG reference data providers to use. These allow Spothole to download, for example, the WWFF directory that maps WWFF # park IDs to their name and location. -sig-ref-data-providers: +sig_ref_data_providers: - class: "POTA" enabled: true @@ -260,7 +260,7 @@ sig-ref-data-providers: # lookups or from web-based services such as QRZ. Note that use of QRZ and HamQTH is *on behalf of the user*, and # requires the user to enter their credentials into the website UI or provide them in an API call. Spothole will not # look up all calls via QRZ/HamQTH using the server owner's credentials, as this is against their usage policy. -callsign-data-providers: +callsign_data_providers: - class: "QRZ" enabled: true # Callsign data providers can often provide conflicting data, e.g. a home location from QRZ vs the centre of the @@ -283,14 +283,14 @@ callsign-data-providers: priority: 3 # API key for Clublog to look up information. Required in order to enable this provider. Unlike QRZ and HamQTH, # Clublog uses an API key issued to Spothole, not to the end user. - api-key: "" + api_key: "" - class: "ClublogXML" enabled: true priority: 4 # API key for Clublog to look up information. Required in order to enable this provider. You will need to request # one via their helpdesk portal if you want to use callsign lookups from Clublog. - api-key: "" + api_key: "" - class: "CountryFiles" priority: 5 @@ -299,50 +299,50 @@ callsign-data-providers: # Maximum time to keep spots and alerts in the system before deleting them. By default, one hour for spots and one week # for alerts. -max-spot-age-sec: 3600 -max-alert-age-sec: 604800 +max_spot_age_sec: 3600 +max_alert_age_sec: 604800 # Allow submitting spots to the Spothole API? -allow-spotting: true +allow_spotting: true # Allow upstream submission of spots to external providers (POTA, SOTA, etc.) via the API? -# Requires allow-spotting to also be true. Set to false to only accept spots into the local +# Requires allow_spotting to also be true. Set to false to only accept spots into the local # Spothole database, without forwarding them to any external service. -allow-upstream-spotting: true +allow_upstream_spotting: true # Google reCAPTCHA v2 keys for CAPTCHA protection on upstream spot submission. Both keys must be set to enable CAPTCHA. -# Leave both empty to disable CAPTCHA (e.g. for a private/trusted server) or if allow-spotting is false, in which case +# Leave both empty to disable CAPTCHA (e.g. for a private/trusted server) or if allow_spotting is false, in which case # they will do nothing. Note that with CAPTCHA enabled, this will prevent third-party clients submitting spots through # Spothole unless the clients are web-based, use the same site key, have their domains enabled in your reCAPTCHA config, # and of course their user solves the CAPTCHA. # You can sign up for reCAPTCHA at https://www.google.com/recaptcha/ -recaptcha-site-key: "" -recaptcha-secret-key: "" +recaptcha_site_key: "" +recaptcha_secret_key: "" # Log web requests? Useful to see what your users are requesting in terms of pages and API endpoints, but will fill up # your log quickly on a popular server. -log-web-requests: false +log_web_requests: false # Minimum severity of log statements to print to the system log. Defaults to INFO, change to DEBUG if you need more # information. -log-level: INFO +log_level: INFO # Options for the web UI. -web-ui-options: - spot-count: [ 10, 25, 50, 100 ] - spot-count-default: 50 - max-spot-age: [ 5, 10, 30, 60 ] - max-spot-age-default: 30 - alert-count: [ 25, 50, 100, 200, 500 ] - alert-count-default: 100 +web_ui_options: + spot_count: [ 10, 25, 50, 100 ] + spot_count_default: 50 + max_spot_age: [ 5, 10, 30, 60 ] + max_spot_age_default: 30 + alert_count: [ 25, 50, 100, 200, 500 ] + alert_count_default: 100 # Default UI colour scheme. Supported values are "light", "dark" and "auto" (i.e. use the browser/OS colour scheme). # Users can still override this in the UI to their own preference. - color-scheme-default: "auto" + color_scheme_default: "auto" # Default band colour scheme. Supported values are the full names of any band colour scheme shown in the UI. # Users can still override this in the UI to their own preference. - band-color-scheme-default: "PSK Reporter (Adjusted)" + band_color_scheme_default: "PSK Reporter (Adjusted)" # Custom HTML insert. This can be any arbitrary HTML. It will be inserted next to the start/stop buttons on the spots # (home) page, although being arbitrary HTML you can also use a div with absolute, relative, float placement etc. This # is designed for a "donate/support the server" type button, though you are free to do whatever you want with it. # As the server owner you are responsible for the safe usage of this option! - support-button-html: "" \ No newline at end of file + support_button_html: "" \ No newline at end of file diff --git a/core/config.py b/core/config.py index 4b8eb03..54a1758 100644 --- a/core/config.py +++ b/core/config.py @@ -15,24 +15,24 @@ with open("config.yml") as f: config = yaml.safe_load(f) logging.info("Loaded config.") -BASE_URL = config.get("base-url", "http://localhost:8080") -MAX_SPOT_AGE = config.get("max-spot-age-sec", 3600) -MAX_ALERT_AGE = config.get("max-alert-age-sec", 604800) -SERVER_OWNER_CALLSIGN = config.get("server-owner-callsign", "N0CALL") -WEB_SERVER_PORT = config.get("web-server-port", 8080) -ALLOW_SPOTTING = config.get("allow-spotting", True) -ALLOW_UPSTREAM_SPOTTING = config.get("allow-upstream-spotting", True) -WEB_UI_OPTIONS = config.get("web-ui-options", {}) -API_ONLY_MODE = config.get("api-only-mode", False) -RECAPTCHA_SECRET_KEY = config.get("recaptcha-secret-key", "") -RECAPTCHA_SITE_KEY = config.get("recaptcha-site-key", "") -LOG_LEVEL = config.get("log-level", "INFO") -LOG_WEB_REQUESTS = config.get("log-web-requests", False) +BASE_URL = config.get("base_url", "http://localhost:8080") +MAX_SPOT_AGE = config.get("max_spot_age_sec", 3600) +MAX_ALERT_AGE = config.get("max_alert_age_sec", 604800) +SERVER_OWNER_CALLSIGN = config.get("server_owner_callsign", "N0CALL") +WEB_SERVER_PORT = config.get("web_server_port", 8080) +ALLOW_SPOTTING = config.get("allow_spotting", True) +ALLOW_UPSTREAM_SPOTTING = config.get("allow_upstream_spotting", True) +WEB_UI_OPTIONS = config.get("web_ui_options", {}) +API_ONLY_MODE = config.get("api_only_mode", False) +RECAPTCHA_SECRET_KEY = config.get("recaptcha_secret_key", "") +RECAPTCHA_SITE_KEY = config.get("recaptcha_site_key", "") +LOG_LEVEL = config.get("log_level", "INFO") +LOG_WEB_REQUESTS = config.get("log_web_requests", False) -WEB_UI_OPTIONS["qrz-enabled"] = any(p["class"] == "QRZ" and p["enabled"] for p in config["callsign-data-providers"]) -WEB_UI_OPTIONS["hamqth-enabled"] = any(p["class"] == "HamQTH" and p["enabled"] for p in config["callsign-data-providers"]) -WEB_UI_OPTIONS["recaptcha-site-key"] = RECAPTCHA_SITE_KEY -WEB_UI_OPTIONS["allow-upstream-spotting"] = ALLOW_SPOTTING and ALLOW_UPSTREAM_SPOTTING +WEB_UI_OPTIONS["qrz_enabled"] = any(p["class"] == "QRZ" and p["enabled"] for p in config["callsign_data_providers"]) +WEB_UI_OPTIONS["hamqth_enabled"] = any(p["class"] == "HamQTH" and p["enabled"] for p in config["callsign_data_providers"]) +WEB_UI_OPTIONS["recaptcha_site_key"] = RECAPTCHA_SITE_KEY +WEB_UI_OPTIONS["allow_upstream_spotting"] = ALLOW_SPOTTING and ALLOW_UPSTREAM_SPOTTING def create_provider_from_config(package, config_providers_entry): diff --git a/core/data_providers.py b/core/data_providers.py index 89a6792..8280248 100644 --- a/core/data_providers.py +++ b/core/data_providers.py @@ -14,22 +14,22 @@ class DataProviders: def setup(self): - for entry in config["spot-providers"]: + for entry in config["spot_providers"]: self.spot_providers.append(create_provider_from_config("providers.spot", entry)) - for entry in config["alert-providers"]: + for entry in config["alert_providers"]: self.alert_providers.append(create_provider_from_config("providers.alert", entry)) - for entry in config.get("solar-condition-providers", []): + for entry in config.get("solar_condition_providers", []): self.solar_condition_providers.append(create_provider_from_config("providers.solarconditions", entry)) - for entry in config.get("static-data-providers", []): + for entry in config.get("static_data_providers", []): self.static_data_providers.append(create_provider_from_config("providers.staticdata", entry)) - for entry in config.get("sig-ref-data-providers", []): + for entry in config.get("sig_ref_data_providers", []): self.sig_ref_data_providers.append(create_provider_from_config("providers.sigrefdata", entry)) - for entry in config.get("callsign-data-providers", []): + for entry in config.get("callsign_data_providers", []): self.callsign_data_providers.append(create_provider_from_config("providers.callsigndata", entry)) def start(self): - # Start data providers before spot/alert providers so the lookup data is there already for incoming spots + # Start data providers before spot/alert providers so the lookup data is there already for incoming spots. for p in self.static_data_providers: if p.enabled: p.start() diff --git a/core/status_reporter.py b/core/status_reporter.py index 019d4de..37917c7 100644 --- a/core/status_reporter.py +++ b/core/status_reporter.py @@ -24,8 +24,8 @@ class StatusReporter: self._stop_event = Event() self._startup_time = datetime.now(pytz.UTC) - DATA_STORE.status_data["software-version"] = SOFTWARE_VERSION - DATA_STORE.status_data["server-owner-callsign"] = SERVER_OWNER_CALLSIGN + DATA_STORE.status_data["software_version"] = SOFTWARE_VERSION + DATA_STORE.status_data["server_owner_callsign"] = SERVER_OWNER_CALLSIGN def start(self): """Start the reporter thread""" diff --git a/data/alert.py b/data/alert.py index 4a894ba..0002519 100644 --- a/data/alert.py +++ b/data/alert.py @@ -86,19 +86,20 @@ class Alert: # DX country, continent, zones etc. from callsign. CQ/ITU zone are better looked up with a location but we don't # have a real location for alerts. - call_info = get_call_info(self.dx_calls[0], credentials) - if self.dx_calls and self.dx_calls[0] and not self.dx_country: - self.dx_country = call_info.country - if self.dx_calls and self.dx_calls[0] and not self.dx_continent: - self.dx_continent = call_info.continent - if self.dx_calls and self.dx_calls[0] and not self.dx_cq_zone: - self.dx_cq_zone = call_info.cq_zone - if self.dx_calls and self.dx_calls[0] and not self.dx_itu_zone: - self.dx_itu_zone = call_info.itu_zone - if self.dx_calls and self.dx_calls[0] and not self.dx_dxcc_id: - self.dx_dxcc_id = call_info.dxcc_id - if self.dx_dxcc_id and not self.dx_flag: - self.dx_flag = get_flag_for_dxcc(self.dx_dxcc_id) + if self.dx_calls and self.dx_calls[0]: + call_info = get_call_info(self.dx_calls[0], credentials) + if self.dx_calls and self.dx_calls[0] and not self.dx_country: + self.dx_country = call_info.country + if self.dx_calls and self.dx_calls[0] and not self.dx_continent: + self.dx_continent = call_info.continent + if self.dx_calls and self.dx_calls[0] and not self.dx_cq_zone: + self.dx_cq_zone = call_info.cq_zone + if self.dx_calls and self.dx_calls[0] and not self.dx_itu_zone: + self.dx_itu_zone = call_info.itu_zone + if self.dx_calls and self.dx_calls[0] and not self.dx_dxcc_id: + self.dx_dxcc_id = call_info.dxcc_id + if self.dx_dxcc_id and not self.dx_flag: + self.dx_flag = get_flag_for_dxcc(self.dx_dxcc_id) # Fetch SIG data. In case a particular API doesn't provide a full set of name, lat, lon & grid for a reference # in its initial call, we use this code to populate the rest of the data. This includes working out grid refs diff --git a/docs/embedding.md b/docs/embedding.md index f897655..506adf2 100644 --- a/docs/embedding.md +++ b/docs/embedding.md @@ -23,8 +23,8 @@ a mapping exists. | Name | Allowed Values | Default | Example | Description | |------------------|-------------------------|---------|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `embedded` | `true`, `false` | `false` | `?embedded=true` | Enables embedded mode. | -| `color-scheme` | `light`, `dark`, `auto` | `auto` | `?color-scheme=dark` | Forces light or dark mode in preference to the operating system default. | -| `time-zone` | `UTC`, `local` | `UTC` | `?time-zone=local` | Sets times to be in UTC or local time. | +| `color_scheme` | `light`, `dark`, `auto` | `auto` | `?color_scheme=dark` | Forces light or dark mode in preference to the operating system default. | +| `time_zone` | `UTC`, `local` | `UTC` | `?time_zone=local` | Sets times to be in UTC or local time. | | `limit` | 10, 25, 50, 100 | 50 | `?limit=50` | Sets the number of spots that will be displayed on the main spots page | | `limit` | 25, 50, 100, 200, 500 | 100 | `?limit=100` | Sets the number of alerts that will be displayed on the alerts page | | `max_age` | 300, 600, 1800, 3600 | 1800 | `?max_age=1800` | Sets the maximum age of spots displayed on the map and bands pages, in seconds. | diff --git a/docs/multicluster.md b/docs/multicluster.md index 3632eae..79e49d5 100644 --- a/docs/multicluster.md +++ b/docs/multicluster.md @@ -32,7 +32,7 @@ Telnet to DXSpider and log in with "callsign-10" and execute the following comma login_prompt: "login: " login_callsign: "callsign-11" allow_rbn_spots: true - enabled-by-default-in-web-ui: false + enabled_by_default_in_web_ui: false ``` Telnet to DXSpider and log in with "callsign-11" and execute the following commands: @@ -54,7 +54,7 @@ Telnet to DXSpider and log in with "callsign-11" and execute the following comma login_prompt: "login: " login_callsign: "callsign-12" allow_rbn_spots: true - enabled-by-default-in-web-ui: false + enabled_by_default_in_web_ui: false ``` Telnet to DXSpider and log in with "callsign-12" and execute the following commands: @@ -76,7 +76,7 @@ Telnet to DXSpider and log in with "callsign-12" and execute the following comma login_prompt: "login: " login_callsign: "callsign-13" allow_rbn_spots: true - enabled-by-default-in-web-ui: false + enabled_by_default_in_web_ui: false ``` Telnet to DXSpider and log in with "callsign-13" and execute the following commands: diff --git a/providers/callsigndata/clublogapi.py b/providers/callsigndata/clublogapi.py index c0f39c4..d33d441 100644 --- a/providers/callsigndata/clublogapi.py +++ b/providers/callsigndata/clublogapi.py @@ -17,7 +17,7 @@ class ClublogAPI(APIQueryCallsignDataProvider): def __init__(self, provider_config): # API key required for this provider - self._api_key = provider_config.get("api-key", "") + self._api_key = provider_config.get("api_key", "") if self._api_key != "": lookuplib = LookupLib(lookuptype="clublogapi", apikey=self._api_key) self._callinfo = Callinfo(lookuplib) diff --git a/providers/callsigndata/clublogxml.py b/providers/callsigndata/clublogxml.py index f5c1b26..acbfaa4 100644 --- a/providers/callsigndata/clublogxml.py +++ b/providers/callsigndata/clublogxml.py @@ -22,7 +22,7 @@ class ClublogXML(FileDownloadCallsignDataProvider): def __init__(self, provider_config): # API key required for this provider - self._api_key = provider_config.get("api-key", "") + self._api_key = provider_config.get("api_key", "") if self._api_key == "": provider_config["enabled"] = False logging.warning( diff --git a/providers/spot/gma.py b/providers/spot/gma.py index 4fb4abd..35456ce 100644 --- a/providers/spot/gma.py +++ b/providers/spot/gma.py @@ -21,7 +21,7 @@ class GMA(HTTPSpotProvider): def __init__(self, provider_config): # Ensure there is an API key in our config, and set up the query URL using it. If no key is provided, # disable this spot provider. - self._api_key = provider_config.get("api-key", "") + self._api_key = provider_config.get("api_key", "") if self._api_key == "": provider_config["enabled"] = False logging.warning("GMA spot provider configured but no api key was provided, this API will not be queried.") @@ -68,7 +68,7 @@ class GMA(HTTPSpotProvider): ref_response = self._url_data_cache.get(self.REF_INFO_URL_ROOT + source_spot["REF"], headers=HTTP_HEADERS) # Sometimes this is blank even if it's a 200 response, so handle that - if ref_response.ok and ref_response.text is not None and ref_response.text != "": + if ref_response.ok and ref_response.text is not None and ref_response.text != "" and ref_response.text != "\n": ref_info = ref_response.json() # If this is POTA, SOTA or WWFF data we already have it through other means, so ignore. POTA and WWFF # spots come through with reftype=POTA or reftype=WWFF. SOTA is harder to figure out because both SOTA @@ -116,7 +116,7 @@ class GMA(HTTPSpotProvider): f"GMA API returned a malformed response when looking up ref {source_spot["REF"]}") except: logging.warning("Exception when looking up " + self.REF_INFO_URL_ROOT + source_spot[ - "REF"] + ", ignoring this spot for now") + "REF"] + ", ignoring this spot for now", exc_info=True) else: logging.warning(f"The GMA API returned an unexpected response (HTTP {http_response.status_code}).") diff --git a/providers/spot/spot_provider.py b/providers/spot/spot_provider.py index fe28d59..46c88b3 100644 --- a/providers/spot/spot_provider.py +++ b/providers/spot/spot_provider.py @@ -13,7 +13,7 @@ class SpotProvider: self.name = name self.enabled = provider_config.get("enabled", True) - self.enabled_by_default_in_web_ui = provider_config.get("enabled-by-default-in-web-ui", True) + self.enabled_by_default_in_web_ui = provider_config.get("enabled_by_default_in_web_ui", True) self.last_update_time = datetime.min.replace(tzinfo=pytz.UTC) self.last_spot_time = datetime.min.replace(tzinfo=pytz.UTC) self.status = "Not Started" if self.enabled else "Disabled" diff --git a/providers/spot/xota.py b/providers/spot/xota.py index c1fcabe..20496e2 100644 --- a/providers/spot/xota.py +++ b/providers/spot/xota.py @@ -23,7 +23,7 @@ class XOTA(WebsocketSpotProvider): name = provider_config["name"] if "name" in provider_config else "xOTA" super().__init__(name, provider_config, provider_config["url"]) self.SIG = str(provider_config["sig"]) if "sig" in provider_config else None - self._sig_ref_prefix = str(provider_config["sig-ref-prefix"]) if "sig-ref-prefix" in provider_config else "" + self._sig_ref_prefix = str(provider_config["sig_ref_prefix"]) if "sig_ref_prefix" in provider_config else "" def _ws_message_to_spot(self, b): string = b.decode("utf-8") diff --git a/server/handlers/api/options.py b/server/handlers/api/options.py index 07ee531..5c2e855 100644 --- a/server/handlers/api/options.py +++ b/server/handlers/api/options.py @@ -63,13 +63,12 @@ class APIOptionsHandler(tornado.web.RequestHandler): spot_providers.append("API") spot_providers_enabled_by_default.append("API") - options = {"bands": BANDS, "modes": ALL_MODES, "mode_types": MODE_TYPES, "sigs": SIGS, "spot_providers": spot_providers, - "spot-providers-enabled-by-default": spot_providers_enabled_by_default, + "spot_providers_enabled_by_default": spot_providers_enabled_by_default, "alert_providers": alert_providers, "callsign_data_providers": callsign_data_providers, "continents": CONTINENTS, diff --git a/static/apidocs/openapi.yml b/static/apidocs/openapi.yml index 990c58f..9474e48 100644 --- a/static/apidocs/openapi.yml +++ b/static/apidocs/openapi.yml @@ -19,13 +19,14 @@ info: * **Breaking change:** The "add spot" API has changed to enable future support for upstream submission to the spotting services associated with various SIGs. Instead of just posting the spot object itself as the JSON content of the POST, this has moved into a `spot` object within the structure. A new `handling` object alongside it contains the `submit_upstream`, `upstream_provider`, `upstream_credentials`, and `captcha_token` fields which control the server handling of the spot. * **Breaking change:** Removed `cleanup` from `/status` response - * **Breaking change:** in the `/options` response, renamed `spot_sources` and `alert_sources` to `spot_providers` and `alert_providers` + * **Breaking change:** In the `/options` response, renamed `spot_sources` and `alert_sources` to `spot_providers` and `alert_providers` * **Breaking change:** A user's QRZ.com and HamQTH credentials are now supplied as request headers (`X-QRZ-Username`, `X-QRZ-Password`, `X-QRZ-Session-Key`, `X-HamQTH-Username`, `X-HamQTH-Password`, `X-HamQTH-Session-ID`) rather than query parameters, to keep credentials out of server logs. * **Breaking change:** "WAB/WAI GRID" as a location source has been renamed to just "GRID", and also applies to Tiles on the Air spots and grid references from cluster spot comments. + * **Breaking change:** In the `/status` response, `software-version` has changed to `software_version` and `server-owner-callsign` to `server_owner_callsign`. * POST `/spot` now supports Google reCaptcha and (if the site owner has set it up) now requires `captcha_token` in order to successfully submit. (This is used to lock down the submit function and prevent submission via Spothole by bots or third-party clients.) * GET `/options` now returns `spot_submit_providers`, a map of SIG names to the names of providers that support upstream spot submission for that SIG. (This allows clients to present the user with options of where a new spot can be sent to.) * Added `sig_ref_data_providers`, `static_data_providers` and `callsign_data_providers` to `/status` response - * Added `callsign_data_providers` to `/options` response + * Added `callsign_data_providers` and `spot_providers_enabled_by_default` to `/options` response #### Upgrading a client from v1 to v2 API endpoints @@ -1926,11 +1927,11 @@ components: ServerStatus: type: object properties: - "software-version": + "software_version": type: string description: The version number of the software. example: "1.0.1" - "server-owner-callsign": + "server_owner_callsign": type: string description: The callsign of this server's operator. example: "M0TRT" diff --git a/static/js/alerts.js b/static/js/alerts.js index 4b40d81..eb1c001 100644 --- a/static/js/alerts.js +++ b/static/js/alerts.js @@ -27,7 +27,7 @@ function buildQueryString() { str = str + getQueryStringFor(fn) + "&"; } }); - str = str + "limit=" + $("#alerts-to-fetch option:selected").val(); + str = str + "limit=" + $("#alerts_to_fetch option:selected").val(); const maxDur = $("#max-duration option:selected").val(); if (maxDur !== "9999999999") { str = str + "&max_duration=" + maxDur; @@ -284,7 +284,7 @@ function loadOptions() { options = jsonData; // Populate the filters panel - generateMultiToggleFilterCard("#dx-continent-options", "dx_continent", options["continents"]); + generateMultiToggleFilterCard("#dx_continent_options", "dx_continent", options["continents"]); generateMultiToggleFilterCard("#source-options", "source", options["alert_providers"]); // Load URL params. These may select things from the various filter & display options, so the function needs @@ -294,7 +294,7 @@ function loadOptions() { // Load filters from settings storage loadSettings(); - setColorScheme($("#color-scheme option:selected").val()); + setColorScheme($("#color_scheme option:selected").val()); // Load alerts and set up the timer loadAlerts(); diff --git a/static/js/bands.js b/static/js/bands.js index 0b95aa6..74373f1 100644 --- a/static/js/bands.js +++ b/static/js/bands.js @@ -61,7 +61,7 @@ function startSSEConnection() { // Remove spots from the display that are older than the selected max age. function expireOldSpots() { - const maxAgeSeconds = parseInt($("#max-spot-age option:selected").val()); + const maxAgeSeconds = parseInt($("#max_spot_age option:selected").val()); const cutoff = (Date.now() / 1000) - maxAgeSeconds; const before = spots.length; spots = spots.filter(s => s["time"] && s["time"] >= cutoff); @@ -79,7 +79,7 @@ function buildQueryString() { str = str + getQueryStringFor(fn) + "&"; } }); - str = str + "max_age=" + $("#max-spot-age option:selected").val(); + str = str + "max_age=" + $("#max_spot_age option:selected").val(); // Additional filters for the bands view: No dupes, no QRT str = str + "&dedupe=true&allow_qrt=false"; return str; @@ -280,8 +280,8 @@ function loadOptions() { // First pass loading settings, so we can load the band colour scheme before the filters that need to use it loadSettings(); - setColorScheme($("#color-scheme option:selected").val()); - setBandColorScheme($("#band-color-scheme option:selected").val()); + setColorScheme($("#color_scheme option:selected").val()); + setBandColorScheme($("#band_color_scheme option:selected").val()); // Add CSS for band toggle buttons addBandToggleColourCSS(options["bands"]); @@ -289,10 +289,10 @@ function loadOptions() { // Populate the filters panel generateBandsMultiToggleFilterCard(options["bands"]); generateSIGsMultiToggleFilterCard(options["sigs"]); - generateMultiToggleFilterCard("#dx-continent-options", "dx_continent", options["continents"]); - generateMultiToggleFilterCard("#de-continent-options", "de_continent", options["continents"]); + generateMultiToggleFilterCard("#dx_continent_options", "dx_continent", options["continents"]); + generateMultiToggleFilterCard("#de_continent_options", "de_continent", options["continents"]); generateModesMultiToggleFilterCard(options["modes"]); - generateSourcesMultiToggleFilterCard(options["spot_providers"], options["spot-providers-enabled-by-default"]); + generateSourcesMultiToggleFilterCard(options["spot_providers"], options["spot_providers_enabled_by_default"]); // Load URL params. These may select things from the various filter & display options, so the function needs // to be called after these are set up, but if the URL params ask for "embedded mode", this will suppress diff --git a/static/js/common.js b/static/js/common.js index a881720..2161a90 100644 --- a/static/js/common.js +++ b/static/js/common.js @@ -61,11 +61,11 @@ function loadURLParams() { } // Handle other params - updateSelectFromParam(params, "color-scheme", "color-scheme"); - updateSelectFromParam(params, "time-zone", "time-zone"); // Only on Spots and Alerts pages - updateSelectFromParam(params, "limit", "spots-to-fetch"); // Only on Spots page - updateSelectFromParam(params, "limit", "alerts-to-fetch"); // Only on Alerts page - updateSelectFromParam(params, "max_age", "max-spot-age"); // Only on Map & Bands pages + updateSelectFromParam(params, "color_scheme", "color_scheme"); + updateSelectFromParam(params, "time_zone", "time_zone"); // Only on Spots and Alerts pages + updateSelectFromParam(params, "limit", "spots_to_fetch"); // Only on Spots page + updateSelectFromParam(params, "limit", "alerts_to_fetch"); // Only on Alerts page + updateSelectFromParam(params, "max_age", "max_spot_age"); // Only on Map & Bands pages updateFilterFromParam(params, "band", "band"); updateFilterFromParam(params, "sig", "sig"); updateFilterFromParam(params, "source", "source"); @@ -80,7 +80,7 @@ function updateSelectFromParam(params, paramName, selectID) { if (v != null) { $("#" + selectID).prop("value", v); // Extra check if this is the "color scheme" select - if (selectID === "color-scheme") { + if (selectID === "color_scheme") { setColorScheme(v); } } @@ -157,7 +157,7 @@ function columnsUpdated() { // Function to set the colour scheme based on the state of the UI select box function setColorSchemeFromUI() { - let theme = $("#color-scheme option:selected").val(); + let theme = $("#color_scheme option:selected").val(); if (theme !== "") { setColorScheme(theme); saveSettings(); @@ -168,7 +168,7 @@ function setColorSchemeFromUI() { function setColorScheme(mode) { let effectiveModeDark = mode === "dark"; if (mode === "auto") { - effectiveModeDark = window.matchMedia('(prefers-color-scheme: dark)').matches + effectiveModeDark = window.matchMedia('(prefers-color_scheme: dark)').matches } $("html").attr("data-bs-theme", effectiveModeDark ? "dark" : "light"); const metaThemeColor = document.querySelector("meta[name=theme-color]"); @@ -182,7 +182,7 @@ function usePreferredTheme() { // Work out if we have ever explicitly saved the value of our select box. If so, we set our colour scheme now based // on that. If not, we let the select stay with nothing selected, so that the server sets it to whatever the // server's default is when the options call is retrieved. - let val = localStorage.getItem("#color-scheme:value"); + let val = localStorage.getItem("#color_scheme:value"); if (val != null) { setColorScheme(JSON.parse(val)); } @@ -191,8 +191,8 @@ function usePreferredTheme() { // Sets up a listener on the OS light-dark theme change. If the Spothole user theme is set to Auto, the UI will be // updated, otherwise if the Spothole user theme is forced to light or dark, that preference will remain. function listenForOSThemeChange() { - window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { - setColorScheme($("#color-scheme option:selected").val()); + window.matchMedia('(prefers-color_scheme: dark)').addEventListener('change', () => { + setColorScheme($("#color_scheme option:selected").val()); }); } @@ -254,13 +254,13 @@ function closeDataPanel() { // provided the corresponding "enabled" checkbox is ticked. function getCredentialHeaders() { const headers = {}; - if ($("#qrz-enabled")[0] && $("#qrz-enabled")[0].checked) { + if ($("#qrz_enabled")[0] && $("#qrz_enabled")[0].checked) { const qrzUsername = $("#qrz-username").val(); const qrzPassword = $("#qrz-password").val(); if (qrzUsername) headers["X-QRZ-Username"] = qrzUsername; if (qrzPassword) headers["X-QRZ-Password"] = qrzPassword; } - if ($("#hamqth-enabled")[0] && $("#hamqth-enabled")[0].checked) { + if ($("#hamqth_enabled")[0] && $("#hamqth_enabled")[0].checked) { const hamqthUsername = $("#hamqth-username").val(); const hamqthPassword = $("#hamqth-password").val(); if (hamqthUsername) headers["X-HamQTH-Username"] = hamqthUsername; diff --git a/static/js/conditions.js b/static/js/conditions.js index 79c21a7..f5b5c6a 100644 --- a/static/js/conditions.js +++ b/static/js/conditions.js @@ -624,7 +624,7 @@ function renderDxStats() { if (!dxStatsData) { return; } - const deContinent = $('#dxstats-de-continent').val(); + const deContinent = $('#sxstats_de_continent').val(); const deData = dxStatsData[deContinent]; if (!deData) { return; diff --git a/static/js/map.js b/static/js/map.js index 6b3c6ca..8a25d81 100644 --- a/static/js/map.js +++ b/static/js/map.js @@ -108,7 +108,7 @@ function startSSEConnection() { // Remove spots from the map that are older than the selected max age. function expireOldSpots() { - const maxAgeSeconds = parseInt($("#max-spot-age option:selected").val()); + const maxAgeSeconds = parseInt($("#max_spot_age option:selected").val()); const cutoff = (Date.now() / 1000) - maxAgeSeconds; spots = spots.filter(function (s) { if (s["time"] && s["time"] < cutoff) { @@ -170,7 +170,7 @@ function buildQueryString() { str = str + getQueryStringFor(fn) + "&"; } }); - str = str + "max_age=" + $("#max-spot-age option:selected").val(); + str = str + "max_age=" + $("#max_spot_age option:selected").val(); // Additional filters for the map view: No dupes, no QRT, only spots with good locations str = str + "&dedupe=true&allow_qrt=false"; return str; @@ -307,8 +307,8 @@ function loadOptions() { // First pass loading settings, so we can load the band colour scheme before the filters that need to use it loadSettings(); - setColorScheme($("#color-scheme option:selected").val()); - setBandColorScheme($("#band-color-scheme option:selected").val()); + setColorScheme($("#color_scheme option:selected").val()); + setBandColorScheme($("#band_color_scheme option:selected").val()); // Add CSS for band toggle buttons addBandToggleColourCSS(options["bands"]); @@ -316,10 +316,10 @@ function loadOptions() { // Populate the filters panel generateBandsMultiToggleFilterCard(options["bands"]); generateSIGsMultiToggleFilterCard(options["sigs"]); - generateMultiToggleFilterCard("#dx-continent-options", "dx_continent", options["continents"]); - generateMultiToggleFilterCard("#de-continent-options", "de_continent", options["continents"]); + generateMultiToggleFilterCard("#dx_continent_options", "dx_continent", options["continents"]); + generateMultiToggleFilterCard("#de_continent_options", "de_continent", options["continents"]); generateModesMultiToggleFilterCard(options["modes"]); - generateSourcesMultiToggleFilterCard(options["spot_providers"], options["spot-providers-enabled-by-default"]); + generateSourcesMultiToggleFilterCard(options["spot_providers"], options["spot_providers_enabled_by_default"]); // Load URL params. These may select things from the various filter & display options, so the function needs // to be called after these are set up, but if the URL params ask for "embedded mode", this will suppress diff --git a/static/js/spots.js b/static/js/spots.js index 432c59c..4524f55 100644 --- a/static/js/spots.js +++ b/static/js/spots.js @@ -55,7 +55,7 @@ function startSSEConnection() { // Add spot to internal data store spots.unshift(newSpot); // Work out if we need to remove an old spot - if (spots.length > $("#spots-to-fetch option:selected").val()) { + if (spots.length > $("#spots_to_fetch option:selected").val()) { spots = spots.slice(0, -1); // Drop oldest spot off the end of the table. This is two rows because of the mobile view extra rows $("#table tbody tr").last().remove(); @@ -93,7 +93,7 @@ function buildQueryString() { str = str + getQueryStringFor(fn) + "&"; } }); - str = str + "limit=" + $("#spots-to-fetch option:selected").val(); + str = str + "limit=" + $("#spots_to_fetch option:selected").val(); if ($("#search").val() !== "") { str = str + "&text_includes=" + encodeURIComponent($("#search").val()); } @@ -421,8 +421,8 @@ function loadOptions() { // First pass loading settings, so we can load the band colour scheme before the filters that need to use it loadSettings(); - setColorScheme($("#color-scheme option:selected").val()); - setBandColorScheme($("#band-color-scheme option:selected").val()); + setColorScheme($("#color_scheme option:selected").val()); + setBandColorScheme($("#band_color_scheme option:selected").val()); // Add CSS for band toggle buttons addBandToggleColourCSS(options["bands"]); @@ -430,10 +430,10 @@ function loadOptions() { // Populate the filters panel generateBandsMultiToggleFilterCard(options["bands"]); generateSIGsMultiToggleFilterCard(options["sigs"]); - generateMultiToggleFilterCard("#dx-continent-options", "dx_continent", options["continents"]); - generateMultiToggleFilterCard("#de-continent-options", "de_continent", options["continents"]); + generateMultiToggleFilterCard("#dx_continent_options", "dx_continent", options["continents"]); + generateMultiToggleFilterCard("#de_continent_options", "de_continent", options["continents"]); generateModesMultiToggleFilterCard(options["modes"]); - generateSourcesMultiToggleFilterCard(options["spot_providers"], options["spot-providers-enabled-by-default"]); + generateSourcesMultiToggleFilterCard(options["spot_providers"], options["spot_providers_enabled_by_default"]); // Load URL params. These may select things from the various filter & display options, so the function needs // to be called after these are set up, but if the URL params ask for "embedded mode", this will suppress diff --git a/static/js/spotsbandsandmap.js b/static/js/spotsbandsandmap.js index fea6e0d..1509e52 100644 --- a/static/js/spotsbandsandmap.js +++ b/static/js/spotsbandsandmap.js @@ -102,7 +102,7 @@ function filtersUpdated() { // Function to update the band colour scheme in spots, bands and map pages function setBandColorSchemeFromUI() { - setBandColorScheme($("#band-color-scheme option:selected").val()); + setBandColorScheme($("#band_color_scheme option:selected").val()); saveSettings(); // Fudge a full reload because we need to update not just colours in the list/map/bands but also the filters window.location.reload(); diff --git a/static/js/status.js b/static/js/status.js index 87ab550..90f9c5e 100644 --- a/static/js/status.js +++ b/static/js/status.js @@ -1,8 +1,8 @@ // Load server status function loadStatus() { $.getJSON('/api/v2/status', function (jsonData) { - $("#software-version").text(jsonData["software-version"]); - $("#server-owner-callsign").text(jsonData["server-owner-callsign"]); + $("#software_version").text(jsonData["software_version"]); + $("#server_owner_callsign").text(jsonData["server_owner_callsign"]); $("#up-since").text(moment().subtract(jsonData["uptime"], 'seconds').fromNow()); $("#memory-use").text(jsonData["mem_use_mb"] + " MB"); $("#total-spots").text(jsonData["num_spots"]); @@ -13,7 +13,7 @@ function loadStatus() { $("#web-server-last-page").text(moment.unix(jsonData["webserver"]["last_page_access"]).utc().fromNow()); jsonData["spot_providers"].forEach(p => { - $("#spot-providers-status-container").append(` + $("#spot_providers-status-container").append(`
Spothole uses no trackers, no ads, and no cookies.
- {% if len(web_ui_options["support-button-html"]) > 0 %} + {% if len(web_ui_options["support_button_html"]) > 0 %}Caveat: The owner of this server has chosen to inject their own content into the "spots" page. This is designed for a "donate" or "support this server" button. The functionality of this injected content is the responsibility of the server owner, rather than the Spothole software.
diff --git a/templates/alerts.html b/templates/alerts.html index 4030d8a..430e1d4 100644 --- a/templates/alerts.html +++ b/templates/alerts.html @@ -4,18 +4,18 @@- {% module Template("widgets/color-scheme.html", web_ui_options=web_ui_options) %} + {% module Template("widgets/color_scheme.html", web_ui_options=web_ui_options) %}
- {% module Template("widgets/color-scheme.html", web_ui_options=web_ui_options) %} + {% module Template("widgets/color_scheme.html", web_ui_options=web_ui_options) %}
- {% module Template("widgets/band-color-scheme.html", web_ui_options=web_ui_options) %} + {% module Template("widgets/band_color_scheme.html", web_ui_options=web_ui_options) %}
Show up to - alerts diff --git a/templates/cards/number-of-spots.html b/templates/cards/number_of_spots.html similarity index 70% rename from templates/cards/number-of-spots.html rename to templates/cards/number_of_spots.html index 0e277b4..bda4234 100644 --- a/templates/cards/number-of-spots.html +++ b/templates/cards/number_of_spots.html @@ -2,11 +2,11 @@
Show up to - spots diff --git a/templates/cards/qrz.html b/templates/cards/qrz.html index 0df2d93..2b43f7e 100644 --- a/templates/cards/qrz.html +++ b/templates/cards/qrz.html @@ -3,9 +3,9 @@
Last - minutes diff --git a/templates/cards/table-columns-alerts.html b/templates/cards/table_columns_alerts.html similarity index 100% rename from templates/cards/table-columns-alerts.html rename to templates/cards/table_columns_alerts.html diff --git a/templates/cards/table-columns-spots.html b/templates/cards/table_columns_spots.html similarity index 100% rename from templates/cards/table-columns-spots.html rename to templates/cards/table_columns_spots.html diff --git a/templates/cards/time-zone.html b/templates/cards/time_zone.html similarity index 100% rename from templates/cards/time-zone.html rename to templates/cards/time_zone.html diff --git a/templates/cards/worked-calls.html b/templates/cards/worked_calls.html similarity index 100% rename from templates/cards/worked-calls.html rename to templates/cards/worked_calls.html diff --git a/templates/conditions.html b/templates/conditions.html index 4a19c4d..0092027 100644 --- a/templates/conditions.html +++ b/templates/conditions.html @@ -233,8 +233,8 @@