Moved eV GUI stuff out of LabThings

This commit is contained in:
Joel Collins 2020-01-07 22:25:19 +00:00
parent 3fef94dc95
commit eeb15453d0
5 changed files with 169 additions and 33 deletions

View file

@ -0,0 +1,134 @@
import logging
import os
import errno
from werkzeug.exceptions import BadRequest
from flask import url_for, Blueprint, current_app
from . import gui
def view_class_from_endpoint(endpoint: str):
return current_app.view_functions[endpoint].view_class
def blueprint_for_module(module_name, api_ver=2, suffix=""):
return Blueprint(
blueprint_name_for_module(module_name, api_ver=api_ver, suffix=suffix),
module_name,
)
def blueprint_name_for_module(module_name, api_ver=2, suffix=""):
bp_name = module_name.split(".")[-1]
return f"v{api_ver}_{bp_name}_blueprint{suffix}"
class JsonResponse:
def __init__(self, request):
"""
Object to wrap up simple functionality for parsing a JSON response.
"""
# Try to load as json
try:
self.json = (
request.get_json()
) #: dict: Dictionary representation of request JSON
# If malformed JSON is passed, make an empty dictionary
except BadRequest as e:
logging.error(e)
self.json = {}
if self.json is None:
self.json = {}
# Store raw response data
self.data = request.get_data() #: str: String representation of request data
if self.data is None:
self.data = ""
def param(self, key, default=None, convert=None):
"""
Check if a key exists in a JSON/dictionary payload, and returns it.
Args:
key (str): JSON key to look for
default: Value to return if no matching key-value pair is found.
convert: Converter function. By passing a type, the value will be converted to that type.
**Use with caution!**
"""
# If no JSON payload exists, make an empty dictionary
if not self.json:
self.json = {}
if key in self.json:
val = self.json[key]
else:
val = default
if convert and (val is not None):
val = convert(val)
return val
def gen(camera):
"""Video streaming generator function."""
while True:
# the obtained frame is a jpeg
frame = camera.get_frame()
yield (b"--frame\r\n" b"Content-Type: image/jpeg\r\n\r\n" + frame + b"\r\n")
def get_bool(get_arg):
"""Convert GET request argument string to a Python bool"""
if get_arg == "true" or get_arg == "True" or get_arg == "1":
return True
else:
return False
def list_routes(app):
output = {}
for rule in app.url_map.iter_rules():
options = {}
for arg in rule.arguments:
options[arg] = "[{0}]".format(arg)
endpoint = rule.endpoint
methods = list(rule.methods)
url = url_for(rule.endpoint, **options)
line = {"endpoint": endpoint, "methods": methods}
output[url] = line
return output
def create_file(config_path):
if not os.path.exists(os.path.dirname(config_path)):
try:
os.makedirs(os.path.dirname(config_path))
except OSError as exc: # Guard against race condition
if exc.errno != errno.EEXIST:
raise
def init_default_extensions(extension_dir):
global _DEFAULT_EXTENSION_INIT
os.makedirs(extension_dir, exist_ok=True)
default_ext_path = os.path.join(extension_dir, "defaults.py")
if not os.path.isfile(default_ext_path): # If user extensions file doesn't exist
logging.warning(
"No extension file found at {}. Creating...".format(extension_dir)
)
create_file(default_ext_path)
logging.info("Populating {}...".format(default_ext_path))
with open(default_ext_path, "w") as outfile:
outfile.write(_DEFAULT_EXTENSION_INIT)
_DEFAULT_EXTENSION_INIT = "from openflexure_microscope.api.default_extensions import *"

View file

@ -0,0 +1,41 @@
import copy
import logging
from functools import wraps
def expand_routes_from_dict(gui_description, extension_object):
api_gui = copy.deepcopy(gui_description)
if "forms" in gui_description and isinstance(api_gui["forms"], list):
for form in api_gui["forms"]:
if "route" in form and form["route"] in extension_object._rules.keys():
form["route"] = extension_object._rules[form["route"]]["rule"]
else:
logging.warn(
"No valid expandable route found for {}".format(form["route"])
)
return api_gui
def expand_routes_from_func(func, extension_object):
@wraps(func)
def wrapped(*args, **kwargs):
return expand_routes_from_dict(func(*args, **kwargs), extension_object)
return wrapped
def expand_routes(gui_description, extension_object):
# If given a function that generates a GUI dictionary
if callable(gui_description):
# Wrap in the route expander
return expand_routes_from_func(gui_description, extension_object)
# If given a dictionary directly
elif isinstance(gui_description, dict):
# Build a GUI generator function
def gui_description_func():
return gui_description
# Wrap in the route expander
return expand_routes_from_func(gui_description_func, extension_object)
else:
raise RuntimeError("GUI description must be a function or a dictionary")