mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-02-04 01:04:33 +00:00
Allow providers to be off-by-default in the web UI. Closes #93
This commit is contained in:
@@ -90,15 +90,22 @@ spot-providers:
|
|||||||
name: "RBN CW/RTTY"
|
name: "RBN CW/RTTY"
|
||||||
enabled: false
|
enabled: false
|
||||||
port: 7000
|
port: 7000
|
||||||
|
# This setting doesn't affect the spot provider itself, or anything in the back-end of Spothole, just the web UI.
|
||||||
|
# By default spots from all enabled providers will be shown in the web UI. However, you might want RBN data to be
|
||||||
|
# received by Spothole but not shown on the web UI unless the user explicitly turns it on. For that behaviour,
|
||||||
|
# set enabled to true, but enabled-by-default-in-web-ui to false.
|
||||||
|
enabled-by-default-in-web-ui: false
|
||||||
-
|
-
|
||||||
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
|
||||||
-
|
-
|
||||||
class: "UKPacketNet"
|
class: "UKPacketNet"
|
||||||
name: "UK Packet Radio Net"
|
name: "UK Packet Radio Net"
|
||||||
enabled: false
|
enabled: false
|
||||||
|
enabled-by-default-in-web-ui: false
|
||||||
-
|
-
|
||||||
class: "XOTA"
|
class: "XOTA"
|
||||||
name: "39C3 TOTA"
|
name: "39C3 TOTA"
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import yaml
|
|||||||
|
|
||||||
# Check you have a config file
|
# Check you have a config file
|
||||||
if not os.path.isfile("config.yml"):
|
if not os.path.isfile("config.yml"):
|
||||||
logging.error("Your config file is missing. Ensure you have copied config-example.yml to config.yml and updated it according to your needs.")
|
logging.error(
|
||||||
|
"Your config file is missing. Ensure you have copied config-example.yml to config.yml and updated it according to your needs.")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
# Load config
|
# Load config
|
||||||
@@ -17,4 +18,9 @@ MAX_ALERT_AGE = config["max-alert-age-sec"]
|
|||||||
SERVER_OWNER_CALLSIGN = config["server-owner-callsign"]
|
SERVER_OWNER_CALLSIGN = config["server-owner-callsign"]
|
||||||
WEB_SERVER_PORT = config["web-server-port"]
|
WEB_SERVER_PORT = config["web-server-port"]
|
||||||
ALLOW_SPOTTING = config["allow-spotting"]
|
ALLOW_SPOTTING = config["allow-spotting"]
|
||||||
WEB_UI_OPTIONS = config["web-ui-options"]
|
WEB_UI_OPTIONS = config["web-ui-options"]
|
||||||
|
|
||||||
|
# For ease of config, each spot provider owns its own config about whether it should be enabled by default in the web UI
|
||||||
|
# but for consistency we provide this to the front-end in web-ui-options because it has no impact outside of the web UI.
|
||||||
|
WEB_UI_OPTIONS["spot-providers-enabled-by-default"] = [p["name"] for p in config["spot-providers"] if p["enabled"] and (
|
||||||
|
"enabled-by-default-in-web-ui" not in p or p["enabled-by-default-in-web-ui"] == True)]
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ class APIOptionsHandler(tornado.web.RequestHandler):
|
|||||||
# one of our proviers.
|
# one of our proviers.
|
||||||
if ALLOW_SPOTTING:
|
if ALLOW_SPOTTING:
|
||||||
options["spot_sources"].append("API")
|
options["spot_sources"].append("API")
|
||||||
|
options["web-ui-options"]["spot-providers-enabled-by-default"].append("API")
|
||||||
|
|
||||||
self.write(json.dumps(options, default=serialize_everything))
|
self.write(json.dumps(options, default=serialize_everything))
|
||||||
self.set_status(200)
|
self.set_status(200)
|
||||||
|
|||||||
10
webassets/.idea/.gitignore
generated
vendored
Normal file
10
webassets/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Ignored default folder with query files
|
||||||
|
/queries/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
6
webassets/.idea/vcs.xml
generated
Normal file
6
webassets/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
openapi: 3.0.4
|
openapi: 3.1.0
|
||||||
info:
|
info:
|
||||||
title: Spothole API
|
title: Spothole API
|
||||||
description: |-
|
description: |-
|
||||||
@@ -557,6 +557,12 @@ paths:
|
|||||||
type: integer
|
type: integer
|
||||||
example: 30
|
example: 30
|
||||||
description: The suggested default "maximum spot age" that the web UI should retrieve from the API
|
description: The suggested default "maximum spot age" that the web UI should retrieve from the API
|
||||||
|
spot-providers-enabled-by-default:
|
||||||
|
type: array
|
||||||
|
description: A list of the spot providers that should be enabled in the web UI on first load, if the user hasn't already got a localStorage setting that sets their preference. This is to allow some high-volume providers like RBN to be enabled in Spothole's back-end and displayable in the web UI if the user wants, but by default the experience will not include them.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: "POTA"
|
||||||
alert-count:
|
alert-count:
|
||||||
type: array
|
type: array
|
||||||
description: An array of suggested "alert counts" that the web UI can retrieve from the API
|
description: An array of suggested "alert counts" that the web UI can retrieve from the API
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ function loadOptions() {
|
|||||||
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"]);
|
||||||
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
|
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
|
||||||
generateMultiToggleFilterCard("#source-options", "source", options["spot_sources"]);
|
generateSourcesMultiToggleFilterCard(options["spot_sources"], options["web-ui-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
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ function loadOptions() {
|
|||||||
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"]);
|
||||||
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
|
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
|
||||||
generateMultiToggleFilterCard("#source-options", "source", options["spot_sources"]);
|
generateSourcesMultiToggleFilterCard(options["spot_sources"], options["web-ui-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
|
||||||
|
|||||||
@@ -422,7 +422,7 @@ function loadOptions() {
|
|||||||
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"]);
|
||||||
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
|
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
|
||||||
generateMultiToggleFilterCard("#source-options", "source", options["spot_sources"]);
|
generateSourcesMultiToggleFilterCard(options["spot_sources"], options["web-ui-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
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ var spots = []
|
|||||||
function addBandToggleColourCSS(band_options) {
|
function addBandToggleColourCSS(band_options) {
|
||||||
var $style = $('<style>');
|
var $style = $('<style>');
|
||||||
band_options.forEach(o => {
|
band_options.forEach(o => {
|
||||||
// CSS doesn't like IDs with decimal points in, so we need to replace that
|
var domSafeName = o["name"].replace(/^[^A-Za-z0-9]+|[^\w]+/gi, "");
|
||||||
var cssFormattedBandName = o['name'] ? o['name'].replace('.', 'p') : "unknown";
|
$style.append(`#filter-button-label-band-${domSafeName} { border-color: ${bandToColor(o['name'])}; color: var(--bs-primary);}`);
|
||||||
$style.append(`#filter-button-label-band-${cssFormattedBandName} { border-color: ${bandToColor(o['name'])}; color: var(--bs-primary);}`);
|
$style.append(`.btn-check:checked + #filter-button-label-band-${domSafeName} { background-color: ${bandToColor(o['name'])}; color: ${bandToContrastColor(o['name'])};}`);
|
||||||
$style.append(`.btn-check:checked + #filter-button-label-band-${cssFormattedBandName} { background-color: ${bandToColor(o['name'])}; color: ${bandToContrastColor(o['name'])};}`);
|
|
||||||
});
|
});
|
||||||
$('html > head').append($style);
|
$('html > head').append($style);
|
||||||
}
|
}
|
||||||
@@ -18,10 +17,8 @@ function addBandToggleColourCSS(band_options) {
|
|||||||
function generateBandsMultiToggleFilterCard(band_options) {
|
function generateBandsMultiToggleFilterCard(band_options) {
|
||||||
// Create a button for each option
|
// Create a button for each option
|
||||||
band_options.forEach(o => {
|
band_options.forEach(o => {
|
||||||
// CSS doesn't like IDs with decimal points in, so we need to replace that in the same way as when we originally
|
var domSafeName = o["name"].replace(/^[^A-Za-z0-9]+|[^\w]+/gi, "");
|
||||||
// queried the options endpoint and set our CSS.
|
$("#band-options").append(`<input type="checkbox" class="btn-check filter-button-band storeable-checkbox" name="options" id="filter-button-band-${domSafeName}" value="${o['name']}" autocomplete="off" onClick="filtersUpdated()" checked><label class="btn btn-outline" id="filter-button-label-band-${domSafeName}" for="filter-button-band-${domSafeName}">${o['name']}</label> `);
|
||||||
var cssFormattedBandName = o['name'] ? o['name'].replace('.', 'p') : "unknown";
|
|
||||||
$("#band-options").append(`<input type="checkbox" class="btn-check filter-button-band storeable-checkbox" name="options" id="filter-button-band-${cssFormattedBandName}" value="${o['name']}" autocomplete="off" onClick="filtersUpdated()" checked><label class="btn btn-outline" id="filter-button-label-band-${cssFormattedBandName}" for="filter-button-band-${cssFormattedBandName}">${o['name']}</label> `);
|
|
||||||
});
|
});
|
||||||
// Create All/None/Ham HF buttons
|
// Create All/None/Ham HF buttons
|
||||||
$("#band-options").append(` <span style="display: inline-block"><button id="filter-button-band-all" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('band', true);">All</button> <button id="filter-button-band-none" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('band', false);">None</button> <button id="filter-button-band-none" type="button" class="btn btn-outline-secondary" onclick="setHamHFBandToggles();">Ham HF</button></span>`);
|
$("#band-options").append(` <span style="display: inline-block"><button id="filter-button-band-all" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('band', true);">All</button> <button id="filter-button-band-none" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('band', false);">None</button> <button id="filter-button-band-none" type="button" class="btn btn-outline-secondary" onclick="setHamHFBandToggles();">Ham HF</button></span>`);
|
||||||
@@ -41,7 +38,8 @@ function setHamHFBandToggles() {
|
|||||||
function generateSIGsMultiToggleFilterCard(sig_options) {
|
function generateSIGsMultiToggleFilterCard(sig_options) {
|
||||||
// Create a button for each option
|
// Create a button for each option
|
||||||
sig_options.forEach(o => {
|
sig_options.forEach(o => {
|
||||||
$("#sig-options").append(`<input type="checkbox" class="btn-check filter-button-sig storeable-checkbox" name="options" id="filter-button-sig-${o['name']}" value="${o['name']}" autocomplete="off" onClick="filtersUpdated()" checked><label class="btn btn-outline-primary" id="filter-button-label-sig-${o['name']}" for="filter-button-sig-${o['name']}" title="${o['description']}"><i class="fa-solid ${sigToIcon(o['name'], 'fa-tower-cell')}"></i> ${o['name']}</label> `);
|
var domSafeName = o["name"].replace(/^[^A-Za-z0-9]+|[^\w]+/gi, "");
|
||||||
|
$("#sig-options").append(`<input type="checkbox" class="btn-check filter-button-sig storeable-checkbox" name="options" id="filter-button-sig-${domSafeName}" value="${o['name']}" autocomplete="off" onClick="filtersUpdated()" checked><label class="btn btn-outline-primary" id="filter-button-label-sig-${domSafeName}" for="filter-button-sig-${domSafeName}" title="${o['description']}"><i class="fa-solid ${sigToIcon(o['name'], 'fa-tower-cell')}"></i> ${o['name']}</label> `);
|
||||||
});
|
});
|
||||||
// Create a bonus "NO_SIG" / "General DX" option
|
// Create a bonus "NO_SIG" / "General DX" option
|
||||||
$("#sig-options").append(`<input type="checkbox" class="btn-check filter-button-sig storeable-checkbox" name="options" id="filter-button-sig-NO_SIG" value="NO_SIG" autocomplete="off" onClick="filtersUpdated()" checked><label class="btn btn-outline-primary" id="filter-button-label-sig-NO_SIG" for="filter-button-sig-NO_SIG"><i class="fa-solid fa-tower-cell"></i> General DX</label> `);
|
$("#sig-options").append(`<input type="checkbox" class="btn-check filter-button-sig storeable-checkbox" name="options" id="filter-button-sig-NO_SIG" value="NO_SIG" autocomplete="off" onClick="filtersUpdated()" checked><label class="btn btn-outline-primary" id="filter-button-label-sig-NO_SIG" for="filter-button-sig-NO_SIG"><i class="fa-solid fa-tower-cell"></i> General DX</label> `);
|
||||||
@@ -49,6 +47,20 @@ function generateSIGsMultiToggleFilterCard(sig_options) {
|
|||||||
$("#sig-options").append(` <span style="display: inline-block"><button id="filter-button-sig-all" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('sig', true);">All</button> <button id="filter-button-sig-none" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('sig', false);">None</button></span>`);
|
$("#sig-options").append(` <span style="display: inline-block"><button id="filter-button-sig-all" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('sig', true);">All</button> <button id="filter-button-sig-none" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('sig', false);">None</button></span>`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate Sources filter card. This one is a minor special case as we create the buttons in the normal way, but then
|
||||||
|
// set which ones are enabled by default based on config rather than having them all enabled by default. We also sanitise
|
||||||
|
// names here for HTML elements.
|
||||||
|
function generateSourcesMultiToggleFilterCard(source_options, sources_enabled_by_default) {
|
||||||
|
// Create a button for each option
|
||||||
|
source_options.forEach(o => {
|
||||||
|
var enable = sources_enabled_by_default.includes(o);
|
||||||
|
var domSafeName = o.replace(/^[^A-Za-z0-9]+|[^\w]+/gi, "");
|
||||||
|
$("#source-options").append(`<input type="checkbox" class="btn-check filter-button-source storeable-checkbox" name="options" id="filter-button-source-${domSafeName}" value="${o}" autocomplete="off" onClick="filtersUpdated()" ${enable ? "checked" : ""}><label class="btn btn-outline-primary" for="filter-button-source-${domSafeName}">${o}</label> `);
|
||||||
|
});
|
||||||
|
// Create All/None buttons
|
||||||
|
$("#source-options").append(` <span style="display: inline-block"><button id="filter-button-source-all" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('source', true);">All</button> <button id="filter-button-source-none" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('source', false);">None</button></span>`);
|
||||||
|
}
|
||||||
|
|
||||||
// Method called when any filter is changed to reload the spots and persist the filter settings.
|
// Method called when any filter is changed to reload the spots and persist the filter settings.
|
||||||
function filtersUpdated() {
|
function filtersUpdated() {
|
||||||
loadSpots();
|
loadSpots();
|
||||||
|
|||||||
Reference in New Issue
Block a user