From a99e8a5e3f16e84ff43aa6eb75752abed99c8481 Mon Sep 17 00:00:00 2001 From: Akhil Rana Date: Wed, 8 Apr 2020 11:01:55 +0530 Subject: [PATCH 1/5] Route for downloading slide image (#26) Co-authored-by: akhil-rana Co-authored-by: Ryan Birmingham --- SlideServer.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/SlideServer.py b/SlideServer.py index 1c87915..592fa96 100644 --- a/SlideServer.py +++ b/SlideServer.py @@ -150,3 +150,11 @@ def singleThumb(filepath): @app.route("/data/many/", methods=['GET']) def multiSlide(filepathlist): return json.dumps(dev_utils.getMetadataList(json.loads(filepathlist), app.config['UPLOAD_FOLDER'])) + +@app.route("/getSlide/") +def getSlide(image_name): + if(os.path.isfile("/images/"+image_name)): + return flask.send_from_directory(app.config["UPLOAD_FOLDER"], filename=image_name, as_attachment=True) + else: + return flask.Response(json.dumps({"error": "File does not exist"}), status=404) + From 0fd813886a6924ada4f09487eefea4bac00675f9 Mon Sep 17 00:00:00 2001 From: Ryan Birmingham Date: Thu, 9 Apr 2020 00:39:57 -0400 Subject: [PATCH 2/5] support comment, modify objective --- dev_utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dev_utils.py b/dev_utils.py index eddf32b..691d60f 100644 --- a/dev_utils.py +++ b/dev_utils.py @@ -34,8 +34,10 @@ def getMetadata(filename, upload_folder): "openslide.level[0].width", None) metadata['vendor'] = slideData.get(openslide.PROPERTY_NAME_VENDOR, None) metadata['level_count'] = int(slideData.get('level_count', 1)) - metadata['objective'] = float(slideData.get("aperio.AppMag", -1.0)) + metadata['objective'] = float(slideData.get(openslide.PROPERTY_NAME_OBJECTIVE_POWER, 0) or + slideData.get("aperio.AppMag", -1.0)) metadata['md5sum'] = file_md5(filepath) + metadata['comment'] = slideData.get(openslide.PROPERTY_NAME_COMMENT metadata['study'] = "" metadata['specimen'] = "" return metadata From ebb1ce2ee605ec319843f65b38cdf7475354f085 Mon Sep 17 00:00:00 2001 From: Ryan Birmingham Date: Thu, 9 Apr 2020 10:33:32 -0400 Subject: [PATCH 3/5] fix comment fetch --- dev_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev_utils.py b/dev_utils.py index 691d60f..4b47a7f 100644 --- a/dev_utils.py +++ b/dev_utils.py @@ -37,7 +37,7 @@ def getMetadata(filename, upload_folder): metadata['objective'] = float(slideData.get(openslide.PROPERTY_NAME_OBJECTIVE_POWER, 0) or slideData.get("aperio.AppMag", -1.0)) metadata['md5sum'] = file_md5(filepath) - metadata['comment'] = slideData.get(openslide.PROPERTY_NAME_COMMENT + metadata['comment'] = slideData.get(openslide.PROPERTY_NAME_COMMENT, None) metadata['study'] = "" metadata['specimen'] = "" return metadata From 4623d7db41fa82a48d8432a20721d7eaacec3c0d Mon Sep 17 00:00:00 2001 From: Akhil Rana Date: Sun, 12 Apr 2020 19:58:56 +0530 Subject: [PATCH 4/5] 'Upload from URL' routes (#28) * urlUploadRoute_and_urlUploadStatusRoute * fixes * Update SlideServer.py * uploadCheckFIX Co-authored-by: Ryan Birmingham --- SlideServer.py | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/SlideServer.py b/SlideServer.py index 592fa96..91b394b 100644 --- a/SlideServer.py +++ b/SlideServer.py @@ -6,12 +6,13 @@ import string import sys import pyvips - +import urllib import flask import flask_cors import openslide from werkzeug.utils import secure_filename import dev_utils +import requests try: from io import BytesIO @@ -157,4 +158,41 @@ def getSlide(image_name): return flask.send_from_directory(app.config["UPLOAD_FOLDER"], filename=image_name, as_attachment=True) else: return flask.Response(json.dumps({"error": "File does not exist"}), status=404) - + + +# using the token from the start url upload endpoint +@app.route('/urlupload/continue/', methods=['POST']) +def continue_urlfile(token): + token = secure_filename(token) + tmppath = os.path.join(app.config['TEMP_FOLDER'], token) + if os.path.isfile(tmppath): + body = flask.request.get_json() + if not body: + return flask.Response(json.dumps({"error": "Missing JSON body"}), status=400) + if not 'url' in body: + return flask.Response(json.dumps({"error": "File url not present in body"}), status=400) + else: + url = body['url'] + try: + url = urllib.parse.unquote(url) + urllib.request.urlretrieve(url, tmppath) + return flask.Response(json.dumps({"status": "OK Uploaded"}), status=200) + except: + return flask.Response(json.dumps({"error": "URL invalid"}), status=400) + else: + return flask.Response(json.dumps({"error": "Token Not Recognised"}), status=400) + +# Route to check if the URL file has completely uploaded to the server +# Query Params: 'url', 'token' +@app.route('/urlupload/check', methods=['GET']) +def urlUploadStatus(): + url = flask.request.args.get('url') + url = urllib.parse.unquote(url) + token = flask.request.args.get('token') + info = requests.head(url) + urlFileSize = int(info.headers['Content-Length']) + fileSize = os.path.getsize(app.config['TEMP_FOLDER']+'/'+token) + if(fileSize >= urlFileSize): + return flask.Response(json.dumps({"uploaded": "True"}), status=200) + else: + return flask.Response(json.dumps({"uploaded": "False"}), status=200) From 4e25bd8c271cd2e6fda11b9534eb8a453274d0a8 Mon Sep 17 00:00:00 2001 From: Vedant Nandoskar Date: Tue, 14 Apr 2020 07:05:38 +0530 Subject: [PATCH 5/5] Added route for deleting slide from file system (#25) * Added route for deleting slide * removed incorrect comment * Secured delete slide route * Removed auth checker from SlideLoader * removed globals for auth checker Co-Authored-By: Ryan Birmingham * Fixed typo Co-Authored-By: Ryan Birmingham Co-authored-by: Ryan Birmingham --- Dockerfile | 4 ++-- SlideServer.py | 35 ++++++++++++++++++++++++++++++++--- requirements.txt | 2 +- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index b04352d..c8b43a3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,8 +26,8 @@ RUN pip3 install -r requirements.txt EXPOSE 4000 #debug/dev only -#ENV FLASK_APP SlideServer.py -#CMD python -m flask run --host=0.0.0.0 --port=4000 +# ENV FLASK_APP SlideServer.py +# CMD python -m flask run --host=0.0.0.0 --port=4000 #prod only CMD gunicorn -w 4 -b 0.0.0.0:4000 SlideServer:app --timeout 400 diff --git a/SlideServer.py b/SlideServer.py index 91b394b..7b7ebaf 100644 --- a/SlideServer.py +++ b/SlideServer.py @@ -6,6 +6,10 @@ import string import sys import pyvips +from os import listdir +from os.path import isfile, join + + import urllib import flask import flask_cors @@ -14,6 +18,7 @@ import dev_utils import requests + try: from io import BytesIO except ImportError: @@ -28,7 +33,7 @@ app.config['TOKEN_SIZE'] = 10 app.config['SECRET_KEY'] = os.urandom(24) -ALLOWED_EXTENSIONS = set(['svs', 'tif', 'tiff', 'vms', 'vmu', 'ndpi', 'scn', 'mrxs', 'bif', 'svslide']) +ALLOWED_EXTENSIONS = set(['svs', 'tif', 'tiff', 'vms', 'vmu', 'ndpi', 'scn', 'mrxs', 'bif', 'svslide']) def allowed_file(filename): @@ -131,6 +136,29 @@ def finish_upload(token): # get info associated with token # move the file out of temp to upload dir +# Delete the requested slide +@app.route('/slide/delete', methods=['POST']) +def slide_delete(): + body = flask.request.get_json() + + if not body: + return flask.Response(json.dumps({"error": "Missing JSON body"}), status=400) + + filename = body['filename'] + if filename and allowed_file(filename): + filename = secure_filename(filename) + filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) + if os.path.isfile(filepath): + os.remove(os.path.join(app.config['UPLOAD_FOLDER'], filename)) + return flask.Response(json.dumps({"deleted": filename, "success": True})) + else: + return flask.Response(json.dumps({"error": "File with name '" + filename + "' does not exist"}), status=400) + + else: + return flask.Response(json.dumps({"error": "Invalid filename"}), status=400) + + # check for file if it exists or not + # delete the file @app.route("/test", methods=['GET']) def testRoute(): @@ -152,13 +180,13 @@ def singleThumb(filepath): def multiSlide(filepathlist): return json.dumps(dev_utils.getMetadataList(json.loads(filepathlist), app.config['UPLOAD_FOLDER'])) + @app.route("/getSlide/") def getSlide(image_name): if(os.path.isfile("/images/"+image_name)): return flask.send_from_directory(app.config["UPLOAD_FOLDER"], filename=image_name, as_attachment=True) else: - return flask.Response(json.dumps({"error": "File does not exist"}), status=404) - + return flask.Response(json.dumps({"error": "File does not exist"}), status=404) # using the token from the start url upload endpoint @app.route('/urlupload/continue/', methods=['POST']) @@ -196,3 +224,4 @@ def urlUploadStatus(): return flask.Response(json.dumps({"uploaded": "True"}), status=200) else: return flask.Response(json.dumps({"uploaded": "False"}), status=200) + diff --git a/requirements.txt b/requirements.txt index 823b15e..3c43dc0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ openslide-python flask Werkzeug flask-cors -requests \ No newline at end of file +requests