From 0f4f1170c754bab258a87d7ab88bbda57ee3ac9e Mon Sep 17 00:00:00 2001 From: Aaron Critchley Date: Sat, 9 Feb 2019 01:33:08 +0000 Subject: [PATCH 1/4] Adding implementations and testing for the row ops --- .idea/codeStyles/Project.xml | 5 ++ .../numerical_methods.py | 28 ++++++++ .../test_numerical_methods.py | 67 +++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/numerical_methods.py create mode 100644 architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/test_numerical_methods.py diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 79a710f..d692c34 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -3,5 +3,10 @@ + + \ No newline at end of file diff --git a/architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/numerical_methods.py b/architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/numerical_methods.py new file mode 100644 index 0000000..df27958 --- /dev/null +++ b/architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/numerical_methods.py @@ -0,0 +1,28 @@ +import numpy as np + + +def row_interchange(arr: np.ndarray, index_one: int, index_two: int): + res = arr.copy() + res[[index_one, index_two]] = res[[index_two, index_one]] + return res + + +def scalar_multiplication( + arr: np.ndarray, + mult_index: int, + multiply_by: np.float64 +): + res = arr.copy() + res[mult_index] *= multiply_by + return res + + +def add_scalar_mult( + arr: np.ndarray, + mult_index: int, + multiply_by: np.float64, + add_index: int +): + res = arr.copy() + res[add_index] += res[mult_index] * multiply_by + return res diff --git a/architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/test_numerical_methods.py b/architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/test_numerical_methods.py new file mode 100644 index 0000000..f9bf920 --- /dev/null +++ b/architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/test_numerical_methods.py @@ -0,0 +1,67 @@ +import numpy as np +import pytest +from hypothesis import given +from hypothesis.strategies import integers, tuples, floats +from hypothesis.extra.numpy import arrays +from numerical_methods import row_interchange, scalar_multiplication, add_scalar_mult + + +# For the sake of simplicity, not bothering with nans or infs. +reasonable_floats = floats(min_value=0.001, max_value=10 * 7, allow_nan=False, allow_infinity=False) +reasonable_f_array = arrays( + dtype=np.float64, + shape=tuples(integers(min_value=2, max_value=250), integers(min_value=2, max_value=250)), + elements=reasonable_floats +) + + +@given(reasonable_f_array) +def test_row_interchange(arr): + num_rows = arr.shape[0] + index_one = np.random.randint(0, num_rows) + index_two = np.random.randint(0, num_rows) + + res = row_interchange(arr, index_one, index_two) + + # Confirm the arrays have been changed + np.testing.assert_array_equal(arr[index_one], res[index_two]) + np.testing.assert_array_equal(res[index_one], arr[index_two]) + + # If we swap the rows back, arrays should be perfectly equal + res[[index_one, index_two]] = res[[index_two, index_one]] + np.testing.assert_array_equal(res, arr) + + +@given(reasonable_f_array, reasonable_floats) +def test_scalar_multiplication(arr, multiplier): + row_to_mult = np.random.randint(0, arr.shape[0]) + + res = scalar_multiplication(arr, row_to_mult, multiplier) + + # We would expect that row_to_mult would be arr[row_to_mult] * multiplier + np.testing.assert_array_equal(arr[row_to_mult] * multiplier, res[row_to_mult]) + + # And if we scale it back down, we'd expect the arrays to be the same. + res[row_to_mult] /= multiplier + # almost_equal needed due to potential imprecision on the way back + np.testing.assert_array_almost_equal(arr, res) + + +@given(reasonable_f_array, reasonable_floats) +def test_add_scalar_mult(arr, multiplier): + row_to_mult = np.random.randint(0, arr.shape[0]) + row_to_add = np.random.randint(0, arr.shape[0]) + + res = add_scalar_mult(arr, row_to_mult, multiplier, row_to_add) + + # We would expect that row_to_add would be row_to_add + (row_to_mult * multiplier) + np.testing.assert_array_equal(arr[row_to_add] + (arr[row_to_mult] * multiplier), res[row_to_add]) + + # And if we scale it back down, we'd expect the arrays to be the same. + res[row_to_add] -= arr[row_to_mult] * multiplier + # almost_equal needed due to potential imprecision on the way back + np.testing.assert_array_almost_equal(arr, res) + + +if __name__ == '__main__': + pytest.main([__file__, '-s']) From f0792e38e36a25f02a5666618c0b501e21576204 Mon Sep 17 00:00:00 2001 From: Aaron Critchley Date: Sun, 10 Feb 2019 20:45:18 +0000 Subject: [PATCH 2/4] Adding first two methods and notebook hitting them --- .../Server usage examples.ipynb | 153 ++++++++++++++++++ .../num_method_server.py | 82 ++++++++++ 2 files changed, 235 insertions(+) create mode 100644 architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/Server usage examples.ipynb create mode 100644 architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/num_method_server.py diff --git a/architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/Server usage examples.ipynb b/architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/Server usage examples.ipynb new file mode 100644 index 0000000..62c7d90 --- /dev/null +++ b/architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/Server usage examples.ipynb @@ -0,0 +1,153 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "import requests\n", + "from requests.auth import HTTPBasicAuth\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "request_content = {\n", + " 'index_one': 1,\n", + " 'index_two': 2,\n", + " 'array': [[1, 2, 3], [4, 5, 6], [7, 8, 9]]\n", + "}\n", + "\n", + "resp = requests.get(\n", + " 'http://localhost:5000/row_interchange',\n", + " auth=HTTPBasicAuth('example_user', 'example_password'),\n", + " json=request_content\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1, 2, 3],\n", + " [7, 8, 9],\n", + " [4, 5, 6]])" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.array(resp.json())" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "request_content = {\n", + " 'mult_index': 0,\n", + " 'mult_by': 2.5,\n", + " 'array': [[1, 2, 3], [4, 5, 6], [7, 8, 9]],\n", + " 'api_token': 'thisshouldbeahashedstringwithhighentropy'\n", + "}\n", + "\n", + "resp = requests.get(\n", + " 'http://localhost:5000/scalar_multiplication',\n", + " json=request_content\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[2.5, 5. , 7.5],\n", + " [4. , 5. , 6. ],\n", + " [7. , 8. , 9. ]])" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.array(resp.json())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.1" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/num_method_server.py b/architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/num_method_server.py new file mode 100644 index 0000000..b3089b8 --- /dev/null +++ b/architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/num_method_server.py @@ -0,0 +1,82 @@ +from functools import wraps +import numpy as np +from flask import Flask, request, Response, jsonify +from numerical_methods import row_interchange, scalar_multiplication, add_scalar_mult +app = Flask(__name__) + +creds = {'example_user': 'example_password'} +acceptable_tokens = {'thisshouldbeahashedstringwithhighentropy', } + + +def check_http_basic_auth(username, password): + # Hopefully it goes without saying that we would never + # store these credentials in plain-text in the real world. + if username is None or password is None: + return False + + return creds.get(username) == password + + +def http_login_required(f): + @wraps(f) + def required_login_wrapper(*args, **kwargs): + auth = request.authorization + if check_http_basic_auth(auth.username, auth.password): + return f(*args, **kwargs) + else: + return Response('Failed auth', 403) + + return required_login_wrapper + + +def check_api_token(api_token): + return api_token in acceptable_tokens + + +def api_token_required(f): + @wraps(f) + def required_login_wrapper(*args, **kwargs): + if check_api_token(request.json['api_token']): + return f(*args, **kwargs) + else: + return Response('Failed token check', 403) + return required_login_wrapper + + +def make_array_from_json(js): + array_as_json = js.get('array') + try: + return np.array(array_as_json, dtype=np.float64) + except Exception: # Should be catching a more specific error + return Response('Could not create a NumPy array from request', 400) + + +@app.route('/row_interchange') +@api_token_required +def basic_auth_row_interchange(): + req_content = request.json + np_array = make_array_from_json(req_content) + res = row_interchange(np_array, req_content['index_one'], req_content['index_two']) + return jsonify(res.tolist()) + + +@app.route('/scalar_multiplication') +@api_token_required +def api_token_scalar_multiplication(): + req_content = request.json + np_array = make_array_from_json(req_content) + res = scalar_multiplication(np_array, req_content['mult_index'], req_content['mult_by']) + return jsonify(res.tolist()) + + +@app.route('/add_scalar_mult') +@api_token_required +def oauth_add_scalar_mult(): + req_content = request.json + np_array = make_array_from_json(req_content) + res = scalar_multiplication(np_array, req_content['mult_index'], req_content['mult_by']) + return jsonify(res.tolist()) + + +if __name__ == '__main__': + app.run(debug=True) From c7c30be162bcb6a085751b3e0fbfb30378bb9af7 Mon Sep 17 00:00:00 2001 From: Aaron Critchley Date: Sun, 17 Feb 2019 20:35:39 +0000 Subject: [PATCH 3/4] Tidying up --- .../Server usage examples.ipynb | 20 +++++++++---------- .../num_method_server.py | 11 +--------- .../numerical_methods.py | 0 .../test_numerical_methods.py | 0 4 files changed, 11 insertions(+), 20 deletions(-) rename architecture/2019_01_linear_systems_APIs_and_authentication/{aaroncritchley_solution => examples}/Server usage examples.ipynb (91%) rename architecture/2019_01_linear_systems_APIs_and_authentication/{aaroncritchley_solution => examples}/num_method_server.py (87%) rename architecture/2019_01_linear_systems_APIs_and_authentication/{aaroncritchley_solution => examples}/numerical_methods.py (100%) rename architecture/2019_01_linear_systems_APIs_and_authentication/{aaroncritchley_solution => examples}/test_numerical_methods.py (100%) diff --git a/architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/Server usage examples.ipynb b/architecture/2019_01_linear_systems_APIs_and_authentication/examples/Server usage examples.ipynb similarity index 91% rename from architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/Server usage examples.ipynb rename to architecture/2019_01_linear_systems_APIs_and_authentication/examples/Server usage examples.ipynb index 62c7d90..c79f885 100644 --- a/architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/Server usage examples.ipynb +++ b/architecture/2019_01_linear_systems_APIs_and_authentication/examples/Server usage examples.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 3, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -32,18 +32,18 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "array([[1, 2, 3],\n", - " [7, 8, 9],\n", - " [4, 5, 6]])" + "array([[1., 2., 3.],\n", + " [7., 8., 9.],\n", + " [4., 5., 6.]])" ] }, - "execution_count": 27, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -54,7 +54,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -73,7 +73,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -84,7 +84,7 @@ " [7. , 8. , 9. ]])" ] }, - "execution_count": 32, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } diff --git a/architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/num_method_server.py b/architecture/2019_01_linear_systems_APIs_and_authentication/examples/num_method_server.py similarity index 87% rename from architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/num_method_server.py rename to architecture/2019_01_linear_systems_APIs_and_authentication/examples/num_method_server.py index b3089b8..35e914f 100644 --- a/architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/num_method_server.py +++ b/architecture/2019_01_linear_systems_APIs_and_authentication/examples/num_method_server.py @@ -52,7 +52,7 @@ def make_array_from_json(js): @app.route('/row_interchange') -@api_token_required +@http_login_required def basic_auth_row_interchange(): req_content = request.json np_array = make_array_from_json(req_content) @@ -69,14 +69,5 @@ def api_token_scalar_multiplication(): return jsonify(res.tolist()) -@app.route('/add_scalar_mult') -@api_token_required -def oauth_add_scalar_mult(): - req_content = request.json - np_array = make_array_from_json(req_content) - res = scalar_multiplication(np_array, req_content['mult_index'], req_content['mult_by']) - return jsonify(res.tolist()) - - if __name__ == '__main__': app.run(debug=True) diff --git a/architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/numerical_methods.py b/architecture/2019_01_linear_systems_APIs_and_authentication/examples/numerical_methods.py similarity index 100% rename from architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/numerical_methods.py rename to architecture/2019_01_linear_systems_APIs_and_authentication/examples/numerical_methods.py diff --git a/architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/test_numerical_methods.py b/architecture/2019_01_linear_systems_APIs_and_authentication/examples/test_numerical_methods.py similarity index 100% rename from architecture/2019_01_linear_systems_APIs_and_authentication/aaroncritchley_solution/test_numerical_methods.py rename to architecture/2019_01_linear_systems_APIs_and_authentication/examples/test_numerical_methods.py From 0dd385864430412908ca2511d2415463a5fc6547 Mon Sep 17 00:00:00 2001 From: Aaron Critchley Date: Sun, 17 Feb 2019 20:39:32 +0000 Subject: [PATCH 4/4] Resetting .idea file (sigh) --- .idea/codeStyles/Project.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index d692c34..79a710f 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -3,10 +3,5 @@ - - \ No newline at end of file