split the controllers into separate files
This commit is contained in:
parent
b110705f45
commit
451eb99f8a
@ -1,210 +0,0 @@
|
||||
import os
|
||||
import mimetypes
|
||||
import json
|
||||
import pkg_resources
|
||||
from datetime import datetime
|
||||
from string import Template
|
||||
from owrx.websocket import WebSocketConnection
|
||||
from owrx.config import PropertyManager
|
||||
from owrx.client import ClientRegistry
|
||||
from owrx.connection import WebSocketMessageHandler
|
||||
from owrx.version import openwebrx_version
|
||||
from owrx.feature import FeatureDetector
|
||||
from owrx.metrics import Metrics
|
||||
from owrx.sdr import SdrService
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Controller(ABC):
|
||||
def __init__(self, handler, request):
|
||||
self.handler = handler
|
||||
self.request = request
|
||||
|
||||
def send_response(self, content, code=200, content_type="text/html", last_modified: datetime = None, max_age=None):
|
||||
self.handler.send_response(code)
|
||||
if content_type is not None:
|
||||
self.handler.send_header("Content-Type", content_type)
|
||||
if last_modified is not None:
|
||||
self.handler.send_header("Last-Modified", last_modified.strftime("%a, %d %b %Y %H:%M:%S GMT"))
|
||||
if max_age is not None:
|
||||
self.handler.send_header("Cache-Control", "max-age: {0}".format(max_age))
|
||||
self.handler.end_headers()
|
||||
if type(content) == str:
|
||||
content = content.encode()
|
||||
self.handler.wfile.write(content)
|
||||
|
||||
@abstractmethod
|
||||
def handle_request(self):
|
||||
pass
|
||||
|
||||
|
||||
class StatusController(Controller):
|
||||
def handle_request(self):
|
||||
pm = PropertyManager.getSharedInstance()
|
||||
# TODO keys that have been left out since they are no longer simple strings: sdr_hw, bands, antenna
|
||||
vars = {
|
||||
"status": "active",
|
||||
"name": pm["receiver_name"],
|
||||
"op_email": pm["receiver_admin"],
|
||||
"users": ClientRegistry.getSharedInstance().clientCount(),
|
||||
"users_max": pm["max_clients"],
|
||||
"gps": pm["receiver_gps"],
|
||||
"asl": pm["receiver_asl"],
|
||||
"loc": pm["receiver_location"],
|
||||
"sw_version": openwebrx_version,
|
||||
"avatar_ctime": os.path.getctime("htdocs/gfx/openwebrx-avatar.png"),
|
||||
}
|
||||
self.send_response("\n".join(["{key}={value}".format(key=key, value=value) for key, value in vars.items()]))
|
||||
|
||||
|
||||
class StatusJsonController(Controller):
|
||||
def getProfileStats(self, profile):
|
||||
return {
|
||||
"name": profile["name"],
|
||||
"center_freq": profile["center_freq"],
|
||||
"sample_rate": profile["samp_rate"],
|
||||
}
|
||||
|
||||
def getReceiverStats(self, receiver):
|
||||
stats = {
|
||||
"name": receiver.getName(),
|
||||
# TODO would be better to have types from the config here
|
||||
"type": type(receiver).__name__,
|
||||
"profiles": [self.getProfileStats(p) for p in receiver.getProfiles().values()]
|
||||
}
|
||||
return stats
|
||||
|
||||
def handle_request(self):
|
||||
pm = PropertyManager.getSharedInstance()
|
||||
|
||||
gps = pm["receiver_gps"]
|
||||
status = {
|
||||
"receiver": {
|
||||
"name": pm["receiver_name"],
|
||||
"admin": pm["receiver_admin"],
|
||||
"gps": {"lat": gps[0], "lon": gps[1]},
|
||||
"asl": pm["receiver_asl"],
|
||||
"location": pm["receiver_location"],
|
||||
},
|
||||
"max_clients": pm["max_clients"],
|
||||
"version": openwebrx_version,
|
||||
"sdrs": [self.getReceiverStats(r) for r in SdrService.getSources().values()]
|
||||
}
|
||||
self.send_response(json.dumps(status), content_type="application/json")
|
||||
|
||||
|
||||
class AssetsController(Controller):
|
||||
def getModified(self, file):
|
||||
return None
|
||||
|
||||
def openFile(self, file):
|
||||
pass
|
||||
|
||||
def serve_file(self, file, content_type=None):
|
||||
try:
|
||||
modified = self.getModified(file)
|
||||
|
||||
if modified is not None and "If-Modified-Since" in self.handler.headers:
|
||||
client_modified = datetime.strptime(
|
||||
self.handler.headers["If-Modified-Since"], "%a, %d %b %Y %H:%M:%S %Z"
|
||||
)
|
||||
if modified <= client_modified:
|
||||
self.send_response("", code=304)
|
||||
return
|
||||
|
||||
f = self.openFile(file)
|
||||
data = f.read()
|
||||
f.close()
|
||||
|
||||
if content_type is None:
|
||||
(content_type, encoding) = mimetypes.MimeTypes().guess_type(file)
|
||||
self.send_response(data, content_type=content_type, last_modified=modified, max_age=3600)
|
||||
except FileNotFoundError:
|
||||
self.send_response("file not found", code=404)
|
||||
|
||||
def handle_request(self):
|
||||
filename = self.request.matches.group(1)
|
||||
self.serve_file(filename)
|
||||
|
||||
|
||||
class OwrxAssetsController(AssetsController):
|
||||
def openFile(self, file):
|
||||
return pkg_resources.resource_stream("htdocs", file)
|
||||
|
||||
|
||||
class AprsSymbolsController(AssetsController):
|
||||
def __init__(self, handler, request):
|
||||
pm = PropertyManager.getSharedInstance()
|
||||
path = pm["aprs_symbols_path"]
|
||||
if not path.endswith("/"):
|
||||
path += "/"
|
||||
self.path = path
|
||||
super().__init__(handler, request)
|
||||
|
||||
def getFilePath(self, file):
|
||||
return self.path + file
|
||||
|
||||
def getModified(self, file):
|
||||
return datetime.fromtimestamp(os.path.getmtime(self.getFilePath(file)))
|
||||
|
||||
def openFile(self, file):
|
||||
return open(self.getFilePath(file), "rb")
|
||||
|
||||
|
||||
class TemplateController(Controller):
|
||||
def render_template(self, file, **vars):
|
||||
file_content = pkg_resources.resource_string("htdocs", file).decode("utf-8")
|
||||
template = Template(file_content)
|
||||
|
||||
return template.safe_substitute(**vars)
|
||||
|
||||
def serve_template(self, file, **vars):
|
||||
self.send_response(self.render_template(file, **vars), content_type="text/html")
|
||||
|
||||
def default_variables(self):
|
||||
return {}
|
||||
|
||||
|
||||
class WebpageController(TemplateController):
|
||||
def template_variables(self):
|
||||
header = self.render_template("include/header.include.html")
|
||||
return {"header": header}
|
||||
|
||||
|
||||
class IndexController(WebpageController):
|
||||
def handle_request(self):
|
||||
self.serve_template("index.html", **self.template_variables())
|
||||
|
||||
|
||||
class MapController(WebpageController):
|
||||
def handle_request(self):
|
||||
# TODO check if we have a google maps api key first?
|
||||
self.serve_template("map.html", **self.template_variables())
|
||||
|
||||
|
||||
class FeatureController(WebpageController):
|
||||
def handle_request(self):
|
||||
self.serve_template("features.html", **self.template_variables())
|
||||
|
||||
|
||||
class ApiController(Controller):
|
||||
def handle_request(self):
|
||||
data = json.dumps(FeatureDetector().feature_report())
|
||||
self.send_response(data, content_type="application/json")
|
||||
|
||||
|
||||
class MetricsController(Controller):
|
||||
def handle_request(self):
|
||||
data = json.dumps(Metrics.getSharedInstance().getMetrics())
|
||||
self.send_response(data, content_type="application/json")
|
||||
|
||||
|
||||
class WebSocketController(Controller):
|
||||
def handle_request(self):
|
||||
conn = WebSocketConnection(self.handler, WebSocketMessageHandler())
|
||||
# enter read loop
|
||||
conn.handle()
|
25
owrx/controllers/__init__.py
Normal file
25
owrx/controllers/__init__.py
Normal file
@ -0,0 +1,25 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class Controller(ABC):
|
||||
def __init__(self, handler, request):
|
||||
self.handler = handler
|
||||
self.request = request
|
||||
|
||||
def send_response(self, content, code=200, content_type="text/html", last_modified: datetime = None, max_age=None):
|
||||
self.handler.send_response(code)
|
||||
if content_type is not None:
|
||||
self.handler.send_header("Content-Type", content_type)
|
||||
if last_modified is not None:
|
||||
self.handler.send_header("Last-Modified", last_modified.strftime("%a, %d %b %Y %H:%M:%S GMT"))
|
||||
if max_age is not None:
|
||||
self.handler.send_header("Cache-Control", "max-age: {0}".format(max_age))
|
||||
self.handler.end_headers()
|
||||
if type(content) == str:
|
||||
content = content.encode()
|
||||
self.handler.wfile.write(content)
|
||||
|
||||
@abstractmethod
|
||||
def handle_request(self):
|
||||
pass
|
9
owrx/controllers/api.py
Normal file
9
owrx/controllers/api.py
Normal file
@ -0,0 +1,9 @@
|
||||
from . import Controller
|
||||
from owrx.feature import FeatureDetector
|
||||
import json
|
||||
|
||||
|
||||
class ApiController(Controller):
|
||||
def handle_request(self):
|
||||
data = json.dumps(FeatureDetector().feature_report())
|
||||
self.send_response(data, content_type="application/json")
|
64
owrx/controllers/assets.py
Normal file
64
owrx/controllers/assets.py
Normal file
@ -0,0 +1,64 @@
|
||||
from . import Controller
|
||||
from owrx.config import PropertyManager
|
||||
from datetime import datetime
|
||||
import mimetypes
|
||||
import os
|
||||
import pkg_resources
|
||||
|
||||
|
||||
class AssetsController(Controller):
|
||||
def getModified(self, file):
|
||||
return None
|
||||
|
||||
def openFile(self, file):
|
||||
pass
|
||||
|
||||
def serve_file(self, file, content_type=None):
|
||||
try:
|
||||
modified = self.getModified(file)
|
||||
|
||||
if modified is not None and "If-Modified-Since" in self.handler.headers:
|
||||
client_modified = datetime.strptime(
|
||||
self.handler.headers["If-Modified-Since"], "%a, %d %b %Y %H:%M:%S %Z"
|
||||
)
|
||||
if modified <= client_modified:
|
||||
self.send_response("", code=304)
|
||||
return
|
||||
|
||||
f = self.openFile(file)
|
||||
data = f.read()
|
||||
f.close()
|
||||
|
||||
if content_type is None:
|
||||
(content_type, encoding) = mimetypes.MimeTypes().guess_type(file)
|
||||
self.send_response(data, content_type=content_type, last_modified=modified, max_age=3600)
|
||||
except FileNotFoundError:
|
||||
self.send_response("file not found", code=404)
|
||||
|
||||
def handle_request(self):
|
||||
filename = self.request.matches.group(1)
|
||||
self.serve_file(filename)
|
||||
|
||||
|
||||
class OwrxAssetsController(AssetsController):
|
||||
def openFile(self, file):
|
||||
return pkg_resources.resource_stream("htdocs", file)
|
||||
|
||||
|
||||
class AprsSymbolsController(AssetsController):
|
||||
def __init__(self, handler, request):
|
||||
pm = PropertyManager.getSharedInstance()
|
||||
path = pm["aprs_symbols_path"]
|
||||
if not path.endswith("/"):
|
||||
path += "/"
|
||||
self.path = path
|
||||
super().__init__(handler, request)
|
||||
|
||||
def getFilePath(self, file):
|
||||
return self.path + file
|
||||
|
||||
def getModified(self, file):
|
||||
return datetime.fromtimestamp(os.path.getmtime(self.getFilePath(file)))
|
||||
|
||||
def openFile(self, file):
|
||||
return open(self.getFilePath(file), "rb")
|
9
owrx/controllers/metrics.py
Normal file
9
owrx/controllers/metrics.py
Normal file
@ -0,0 +1,9 @@
|
||||
from . import Controller
|
||||
from owrx.metrics import Metrics
|
||||
import json
|
||||
|
||||
|
||||
class MetricsController(Controller):
|
||||
def handle_request(self):
|
||||
data = json.dumps(Metrics.getSharedInstance().getMetrics())
|
||||
self.send_response(data, content_type="application/json")
|
62
owrx/controllers/status.py
Normal file
62
owrx/controllers/status.py
Normal file
@ -0,0 +1,62 @@
|
||||
from . import Controller
|
||||
from owrx.client import ClientRegistry
|
||||
from owrx.version import openwebrx_version
|
||||
from owrx.sdr import SdrService
|
||||
from owrx.config import PropertyManager
|
||||
import os
|
||||
import json
|
||||
|
||||
|
||||
class StatusController(Controller):
|
||||
def handle_request(self):
|
||||
pm = PropertyManager.getSharedInstance()
|
||||
# TODO keys that have been left out since they are no longer simple strings: sdr_hw, bands, antenna
|
||||
vars = {
|
||||
"status": "active",
|
||||
"name": pm["receiver_name"],
|
||||
"op_email": pm["receiver_admin"],
|
||||
"users": ClientRegistry.getSharedInstance().clientCount(),
|
||||
"users_max": pm["max_clients"],
|
||||
"gps": pm["receiver_gps"],
|
||||
"asl": pm["receiver_asl"],
|
||||
"loc": pm["receiver_location"],
|
||||
"sw_version": openwebrx_version,
|
||||
"avatar_ctime": os.path.getctime("htdocs/gfx/openwebrx-avatar.png"),
|
||||
}
|
||||
self.send_response("\n".join(["{key}={value}".format(key=key, value=value) for key, value in vars.items()]))
|
||||
|
||||
|
||||
class StatusJsonController(Controller):
|
||||
def getProfileStats(self, profile):
|
||||
return {
|
||||
"name": profile["name"],
|
||||
"center_freq": profile["center_freq"],
|
||||
"sample_rate": profile["samp_rate"],
|
||||
}
|
||||
|
||||
def getReceiverStats(self, receiver):
|
||||
stats = {
|
||||
"name": receiver.getName(),
|
||||
# TODO would be better to have types from the config here
|
||||
"type": type(receiver).__name__,
|
||||
"profiles": [self.getProfileStats(p) for p in receiver.getProfiles().values()]
|
||||
}
|
||||
return stats
|
||||
|
||||
def handle_request(self):
|
||||
pm = PropertyManager.getSharedInstance()
|
||||
|
||||
gps = pm["receiver_gps"]
|
||||
status = {
|
||||
"receiver": {
|
||||
"name": pm["receiver_name"],
|
||||
"admin": pm["receiver_admin"],
|
||||
"gps": {"lat": gps[0], "lon": gps[1]},
|
||||
"asl": pm["receiver_asl"],
|
||||
"location": pm["receiver_location"],
|
||||
},
|
||||
"max_clients": pm["max_clients"],
|
||||
"version": openwebrx_version,
|
||||
"sdrs": [self.getReceiverStats(r) for r in SdrService.getSources().values()]
|
||||
}
|
||||
self.send_response(json.dumps(status), content_type="application/json")
|
40
owrx/controllers/template.py
Normal file
40
owrx/controllers/template.py
Normal file
@ -0,0 +1,40 @@
|
||||
from . import Controller
|
||||
import pkg_resources
|
||||
from string import Template
|
||||
from abc import ABCMeta
|
||||
|
||||
|
||||
class TemplateController(Controller, metaclass=ABCMeta):
|
||||
def render_template(self, file, **vars):
|
||||
file_content = pkg_resources.resource_string("htdocs", file).decode("utf-8")
|
||||
template = Template(file_content)
|
||||
|
||||
return template.safe_substitute(**vars)
|
||||
|
||||
def serve_template(self, file, **vars):
|
||||
self.send_response(self.render_template(file, **vars), content_type="text/html")
|
||||
|
||||
def default_variables(self):
|
||||
return {}
|
||||
|
||||
|
||||
class WebpageController(TemplateController, metaclass=ABCMeta):
|
||||
def template_variables(self):
|
||||
header = self.render_template("include/header.include.html")
|
||||
return {"header": header}
|
||||
|
||||
|
||||
class IndexController(WebpageController):
|
||||
def handle_request(self):
|
||||
self.serve_template("index.html", **self.template_variables())
|
||||
|
||||
|
||||
class MapController(WebpageController):
|
||||
def handle_request(self):
|
||||
# TODO check if we have a google maps api key first?
|
||||
self.serve_template("map.html", **self.template_variables())
|
||||
|
||||
|
||||
class FeatureController(WebpageController):
|
||||
def handle_request(self):
|
||||
self.serve_template("features.html", **self.template_variables())
|
10
owrx/controllers/websocket.py
Normal file
10
owrx/controllers/websocket.py
Normal file
@ -0,0 +1,10 @@
|
||||
from . import Controller
|
||||
from owrx.websocket import WebSocketConnection
|
||||
from owrx.connection import WebSocketMessageHandler
|
||||
|
||||
|
||||
class WebSocketController(Controller):
|
||||
def handle_request(self):
|
||||
conn = WebSocketConnection(self.handler, WebSocketMessageHandler())
|
||||
# enter read loop
|
||||
conn.handle()
|
24
owrx/http.py
24
owrx/http.py
@ -1,15 +1,19 @@
|
||||
from owrx.controllers import (
|
||||
StatusController,
|
||||
from owrx.controllers.status import (
|
||||
StatusJsonController,
|
||||
IndexController,
|
||||
OwrxAssetsController,
|
||||
WebSocketController,
|
||||
MapController,
|
||||
FeatureController,
|
||||
ApiController,
|
||||
MetricsController,
|
||||
AprsSymbolsController,
|
||||
StatusController
|
||||
)
|
||||
from owrx.controllers.template import (
|
||||
IndexController,
|
||||
MapController,
|
||||
FeatureController
|
||||
)
|
||||
from owrx.controllers.assets import (
|
||||
OwrxAssetsController,
|
||||
AprsSymbolsController
|
||||
)
|
||||
from owrx.controllers.websocket import WebSocketController
|
||||
from owrx.controllers.api import ApiController
|
||||
from owrx.controllers.metrics import MetricsController
|
||||
from http.server import BaseHTTPRequestHandler
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
import re
|
||||
|
2
setup.py
2
setup.py
@ -11,7 +11,7 @@ except ImportError:
|
||||
setup(
|
||||
name="OpenWebRX",
|
||||
version=str(strictversion),
|
||||
packages=find_namespace_packages(include=["owrx", "owrx.source", "owrx.service", "csdr", "htdocs"]),
|
||||
packages=find_namespace_packages(include=["owrx", "owrx.source", "owrx.service", "owrx.controllers", "csdr", "htdocs"]),
|
||||
package_data={"htdocs": [f[len("htdocs/") :] for f in glob("htdocs/**/*", recursive=True)]},
|
||||
entry_points={"console_scripts": ["openwebrx=owrx.__main__:main"]},
|
||||
# use the github page for now
|
||||
|
Loading…
Reference in New Issue
Block a user