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 # 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 # 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. # 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 # 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 # 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 # 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. # 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. # 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. # 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. # 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 # 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. # 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. # Feel free to write your own provider classes! There are details in the README.
spot-providers: spot_providers:
- class: "POTA" - class: "POTA"
enabled: true enabled: true
@@ -39,7 +39,7 @@ spot-providers:
- class: "GMA" - class: "GMA"
enabled: true enabled: true
# GMA requires an API key to fetch spots. After creating an account on cqgma.org, email support and request one. # 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" - class: "HEMA"
enabled: true 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. # 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 # 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, # 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. # set enabled to true, but enabled_by_default_in_web_ui to false.
enabled-by-default-in-web-ui: false enabled_by_default_in_web_ui: false
- class: "RBN" - class: "RBN"
name: "RBN FT8" name: "RBN FT8"
enabled: false enabled: false
port: 7001 port: 7001
enabled-by-default-in-web-ui: false enabled_by_default_in_web_ui: false
- class: "UKPacketNet" - class: "UKPacketNet"
enabled: false enabled: false
enabled-by-default-in-web-ui: false enabled_by_default_in_web_ui: false
- class: "XOTA" - class: "XOTA"
# xOTA sources have a "name" property so you can define what programme it is for, since xOTA is generic software # 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: # 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 # 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!) # but refers to a toilet in a different building (or continent!)
sig-ref-prefix: "C3" sig_ref_prefix: "C3"
- class: "XOTA" - class: "XOTA"
name: "EH23 TOTA" name: "EH23 TOTA"
enabled: false enabled: false
url: "wss://eh23.totawatch.de/api/spot/live" url: "wss://eh23.totawatch.de/api/spot/live"
sig: "Toilets" sig: "Toilets"
sig-ref-prefix: "EH" sig_ref_prefix: "EH"
- class: "XOTA" - class: "XOTA"
name: "HOPE26 TOTA" name: "HOPE26 TOTA"
enabled: false enabled: false
url: "wss://hope-26.totawatch.de/api/spot/live" url: "wss://hope-26.totawatch.de/api/spot/live"
sig: "Toilets" sig: "Toilets"
sig-ref-prefix: "HOPE" sig_ref_prefix: "HOPE"
# Alert providers to use. Same setup as the spot providers list above. # Alert providers to use. Same setup as the spot providers list above.
alert-providers: alert_providers:
- class: "POTA" - class: "POTA"
enabled: true 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 # 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. # conditions, etc.) and make it available via the /api/v2/solar endpoint.
solar-condition-providers: solar_condition_providers:
- class: "HamQSL" - class: "HamQSL"
enabled: true 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 # Static reference data providers to use. These allow Spothole to download data such as mapping between callsign
# prefixes and DXCC entities. # prefixes and DXCC entities.
static-data-providers: static_data_providers:
- class: "K0SWE" - class: "K0SWE"
enabled: true 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 # 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. # park IDs to their name and location.
sig-ref-data-providers: sig_ref_data_providers:
- class: "POTA" - class: "POTA"
enabled: true 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 # 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 # 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. # 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" - class: "QRZ"
enabled: true enabled: true
# Callsign data providers can often provide conflicting data, e.g. a home location from QRZ vs the centre of the # 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 priority: 3
# API key for Clublog to look up information. Required in order to enable this provider. Unlike QRZ and HamQTH, # 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. # Clublog uses an API key issued to Spothole, not to the end user.
api-key: "" api_key: ""
- class: "ClublogXML" - class: "ClublogXML"
enabled: true enabled: true
priority: 4 priority: 4
# API key for Clublog to look up information. Required in order to enable this provider. You will need to request # 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. # one via their helpdesk portal if you want to use callsign lookups from Clublog.
api-key: "" api_key: ""
- class: "CountryFiles" - class: "CountryFiles"
priority: 5 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 # Maximum time to keep spots and alerts in the system before deleting them. By default, one hour for spots and one week
# for alerts. # for alerts.
max-spot-age-sec: 3600 max_spot_age_sec: 3600
max-alert-age-sec: 604800 max_alert_age_sec: 604800
# Allow submitting spots to the Spothole API? # 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? # 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. # 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. # 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 # 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, # 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. # and of course their user solves the CAPTCHA.
# You can sign up for reCAPTCHA at https://www.google.com/recaptcha/ # You can sign up for reCAPTCHA at https://www.google.com/recaptcha/
recaptcha-site-key: "" recaptcha_site_key: ""
recaptcha-secret-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 # 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. # 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 # Minimum severity of log statements to print to the system log. Defaults to INFO, change to DEBUG if you need more
# information. # information.
log-level: INFO log_level: INFO
# Options for the web UI. # Options for the web UI.
web-ui-options: web_ui_options:
spot-count: [ 10, 25, 50, 100 ] spot_count: [ 10, 25, 50, 100 ]
spot-count-default: 50 spot_count_default: 50
max-spot-age: [ 5, 10, 30, 60 ] max_spot_age: [ 5, 10, 30, 60 ]
max-spot-age-default: 30 max_spot_age_default: 30
alert-count: [ 25, 50, 100, 200, 500 ] alert_count: [ 25, 50, 100, 200, 500 ]
alert-count-default: 100 alert_count_default: 100
# Default UI colour scheme. Supported values are "light", "dark" and "auto" (i.e. use the browser/OS colour scheme). # 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. # 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. # 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. # 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 # 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 # (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. # 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! # 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) config = yaml.safe_load(f)
logging.info("Loaded config.") logging.info("Loaded config.")
BASE_URL = config.get("base-url", "http://localhost:8080") BASE_URL = config.get("base_url", "http://localhost:8080")
MAX_SPOT_AGE = config.get("max-spot-age-sec", 3600) MAX_SPOT_AGE = config.get("max_spot_age_sec", 3600)
MAX_ALERT_AGE = config.get("max-alert-age-sec", 604800) MAX_ALERT_AGE = config.get("max_alert_age_sec", 604800)
SERVER_OWNER_CALLSIGN = config.get("server-owner-callsign", "N0CALL") SERVER_OWNER_CALLSIGN = config.get("server_owner_callsign", "N0CALL")
WEB_SERVER_PORT = config.get("web-server-port", 8080) WEB_SERVER_PORT = config.get("web_server_port", 8080)
ALLOW_SPOTTING = config.get("allow-spotting", True) ALLOW_SPOTTING = config.get("allow_spotting", True)
ALLOW_UPSTREAM_SPOTTING = config.get("allow-upstream-spotting", True) ALLOW_UPSTREAM_SPOTTING = config.get("allow_upstream_spotting", True)
WEB_UI_OPTIONS = config.get("web-ui-options", {}) WEB_UI_OPTIONS = config.get("web_ui_options", {})
API_ONLY_MODE = config.get("api-only-mode", False) API_ONLY_MODE = config.get("api_only_mode", False)
RECAPTCHA_SECRET_KEY = config.get("recaptcha-secret-key", "") RECAPTCHA_SECRET_KEY = config.get("recaptcha_secret_key", "")
RECAPTCHA_SITE_KEY = config.get("recaptcha-site-key", "") RECAPTCHA_SITE_KEY = config.get("recaptcha_site_key", "")
LOG_LEVEL = config.get("log-level", "INFO") LOG_LEVEL = config.get("log_level", "INFO")
LOG_WEB_REQUESTS = config.get("log-web-requests", False) 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["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["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["recaptcha_site_key"] = RECAPTCHA_SITE_KEY
WEB_UI_OPTIONS["allow-upstream-spotting"] = ALLOW_SPOTTING and ALLOW_UPSTREAM_SPOTTING WEB_UI_OPTIONS["allow_upstream_spotting"] = ALLOW_SPOTTING and ALLOW_UPSTREAM_SPOTTING
def create_provider_from_config(package, config_providers_entry): def create_provider_from_config(package, config_providers_entry):
+7 -7
View File
@@ -14,22 +14,22 @@ class DataProviders:
def setup(self): 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)) 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)) 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)) 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)) 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)) 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)) self.callsign_data_providers.append(create_provider_from_config("providers.callsigndata", entry))
def start(self): 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: for p in self.static_data_providers:
if p.enabled: if p.enabled:
p.start() p.start()
+2 -2
View File
@@ -24,8 +24,8 @@ class StatusReporter:
self._stop_event = Event() self._stop_event = Event()
self._startup_time = datetime.now(pytz.UTC) self._startup_time = datetime.now(pytz.UTC)
DATA_STORE.status_data["software-version"] = SOFTWARE_VERSION DATA_STORE.status_data["software_version"] = SOFTWARE_VERSION
DATA_STORE.status_data["server-owner-callsign"] = SERVER_OWNER_CALLSIGN DATA_STORE.status_data["server_owner_callsign"] = SERVER_OWNER_CALLSIGN
def start(self): def start(self):
"""Start the reporter thread""" """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 # 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. # 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]:
if self.dx_calls and self.dx_calls[0] and not self.dx_country: call_info = get_call_info(self.dx_calls[0], credentials)
self.dx_country = call_info.country if self.dx_calls and self.dx_calls[0] and not self.dx_country:
if self.dx_calls and self.dx_calls[0] and not self.dx_continent: self.dx_country = call_info.country
self.dx_continent = call_info.continent if self.dx_calls and self.dx_calls[0] and not self.dx_continent:
if self.dx_calls and self.dx_calls[0] and not self.dx_cq_zone: self.dx_continent = call_info.continent
self.dx_cq_zone = call_info.cq_zone if self.dx_calls and self.dx_calls[0] and not self.dx_cq_zone:
if self.dx_calls and self.dx_calls[0] and not self.dx_itu_zone: self.dx_cq_zone = call_info.cq_zone
self.dx_itu_zone = call_info.itu_zone if self.dx_calls and self.dx_calls[0] and not self.dx_itu_zone:
if self.dx_calls and self.dx_calls[0] and not self.dx_dxcc_id: self.dx_itu_zone = call_info.itu_zone
self.dx_dxcc_id = call_info.dxcc_id if self.dx_calls and self.dx_calls[0] and not self.dx_dxcc_id:
if self.dx_dxcc_id and not self.dx_flag: self.dx_dxcc_id = call_info.dxcc_id
self.dx_flag = get_flag_for_dxcc(self.dx_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 # 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 # 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 | | Name | Allowed Values | Default | Example | Description |
|------------------|-------------------------|---------|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| |------------------|-------------------------|---------|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `embedded` | `true`, `false` | `false` | `?embedded=true` | Enables embedded mode. | | `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. | | `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. | | `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` | 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 | | `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. | | `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_prompt: "login: "
login_callsign: "callsign-11" login_callsign: "callsign-11"
allow_rbn_spots: true 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: 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_prompt: "login: "
login_callsign: "callsign-12" login_callsign: "callsign-12"
allow_rbn_spots: true 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: 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_prompt: "login: "
login_callsign: "callsign-13" login_callsign: "callsign-13"
allow_rbn_spots: true 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: 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): def __init__(self, provider_config):
# API key required for this provider # 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 != "": if self._api_key != "":
lookuplib = LookupLib(lookuptype="clublogapi", apikey=self._api_key) lookuplib = LookupLib(lookuptype="clublogapi", apikey=self._api_key)
self._callinfo = Callinfo(lookuplib) self._callinfo = Callinfo(lookuplib)
+1 -1
View File
@@ -22,7 +22,7 @@ class ClublogXML(FileDownloadCallsignDataProvider):
def __init__(self, provider_config): def __init__(self, provider_config):
# API key required for this provider # 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 == "": if self._api_key == "":
provider_config["enabled"] = False provider_config["enabled"] = False
logging.warning( logging.warning(
+3 -3
View File
@@ -21,7 +21,7 @@ class GMA(HTTPSpotProvider):
def __init__(self, provider_config): 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, # 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. # disable this spot provider.
self._api_key = provider_config.get("api-key", "") self._api_key = provider_config.get("api_key", "")
if self._api_key == "": if self._api_key == "":
provider_config["enabled"] = False provider_config["enabled"] = False
logging.warning("GMA spot provider configured but no api key was provided, this API will not be queried.") 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"], ref_response = self._url_data_cache.get(self.REF_INFO_URL_ROOT + source_spot["REF"],
headers=HTTP_HEADERS) headers=HTTP_HEADERS)
# Sometimes this is blank even if it's a 200 response, so handle that # 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() 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 # 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 # 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"]}") f"GMA API returned a malformed response when looking up ref {source_spot["REF"]}")
except: except:
logging.warning("Exception when looking up " + self.REF_INFO_URL_ROOT + source_spot[ 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: else:
logging.warning(f"The GMA API returned an unexpected response (HTTP {http_response.status_code}).") 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.name = name
self.enabled = provider_config.get("enabled", True) 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_update_time = datetime.min.replace(tzinfo=pytz.UTC)
self.last_spot_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" 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" name = provider_config["name"] if "name" in provider_config else "xOTA"
super().__init__(name, provider_config, provider_config["url"]) super().__init__(name, provider_config, provider_config["url"])
self.SIG = str(provider_config["sig"]) if "sig" in provider_config else None 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): def _ws_message_to_spot(self, b):
string = b.decode("utf-8") string = b.decode("utf-8")
+1 -2
View File
@@ -63,13 +63,12 @@ class APIOptionsHandler(tornado.web.RequestHandler):
spot_providers.append("API") spot_providers.append("API")
spot_providers_enabled_by_default.append("API") spot_providers_enabled_by_default.append("API")
options = {"bands": BANDS, options = {"bands": BANDS,
"modes": ALL_MODES, "modes": ALL_MODES,
"mode_types": MODE_TYPES, "mode_types": MODE_TYPES,
"sigs": SIGS, "sigs": SIGS,
"spot_providers": spot_providers, "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, "alert_providers": alert_providers,
"callsign_data_providers": callsign_data_providers, "callsign_data_providers": callsign_data_providers,
"continents": CONTINENTS, "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:** 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:** 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:** 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:** "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.) * 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.) * 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 `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 #### Upgrading a client from v1 to v2 API endpoints
@@ -1926,11 +1927,11 @@ components:
ServerStatus: ServerStatus:
type: object type: object
properties: properties:
"software-version": "software_version":
type: string type: string
description: The version number of the software. description: The version number of the software.
example: "1.0.1" example: "1.0.1"
"server-owner-callsign": "server_owner_callsign":
type: string type: string
description: The callsign of this server's operator. description: The callsign of this server's operator.
example: "M0TRT" example: "M0TRT"
+3 -3
View File
@@ -27,7 +27,7 @@ function buildQueryString() {
str = str + getQueryStringFor(fn) + "&"; 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(); const maxDur = $("#max-duration option:selected").val();
if (maxDur !== "9999999999") { if (maxDur !== "9999999999") {
str = str + "&max_duration=" + maxDur; str = str + "&max_duration=" + maxDur;
@@ -284,7 +284,7 @@ function loadOptions() {
options = jsonData; options = jsonData;
// Populate the filters panel // 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"]); generateMultiToggleFilterCard("#source-options", "source", options["alert_providers"]);
// Load URL params. These may select things from the various filter & display options, so the function needs // 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 // Load filters from settings storage
loadSettings(); loadSettings();
setColorScheme($("#color-scheme option:selected").val()); setColorScheme($("#color_scheme option:selected").val());
// Load alerts and set up the timer // Load alerts and set up the timer
loadAlerts(); loadAlerts();
+7 -7
View File
@@ -61,7 +61,7 @@ function startSSEConnection() {
// Remove spots from the display that are older than the selected max age. // Remove spots from the display that are older than the selected max age.
function expireOldSpots() { 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 cutoff = (Date.now() / 1000) - maxAgeSeconds;
const before = spots.length; const before = spots.length;
spots = spots.filter(s => s["time"] && s["time"] >= cutoff); spots = spots.filter(s => s["time"] && s["time"] >= cutoff);
@@ -79,7 +79,7 @@ function buildQueryString() {
str = str + getQueryStringFor(fn) + "&"; 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 // Additional filters for the bands view: No dupes, no QRT
str = str + "&dedupe=true&allow_qrt=false"; str = str + "&dedupe=true&allow_qrt=false";
return str; 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 // First pass loading settings, so we can load the band colour scheme before the filters that need to use it
loadSettings(); loadSettings();
setColorScheme($("#color-scheme option:selected").val()); setColorScheme($("#color_scheme option:selected").val());
setBandColorScheme($("#band-color-scheme option:selected").val()); setBandColorScheme($("#band_color_scheme option:selected").val());
// Add CSS for band toggle buttons // Add CSS for band toggle buttons
addBandToggleColourCSS(options["bands"]); addBandToggleColourCSS(options["bands"]);
@@ -289,10 +289,10 @@ function loadOptions() {
// Populate the filters panel // Populate the filters panel
generateBandsMultiToggleFilterCard(options["bands"]); generateBandsMultiToggleFilterCard(options["bands"]);
generateSIGsMultiToggleFilterCard(options["sigs"]); generateSIGsMultiToggleFilterCard(options["sigs"]);
generateMultiToggleFilterCard("#dx-continent-options", "dx_continent", options["continents"]); generateMultiToggleFilterCard("#dx_continent_options", "dx_continent", options["continents"]);
generateMultiToggleFilterCard("#de-continent-options", "de_continent", options["continents"]); generateMultiToggleFilterCard("#de_continent_options", "de_continent", options["continents"]);
generateModesMultiToggleFilterCard(options["modes"]); 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 // 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 // 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 // Handle other params
updateSelectFromParam(params, "color-scheme", "color-scheme"); updateSelectFromParam(params, "color_scheme", "color_scheme");
updateSelectFromParam(params, "time-zone", "time-zone"); // Only on Spots and Alerts pages 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", "spots_to_fetch"); // Only on Spots page
updateSelectFromParam(params, "limit", "alerts-to-fetch"); // Only on Alerts 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, "max_age", "max_spot_age"); // Only on Map & Bands pages
updateFilterFromParam(params, "band", "band"); updateFilterFromParam(params, "band", "band");
updateFilterFromParam(params, "sig", "sig"); updateFilterFromParam(params, "sig", "sig");
updateFilterFromParam(params, "source", "source"); updateFilterFromParam(params, "source", "source");
@@ -80,7 +80,7 @@ function updateSelectFromParam(params, paramName, selectID) {
if (v != null) { if (v != null) {
$("#" + selectID).prop("value", v); $("#" + selectID).prop("value", v);
// Extra check if this is the "color scheme" select // Extra check if this is the "color scheme" select
if (selectID === "color-scheme") { if (selectID === "color_scheme") {
setColorScheme(v); setColorScheme(v);
} }
} }
@@ -157,7 +157,7 @@ function columnsUpdated() {
// Function to set the colour scheme based on the state of the UI select box // Function to set the colour scheme based on the state of the UI select box
function setColorSchemeFromUI() { function setColorSchemeFromUI() {
let theme = $("#color-scheme option:selected").val(); let theme = $("#color_scheme option:selected").val();
if (theme !== "") { if (theme !== "") {
setColorScheme(theme); setColorScheme(theme);
saveSettings(); saveSettings();
@@ -168,7 +168,7 @@ function setColorSchemeFromUI() {
function setColorScheme(mode) { function setColorScheme(mode) {
let effectiveModeDark = mode === "dark"; let effectiveModeDark = mode === "dark";
if (mode === "auto") { 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"); $("html").attr("data-bs-theme", effectiveModeDark ? "dark" : "light");
const metaThemeColor = document.querySelector("meta[name=theme-color]"); 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 // 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 // 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. // 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) { if (val != null) {
setColorScheme(JSON.parse(val)); 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 // 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. // updated, otherwise if the Spothole user theme is forced to light or dark, that preference will remain.
function listenForOSThemeChange() { function listenForOSThemeChange() {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { window.matchMedia('(prefers-color_scheme: dark)').addEventListener('change', () => {
setColorScheme($("#color-scheme option:selected").val()); setColorScheme($("#color_scheme option:selected").val());
}); });
} }
@@ -254,13 +254,13 @@ function closeDataPanel() {
// provided the corresponding "enabled" checkbox is ticked. // provided the corresponding "enabled" checkbox is ticked.
function getCredentialHeaders() { function getCredentialHeaders() {
const headers = {}; const headers = {};
if ($("#qrz-enabled")[0] && $("#qrz-enabled")[0].checked) { if ($("#qrz_enabled")[0] && $("#qrz_enabled")[0].checked) {
const qrzUsername = $("#qrz-username").val(); const qrzUsername = $("#qrz-username").val();
const qrzPassword = $("#qrz-password").val(); const qrzPassword = $("#qrz-password").val();
if (qrzUsername) headers["X-QRZ-Username"] = qrzUsername; if (qrzUsername) headers["X-QRZ-Username"] = qrzUsername;
if (qrzPassword) headers["X-QRZ-Password"] = qrzPassword; 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 hamqthUsername = $("#hamqth-username").val();
const hamqthPassword = $("#hamqth-password").val(); const hamqthPassword = $("#hamqth-password").val();
if (hamqthUsername) headers["X-HamQTH-Username"] = hamqthUsername; if (hamqthUsername) headers["X-HamQTH-Username"] = hamqthUsername;
+1 -1
View File
@@ -624,7 +624,7 @@ function renderDxStats() {
if (!dxStatsData) { if (!dxStatsData) {
return; return;
} }
const deContinent = $('#dxstats-de-continent').val(); const deContinent = $('#sxstats_de_continent').val();
const deData = dxStatsData[deContinent]; const deData = dxStatsData[deContinent];
if (!deData) { if (!deData) {
return; return;
+7 -7
View File
@@ -108,7 +108,7 @@ function startSSEConnection() {
// Remove spots from the map that are older than the selected max age. // Remove spots from the map that are older than the selected max age.
function expireOldSpots() { 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 cutoff = (Date.now() / 1000) - maxAgeSeconds;
spots = spots.filter(function (s) { spots = spots.filter(function (s) {
if (s["time"] && s["time"] < cutoff) { if (s["time"] && s["time"] < cutoff) {
@@ -170,7 +170,7 @@ function buildQueryString() {
str = str + getQueryStringFor(fn) + "&"; 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 // Additional filters for the map view: No dupes, no QRT, only spots with good locations
str = str + "&dedupe=true&allow_qrt=false"; str = str + "&dedupe=true&allow_qrt=false";
return str; 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 // First pass loading settings, so we can load the band colour scheme before the filters that need to use it
loadSettings(); loadSettings();
setColorScheme($("#color-scheme option:selected").val()); setColorScheme($("#color_scheme option:selected").val());
setBandColorScheme($("#band-color-scheme option:selected").val()); setBandColorScheme($("#band_color_scheme option:selected").val());
// Add CSS for band toggle buttons // Add CSS for band toggle buttons
addBandToggleColourCSS(options["bands"]); addBandToggleColourCSS(options["bands"]);
@@ -316,10 +316,10 @@ function loadOptions() {
// Populate the filters panel // Populate the filters panel
generateBandsMultiToggleFilterCard(options["bands"]); generateBandsMultiToggleFilterCard(options["bands"]);
generateSIGsMultiToggleFilterCard(options["sigs"]); generateSIGsMultiToggleFilterCard(options["sigs"]);
generateMultiToggleFilterCard("#dx-continent-options", "dx_continent", options["continents"]); generateMultiToggleFilterCard("#dx_continent_options", "dx_continent", options["continents"]);
generateMultiToggleFilterCard("#de-continent-options", "de_continent", options["continents"]); generateMultiToggleFilterCard("#de_continent_options", "de_continent", options["continents"]);
generateModesMultiToggleFilterCard(options["modes"]); 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 // 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 // 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 // Add spot to internal data store
spots.unshift(newSpot); spots.unshift(newSpot);
// Work out if we need to remove an old spot // 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); 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 // 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(); $("#table tbody tr").last().remove();
@@ -93,7 +93,7 @@ function buildQueryString() {
str = str + getQueryStringFor(fn) + "&"; 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() !== "") { if ($("#search").val() !== "") {
str = str + "&text_includes=" + encodeURIComponent($("#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 // First pass loading settings, so we can load the band colour scheme before the filters that need to use it
loadSettings(); loadSettings();
setColorScheme($("#color-scheme option:selected").val()); setColorScheme($("#color_scheme option:selected").val());
setBandColorScheme($("#band-color-scheme option:selected").val()); setBandColorScheme($("#band_color_scheme option:selected").val());
// Add CSS for band toggle buttons // Add CSS for band toggle buttons
addBandToggleColourCSS(options["bands"]); addBandToggleColourCSS(options["bands"]);
@@ -430,10 +430,10 @@ function loadOptions() {
// Populate the filters panel // Populate the filters panel
generateBandsMultiToggleFilterCard(options["bands"]); generateBandsMultiToggleFilterCard(options["bands"]);
generateSIGsMultiToggleFilterCard(options["sigs"]); generateSIGsMultiToggleFilterCard(options["sigs"]);
generateMultiToggleFilterCard("#dx-continent-options", "dx_continent", options["continents"]); generateMultiToggleFilterCard("#dx_continent_options", "dx_continent", options["continents"]);
generateMultiToggleFilterCard("#de-continent-options", "de_continent", options["continents"]); generateMultiToggleFilterCard("#de_continent_options", "de_continent", options["continents"]);
generateModesMultiToggleFilterCard(options["modes"]); 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 // 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 // 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 to update the band colour scheme in spots, bands and map pages
function setBandColorSchemeFromUI() { function setBandColorSchemeFromUI() {
setBandColorScheme($("#band-color-scheme option:selected").val()); setBandColorScheme($("#band_color_scheme option:selected").val());
saveSettings(); saveSettings();
// Fudge a full reload because we need to update not just colours in the list/map/bands but also the filters // Fudge a full reload because we need to update not just colours in the list/map/bands but also the filters
window.location.reload(); window.location.reload();
+7 -7
View File
@@ -1,8 +1,8 @@
// Load server status // Load server status
function loadStatus() { function loadStatus() {
$.getJSON('/api/v2/status', function (jsonData) { $.getJSON('/api/v2/status', function (jsonData) {
$("#software-version").text(jsonData["software-version"]); $("#software_version").text(jsonData["software_version"]);
$("#server-owner-callsign").text(jsonData["server-owner-callsign"]); $("#server_owner_callsign").text(jsonData["server_owner_callsign"]);
$("#up-since").text(moment().subtract(jsonData["uptime"], 'seconds').fromNow()); $("#up-since").text(moment().subtract(jsonData["uptime"], 'seconds').fromNow());
$("#memory-use").text(jsonData["mem_use_mb"] + " MB"); $("#memory-use").text(jsonData["mem_use_mb"] + " MB");
$("#total-spots").text(jsonData["num_spots"]); $("#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()); $("#web-server-last-page").text(moment.unix(jsonData["webserver"]["last_page_access"]).utc().fromNow());
jsonData["spot_providers"].forEach(p => { 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="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"><strong>${p["name"]}</strong></div>
<div class="col">Status: ${p["status"]}</div> <div class="col">Status: ${p["status"]}</div>
@@ -23,7 +23,7 @@ function loadStatus() {
}); });
jsonData["alert_providers"].forEach(p => { 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="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"><strong>${p["name"]}</strong></div>
<div class="col">Status: ${p["status"]}</div> <div class="col">Status: ${p["status"]}</div>
@@ -41,7 +41,7 @@ function loadStatus() {
}); });
jsonData["static_data_providers"].forEach(p => { 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="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"><strong>${p["name"]}</strong></div>
<div class="col">Status: ${p["status"]}</div> <div class="col">Status: ${p["status"]}</div>
@@ -50,7 +50,7 @@ function loadStatus() {
}); });
jsonData["sig_ref_data_providers"].forEach(p => { 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="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"><strong>${p["sig_name"]}</strong></div>
<div class="col">Status: ${p["status"]}</div> <div class="col">Status: ${p["status"]}</div>
@@ -60,7 +60,7 @@ function loadStatus() {
}); });
jsonData["callsign_data_providers"].forEach(p => { 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="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"><strong>${p["name"]}</strong></div>
<div class="col">Status: ${p["status"]}</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 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> sensitive data in order to use this feature.</p>
<p>Spothole uses no trackers, no ads, and no cookies.</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. <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 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> 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 class="mt-3">
<div id="settingsButtonRow" class="row mb-3"> <div id="settingsButtonRow" class="row mb-3">
<div class="col-auto me-auto pt-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>
<div class="col-auto"> <div class="col-auto">
<div class="d-inline-flex gap-1"> <div class="d-inline-flex gap-1">
{% module Template("widgets/filters-display-data-buttons.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"]) %} show_data_button=web_ui_options["qrz_enabled"] or web_ui_options["hamqth_enabled"]) %}
</div> </div>
</div> </div>
</div> </div>
<div id="filters-area" class="appearing-panel card mb-3"> <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="card-body">
<div class="row row-cols-1 row-cols-md-3 g-4"> <div class="row row-cols-1 row-cols-md-3 g-4">
<div class="col"> <div class="col">
@@ -25,43 +25,43 @@
{% module Template("cards/sources.html", web_ui_options=web_ui_options) %} {% module Template("cards/sources.html", web_ui_options=web_ui_options) %}
</div> </div>
<div class="col"> <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> </div>
</div> </div>
<div id="display-area" class="appearing-panel card mb-3"> <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 class="card-body">
<div id="display-container" class="row row-cols-1 row-cols-md-4 g-4"> <div id="display-container" class="row row-cols-1 row-cols-md-4 g-4">
<div class="col"> <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>
<div class="col"> <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>
<div class="col"> <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>
<div class="col"> <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>
</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"> <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="card-body">
<div class="row row-cols-1 row-cols-md-4 g-4"> <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"> <div class="col">
{% module Template("cards/qrz.html", web_ui_options=web_ui_options) %} {% module Template("cards/qrz.html", web_ui_options=web_ui_options) %}
</div> </div>
{% end %} {% end %}
{% if web_ui_options["hamqth-enabled"] %} {% if web_ui_options["hamqth_enabled"] %}
<div class="col"> <div class="col">
{% module Template("cards/hamqth.html", web_ui_options=web_ui_options) %} {% module Template("cards/hamqth.html", web_ui_options=web_ui_options) %}
</div> </div>
+11 -11
View File
@@ -6,14 +6,14 @@
<div class="col-auto me-auto pt-3"></div> <div class="col-auto me-auto pt-3"></div>
<div class="col-auto"> <div class="col-auto">
<div class="d-inline-flex gap-1"> <div class="d-inline-flex gap-1">
{% module Template("widgets/filters-display-data-buttons.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"]) %} show_data_button=web_ui_options["qrz_enabled"] or web_ui_options["hamqth_enabled"]) %}
</div> </div>
</div> </div>
</div> </div>
<div id="filters-area" class="appearing-panel card mb-3"> <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="card-body">
<div class="row row-cols-1 g-4 mb-4 row-cols-md-3"> <div class="row row-cols-1 g-4 mb-4 row-cols-md-3">
<div class="col"> <div class="col">
@@ -31,7 +31,7 @@
{% module Template("cards/dx-continent.html", web_ui_options=web_ui_options) %} {% module Template("cards/dx-continent.html", web_ui_options=web_ui_options) %}
</div> </div>
<div class="col"> <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>
<div class="col"> <div class="col">
{% module Template("cards/modes.html", web_ui_options=web_ui_options) %} {% module Template("cards/modes.html", web_ui_options=web_ui_options) %}
@@ -41,31 +41,31 @@
</div> </div>
<div id="display-area" class="appearing-panel card mb-3"> <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 class="card-body">
<div id="display-container" class="row row-cols-1 row-cols-md-4 g-4"> <div id="display-container" class="row row-cols-1 row-cols-md-4 g-4">
<div class="col"> <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>
<div class="col"> <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>
</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"> <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="card-body">
<div class="row row-cols-1 row-cols-md-4 g-4"> <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"> <div class="col">
{% module Template("cards/qrz.html", web_ui_options=web_ui_options) %} {% module Template("cards/qrz.html", web_ui_options=web_ui_options) %}
</div> </div>
{% end %} {% end %}
{% if web_ui_options["hamqth-enabled"] %} {% if web_ui_options["hamqth_enabled"] %}
<div class="col"> <div class="col">
{% module Template("cards/hamqth.html", web_ui_options=web_ui_options) %} {% module Template("cards/hamqth.html", web_ui_options=web_ui_options) %}
</div> </div>
@@ -2,7 +2,7 @@
<div class="card-body"> <div class="card-body">
<h5 class="card-title">Theme</h5> <h5 class="card-title">Theme</h5>
<p class="card-text spothole-card-text"> <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>
</div> </div>
</div> </div>
@@ -2,10 +2,10 @@
<div class="card-body"> <div class="card-body">
<h5 class="card-title">Theme</h5> <h5 class="card-title">Theme</h5>
<p class="card-text spothole-card-text"> <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>
<p class="card-text spothole-card-text"> <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> </p>
</div> </div>
</div> </div>
@@ -1,6 +1,6 @@
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">DE Continent</h5> <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>
</div> </div>
@@ -1,6 +1,6 @@
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">DX Continent</h5> <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>
</div> </div>
+2 -2
View File
@@ -3,9 +3,9 @@
<h5 class="card-title">HamQTH</h5> <h5 class="card-title">HamQTH</h5>
<div class="card-text spothole-card-text"> <div class="card-text spothole-card-text">
<div class="form-check mb-2"> <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();"> 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>
<div class="mb-2"> <div class="mb-2">
<input type="text" class="storeable-text form-control" id="hamqth-username" <input type="text" class="storeable-text form-control" id="hamqth-username"
@@ -2,11 +2,11 @@
<div class="card-body"> <div class="card-body">
<h5 class="card-title">Number of Alerts</h5> <h5 class="card-title">Number of Alerts</h5>
<p class="card-text spothole-card-text">Show up to <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;"> 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[ <option value="{{c}}" {% if web_ui_options[
"alert-count-default"] == c %}selected{% end %}>{{c}}</option> "alert_count_default"] == c %}selected{% end %}>{{c}}</option>
{% end %} {% end %}
</select> </select>
alerts alerts
@@ -2,11 +2,11 @@
<div class="card-body"> <div class="card-body">
<h5 class="card-title">Number of Spots</h5> <h5 class="card-title">Number of Spots</h5>
<p class="card-text spothole-card-text">Show up to <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;"> 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[ <option value="{{c}}" {% if web_ui_options[
"spot-count-default"] == c %}selected{% end %}>{{c}}</option> "spot_count_default"] == c %}selected{% end %}>{{c}}</option>
{% end %} {% end %}
</select> </select>
spots spots
+2 -2
View File
@@ -3,9 +3,9 @@
<h5 class="card-title">QRZ.com</h5> <h5 class="card-title">QRZ.com</h5>
<div class="card-text spothole-card-text"> <div class="card-text spothole-card-text">
<div class="form-check mb-2"> <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();"> 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>
<div class="mb-2"> <div class="mb-2">
<input type="text" class="storeable-text form-control" id="qrz-username" <input type="text" class="storeable-text form-control" id="qrz-username"
@@ -2,11 +2,11 @@
<div class="card-body"> <div class="card-body">
<h5 class="card-title">Spot Age</h5> <h5 class="card-title">Spot Age</h5>
<p class="card-text spothole-card-text">Last <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;"> 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[ <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 %} {% end %}
</select> </select>
minutes minutes
+2 -2
View File
@@ -233,8 +233,8 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="mb-3"> <div class="mb-3">
<label for="dxstats-de-continent" class="form-label">Your continent:</label> <label for="sxstats_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" <select id="sxstats_de_continent" class="form-select storeable-select d-inline-block ms-2 w-auto"
oninput="dxStatsContientChanged();"> oninput="dxStatsContientChanged();">
<option value="EU">Europe</option> <option value="EU">Europe</option>
<option value="NA">North America</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 me-auto pt-3"></div>
<div class="col-auto"> <div class="col-auto">
<div class="d-inline-flex gap-1"> <div class="d-inline-flex gap-1">
{% module Template("widgets/filters-display-data-buttons.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"]) %} show_data_button=web_ui_options["qrz_enabled"] or web_ui_options["hamqth_enabled"]) %}
</div> </div>
</div> </div>
</div> </div>
<div id="filters-area" class="appearing-panel card mb-3"> <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="card-body">
<div class="row row-cols-1 g-4 mb-4 row-cols-md-3"> <div class="row row-cols-1 g-4 mb-4 row-cols-md-3">
<div class="col"> <div class="col">
@@ -45,7 +45,7 @@
{% module Template("cards/dx-continent.html", web_ui_options=web_ui_options) %} {% module Template("cards/dx-continent.html", web_ui_options=web_ui_options) %}
</div> </div>
<div class="col"> <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>
<div class="col"> <div class="col">
{% module Template("cards/modes.html", web_ui_options=web_ui_options) %} {% module Template("cards/modes.html", web_ui_options=web_ui_options) %}
@@ -55,37 +55,37 @@
</div> </div>
<div id="display-area" class="appearing-panel card mb-3"> <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 class="card-body">
<div id="display-container" class="row row-cols-1 row-cols-md-4 g-4"> <div id="display-container" class="row row-cols-1 row-cols-md-4 g-4">
<div class="col"> <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>
<div class="col"> <div class="col">
{% module Template("cards/basemap.html", web_ui_options=web_ui_options) %} {% module Template("cards/basemap.html", web_ui_options=web_ui_options) %}
</div> </div>
<div class="col"> <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>
<div class="col"> <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) %} web_ui_options=web_ui_options) %}
</div> </div>
</div> </div>
</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"> <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="card-body">
<div class="row row-cols-1 row-cols-md-4 g-4"> <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"> <div class="col">
{% module Template("cards/qrz.html", web_ui_options=web_ui_options) %} {% module Template("cards/qrz.html", web_ui_options=web_ui_options) %}
</div> </div>
{% end %} {% end %}
{% if web_ui_options["hamqth-enabled"] %} {% if web_ui_options["hamqth_enabled"] %}
<div class="col"> <div class="col">
{% module Template("cards/hamqth.html", web_ui_options=web_ui_options) %} {% module Template("cards/hamqth.html", web_ui_options=web_ui_options) %}
</div> </div>
+1 -1
View File
@@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"> <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="theme-color" content="white"/>
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
<meta name="apple-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 id="settingsButtonRow" class="row mb-3">
<div class="col-md-4 mb-3 mb-md-0"> <div class="col-md-4 mb-3 mb-md-0">
<div class="d-inline-flex gap-3"> <div class="d-inline-flex gap-3">
{% module Template("widgets/run-pause.html", web_ui_options=web_ui_options) %} {% 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 class="d-inline-flex">{% raw web_ui_options["support_button_html"] %}</div>
</div> </div>
</div> </div>
<div class="col-md-8 text-end"> <div class="col-md-8 text-end">
<div class="d-inline-flex gap-3"> <div class="d-inline-flex gap-3">
{% module Template("widgets/search.html", web_ui_options=web_ui_options) %} {% 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, {% 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"]) %} show_data_button=web_ui_options["qrz_enabled"] or web_ui_options["hamqth_enabled"]) %}
</div> </div>
</div> </div>
</div> </div>
<div id="filters-area" class="appearing-panel card mb-3"> <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="card-body">
<div class="row row-cols-1 g-4 mb-4 row-cols-md-3"> <div class="row row-cols-1 g-4 mb-4 row-cols-md-3">
<div class="col"> <div class="col">
@@ -50,7 +50,7 @@
{% module Template("cards/dx-continent.html", web_ui_options=web_ui_options) %} {% module Template("cards/dx-continent.html", web_ui_options=web_ui_options) %}
</div> </div>
<div class="col"> <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>
<div class="col"> <div class="col">
{% module Template("cards/modes.html", web_ui_options=web_ui_options) %} {% module Template("cards/modes.html", web_ui_options=web_ui_options) %}
@@ -60,21 +60,21 @@
</div> </div>
<div id="display-area" class="appearing-panel card mb-3"> <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 class="card-body">
<div id="display-container" class="row row-cols-1 row-cols-md-4 g-4"> <div id="display-container" class="row row-cols-1 row-cols-md-4 g-4">
<div class="col"> <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>
<div class="col"> <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>
<div class="col"> <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 class="col"> <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>
<div class="col"> <div class="col">
{% module Template("cards/audio.html", web_ui_options=web_ui_options) %} {% module Template("cards/audio.html", web_ui_options=web_ui_options) %}
@@ -84,15 +84,15 @@
</div> </div>
<div id="data-area" class="appearing-panel card mb-3"> <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="card-body">
<div class="row row-cols-1 row-cols-md-4 g-4"> <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"> <div class="col">
{% module Template("cards/qrz.html", web_ui_options=web_ui_options) %} {% module Template("cards/qrz.html", web_ui_options=web_ui_options) %}
</div> </div>
{% end %} {% end %}
{% if web_ui_options["hamqth-enabled"] %} {% if web_ui_options["hamqth_enabled"] %}
<div class="col"> <div class="col">
{% module Template("cards/hamqth.html", web_ui_options=web_ui_options) %} {% module Template("cards/hamqth.html", web_ui_options=web_ui_options) %}
</div> </div>
@@ -101,7 +101,7 @@
{% module Template("cards/location.html", web_ui_options=web_ui_options) %} {% module Template("cards/location.html", web_ui_options=web_ui_options) %}
</div> </div>
<div class="col"> <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> </div>
</div> </div>
+7 -7
View File
@@ -8,8 +8,8 @@
<div class="card-body"> <div class="card-body">
<div class="row row-cols-1 row-cols-md-4 g-4 mb-4 mb-md-2"> <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"><strong>Metadata</strong></div>
<div class="col">Software Version: <span id="software-version"></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">Owner Callsign: <span id="server_owner_callsign"></span></div>
<div class="col">Up since: <span id="up-since"></span></div> <div class="col">Up since: <span id="up-since"></span></div>
</div> </div>
<div class="row row-cols-1 row-cols-md-4 g-4 mb-4 mb-md-2"> <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"> <div class="card-header">
Spot Providers Spot Providers
</div> </div>
<div class="card-body" id="spot-providers-status-container"> <div class="card-body" id="spot_providers-status-container">
</div> </div>
</div> </div>
@@ -40,7 +40,7 @@
<div class="card-header"> <div class="card-header">
Alert Providers Alert Providers
</div> </div>
<div class="card-body" id="alert-providers-status-container"> <div class="card-body" id="alert_providers-status-container">
</div> </div>
</div> </div>
@@ -58,7 +58,7 @@
<div class="card-header"> <div class="card-header">
Static Reference Data Providers Static Reference Data Providers
</div> </div>
<div class="card-body" id="static-data-providers-status-container"> <div class="card-body" id="static_data_providers-status-container">
</div> </div>
</div> </div>
@@ -67,7 +67,7 @@
<div class="card-header"> <div class="card-header">
SIG Reference Data Providers SIG Reference Data Providers
</div> </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>
</div> </div>
@@ -76,7 +76,7 @@
<div class="card-header"> <div class="card-header">
Callsign Data Providers Callsign Data Providers
</div> </div>
<div class="card-body" id="callsign-data-providers-status-container"> <div class="card-body" id="callsign_data_providers-status-container">
</div> </div>
</div> </div>
@@ -1,24 +1,24 @@
<label class="form-check-label form-label" for="band-color-scheme">Band color scheme</label><br/> <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();" <select id="band_color_scheme" class="storeable-select form-select d-inline-block" oninput="setBandColorSchemeFromUI();"
style="display: inline-block;"> style="display: inline-block;">
<option value="PSK Reporter" {% if web_ui_options[ <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[ <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[ <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[ <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[ <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[ <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[ <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[ <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[ <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[ <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> </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>