Skip to content

Commit

Permalink
Merge pull request #2 from eipm/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
zisimo authored Jan 17, 2019
2 parents 53534ec + f28c961 commit 5bfe242
Show file tree
Hide file tree
Showing 11 changed files with 295 additions and 123 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
data
data
.vscode
62 changes: 33 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,52 @@
flask swagger
flask uploader
flask and nginx
# Stork

# Export Requirements
Classify IVF images in real time.

```bash
pip freeze > requirements.txt
```
[![Python 3.6](https://img.shields.io/badge/python-3.6-blue.svg)](https://www.python.org/downloads/release/python-360/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Production Consideration
Use a production WSGI server instead
![Stork Logo](docs/images/logo.jpg)

## Run Manually
## Stork Requirements

```bash
$ python convert.py ../Images/train process/ 0
$ cd run
$ chmod 777 load_inception_v1.sh
$ cd ..
$ ./run/load_inception_v1.sh
$ python predict.py v1 ../result/ ../../Images/test output.txt 2
```
- Docker. Get it from [here](https://www.docker.com/).
- `process` and `result` folders from ML training (Not included in this repository).

### Run with Docker
## Running Stork using Docker

#### Environment Variables
### Load Environment Variables

```bash
OUTPUT_DIR=~/Documents/2.GitHub/eipm/stork-ui/data/output/
UPLOAD_DIR=~/Documents/2.GitHub/eipm/stork-ui/data/uploads/
PROCESS_DIR=~/Documents/2.GitHub/eipm/stork-ui/data/process/
RESULT_DIR=~/Documents/2.GitHub/eipm/stork-ui/data/result/
DOCKER_CONTAINER_NAME=stork \
STORK_PORT=3000 \
OUTPUT_DIR=/stork/data/output/
UPLOAD_DIR=/stork/data/uploads/
PROCESS_DIR=/stork/data/process/
RESULT_DIR=/stork/data/result/
STORK_TAG=latest
```

-v ${INPUT_DIR}:/input:ro \
### Run Docker Container

```bash
docker run -it --rm --name stork \
-p 3000:80 \
docker run -d --name ${DOCKER_CONTAINER_NAME} \
--restart on-failure:5 \
-p ${STORK_PORT}:80 \
-v ${OUTPUT_DIR}:/output \
-v ${UPLOAD_DIR}:/uploads \
-v ${PROCESS_DIR}:/stork/src/stork_src/process:ro \
-v ${RESULT_DIR}:/stork/src/stork_src/result:ro \
stork:latest /bin/bash
--env USERS_DICT="{ 'eipm': 'stork', 'embryology': 'RyJv3n', 'reviewer': 'e9wR8S' }" \
eipm/stork:${STORK_TAG}
```

python3 ${PREDICT_DIR}/predict.py v1 ${RESULT_DIR} /uploads /output/output_results.txt 2
Where:

- **${DOCKER_CONTAINER_NAME}**: The Stork docker container name.
- **${STORK_PORT}**: The Stork host port.
- **${OUTPUT_DIR}**: Where Stork image classification logs will be written.
- **${UPLOAD_DIR}**: Where Stork image will be saved.
- **${PROCESS_DIR}**: Required directory from ML training.
- **${RESULT_DIR}**: Required directory from ML training.
- **${USERS_DICT}**: The users credentials dictionary to authenticate.
- **${STORK_TAG}**: The stork version to deploy.
Binary file added docs/images/logo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
121 changes: 73 additions & 48 deletions src/api/app.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import os
import random
from flask import Flask, jsonify, flash, request, redirect, url_for,send_from_directory
from flask import Flask, jsonify, flash, request, redirect, url_for,send_from_directory,make_response
from werkzeug.utils import secure_filename
from flask_cors import CORS
import uuid as myuuid
import csv
import ntpath
import datetime
from ast import literal_eval

# Relative Imports
from api.version import api_version
Expand All @@ -25,6 +27,8 @@
app.config['CORS_HEADERS'] = 'Content-Type'
CORS(app)

users_dict = literal_eval(os.environ['USERS_DICT'])

def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
Expand All @@ -45,55 +49,76 @@ def api_swagger():
def healthcheck():
return jsonify({'status':'Healthy', 'version':api_version()})

@app.route('/login', methods = ['GET'])
def serve_login_page():
return send_from_directory(static_file_dir, 'login.html')

@app.route('/api/login', methods = ['POST'])
def login():
username = request.form['username']
password = request.form['password']
if username and users_dict[username] == password:
redirect_to_index = redirect('/')
response = make_response(redirect_to_index)
uuid = str(myuuid.uuid4())
response.set_cookie('stork-auth', uuid, max_age=3600)
return response
return jsonify({}), 401

@app.route('/api/upload', methods = ['POST'])
def upload_image():

if request.method == 'POST':
# check if the post request has the file part
if 'image' not in request.files:
flash('No file part')
return redirect(request.url)

# 1. Create request directory
request_id = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S_%f")
request_dir = (os.path.join(app.config['UPLOAD_DIR'], request_id))
if not os.path.exists(request_dir):
os.makedirs(request_dir)

response_dict = {}
images_dict = {}
# For each uploaded image
for image in request.files.getlist("image"):

# 2. Save Image
filename = secure_filename(image.filename)
image.save(os.path.join(request_dir, filename))

images_dict[filename] = image.filename

# =============#
# Analyze Data #
# =============#

# 3. Specify Output log
output_filename = 'output_' + request_id + '.txt'
output_file = os.path.join(OUTPUT_DIR, output_filename)

# 4. Run Stork
python_command='python3 ' + os.environ['PREDICT_DIR'] + '/predict.py v1 ' + os.environ['RESULT_DIR'] + ' ' + request_dir + ' ' + output_file + ' 2'
os.system(python_command)

# 5. Parse Stork Results
image_results = list(csv.reader(open(output_file, 'r', encoding='utf8'), delimiter='\t'))

# ==================#
# Send JSON Results #
# ==================#

for image_result in image_results:
response_dict[images_dict[ntpath.basename(image_result[0])]] = { 'Good': image_result[1], 'Poor': image_result[2]}

return jsonify(response_dict), 200
auth_token = None
auth_header = request.headers.get('Authorization')
print(auth_header)
if auth_header is None or auth_header.split(" ")[1] is None:
flash('No Authorization header')
return jsonify({}), 401

# check if the post request has the file part
if 'image' not in request.files:
flash('No file part')
return redirect(request.url)

# 1. Create request directory
request_id = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S_%f")
request_dir = (os.path.join(app.config['UPLOAD_DIR'], request_id))
if not os.path.exists(request_dir):
os.makedirs(request_dir)

response_dict = {}
images_dict = {}
# For each uploaded image
for image in request.files.getlist("image"):

# 2. Save Image
filename = secure_filename(image.filename)
image.save(os.path.join(request_dir, filename))

images_dict[filename] = image.filename

# =============#
# Analyze Data #
# =============#

# 3. Specify Output log
output_filename = 'output_' + request_id + '.txt'
output_file = os.path.join(OUTPUT_DIR, output_filename)

# 4. Run Stork
python_command='python3 ' + os.environ['PREDICT_DIR'] + '/predict.py v1 ' + os.environ['RESULT_DIR'] + ' ' + request_dir + ' ' + output_file + ' 2'
os.system(python_command)

# 5. Parse Stork Results
image_results = list(csv.reader(open(output_file, 'r', encoding='utf8'), delimiter='\t'))

# ==================#
# Send JSON Results #
# ==================#

for image_result in image_results:
response_dict[images_dict[ntpath.basename(image_result[0])]] = { 'Good': image_result[1], 'Poor': image_result[2]}

return jsonify(response_dict), 200

if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)
Binary file added src/static/assets/LOGO_ENGLANDER_2LINE_RGB.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions src/static/assets/materialize.min.css

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions src/static/assets/stork.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,27 @@ body {
margin:20px;
}

footer {
position: absolute;
right: 0;
left: 0;
bottom: 0;
text-align: center;
background-color: whitesmoke;
background-origin: padding-box;
display: block;
font-size: 12px;
min-height: 125px;
margin-top: 40px;
padding: 12px;
align-items: center;
}

.footer-logo {
margin: 10px;
height: 40px;
}

.btn {
background-color: #3498db;
}
Expand All @@ -25,6 +46,7 @@ body {

.center {
text-align: center;
margin: auto;
}

.loader {
Expand Down Expand Up @@ -53,6 +75,17 @@ body {
100% { transform: rotate(360deg); }
}

.login-container {
background-color: whitesmoke;
padding: 40px;
margin-top: 100px;
max-width: 300px
}

.login-title {
margin-bottom: 30px;
}

#imageCards-placeholder {
background-color: whitesmoke;
padding: 5px;
Expand Down
Loading

0 comments on commit 5bfe242

Please sign in to comment.