mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-09-20 22:37:44 +00:00
Start converting some enum-like strings to proper enums, plus global reformat
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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:
|
||||||
|
|
||||||
|
|||||||
@@ -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__)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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,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."""
|
||||||
|
|||||||
@@ -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."""
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -179,12 +179,14 @@
|
|||||||
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
|
||||||
|
that
|
||||||
query and is not saved anywhere apart from your own device.</p>
|
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 %}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
+7
-6
@@ -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">
|
||||||
|
|||||||
@@ -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
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user