Allow adding spots #2

This commit is contained in:
Ian Renton
2025-10-04 13:03:52 +01:00
parent f75f79a108
commit 0419aab83f
2 changed files with 30 additions and 5 deletions

View File

@@ -2,6 +2,7 @@ import json
import logging import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta
from threading import Thread from threading import Thread
from types import SimpleNamespace
import bottle import bottle
import pytz import pytz
@@ -10,6 +11,7 @@ from bottle import run, response, template
from core.config import MAX_SPOT_AGE from core.config import MAX_SPOT_AGE
from core.constants import BANDS, ALL_MODES, MODE_TYPES, SIGS, CONTINENTS from core.constants import BANDS, ALL_MODES, MODE_TYPES, SIGS, CONTINENTS
from core.utils import serialize_everything from core.utils import serialize_everything
from data.spot import Spot
# Provides the public-facing web server. # Provides the public-facing web server.
@@ -61,11 +63,34 @@ class WebServer:
self.last_api_access_time = datetime.now(pytz.UTC) self.last_api_access_time = datetime.now(pytz.UTC)
self.status = "OK" self.status = "OK"
print(bottle.request.forms.spot) try:
# Reject if no spot
if not bottle.request.query.spot:
response.content_type = 'application/json'
response.status = 422
return json.dumps("Error - no 'spot' parameter provided", default=serialize_everything)
response.content_type = 'application/json' # Read in the spot as JSON then convert to a Spot object
response.set_header('Cache-Control', 'no-store') json_spot = json.loads(bottle.request.query.spot)
return json.dumps("OK", default=serialize_everything) spot = Spot(**json_spot)
# Reject if no timestamp or dx_call
if not spot.time or not spot.dx_call:
response.content_type = 'application/json'
response.status = 422
return json.dumps("Error - 'time' and 'dx_call' must be provided as a minimum.", default=serialize_everything)
# infer missing data, and add it to our database.
spot.infer_missing()
self.spots.add(spot.guid, spot, expire=MAX_SPOT_AGE)
response.content_type = 'application/json'
response.set_header('Cache-Control', 'no-store')
return json.dumps("OK", default=serialize_everything)
except Exception:
response.content_type = 'application/json'
response.status = 422
return json.dumps("An error occurred parsing your spot. Check it is compliant with the API.", default=serialize_everything)
# Serve a templated page # Serve a templated page
def serve_template(self, template_name): def serve_template(self, template_name):

View File

@@ -5,7 +5,7 @@
<p>Spothole is a utility to aggregate "spots" from amateur radio DX clusters and xOTA spotting sites, and provide an open JSON API as well as a website to browse the data.</p> <p>Spothole is a utility to aggregate "spots" from amateur radio DX clusters and xOTA spotting sites, and provide an open JSON API as well as a website to browse the data.</p>
<p>While there are several other web-based interfaces to DX clusters, and sites that aggregate spots from various outdoor activity programmes for amateur radio, Spothole differentiates itself by supporting a large number of data sources, and by being "API first" rather than just providing a web front-end. This allows other software to be built on top of it.</p> <p>While there are several other web-based interfaces to DX clusters, and sites that aggregate spots from various outdoor activity programmes for amateur radio, Spothole differentiates itself by supporting a large number of data sources, and by being "API first" rather than just providing a web front-end. This allows other software to be built on top of it.</p>
<p>The API is deliberately well-defined with an <a href="/apidocs/openapi.yml">OpenAPI specification</a> and auto-generated <a href="/apidocs">API documentation</a>. The API delivers spots in a consistent format regardless of the data source, freeing developers from needing to know how each individual data source presents its data.</p> <p>The API is deliberately well-defined with an <a href="/apidocs/openapi.yml">OpenAPI specification</a> and auto-generated <a href="/apidocs">API documentation</a>. The API delivers spots in a consistent format regardless of the data source, freeing developers from needing to know how each individual data source presents its data.</p>
<p>Spothole itself is also open source, Public Domain licenced code that anyone can take and modify. <a href="https://git.ianrenton.com/ian/metaspot/">The source code is here</a>. If you want to run your own copy of Spothole, or start modifying it for your own purposes, the README file there has all the details.</p> <p>Spothole itself is also open source, Public Domain licenced code that anyone can take and modify. <a href="https://git.ianrenton.com/ian/metaspot/">The source code is here</a>. If you want to run your own copy of Spothole, or start modifying it for your own purposes, the <a href="https://git.ianrenton.com/ian/spothole/src/branch/main/README.md">README file</a> contains a description of how the software works and how it's laid out, as well as instructions for configuring systemd, nginx and anything else you might need to run your own server.</p>
<p>Supported data sources include DX Clusters, the Reverse Beacon Network (RBN), the APRS Internet Service (APRS-IS), POTA, SOTA, WWFF, GMA, WWBOTA, HEMA, and Parks 'n' Peaks.</p> <p>Supported data sources include DX Clusters, the Reverse Beacon Network (RBN), the APRS Internet Service (APRS-IS), POTA, SOTA, WWFF, GMA, WWBOTA, HEMA, and Parks 'n' Peaks.</p>
<p>The software was written by <a href="https://ianrenton.com">Ian Renton, MØTRT</a>.</p> <p>The software was written by <a href="https://ianrenton.com">Ian Renton, MØTRT</a>.</p>
<p><a href="/">&laquo; Back home</a></p> <p><a href="/">&laquo; Back home</a></p>