Blackened everything
This commit is contained in:
parent
e213647217
commit
5966ce29be
57 changed files with 1938 additions and 1414 deletions
|
|
@ -1,3 +1,3 @@
|
|||
__all__ = ['utilities']
|
||||
__all__ = ["utilities"]
|
||||
|
||||
from . import utilities
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ import logging
|
|||
import sys
|
||||
import os
|
||||
|
||||
from flask import (
|
||||
Flask, jsonify, send_file)
|
||||
from flask import Flask, jsonify, send_file
|
||||
|
||||
from serial import SerialException
|
||||
from datetime import datetime
|
||||
|
|
@ -39,7 +38,7 @@ from openflexure_microscope.stage.mock import MockStage
|
|||
# Handle logging
|
||||
is_gunicorn = "gunicorn" in os.environ.get("SERVER_SOFTWARE", "")
|
||||
|
||||
DEFAULT_LOGFILE = os.path.join(USER_CONFIG_DIR, 'openflexure_microscope.log')
|
||||
DEFAULT_LOGFILE = os.path.join(USER_CONFIG_DIR, "openflexure_microscope.log")
|
||||
|
||||
if (__name__ == "__main__") or (not is_gunicorn):
|
||||
# If imported, but not by gunicorn
|
||||
|
|
@ -48,18 +47,15 @@ if (__name__ == "__main__") or (not is_gunicorn):
|
|||
else:
|
||||
# Direct standard Python logging to file and console
|
||||
root = logging.getLogger()
|
||||
error_formatter = logging.Formatter("[%(asctime)s] [%(threadName)s] [%(levelname)s] %(message)s")
|
||||
|
||||
rotating_logfile = logging.handlers.RotatingFileHandler(
|
||||
DEFAULT_LOGFILE,
|
||||
maxBytes=1000000,
|
||||
backupCount=7
|
||||
error_formatter = logging.Formatter(
|
||||
"[%(asctime)s] [%(threadName)s] [%(levelname)s] %(message)s"
|
||||
)
|
||||
|
||||
error_handlers = [
|
||||
rotating_logfile,
|
||||
logging.StreamHandler()
|
||||
]
|
||||
rotating_logfile = logging.handlers.RotatingFileHandler(
|
||||
DEFAULT_LOGFILE, maxBytes=1000000, backupCount=7
|
||||
)
|
||||
|
||||
error_handlers = [rotating_logfile, logging.StreamHandler()]
|
||||
|
||||
for handler in error_handlers:
|
||||
handler.setFormatter(error_formatter)
|
||||
|
|
@ -88,7 +84,7 @@ def uri(suffix, api_version, base=None):
|
|||
app = Flask(__name__)
|
||||
app.url_map.strict_slashes = False
|
||||
|
||||
CORS(app, resources=r'/api/*')
|
||||
CORS(app, resources=r"/api/*")
|
||||
|
||||
# Make errors more API friendly
|
||||
handler = JSONExceptionHandler(app)
|
||||
|
|
@ -121,10 +117,7 @@ def attach_microscope():
|
|||
|
||||
# Attach devices to microscope
|
||||
logging.debug("Attaching devices to microscope...")
|
||||
api_microscope.attach(
|
||||
api_camera,
|
||||
api_stage
|
||||
)
|
||||
api_microscope.attach(api_camera, api_stage)
|
||||
|
||||
# Restore loaded capture array to camera object
|
||||
logging.debug("Restoring captures...")
|
||||
|
|
@ -140,26 +133,26 @@ def attach_microscope():
|
|||
|
||||
# Base routes
|
||||
base_blueprint = blueprints.base.construct_blueprint(api_microscope)
|
||||
app.register_blueprint(base_blueprint, url_prefix=uri('', 'v1'))
|
||||
app.register_blueprint(base_blueprint, url_prefix=uri("", "v1"))
|
||||
|
||||
# Stage routes
|
||||
stage_blueprint = blueprints.stage.construct_blueprint(api_microscope)
|
||||
app.register_blueprint(stage_blueprint, url_prefix=uri('/stage', 'v1'))
|
||||
app.register_blueprint(stage_blueprint, url_prefix=uri("/stage", "v1"))
|
||||
|
||||
# Camera routes
|
||||
camera_blueprint = blueprints.camera.construct_blueprint(api_microscope)
|
||||
app.register_blueprint(camera_blueprint, url_prefix=uri('/camera', 'v1'))
|
||||
app.register_blueprint(camera_blueprint, url_prefix=uri("/camera", "v1"))
|
||||
|
||||
# Plugin routes
|
||||
plugin_blueprint = blueprints.plugins.construct_blueprint(api_microscope)
|
||||
app.register_blueprint(plugin_blueprint, url_prefix=uri('/plugin', 'v1'))
|
||||
app.register_blueprint(plugin_blueprint, url_prefix=uri("/plugin", "v1"))
|
||||
|
||||
# Task routes
|
||||
task_blueprint = blueprints.task.construct_blueprint(api_microscope)
|
||||
app.register_blueprint(task_blueprint, url_prefix=uri('/task', 'v1'))
|
||||
app.register_blueprint(task_blueprint, url_prefix=uri("/task", "v1"))
|
||||
|
||||
|
||||
@app.route('/routes')
|
||||
@app.route("/routes")
|
||||
def routes():
|
||||
"""
|
||||
List of all connected API routes
|
||||
|
|
@ -173,7 +166,7 @@ def routes():
|
|||
return jsonify(list_routes(app))
|
||||
|
||||
|
||||
@app.route('/log')
|
||||
@app.route("/log")
|
||||
def err_log():
|
||||
"""
|
||||
Most recent 1mb of log output
|
||||
|
|
@ -186,9 +179,9 @@ def err_log():
|
|||
"""
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||
return send_file(
|
||||
DEFAULT_LOGFILE,
|
||||
as_attachment=True,
|
||||
attachment_filename='openflexure_microscope_{}.log'.format(timestamp)
|
||||
DEFAULT_LOGFILE,
|
||||
as_attachment=True,
|
||||
attachment_filename="openflexure_microscope_{}.log".format(timestamp),
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -219,4 +212,4 @@ def cleanup():
|
|||
atexit.register(cleanup)
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host='0.0.0.0', port="5000", threaded=True, debug=True, use_reloader=False)
|
||||
app.run(host="0.0.0.0", port="5000", threaded=True, debug=True, use_reloader=False)
|
||||
|
|
|
|||
|
|
@ -17,17 +17,14 @@ class JSONExceptionHandler(object):
|
|||
def std_handler(self, error):
|
||||
if isinstance(error, HTTPException):
|
||||
message = error.description
|
||||
elif hasattr(error, 'message'):
|
||||
elif hasattr(error, "message"):
|
||||
message = error.message
|
||||
else:
|
||||
message = str(error)
|
||||
|
||||
status_code = error.code if isinstance(error, HTTPException) else 500
|
||||
|
||||
response = {
|
||||
'status_code': status_code,
|
||||
'message': escape(message)
|
||||
}
|
||||
response = {"status_code": status_code, "message": escape(message)}
|
||||
return jsonify(response), status_code
|
||||
|
||||
def init_app(self, app):
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ class JsonResponse:
|
|||
"""
|
||||
# Try to load as json
|
||||
try:
|
||||
self.json = request.get_json() #: dict: Dictionary representation of request JSON
|
||||
self.json = (
|
||||
request.get_json()
|
||||
) #: dict: Dictionary representation of request JSON
|
||||
# If malformed JSON is passed, make an empty dictionary
|
||||
except BadRequest as e:
|
||||
logging.error(e)
|
||||
|
|
@ -55,15 +57,12 @@ def gen(camera):
|
|||
# the obtained frame is a jpeg
|
||||
frame = camera.get_frame()
|
||||
|
||||
yield (b'--frame\r\n'
|
||||
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
|
||||
yield (b"--frame\r\n" b"Content-Type: image/jpeg\r\n\r\n" + frame + b"\r\n")
|
||||
|
||||
|
||||
def get_bool(get_arg):
|
||||
"""Convert GET request argument string to a Python bool"""
|
||||
if (get_arg == 'true' or
|
||||
get_arg == 'True' or
|
||||
get_arg == '1'):
|
||||
if get_arg == "true" or get_arg == "True" or get_arg == "1":
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
|
@ -80,10 +79,7 @@ def list_routes(app):
|
|||
endpoint = rule.endpoint
|
||||
methods = list(rule.methods)
|
||||
url = url_for(rule.endpoint, **options)
|
||||
line = {
|
||||
'endpoint': endpoint,
|
||||
'methods': methods
|
||||
}
|
||||
line = {"endpoint": endpoint, "methods": methods}
|
||||
output[url] = line
|
||||
|
||||
return output
|
||||
|
||||
return output
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import logging
|
|||
|
||||
|
||||
class StreamAPI(MicroscopeView):
|
||||
|
||||
def get(self):
|
||||
"""
|
||||
Real-time MJPEG stream from the microscope camera
|
||||
|
|
@ -22,11 +21,11 @@ class StreamAPI(MicroscopeView):
|
|||
|
||||
return Response(
|
||||
gen(self.microscope.camera),
|
||||
mimetype='multipart/x-mixed-replace; boundary=frame')
|
||||
mimetype="multipart/x-mixed-replace; boundary=frame",
|
||||
)
|
||||
|
||||
|
||||
class StateAPI(MicroscopeView):
|
||||
|
||||
def get(self):
|
||||
"""
|
||||
JSON representation of the microscope object.
|
||||
|
|
@ -77,7 +76,6 @@ class StateAPI(MicroscopeView):
|
|||
|
||||
|
||||
class ConfigAPI(MicroscopeView):
|
||||
|
||||
def get(self):
|
||||
"""
|
||||
JSON representation of the microscope config.
|
||||
|
|
@ -216,21 +214,18 @@ class ConfigAPI(MicroscopeView):
|
|||
|
||||
def construct_blueprint(microscope_obj):
|
||||
|
||||
blueprint = Blueprint('base_blueprint', __name__)
|
||||
blueprint = Blueprint("base_blueprint", __name__)
|
||||
|
||||
blueprint.add_url_rule(
|
||||
'/stream',
|
||||
view_func=StreamAPI.as_view('stream', microscope=microscope_obj)
|
||||
"/stream", view_func=StreamAPI.as_view("stream", microscope=microscope_obj)
|
||||
)
|
||||
|
||||
blueprint.add_url_rule(
|
||||
'/state',
|
||||
view_func=StateAPI.as_view('state', microscope=microscope_obj)
|
||||
"/state", view_func=StateAPI.as_view("state", microscope=microscope_obj)
|
||||
)
|
||||
|
||||
blueprint.add_url_rule(
|
||||
'/config',
|
||||
view_func=ConfigAPI.as_view('config', microscope=microscope_obj)
|
||||
"/config", view_func=ConfigAPI.as_view("config", microscope=microscope_obj)
|
||||
)
|
||||
|
||||
return blueprint
|
||||
|
|
|
|||
|
|
@ -7,51 +7,70 @@ from . import capture, record, preview, function
|
|||
|
||||
def construct_blueprint(microscope_obj):
|
||||
|
||||
blueprint = Blueprint('camera_blueprint', __name__)
|
||||
blueprint = Blueprint("camera_blueprint", __name__)
|
||||
|
||||
# Metadata routes
|
||||
blueprint.add_url_rule(
|
||||
'/capture/<capture_id>/metadata/<filename>',
|
||||
view_func=capture.MetadataAPI.as_view('metadata_download', microscope=microscope_obj))
|
||||
"/capture/<capture_id>/metadata/<filename>",
|
||||
view_func=capture.MetadataAPI.as_view(
|
||||
"metadata_download", microscope=microscope_obj
|
||||
),
|
||||
)
|
||||
|
||||
blueprint.add_url_rule(
|
||||
'/capture/<capture_id>/metadata',
|
||||
view_func=capture.MetadataRedirectAPI.as_view('metadata_download_redirect', microscope=microscope_obj))
|
||||
"/capture/<capture_id>/metadata",
|
||||
view_func=capture.MetadataRedirectAPI.as_view(
|
||||
"metadata_download_redirect", microscope=microscope_obj
|
||||
),
|
||||
)
|
||||
|
||||
# Tag routes
|
||||
blueprint.add_url_rule(
|
||||
'/capture/<capture_id>/tags',
|
||||
view_func=capture.TagsAPI.as_view('capture_tags', microscope=microscope_obj))
|
||||
"/capture/<capture_id>/tags",
|
||||
view_func=capture.TagsAPI.as_view("capture_tags", microscope=microscope_obj),
|
||||
)
|
||||
|
||||
# Capture routes
|
||||
blueprint.add_url_rule(
|
||||
'/capture/<capture_id>/download/<filename>',
|
||||
view_func=capture.DownloadAPI.as_view('capture_download', microscope=microscope_obj))
|
||||
"/capture/<capture_id>/download/<filename>",
|
||||
view_func=capture.DownloadAPI.as_view(
|
||||
"capture_download", microscope=microscope_obj
|
||||
),
|
||||
)
|
||||
|
||||
blueprint.add_url_rule(
|
||||
'/capture/<capture_id>/download',
|
||||
view_func=capture.DownloadRedirectAPI.as_view('capture_download_redirect', microscope=microscope_obj))
|
||||
"/capture/<capture_id>/download",
|
||||
view_func=capture.DownloadRedirectAPI.as_view(
|
||||
"capture_download_redirect", microscope=microscope_obj
|
||||
),
|
||||
)
|
||||
|
||||
blueprint.add_url_rule(
|
||||
'/capture/<capture_id>/',
|
||||
view_func=capture.CaptureAPI.as_view('capture', microscope=microscope_obj))
|
||||
"/capture/<capture_id>/",
|
||||
view_func=capture.CaptureAPI.as_view("capture", microscope=microscope_obj),
|
||||
)
|
||||
|
||||
blueprint.add_url_rule(
|
||||
'/capture/',
|
||||
view_func=capture.ListAPI.as_view('capture_list', microscope=microscope_obj))
|
||||
"/capture/",
|
||||
view_func=capture.ListAPI.as_view("capture_list", microscope=microscope_obj),
|
||||
)
|
||||
|
||||
# Preview routes
|
||||
blueprint.add_url_rule(
|
||||
'/preview/<string:operation>',
|
||||
view_func=preview.GPUPreviewAPI.as_view('gpu_preview', microscope=microscope_obj))
|
||||
"/preview/<string:operation>",
|
||||
view_func=preview.GPUPreviewAPI.as_view(
|
||||
"gpu_preview", microscope=microscope_obj
|
||||
),
|
||||
)
|
||||
|
||||
# Function routes
|
||||
blueprint.add_url_rule(
|
||||
'/overlay',
|
||||
view_func=function.OverlayAPI.as_view('overlay', microscope=microscope_obj))
|
||||
"/overlay",
|
||||
view_func=function.OverlayAPI.as_view("overlay", microscope=microscope_obj),
|
||||
)
|
||||
|
||||
blueprint.add_url_rule(
|
||||
'/zoom',
|
||||
view_func=function.ZoomAPI.as_view('zoom', microscope=microscope_obj))
|
||||
"/zoom", view_func=function.ZoomAPI.as_view("zoom", microscope=microscope_obj)
|
||||
)
|
||||
|
||||
return(blueprint)
|
||||
return blueprint
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ from flask import Response, jsonify, request, abort, url_for, redirect, send_fil
|
|||
|
||||
|
||||
class ListAPI(MicroscopeView):
|
||||
|
||||
def get(self):
|
||||
"""
|
||||
Get list of image captures.
|
||||
|
|
@ -30,12 +29,16 @@ class ListAPI(MicroscopeView):
|
|||
:status 200: capture found
|
||||
:status 404: no capture found with that id
|
||||
"""
|
||||
include_unavailable = get_bool(request.args.get('include_unavailable'))
|
||||
include_unavailable = get_bool(request.args.get("include_unavailable"))
|
||||
|
||||
if include_unavailable:
|
||||
captures = [image.state for image in self.microscope.camera.images]
|
||||
else:
|
||||
captures = [image.state for image in self.microscope.camera.images if image.state['available']]
|
||||
captures = [
|
||||
image.state
|
||||
for image in self.microscope.camera.images
|
||||
if image.state["available"]
|
||||
]
|
||||
|
||||
return jsonify(captures)
|
||||
|
||||
|
|
@ -100,38 +103,40 @@ class ListAPI(MicroscopeView):
|
|||
"""
|
||||
payload = JsonResponse(request)
|
||||
|
||||
filename = payload.param('filename')
|
||||
temporary = payload.param('temporary', default=False, convert=bool)
|
||||
use_video_port = payload.param('use_video_port', default=False, convert=bool)
|
||||
bayer = payload.param('bayer', default=True, convert=bool)
|
||||
metadata = payload.param('metadata', default={}, convert=dict)
|
||||
tags = payload.param('tags', default=[], convert=list)
|
||||
filename = payload.param("filename")
|
||||
temporary = payload.param("temporary", default=False, convert=bool)
|
||||
use_video_port = payload.param("use_video_port", default=False, convert=bool)
|
||||
bayer = payload.param("bayer", default=True, convert=bool)
|
||||
metadata = payload.param("metadata", default={}, convert=dict)
|
||||
tags = payload.param("tags", default=[], convert=list)
|
||||
|
||||
resize = payload.param('size', default=None)
|
||||
resize = payload.param("size", default=None)
|
||||
if resize:
|
||||
if ('width' in resize) and ('height' in resize):
|
||||
resize = (int(resize['width']), int(resize['height'])) # Convert dict to tuple
|
||||
if ("width" in resize) and ("height" in resize):
|
||||
resize = (
|
||||
int(resize["width"]),
|
||||
int(resize["height"]),
|
||||
) # Convert dict to tuple
|
||||
else:
|
||||
abort(404)
|
||||
|
||||
# Explicitally acquire lock (prevents empty files being created if lock is unavailable)
|
||||
with self.microscope.camera.lock:
|
||||
output = self.microscope.camera.new_image(
|
||||
write_to_file=True,
|
||||
temporary=temporary,
|
||||
filename=filename)
|
||||
write_to_file=True, temporary=temporary, filename=filename
|
||||
)
|
||||
|
||||
self.microscope.camera.capture(
|
||||
output,
|
||||
use_video_port=use_video_port,
|
||||
resize=resize,
|
||||
bayer=bayer)
|
||||
output, use_video_port=use_video_port, resize=resize, bayer=bayer
|
||||
)
|
||||
|
||||
metadata.update({
|
||||
'position': self.microscope.state['stage']['position'],
|
||||
'microscope_id': self.microscope.id,
|
||||
'microscope_name': self.microscope.name
|
||||
})
|
||||
metadata.update(
|
||||
{
|
||||
"position": self.microscope.state["stage"]["position"],
|
||||
"microscope_id": self.microscope.id,
|
||||
"microscope_name": self.microscope.name,
|
||||
}
|
||||
)
|
||||
|
||||
output.put_metadata(metadata)
|
||||
output.put_tags(tags)
|
||||
|
|
@ -140,7 +145,6 @@ class ListAPI(MicroscopeView):
|
|||
|
||||
|
||||
class CaptureAPI(MicroscopeView):
|
||||
|
||||
def get(self, capture_id):
|
||||
"""
|
||||
Get JSON representation of a capture
|
||||
|
|
@ -200,14 +204,15 @@ class CaptureAPI(MicroscopeView):
|
|||
|
||||
# Add API routes to returned state
|
||||
uri_dict = {
|
||||
'uri': {'state': '{}'.format(url_for('.capture', capture_id=capture_obj.id))}
|
||||
"uri": {
|
||||
"state": "{}".format(url_for(".capture", capture_id=capture_obj.id))
|
||||
}
|
||||
}
|
||||
|
||||
# If available, also add download link
|
||||
if capture_metadata['available']:
|
||||
uri_dict['uri']['download'] = '{}download/{}'.format(
|
||||
url_for('.capture', capture_id=capture_obj.id),
|
||||
capture_obj.filename
|
||||
if capture_metadata["available"]:
|
||||
uri_dict["uri"]["download"] = "{}download/{}".format(
|
||||
url_for(".capture", capture_id=capture_obj.id), capture_obj.filename
|
||||
)
|
||||
|
||||
capture_metadata.update(uri_dict)
|
||||
|
|
@ -257,7 +262,7 @@ class CaptureAPI(MicroscopeView):
|
|||
|
||||
if not capture_obj:
|
||||
return abort(404) # 404 Not Found
|
||||
|
||||
|
||||
data_dict = JsonResponse(request).json
|
||||
|
||||
capture_obj.put_metadata(data_dict)
|
||||
|
|
@ -299,17 +304,20 @@ class DownloadRedirectAPI(MicroscopeView):
|
|||
"""
|
||||
capture_obj = self.microscope.camera.image_from_id(capture_id)
|
||||
|
||||
if not capture_obj or not capture_obj.state['available']:
|
||||
if not capture_obj or not capture_obj.state["available"]:
|
||||
return abort(404) # 404 Not Found
|
||||
|
||||
thumbnail = get_bool(request.args.get('thumbnail'))
|
||||
thumbnail = get_bool(request.args.get("thumbnail"))
|
||||
|
||||
return redirect(url_for(
|
||||
'.capture_download',
|
||||
capture_id=capture_id,
|
||||
filename=capture_obj.filename,
|
||||
thumbnail=thumbnail
|
||||
), code=307)
|
||||
return redirect(
|
||||
url_for(
|
||||
".capture_download",
|
||||
capture_id=capture_id,
|
||||
filename=capture_obj.filename,
|
||||
thumbnail=thumbnail,
|
||||
),
|
||||
code=307,
|
||||
)
|
||||
|
||||
|
||||
class DownloadAPI(MicroscopeView):
|
||||
|
|
@ -317,19 +325,22 @@ class DownloadAPI(MicroscopeView):
|
|||
|
||||
capture_obj = self.microscope.camera.image_from_id(capture_id)
|
||||
|
||||
if not capture_obj or not capture_obj.state['available']:
|
||||
if not capture_obj or not capture_obj.state["available"]:
|
||||
return abort(404) # 404 Not Found
|
||||
|
||||
thumbnail = get_bool(request.args.get('thumbnail'))
|
||||
thumbnail = get_bool(request.args.get("thumbnail"))
|
||||
|
||||
# If no filename is specified, redirect to the capture's currently set filename
|
||||
if not filename:
|
||||
return redirect(url_for(
|
||||
'capture_download',
|
||||
capture_id=capture_id,
|
||||
filename=capture_obj.filename,
|
||||
thumbnail=thumbnail
|
||||
), code=307)
|
||||
return redirect(
|
||||
url_for(
|
||||
"capture_download",
|
||||
capture_id=capture_id,
|
||||
filename=capture_obj.filename,
|
||||
thumbnail=thumbnail,
|
||||
),
|
||||
code=307,
|
||||
)
|
||||
|
||||
# Download the image data using the requested filename
|
||||
if thumbnail:
|
||||
|
|
@ -337,9 +348,7 @@ class DownloadAPI(MicroscopeView):
|
|||
else:
|
||||
img = capture_obj.data
|
||||
|
||||
return send_file(
|
||||
img,
|
||||
mimetype='image/jpeg')
|
||||
return send_file(img, mimetype="image/jpeg")
|
||||
|
||||
|
||||
class MetadataRedirectAPI(MicroscopeView):
|
||||
|
|
@ -374,14 +383,17 @@ class MetadataRedirectAPI(MicroscopeView):
|
|||
"""
|
||||
capture_obj = self.microscope.camera.image_from_id(capture_id)
|
||||
|
||||
if not capture_obj or not capture_obj.state['available']:
|
||||
if not capture_obj or not capture_obj.state["available"]:
|
||||
return abort(404) # 404 Not Found
|
||||
|
||||
return redirect(url_for(
|
||||
'.metadata_download',
|
||||
capture_id=capture_id,
|
||||
filename=capture_obj.metadataname
|
||||
), code=307)
|
||||
return redirect(
|
||||
url_for(
|
||||
".metadata_download",
|
||||
capture_id=capture_id,
|
||||
filename=capture_obj.metadataname,
|
||||
),
|
||||
code=307,
|
||||
)
|
||||
|
||||
|
||||
class MetadataAPI(MicroscopeView):
|
||||
|
|
@ -389,23 +401,24 @@ class MetadataAPI(MicroscopeView):
|
|||
|
||||
capture_obj = self.microscope.camera.image_from_id(capture_id)
|
||||
|
||||
if not capture_obj or not capture_obj.state['available']:
|
||||
if not capture_obj or not capture_obj.state["available"]:
|
||||
return abort(404) # 404 Not Found
|
||||
|
||||
# If no filename is specified, redirect to the capture's currently set filename
|
||||
if not filename:
|
||||
return redirect(url_for(
|
||||
'capture_download',
|
||||
capture_id=capture_id,
|
||||
filename=capture_obj.metadataname
|
||||
), code=307)
|
||||
return redirect(
|
||||
url_for(
|
||||
"capture_download",
|
||||
capture_id=capture_id,
|
||||
filename=capture_obj.metadataname,
|
||||
),
|
||||
code=307,
|
||||
)
|
||||
|
||||
# Download the metadata using the requested filename
|
||||
data = capture_obj.yaml
|
||||
|
||||
return Response(
|
||||
data,
|
||||
mimetype="text/yaml")
|
||||
return Response(data, mimetype="text/yaml")
|
||||
|
||||
|
||||
class TagsAPI(MicroscopeView):
|
||||
|
|
@ -430,13 +443,13 @@ class TagsAPI(MicroscopeView):
|
|||
"""
|
||||
capture_obj = self.microscope.camera.image_from_id(capture_id)
|
||||
|
||||
if not capture_obj or not capture_obj.state['available']:
|
||||
if not capture_obj or not capture_obj.state["available"]:
|
||||
return abort(404) # 404 Not Found
|
||||
|
||||
metadata_tags = filter_dict(capture_obj.state, ['metadata', 'tags'])
|
||||
metadata_tags = filter_dict(capture_obj.state, ["metadata", "tags"])
|
||||
|
||||
return jsonify(metadata_tags)
|
||||
|
||||
|
||||
def put(self, capture_id):
|
||||
"""
|
||||
Add tags to the capture
|
||||
|
|
@ -460,9 +473,9 @@ class TagsAPI(MicroscopeView):
|
|||
|
||||
capture_obj = self.microscope.camera.image_from_id(capture_id)
|
||||
|
||||
if not capture_obj or not capture_obj.state['available']:
|
||||
if not capture_obj or not capture_obj.state["available"]:
|
||||
return abort(404) # 404 Not Found
|
||||
|
||||
|
||||
data_dict = JsonResponse(request).json
|
||||
|
||||
if type(data_dict) != list:
|
||||
|
|
@ -470,7 +483,7 @@ class TagsAPI(MicroscopeView):
|
|||
|
||||
capture_obj.put_tags(data_dict)
|
||||
|
||||
metadata_tags = filter_dict(capture_obj.state, ['metadata', 'tags'])
|
||||
metadata_tags = filter_dict(capture_obj.state, ["metadata", "tags"])
|
||||
|
||||
return jsonify(metadata_tags)
|
||||
|
||||
|
|
@ -507,6 +520,6 @@ class TagsAPI(MicroscopeView):
|
|||
for tag in data_dict:
|
||||
capture_obj.delete_tag(str(tag))
|
||||
|
||||
metadata_tags = filter_dict(capture_obj.state, ['metadata', 'tags'])
|
||||
metadata_tags = filter_dict(capture_obj.state, ["metadata", "tags"])
|
||||
|
||||
return jsonify(metadata_tags)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ from flask import jsonify, request
|
|||
|
||||
|
||||
class ZoomAPI(MicroscopeView):
|
||||
|
||||
def get(self):
|
||||
"""
|
||||
Get the current zoom value
|
||||
|
|
@ -17,9 +16,9 @@ class ZoomAPI(MicroscopeView):
|
|||
:<header Content-Type: application/json
|
||||
:status 200: preview started/stopped
|
||||
"""
|
||||
zoom_value = self.microscope.camera.state['zoom_value']
|
||||
zoom_value = self.microscope.camera.state["zoom_value"]
|
||||
|
||||
return jsonify({'zoom_value': zoom_value})
|
||||
return jsonify({"zoom_value": zoom_value})
|
||||
|
||||
def post(self):
|
||||
"""
|
||||
|
|
@ -44,7 +43,7 @@ class ZoomAPI(MicroscopeView):
|
|||
:status 200: preview started/stopped
|
||||
"""
|
||||
payload = JsonResponse(request)
|
||||
zoom_value = payload.param('zoom_value', default=1.0, convert=float)
|
||||
zoom_value = payload.param("zoom_value", default=1.0, convert=float)
|
||||
|
||||
self.microscope.camera.set_zoom(zoom_value)
|
||||
|
||||
|
|
@ -52,7 +51,6 @@ class ZoomAPI(MicroscopeView):
|
|||
|
||||
|
||||
class OverlayAPI(MicroscopeView):
|
||||
|
||||
def get(self):
|
||||
"""
|
||||
Get overlay text
|
||||
|
|
@ -67,7 +65,7 @@ class OverlayAPI(MicroscopeView):
|
|||
text = self.microscope.camera.camera.annotate_text
|
||||
size = self.microscope.camera.camera.annotate_text_size
|
||||
|
||||
return jsonify({'text': text, 'size': size})
|
||||
return jsonify({"text": text, "size": size})
|
||||
|
||||
def post(self):
|
||||
"""
|
||||
|
|
@ -94,10 +92,10 @@ class OverlayAPI(MicroscopeView):
|
|||
"""
|
||||
|
||||
payload = JsonResponse(request)
|
||||
text = payload.param('text', default="", convert=str)
|
||||
size = payload.param('size', default=50, convert=int)
|
||||
text = payload.param("text", default="", convert=str)
|
||||
size = payload.param("size", default=50, convert=int)
|
||||
|
||||
self.microscope.camera.camera.annotate_text = text
|
||||
self.microscope.camera.camera.annotate_text_size = size
|
||||
|
||||
return jsonify({'text': text, 'size': size})
|
||||
return jsonify({"text": text, "size": size})
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import logging
|
|||
|
||||
|
||||
class GPUPreviewAPI(MicroscopeView):
|
||||
|
||||
def post(self, operation):
|
||||
"""
|
||||
Start or stop the onboard GPU preview.
|
||||
|
|
@ -39,7 +38,7 @@ class GPUPreviewAPI(MicroscopeView):
|
|||
if operation == "start":
|
||||
payload = JsonResponse(request)
|
||||
|
||||
window = payload.param('window', default=[])
|
||||
window = payload.param("window", default=[])
|
||||
logging.debug(window)
|
||||
|
||||
if len(window) != 4:
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from openflexure_microscope.api.v1.views import MicroscopeView
|
|||
import logging
|
||||
import warnings
|
||||
|
||||
|
||||
class PluginFormAPI(MicroscopeView):
|
||||
def get(self):
|
||||
"""
|
||||
|
|
@ -26,12 +27,12 @@ class PluginFormAPI(MicroscopeView):
|
|||
|
||||
def construct_blueprint(microscope_obj):
|
||||
|
||||
blueprint = Blueprint('plugin_blueprint', __name__)
|
||||
blueprint = Blueprint("plugin_blueprint", __name__)
|
||||
|
||||
# Create a base route to return plugin API forms, if any exist
|
||||
blueprint.add_url_rule(
|
||||
'/',
|
||||
view_func=PluginFormAPI.as_view('plugin_api_form', microscope=microscope_obj)
|
||||
"/",
|
||||
view_func=PluginFormAPI.as_view("plugin_api_form", microscope=microscope_obj),
|
||||
)
|
||||
|
||||
all_routes = []
|
||||
|
|
@ -40,7 +41,7 @@ def construct_blueprint(microscope_obj):
|
|||
for plugin_name, plugin_obj in microscope_obj.plugin.plugins:
|
||||
|
||||
# If plugin contains valid endpoints
|
||||
if hasattr(plugin_obj, 'api_views') and isinstance(plugin_obj.api_views, dict):
|
||||
if hasattr(plugin_obj, "api_views") and isinstance(plugin_obj.api_views, dict):
|
||||
|
||||
# We'll keep a record of how each route was expanded
|
||||
expanded_routes = {}
|
||||
|
|
@ -50,7 +51,7 @@ def construct_blueprint(microscope_obj):
|
|||
|
||||
# Remove all leading slashes from view route
|
||||
cleaned_route = view_route
|
||||
while cleaned_route[0] == '/':
|
||||
while cleaned_route[0] == "/":
|
||||
cleaned_route = cleaned_route[1:]
|
||||
|
||||
# Construct a full view route from the plugin name
|
||||
|
|
@ -61,12 +62,16 @@ def construct_blueprint(microscope_obj):
|
|||
expanded_routes[view_route] = full_view_route
|
||||
|
||||
# Check if endpoint name clashes
|
||||
if full_view_route not in all_routes and issubclass(view_class, MicroscopeViewPlugin):
|
||||
if full_view_route not in all_routes and issubclass(
|
||||
view_class, MicroscopeViewPlugin
|
||||
):
|
||||
# Add route to main route dictionary
|
||||
all_routes.append(full_view_route)
|
||||
|
||||
# Create a Python-safe name for the route
|
||||
plugin_route_id = 'plugin{}'.format(full_view_route).replace('/', '_')
|
||||
plugin_route_id = "plugin{}".format(full_view_route).replace(
|
||||
"/", "_"
|
||||
)
|
||||
|
||||
# Add route to the plugins blueprint
|
||||
blueprint.add_url_rule(
|
||||
|
|
@ -74,30 +79,37 @@ def construct_blueprint(microscope_obj):
|
|||
view_func=view_class.as_view(
|
||||
plugin_route_id,
|
||||
microscope=microscope_obj,
|
||||
plugin=plugin_obj
|
||||
)
|
||||
plugin=plugin_obj,
|
||||
),
|
||||
)
|
||||
|
||||
else:
|
||||
warnings.warn(
|
||||
"An endpoint /{} has already been loaded. Skipping {}.".format(
|
||||
full_view_route,
|
||||
view_class
|
||||
full_view_route, view_class
|
||||
)
|
||||
)
|
||||
|
||||
# If plugin includes an API form
|
||||
if hasattr(plugin_obj, 'api_form') and isinstance(plugin_obj.api_form, dict):
|
||||
if hasattr(plugin_obj, "api_form") and isinstance(
|
||||
plugin_obj.api_form, dict
|
||||
):
|
||||
api_form_info = plugin_obj.api_form
|
||||
# TODO: Validate form? We need to make sure no single plugin can break all plugins.
|
||||
api_form_info['id'] = plugin_name
|
||||
if 'forms' in api_form_info and isinstance(api_form_info['forms'], list):
|
||||
for form in api_form_info['forms']:
|
||||
if 'route' in form and form['route'] in expanded_routes.keys():
|
||||
form['route'] = expanded_routes[form['route']]
|
||||
api_form_info["id"] = plugin_name
|
||||
if "forms" in api_form_info and isinstance(
|
||||
api_form_info["forms"], list
|
||||
):
|
||||
for form in api_form_info["forms"]:
|
||||
if "route" in form and form["route"] in expanded_routes.keys():
|
||||
form["route"] = expanded_routes[form["route"]]
|
||||
else:
|
||||
logging.warn("No valid expandable route found for {}".format(form['route']))
|
||||
|
||||
logging.warn(
|
||||
"No valid expandable route found for {}".format(
|
||||
form["route"]
|
||||
)
|
||||
)
|
||||
|
||||
# Store the complete form in Microscope().plugin.form
|
||||
microscope_obj.plugin.forms.append(api_form_info)
|
||||
print(microscope_obj.plugin.forms)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import logging
|
|||
|
||||
|
||||
class PositionAPI(MicroscopeView):
|
||||
|
||||
def get(self):
|
||||
"""
|
||||
Return current x, y and z positions of the stage.
|
||||
|
|
@ -42,7 +41,7 @@ class PositionAPI(MicroscopeView):
|
|||
}
|
||||
|
||||
"""
|
||||
out = filter_dict(self.microscope.state, ['stage', 'position'])
|
||||
out = filter_dict(self.microscope.state, ["stage", "position"])
|
||||
return jsonify(out)
|
||||
|
||||
def post(self):
|
||||
|
|
@ -67,15 +66,19 @@ class PositionAPI(MicroscopeView):
|
|||
position = [0, 0, 0]
|
||||
|
||||
# Handle absolute positioning (calculate a relative move from current position and target)
|
||||
if (payload.param('absolute') is True) and (self.microscope.stage): # Only if stage exists
|
||||
target_position = axes_to_array(payload.json, ['x', 'y', 'z'])
|
||||
if (payload.param("absolute") is True) and (
|
||||
self.microscope.stage
|
||||
): # Only if stage exists
|
||||
target_position = axes_to_array(payload.json, ["x", "y", "z"])
|
||||
logging.debug("TARGET: {}".format(target_position))
|
||||
position = [target_position[i] - self.microscope.stage.position[i] for i in range(3)]
|
||||
position = [
|
||||
target_position[i] - self.microscope.stage.position[i] for i in range(3)
|
||||
]
|
||||
logging.debug("DELTA: {}".format(position))
|
||||
|
||||
else:
|
||||
# Get coordinates from payload
|
||||
position = axes_to_array(payload.json, ['x', 'y', 'z'], [0, 0, 0])
|
||||
position = axes_to_array(payload.json, ["x", "y", "z"], [0, 0, 0])
|
||||
|
||||
logging.debug(position)
|
||||
|
||||
|
|
@ -85,18 +88,18 @@ class PositionAPI(MicroscopeView):
|
|||
with self.microscope.stage.lock:
|
||||
self.microscope.stage.move_rel(position)
|
||||
|
||||
out = filter_dict(self.microscope.state, ['stage', 'position'])
|
||||
out = filter_dict(self.microscope.state, ["stage", "position"])
|
||||
|
||||
return jsonify(out)
|
||||
|
||||
|
||||
def construct_blueprint(microscope_obj):
|
||||
|
||||
blueprint = Blueprint('stage_blueprint', __name__)
|
||||
blueprint = Blueprint("stage_blueprint", __name__)
|
||||
|
||||
blueprint.add_url_rule(
|
||||
'/position',
|
||||
view_func=PositionAPI.as_view('position', microscope=microscope_obj)
|
||||
"/position",
|
||||
view_func=PositionAPI.as_view("position", microscope=microscope_obj),
|
||||
)
|
||||
|
||||
return blueprint
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ from flask import jsonify, abort, Blueprint
|
|||
|
||||
|
||||
class TaskListAPI(MicroscopeView):
|
||||
|
||||
def get(self):
|
||||
"""
|
||||
Get list of long-running tasks.
|
||||
|
|
@ -66,7 +65,6 @@ class TaskListAPI(MicroscopeView):
|
|||
|
||||
|
||||
class TaskAPI(MicroscopeView):
|
||||
|
||||
def get(self, task_id):
|
||||
"""
|
||||
Get JSON representation of a task
|
||||
|
|
@ -123,14 +121,11 @@ class TaskAPI(MicroscopeView):
|
|||
success = self.microscope.task.delete(task_id)
|
||||
|
||||
if success:
|
||||
data = {
|
||||
'status': 'success',
|
||||
'message': 'task successfully deleted'
|
||||
}
|
||||
data = {"status": "success", "message": "task successfully deleted"}
|
||||
else:
|
||||
data = {
|
||||
'status': 'fail',
|
||||
'message': 'task could not be deleted, as it is currently running or does not exist'
|
||||
"status": "fail",
|
||||
"message": "task could not be deleted, as it is currently running or does not exist",
|
||||
}
|
||||
|
||||
return jsonify(data)
|
||||
|
|
@ -138,16 +133,14 @@ class TaskAPI(MicroscopeView):
|
|||
|
||||
def construct_blueprint(microscope_obj):
|
||||
|
||||
blueprint = Blueprint('task_blueprint', __name__)
|
||||
blueprint = Blueprint("task_blueprint", __name__)
|
||||
|
||||
blueprint.add_url_rule(
|
||||
'/',
|
||||
view_func=TaskListAPI.as_view('task_list', microscope=microscope_obj)
|
||||
"/", view_func=TaskListAPI.as_view("task_list", microscope=microscope_obj)
|
||||
)
|
||||
|
||||
blueprint.add_url_rule(
|
||||
'/<task_id>/',
|
||||
view_func=TaskAPI.as_view('task', microscope=microscope_obj)
|
||||
"/<task_id>/", view_func=TaskAPI.as_view("task", microscope=microscope_obj)
|
||||
)
|
||||
|
||||
return blueprint
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ class MicroscopeView(MethodView):
|
|||
Create a generic MethodView with a globally available
|
||||
microscope object passed as an argument.
|
||||
"""
|
||||
|
||||
def __init__(self, microscope, **kwargs):
|
||||
|
||||
self.microscope = microscope
|
||||
|
|
@ -19,6 +20,7 @@ class MicroscopeViewPlugin(MicroscopeView):
|
|||
microscope object passed as an argument, and a plugin
|
||||
reference stored in 'self'. Initially None.
|
||||
"""
|
||||
|
||||
def __init__(self, microscope, plugin=None, **kwargs):
|
||||
|
||||
self.plugin = plugin
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue