Files
spothole/webserver/handlers/api/options.py
T

91 lines
3.6 KiB
Python

import functools
import logging
from typing import Any
import tornado
from tornado import httputil
from tornado.web import Application
from core.config import ALLOW_SPOTTING, MAX_SPOT_AGE
from core.constants import BANDS, PROPAGATION_MODES
from core.data_providers import DATA_PROVIDERS
from core.enums import Continent, Mode, ModeType
from core.utils import safe_json_dumps
from data.activities import ACTIVITIES
logger = logging.getLogger(__name__)
@functools.cache
def build_options():
"""Build the options data that is returned by the /options API and used to populate the web UI templates. This
doesn't change at runtime, so it is built once on first use and then cached."""
# Build a map of activity name -> list of provider names that can submit spots for that activity. Spots with no
# activity use the empty string as the key as that's effectively what is in the "blank" entry in the drop-down
# on the Add Spot page
spot_submit_providers = {}
for provider in DATA_PROVIDERS.spot_providers:
if not provider.enabled:
continue
for activity_name in [None, *(activity.name for activity in ACTIVITIES.values())]:
if provider.can_submit_spot(activity_name):
names = spot_submit_providers.setdefault(activity_name or "", [])
if provider.name not in names:
names.append(provider.name)
# Spot/alert sources are filtered for only ones that are enabled in config, no point letting the user toggle
# things that aren't even available.
spot_providers = [p.name for p in DATA_PROVIDERS.spot_providers if p.enabled]
alert_providers = [p.name for p in DATA_PROVIDERS.alert_providers if p.enabled]
callsign_data_providers = [p.name for p in DATA_PROVIDERS.callsign_data_providers if p.enabled]
spot_providers_enabled_by_default = [
p.name for p in DATA_PROVIDERS.spot_providers if p.enabled and p.enabled_by_default_in_web_ui
]
# If spotting to this server is enabled, "API" is another valid spot source even though it does not come from
# one of our providers.
if ALLOW_SPOTTING:
spot_providers.append("API")
spot_providers_enabled_by_default.append("API")
return {
"bands": BANDS,
"modes": [m.value for m in Mode],
"mode_types": [t.value for t in ModeType],
"activities": list(ACTIVITIES.values()),
"spot_providers": spot_providers,
"spot_providers_enabled_by_default": spot_providers_enabled_by_default,
"alert_providers": alert_providers,
"callsign_data_providers": callsign_data_providers,
"continents": [c.value for c in Continent],
"propagation_modes": list(PROPAGATION_MODES.values()),
"max_spot_age": MAX_SPOT_AGE,
"spot_allowed": ALLOW_SPOTTING,
"spot_submit_providers": spot_submit_providers,
}
class APIOptionsHandler(tornado.web.RequestHandler):
"""API request handler for /api/v3/options"""
def __init__(
self,
application: "Application",
request: httputil.HTTPServerRequest,
**kwargs: Any,
):
super().__init__(application, request, **kwargs)
def get(self):
try:
self.write(safe_json_dumps(build_options()))
self.set_status(200)
self.set_header("Cache-Control", "max-age=3600")
self.set_header("Content-Type", "application/json")
except Exception:
logger.exception("Exception when handling client request to options API")
self.write(safe_json_dumps("Error - an internal server error occurred."))
self.set_status(500)