Replace dash-separated config & API properties with underscore-separated versions. #127

This commit is contained in:
Ian Renton
2026-08-10 20:55:18 +01:00
parent 5c86346074
commit c687202a61
54 changed files with 247 additions and 246 deletions
+40 -40
View File
@@ -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: ""
support_button_html: ""
+17 -17
View File
@@ -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):
+7 -7
View File
@@ -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()
+2 -2
View File
@@ -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"""
+14 -13
View File
@@ -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
+2 -2
View File
@@ -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. |
+3 -3
View File
@@ -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:
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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(
+3 -3
View File
@@ -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}).")
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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")
+1 -2
View File
@@ -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,
+5 -4
View File
@@ -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"
+3 -3
View File
@@ -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();
+7 -7
View File
@@ -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
+13 -13
View File
@@ -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;
+1 -1
View File
@@ -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;
+7 -7
View File
@@ -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
+7 -7
View File
@@ -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
+1 -1
View File
@@ -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();
+7 -7
View File
@@ -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(`
<div class="row row-cols-1 row-cols-md-4 g-4 mb-4 mb-md-2">
<div class="col"><strong>${p["name"]}</strong></div>
<div class="col">Status: ${p["status"]}</div>
@@ -23,7 +23,7 @@ function loadStatus() {
});
jsonData["alert_providers"].forEach(p => {
$("#alert-providers-status-container").append(`
$("#alert_providers-status-container").append(`
<div class="row row-cols-1 row-cols-md-4 g-4 mb-4 mb-md-2">
<div class="col"><strong>${p["name"]}</strong></div>
<div class="col">Status: ${p["status"]}</div>
@@ -41,7 +41,7 @@ function loadStatus() {
});
jsonData["static_data_providers"].forEach(p => {
$("#static-data-providers-status-container").append(`
$("#static_data_providers-status-container").append(`
<div class="row row-cols-1 row-cols-md-4 g-4 mb-4 mb-md-2">
<div class="col"><strong>${p["name"]}</strong></div>
<div class="col">Status: ${p["status"]}</div>
@@ -50,7 +50,7 @@ function loadStatus() {
});
jsonData["sig_ref_data_providers"].forEach(p => {
$("#sig-ref-data-providers-status-container").append(`
$("#sig_ref_data_providers-status-container").append(`
<div class="row row-cols-1 row-cols-md-4 g-4 mb-4 mb-md-2">
<div class="col"><strong>${p["sig_name"]}</strong></div>
<div class="col">Status: ${p["status"]}</div>
@@ -60,7 +60,7 @@ function loadStatus() {
});
jsonData["callsign_data_providers"].forEach(p => {
$("#callsign-data-providers-status-container").append(`
$("#callsign_data_providers-status-container").append(`
<div class="row row-cols-1 row-cols-md-4 g-4 mb-4 mb-md-2">
<div class="col"><strong>${p["name"]}</strong></div>
<div class="col">Status: ${p["status"]}</div>
+1 -1
View File
@@ -188,7 +188,7 @@
Spothole via HTTPS so are protected in transit, though of course you do have to trust Spothole with this
sensitive data in order to use this feature.</p>
<p>Spothole uses no trackers, no ads, and no cookies.</p>
{% if len(web_ui_options["support-button-html"]) > 0 %}
{% if len(web_ui_options["support_button_html"]) > 0 %}
<p><strong>Caveat: </strong> 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.</p>
+14 -14
View File
@@ -4,18 +4,18 @@
<div class="mt-3">
<div id="settingsButtonRow" class="row mb-3">
<div class="col-auto me-auto pt-3">
{% module Template("widgets/refresh-timer.html", web_ui_options=web_ui_options) %}
{% module Template("widgets/refresh_timer.html", web_ui_options=web_ui_options) %}
</div>
<div class="col-auto">
<div class="d-inline-flex gap-1">
{% module Template("widgets/filters-display-data-buttons.html", web_ui_options=web_ui_options,
show_data_button=web_ui_options["qrz-enabled"] or web_ui_options["hamqth-enabled"]) %}
{% module Template("widgets/filters_display_data_buttons.html", web_ui_options=web_ui_options,
show_data_button=web_ui_options["qrz_enabled"] or web_ui_options["hamqth_enabled"]) %}
</div>
</div>
</div>
<div id="filters-area" class="appearing-panel card mb-3">
{% module Template("widgets/filters-area-header.html", web_ui_options=web_ui_options) %}
{% module Template("widgets/filters_area_header.html", web_ui_options=web_ui_options) %}
<div class="card-body">
<div class="row row-cols-1 row-cols-md-3 g-4">
<div class="col">
@@ -25,43 +25,43 @@
{% module Template("cards/sources.html", web_ui_options=web_ui_options) %}
</div>
<div class="col">
{% module Template("cards/duration-limit-alerts.html", web_ui_options=web_ui_options) %}
{% module Template("cards/duration_limit_alerts.html", web_ui_options=web_ui_options) %}
</div>
</div>
</div>
</div>
<div id="display-area" class="appearing-panel card mb-3">
{% module Template("widgets/display-area-header.html", web_ui_options=web_ui_options) %}
{% module Template("widgets/display_area_header.html", web_ui_options=web_ui_options) %}
<div class="card-body">
<div id="display-container" class="row row-cols-1 row-cols-md-4 g-4">
<div class="col">
{% module Template("cards/time-zone.html", web_ui_options=web_ui_options) %}
{% module Template("cards/time_zone.html", web_ui_options=web_ui_options) %}
</div>
<div class="col">
{% module Template("cards/number-of-alerts.html", web_ui_options=web_ui_options) %}
{% module Template("cards/number_of_alerts.html", web_ui_options=web_ui_options) %}
</div>
<div class="col">
{% module Template("cards/color-scheme.html", web_ui_options=web_ui_options) %}
{% module Template("cards/color_scheme.html", web_ui_options=web_ui_options) %}
</div>
<div class="col">
{% module Template("cards/table-columns-alerts.html", web_ui_options=web_ui_options) %}
{% module Template("cards/table_columns_alerts.html", web_ui_options=web_ui_options) %}
</div>
</div>
</div>
</div>
{% if web_ui_options["qrz-enabled"] or web_ui_options["hamqth-enabled"] %}
{% if web_ui_options["qrz_enabled"] or web_ui_options["hamqth_enabled"] %}
<div id="data-area" class="appearing-panel card mb-3">
{% module Template("widgets/data-area-header.html", web_ui_options=web_ui_options) %}
{% module Template("widgets/data_area_header.html", web_ui_options=web_ui_options) %}
<div class="card-body">
<div class="row row-cols-1 row-cols-md-4 g-4">
{% if web_ui_options["qrz-enabled"] %}
{% if web_ui_options["qrz_enabled"] %}
<div class="col">
{% module Template("cards/qrz.html", web_ui_options=web_ui_options) %}
</div>
{% end %}
{% if web_ui_options["hamqth-enabled"] %}
{% if web_ui_options["hamqth_enabled"] %}
<div class="col">
{% module Template("cards/hamqth.html", web_ui_options=web_ui_options) %}
</div>
+11 -11
View File
@@ -6,14 +6,14 @@
<div class="col-auto me-auto pt-3"></div>
<div class="col-auto">
<div class="d-inline-flex gap-1">
{% module Template("widgets/filters-display-data-buttons.html", web_ui_options=web_ui_options,
show_data_button=web_ui_options["qrz-enabled"] or web_ui_options["hamqth-enabled"]) %}
{% module Template("widgets/filters_display_data_buttons.html", web_ui_options=web_ui_options,
show_data_button=web_ui_options["qrz_enabled"] or web_ui_options["hamqth_enabled"]) %}
</div>
</div>
</div>
<div id="filters-area" class="appearing-panel card mb-3">
{% module Template("widgets/filters-area-header.html", web_ui_options=web_ui_options) %}
{% module Template("widgets/filters_area_header.html", web_ui_options=web_ui_options) %}
<div class="card-body">
<div class="row row-cols-1 g-4 mb-4 row-cols-md-3">
<div class="col">
@@ -31,7 +31,7 @@
{% module Template("cards/dx-continent.html", web_ui_options=web_ui_options) %}
</div>
<div class="col">
{% module Template("cards/de-continent.html", web_ui_options=web_ui_options) %}
{% module Template("cards/de_continent.html", web_ui_options=web_ui_options) %}
</div>
<div class="col">
{% module Template("cards/modes.html", web_ui_options=web_ui_options) %}
@@ -41,31 +41,31 @@
</div>
<div id="display-area" class="appearing-panel card mb-3">
{% module Template("widgets/display-area-header.html", web_ui_options=web_ui_options) %}
{% module Template("widgets/display_area_header.html", web_ui_options=web_ui_options) %}
<div class="card-body">
<div id="display-container" class="row row-cols-1 row-cols-md-4 g-4">
<div class="col">
{% module Template("cards/spot-age.html", web_ui_options=web_ui_options) %}
{% module Template("cards/spot_age.html", web_ui_options=web_ui_options) %}
</div>
<div class="col">
{% module Template("cards/color-scheme-and-band-color-scheme.html", web_ui_options=web_ui_options)
{% module Template("cards/color_scheme_and_band_color_scheme.html", web_ui_options=web_ui_options)
%}
</div>
</div>
</div>
</div>
{% if web_ui_options["qrz-enabled"] or web_ui_options["hamqth-enabled"] %}
{% if web_ui_options["qrz_enabled"] or web_ui_options["hamqth_enabled"] %}
<div id="data-area" class="appearing-panel card mb-3">
{% module Template("widgets/data-area-header.html", web_ui_options=web_ui_options) %}
{% module Template("widgets/data_area_header.html", web_ui_options=web_ui_options) %}
<div class="card-body">
<div class="row row-cols-1 row-cols-md-4 g-4">
{% if web_ui_options["qrz-enabled"] %}
{% if web_ui_options["qrz_enabled"] %}
<div class="col">
{% module Template("cards/qrz.html", web_ui_options=web_ui_options) %}
</div>
{% end %}
{% if web_ui_options["hamqth-enabled"] %}
{% if web_ui_options["hamqth_enabled"] %}
<div class="col">
{% module Template("cards/hamqth.html", web_ui_options=web_ui_options) %}
</div>
@@ -2,7 +2,7 @@
<div class="card-body">
<h5 class="card-title">Theme</h5>
<p class="card-text spothole-card-text">
{% module Template("widgets/color-scheme.html", web_ui_options=web_ui_options) %}
{% module Template("widgets/color_scheme.html", web_ui_options=web_ui_options) %}
</p>
</div>
</div>
@@ -2,10 +2,10 @@
<div class="card-body">
<h5 class="card-title">Theme</h5>
<p class="card-text spothole-card-text">
{% module Template("widgets/color-scheme.html", web_ui_options=web_ui_options) %}
{% module Template("widgets/color_scheme.html", web_ui_options=web_ui_options) %}
</p>
<p class="card-text spothole-card-text">
{% 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) %}
</p>
</div>
</div>
@@ -1,6 +1,6 @@
<div class="card">
<div class="card-body">
<h5 class="card-title">DE Continent</h5>
<div id="de-continent-options" class="card-text spothole-card-text"></div>
<div id="de_continent_options" class="card-text spothole-card-text"></div>
</div>
</div>
@@ -1,6 +1,6 @@
<div class="card">
<div class="card-body">
<h5 class="card-title">DX Continent</h5>
<div id="dx-continent-options" class="card-text spothole-card-text"></div>
<div id="dx_continent_options" class="card-text spothole-card-text"></div>
</div>
</div>
+2 -2
View File
@@ -3,9 +3,9 @@
<h5 class="card-title">HamQTH</h5>
<div class="card-text spothole-card-text">
<div class="form-check mb-2">
<input type="checkbox" class="storeable-checkbox form-check-input" id="hamqth-enabled"
<input type="checkbox" class="storeable-checkbox form-check-input" id="hamqth_enabled"
onchange="saveSettings();">
<label for="hamqth-enabled" class="form-check-label">Use data from HamQTH</label>
<label for="hamqth_enabled" class="form-check-label">Use data from HamQTH</label>
</div>
<div class="mb-2">
<input type="text" class="storeable-text form-control" id="hamqth-username"
@@ -2,11 +2,11 @@
<div class="card-body">
<h5 class="card-title">Number of Alerts</h5>
<p class="card-text spothole-card-text">Show up to
<select id="alerts-to-fetch" class="storeable-select form-select ms-2 me-2" oninput="filtersUpdated();"
<select id="alerts_to_fetch" class="storeable-select form-select ms-2 me-2" oninput="filtersUpdated();"
style="width: 5em;display: inline-block;">
{% for c in web_ui_options["alert-count"] %}
{% for c in web_ui_options["alert_count"] %}
<option value="{{c}}" {% if web_ui_options[
"alert-count-default"] == c %}selected{% end %}>{{c}}</option>
"alert_count_default"] == c %}selected{% end %}>{{c}}</option>
{% end %}
</select>
alerts
@@ -2,11 +2,11 @@
<div class="card-body">
<h5 class="card-title">Number of Spots</h5>
<p class="card-text spothole-card-text">Show up to
<select id="spots-to-fetch" class="storeable-select form-select ms-2 me-2 d-inline-block"
<select id="spots_to_fetch" class="storeable-select form-select ms-2 me-2 d-inline-block"
oninput="filtersUpdated();" style="width: 5em; display: inline-block;">
{% for c in web_ui_options["spot-count"] %}
{% for c in web_ui_options["spot_count"] %}
<option value="{{c}}" {% if web_ui_options[
"spot-count-default"] == c %}selected{% end %}>{{c}}</option>
"spot_count_default"] == c %}selected{% end %}>{{c}}</option>
{% end %}
</select>
spots
+2 -2
View File
@@ -3,9 +3,9 @@
<h5 class="card-title">QRZ.com</h5>
<div class="card-text spothole-card-text">
<div class="form-check mb-2">
<input type="checkbox" class="storeable-checkbox form-check-input" id="qrz-enabled"
<input type="checkbox" class="storeable-checkbox form-check-input" id="qrz_enabled"
onchange="saveSettings();">
<label for="qrz-enabled" class="form-check-label">Use data from QRZ.com</label>
<label for="qrz_enabled" class="form-check-label">Use data from QRZ.com</label>
</div>
<div class="mb-2">
<input type="text" class="storeable-text form-control" id="qrz-username"
@@ -2,11 +2,11 @@
<div class="card-body">
<h5 class="card-title">Spot Age</h5>
<p class="card-text spothole-card-text">Last
<select id="max-spot-age" class="storeable-select form-select ms-2 me-2 d-inline-block"
<select id="max_spot_age" class="storeable-select form-select ms-2 me-2 d-inline-block"
oninput="filtersUpdated();" style="width: 5em; display: inline-block;">
{% for a in web_ui_options["max-spot-age"] %}
{% for a in web_ui_options["max_spot_age"] %}
<option value="{{a*60}}" {% if web_ui_options[
"max-spot-age-default"] == a*60 %}selected{% end %}>{{a}}</option>
"max_spot_age_default"] == a*60 %}selected{% end %}>{{a}}</option>
{% end %}
</select>
minutes
+2 -2
View File
@@ -233,8 +233,8 @@
</div>
<div class="card-body">
<div class="mb-3">
<label for="dxstats-de-continent" class="form-label">Your continent:</label>
<select id="dxstats-de-continent" class="form-select storeable-select d-inline-block ms-2 w-auto"
<label for="sxstats_de_continent" class="form-label">Your continent:</label>
<select id="sxstats_de_continent" class="form-select storeable-select d-inline-block ms-2 w-auto"
oninput="dxStatsContientChanged();">
<option value="EU">Europe</option>
<option value="NA">North America</option>
+12 -12
View File
@@ -20,14 +20,14 @@
<div class="col-auto me-auto pt-3"></div>
<div class="col-auto">
<div class="d-inline-flex gap-1">
{% module Template("widgets/filters-display-data-buttons.html", web_ui_options=web_ui_options,
show_data_button=web_ui_options["qrz-enabled"] or web_ui_options["hamqth-enabled"]) %}
{% module Template("widgets/filters_display_data_buttons.html", web_ui_options=web_ui_options,
show_data_button=web_ui_options["qrz_enabled"] or web_ui_options["hamqth_enabled"]) %}
</div>
</div>
</div>
<div id="filters-area" class="appearing-panel card mb-3">
{% module Template("widgets/filters-area-header.html", web_ui_options=web_ui_options) %}
{% module Template("widgets/filters_area_header.html", web_ui_options=web_ui_options) %}
<div class="card-body">
<div class="row row-cols-1 g-4 mb-4 row-cols-md-3">
<div class="col">
@@ -45,7 +45,7 @@
{% module Template("cards/dx-continent.html", web_ui_options=web_ui_options) %}
</div>
<div class="col">
{% module Template("cards/de-continent.html", web_ui_options=web_ui_options) %}
{% module Template("cards/de_continent.html", web_ui_options=web_ui_options) %}
</div>
<div class="col">
{% module Template("cards/modes.html", web_ui_options=web_ui_options) %}
@@ -55,37 +55,37 @@
</div>
<div id="display-area" class="appearing-panel card mb-3">
{% module Template("widgets/display-area-header.html", web_ui_options=web_ui_options) %}
{% module Template("widgets/display_area_header.html", web_ui_options=web_ui_options) %}
<div class="card-body">
<div id="display-container" class="row row-cols-1 row-cols-md-4 g-4">
<div class="col">
{% module Template("cards/spot-age.html", web_ui_options=web_ui_options) %}
{% module Template("cards/spot_age.html", web_ui_options=web_ui_options) %}
</div>
<div class="col">
{% module Template("cards/basemap.html", web_ui_options=web_ui_options) %}
</div>
<div class="col">
{% module Template("cards/map-features.html", web_ui_options=web_ui_options) %}
{% module Template("cards/map_features.html", web_ui_options=web_ui_options) %}
</div>
<div class="col">
{% module Template("cards/color-scheme-and-band-color-scheme.html",
{% module Template("cards/color_scheme_and_band_color_scheme.html",
web_ui_options=web_ui_options) %}
</div>
</div>
</div>
</div>
{% if web_ui_options["qrz-enabled"] or web_ui_options["hamqth-enabled"] %}
{% if web_ui_options["qrz_enabled"] or web_ui_options["hamqth_enabled"] %}
<div id="data-area" class="appearing-panel card mb-3">
{% module Template("widgets/data-area-header.html", web_ui_options=web_ui_options) %}
{% module Template("widgets/data_area_header.html", web_ui_options=web_ui_options) %}
<div class="card-body">
<div class="row row-cols-1 row-cols-md-4 g-4">
{% if web_ui_options["qrz-enabled"] %}
{% if web_ui_options["qrz_enabled"] %}
<div class="col">
{% module Template("cards/qrz.html", web_ui_options=web_ui_options) %}
</div>
{% end %}
{% if web_ui_options["hamqth-enabled"] %}
{% if web_ui_options["hamqth_enabled"] %}
<div class="col">
{% module Template("cards/hamqth.html", web_ui_options=web_ui_options) %}
</div>
+1 -1
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="color-scheme" content="light dark">
<meta name="color_scheme" content="light dark">
<meta name="theme-color" content="white"/>
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
+15 -15
View File
@@ -18,21 +18,21 @@
<div id="settingsButtonRow" class="row mb-3">
<div class="col-md-4 mb-3 mb-md-0">
<div class="d-inline-flex gap-3">
{% module Template("widgets/run-pause.html", web_ui_options=web_ui_options) %}
<div class="d-inline-flex">{% raw web_ui_options["support-button-html"] %}</div>
{% module Template("widgets/run_pause.html", web_ui_options=web_ui_options) %}
<div class="d-inline-flex">{% raw web_ui_options["support_button_html"] %}</div>
</div>
</div>
<div class="col-md-8 text-end">
<div class="d-inline-flex gap-3">
{% module Template("widgets/search.html", web_ui_options=web_ui_options) %}
{% module Template("widgets/filters-display-data-buttons.html", web_ui_options=web_ui_options,
show_data_button=web_ui_options["qrz-enabled"] or web_ui_options["hamqth-enabled"]) %}
{% module Template("widgets/filters_display_data_buttons.html", web_ui_options=web_ui_options,
show_data_button=web_ui_options["qrz_enabled"] or web_ui_options["hamqth_enabled"]) %}
</div>
</div>
</div>
<div id="filters-area" class="appearing-panel card mb-3">
{% module Template("widgets/filters-area-header.html", web_ui_options=web_ui_options) %}
{% module Template("widgets/filters_area_header.html", web_ui_options=web_ui_options) %}
<div class="card-body">
<div class="row row-cols-1 g-4 mb-4 row-cols-md-3">
<div class="col">
@@ -50,7 +50,7 @@
{% module Template("cards/dx-continent.html", web_ui_options=web_ui_options) %}
</div>
<div class="col">
{% module Template("cards/de-continent.html", web_ui_options=web_ui_options) %}
{% module Template("cards/de_continent.html", web_ui_options=web_ui_options) %}
</div>
<div class="col">
{% module Template("cards/modes.html", web_ui_options=web_ui_options) %}
@@ -60,21 +60,21 @@
</div>
<div id="display-area" class="appearing-panel card mb-3">
{% module Template("widgets/display-area-header.html", web_ui_options=web_ui_options) %}
{% module Template("widgets/display_area_header.html", web_ui_options=web_ui_options) %}
<div class="card-body">
<div id="display-container" class="row row-cols-1 row-cols-md-4 g-4">
<div class="col">
{% module Template("cards/time-zone.html", web_ui_options=web_ui_options) %}
{% module Template("cards/time_zone.html", web_ui_options=web_ui_options) %}
</div>
<div class="col">
{% module Template("cards/number-of-spots.html", web_ui_options=web_ui_options) %}
{% module Template("cards/number_of_spots.html", web_ui_options=web_ui_options) %}
</div>
<div class="col">
{% module Template("cards/color-scheme-and-band-color-scheme.html", web_ui_options=web_ui_options)
{% module Template("cards/color_scheme_and_band_color_scheme.html", web_ui_options=web_ui_options)
%}
</div>
<div class="col">
{% module Template("cards/table-columns-spots.html", web_ui_options=web_ui_options) %}
{% module Template("cards/table_columns_spots.html", web_ui_options=web_ui_options) %}
</div>
<div class="col">
{% module Template("cards/audio.html", web_ui_options=web_ui_options) %}
@@ -84,15 +84,15 @@
</div>
<div id="data-area" class="appearing-panel card mb-3">
{% module Template("widgets/data-area-header.html", web_ui_options=web_ui_options) %}
{% module Template("widgets/data_area_header.html", web_ui_options=web_ui_options) %}
<div class="card-body">
<div class="row row-cols-1 row-cols-md-4 g-4">
{% if web_ui_options["qrz-enabled"] %}
{% if web_ui_options["qrz_enabled"] %}
<div class="col">
{% module Template("cards/qrz.html", web_ui_options=web_ui_options) %}
</div>
{% end %}
{% if web_ui_options["hamqth-enabled"] %}
{% if web_ui_options["hamqth_enabled"] %}
<div class="col">
{% module Template("cards/hamqth.html", web_ui_options=web_ui_options) %}
</div>
@@ -101,7 +101,7 @@
{% module Template("cards/location.html", web_ui_options=web_ui_options) %}
</div>
<div class="col">
{% module Template("cards/worked-calls.html", web_ui_options=web_ui_options) %}
{% module Template("cards/worked_calls.html", web_ui_options=web_ui_options) %}
</div>
</div>
</div>
+7 -7
View File
@@ -8,8 +8,8 @@
<div class="card-body">
<div class="row row-cols-1 row-cols-md-4 g-4 mb-4 mb-md-2">
<div class="col"><strong>Metadata</strong></div>
<div class="col">Software Version: <span id="software-version"></span></div>
<div class="col">Owner Callsign: <span id="server-owner-callsign"></span></div>
<div class="col">Software Version: <span id="software_version"></span></div>
<div class="col">Owner Callsign: <span id="server_owner_callsign"></span></div>
<div class="col">Up since: <span id="up-since"></span></div>
</div>
<div class="row row-cols-1 row-cols-md-4 g-4 mb-4 mb-md-2">
@@ -31,7 +31,7 @@
<div class="card-header">
Spot Providers
</div>
<div class="card-body" id="spot-providers-status-container">
<div class="card-body" id="spot_providers-status-container">
</div>
</div>
@@ -40,7 +40,7 @@
<div class="card-header">
Alert Providers
</div>
<div class="card-body" id="alert-providers-status-container">
<div class="card-body" id="alert_providers-status-container">
</div>
</div>
@@ -58,7 +58,7 @@
<div class="card-header">
Static Reference Data Providers
</div>
<div class="card-body" id="static-data-providers-status-container">
<div class="card-body" id="static_data_providers-status-container">
</div>
</div>
@@ -67,7 +67,7 @@
<div class="card-header">
SIG Reference Data Providers
</div>
<div class="card-body" id="sig-ref-data-providers-status-container">
<div class="card-body" id="sig_ref_data_providers-status-container">
</div>
</div>
@@ -76,7 +76,7 @@
<div class="card-header">
Callsign Data Providers
</div>
<div class="card-body" id="callsign-data-providers-status-container">
<div class="card-body" id="callsign_data_providers-status-container">
</div>
</div>
@@ -1,24 +1,24 @@
<label class="form-check-label form-label" for="band-color-scheme">Band color scheme</label><br/>
<select id="band-color-scheme" class="storeable-select form-select d-inline-block" oninput="setBandColorSchemeFromUI();"
<label class="form-check-label form-label" for="band_color_scheme">Band color scheme</label><br/>
<select id="band_color_scheme" class="storeable-select form-select d-inline-block" oninput="setBandColorSchemeFromUI();"
style="display: inline-block;">
<option value="PSK Reporter" {% if web_ui_options[
"band-color-scheme-default"] == "PSK Reporter" %}selected{% end %}>PSK Reporter</option>
"band_color_scheme_default"] == "PSK Reporter" %}selected{% end %}>PSK Reporter</option>
<option value="PSK Reporter (Adjusted)" {% if web_ui_options[
"band-color-scheme-default"] == "PSK Reporter (Adjusted)" %}selected{% end %}>PSK Reporter (Adjusted)</option>
"band_color_scheme_default"] == "PSK Reporter (Adjusted)" %}selected{% end %}>PSK Reporter (Adjusted)</option>
<option value="RBN" {% if web_ui_options[
"band-color-scheme-default"] == "RBN" %}selected{% end %}>RBN</option>
"band_color_scheme_default"] == "RBN" %}selected{% end %}>RBN</option>
<option value="Ham Rainbow" {% if web_ui_options[
"band-color-scheme-default"] == "Ham Rainbow" %}selected{% end %}>Ham Rainbow</option>
"band_color_scheme_default"] == "Ham Rainbow" %}selected{% end %}>Ham Rainbow</option>
<option value="Ham Rainbow (Reverse)" {% if web_ui_options[
"band-color-scheme-default"] == "Ham Rainbow (Reverse)" %}selected{% end %}>Ham Rainbow (Reverse)</option>
"band_color_scheme_default"] == "Ham Rainbow (Reverse)" %}selected{% end %}>Ham Rainbow (Reverse)</option>
<option value="Kate Morley" {% if web_ui_options[
"band-color-scheme-default"] == "Kate Morley" %}selected{% end %}>Kate Morley</option>
"band_color_scheme_default"] == "Kate Morley" %}selected{% end %}>Kate Morley</option>
<option value="ColorBrewer" {% if web_ui_options[
"band-color-scheme-default"] == "ColorBrewer" %}selected{% end %}>ColorBrewer</option>
"band_color_scheme_default"] == "ColorBrewer" %}selected{% end %}>ColorBrewer</option>
<option value="IWantHue" {% if web_ui_options[
"band-color-scheme-default"] == "IWantHue" %}selected{% end %}>IWantHue</option>
"band_color_scheme_default"] == "IWantHue" %}selected{% end %}>IWantHue</option>
<option value="IWantHue (Color Blind)" {% if web_ui_options[
"band-color-scheme-default"] == "IWantHue (Color Blind)" %}selected{% end %}>IWantHue (Color Blind)</option>
"band_color_scheme_default"] == "IWantHue (Color Blind)" %}selected{% end %}>IWantHue (Color Blind)</option>
<option value="Mokole" {% if web_ui_options[
"band-color-scheme-default"] == "Mokole" %}selected{% end %}>Mokole</option>
"band_color_scheme_default"] == "Mokole" %}selected{% end %}>Mokole</option>
</select>
-10
View File
@@ -1,10 +0,0 @@
<label class="form-check-label form-label" for="color-scheme">UI color scheme</label>
<select id="color-scheme" class="storeable-select form-select d-inline-block" oninput="setColorSchemeFromUI();"
style="display: inline-block;">
<option value="auto" {% if web_ui_options[
"color-scheme-default"] == "auto" %}selected{% end %}>Automatic</option>
<option value="light" {% if web_ui_options[
"color-scheme-default"] == "light" %}selected{% end %}>Light</option>
<option value="dark" {% if web_ui_options[
"color-scheme-default"] == "dark" %}selected{% end %}>Dark</option>
</select>
+10
View File
@@ -0,0 +1,10 @@
<label class="form-check-label form-label" for="color_scheme">UI color scheme</label>
<select id="color_scheme" class="storeable-select form-select d-inline-block" oninput="setColorSchemeFromUI();"
style="display: inline-block;">
<option value="auto" {% if web_ui_options[
"color_scheme_default"] == "auto" %}selected{% end %}>Automatic</option>
<option value="light" {% if web_ui_options[
"color_scheme_default"] == "light" %}selected{% end %}>Light</option>
<option value="dark" {% if web_ui_options[
"color_scheme_default"] == "dark" %}selected{% end %}>Dark</option>
</select>