Loading openflexure_microscope/api/doc/source/index.rst +5 −0 Original line number Diff line number Diff line Loading @@ -27,10 +27,15 @@ API Documentation Summary ------- .. qrefflask:: openflexure_microscope.api.v1:app :undoc-endpoints: index :undoc-static: :endpoints: API Details ----------- .. autoflask:: openflexure_microscope.api.v1:app :undoc-endpoints: index :undoc-static: :endpoints: :order: path No newline at end of file openflexure_microscope/api/v1.py +91 −73 Original line number Diff line number Diff line Loading @@ -7,7 +7,7 @@ TODO: Implement API route to cleanly shut down server TODO: Implement microscope function API routes (autofocus etc) """ from pprint import pprint import numpy as np from importlib import import_module import time import datetime Loading @@ -19,8 +19,6 @@ from flask import ( from flask.views import MethodView import numpy as np from openflexure_microscope.api.utilities import parse_payload, gen, get_bool from openflexure_microscope import Microscope Loading @@ -28,39 +26,43 @@ from openflexure_microscope.camera.pi import StreamingCamera from openflexure_stage import OpenFlexureStage import logging, sys logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) logging.info("Creating StreamingCamera") cam = StreamingCamera() logging.info("Creating stage") stage = OpenFlexureStage("/dev/ttyUSB0") logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) # Create the microscope object globally (common to all spawned server threads) logging.info("Creating microscope") microscope = Microscope( cam, stage api_microscope = Microscope( StreamingCamera(), OpenFlexureStage("/dev/ttyUSB0") ) logging.info("Creating app") # Create flask app app = Flask(__name__) # Make errors more API friendly @app.errorhandler(404) def not_found(error): return make_response(jsonify({'error': 'Not found'}), 404) # Some useful functions # TODO: Maybe auto-generate API URI base from module name def uri(suffix, base='/api/v1'): return base + suffix # Define front-end routes ##### WEBAPP ROUTES ###### @app.route('/') def index(): """Video streaming home page.""" """ API demo app """ return render_template( 'index_v1.html' ) ##### API ROUTES ###### ##### Basic microscope view ###### # Basic microscope view class MicroscopeView(MethodView): def __init__(self, microscope): Loading @@ -69,31 +71,14 @@ class MicroscopeView(MethodView): microscope object passed as an argument. """ self.microscope = microscope MethodView.__init__(self) # Test method class TestAPI(MicroscopeView): def get(self): """ Get method for my cool new MethodView """ return jsonify(self.microscope.state) def post(self): """ Post method for my cool new MethodView """ return jsonify(request.get_json()) app.add_url_rule('/test', view_func=TestAPI.as_view('test', microscope=microscope)) # Define API routes MethodView.__init__(self) # Basic views # State endpoints class StreamAPI(MicroscopeView): def get(self): """ Video streaming route. Put this in the src attribute of an img tag. Loading @@ -105,20 +90,25 @@ class StreamAPI(MicroscopeView): gen(self.microscope.camera), mimetype='multipart/x-mixed-replace; boundary=frame') app.add_url_rule(uri('/stream'), view_func=StreamAPI.as_view('stream', microscope=microscope)) app.add_url_rule( uri('/stream/'), view_func=StreamAPI.as_view('stream', microscope=api_microscope)) class StateAPI(MicroscopeView): def get(self): """ Return JSONified microscope state. """ return jsonify(self.microscope.state) app.add_url_rule(uri('/state'), view_func=StateAPI.as_view('state', microscope=microscope)) app.add_url_rule( uri('/state/'), view_func=StateAPI.as_view('state', microscope=api_microscope)) # Positioning routes # Positioning endpoints class PositionAPI(MicroscopeView): Loading Loading @@ -169,17 +159,19 @@ class PositionAPI(MicroscopeView): return jsonify(self.microscope.state['position']) app.add_url_rule(uri('/position'), view_func=PositionAPI.as_view('position', microscope=microscope)) app.add_url_rule( uri('/position/'), view_func=PositionAPI.as_view('position', microscope=api_microscope)) # Capture routes # Capture endpoints class CaptureAPI(MicroscopeView): def get(self, capture_id): def get(self): """ Get list of image captures """ if not capture_id: include_unavailable = get_bool(request.args.get('include_unavailable')) if include_unavailable: Loading @@ -188,30 +180,12 @@ class CaptureAPI(MicroscopeView): captures = [image.metadata for image in self.microscope.camera.images] return jsonify(captures) else: print(capture_id) capture_obj = microscope.camera.image_from_id(capture_id) if not capture_obj: return abort(404) # 404 Not Found # Get capture metadata capture_metadata = capture_obj.metadata # Add API routes to returned metadata uri_dict = { 'uri': {'metadata': uri('/capture/{}/'.format(capture_id))} } # If available, also add download link if capture_metadata['available']: uri_dict['uri']['download'] = uri('/capture/{}/download'.format(capture_id)) capture_metadata.update(uri_dict) return jsonify(capture_metadata) def delete(self, capture_id): def delete(self): """ Delete all captures """ return jsonify({"error": "not yet implemented"}) def post(self): Loading Loading @@ -252,20 +226,62 @@ class CaptureAPI(MicroscopeView): return jsonify(capture_obj.metadata) capture_view = CaptureAPI.as_view('capture', microscope=microscope) app.add_url_rule(uri('/capture/'), defaults={'capture_id': None}, view_func=capture_view, methods=['GET',]) app.add_url_rule(uri('/capture/<capture_id>/'), view_func=capture_view, methods=['GET', 'DELETE']) app.add_url_rule(uri('/capture/'), view_func=capture_view, methods=['POST',]) app.add_url_rule( uri('/capture/'), view_func=CaptureAPI.as_view('capture', microscope=api_microscope)) class CaptureIdAPI(MicroscopeView): def get(self, capture_id): """ Get JSON representation of a capture """ capture_obj = self.microscope.camera.image_from_id(capture_id) if not capture_obj: return abort(404) # 404 Not Found # Get capture metadata capture_metadata = capture_obj.metadata # Add API routes to returned metadata uri_dict = { 'uri': {'metadata': uri('/capture/{}/'.format(capture_id))} } # If available, also add download link if capture_metadata['available']: uri_dict['uri']['download'] = uri('/capture/{}/download'.format(capture_id)) capture_metadata.update(uri_dict) return jsonify(capture_metadata) def delete(self, capture_id): """ Delete a capture """ return jsonify({"return": capture_id}) def put(self, capture_id): """ Modify the metadata of a capture """ return jsonify({"return": capture_id}) app.add_url_rule( uri('/capture/<capture_id>/'), view_func=CaptureIdAPI.as_view('capture_id', microscope=api_microscope)) # Capture download routes class CaptureDlAPI(MicroscopeView): class CaptureDownloadAPI(MicroscopeView): def get(self, capture_id): """ Return image data for a capture. """ print(capture_id) capture_obj = microscope.camera.image_from_id(capture_id) capture_obj = self.microscope.camera.image_from_id(capture_id) if not capture_obj: return abort(404) # 404 Not Found Loading @@ -284,7 +300,9 @@ class CaptureDlAPI(MicroscopeView): as_attachment=as_attachment, attachment_filename=capture_obj.filename) app.add_url_rule(uri('/capture/<capture_id>/download/'), view_func=CaptureDlAPI.as_view('capture_download', microscope=microscope)) app.add_url_rule( uri('/capture/<capture_id>/download/'), view_func=CaptureDownloadAPI.as_view('capture_download', microscope=api_microscope)) if __name__ == "__main__": Loading Loading
openflexure_microscope/api/doc/source/index.rst +5 −0 Original line number Diff line number Diff line Loading @@ -27,10 +27,15 @@ API Documentation Summary ------- .. qrefflask:: openflexure_microscope.api.v1:app :undoc-endpoints: index :undoc-static: :endpoints: API Details ----------- .. autoflask:: openflexure_microscope.api.v1:app :undoc-endpoints: index :undoc-static: :endpoints: :order: path No newline at end of file
openflexure_microscope/api/v1.py +91 −73 Original line number Diff line number Diff line Loading @@ -7,7 +7,7 @@ TODO: Implement API route to cleanly shut down server TODO: Implement microscope function API routes (autofocus etc) """ from pprint import pprint import numpy as np from importlib import import_module import time import datetime Loading @@ -19,8 +19,6 @@ from flask import ( from flask.views import MethodView import numpy as np from openflexure_microscope.api.utilities import parse_payload, gen, get_bool from openflexure_microscope import Microscope Loading @@ -28,39 +26,43 @@ from openflexure_microscope.camera.pi import StreamingCamera from openflexure_stage import OpenFlexureStage import logging, sys logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) logging.info("Creating StreamingCamera") cam = StreamingCamera() logging.info("Creating stage") stage = OpenFlexureStage("/dev/ttyUSB0") logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) # Create the microscope object globally (common to all spawned server threads) logging.info("Creating microscope") microscope = Microscope( cam, stage api_microscope = Microscope( StreamingCamera(), OpenFlexureStage("/dev/ttyUSB0") ) logging.info("Creating app") # Create flask app app = Flask(__name__) # Make errors more API friendly @app.errorhandler(404) def not_found(error): return make_response(jsonify({'error': 'Not found'}), 404) # Some useful functions # TODO: Maybe auto-generate API URI base from module name def uri(suffix, base='/api/v1'): return base + suffix # Define front-end routes ##### WEBAPP ROUTES ###### @app.route('/') def index(): """Video streaming home page.""" """ API demo app """ return render_template( 'index_v1.html' ) ##### API ROUTES ###### ##### Basic microscope view ###### # Basic microscope view class MicroscopeView(MethodView): def __init__(self, microscope): Loading @@ -69,31 +71,14 @@ class MicroscopeView(MethodView): microscope object passed as an argument. """ self.microscope = microscope MethodView.__init__(self) # Test method class TestAPI(MicroscopeView): def get(self): """ Get method for my cool new MethodView """ return jsonify(self.microscope.state) def post(self): """ Post method for my cool new MethodView """ return jsonify(request.get_json()) app.add_url_rule('/test', view_func=TestAPI.as_view('test', microscope=microscope)) # Define API routes MethodView.__init__(self) # Basic views # State endpoints class StreamAPI(MicroscopeView): def get(self): """ Video streaming route. Put this in the src attribute of an img tag. Loading @@ -105,20 +90,25 @@ class StreamAPI(MicroscopeView): gen(self.microscope.camera), mimetype='multipart/x-mixed-replace; boundary=frame') app.add_url_rule(uri('/stream'), view_func=StreamAPI.as_view('stream', microscope=microscope)) app.add_url_rule( uri('/stream/'), view_func=StreamAPI.as_view('stream', microscope=api_microscope)) class StateAPI(MicroscopeView): def get(self): """ Return JSONified microscope state. """ return jsonify(self.microscope.state) app.add_url_rule(uri('/state'), view_func=StateAPI.as_view('state', microscope=microscope)) app.add_url_rule( uri('/state/'), view_func=StateAPI.as_view('state', microscope=api_microscope)) # Positioning routes # Positioning endpoints class PositionAPI(MicroscopeView): Loading Loading @@ -169,17 +159,19 @@ class PositionAPI(MicroscopeView): return jsonify(self.microscope.state['position']) app.add_url_rule(uri('/position'), view_func=PositionAPI.as_view('position', microscope=microscope)) app.add_url_rule( uri('/position/'), view_func=PositionAPI.as_view('position', microscope=api_microscope)) # Capture routes # Capture endpoints class CaptureAPI(MicroscopeView): def get(self, capture_id): def get(self): """ Get list of image captures """ if not capture_id: include_unavailable = get_bool(request.args.get('include_unavailable')) if include_unavailable: Loading @@ -188,30 +180,12 @@ class CaptureAPI(MicroscopeView): captures = [image.metadata for image in self.microscope.camera.images] return jsonify(captures) else: print(capture_id) capture_obj = microscope.camera.image_from_id(capture_id) if not capture_obj: return abort(404) # 404 Not Found # Get capture metadata capture_metadata = capture_obj.metadata # Add API routes to returned metadata uri_dict = { 'uri': {'metadata': uri('/capture/{}/'.format(capture_id))} } # If available, also add download link if capture_metadata['available']: uri_dict['uri']['download'] = uri('/capture/{}/download'.format(capture_id)) capture_metadata.update(uri_dict) return jsonify(capture_metadata) def delete(self, capture_id): def delete(self): """ Delete all captures """ return jsonify({"error": "not yet implemented"}) def post(self): Loading Loading @@ -252,20 +226,62 @@ class CaptureAPI(MicroscopeView): return jsonify(capture_obj.metadata) capture_view = CaptureAPI.as_view('capture', microscope=microscope) app.add_url_rule(uri('/capture/'), defaults={'capture_id': None}, view_func=capture_view, methods=['GET',]) app.add_url_rule(uri('/capture/<capture_id>/'), view_func=capture_view, methods=['GET', 'DELETE']) app.add_url_rule(uri('/capture/'), view_func=capture_view, methods=['POST',]) app.add_url_rule( uri('/capture/'), view_func=CaptureAPI.as_view('capture', microscope=api_microscope)) class CaptureIdAPI(MicroscopeView): def get(self, capture_id): """ Get JSON representation of a capture """ capture_obj = self.microscope.camera.image_from_id(capture_id) if not capture_obj: return abort(404) # 404 Not Found # Get capture metadata capture_metadata = capture_obj.metadata # Add API routes to returned metadata uri_dict = { 'uri': {'metadata': uri('/capture/{}/'.format(capture_id))} } # If available, also add download link if capture_metadata['available']: uri_dict['uri']['download'] = uri('/capture/{}/download'.format(capture_id)) capture_metadata.update(uri_dict) return jsonify(capture_metadata) def delete(self, capture_id): """ Delete a capture """ return jsonify({"return": capture_id}) def put(self, capture_id): """ Modify the metadata of a capture """ return jsonify({"return": capture_id}) app.add_url_rule( uri('/capture/<capture_id>/'), view_func=CaptureIdAPI.as_view('capture_id', microscope=api_microscope)) # Capture download routes class CaptureDlAPI(MicroscopeView): class CaptureDownloadAPI(MicroscopeView): def get(self, capture_id): """ Return image data for a capture. """ print(capture_id) capture_obj = microscope.camera.image_from_id(capture_id) capture_obj = self.microscope.camera.image_from_id(capture_id) if not capture_obj: return abort(404) # 404 Not Found Loading @@ -284,7 +300,9 @@ class CaptureDlAPI(MicroscopeView): as_attachment=as_attachment, attachment_filename=capture_obj.filename) app.add_url_rule(uri('/capture/<capture_id>/download/'), view_func=CaptureDlAPI.as_view('capture_download', microscope=microscope)) app.add_url_rule( uri('/capture/<capture_id>/download/'), view_func=CaptureDownloadAPI.as_view('capture_download', microscope=api_microscope)) if __name__ == "__main__": Loading