Status API

This commit is contained in:
Ian Renton
2025-09-27 17:07:55 +01:00
parent 9d9687adbf
commit 624a5fe28a
4 changed files with 33 additions and 13 deletions

12
main.py
View File

@@ -55,8 +55,9 @@ if __name__ == '__main__':
DXCluster("hrd.wa9pie.net", 8000), DXCluster("hrd.wa9pie.net", 8000),
# DXCluster("dxc.w3lpl.net", 22) # DXCluster("dxc.w3lpl.net", 22)
] ]
# Set up spot list # Set up spot list & status data areas
spot_list = [] spot_list = []
status_data = {}
# Set up data providers # Set up data providers
for p in providers: p.setup(spot_list=spot_list) for p in providers: p.setup(spot_list=spot_list)
# Start data providers # Start data providers
@@ -67,13 +68,18 @@ if __name__ == '__main__':
cleanup_timer.start() cleanup_timer.start()
# Set up web server # Set up web server
web_server = WebServer(spot_list=spot_list, port=config["web-server-port"]) web_server = WebServer(spot_list=spot_list, status_data=status_data, port=config["web-server-port"])
web_server.start() web_server.start()
logging.info("Startup complete.") logging.info("Startup complete.")
# While running, update the status information at a regular interval
while run: while run:
sleep(1) sleep(5)
for p in providers: status_data[p.name()] = {"status": p.status, "last_updated": p.last_update_time, "last_spot": p.last_spot_time}
status_data["Cleanup Timer"] = {"status": cleanup_timer.status, "last_ran": cleanup_timer.last_cleanup_time}
status_data["Web Server"] = {"status": web_server.status, "last_api_access": web_server.last_api_access_time, "last_page_access": web_server.last_page_access_time}
# TODO NOTES FOR NGINX REVERSE PROXY # TODO NOTES FOR NGINX REVERSE PROXY

View File

@@ -67,7 +67,7 @@ class DXCluster(Provider):
if match: if match:
spot_time = datetime.strptime(match.group(5), "%H%MZ") spot_time = datetime.strptime(match.group(5), "%H%MZ")
spot_datetime = datetime.combine(datetime.today(), spot_time.time()).replace(tzinfo=pytz.UTC) spot_datetime = datetime.combine(datetime.today(), spot_time.time()).replace(tzinfo=pytz.UTC)
spot = Spot(source="DX Cluster", spot = Spot(source="Cluster",
dx_call=match.group(3), dx_call=match.group(3),
de_call=match.group(1), de_call=match.group(1),
freq=float(match.group(2)), freq=float(match.group(2)),

View File

@@ -30,7 +30,7 @@ class GMA(HTTPProvider):
spot = Spot(source=self.name(), spot = Spot(source=self.name(),
dx_call=source_spot["ACTIVATOR"].upper(), dx_call=source_spot["ACTIVATOR"].upper(),
de_call=source_spot["SPOTTER"].upper(), de_call=source_spot["SPOTTER"].upper(),
freq=float(source_spot["QRG"]), freq=float(source_spot["QRG"]) if (source_spot["QRG"] != "") else None, # Seen GMA spots with no frequency
mode=source_spot["MODE"].upper(), mode=source_spot["MODE"].upper(),
comment=source_spot["TEXT"], comment=source_spot["TEXT"],
sig_refs=[source_spot["REF"]], sig_refs=[source_spot["REF"]],

View File

@@ -1,8 +1,10 @@
import json import json
import logging import logging
from datetime import datetime
from threading import Thread from threading import Thread
import bottle import bottle
import pytz
from bottle import run, response from bottle import run, response
from core.utils import serialize_everything from core.utils import serialize_everything
@@ -12,14 +14,19 @@ from core.utils import serialize_everything
class WebServer: class WebServer:
# Constructor # Constructor
def __init__(self, spot_list, port): def __init__(self, spot_list, status_data, port):
self.last_page_access_time = None
self.last_api_access_time = None
self.spot_list = spot_list self.spot_list = spot_list
self.status_data = status_data
self.port = port self.port = port
self.thread = Thread(target=self.run) self.thread = Thread(target=self.run)
self.thread.daemon = True self.thread.daemon = True
self.status = "Starting"
# Set up routing # Set up routing
bottle.get("/api/spots")(self.serve_api_spots) bottle.get("/api/spots")(self.serve_api_spots)
bottle.get("/api/status")(self.serve_api_status)
bottle.get("/")(self.serve_index) bottle.get("/")(self.serve_index)
bottle.get("/<filepath:path>")(self.serve_static_file) bottle.get("/<filepath:path>")(self.serve_static_file)
@@ -30,16 +37,29 @@ class WebServer:
# Run the web server itself. This blocks until the server is shut down, so it runs in a separate thread. # Run the web server itself. This blocks until the server is shut down, so it runs in a separate thread.
def run(self): def run(self):
logging.info("Starting web server on port " + str(self.port) + "...") logging.info("Starting web server on port " + str(self.port) + "...")
self.status = "Waiting"
run(host='localhost', port=self.port) run(host='localhost', port=self.port)
# Main spots API # Main spots API
def serve_api_spots(self): def serve_api_spots(self):
self.last_api_access_time = datetime.now(pytz.UTC)
self.status = "OK"
spots_json = json.dumps(self.spot_list, default=serialize_everything) spots_json = json.dumps(self.spot_list, default=serialize_everything)
response.content_type = 'application/json' response.content_type = 'application/json'
return spots_json return spots_json
# Server status API
def serve_api_status(self):
self.last_api_access_time = datetime.now(pytz.UTC)
self.status = "OK"
status_json = json.dumps(self.status_data, default=serialize_everything)
response.content_type = 'application/json'
return status_json
# Serve the home page. This would be accessible as /index.html but we need this workaround to make it available as / # Serve the home page. This would be accessible as /index.html but we need this workaround to make it available as /
def serve_index(self): def serve_index(self):
self.last_page_access_time = datetime.now(pytz.UTC)
self.status = "OK"
return bottle.static_file("index.html", root="webassets") return bottle.static_file("index.html", root="webassets")
# Serve general static files from "webassets" directory # Serve general static files from "webassets" directory
@@ -50,9 +70,3 @@ class WebServer:
# Todo spot API arguments e.g. "since" based on received_time of spots, sources, sigs, dx cont, dxcc, de cont, band, mode, filter out qrt, filter pre-qsy, sort order, list of fields # Todo spot API arguments e.g. "since" based on received_time of spots, sources, sigs, dx cont, dxcc, de cont, band, mode, filter out qrt, filter pre-qsy, sort order, list of fields
# Todo serve status API # Todo serve status API
# Todo serve apidocs # Todo serve apidocs
# Todo serve website
# Examples
# @route('/download/<filename>')
# def download(filename):
# return static_file(filename, root='/path/to/static/files', download=f"download-{filename}")