Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
FROM tiangolo/uwsgi-nginx-flask:python3.7-alpine3.8
FROM python:3.8-alpine

COPY requirements.txt /tmp/
RUN pip install --upgrade pip
RUN pip install --requirement /tmp/requirements.txt

RUN apk --update add openjdk8-jre-base

COPY ./app /app
WORKDIR /app

CMD ["waitress-serve", "--port=80", "--call", "main:app"]
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,20 @@ pyxform-http is a Flask-based web service that uses pyxform to convert a XLSForm
# Run locally
```
pip install --requirement requirements.txt
FLASK_APP=app/main.py FLASK_DEBUG=1 flask run
FLASK_APP=app/main.py:app FLASK_DEBUG=1 flask run
```

# Run in Docker
```
docker build --tag pyxform-http .
docker run --detach --name pyxform-http --publish 5000:5000 pyxform-http
docker run --detach --name pyxform-http --publish 5000:80 pyxform-http
```

# Test forms

A form that converts successfully
A form that converts successfully (with chunked encoding!)
```
curl --request POST --header "X-XlsForm-FormId-Fallback: pyxform-clean" --data-binary @test/pyxform-clean.xlsx http://127.0.0.1:5000/api/v1/convert
curl --request POST --header "X-XlsForm-FormId-Fallback: pyxform-clean" --header 'Transfer-Encoding: chunked' --data-binary @test/pyxform-clean.xlsx http://127.0.0.1:5000/api/v1/convert
```

A form that fails to convert and returns a pyxform error
Expand Down
92 changes: 48 additions & 44 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,50 +5,54 @@
from flask import Flask, jsonify, request, escape
from pyxform import xls2xform

app = Flask(__name__)
logger = logging.getLogger(__name__)


@app.route("/")
def index():
return "Welcome to the pyxform-http! Make a POST request to '/api/v1/convert' to convert an XLSForm to an ODK XForm."


@app.route("/api/v1/convert", methods=["POST"])
def post():

xlsform_formid_fallback = sanitize(request.headers.get("X-XlsForm-FormId-Fallback"))
if xlsform_formid_fallback is None:
xlsform_formid_fallback = "tmp"

with TemporaryDirectory() as temp_dir_name:
try:
with open(
os.path.join(temp_dir_name, xlsform_formid_fallback + ".xml"), "w+"
) as xform, open(
os.path.join(temp_dir_name, xlsform_formid_fallback + ".xlsx"), "wb"
) as xlsform:
xlsform.write(request.get_data())
convert_status = xls2xform.xls2xform_convert(
xlsform_path=str(xlsform.name),
xform_path=str(xform.name),
validate=True,
pretty_print=False,
)

if convert_status:
logger.warning(convert_status)

if os.path.isfile(xform.name):
return response(
status=200, result=xform.read(), warnings=convert_status

def app():
app = Flask(__name__)
logger = logging.getLogger(__name__)

@app.route("/")
def index():
return "Welcome to the pyxform-http! Make a POST request to '/api/v1/convert' to convert an XLSForm to an ODK XForm."

@app.route("/api/v1/convert", methods=["POST"])
def post():

xlsform_formid_fallback = sanitize(
request.headers.get("X-XlsForm-FormId-Fallback")
)
if xlsform_formid_fallback is None:
xlsform_formid_fallback = "tmp"

with TemporaryDirectory() as temp_dir_name:
try:
with open(
os.path.join(temp_dir_name, xlsform_formid_fallback + ".xml"), "w+"
) as xform, open(
os.path.join(temp_dir_name, xlsform_formid_fallback + ".xlsx"), "wb"
) as xlsform:
xlsform.write(request.get_data())
convert_status = xls2xform.xls2xform_convert(
xlsform_path=str(xlsform.name),
xform_path=str(xform.name),
validate=True,
pretty_print=False,
)
else:
return response(error=convert_status)

except Exception as e:
logger.error(e)
return response(error=str(e))
if convert_status:
logger.warning(convert_status)

if os.path.isfile(xform.name):
return response(
status=200, result=xform.read(), warnings=convert_status
)
else:
return response(error=convert_status)

except Exception as e:
logger.error(e)
return response(error=str(e))

return app


def sanitize(string):
Expand All @@ -60,5 +64,5 @@ def response(status=400, result=None, warnings=None, error=None):


if __name__ == "__main__":
# Only for debugging while developing
app.run(host="0.0.0.0", debug=True, port=80)
app = app()
app.run()
3 changes: 0 additions & 3 deletions app/uwsgi.ini

This file was deleted.

3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
Flask==1.1.0
Flask==1.1.1
waitress==1.2.1
pyxform==0.15.1