From a4196b6765c1e2a1cb2dc3272a848977658f7b37 Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Sat, 11 Jan 2020 14:58:50 +0000 Subject: [PATCH] Refactored adding views, actions, props and components --- openflexure_microscope/api/app.py | 32 +++++-------- .../api/default_extensions/autofocus.py | 10 ++-- .../api/default_extensions/scan.py | 4 +- .../api/default_extensions/zip_builder.py | 7 ++- .../api/example_extensions/ev_gui.py | 6 +++ .../common/flask_labthings/decorators.py | 8 ++++ .../common/flask_labthings/labthing.py | 48 ++++++------------- .../common/flask_labthings/spec.py | 2 + .../flask_labthings/views/extensions.py | 8 +--- .../common/flask_labthings/views/tasks.py | 3 +- 10 files changed, 62 insertions(+), 66 deletions(-) diff --git a/openflexure_microscope/api/app.py b/openflexure_microscope/api/app.py index afbae740..f90d24c1 100644 --- a/openflexure_microscope/api/app.py +++ b/openflexure_microscope/api/app.py @@ -69,7 +69,7 @@ app, labthing = create_app( app.json_encoder = JSONEncoder # Attach lab devices -labthing.register_device(api_microscope, "org.openflexure.microscope") +labthing.add_component(api_microscope, "org.openflexure.microscope") # Attach extensions if not os.path.isfile(USER_EXTENSIONS_PATH): @@ -78,33 +78,27 @@ for extension in find_extensions(USER_EXTENSIONS_PATH): labthing.register_extension(extension) # Attach captures resources -labthing.add_resource(views.CaptureList, f"/captures") -labthing.register_property(views.CaptureList) -labthing.add_resource(views.CaptureResource, f"/captures/") -labthing.add_resource(views.CaptureDownload, f"/captures//download/") -labthing.add_resource(views.CaptureTags, f"/captures//tags") -labthing.add_resource(views.CaptureMetadata, f"/captures//metadata") +labthing.add_view(views.CaptureList, f"/captures") +labthing.add_view(views.CaptureResource, f"/captures/") +labthing.add_view(views.CaptureDownload, f"/captures//download/") +labthing.add_view(views.CaptureTags, f"/captures//tags") +labthing.add_view(views.CaptureMetadata, f"/captures//metadata") # Attach settings and status resources -labthing.add_resource(views.SettingsProperty, f"/settings") -labthing.register_property(views.SettingsProperty) -labthing.add_resource(views.NestedSettingsProperty, "/settings/") -labthing.add_resource(views.StatusProperty, "/status") -labthing.register_property(views.StatusProperty) -labthing.add_resource(views.NestedStatusProperty, "/status/") +labthing.add_view(views.SettingsProperty, f"/settings") +labthing.add_view(views.NestedSettingsProperty, "/settings/") +labthing.add_view(views.StatusProperty, "/status") +labthing.add_view(views.NestedStatusProperty, "/status/") # Attach streams resources -labthing.add_resource(views.MjpegStream, f"/streams/mjpeg") -labthing.register_property(views.MjpegStream) -labthing.add_resource(views.SnapshotStream, f"/streams/snapshot") -labthing.register_property(views.SnapshotStream) +labthing.add_view(views.MjpegStream, f"/streams/mjpeg") +labthing.add_view(views.SnapshotStream, f"/streams/snapshot") # Attach microscope action resources for name, action in views.enabled_root_actions().items(): view_class = action["view_class"] rule = action["rule"] - labthing.add_resource(view_class, f"/actions{rule}") - labthing.register_action(view_class) + labthing.add_view(view_class, f"/actions{rule}") @app.route("/routes") diff --git a/openflexure_microscope/api/default_extensions/autofocus.py b/openflexure_microscope/api/default_extensions/autofocus.py index 913ce367..bfbe6cdc 100644 --- a/openflexure_microscope/api/default_extensions/autofocus.py +++ b/openflexure_microscope/api/default_extensions/autofocus.py @@ -1,6 +1,10 @@ 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.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.utilities import set_properties @@ -298,6 +302,7 @@ class MeasureSharpnessAPI(Resource): return jsonify({"sharpness": measure_sharpness(microscope)}) +@ThingAction class AutofocusAPI(Resource): """ Run a standard autofocus @@ -324,6 +329,7 @@ class AutofocusAPI(Resource): abort(503, "No stage connected. Unable to autofocus.") +@ThingAction class FastAutofocusAPI(Resource): """ 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_view(MeasureSharpnessAPI, "/measure_sharpness") - autofocus_extension_v2.add_view(AutofocusAPI, "/autofocus") -autofocus_extension_v2.register_action(AutofocusAPI) - autofocus_extension_v2.add_view(FastAutofocusAPI, "/fast_autofocus") -autofocus_extension_v2.register_action(FastAutofocusAPI) diff --git a/openflexure_microscope/api/default_extensions/scan.py b/openflexure_microscope/api/default_extensions/scan.py index 7cff8621..214c0694 100644 --- a/openflexure_microscope/api/default_extensions/scan.py +++ b/openflexure_microscope/api/default_extensions/scan.py @@ -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.decorators import ( marshal_task, - marshal_with, use_args, + ThingAction, ) from openflexure_microscope.common.flask_labthings import fields @@ -340,6 +340,7 @@ def stack( ### Web views +@ThingAction class TileScanAPI(Resource): @use_args( { @@ -398,4 +399,3 @@ class TileScanAPI(Resource): scan_extension_v2 = BaseExtension("scan") scan_extension_v2.add_view(TileScanAPI, "/tile") -scan_extension_v2.register_action(TileScanAPI) diff --git a/openflexure_microscope/api/default_extensions/zip_builder.py b/openflexure_microscope/api/default_extensions/zip_builder.py index 286789fc..fa1ea199 100644 --- a/openflexure_microscope/api/default_extensions/zip_builder.py +++ b/openflexure_microscope/api/default_extensions/zip_builder.py @@ -17,6 +17,10 @@ import logging 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.extensions import BaseExtension +from openflexure_microscope.common.flask_labthings.decorators import ( + ThingAction, + ThingProperty, +) class ZipManager: @@ -91,6 +95,7 @@ class ZipManager: default_zip_manager = ZipManager() +@ThingAction class ZipBuilderAPIView(Resource): def post(self): @@ -103,6 +108,7 @@ class ZipBuilderAPIView(Resource): return jsonify(task.state), 201 +@ThingProperty class ZipListAPIView(Resource): def get(self): return jsonify(default_zip_manager.session_zips) @@ -146,4 +152,3 @@ zip_extension_v2.add_view(ZipGetterAPIView, "/get/") zip_extension_v2.add_view(ZipListAPIView, "/get") zip_extension_v2.add_view(ZipBuilderAPIView, "/build") -zip_extension_v2.register_action(ZipBuilderAPIView) diff --git a/openflexure_microscope/api/example_extensions/ev_gui.py b/openflexure_microscope/api/example_extensions/ev_gui.py index aa950e4b..561e4acf 100644 --- a/openflexure_microscope/api/example_extensions/ev_gui.py +++ b/openflexure_microscope/api/example_extensions/ev_gui.py @@ -1,5 +1,9 @@ from openflexure_microscope.common.flask_labthings.resource import Resource 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 @@ -72,6 +76,7 @@ static_form = { } +@ThingProperty class TestAPIView(Resource): def get(self): global val_int @@ -79,6 +84,7 @@ class TestAPIView(Resource): return jsonify({"val": True}) +@ThingAction class TestDoAPIView(Resource): def post(self): global val_int diff --git a/openflexure_microscope/common/flask_labthings/decorators.py b/openflexure_microscope/common/flask_labthings/decorators.py index 2ebdc540..e186a5e1 100644 --- a/openflexure_microscope/common/flask_labthings/decorators.py +++ b/openflexure_microscope/common/flask_labthings/decorators.py @@ -77,16 +77,20 @@ def ThingAction(viewcls): update_spec(viewcls, {"_groups": ["actions"]}) return viewcls + thing_action = ThingAction + def ThingProperty(viewcls): # Pass params to call function attribute for external access update_spec(viewcls, {"tags": ["properties"]}) update_spec(viewcls, {"_groups": ["properties"]}) return viewcls + thing_property = ThingProperty + class use_args(object): def __init__(self, schema, **kwargs): """ @@ -121,8 +125,10 @@ class Doc(object): update_spec(f, self.kwargs) return f + doc = Doc + class Tag(object): def __init__(self, tags): if isinstance(tags, str): @@ -137,8 +143,10 @@ class Tag(object): update_spec(f, {"tags": self.tags}) return f + tag = Tag + class doc_response(object): def __init__(self, code, description=None, **kwargs): self.code = code diff --git a/openflexure_microscope/common/flask_labthings/labthing.py b/openflexure_microscope/common/flask_labthings/labthing.py index 58bbe574..df8888a4 100644 --- a/openflexure_microscope/common/flask_labthings/labthing.py +++ b/openflexure_microscope/common/flask_labthings/labthing.py @@ -28,11 +28,11 @@ class LabThing(object): ): self.app = app - self.devices = {} + self.components = {} self.extensions = {} - self.resources = [] + self.views = [] self.properties = {} self.actions = {} @@ -93,8 +93,8 @@ class LabThing(object): app.extensions[EXTENSION_NAME] = self # Add resources, if registered before tying to a Flask app - if len(self.resources) > 0: - for resource, urls, endpoint, kwargs in self.resources: + if len(self.views) > 0: + for resource, urls, endpoint, kwargs in self.views: self._register_view(app, resource, *urls, endpoint=endpoint, **kwargs) # Create base routes @@ -110,19 +110,15 @@ class LabThing(object): self.app.register_blueprint(docs_blueprint, url_prefix=self.url_prefix) # Add extension overview - self.add_resource( - ExtensionList, "/extensions", endpoint=EXTENSION_LIST_ENDPOINT - ) - self.register_property(ExtensionList) + self.add_view(ExtensionList, "/extensions", endpoint=EXTENSION_LIST_ENDPOINT) # Add task routes - self.add_resource(TaskList, "/tasks", endpoint=TASK_LIST_ENDPOINT) - self.register_property(TaskList) - self.add_resource(TaskResource, "/tasks/", endpoint=TASK_ENDPOINT) + self.add_view(TaskList, "/tasks", endpoint=TASK_LIST_ENDPOINT) + self.add_view(TaskResource, "/tasks/", endpoint=TASK_ENDPOINT) ### Device stuff - def register_device(self, device_object, device_name: str): - self.devices[device_name] = device_object + def add_component(self, device_object, device_name: str): + self.components[device_name] = device_object ### Extension stuff @@ -134,18 +130,12 @@ class LabThing(object): for extension_view_id, extension_view in extension_object.views.items(): # Add route to the extensions blueprint - self.add_resource( + self.add_view( extension_view["view"], "/extensions" + extension_view["rule"], **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 def _complete_url(self, url_part, registration_prefix): @@ -158,16 +148,8 @@ class LabThing(object): parts = [registration_prefix, self.url_prefix, url_part] return "".join([part for part in parts if part]) - def register_property(self, resource): - logging.warning("register_property is deprecated. Use the @ltproperty class decorator instead.") - 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. + def add_view(self, resource, *urls, endpoint=None, **kwargs): + """Adds a view to the api. :param resource: the class name of your resource :type resource: :class:`Type[Resource]` :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: 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): - self.add_resource(cls, *urls, **kwargs) + self.add_view(cls, *urls, **kwargs) return cls return decorator diff --git a/openflexure_microscope/common/flask_labthings/spec.py b/openflexure_microscope/common/flask_labthings/spec.py index d21687a5..13d9fcb3 100644 --- a/openflexure_microscope/common/flask_labthings/spec.py +++ b/openflexure_microscope/common/flask_labthings/spec.py @@ -20,10 +20,12 @@ def update_spec(obj, spec): rupdate(obj.__apispec__, spec) return obj.__apispec__ + def get_spec(obj): obj.__apispec__ = obj.__dict__.get("__apispec__", {}) return obj.__apispec__ + def view2path(rule: str, view: Resource, spec: APISpec): params = { "path": rule, # TODO: Validate this slightly (leading / etc) diff --git a/openflexure_microscope/common/flask_labthings/views/extensions.py b/openflexure_microscope/common/flask_labthings/views/extensions.py index 364c061d..6e1c01c8 100644 --- a/openflexure_microscope/common/flask_labthings/views/extensions.py +++ b/openflexure_microscope/common/flask_labthings/views/extensions.py @@ -1,17 +1,13 @@ """ 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 ..find import registered_extensions from ..schema import ExtensionSchema -from ..decorators import marshal_with +from ..decorators import marshal_with, ThingProperty +@ThingProperty class ExtensionList(Resource): """ List and basic documentation for all enabled Extensions diff --git a/openflexure_microscope/common/flask_labthings/views/tasks.py b/openflexure_microscope/common/flask_labthings/views/tasks.py index 11768fb0..3e446277 100644 --- a/openflexure_microscope/common/flask_labthings/views/tasks.py +++ b/openflexure_microscope/common/flask_labthings/views/tasks.py @@ -1,12 +1,13 @@ from flask import abort, url_for -from ..decorators import marshal_with +from ..decorators import marshal_with, ThingProperty from ..resource import Resource from ..schema import TaskSchema from openflexure_microscope.common.labthings_core import tasks +@ThingProperty class TaskList(Resource): """ List and basic documentation for all session tasks