Restructures API views

This commit is contained in:
jtc42 2018-11-15 22:06:55 +00:00
parent a274c561e7
commit 6b62737aaf
2 changed files with 102 additions and 79 deletions

View file

@ -27,10 +27,15 @@ API Documentation
Summary Summary
------- -------
.. qrefflask:: openflexure_microscope.api.v1:app .. qrefflask:: openflexure_microscope.api.v1:app
:undoc-endpoints: index
:undoc-static: :undoc-static:
:endpoints:
API Details API Details
----------- -----------
.. autoflask:: openflexure_microscope.api.v1:app .. autoflask:: openflexure_microscope.api.v1:app
:undoc-static: :undoc-endpoints: index
:undoc-static:
:endpoints:
:order: path

View file

@ -7,7 +7,7 @@ TODO: Implement API route to cleanly shut down server
TODO: Implement microscope function API routes (autofocus etc) TODO: Implement microscope function API routes (autofocus etc)
""" """
from pprint import pprint import numpy as np
from importlib import import_module from importlib import import_module
import time import time
import datetime import datetime
@ -19,8 +19,6 @@ from flask import (
from flask.views import MethodView from flask.views import MethodView
import numpy as np
from openflexure_microscope.api.utilities import parse_payload, gen, get_bool from openflexure_microscope.api.utilities import parse_payload, gen, get_bool
from openflexure_microscope import Microscope from openflexure_microscope import Microscope
@ -28,39 +26,43 @@ from openflexure_microscope.camera.pi import StreamingCamera
from openflexure_stage import OpenFlexureStage from openflexure_stage import OpenFlexureStage
import logging, sys import logging, sys
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
logging.info("Creating StreamingCamera")
cam = StreamingCamera()
logging.info("Creating stage")
stage = OpenFlexureStage("/dev/ttyUSB0")
# Create the microscope object globally (common to all spawned server threads) # Create the microscope object globally (common to all spawned server threads)
logging.info("Creating microscope") api_microscope = Microscope(
microscope = Microscope( StreamingCamera(),
cam, OpenFlexureStage("/dev/ttyUSB0")
stage
) )
logging.info("Creating app")
# Create flask app # Create flask app
app = Flask(__name__) 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 # Some useful functions
# TODO: Maybe auto-generate API URI base from module name
def uri(suffix, base='/api/v1'): def uri(suffix, base='/api/v1'):
return base + suffix return base + suffix
# Define front-end routes
##### WEBAPP ROUTES ######
@app.route('/') @app.route('/')
def index(): def index():
"""Video streaming home page.""" """
API demo app
"""
return render_template( return render_template(
'index_v1.html' 'index_v1.html'
) )
##### API ROUTES ######
##### Basic microscope view ###### # Basic microscope view
class MicroscopeView(MethodView): class MicroscopeView(MethodView):
def __init__(self, microscope): def __init__(self, microscope):
@ -69,31 +71,14 @@ class MicroscopeView(MethodView):
microscope object passed as an argument. microscope object passed as an argument.
""" """
self.microscope = microscope self.microscope = microscope
MethodView.__init__(self) 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
# Basic views
# State endpoints
class StreamAPI(MicroscopeView): class StreamAPI(MicroscopeView):
def get(self): def get(self):
""" """
Video streaming route. Put this in the src attribute of an img tag. Video streaming route. Put this in the src attribute of an img tag.
@ -105,20 +90,25 @@ class StreamAPI(MicroscopeView):
gen(self.microscope.camera), gen(self.microscope.camera),
mimetype='multipart/x-mixed-replace; boundary=frame') 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): class StateAPI(MicroscopeView):
def get(self): def get(self):
""" """
Return JSONified microscope state. Return JSONified microscope state.
""" """
return jsonify(self.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): class PositionAPI(MicroscopeView):
@ -169,49 +159,33 @@ class PositionAPI(MicroscopeView):
return jsonify(self.microscope.state['position']) 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): class CaptureAPI(MicroscopeView):
def get(self, capture_id): def get(self):
""" """
Get list of image captures Get list of image captures
""" """
if not capture_id: include_unavailable = get_bool(request.args.get('include_unavailable'))
include_unavailable = get_bool(request.args.get('include_unavailable'))
if include_unavailable: if include_unavailable:
captures = [image.metadata for image in self.microscope.camera.images if image.metadata['available']] captures = [image.metadata for image in self.microscope.camera.images if image.metadata['available']]
else:
captures = [image.metadata for image in self.microscope.camera.images]
return jsonify(captures)
else: else:
print(capture_id) captures = [image.metadata for image in self.microscope.camera.images]
capture_obj = microscope.camera.image_from_id(capture_id)
if not capture_obj: return jsonify(captures)
return abort(404) # 404 Not Found
# Get capture metadata
capture_metadata = capture_obj.metadata
# Add API routes to returned metadata def delete(self):
uri_dict = { """
'uri': {'metadata': uri('/capture/{}/'.format(capture_id))} Delete all captures
} """
# 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):
return jsonify({"error": "not yet implemented"}) return jsonify({"error": "not yet implemented"})
def post(self): def post(self):
@ -252,20 +226,62 @@ class CaptureAPI(MicroscopeView):
return jsonify(capture_obj.metadata) return jsonify(capture_obj.metadata)
capture_view = CaptureAPI.as_view('capture', microscope=microscope) app.add_url_rule(
app.add_url_rule(uri('/capture/'), defaults={'capture_id': None}, view_func=capture_view, methods=['GET',]) uri('/capture/'),
app.add_url_rule(uri('/capture/<capture_id>/'), view_func=capture_view, methods=['GET', 'DELETE']) view_func=CaptureAPI.as_view('capture', microscope=api_microscope))
app.add_url_rule(uri('/capture/'), view_func=capture_view, methods=['POST',])
# Capture download routes class CaptureIdAPI(MicroscopeView):
class CaptureDlAPI(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))
class CaptureDownloadAPI(MicroscopeView):
def get(self, capture_id): def get(self, capture_id):
""" """
Return image data for a capture. Return image data for a capture.
""" """
print(capture_id) 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: if not capture_obj:
return abort(404) # 404 Not Found return abort(404) # 404 Not Found
@ -284,7 +300,9 @@ class CaptureDlAPI(MicroscopeView):
as_attachment=as_attachment, as_attachment=as_attachment,
attachment_filename=capture_obj.filename) 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__": if __name__ == "__main__":