Commit c995a15f authored by Joel Collins's avatar Joel Collins
Browse files

Restructured entire API into blueprints

parent aede078e
Loading
Loading
Loading
Loading
+82 −151
Original line number Diff line number Diff line
#!/usr/bin/env python
"""
TODO: Implement API route to cleanly shut down server
TODO: Implement plugin API routes somehow
"""

from pprint import pprint
import numpy as np
from importlib import import_module
import time
import datetime
import os

from flask import (
    Flask, render_template, Response,
    redirect, request, jsonify, send_file)

import numpy as np

from openflexure_microscope.camera.pi import StreamingCamera

app = Flask(__name__)
cam = StreamingCamera()


def parse_payload(request):
    """Convert request to JSON. Will eventually handle error-checking."""
    # TODO: Handle invalid JSON payloads
    state = request.get_json()
    return state

    Flask, render_template, Response, url_for,
    redirect, request, jsonify, send_file, abort,
    make_response)

def gen(camera):
    """Video streaming generator function."""
    while True:
        # the obtained frame is a jpeg
        frame = camera.get_frame()
from flask.views import MethodView
from werkzeug.exceptions import default_exceptions

        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
from openflexure_microscope.api.utilities import parse_payload, get_from_payload, gen, get_bool, list_routes

from openflexure_microscope import Microscope, config
from openflexure_microscope.camera.pi import StreamingCamera
from openflexure_stage import OpenFlexureStage

@app.route('/')
def index():
    """Video streaming home page."""
    cam.start_worker()  # Start the stream
    return render_template('index.html')

import atexit
import logging, sys

@app.route('/stream')
def stream():
    """Video streaming route. Put this in the src attribute of an img tag."""
    return Response(
        gen(cam),
        mimetype='multipart/x-mixed-replace; boundary=frame')
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)

# Create a dummy microscope object, with no hardware attachments
api_microscope = Microscope(None, None)
logging.debug("Created an empty microscope in global.")

# TODO: Be able to change the image resolution with an option
@app.route('/capture/', methods=['GET', 'POST', 'PUT'])
def capture():
    """
    POST/PUT: Capture a single image.
    GET: Return capture status, or download latest capture.
    """
    if request.method == 'POST' or request.method == 'PUT':
        state = parse_payload(request)
        print(state)

        # Handle filename argument
        if 'filename' in state:
            filename = state['filename']
        else:
            filename = None
# Generate API URI based on version from filename
def uri(suffix, api_version, base=None):
    if not base:
        base = "/api/{}".format(api_version)
    uri = base + suffix
    logging.debug("Created app route: {}".format(uri))
    return uri

        if 'write_to_file' in state:
            write_to_file = bool(state['write_to_file'])
        else:
            write_to_file = False
# Create flask app
app = Flask(__name__)
app.url_map.strict_slashes = False

        if 'use_video_port' in state:
            use_video_port = bool(state['use_video_port'])
        else:
            use_video_port = False

        if 'resize' in state:
            resize_h = int(state['resize'])
            resize_w = int(resize_h*(4/3))
            resize = (resize_w, resize_h)
        else:
            resize = None
# Make errors more API friendly

        response = cam.capture(
            write_to_file=write_to_file,
            use_video_port=use_video_port,
            filename=filename,
            resize=resize)
def _handle_http_exception(e):
    return make_response(
        jsonify({
            'status_code': e.code,
            'error': e.name,
            'details': e.description
        }),
        e.code)

        return str(response) + "\n"
for code in default_exceptions:
    app.errorhandler(code)(_handle_http_exception)

    else:  # If GET request

        download = request.args.get('download')
# After app starts, but before first request, attach hardware to global microscope
@app.before_first_request
def attach_microscope():
    # Create the microscope object globally (common to all spawned server threads)
    global api_microscope
    logging.debug("First request made. Populating microscope with hardware...")
    openflexurerc = config.load_config()  # Load default user config

        state = cam.state
    api_microscope.attach(
        StreamingCamera(config=openflexurerc),
        OpenFlexureStage("/dev/ttyUSB0")
    )

        if ((
                download == 'true' or
                download == 'True' or
                download == '1') and
                cam.image is not None):
    logging.debug("Microscope successfully attached!")

            return send_file(cam.image.data, mimetype='image/jpeg')
        else:
            return jsonify(state)

##### WEBAPP ROUTES ######

@app.route('/record/', methods=['GET', 'POST', 'PUT'])
def record():
    """
    POST/PUT: Start or stop a video recording.
    GET: Return recording status, or download latest recording
@app.route('/')
def index():
    """
    if request.method == 'POST' or request.method == 'PUT':
        state = request.get_json()
        print(state)

        # Handle filename argument
        if 'filename' in state:
            filename = state['filename']
        else:
            filename = None

        # Handle status argument
        if 'status' in state:
            status = bool(state['status'])

        # synchronise the arduino_time
        if status is True:
            response = cam.start_recording(filename=filename)
            return str(response)

        elif status is False:
            response = cam.stop_recording()
            return str(response)

    else:
        download = request.args.get('download')

        state = cam.state

        if ((
                download == 'true' or
                download == 'True' or
                download == '1') and
                cam.state['recent_video'] is not None):

            return send_file(
                cam.state['recent_video'],
                mimetype='video/H264',
                as_attachment=True)
        else:
            return jsonify(state)


@app.route('/preview/', methods=['GET', 'POST', 'PUT'])
def preview():
    API demo app
    """
    POST/PUT: Start or stop the direct-to-GPU camera preview on the Pi.
    return render_template(
        'index_v1.html'
    )

    GET: Return preview status
    """
    if request.method == 'POST' or request.method == 'PUT':
        state = request.get_json()
        print(state)
##### API ROUTES ######
from openflexure_microscope.api.v1 import blueprints

# Base routes
base_blueprint = blueprints.base.construct_blueprint(api_microscope)
app.register_blueprint(base_blueprint, url_prefix=uri('', 'v1'))

        if 'status' in state:
            status = bool(state['status'])
# Stage routes
stage_blueprint = blueprints.stage.construct_blueprint(api_microscope)
app.register_blueprint(stage_blueprint, url_prefix=uri('/stage', 'v1'))

        if status is False:
            response = cam.stop_preview()
        else:
            response = cam.start_preview()
# Camera routes
camera_blueprint = blueprints.camera.construct_blueprint(api_microscope)
app.register_blueprint(camera_blueprint, url_prefix=uri('/camera', 'v1'))

        return str(response)
# List all routes
list_routes(app)

    else:
        # Get args passed via URL (not useful here. Just for reference.)
        state = cam.state
        return jsonify(state)
# Automatically clean up microscope at exit
def cleanup():
    global api_microscope
    api_microscope.close()

atexit.register(cleanup)

if __name__ == '__main__':
    app.run(host='0.0.0.0', threaded=True, debug=True, use_reloader=False)
if __name__ == "__main__":
    app.run(host='0.0.0.0', port="5000", threaded=True, debug=True, use_reloader=False)
+8 −1
Original line number Diff line number Diff line
import pprint

def parse_payload(request):
    """Convert request to JSON. Will eventually handle error-checking."""
    # TODO: Handle invalid JSON payloads
@@ -32,3 +34,8 @@ def get_bool(get_arg):
        return True
    else:
        return False


def list_routes(app):
    """Print available functions."""
    pprint.pprint(list(map(lambda x: repr(x), app.url_map.iter_rules())))
 No newline at end of file

openflexure_microscope/api/v0.py

deleted100644 → 0
+0 −172
Original line number Diff line number Diff line
#!/usr/bin/env python

from pprint import pprint
from importlib import import_module
import time
import datetime

from flask import (
    Flask, render_template, Response,
    redirect, request, jsonify, send_file)

import numpy as np

from openflexure_microscope.api.utilities import parse_payload, gen
from openflexure_microscope.camera.pi import StreamingCamera

app = Flask(__name__)
cam = StreamingCamera()



@app.route('/')
def index():
    """Video streaming home page."""
    cam.start_worker()  # Start the stream
    return render_template('index_v0.html')


@app.route('/stream')
def stream():
    """Video streaming route. Put this in the src attribute of an img tag."""
    return Response(
        gen(cam),
        mimetype='multipart/x-mixed-replace; boundary=frame')


# TODO: Be able to change the image resolution with an option
@app.route('/capture/', methods=['GET', 'POST', 'PUT'])
def capture():
    """
    POST/PUT: Capture a single image.
    GET: Return capture status, or download latest capture.
    """
    if request.method == 'POST' or request.method == 'PUT':
        state = parse_payload(request)
        print(state)

        # Handle filename argument
        if 'filename' in state:
            filename = state['filename']
        else:
            filename = None

        if 'write_to_file' in state:
            write_to_file = bool(state['write_to_file'])
        else:
            write_to_file = False

        if 'use_video_port' in state:
            use_video_port = bool(state['use_video_port'])
        else:
            use_video_port = False

        if 'resize' in state:
            resize_h = int(state['resize'])
            resize_w = int(resize_h*(4/3))
            resize = (resize_w, resize_h)
        else:
            resize = None

        response = cam.capture(
            write_to_file=write_to_file,
            use_video_port=use_video_port,
            filename=filename,
            resize=resize)

        return str(response) + "\n"

    else:  # If GET request

        download = request.args.get('download')

        state = cam.state

        if ((
                download == 'true' or
                download == 'True' or
                download == '1') and
                cam.image is not None):

            return send_file(cam.image.data, mimetype='image/jpeg')
        else:
            return jsonify(state)


@app.route('/record/', methods=['GET', 'POST', 'PUT'])
def record():
    """
    POST/PUT: Start or stop a video recording.
    GET: Return recording status, or download latest recording
    """
    if request.method == 'POST' or request.method == 'PUT':
        state = request.get_json()
        print(state)

        # Handle filename argument
        if 'filename' in state:
            filename = state['filename']
        else:
            filename = None

        # Handle status argument
        if 'status' in state:
            status = bool(state['status'])

        # synchronise the arduino_time
        if status is True:
            response = cam.start_recording(filename=filename)
            return str(response)

        elif status is False:
            response = cam.stop_recording()
            return str(response)

    else:
        download = request.args.get('download')

        state = cam.state

        if ((
                download == 'true' or
                download == 'True' or
                download == '1') and
                cam.state['recent_video'] is not None):

            return send_file(
                cam.state['recent_video'],
                mimetype='video/H264',
                as_attachment=True)
        else:
            return jsonify(state)


@app.route('/preview/', methods=['GET', 'POST', 'PUT'])
def preview():
    """
    POST/PUT: Start or stop the direct-to-GPU camera preview on the Pi.

    GET: Return preview status
    """
    if request.method == 'POST' or request.method == 'PUT':
        state = request.get_json()
        print(state)

        if 'status' in state:
            status = bool(state['status'])

        if status is False:
            response = cam.stop_preview()
        else:
            response = cam.start_preview()

        return str(response)

    else:
        # Get args passed via URL (not useful here. Just for reference.)
        state = cam.state
        return jsonify(state)


if __name__ == '__main__':
    app.run(host='0.0.0.0', threaded=True, debug=True, use_reloader=False)
+0 −0

Empty file added.

+1 −0
Original line number Diff line number Diff line
from . import camera, stage, base
Loading