diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000..70e4300 --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,27 @@ +name: Docker Image CI + +on: + push: + branches: [ development ] + pull_request: + branches: [ development ] + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Update the environment files + run: | + echo "MATHPIX_APP_KEY=${{ secrets.MATHPIX_APP_KEY }}" >> mathpix-variables.env + echo "MATHPIX_APP_ID=${{ secrets.MATHPIX_APP_ID }}" >> mathpix-variables.env + - name: Build the Docker image + run: | + docker-compose build + - name: Run the test suite + run: | + docker-compose run flask bash -c "cd /app/flask/flaskapp && python -m pytest" + diff --git a/flask/flaskapp/tests/conftest.py b/flask/flaskapp/tests/conftest.py new file mode 100644 index 0000000..15b5ada --- /dev/null +++ b/flask/flaskapp/tests/conftest.py @@ -0,0 +1,12 @@ +import pytest + +from flaskapp import app as flask_app + +@pytest.fixture +def app(): + yield flask_app + + +@pytest.fixture +def client(app): + return app.test_client() \ No newline at end of file diff --git a/flask/flaskapp/tests/imagetest.txt b/flask/flaskapp/tests/imagetest.txt new file mode 100644 index 0000000..2b2d443 --- /dev/null +++ b/flask/flaskapp/tests/imagetest.txt @@ -0,0 +1 @@ +data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABNYAAAFDCAYAAAD25oPoAAAgAElEQVR4Ae3dMZTURpo4cO3/XbBkR4Yz5qIN7ejs7JxBtJAx0Q6RTcZEw0TgCBN5MsiwI3YjnEG2ZNxFkN1m5jJC3rt3j43O//eNr7Q1QjPdrVZLKumn9+Z1T3dLqvqVZlr69FXV73799ddfKwsBAgQIECBAgAABAgQIECBAgAABAhsJ/L+NPu3DBAgQIECAAAECBAgQIECAAAECBAicCgisORAIECBAgAABAgQIECBAgAABAgQIdBAQWOuAZhUCBAgQIECAAAECBAgQIECAAAECAmuOAQIECBAgQIAAAQIECBAgQIAAAQIdBATWOqBZhQABAgQIECBAgAABAgQIECBAgIDAmmOAAAECBAgQIECAAAECBAgQIECAQAcBgbUOaFYhQIAAAQIECBAgQIAAAQIECBAgILDmGCBAgAABAgQIECBAgAABAgQIECDQQUBgrQOaVQgQIECAAAECBAgQIECAAAECBAgIrDkGCBAgQIAAAQIECBAgQIAAAQIECHQQEFjrgGYVAgQIECBAgAABAgQIECBAgAABAgJrjgECBAgQIECAAAECBAgQIECAAAECHQQE1jqgWYUAAQIECBAgQIAAAQIECBAgQICAwJpjgAABAgQIECBAgAABAgQIECBAgEAHAYG1DmhWIUCAAAECBAgQIECAAAECBAgQICCw5hggQIAAAQIECBAgQIAAAQIECBAg0EFAYK0DmlUIECBAgAABAgQIECBAgAABAgQICKw5BggQIECAAAECBAgQIECAAAECBAh0EBBY64BmFQIECBAgQIAAAQIECBAgQIAAAQICa44BAgQIECBAgAABAgQIECBAgAABAh0EBNY6oFmFAAECBAgQIECAAAECBAgQIECAgMCaY4AAAQIECBAgQIAAAQIECBAgQIBABwGBtQ5oViFAgAABAgQIECBAgAABAgQIECAgsOYYIECAAAECBAgQIECAAAECBAgQINBBQGCtA5pVCBAgQIAAAQIECBAgQIAAAQIECAisOQYIECBAgAABAgQIECBAgAABAgQIdBAQWOuAZhUCBAgQIECAAAECBAgQIECAAAECAmuOAQIECBAgQIAAAQIECBAgQIAAAQIdBATWOqBZhQABAgQIECBAgAABAgQIECBAgIDAmmOAAAECBAgQIECAAAECBAgQIECAQAcBgbUOaFYhQIAAAQIECBAgQIAAAQIECBAgILDmGCBAgAABAgQIECBAgAABAgQIECDQQUBgrQOaVQgQIECAAAECBAgQIECAAAECBAgIrDkGCBAgQIAAAQIECBAgQIAAAQIECHQQEFjrgGYVAgQIECBAgAABAgQIECBAgAABAgJrjgECBAgQIECAAAECBAgQIECAAAECHQQE1jqgWYUAAQIECBAgQIAAAQIECBAgQICAwJpjgAABAgQIECBAgAABAgQIECBAgEAHAYG1DmhWIUCAAAECBAgQIECAAAECBAgQICCw5hggQIAAAQIECBAgQIAAAQIECBAg0EFAYK0DmlUIECBAgAABAgQIECBAgAABAgQICKw5BggQIECAAAECBAgQIECAAAECBAh0EBBY64BmFQIECBAgQIAAAQIECBAgQIAAAQICa44BAgQIECBAgAABAgQIECBAgAABAh0EBNY6oFmFAAECBAgQIECAAAECBAgQIECAgMCaY4AAAQIECBAgQIAAAQIECBAgQIBABwGBtQ5oViFAgAABAgQIECBAgAABAgQIECAgsOYYIECAAAECBAgQIECAAAECBAgQINBBQGCtA5pVCBAgQIAAAQIECBAgQIAAAQIECAisOQYIECBAgAABAgQIECBAgAABAgQIdBAQWOuAZhUCBAgQIECAAAECBAgQIECAAAECAmuOAQIECBAgQIAAAQIECBAgQIAAAQIdBATWOqBZhQABAgQIECBAgAABAgQIECBAgIDAmmOAAAECBAgQIECAAAECBAgQIECAQAcBgbUOaFYhQIAAAQIECBAgQIAAAQIECBAgILDmGCBAgAABAgQIECBAgAABAgQIECDQQUBgrQOaVQgQIECAAAECBAgQIECAAAECBAgIrDkGCBAgQIAAAQIECBAgQIAAAQIECHQQEFjrgGYVAgQIECBAgAABAgQIECBAgAABAgJrjgECBAgQIECAAAECBAgQIECAAAECHQQE1jqgWYUAAQIECBAgQIAAAQIECBAgQICAwJpjgAABAgQIECBAgAABAgQIECBAgEAHAYG1DmhWIUCAAAECBAgQIECAAAECBAgQICCw5hggQIAAAQIECBAgQIAAAQIECBAg0EFAYK0DmlUIECBAgAABAgQIECBAgAABAgQICKw5BggQIECAAAECBAgQIECAAAECBAh0EBBY64BmFQIECBAgQIAAAQIECBAgQIAAAQICa44BAgQIECBAgAABAgQIECBAgAABAh0EBNY6oFmFAAECBAgQIECAAAECBAgQIECAgMCaY4AAAQIECBAgQIAAAQIECBAgQIBABwGBtQ5oViFAgAABAgQIECBAgAABAgQIECAgsOYYIECAAAECBAgQIECAAAECBAgQINBBQGCtA5pVCBAgQIAAAQIECBAgQIAAAQIECAisOQYIECBAgAABAgQIECBAgAABAgQIdBAQWOuAZhUCBAgQIECAAAECBAgQIECAAAECAmuOAQIECBAgQIAAAQIECBAgQIAAAQIdBATWOqBZhQABAgQIECBAgAABAgQIECBAgIDAmmOAAAECBAgQIECAAAECBAgQIECAQAcBgbUOaFYhQIAAgWkKvHz5svr666+rJ0+eTLOASkWAAAECBAgQIECAwKwEBNZm1ZwqQ2A8gRTQ+PHHH8crhD0vXuDOnTvVq1evqnj8/vvvF+8BgAABAgQIECBAgACB3Qr87tdff/11t7uwdQIEliDw2WefVe/fv69+//vfVx8/flxCldVxggKHh4fVycnJackcixNsIEUiQIAAAQIECBAgMDMBgbWZNajqEBhL4He/+129a/H6msKTgQX+/ve/V5cvX67iMZaHDx9W9+7dG7gUdkeAAAECBAgQIECAwFIEdAVdSkurJwECBBYgEFlq3377bV3T7777znhrtYYnBAgQIECAAAECBAj0LSBjrW9R2yOwUAEZawtt+AlWu5m1FkWUuTbBhlIkAgQIECBAgAABAjMQkLE2g0ZUBQJjC8TEBRYCUxGIrLX79++fKU5krplY4wyJXwgQIECAAAECBAgQ6EFAxloPiDZBYOkCaeKC5GCMtSThcUyBZuaayQzGbA37JkCAAAECBAgQIDBPARlr82xXtSIwmEBkq8VsoGnJx7dKr3kkMIZAM3MtAm3ff//9GEWxTwIECBAgQIAAAQIEZiogY22mDataBIYSkK02lLT9dBU4PDysTk5O6tWvXr162lX04OCgfs0TAgQIECBAgAABAgQIdBGQsdZFbcU6kcGzt7dXxWDu+U+89uTJkxVre5tAOQKy1cppqyWXNCYuiOy1tLx79666c+dO+tUjAQIECBAgQIAAAQIEOgsIrHWmO3/FuGCLC7fmki7m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QGy7IkCAAIFpCOzt7bWOpyaoNo32UQoCBAgQIECAAAECpQiYvKCUllJOAgQIEOhN4Jtvvqm3FV0+P378WMWssY8fP65f94QAAQIECBAgQIAAAQKrBGSsrRLyPgECBAgQIECAAAECBAgQIECAAIEWARlrLSheIkCAAAECBAgQIECAAAECBAgQILBKQGBtlZD3CRAgQIAAAQIECBAgQIAAAQIECLQICKy1oHiJAAECBAgQIECAAAECBAgQIECAwCoBgbVVQt4nQIAAAQIECBAgQIAAAQIECBAg0CIgsNaC4iUCBAgQIECAAAECBAgQIECAAAECqwQE1lYJeZ8AAQIECBAgQIAAAQIECBAgQIBAi4DAWguKlwgQIECAAAECBAgQIECAAAECBAisEhBYWyXkfQIECBAgQIAAAQIECBAgQIAAAQItAgJrLSheIkCAAAECBAgQIECAAAECBAgQILBKQGBtlZD3CRAgQIAAAQIECBAgQIAAAQIECLQICKy1oHiJAAECBAgQIECAAAECBAgQIECAwCoBgbVVQt4nQIAAAQIECBAgQIAAAQIECBAg0CIgsNaC4iUCBAgQIECAAAECBAgQIECAAAECqwQE1lYJeZ8AAQIECBAgQIAAAQIECBAgQIBAi4DAWguKlwgQIECAAAECBAgQIECAAAECBAisEhBYWyXkfQIECBAgQIAAAQIECBAgQIAAAQItAgJrLSheIkCAAAECBAgQIECAAAECBAgQILBKQGBtlZD3CRAgQIAAAQIECBAgQIAAAQIECLQICKy1oHiJAAECBAgQIECAAAECBAgQIECAwCoBgbVVQt4nQIAAAQIECBAgQIAAAQIECBAg0CIgsNaC4iUCBAgQIECAAAECBAgQIECAAAECqwQE1lYJeZ8AAQIECBAgQIAAAQIECBAgQIBAi4DAWguKlwgQIECAAAECBAgQIECAAAECBAisEhBYWyXkfQIECBAgQIAAAQIECBAgQIAAAQItAgJrLSheIkCAAAECBAgQIECAAAECBAgQILBKQGBtlZD3CRAgQIAAAQIECBAgQIAAAQIECLQICKy1oHiJAAECBAgQIECAAAECBAgQIECAwCoBgbVVQt4nQIAAAQIECBAgQIAAAQIECBAg0CIgsNaC4iUCBAgQIECAAAECBAgQIECAAAECqwQE1lYJeZ8AAQIECBAgQIAAAQIECBAgQIBAi4DAWguKlwgQIECAAAECBAgQIECAAAECBAisEhBYWyXkfQIECBAgQIAAAQIECBAgQIAAAQItAgJrLSheIkCAAAECBAgQIECAAAECBAgQILBKQGBtlZD3CRAgQIAAAQIECBAgQIAAAQIECLQICKy1oHiJAAECBAgQIECAAAECBAgQIECAwCoBgbVVQt4nQIAAAQIECBAgQIAAAQIECBAg0CIgsNaC4iUCBAgQIECAAAECBAgQIECAAAECqwQE1lYJeZ8AAQIECBAgQIAAAQIECBAgQIBAi4DAWguKlwgQIECAAAECBAgQIECAAAECBAisEhBYWyXkfQIECBAgQIAAAQIECBAgQIAAAQItAgJrLSheIkCAAAECBAgQIECAAAECBAgQILBKQGBtlZD3CRAgQIAAAQIECBAgQIAAAQIECLQICKy1oHiJAAECBAgQIECAAAECBAgQIECAwCoBgbVVQt4nQIAAAQIECBAgQIAAAQIECBAg0CIgsNaC4iUCBAgQIECAAAECBAgQIECAAAECqwQE1lYJeZ8AAQIECBAgQIAAAQIECBAgQIBAi4DAWguKlwgQIECAAAECBAgQIECAAAECBAisEhBYWyXkfQIECBAgQIAAAQIECBAgQIAAAQItAgJrLSheIkCAAAECBAgQIECAAAECBAgQILBKQGBtlZD3CRAgQIAAAQIECBAgQIAAAQIECLQICKy1oHiJAAECBAgQIECAAAECBAgQIECAwCoBgbVVQt4nQIAAAQIECBAgQIAAAQIECBAg0CIgsNaC4iUCBAgQIECAAAECBAgQIECAAAECqwT+P0WT2QTyyiSRAAAAAElFTkSuQmCC \ No newline at end of file diff --git a/flask/flaskapp/tests/test_flaskapp.py b/flask/flaskapp/tests/test_flaskapp.py new file mode 100644 index 0000000..bf508a4 --- /dev/null +++ b/flask/flaskapp/tests/test_flaskapp.py @@ -0,0 +1,72 @@ +import jsonschema +from jsonschema import validate + +def test_home_page(client, app): + """A basic test to ensure the home page is functioning correctly""" + res = client.get('/') + assert b'Hello, World!' == res.data + + +def test_mathpix_ocr(client, app): + with open('tests/imagetest.txt', 'r') as file: + image = file.read().replace('\n', '') + + response_schema = { + "type": "object", + "properties": { + "confidence": {"type": "number"}, + "latex_styled": {"type": "string"}, + "request_id": {"type": "string"}, + }, + } + res = client.post('/mathpix-ocr', json={ + 'b64_img': image + }) + + assert True == validate_json(instance=res.get_json(), schema=response_schema) + + +def test_solve_image(client, app): + with open('tests/imagetest.txt', 'r') as file: + image = file.read().replace('\n', '') + + response_schema = { + "type": "object", + "properties": { + "confidence": {"type": "number"}, + "input_detected": {"type": "string"}, + "solved": {"type": "string"}, + }, + } + + res = client.post('/solve-image', json={ + 'b64_img': image + }) + + assert True == validate_json(instance=res.get_json(), schema=response_schema) + assert r'F(0)=x' == res.get_json()['input_detected'] + assert r'F{\left(0 \right)} = x' == res.get_json()['solved'] + + +def test_solve_latex(client, app): + response_schema = { + "type": "object", + "properties": { + "solved": {"type": "string"}, + }, + } + + res = client.post('/solve-latex', json={ + 'latex': r'F(x)=2+2' + }) + + assert True == validate_json(instance=res.get_json(), schema=response_schema) + assert r'F{\left(x \right)} = 4' == res.get_json()['solved'] + + +def validate_json(instance, schema): + try: + validate(instance, schema) + except jsonschema.exceptions.ValidationError as err: + return False + return True diff --git a/readme.md b/readme.md index 7d2b3d9..9c0f925 100644 --- a/readme.md +++ b/readme.md @@ -37,8 +37,13 @@ Open the integrated terminal to run commands in the container. `$ flask run` will start the flask development server and send requests to the flask application. -## Running Unit Tests -Run pytest in the top-level project directory to start unit tests. +## Running Tests + +To run the tests: + + $ docker-compose build + $ docker-compose run flask bash -c "cd /app/flask/flaskapp && python -m pytest" + ## Public API @@ -102,3 +107,5 @@ Run pytest in the top-level project directory to start unit tests. "text":(str) Doesnt mean much as we are using latex } + + diff --git a/requirements.txt b/requirements.txt index c6a5bed..713c61b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,26 +1,32 @@ +antlr4-python3-runtime==4.8 attrs==20.2.0 +certifi==2020.6.20 +chardet==3.0.4 click==7.1.2 Flask==1.1.2 hypothesis==5.33.0 +idna==2.10 importlib-metadata==1.7.0 iniconfig==1.0.1 itsdangerous==1.1.0 Jinja2==2.11.2 +jsonschema==3.2.0 +latex2sympy==1.0.3 MarkupSafe==1.1.1 more-itertools==8.5.0 +mpmath==1.1.0 packaging==20.4 pathlib2==2.3.5 -pkg-resources==0.0.0 pluggy==0.13.1 py==1.9.0 pyparsing==2.4.7 +pyrsistent==0.17.3 pytest==6.0.1 +requests==2.24.0 six==1.15.0 sortedcontainers==2.2.2 +sympy==1.6.2 toml==0.10.1 +urllib3==1.25.10 Werkzeug==1.0.1 zipp==3.1.0 -requests==2.24.0 -sympy==1.6.2 -latex2sympy==1.0.3 -antlr4-python3-runtime==4.8