summaryrefslogtreecommitdiff
path: root/lib/api
diff options
context:
space:
mode:
Diffstat (limited to 'lib/api')
-rw-r--r--lib/api/__init__.py1
-rw-r--r--lib/api/server.py110
-rw-r--r--lib/api/simulator.py3
-rw-r--r--lib/api/stitch_plan.py17
4 files changed, 131 insertions, 0 deletions
diff --git a/lib/api/__init__.py b/lib/api/__init__.py
new file mode 100644
index 00000000..f93e59a9
--- /dev/null
+++ b/lib/api/__init__.py
@@ -0,0 +1 @@
+from .server import APIServer \ No newline at end of file
diff --git a/lib/api/server.py b/lib/api/server.py
new file mode 100644
index 00000000..c57a1785
--- /dev/null
+++ b/lib/api/server.py
@@ -0,0 +1,110 @@
+import errno
+import logging
+import socket
+from threading import Thread
+import time
+
+from flask import Flask, request, g
+import requests
+
+from ..utils.json import InkStitchJSONEncoder
+from .simulator import simulator
+from .stitch_plan import stitch_plan
+
+
+class APIServer(Thread):
+ def __init__(self, *args, **kwargs):
+ self.extension = args[0]
+ Thread.__init__(self, *args[1:], **kwargs)
+ self.daemon = True
+ self.shutting_down = False
+ self.app = None
+ self.host = None
+ self.port = None
+ self.ready = False
+
+ self.__setup_app()
+
+ def __setup_app(self): # noqa: C901
+ self.app = Flask(__name__)
+ self.app.json_encoder = InkStitchJSONEncoder
+
+ self.app.register_blueprint(simulator, url_prefix="/simulator")
+ self.app.register_blueprint(stitch_plan, url_prefix="/stitch_plan")
+
+ @self.app.before_request
+ def store_extension():
+ # make the InkstitchExtension object available to the view handling
+ # this request
+ g.extension = self.extension
+
+ @self.app.route('/shutdown', methods=['POST'])
+ def shutdown():
+ self.shutting_down = True
+ request.environ.get('werkzeug.server.shutdown')()
+ return "shutting down"
+
+ @self.app.route('/ping')
+ def ping():
+ return "pong"
+
+ def stop(self):
+ # for whatever reason, shutting down only seems possible in
+ # the context of a flask request, so we'll just make one
+ requests.post("http://%s:%s/shutdown" % (self.host, self.port))
+
+ def disable_logging(self):
+ logging.getLogger('werkzeug').setLevel(logging.ERROR)
+
+ def run(self):
+ self.disable_logging()
+
+ self.host = "127.0.0.1"
+ self.port = 5000
+
+ while True:
+ try:
+ self.app.run(self.host, self.port, threaded=True)
+ except socket.error as e:
+ if e.errno == errno.EADDRINUSE:
+ self.port += 1
+ continue
+ else:
+ raise
+ else:
+ break
+
+ def ready_checker(self):
+ """Wait until the server is started.
+
+ Annoyingly, there's no way to get a callback to be run when the Flask
+ server starts. Instead, we'll have to poll.
+ """
+
+ while True:
+ if self.port:
+ try:
+ response = requests.get("http://%s:%s/ping" % (self.host, self.port))
+ if response.status_code == 200:
+ break
+ except socket.error, e:
+ if e.errno == errno.ECONNREFUSED:
+ pass
+ else:
+ raise
+
+ time.sleep(0.1)
+
+ def start_server(self):
+ """Start the API server.
+
+ returns: port (int) -- the port that the server is listening on
+ (on localhost)
+ """
+
+ checker = Thread(target=self.ready_checker)
+ checker.start()
+ self.start()
+ checker.join()
+
+ return self.port
diff --git a/lib/api/simulator.py b/lib/api/simulator.py
new file mode 100644
index 00000000..d11bd308
--- /dev/null
+++ b/lib/api/simulator.py
@@ -0,0 +1,3 @@
+from flask import Blueprint
+
+simulator = Blueprint('simulator', __name__)
diff --git a/lib/api/stitch_plan.py b/lib/api/stitch_plan.py
new file mode 100644
index 00000000..fd6bf9c9
--- /dev/null
+++ b/lib/api/stitch_plan.py
@@ -0,0 +1,17 @@
+from flask import Blueprint, g, jsonify
+
+from ..stitch_plan import patches_to_stitch_plan
+
+
+stitch_plan = Blueprint('stitch_plan', __name__)
+
+
+@stitch_plan.route('')
+def get_stitch_plan():
+ if not g.extension.get_elements():
+ return dict(colors=[], stitch_blocks=[], commands=[])
+
+ patches = g.extension.elements_to_patches(g.extension.elements)
+ stitch_plan = patches_to_stitch_plan(patches)
+
+ return jsonify(stitch_plan)