Restructures API views
This commit is contained in:
parent
a274c561e7
commit
6b62737aaf
2 changed files with 102 additions and 79 deletions
|
|
@ -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
|
||||||
|
|
@ -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__":
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue