Start converting some enum-like strings to proper enums, plus global reformat

This commit is contained in:
Ian Renton
2026-09-02 08:37:13 +01:00
parent 5a6fc19ab5
commit 606cf1be68
24 changed files with 81 additions and 79 deletions
-1
View File
@@ -276,7 +276,6 @@ DATA_MODES = [
"MSK144", "MSK144",
] ]
ALL_MODES = CW_MODES + PHONE_MODES + DATA_MODES ALL_MODES = CW_MODES + PHONE_MODES + DATA_MODES
MODE_TYPES = ["CW", "PHONE", "DATA"]
# Mode aliases. Sometimes we get spots with a mode described in a different way that is effectively the same as a mode # Mode aliases. Sometimes we get spots with a mode described in a different way that is effectively the same as a mode
# we already know, or we want to normalise things for consistency. The lookup table for this is here. Incoming spots # we already know, or we want to normalise things for consistency. The lookup table for this is here. Incoming spots
+7
View File
@@ -11,6 +11,13 @@ class Continent(str, Enum):
AN = "AN" AN = "AN"
class ModeType(str, Enum):
PHONE = "PHONE"
CW = "CW"
DATA = "DATA"
UNKNOWN = "UNKNOWN"
class ModeSource(str, Enum): class ModeSource(str, Enum):
"""Where the mode data came from in a spot.""" """Where the mode data came from in a spot."""
+4 -4
View File
@@ -15,7 +15,7 @@ from core.constants import (
UNKNOWN_BAND, UNKNOWN_BAND,
) )
from core.data_store import DATA_STORE from core.data_store import DATA_STORE
from core.enums import Continent from core.enums import Continent, ModeType
from data.callsign import Callsign, LocationSourceForCallsign from data.callsign import Callsign, LocationSourceForCallsign
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -44,11 +44,11 @@ def infer_mode_type_from_mode(mode):
"""Infer a "mode family" from a mode.""" """Infer a "mode family" from a mode."""
if mode.upper() in CW_MODES: if mode.upper() in CW_MODES:
return "CW" return ModeType.CW
elif mode.upper() in PHONE_MODES: elif mode.upper() in PHONE_MODES:
return "PHONE" return ModeType.PHONE
elif mode.upper() in DATA_MODES: elif mode.upper() in DATA_MODES:
return "DATA" return ModeType.DATA
else: else:
if mode.upper() != "OTHER" and mode != "?": if mode.upper() != "OTHER" and mode != "?":
logger.warning(f"Found an unrecognised mode: {mode}. Developer should categorise this.") logger.warning(f"Found an unrecognised mode: {mode}. Developer should categorise this.")
+3 -3
View File
@@ -1,6 +1,6 @@
from dataclasses import dataclass from dataclasses import dataclass
from core.enums import LocationSourceForCallsign from core.enums import Continent, LocationSourceForCallsign
@dataclass @dataclass
@@ -30,14 +30,14 @@ class Callsign:
# Country in which the callsign indicates they are operating # Country in which the callsign indicates they are operating
country: str | None = None country: str | None = None
# Continent in which the callsign indicates they are operating # Continent in which the callsign indicates they are operating
continent: str | None = None continent: Continent | None = None
# DXCC ID in which the callsign indicates they are operating # DXCC ID in which the callsign indicates they are operating
dxcc_id: int | None = None dxcc_id: int | None = None
# CQ zone in which the callsign indicates they are operating # CQ zone in which the callsign indicates they are operating
cq_zone: int | None = None cq_zone: int | None = None
# ITU zone in which the callsign indicates they are operating # ITU zone in which the callsign indicates they are operating
itu_zone: int | None = None itu_zone: int | None = None
# Location source. This can be "HOME QTH" or "DXCC" depending on which provider gave us a location # Location source
location_source: LocationSourceForCallsign = LocationSourceForCallsign.NONE location_source: LocationSourceForCallsign = LocationSourceForCallsign.NONE
def fully_populated(self): def fully_populated(self):
+4 -4
View File
@@ -12,7 +12,7 @@ from pyhamtools.locator import latlong_to_locator, locator_to_latlong
from core.call_lookup_helper import get_call_info from core.call_lookup_helper import get_call_info
from core.config import MAX_SPOT_AGE from core.config import MAX_SPOT_AGE
from core.constants import MODE_ALIASES, PROPAGATION_MODES, SIGS from core.constants import MODE_ALIASES, PROPAGATION_MODES, SIGS
from core.enums import Continent, LocationSourceForSpot, ModeSource from core.enums import Continent, LocationSourceForSpot, ModeSource, ModeType
from core.geo_utils import lat_lon_to_cq_zone, lat_lon_to_itu_zone from core.geo_utils import lat_lon_to_cq_zone, lat_lon_to_itu_zone
from core.sig_lookup_helper import populate_missing_sig_ref_info from core.sig_lookup_helper import populate_missing_sig_ref_info
from core.sig_utils import ( from core.sig_utils import (
@@ -104,9 +104,9 @@ class Spot:
# Reported mode, such as SSB, PHONE, CW, FT8... # Reported mode, such as SSB, PHONE, CW, FT8...
mode: str | None = None mode: str | None = None
# Inferred mode "family". One of "CW", "PHONE" or "DIGI". # Inferred mode "family".
mode_type: str | None = None mode_type: ModeType = ModeType.UNKNOWN
# Source of the mode information. "SPOT", "COMMENT", "BANDPLAN" or "NONE" # Source of the mode information.
mode_source: ModeSource = ModeSource.NONE mode_source: ModeSource = ModeSource.NONE
# Frequency, in Hz # Frequency, in Hz
freq: float | None = None freq: float | None = None
+12 -11
View File
@@ -23,15 +23,16 @@ You can replace `#main` with any other branch or tag reference, for example `#1.
Save the file. You will still need to create a copy of `config-example.yml` and name it `config.yml`, though with the Save the file. You will still need to create a copy of `config-example.yml` and name it `config.yml`, though with the
Docker setup nothing has actually been downloaded yet, so you will have to copy the example from the repository some Docker setup nothing has actually been downloaded yet, so you will have to copy the example from the repository some
other way, e.g. [from the repo in a web browser](https://git.ianrenton.com/ian/spothole/src/branch/main/config-example.yml). other way,
e.g. [from the repo in a web browser](https://git.ianrenton.com/ian/spothole/src/branch/main/config-example.yml).
With that in place, run `docker compose up` and you should be good to go. To detach, press `d` or run the command with With that in place, run `docker compose up` and you should be good to go. To detach, press `d` or run the command with
the `-d` flag. the `-d` flag.
### nginx Reverse Proxy with Docker ### nginx Reverse Proxy with Docker
In a containerised setup, it's typical to run an nginx reverse proxy in one container, alongside certbot for renewal In a containerised setup, it's typical to run an nginx reverse proxy in one container, alongside certbot for renewal of
of HTTPS certificates, and then applications like Spothole in a separate container. In this case, there are a couple of HTTPS certificates, and then applications like Spothole in a separate container. In this case, there are a couple of
variations of the docker compose file above, and the nginx reverse proxy configuration covered [here](./nginx.md), that variations of the docker compose file above, and the nginx reverse proxy configuration covered [here](./nginx.md), that
you will want to make. you will want to make.
@@ -60,8 +61,8 @@ networks:
``` ```
In your nginx site configuration, you'll want to refer to the Spothole container directly, and drop the block that In your nginx site configuration, you'll want to refer to the Spothole container directly, and drop the block that
allows nginx to access static files directly, as these will be inaccessible in another container. So you may end up allows nginx to access static files directly, as these will be inaccessible in another container. So you may end up with
with something like: something like:
```nginx ```nginx
server { server {
@@ -146,13 +147,13 @@ server {
``` ```
If desired, you could even change the port on which Spothole runs from 8080 to a plain 80, in which case your If desired, you could even change the port on which Spothole runs from 8080 to a plain 80, in which case your
`proxy_pass` statements could drop the `:8080` suffix. Since Spothole is in a container, it can serve HTTP on port 80 `proxy_pass` statements could drop the `:8080` suffix. Since Spothole is in a container, it can serve HTTP on port 80 if
if desired, because it doesn't conflict with the host system. desired, because it doesn't conflict with the host system.
### Restoring the static files bypass ### Restoring the static files bypass
If you would still like to bypass Spothole's web server for the static files, and serve them with nginx, you can If you would still like to bypass Spothole's web server for the static files, and serve them with nginx, you can do. The
do. The easiest way is to run another nginx container to serve the files, so your Spothole `compose.yaml` becomes: easiest way is to run another nginx container to serve the files, so your Spothole `compose.yaml` becomes:
```yaml ```yaml
services: services:
@@ -183,8 +184,8 @@ networks:
external: true external: true
``` ```
Then you can re-add the block that handles the `/static` path in your nginx reverse proxy config, but this time Then you can re-add the block that handles the `/static` path in your nginx reverse proxy config, but this time point it
point it at the new container rather than at a filesystem path: at the new container rather than at a filesystem path:
```nginx ```nginx
# Load static assets from the spothole-static-nginx container # Load static assets from the spothole-static-nginx container
+2 -2
View File
@@ -48,8 +48,8 @@ To navigate your way around the source code, this list may help.
### Extending the server ### Extending the server
Spothole is designed to be easily extensible. If you want to write your own spot provider, for example, simply add a Spothole is designed to be easily extensible. If you want to write your own spot provider, for example, simply add a
module to the `providers.spot` package containing your class. (Currently, in order to be loaded correctly, the module ( module to the `providers.spot` package containing your class. (Currently, in order to be loaded correctly, the module
file) name should be the same as the class name, but lower case.) (file) name should be the same as the class name, but lower case.)
Your class should extend "SpotProvider"; if it operates by polling an HTTP Server on a timer, it can instead extend " Your class should extend "SpotProvider"; if it operates by polling an HTTP Server on a timer, it can instead extend "
HTTPSpotProvider" where some of the work is done for you. HTTPSpotProvider" where some of the work is done for you.
+10 -15
View File
@@ -2,8 +2,8 @@
Web servers generally serve their pages from port 80. However, it's best not to serve Spothole's web interface directly Web servers generally serve their pages from port 80. However, it's best not to serve Spothole's web interface directly
on port 80, as that requires root privileges on a Linux system. It also and prevents us using HTTPS to serve a secure on port 80, as that requires root privileges on a Linux system. It also and prevents us using HTTPS to serve a secure
site, since Spothole itself doesn't directly support acting as an HTTPS server. The normal solution to this is to use site, since Spothole itself doesn't directly support acting as an HTTPS server. The normal solution to this is to use a
a "reverse proxy" setup, where a general web server handles HTTP and HTTP requests (to port 80 & 443 respectively), then "reverse proxy" setup, where a general web server handles HTTP and HTTP requests (to port 80 & 443 respectively), then
passes on the request to the back-end application (in this case Spothole). nginx is a common choice for this general web passes on the request to the back-end application (in this case Spothole). nginx is a common choice for this general web
server. server.
@@ -89,19 +89,14 @@ server {
``` ```
One further change you might want to make to the file above is the `add_header Access-Control-Allow-Origin` statements. One further change you might want to make to the file above is the `add_header Access-Control-Allow-Origin` statements.
These are what's used on These are what's used on my own Spothole server to make sure that other third-party web-based software can get the data
my own Spothole server to make sure that other third-party web-based software can get the data from my instance, and from my instance, and applies to any endpoint underneath `/api`. If you want *your* Spothole instance to be set up the
applies to any endpoint underneath `/api`. If you want same way, so that others can write software in JavaScript that can access it, leave this intact. But if you want your
*your* Spothole instance to be set up the same way, so that others can write software in JavaScript that can access it, Spothole instance to only be usable by scripts running on the web server you write, you can remove these lines. (Note
leave this intact. But if you want your Spothole instance to only be usable by scripts running on the web server you that this doesn't stop other people writing *non-web-based* software that accesses your Spothole API—the
write, enforcement of cross-origin headers only happens within the user's browser. If you need to lock your instance down so
you can remove these lines. (Note that this doesn't stop other people writing *non-web-based* software that accesses that no-one else can access it with *any* software, that's an aspect of nginx or firewall config that you will need to
your find help with elsewhere.)
Spothole API—the enforcement of cross-origin headers only happens within the user's browser. If you need to lock
your
instance down so that no-one else can access it with *any* software, that's an aspect of nginx or firewall config that
you will need
to find help with elsewhere.)
Now, make a symbolic link to enable the site: Now, make a symbolic link to enable the site:
+2 -4
View File
@@ -10,12 +10,10 @@ from requests_cache import CachedSession
from core.constants import HTTP_HEADERS from core.constants import HTTP_HEADERS
from core.data_store import CACHE_DIR, DATA_STORE from core.data_store import CACHE_DIR, DATA_STORE
from core.enums import LocationSourceForCallsign, Continent from core.enums import Continent, LocationSourceForCallsign
from core.url_data_cache import URLDataCache from core.url_data_cache import URLDataCache
from data.callsign import Callsign from data.callsign import Callsign
from providers.callsigndata.api_query_callsign_data_provider import ( from providers.callsigndata.api_query_callsign_data_provider import APIQueryCallsignDataProvider
APIQueryCallsignDataProvider,
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
+1 -8
View File
@@ -20,14 +20,7 @@ class DTMBA(FileDownloadSIGRefDataProvider):
split = row.split(";") split = row.split(";")
ref_id = split[0] ref_id = split[0]
ref_name = split[1] ref_name = split[1]
new_data.append( new_data.append(SIGRef(sig=self.SIG, id=ref_id, name=ref_name, ref_type="Building"))
SIGRef(
sig=self.SIG,
id=ref_id,
name=ref_name,
ref_type="Building"
)
)
# Bail out if a stop has been requested, i.e. the program is shutting down - no need to parse the rest of # Bail out if a stop has been requested, i.e. the program is shutting down - no need to parse the rest of
# the data in this case # the data in this case
+3 -1
View File
@@ -76,7 +76,9 @@ class WOTA(HTTPSpotProvider):
except Exception: except Exception:
logger.warning(f"Could not parse WOTA spot description: {source_spot.description}") logger.warning(f"Could not parse WOTA spot description: {source_spot.description}")
time = datetime.strptime(source_spot.pub_date.content, self.RSS_DATE_TIME_FORMAT).astimezone(pytz.UTC) time = datetime.strptime(source_spot.pub_date.content, self.RSS_DATE_TIME_FORMAT).astimezone(
pytz.UTC
)
# Convert to our spot format # Convert to our spot format
spot = Spot( spot = Spot(
+3 -3
View File
@@ -8,8 +8,8 @@ from tornado import httputil
from tornado.web import Application from tornado.web import Application
from core.config import ALLOW_SPOTTING, MAX_SPOT_AGE from core.config import ALLOW_SPOTTING, MAX_SPOT_AGE
from core.constants import ALL_MODES, BANDS, MODE_TYPES, PROPAGATION_MODES, SIGS from core.constants import ALL_MODES, BANDS, PROPAGATION_MODES, SIGS
from core.enums import Continent from core.enums import Continent, ModeType
from core.prometheus_metrics_handler import api_requests_counter from core.prometheus_metrics_handler import api_requests_counter
from core.utils import safe_json_dumps from core.utils import safe_json_dumps
@@ -84,7 +84,7 @@ class APIOptionsHandler(tornado.web.RequestHandler):
options = { options = {
"bands": BANDS, "bands": BANDS,
"modes": ALL_MODES, "modes": ALL_MODES,
"mode_types": MODE_TYPES, "mode_types": [t.value for t in ModeType],
"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,
+2 -2
View File
@@ -2,7 +2,6 @@ import tornado
from tornado.httpclient import AsyncHTTPClient from tornado.httpclient import AsyncHTTPClient
from tornado.httputil import HTTPHeaders from tornado.httputil import HTTPHeaders
_LEGACY_PARAM_TO_HEADER_MAP = { _LEGACY_PARAM_TO_HEADER_MAP = {
"qrz_username": "X-QRZ-Username", "qrz_username": "X-QRZ-Username",
"qrz_password": "X-QRZ-Password", "qrz_password": "X-QRZ-Password",
@@ -12,6 +11,7 @@ _LEGACY_PARAM_TO_HEADER_MAP = {
"hamqth_session_id": "X-HamQTH-Session-ID", "hamqth_session_id": "X-HamQTH-Session-ID",
} }
class V1RedirectHandler(tornado.web.RequestHandler): class V1RedirectHandler(tornado.web.RequestHandler):
"""Transparently proxies requests from the old API to the new one, """Transparently proxies requests from the old API to the new one,
returning whatever the v2 endpoint returns, for endpoints with no breaking changes.""" returning whatever the v2 endpoint returns, for endpoints with no breaking changes."""
@@ -60,4 +60,4 @@ class V1RedirectHandler(tornado.web.RequestHandler):
await self._proxy(path) await self._proxy(path)
async def post(self, path): async def post(self, path):
await self._proxy(path) await self._proxy(path)
+1
View File
@@ -23,6 +23,7 @@ def _handle_legacy_params(handler):
if value: if value:
handler.request.headers[header] = value handler.request.headers[header] = value
class V1APISpotsHandler(APISpotsHandler): class V1APISpotsHandler(APISpotsHandler):
"""API request handler for /api/v1/spots (GET). Included in early Spothole v2 for backwards compatibility.""" """API request handler for /api/v1/spots (GET). Included in early Spothole v2 for backwards compatibility."""
+2
View File
@@ -20,6 +20,7 @@ info:
* Added DTMBA, FEA, BIWOTA, COTA & PGA SIGs * Added DTMBA, FEA, BIWOTA, COTA & PGA SIGs
* Removed the distinction between LSB & USB (both will now show as SSB) and between the various digital voice modes, which will now show as DV. * Removed the distinction between LSB & USB (both will now show as SSB) and between the various digital voice modes, which will now show as DV.
* Added `icon`, `region_flag` and `refs_globally_unique` to SIG information * Added `icon`, `region_flag` and `refs_globally_unique` to SIG information
* Unknown mode types now return "UNKNOWN" not null
### 2.0 ### 2.0
@@ -945,6 +946,7 @@ components:
- CW - CW
- PHONE - PHONE
- DATA - DATA
- UNKNOWN
example: CW example: CW
ModeSource: ModeSource:
+5 -3
View File
@@ -179,13 +179,15 @@
filters. They are also stored in your browser's local storage, so that your preferences are remembered between filters. They are also stored in your browser's local storage, so that your preferences are remembered between
sessions.</p> sessions.</p>
<p>The data you provide can optionally include your login credentials for QRZ.com and HamQTH. You can provide these <p>The data you provide can optionally include your login credentials for QRZ.com and HamQTH. You can provide these
in the "Your Data" menu of most pages. If you do, Spothole will augment the data it produces with lookups from these in the "Your Data" menu of most pages. If you do, Spothole will augment the data it produces with lookups from
these
services, which can for example provide more accurate markers on the map tab, and operator names when you mouse services, which can for example provide more accurate markers on the map tab, and operator names when you mouse
over a DX callsign. Spothole will still work fine if you don't provide these. The values you enter are sent to over a DX callsign. Spothole will still work fine if you don't provide these. The values you enter are sent to
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>Any data you send as part of a query, such as your QRZ or HamQTH credentials, is used only for the lifetime of that <p>Any data you send as part of a query, such as your QRZ or HamQTH credentials, is used only for the lifetime of
query and is not saved anywhere apart from your own device.</p> that
query and is not saved anywhere apart from your own device.</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.
+3 -2
View File
@@ -2,7 +2,8 @@
{% block content %} {% block content %}
<div id="add-spot-intro-box" class="permanently-dismissible-box mt-3"> <div id="add-spot-intro-box" class="permanently-dismissible-box mt-3">
<div class="alert alert-primary alert-dismissible fade show" role="alert"> <!-- TODO Remove when feature available --> <div class="alert alert-primary alert-dismissible fade show" role="alert">
<!-- TODO Remove when feature available -->
<i class="fa-solid fa-circle-info"></i> <strong>Adding spots to Spothole</strong><br/>This page is implemented <i class="fa-solid fa-circle-info"></i> <strong>Adding spots to Spothole</strong><br/>This page is implemented
as a proof of concept for adding spots to the Spothole system. Currently, spots added in this way are only as a proof of concept for adding spots to the Spothole system. Currently, spots added in this way are only
visible within Spothole and are not sent "upstream" to DX clusters or xOTA spotting sites. The functionality visible within Spothole and are not sent "upstream" to DX clusters or xOTA spotting sites. The functionality
@@ -76,7 +77,7 @@
</div> </div>
<script src="/static/js/add-spot.js?v=1788331562"></script> <script src="/static/js/add-spot.js?v=1788334633"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-add-spot").addClass("active"); $("#nav-link-add-spot").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+1 -1
View File
@@ -84,7 +84,7 @@
</div> </div>
<script src="/static/js/alerts.js?v=1788331562"></script> <script src="/static/js/alerts.js?v=1788334633"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-alerts").addClass("active"); $("#nav-link-alerts").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -76,8 +76,8 @@
</div> </div>
<script src="/static/js/spotsbandsandmap.js?v=1788331562"></script> <script src="/static/js/spotsbandsandmap.js?v=1788334633"></script>
<script src="/static/js/bands.js?v=1788331562"></script> <script src="/static/js/bands.js?v=1788334633"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-bands").addClass("active"); $("#nav-link-bands").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+8 -7
View File
@@ -1,6 +1,6 @@
{% extends "skeleton.html" %} {% extends "skeleton.html" %}
{% block head_extra %} {% block head_extra %}
<link rel="stylesheet" href="/static/css/style.css?v=1788331562" type="text/css"> <link rel="stylesheet" href="/static/css/style.css?v=1788334633" type="text/css">
<link href="/static/vendor/css/bootstrap-5.3.8.min.css" rel="stylesheet"> <link href="/static/vendor/css/bootstrap-5.3.8.min.css" rel="stylesheet">
<link href="/static/vendor/css/fontawesome-6.7.2.min.css" rel="stylesheet"> <link href="/static/vendor/css/fontawesome-6.7.2.min.css" rel="stylesheet">
<link href="/static/vendor/css/solid-6.7.2.min.css" rel="stylesheet"> <link href="/static/vendor/css/solid-6.7.2.min.css" rel="stylesheet">
@@ -11,14 +11,15 @@
<script src="/static/vendor/js/tinycolor2-1.6.0.min.js"></script> <script src="/static/vendor/js/tinycolor2-1.6.0.min.js"></script>
<script type="module"> <script type="module">
import { fetchEventSource } from '/static/vendor/js/fetch-event-source-2.0.1/index.js'; import {fetchEventSource} from '/static/vendor/js/fetch-event-source-2.0.1/index.js';
window.fetchEventSource = fetchEventSource;
window.fetchEventSource = fetchEventSource;
</script> </script>
<script src="/static/js/utils.js?v=1788331562"></script> <script src="/static/js/utils.js?v=1788334633"></script>
<script src="/static/js/ui-ham.js?v=1788331562"></script> <script src="/static/js/ui-ham.js?v=1788334633"></script>
<script src="/static/js/geo.js?v=1788331562"></script> <script src="/static/js/geo.js?v=1788334633"></script>
<script src="/static/js/common.js?v=1788331562"></script> <script src="/static/js/common.js?v=1788334633"></script>
{% end %} {% end %}
{% block body %} {% block body %}
<div class="container"> <div class="container">
+1 -1
View File
@@ -284,7 +284,7 @@
</div> </div>
<script src="/static/vendor/js/chart-4.4.9.umd.min.js"></script> <script src="/static/vendor/js/chart-4.4.9.umd.min.js"></script>
<script src="/static/js/conditions.js?v=1788331562"></script> <script src="/static/js/conditions.js?v=1788334633"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-conditions").addClass("active"); $("#nav-link-conditions").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -113,8 +113,8 @@
const CARTODB_API_KEY = "{{ web_ui_options.get('cartodb_api_key', '') }}"; const CARTODB_API_KEY = "{{ web_ui_options.get('cartodb_api_key', '') }}";
</script> </script>
<script src="/static/js/spotsbandsandmap.js?v=1788331562"></script> <script src="/static/js/spotsbandsandmap.js?v=1788334633"></script>
<script src="/static/js/map.js?v=1788331562"></script> <script src="/static/js/map.js?v=1788334633"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-map").addClass("active"); $("#nav-link-map").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -113,8 +113,8 @@
</div> </div>
<script src="/static/js/spotsbandsandmap.js?v=1788331562"></script> <script src="/static/js/spotsbandsandmap.js?v=1788334633"></script>
<script src="/static/js/spots.js?v=1788331562"></script> <script src="/static/js/spots.js?v=1788334633"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-spots").addClass("active"); $("#nav-link-spots").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+1 -1
View File
@@ -86,7 +86,7 @@
</div> </div>
</div> </div>
<script src="/static/js/status.js?v=1788331562"></script> <script src="/static/js/status.js?v=1788334633"></script>
<script> <script>
$(document).ready(function () { $(document).ready(function () {
$("#nav-link-status").addClass("active"); $("#nav-link-status").addClass("active");