Refactored adding views, actions, props and components

This commit is contained in:
Joel Collins 2020-01-11 14:58:50 +00:00
parent 7715c19305
commit a4196b6765
10 changed files with 62 additions and 66 deletions

View file

@ -69,7 +69,7 @@ app, labthing = create_app(
app.json_encoder = JSONEncoder app.json_encoder = JSONEncoder
# Attach lab devices # Attach lab devices
labthing.register_device(api_microscope, "org.openflexure.microscope") labthing.add_component(api_microscope, "org.openflexure.microscope")
# Attach extensions # Attach extensions
if not os.path.isfile(USER_EXTENSIONS_PATH): if not os.path.isfile(USER_EXTENSIONS_PATH):
@ -78,33 +78,27 @@ for extension in find_extensions(USER_EXTENSIONS_PATH):
labthing.register_extension(extension) labthing.register_extension(extension)
# Attach captures resources # Attach captures resources
labthing.add_resource(views.CaptureList, f"/captures") labthing.add_view(views.CaptureList, f"/captures")
labthing.register_property(views.CaptureList) labthing.add_view(views.CaptureResource, f"/captures/<id>")
labthing.add_resource(views.CaptureResource, f"/captures/<id>") labthing.add_view(views.CaptureDownload, f"/captures/<id>/download/<filename>")
labthing.add_resource(views.CaptureDownload, f"/captures/<id>/download/<filename>") labthing.add_view(views.CaptureTags, f"/captures/<id>/tags")
labthing.add_resource(views.CaptureTags, f"/captures/<id>/tags") labthing.add_view(views.CaptureMetadata, f"/captures/<id>/metadata")
labthing.add_resource(views.CaptureMetadata, f"/captures/<id>/metadata")
# Attach settings and status resources # Attach settings and status resources
labthing.add_resource(views.SettingsProperty, f"/settings") labthing.add_view(views.SettingsProperty, f"/settings")
labthing.register_property(views.SettingsProperty) labthing.add_view(views.NestedSettingsProperty, "/settings/<path:route>")
labthing.add_resource(views.NestedSettingsProperty, "/settings/<path:route>") labthing.add_view(views.StatusProperty, "/status")
labthing.add_resource(views.StatusProperty, "/status") labthing.add_view(views.NestedStatusProperty, "/status/<path:route>")
labthing.register_property(views.StatusProperty)
labthing.add_resource(views.NestedStatusProperty, "/status/<path:route>")
# Attach streams resources # Attach streams resources
labthing.add_resource(views.MjpegStream, f"/streams/mjpeg") labthing.add_view(views.MjpegStream, f"/streams/mjpeg")
labthing.register_property(views.MjpegStream) labthing.add_view(views.SnapshotStream, f"/streams/snapshot")
labthing.add_resource(views.SnapshotStream, f"/streams/snapshot")
labthing.register_property(views.SnapshotStream)
# Attach microscope action resources # Attach microscope action resources
for name, action in views.enabled_root_actions().items(): for name, action in views.enabled_root_actions().items():
view_class = action["view_class"] view_class = action["view_class"]
rule = action["rule"] rule = action["rule"]
labthing.add_resource(view_class, f"/actions{rule}") labthing.add_view(view_class, f"/actions{rule}")
labthing.register_action(view_class)
@app.route("/routes") @app.route("/routes")

View file

@ -1,6 +1,10 @@
from openflexure_microscope.common.flask_labthings.find import find_device from openflexure_microscope.common.flask_labthings.find import find_device
from openflexure_microscope.common.flask_labthings.extensions import BaseExtension from openflexure_microscope.common.flask_labthings.extensions import BaseExtension
from openflexure_microscope.common.flask_labthings.resource import Resource from openflexure_microscope.common.flask_labthings.resource import Resource
from openflexure_microscope.common.flask_labthings.decorators import (
ThingAction,
ThingProperty,
)
from openflexure_microscope.devel import JsonResponse, request, jsonify, taskify, abort from openflexure_microscope.devel import JsonResponse, request, jsonify, taskify, abort
from openflexure_microscope.utilities import set_properties from openflexure_microscope.utilities import set_properties
@ -298,6 +302,7 @@ class MeasureSharpnessAPI(Resource):
return jsonify({"sharpness": measure_sharpness(microscope)}) return jsonify({"sharpness": measure_sharpness(microscope)})
@ThingAction
class AutofocusAPI(Resource): class AutofocusAPI(Resource):
""" """
Run a standard autofocus Run a standard autofocus
@ -324,6 +329,7 @@ class AutofocusAPI(Resource):
abort(503, "No stage connected. Unable to autofocus.") abort(503, "No stage connected. Unable to autofocus.")
@ThingAction
class FastAutofocusAPI(Resource): class FastAutofocusAPI(Resource):
""" """
Run a fast autofocus Run a fast autofocus
@ -359,9 +365,5 @@ autofocus_extension_v2.add_method(fast_autofocus, "fast_autofocus")
autofocus_extension_v2.add_method(autofocus, "autofocus") autofocus_extension_v2.add_method(autofocus, "autofocus")
autofocus_extension_v2.add_view(MeasureSharpnessAPI, "/measure_sharpness") autofocus_extension_v2.add_view(MeasureSharpnessAPI, "/measure_sharpness")
autofocus_extension_v2.add_view(AutofocusAPI, "/autofocus") autofocus_extension_v2.add_view(AutofocusAPI, "/autofocus")
autofocus_extension_v2.register_action(AutofocusAPI)
autofocus_extension_v2.add_view(FastAutofocusAPI, "/fast_autofocus") autofocus_extension_v2.add_view(FastAutofocusAPI, "/fast_autofocus")
autofocus_extension_v2.register_action(FastAutofocusAPI)

View file

@ -12,8 +12,8 @@ from openflexure_microscope.common.flask_labthings.find import (
from openflexure_microscope.common.flask_labthings.extensions import BaseExtension from openflexure_microscope.common.flask_labthings.extensions import BaseExtension
from openflexure_microscope.common.flask_labthings.decorators import ( from openflexure_microscope.common.flask_labthings.decorators import (
marshal_task, marshal_task,
marshal_with,
use_args, use_args,
ThingAction,
) )
from openflexure_microscope.common.flask_labthings import fields from openflexure_microscope.common.flask_labthings import fields
@ -340,6 +340,7 @@ def stack(
### Web views ### Web views
@ThingAction
class TileScanAPI(Resource): class TileScanAPI(Resource):
@use_args( @use_args(
{ {
@ -398,4 +399,3 @@ class TileScanAPI(Resource):
scan_extension_v2 = BaseExtension("scan") scan_extension_v2 = BaseExtension("scan")
scan_extension_v2.add_view(TileScanAPI, "/tile") scan_extension_v2.add_view(TileScanAPI, "/tile")
scan_extension_v2.register_action(TileScanAPI)

View file

@ -17,6 +17,10 @@ import logging
from openflexure_microscope.common.flask_labthings.find import find_device from openflexure_microscope.common.flask_labthings.find import find_device
from openflexure_microscope.common.flask_labthings.resource import Resource from openflexure_microscope.common.flask_labthings.resource import Resource
from openflexure_microscope.common.flask_labthings.extensions import BaseExtension from openflexure_microscope.common.flask_labthings.extensions import BaseExtension
from openflexure_microscope.common.flask_labthings.decorators import (
ThingAction,
ThingProperty,
)
class ZipManager: class ZipManager:
@ -91,6 +95,7 @@ class ZipManager:
default_zip_manager = ZipManager() default_zip_manager = ZipManager()
@ThingAction
class ZipBuilderAPIView(Resource): class ZipBuilderAPIView(Resource):
def post(self): def post(self):
@ -103,6 +108,7 @@ class ZipBuilderAPIView(Resource):
return jsonify(task.state), 201 return jsonify(task.state), 201
@ThingProperty
class ZipListAPIView(Resource): class ZipListAPIView(Resource):
def get(self): def get(self):
return jsonify(default_zip_manager.session_zips) return jsonify(default_zip_manager.session_zips)
@ -146,4 +152,3 @@ zip_extension_v2.add_view(ZipGetterAPIView, "/get/<string:session_id>")
zip_extension_v2.add_view(ZipListAPIView, "/get") zip_extension_v2.add_view(ZipListAPIView, "/get")
zip_extension_v2.add_view(ZipBuilderAPIView, "/build") zip_extension_v2.add_view(ZipBuilderAPIView, "/build")
zip_extension_v2.register_action(ZipBuilderAPIView)

View file

@ -1,5 +1,9 @@
from openflexure_microscope.common.flask_labthings.resource import Resource from openflexure_microscope.common.flask_labthings.resource import Resource
from openflexure_microscope.common.flask_labthings.extensions import BaseExtension from openflexure_microscope.common.flask_labthings.extensions import BaseExtension
from openflexure_microscope.common.flask_labthings.decorators import (
ThingAction,
ThingProperty,
)
from openflexure_microscope.api.utilities.gui import expand_routes from openflexure_microscope.api.utilities.gui import expand_routes
@ -72,6 +76,7 @@ static_form = {
} }
@ThingProperty
class TestAPIView(Resource): class TestAPIView(Resource):
def get(self): def get(self):
global val_int global val_int
@ -79,6 +84,7 @@ class TestAPIView(Resource):
return jsonify({"val": True}) return jsonify({"val": True})
@ThingAction
class TestDoAPIView(Resource): class TestDoAPIView(Resource):
def post(self): def post(self):
global val_int global val_int

View file

@ -77,16 +77,20 @@ def ThingAction(viewcls):
update_spec(viewcls, {"_groups": ["actions"]}) update_spec(viewcls, {"_groups": ["actions"]})
return viewcls return viewcls
thing_action = ThingAction thing_action = ThingAction
def ThingProperty(viewcls): def ThingProperty(viewcls):
# Pass params to call function attribute for external access # Pass params to call function attribute for external access
update_spec(viewcls, {"tags": ["properties"]}) update_spec(viewcls, {"tags": ["properties"]})
update_spec(viewcls, {"_groups": ["properties"]}) update_spec(viewcls, {"_groups": ["properties"]})
return viewcls return viewcls
thing_property = ThingProperty thing_property = ThingProperty
class use_args(object): class use_args(object):
def __init__(self, schema, **kwargs): def __init__(self, schema, **kwargs):
""" """
@ -121,8 +125,10 @@ class Doc(object):
update_spec(f, self.kwargs) update_spec(f, self.kwargs)
return f return f
doc = Doc doc = Doc
class Tag(object): class Tag(object):
def __init__(self, tags): def __init__(self, tags):
if isinstance(tags, str): if isinstance(tags, str):
@ -137,8 +143,10 @@ class Tag(object):
update_spec(f, {"tags": self.tags}) update_spec(f, {"tags": self.tags})
return f return f
tag = Tag tag = Tag
class doc_response(object): class doc_response(object):
def __init__(self, code, description=None, **kwargs): def __init__(self, code, description=None, **kwargs):
self.code = code self.code = code

View file

@ -28,11 +28,11 @@ class LabThing(object):
): ):
self.app = app self.app = app
self.devices = {} self.components = {}
self.extensions = {} self.extensions = {}
self.resources = [] self.views = []
self.properties = {} self.properties = {}
self.actions = {} self.actions = {}
@ -93,8 +93,8 @@ class LabThing(object):
app.extensions[EXTENSION_NAME] = self app.extensions[EXTENSION_NAME] = self
# Add resources, if registered before tying to a Flask app # Add resources, if registered before tying to a Flask app
if len(self.resources) > 0: if len(self.views) > 0:
for resource, urls, endpoint, kwargs in self.resources: for resource, urls, endpoint, kwargs in self.views:
self._register_view(app, resource, *urls, endpoint=endpoint, **kwargs) self._register_view(app, resource, *urls, endpoint=endpoint, **kwargs)
# Create base routes # Create base routes
@ -110,19 +110,15 @@ class LabThing(object):
self.app.register_blueprint(docs_blueprint, url_prefix=self.url_prefix) self.app.register_blueprint(docs_blueprint, url_prefix=self.url_prefix)
# Add extension overview # Add extension overview
self.add_resource( self.add_view(ExtensionList, "/extensions", endpoint=EXTENSION_LIST_ENDPOINT)
ExtensionList, "/extensions", endpoint=EXTENSION_LIST_ENDPOINT
)
self.register_property(ExtensionList)
# Add task routes # Add task routes
self.add_resource(TaskList, "/tasks", endpoint=TASK_LIST_ENDPOINT) self.add_view(TaskList, "/tasks", endpoint=TASK_LIST_ENDPOINT)
self.register_property(TaskList) self.add_view(TaskResource, "/tasks/<id>", endpoint=TASK_ENDPOINT)
self.add_resource(TaskResource, "/tasks/<id>", endpoint=TASK_ENDPOINT)
### Device stuff ### Device stuff
def register_device(self, device_object, device_name: str): def add_component(self, device_object, device_name: str):
self.devices[device_name] = device_object self.components[device_name] = device_object
### Extension stuff ### Extension stuff
@ -134,18 +130,12 @@ class LabThing(object):
for extension_view_id, extension_view in extension_object.views.items(): for extension_view_id, extension_view in extension_object.views.items():
# Add route to the extensions blueprint # Add route to the extensions blueprint
self.add_resource( self.add_view(
extension_view["view"], extension_view["view"],
"/extensions" + extension_view["rule"], "/extensions" + extension_view["rule"],
**extension_view["kwargs"], **extension_view["kwargs"],
) )
for prop in extension_object.properties:
self.register_property(prop)
for action in extension_object.actions:
self.register_action(action)
### Resource stuff ### Resource stuff
def _complete_url(self, url_part, registration_prefix): def _complete_url(self, url_part, registration_prefix):
@ -158,16 +148,8 @@ class LabThing(object):
parts = [registration_prefix, self.url_prefix, url_part] parts = [registration_prefix, self.url_prefix, url_part]
return "".join([part for part in parts if part]) return "".join([part for part in parts if part])
def register_property(self, resource): def add_view(self, resource, *urls, endpoint=None, **kwargs):
logging.warning("register_property is deprecated. Use the @ltproperty class decorator instead.") """Adds a view to the api.
pass
def register_action(self, resource):
logging.warning("register_action is deprecated. Use the @ltaction class decorator instead.")
pass
def add_resource(self, resource, *urls, endpoint=None, **kwargs):
"""Adds a resource to the api.
:param resource: the class name of your resource :param resource: the class name of your resource
:type resource: :class:`Type[Resource]` :type resource: :class:`Type[Resource]`
:param urls: one or more url routes to match for the resource, standard :param urls: one or more url routes to match for the resource, standard
@ -197,11 +179,11 @@ class LabThing(object):
if self.app is not None: if self.app is not None:
self._register_view(self.app, resource, *urls, endpoint=endpoint, **kwargs) self._register_view(self.app, resource, *urls, endpoint=endpoint, **kwargs)
self.resources.append((resource, urls, endpoint, kwargs)) self.views.append((resource, urls, endpoint, kwargs))
def resource(self, *urls, **kwargs): def view(self, *urls, **kwargs):
def decorator(cls): def decorator(cls):
self.add_resource(cls, *urls, **kwargs) self.add_view(cls, *urls, **kwargs)
return cls return cls
return decorator return decorator

View file

@ -20,10 +20,12 @@ def update_spec(obj, spec):
rupdate(obj.__apispec__, spec) rupdate(obj.__apispec__, spec)
return obj.__apispec__ return obj.__apispec__
def get_spec(obj): def get_spec(obj):
obj.__apispec__ = obj.__dict__.get("__apispec__", {}) obj.__apispec__ = obj.__dict__.get("__apispec__", {})
return obj.__apispec__ return obj.__apispec__
def view2path(rule: str, view: Resource, spec: APISpec): def view2path(rule: str, view: Resource, spec: APISpec):
params = { params = {
"path": rule, # TODO: Validate this slightly (leading / etc) "path": rule, # TODO: Validate this slightly (leading / etc)

View file

@ -1,17 +1,13 @@
""" """
Top-level representation of attached and enabled Extensions Top-level representation of attached and enabled Extensions
""" """
from openflexure_microscope.common.flask_labthings.utilities import get_docstring
from openflexure_microscope.common.flask_labthings.schema import Schema
from openflexure_microscope.common.flask_labthings import fields
from ..resource import Resource from ..resource import Resource
from ..find import registered_extensions from ..find import registered_extensions
from ..schema import ExtensionSchema from ..schema import ExtensionSchema
from ..decorators import marshal_with from ..decorators import marshal_with, ThingProperty
@ThingProperty
class ExtensionList(Resource): class ExtensionList(Resource):
""" """
List and basic documentation for all enabled Extensions List and basic documentation for all enabled Extensions

View file

@ -1,12 +1,13 @@
from flask import abort, url_for from flask import abort, url_for
from ..decorators import marshal_with from ..decorators import marshal_with, ThingProperty
from ..resource import Resource from ..resource import Resource
from ..schema import TaskSchema from ..schema import TaskSchema
from openflexure_microscope.common.labthings_core import tasks from openflexure_microscope.common.labthings_core import tasks
@ThingProperty
class TaskList(Resource): class TaskList(Resource):
""" """
List and basic documentation for all session tasks List and basic documentation for all session tasks