From 74dcdc684036268f53b40e51a9e8fa85fcba9163 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 11 May 2018 17:55:46 +0200 Subject: [PATCH] API & Client: now activity w/ gpx can be modified + fix (source and tests) --- mpwo_api/mpwo_api/activities/activities.py | 18 +- mpwo_api/mpwo_api/activities/models.py | 3 +- mpwo_api/mpwo_api/activities/utils.py | 31 +- mpwo_api/mpwo_api/tests/test_activities.py | 977 ------------------ .../mpwo_api/tests/test_activities_0_get.py | 260 +++++ .../mpwo_api/tests/test_activities_1_post.py | 419 ++++++++ .../mpwo_api/tests/test_activities_2_patch.py | 392 +++++++ .../tests/test_activities_3_delete.py | 153 +++ mpwo_api/mpwo_api/tests/test_sports.py | 9 +- mpwo_api/mpwo_api/tests/utils.py | 3 +- mpwo_api/server.py | 4 + .../Activities/ActivityAddOrEdit.jsx | 8 +- .../components/Activities/ActivityDisplay.jsx | 14 +- .../Activities/ActivityForms/FormWithGpx.jsx | 50 +- 14 files changed, 1298 insertions(+), 1043 deletions(-) delete mode 100644 mpwo_api/mpwo_api/tests/test_activities.py create mode 100644 mpwo_api/mpwo_api/tests/test_activities_0_get.py create mode 100644 mpwo_api/mpwo_api/tests/test_activities_1_post.py create mode 100644 mpwo_api/mpwo_api/tests/test_activities_2_patch.py create mode 100644 mpwo_api/mpwo_api/tests/test_activities_3_delete.py diff --git a/mpwo_api/mpwo_api/activities/activities.py b/mpwo_api/mpwo_api/activities/activities.py index 7f8536fc..c22a707f 100644 --- a/mpwo_api/mpwo_api/activities/activities.py +++ b/mpwo_api/mpwo_api/activities/activities.py @@ -8,7 +8,7 @@ from sqlalchemy import exc from ..users.utils import authenticate, verify_extension from .models import Activity, Sport from .utils import ( - create_activity, edit_activity_wo_gpx, get_file_path, get_gpx_info, + create_activity, edit_activity, get_file_path, get_gpx_info, get_new_file_path ) @@ -225,10 +225,7 @@ def post_activity_no_gpx(auth_user_id): def update_activity(auth_user_id, activity_id): """Update an activity""" activity_data = request.get_json() - if not activity_data or activity_data.get('sport_id') is None \ - or activity_data.get('duration') is None \ - or activity_data.get('distance') is None \ - or activity_data.get('activity_date') is None: + if not activity_data: response_object = { 'status': 'error', 'message': 'Invalid payload.' @@ -238,16 +235,7 @@ def update_activity(auth_user_id, activity_id): try: activity = Activity.query.filter_by(id=activity_id).first() if activity: - if activity.gpx: - response_object = { - 'status': 'error', - 'message': 'You can not modify an activity with gpx file. ' - 'Please delete and re-import the gpx file.' - } - code = 500 - return jsonify(response_object), code - - activity = edit_activity_wo_gpx(activity, activity_data) + activity = edit_activity(activity, activity_data) db.session.commit() response_object = { 'status': 'success', diff --git a/mpwo_api/mpwo_api/activities/models.py b/mpwo_api/mpwo_api/activities/models.py index e12bebd3..69246168 100644 --- a/mpwo_api/mpwo_api/activities/models.py +++ b/mpwo_api/mpwo_api/activities/models.py @@ -60,10 +60,11 @@ class Activity(db.Model): return str(self.sports.label) + \ " - " + self.activity_date.strftime('%Y-%m-%d') - def __init__(self, user_id, sport_id, activity_date, duration): + def __init__(self, user_id, sport_id, activity_date, distance, duration): self.user_id = user_id self.sport_id = sport_id self.activity_date = activity_date + self.distance = distance self.duration = duration def serialize(self): diff --git a/mpwo_api/mpwo_api/activities/utils.py b/mpwo_api/mpwo_api/activities/utils.py index 27c29c19..30175440 100644 --- a/mpwo_api/mpwo_api/activities/utils.py +++ b/mpwo_api/mpwo_api/activities/utils.py @@ -16,11 +16,14 @@ def create_activity( activity_data.get('activity_date'), '%Y-%m-%d %H:%M') duration = timedelta(seconds=gpx_data['duration']) if gpx_data \ else timedelta(seconds=activity_data.get('duration')) + distance = gpx_data['distance'] if gpx_data \ + else activity_data.get('distance') new_activity = Activity( user_id=auth_user_id, sport_id=activity_data.get('sport_id'), activity_date=activity_date, + distance=distance, duration=duration ) @@ -28,7 +31,6 @@ def create_activity( new_activity.gpx = gpx_data['filename'] new_activity.pauses = timedelta(seconds=gpx_data['stop_time']) new_activity.moving = timedelta(seconds=gpx_data['moving_time']) - new_activity.distance = gpx_data['distance'] new_activity.min_alt = gpx_data['elevation_min'] new_activity.max_alt = gpx_data['elevation_max'] new_activity.descent = gpx_data['downhill'] @@ -37,23 +39,26 @@ def create_activity( new_activity.ave_speed = gpx_data['average_speed'] else: new_activity.moving = duration - new_activity.distance = activity_data.get('distance') new_activity.ave_speed = new_activity.distance / ( duration.seconds / 3600) new_activity.max_speed = new_activity.ave_speed return new_activity -def edit_activity_wo_gpx(activity, activity_data): +def edit_activity(activity, activity_data): activity.sport_id = activity_data.get('sport_id') - activity.activity_date = datetime.strptime( - activity_data.get('activity_date'), '%Y-%m-%d %H:%M') - activity.duration = timedelta(seconds=activity_data.get('duration')) - activity.moving = activity.duration - activity.distance = activity_data.get('distance') - activity.ave_speed = activity.distance / ( - activity.duration.seconds / 3600) - activity.max_speed = activity.ave_speed + if not activity.gpx: + if activity_data.get('activity_date'): + activity.activity_date = datetime.strptime( + activity_data.get('activity_date'), '%Y-%m-%d %H:%M') + if activity_data.get('duration'): + activity.duration = timedelta(seconds=activity_data.get('duration')) # noqa + activity.moving = activity.duration + if activity_data.get('distance'): + activity.distance = activity_data.get('distance') + activity.ave_speed = activity.distance / ( + activity.duration.seconds / 3600) + activity.max_speed = activity.ave_speed return activity @@ -121,7 +126,7 @@ def get_file_path(auth_user_id, activity_file): def get_new_file_path(auth_user_id, activity_date, activity_file, sport): old_filename = secure_filename(activity_file.filename) extension = '.{}'.format(old_filename.rsplit('.', 1)[1].lower()) - fi, new_filename = tempfile.mkstemp( + _, new_filename = tempfile.mkstemp( prefix='{}_{}_'.format(activity_date, sport), suffix=extension ) @@ -129,5 +134,5 @@ def get_new_file_path(auth_user_id, activity_date, activity_file, sport): current_app.config['UPLOAD_FOLDER'], 'activities', str(auth_user_id)) if not os.path.exists(dir_path): os.makedirs(dir_path) - file_path = os.path.join(dir_path, new_filename.replace('/tmp/', '')) + file_path = os.path.join(dir_path, new_filename.replace('/tmp/', '')) # noqa return file_path diff --git a/mpwo_api/mpwo_api/tests/test_activities.py b/mpwo_api/mpwo_api/tests/test_activities.py deleted file mode 100644 index 1a4f1097..00000000 --- a/mpwo_api/mpwo_api/tests/test_activities.py +++ /dev/null @@ -1,977 +0,0 @@ -import datetime -import json -import os -from io import BytesIO - -from mpwo_api.tests.utils import ( - add_activity, add_sport, add_user, get_gpx_filepath -) -from mpwo_api.tests.utils_gpx import gpx_file - - -def test_get_all_activities_for_authenticated_user(app): - add_user('test', 'test@test.com', '12345678') - add_user('toto', 'toto@toto.com', '12345678') - add_sport('cycling') - add_sport('running') - add_activity( - 1, - 2, - datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), - datetime.timedelta(seconds=1024)) - add_activity( - 2, - 1, - datetime.datetime.strptime('23/01/2018', '%d/%m/%Y'), - datetime.timedelta(seconds=3600)) - add_activity( - 1, - 1, - datetime.datetime.strptime('01/04/2018', '%d/%m/%Y'), - datetime.timedelta(seconds=6000)) - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = client.get( - '/api/activities', - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - - assert response.status_code == 200 - assert 'success' in data['status'] - assert len(data['data']['activities']) == 2 - - assert 'creation_date' in data['data']['activities'][0] - assert 'Sun, 01 Apr 2018 00:00:00 GMT' == data['data']['activities'][0]['activity_date'] # noqa - assert 1 == data['data']['activities'][0]['user_id'] - assert 1 == data['data']['activities'][0]['sport_id'] - assert '1:40:00' == data['data']['activities'][0]['duration'] - assert data['data']['activities'][0]['with_gpx'] is False - - assert 'creation_date' in data['data']['activities'][1] - assert 'Mon, 01 Jan 2018 00:00:00 GMT' == data['data']['activities'][1]['activity_date'] # noqa - assert 1 == data['data']['activities'][1]['user_id'] - assert 2 == data['data']['activities'][1]['sport_id'] - assert '0:17:04' == data['data']['activities'][1]['duration'] - assert data['data']['activities'][1]['with_gpx'] is False - - -def test_get_activities_for_authenticated_user_no_activity(app): - add_user('test', 'test@test.com', '12345678') - add_user('toto', 'toto@toto.com', '12345678') - add_sport('cycling') - add_sport('running') - add_activity( - 1, - 2, - datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), - datetime.timedelta(seconds=1024)) - add_activity( - 1, - 1, - datetime.datetime.strptime('01/04/2018', '%d/%m/%Y'), - datetime.timedelta(seconds=6000)) - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='toto@toto.com', - password='12345678' - )), - content_type='application/json' - ) - response = client.get( - '/api/activities', - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - - assert response.status_code == 200 - assert 'success' in data['status'] - assert len(data['data']['activities']) == 0 - - -def test_get_activities_for_authenticated_user_no_authentication(app): - client = app.test_client() - response = client.get('/api/activities') - data = json.loads(response.data.decode()) - - assert response.status_code == 403 - assert 'error' in data['status'] - assert 'Provide a valid auth token.' in data['message'] - - -def test_get_activities_pagination(app): - add_user('test', 'test@test.com', '12345678') - add_sport('cycling') - add_activity( - 1, - 1, - datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), - datetime.timedelta(seconds=1024)) - add_activity( - 1, - 1, - datetime.datetime.strptime('01/04/2018', '%d/%m/%Y'), - datetime.timedelta(seconds=6000)) - add_activity( - 1, - 1, - datetime.datetime.strptime('20/03/2017', '%d/%m/%Y'), - datetime.timedelta(seconds=1024)) - add_activity( - 1, - 1, - datetime.datetime.strptime('09/05/2018', '%d/%m/%Y'), - datetime.timedelta(seconds=3000)) - add_activity( - 1, - 1, - datetime.datetime.strptime('01/06/2017', '%d/%m/%Y'), - datetime.timedelta(seconds=3456)) - add_activity( - 1, - 1, - datetime.datetime.strptime('23/02/2018', '%d/%m/%Y'), - datetime.timedelta(seconds=600)) - add_activity( - 1, - 1, - datetime.datetime.strptime('23/02/2018', '%d/%m/%Y'), - datetime.timedelta(seconds=1000)) - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = client.get( - '/api/activities', - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - - assert response.status_code == 200 - assert 'success' in data['status'] - assert len(data['data']['activities']) == 5 - assert 'creation_date' in data['data']['activities'][0] - assert 'Wed, 09 May 2018 00:00:00 GMT' == data['data']['activities'][0]['activity_date'] # noqa - assert '0:50:00' == data['data']['activities'][0]['duration'] - assert 'creation_date' in data['data']['activities'][4] - assert 'Mon, 01 Jan 2018 00:00:00 GMT' == data['data']['activities'][4]['activity_date'] # noqa - assert '0:17:04' == data['data']['activities'][4]['duration'] - - response = client.get( - '/api/activities?page=1', - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - - assert response.status_code == 200 - assert 'success' in data['status'] - assert len(data['data']['activities']) == 5 - assert 'creation_date' in data['data']['activities'][0] - assert 'Wed, 09 May 2018 00:00:00 GMT' == data['data']['activities'][0]['activity_date'] # noqa - assert '0:50:00' == data['data']['activities'][0]['duration'] - assert 'creation_date' in data['data']['activities'][4] - assert 'Mon, 01 Jan 2018 00:00:00 GMT' == data['data']['activities'][4]['activity_date'] # noqa - assert '0:17:04' == data['data']['activities'][4]['duration'] - - response = client.get( - '/api/activities?page=2', - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - - assert response.status_code == 200 - assert 'success' in data['status'] - assert len(data['data']['activities']) == 2 - assert 'creation_date' in data['data']['activities'][0] - assert 'Thu, 01 Jun 2017 00:00:00 GMT' == data['data']['activities'][0]['activity_date'] # noqa - assert '0:57:36' == data['data']['activities'][0]['duration'] - assert 'creation_date' in data['data']['activities'][1] - assert 'Mon, 20 Mar 2017 00:00:00 GMT' == data['data']['activities'][1]['activity_date'] # noqa - assert '0:17:04' == data['data']['activities'][1]['duration'] - - response = client.get( - '/api/activities?page=3', - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - - assert response.status_code == 200 - assert 'success' in data['status'] - assert len(data['data']['activities']) == 0 - - -def test_add_an_activity_gpx(app): - add_user('test', 'test@test.com', '12345678') - add_sport('cycling') - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = client.post( - '/api/activities', - data=dict( - file=(BytesIO(str.encode(gpx_file)), 'example.gpx'), - data='{"sport_id": 1}' - ), - headers=dict( - content_type='multipart/form-data', - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - - assert response.status_code == 201 - assert 'created' in data['status'] - assert len(data['data']['activities']) == 1 - assert 'creation_date' in data['data']['activities'][0] - assert 'Tue, 13 Mar 2018 12:44:45 GMT' == data['data']['activities'][0]['activity_date'] # noqa - assert 1 == data['data']['activities'][0]['user_id'] - assert 1 == data['data']['activities'][0]['sport_id'] - assert '0:04:10' == data['data']['activities'][0]['duration'] - assert data['data']['activities'][0]['ascent'] == 0.4 - assert data['data']['activities'][0]['ave_speed'] == 4.6 - assert data['data']['activities'][0]['descent'] == 23.4 - assert data['data']['activities'][0]['distance'] == 0.32 - assert data['data']['activities'][0]['max_alt'] == 998.0 - assert data['data']['activities'][0]['max_speed'] == 5.09 - assert data['data']['activities'][0]['min_alt'] == 975.0 - assert data['data']['activities'][0]['moving'] == '0:04:10' - assert data['data']['activities'][0]['pauses'] is None - assert data['data']['activities'][0]['with_gpx'] is True - - -def test_add_an_activity_gpx_invalid_file(app): - add_user('test', 'test@test.com', '12345678') - add_sport('cycling') - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = client.post( - '/api/activities', - data=dict( - file=(BytesIO(str.encode(gpx_file)), 'example.png'), - data='{"sport_id": 1}' - ), - headers=dict( - content_type='multipart/form-data', - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - - assert response.status_code == 400 - assert data['status'] == 'fail' - assert data['message'] == 'File extension not allowed.' - - -def test_add_an_activity_gpx_no_sport_id(app): - add_user('test', 'test@test.com', '12345678') - add_sport('cycling') - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = client.post( - '/api/activities', - data=dict( - file=(BytesIO(str.encode(gpx_file)), 'example.gpx'), - data='{}' - ), - headers=dict( - content_type='multipart/form-data', - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - - assert response.status_code == 400 - assert data['status'] == 'error' - assert data['message'] == 'Invalid payload.' - - -def test_add_an_activity_gpx_no_file(app): - add_user('test', 'test@test.com', '12345678') - add_sport('cycling') - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = client.post( - '/api/activities', - data=dict( - data='{}' - ), - headers=dict( - content_type='multipart/form-data', - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - - assert response.status_code == 400 - assert data['status'] == 'fail' - assert data['message'] == 'No file part.' - - -def test_add_an_activity_no_gpx(app): - add_user('test', 'test@test.com', '12345678') - add_sport('cycling') - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = client.post( - '/api/activities/no_gpx', - content_type='application/json', - data=json.dumps(dict( - sport_id=1, - duration=3600, - activity_date='2018-05-15 14:05', - distance=10 - )), - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - - assert response.status_code == 201 - assert 'created' in data['status'] - assert len(data['data']['activities']) == 1 - assert 'creation_date' in data['data']['activities'][0] - assert data['data']['activities'][0]['activity_date'] == 'Tue, 15 May 2018 14:05:00 GMT' # noqa - assert data['data']['activities'][0]['user_id'] == 1 - assert data['data']['activities'][0]['sport_id'] == 1 - assert data['data']['activities'][0]['duration'] == '1:00:00' - assert data['data']['activities'][0]['ascent'] is None - assert data['data']['activities'][0]['ave_speed'] == 10.0 - assert data['data']['activities'][0]['descent'] is None - assert data['data']['activities'][0]['distance'] == 10.0 - assert data['data']['activities'][0]['max_alt'] is None - assert data['data']['activities'][0]['max_speed'] == 10.0 - assert data['data']['activities'][0]['min_alt'] is None - assert data['data']['activities'][0]['moving'] == '1:00:00' - assert data['data']['activities'][0]['pauses'] is None - assert data['data']['activities'][0]['with_gpx'] is False - - -def test_add_an_activity_no_gpx_invalid_payload(app): - add_user('test', 'test@test.com', '12345678') - add_sport('cycling') - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = client.post( - '/api/activities/no_gpx', - content_type='application/json', - data=json.dumps(dict( - sport_id=1, - duration=3600, - distance=10 - )), - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - - assert response.status_code == 400 - assert 'error' in data['status'] - assert 'Invalid payload.' in data['message'] - - -def test_add_an_activity_no_gpx_error(app): - add_user('test', 'test@test.com', '12345678') - add_sport('cycling') - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = client.post( - '/api/activities/no_gpx', - content_type='application/json', - data=json.dumps(dict( - sport_id=1, - duration=3600, - activity_date='15/2018', - distance=10 - )), - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - - assert response.status_code == 500 - assert 'fail' in data['status'] - assert 'Error during activity save.' in data['message'] - - -def test_get_an_activity_without_gpx(app): - add_user('test', 'test@test.com', '12345678') - add_sport('cycling') - add_activity( - 1, - 1, - datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), - datetime.timedelta(seconds=1024)) - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = client.get( - '/api/activities/1', - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - - assert response.status_code == 200 - assert 'success' in data['status'] - assert len(data['data']['activities']) == 1 - assert 'creation_date' in data['data']['activities'][0] - assert 'Mon, 01 Jan 2018 00:00:00 GMT' == data['data']['activities'][0]['activity_date'] # noqa - assert 1 == data['data']['activities'][0]['user_id'] - assert 1 == data['data']['activities'][0]['sport_id'] - assert '0:17:04' == data['data']['activities'][0]['duration'] - assert data['data']['activities'][0]['ascent'] is None - assert data['data']['activities'][0]['ave_speed'] is None - assert data['data']['activities'][0]['descent'] is None - assert data['data']['activities'][0]['distance'] is None - assert data['data']['activities'][0]['max_alt'] is None - assert data['data']['activities'][0]['max_speed'] is None - assert data['data']['activities'][0]['min_alt'] is None - assert data['data']['activities'][0]['moving'] is None - assert data['data']['activities'][0]['pauses'] is None - assert data['data']['activities'][0]['with_gpx'] is False - - -def test_get_an_activity_with_gpx(app): - add_user('test', 'test@test.com', '12345678') - add_sport('cycling') - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - client.post( - '/api/activities', - data=dict( - file=(BytesIO(str.encode(gpx_file)), 'example.gpx'), - data='{"sport_id": 1}' - ), - headers=dict( - content_type='multipart/form-data', - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - response = client.get( - '/api/activities/1', - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - - data = json.loads(response.data.decode()) - - assert response.status_code == 200 - assert 'success' in data['status'] - assert len(data['data']['activities']) == 1 - assert 'creation_date' in data['data']['activities'][0] - assert 'Tue, 13 Mar 2018 12:44:45 GMT' == data['data']['activities'][0]['activity_date'] # noqa - assert 1 == data['data']['activities'][0]['user_id'] - assert 1 == data['data']['activities'][0]['sport_id'] - assert '0:04:10' == data['data']['activities'][0]['duration'] - assert data['data']['activities'][0]['ascent'] == 0.4 - assert data['data']['activities'][0]['ave_speed'] == 4.6 - assert data['data']['activities'][0]['descent'] == 23.4 - assert data['data']['activities'][0]['distance'] == 0.32 - assert data['data']['activities'][0]['max_alt'] == 998.0 - assert data['data']['activities'][0]['max_speed'] == 5.09 - assert data['data']['activities'][0]['min_alt'] == 975.0 - assert data['data']['activities'][0]['moving'] == '0:04:10' - assert data['data']['activities'][0]['pauses'] is None - assert data['data']['activities'][0]['with_gpx'] is True - - -def test_edit_an_activity_wo_gpx(app): - add_user('test', 'test@test.com', '12345678') - add_sport('cycling') - add_activity( - 1, - 1, - datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), - datetime.timedelta(seconds=1024)) - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = client.patch( - '/api/activities/1', - content_type='application/json', - data=json.dumps(dict( - sport_id=1, - duration=3600, - activity_date='2018-05-15 14:05', - distance=10 - )), - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - - data = json.loads(response.data.decode()) - - assert response.status_code == 200 - assert 'success' in data['status'] - assert len(data['data']['activities']) == 1 - assert 'creation_date' in data['data']['activities'][0] - assert data['data']['activities'][0]['activity_date'] == 'Tue, 15 May 2018 14:05:00 GMT' # noqa - assert data['data']['activities'][0]['user_id'] == 1 - assert data['data']['activities'][0]['sport_id'] == 1 - assert data['data']['activities'][0]['duration'] == '1:00:00' - assert data['data']['activities'][0]['ascent'] is None - assert data['data']['activities'][0]['ave_speed'] == 10.0 - assert data['data']['activities'][0]['descent'] is None - assert data['data']['activities'][0]['distance'] == 10.0 - assert data['data']['activities'][0]['max_alt'] is None - assert data['data']['activities'][0]['max_speed'] == 10.0 - assert data['data']['activities'][0]['min_alt'] is None - assert data['data']['activities'][0]['moving'] == '1:00:00' - assert data['data']['activities'][0]['pauses'] is None - assert data['data']['activities'][0]['with_gpx'] is False - - -def test_edit_an_activity_with_gpx(app): - add_user('test', 'test@test.com', '12345678') - add_sport('cycling') - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - client.post( - '/api/activities', - data=dict( - file=(BytesIO(str.encode(gpx_file)), 'example.gpx'), - data='{"sport_id": 1}' - ), - headers=dict( - content_type='multipart/form-data', - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - response = client.patch( - '/api/activities/1', - content_type='application/json', - data=json.dumps(dict( - sport_id=1, - duration=3600, - activity_date='2018-05-15 14:05', - distance=10 - )), - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - - data = json.loads(response.data.decode()) - - assert response.status_code == 500 - assert 'error' in data['status'] - assert ('You can not modify an activity with gpx file. ' - 'Please delete and re-import the gpx file.') in data['message'] - - -def test_edit_an_activity_wo_gpx_invalid_payload(app): - add_user('test', 'test@test.com', '12345678') - add_sport('cycling') - add_activity( - 1, - 1, - datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), - datetime.timedelta(seconds=1024)) - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = client.patch( - '/api/activities/1', - content_type='application/json', - data=json.dumps(dict( - sport_id=1, - activity_date='2018-05-15 14:05', - distance=10 - )), - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - - data = json.loads(response.data.decode()) - - assert response.status_code == 400 - assert 'error' in data['status'] - assert 'Invalid payload.' in data['message'] - - -def test_edit_an_activity_wo_gpx_incorrect_data(app): - add_user('test', 'test@test.com', '12345678') - add_sport('cycling') - add_activity( - 1, - 1, - datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), - datetime.timedelta(seconds=1024)) - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = client.patch( - '/api/activities/1', - content_type='application/json', - data=json.dumps(dict( - sport_id=1, - duration=3600, - activity_date='15/2018', - distance=10 - )), - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - - data = json.loads(response.data.decode()) - - assert response.status_code == 500 - assert 'error' in data['status'] - assert 'Error. Please try again or contact the administrator.' \ - in data['message'] - - -def test_edit_an_activity_no_activity(app): - add_user('test', 'test@test.com', '12345678') - add_sport('cycling') - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = client.patch( - '/api/activities/1', - content_type='application/json', - data=json.dumps(dict( - sport_id=1, - duration=3600, - activity_date='2018-05-15 14:05', - distance=10 - )), - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - - data = json.loads(response.data.decode()) - - assert response.status_code == 404 - assert 'not found' in data['status'] - assert len(data['data']['activities']) == 0 - - -def test_delete_an_activity_with_gpx(app): - add_user('test', 'test@test.com', '12345678') - add_sport('cycling') - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - client.post( - '/api/activities', - data=dict( - file=(BytesIO(str.encode(gpx_file)), 'example.gpx'), - data='{"sport_id": 1}' - ), - headers=dict( - content_type='multipart/form-data', - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - response = client.delete( - '/api/activities/1', - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - - assert response.status_code == 204 - - -def test_delete_an_activity_wo_gpx(app): - add_user('test', 'test@test.com', '12345678') - add_sport('cycling') - add_activity( - 1, - 1, - datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), - datetime.timedelta(seconds=1024)) - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = client.delete( - '/api/activities/1', - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - - assert response.status_code == 204 - - -def test_delete_an_activity_no_activityy(app): - add_user('test', 'test@test.com', '12345678') - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = client.delete( - '/api/activities/9999', - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - - data = json.loads(response.data.decode()) - - assert response.status_code == 404 - assert 'not found' in data['status'] - - -def test_delete_an_activity_with_gpx_invalid_file(app): - add_user('test', 'test@test.com', '12345678') - add_sport('cycling') - - client = app.test_client() - resp_login = client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - client.post( - '/api/activities', - data=dict( - file=(BytesIO(str.encode(gpx_file)), 'example.gpx'), - data='{"sport_id": 1}' - ), - headers=dict( - content_type='multipart/form-data', - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - - gpx_filepath = get_gpx_filepath(1) - os.remove(gpx_filepath) - - response = client.delete( - '/api/activities/1', - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - - data = json.loads(response.data.decode()) - - assert response.status_code == 500 - assert 'error' in data['status'] - assert 'Error. Please try again or contact the administrator.' \ - in data['message'] diff --git a/mpwo_api/mpwo_api/tests/test_activities_0_get.py b/mpwo_api/mpwo_api/tests/test_activities_0_get.py new file mode 100644 index 00000000..22eda9f5 --- /dev/null +++ b/mpwo_api/mpwo_api/tests/test_activities_0_get.py @@ -0,0 +1,260 @@ +import datetime +import json + +from mpwo_api.tests.utils import add_activity, add_sport, add_user + + +def test_get_all_activities_for_authenticated_user(app): + add_user('test', 'test@test.com', '12345678') + add_user('toto', 'toto@toto.com', '12345678') + add_sport('cycling') + add_sport('running') + add_activity( + user_id=1, + sport_id=2, + activity_date=datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), + distance=10, + duration=datetime.timedelta(seconds=1024) + ) + add_activity( + user_id=2, + sport_id=1, + activity_date=datetime.datetime.strptime('23/01/2018', '%d/%m/%Y'), + distance=15, + duration=datetime.timedelta(seconds=3600), + ) + add_activity( + user_id=1, + sport_id=1, + activity_date=datetime.datetime.strptime('01/04/2018', '%d/%m/%Y'), + distance=12, + duration=datetime.timedelta(seconds=6000) + ) + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.get( + '/api/activities', + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + + assert response.status_code == 200 + assert 'success' in data['status'] + assert len(data['data']['activities']) == 2 + + assert 'creation_date' in data['data']['activities'][0] + assert 'Sun, 01 Apr 2018 00:00:00 GMT' == data['data']['activities'][0]['activity_date'] # noqa + assert 1 == data['data']['activities'][0]['user_id'] + assert 1 == data['data']['activities'][0]['sport_id'] + assert 12.0 == data['data']['activities'][0]['distance'] + assert '1:40:00' == data['data']['activities'][0]['duration'] + + assert 'creation_date' in data['data']['activities'][1] + assert 'Mon, 01 Jan 2018 00:00:00 GMT' == data['data']['activities'][1]['activity_date'] # noqa + assert 1 == data['data']['activities'][1]['user_id'] + assert 2 == data['data']['activities'][1]['sport_id'] + assert 10.0 == data['data']['activities'][1]['distance'] + assert '0:17:04' == data['data']['activities'][1]['duration'] + + +def test_get_activities_for_authenticated_user_no_activity(app): + add_user('test', 'test@test.com', '12345678') + add_user('toto', 'toto@toto.com', '12345678') + add_sport('cycling') + add_sport('running') + add_activity( + user_id=1, + sport_id=2, + activity_date=datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), + distance=10, + duration=datetime.timedelta(seconds=1024) + ) + add_activity( + user_id=1, + sport_id=1, + activity_date=datetime.datetime.strptime('01/04/2018', '%d/%m/%Y'), + distance=12, + duration=datetime.timedelta(seconds=6000) + ) + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='toto@toto.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.get( + '/api/activities', + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + + assert response.status_code == 200 + assert 'success' in data['status'] + assert len(data['data']['activities']) == 0 + + +def test_get_activities_for_authenticated_user_no_authentication(app): + client = app.test_client() + response = client.get('/api/activities') + data = json.loads(response.data.decode()) + + assert response.status_code == 403 + assert 'error' in data['status'] + assert 'Provide a valid auth token.' in data['message'] + + +def test_get_activities_pagination(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + add_activity( + user_id=1, + sport_id=1, + activity_date=datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), + distance=10, + duration=datetime.timedelta(seconds=1024) + ) + add_activity( + user_id=1, + sport_id=1, + activity_date=datetime.datetime.strptime('01/04/2018', '%d/%m/%Y'), + distance=8, + duration=datetime.timedelta(seconds=6000) + ) + add_activity( + user_id=1, + sport_id=1, + activity_date=datetime.datetime.strptime('20/03/2017', '%d/%m/%Y'), + distance=5, + duration=datetime.timedelta(seconds=1024) + ) + add_activity( + user_id=1, + sport_id=1, + activity_date=datetime.datetime.strptime('09/05/2018', '%d/%m/%Y'), + distance=10, + duration=datetime.timedelta(seconds=3000) + ) + add_activity( + user_id=1, + sport_id=1, + activity_date=datetime.datetime.strptime('01/06/2017', '%d/%m/%Y'), + distance=10, + duration=datetime.timedelta(seconds=3456) + ) + add_activity( + user_id=1, + sport_id=1, + activity_date=datetime.datetime.strptime('23/02/2018', '%d/%m/%Y'), + distance=1, + duration=datetime.timedelta(seconds=600) + ) + add_activity( + user_id=1, + sport_id=1, + activity_date=datetime.datetime.strptime('23/02/2018', '%d/%m/%Y'), + distance=10, + duration=datetime.timedelta(seconds=1000) + ) + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.get( + '/api/activities', + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + + assert response.status_code == 200 + assert 'success' in data['status'] + assert len(data['data']['activities']) == 5 + assert 'creation_date' in data['data']['activities'][0] + assert 'Wed, 09 May 2018 00:00:00 GMT' == data['data']['activities'][0]['activity_date'] # noqa + assert '0:50:00' == data['data']['activities'][0]['duration'] + assert 'creation_date' in data['data']['activities'][4] + assert 'Mon, 01 Jan 2018 00:00:00 GMT' == data['data']['activities'][4]['activity_date'] # noqa + assert '0:17:04' == data['data']['activities'][4]['duration'] + + response = client.get( + '/api/activities?page=1', + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + + assert response.status_code == 200 + assert 'success' in data['status'] + assert len(data['data']['activities']) == 5 + assert 'creation_date' in data['data']['activities'][0] + assert 'Wed, 09 May 2018 00:00:00 GMT' == data['data']['activities'][0]['activity_date'] # noqa + assert '0:50:00' == data['data']['activities'][0]['duration'] + assert 'creation_date' in data['data']['activities'][4] + assert 'Mon, 01 Jan 2018 00:00:00 GMT' == data['data']['activities'][4]['activity_date'] # noqa + assert '0:17:04' == data['data']['activities'][4]['duration'] + + response = client.get( + '/api/activities?page=2', + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + + assert response.status_code == 200 + assert 'success' in data['status'] + assert len(data['data']['activities']) == 2 + assert 'creation_date' in data['data']['activities'][0] + assert 'Thu, 01 Jun 2017 00:00:00 GMT' == data['data']['activities'][0]['activity_date'] # noqa + assert '0:57:36' == data['data']['activities'][0]['duration'] + assert 'creation_date' in data['data']['activities'][1] + assert 'Mon, 20 Mar 2017 00:00:00 GMT' == data['data']['activities'][1]['activity_date'] # noqa + assert '0:17:04' == data['data']['activities'][1]['duration'] + + response = client.get( + '/api/activities?page=3', + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + + assert response.status_code == 200 + assert 'success' in data['status'] + assert len(data['data']['activities']) == 0 diff --git a/mpwo_api/mpwo_api/tests/test_activities_1_post.py b/mpwo_api/mpwo_api/tests/test_activities_1_post.py new file mode 100644 index 00000000..8f805447 --- /dev/null +++ b/mpwo_api/mpwo_api/tests/test_activities_1_post.py @@ -0,0 +1,419 @@ +import json +from io import BytesIO + +from mpwo_api.tests.utils import add_sport, add_user +from mpwo_api.tests.utils_gpx import gpx_file + + +def test_add_an_activity_gpx(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.post( + '/api/activities', + data=dict( + file=(BytesIO(str.encode(gpx_file)), 'example.gpx'), + data='{"sport_id": 1}' + ), + headers=dict( + content_type='multipart/form-data', + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + + assert response.status_code == 201 + assert 'created' in data['status'] + assert len(data['data']['activities']) == 1 + assert 'creation_date' in data['data']['activities'][0] + assert 'Tue, 13 Mar 2018 12:44:45 GMT' == data['data']['activities'][0]['activity_date'] # noqa + assert 1 == data['data']['activities'][0]['user_id'] + assert 1 == data['data']['activities'][0]['sport_id'] + assert '0:04:10' == data['data']['activities'][0]['duration'] + assert data['data']['activities'][0]['ascent'] == 0.4 + assert data['data']['activities'][0]['ave_speed'] == 4.6 + assert data['data']['activities'][0]['descent'] == 23.4 + assert data['data']['activities'][0]['distance'] == 0.32 + assert data['data']['activities'][0]['max_alt'] == 998.0 + assert data['data']['activities'][0]['max_speed'] == 5.09 + assert data['data']['activities'][0]['min_alt'] == 975.0 + assert data['data']['activities'][0]['moving'] == '0:04:10' + assert data['data']['activities'][0]['pauses'] is None + assert data['data']['activities'][0]['with_gpx'] is True + + +def test_get_an_activity_with_gpx(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + client.post( + '/api/activities', + data=dict( + file=(BytesIO(str.encode(gpx_file)), 'example.gpx'), + data='{"sport_id": 1}' + ), + headers=dict( + content_type='multipart/form-data', + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + response = client.get( + '/api/activities/1', + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + + data = json.loads(response.data.decode()) + + assert response.status_code == 200 + assert 'success' in data['status'] + assert len(data['data']['activities']) == 1 + assert 'creation_date' in data['data']['activities'][0] + assert 'Tue, 13 Mar 2018 12:44:45 GMT' == data['data']['activities'][0]['activity_date'] # noqa + assert 1 == data['data']['activities'][0]['user_id'] + assert 1 == data['data']['activities'][0]['sport_id'] + assert '0:04:10' == data['data']['activities'][0]['duration'] + assert data['data']['activities'][0]['ascent'] == 0.4 + assert data['data']['activities'][0]['ave_speed'] == 4.6 + assert data['data']['activities'][0]['descent'] == 23.4 + assert data['data']['activities'][0]['distance'] == 0.32 + assert data['data']['activities'][0]['max_alt'] == 998.0 + assert data['data']['activities'][0]['max_speed'] == 5.09 + assert data['data']['activities'][0]['min_alt'] == 975.0 + assert data['data']['activities'][0]['moving'] == '0:04:10' + assert data['data']['activities'][0]['pauses'] is None + assert data['data']['activities'][0]['with_gpx'] is True + + +def test_add_an_activity_gpx_invalid_file(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.post( + '/api/activities', + data=dict( + file=(BytesIO(str.encode(gpx_file)), 'example.png'), + data='{"sport_id": 1}' + ), + headers=dict( + content_type='multipart/form-data', + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + + assert response.status_code == 400 + assert data['status'] == 'fail' + assert data['message'] == 'File extension not allowed.' + + +def test_add_an_activity_gpx_no_sport_id(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.post( + '/api/activities', + data=dict( + file=(BytesIO(str.encode(gpx_file)), 'example.gpx'), + data='{}' + ), + headers=dict( + content_type='multipart/form-data', + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + + assert response.status_code == 400 + assert data['status'] == 'error' + assert data['message'] == 'Invalid payload.' + + +def test_add_an_activity_gpx_incorrect_sport_id(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.post( + '/api/activities', + data=dict( + file=(BytesIO(str.encode(gpx_file)), 'example.gpx'), + data='{"sport_id": 2}' + ), + headers=dict( + content_type='multipart/form-data', + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + + assert response.status_code == 500 + assert data['status'] == 'error' + assert data['message'] == \ + 'Error during activity file save.' + + +def test_add_an_activity_gpx_no_file(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.post( + '/api/activities', + data=dict( + data='{}' + ), + headers=dict( + content_type='multipart/form-data', + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + + assert response.status_code == 400 + assert data['status'] == 'fail' + assert data['message'] == 'No file part.' + + +def test_add_an_activity_no_gpx(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.post( + '/api/activities/no_gpx', + content_type='application/json', + data=json.dumps(dict( + sport_id=1, + duration=3600, + activity_date='2018-05-15 14:05', + distance=10 + )), + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + + assert response.status_code == 201 + assert 'created' in data['status'] + assert len(data['data']['activities']) == 1 + assert 'creation_date' in data['data']['activities'][0] + assert data['data']['activities'][0]['activity_date'] == 'Tue, 15 May 2018 14:05:00 GMT' # noqa + assert data['data']['activities'][0]['user_id'] == 1 + assert data['data']['activities'][0]['sport_id'] == 1 + assert data['data']['activities'][0]['duration'] == '1:00:00' + assert data['data']['activities'][0]['ascent'] is None + assert data['data']['activities'][0]['ave_speed'] == 10.0 + assert data['data']['activities'][0]['descent'] is None + assert data['data']['activities'][0]['distance'] == 10.0 + assert data['data']['activities'][0]['max_alt'] is None + assert data['data']['activities'][0]['max_speed'] == 10.0 + assert data['data']['activities'][0]['min_alt'] is None + assert data['data']['activities'][0]['moving'] == '1:00:00' + assert data['data']['activities'][0]['pauses'] is None + assert data['data']['activities'][0]['with_gpx'] is False + + +def test_get_an_activity_wo_gpx(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + client.post( + '/api/activities/no_gpx', + content_type='application/json', + data=json.dumps(dict( + sport_id=1, + duration=3600, + activity_date='2018-05-15 14:05', + distance=10 + )), + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + response = client.get( + '/api/activities/1', + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + + assert response.status_code == 200 + assert 'success' in data['status'] + assert len(data['data']['activities']) == 1 + assert 'creation_date' in data['data']['activities'][0] + assert data['data']['activities'][0]['activity_date'] == 'Tue, 15 May 2018 14:05:00 GMT' # noqa + assert data['data']['activities'][0]['user_id'] == 1 + assert data['data']['activities'][0]['sport_id'] == 1 + assert data['data']['activities'][0]['duration'] == '1:00:00' + assert data['data']['activities'][0]['ascent'] is None + assert data['data']['activities'][0]['ave_speed'] == 10.0 + assert data['data']['activities'][0]['descent'] is None + assert data['data']['activities'][0]['distance'] == 10.0 + assert data['data']['activities'][0]['max_alt'] is None + assert data['data']['activities'][0]['max_speed'] == 10.0 + assert data['data']['activities'][0]['min_alt'] is None + assert data['data']['activities'][0]['moving'] == '1:00:00' + assert data['data']['activities'][0]['pauses'] is None + assert data['data']['activities'][0]['with_gpx'] is False + + +def test_add_an_activity_no_gpx_invalid_payload(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.post( + '/api/activities/no_gpx', + content_type='application/json', + data=json.dumps(dict( + sport_id=1, + duration=3600, + distance=10 + )), + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + + assert response.status_code == 400 + assert 'error' in data['status'] + assert 'Invalid payload.' in data['message'] + + +def test_add_an_activity_no_gpx_error(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.post( + '/api/activities/no_gpx', + content_type='application/json', + data=json.dumps(dict( + sport_id=1, + duration=3600, + activity_date='15/2018', + distance=10 + )), + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + + assert response.status_code == 500 + assert 'fail' in data['status'] + assert 'Error during activity save.' in data['message'] diff --git a/mpwo_api/mpwo_api/tests/test_activities_2_patch.py b/mpwo_api/mpwo_api/tests/test_activities_2_patch.py new file mode 100644 index 00000000..f6eeb8a9 --- /dev/null +++ b/mpwo_api/mpwo_api/tests/test_activities_2_patch.py @@ -0,0 +1,392 @@ +import datetime +import json +from io import BytesIO + +from mpwo_api.tests.utils import add_activity, add_sport, add_user +from mpwo_api.tests.utils_gpx import gpx_file + + +def test_edit_an_activity_with_gpx(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + add_sport('running') + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + client.post( + '/api/activities', + data=dict( + file=(BytesIO(str.encode(gpx_file)), 'example.gpx'), + data='{"sport_id": 1}' + ), + headers=dict( + content_type='multipart/form-data', + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + response = client.patch( + '/api/activities/1', + content_type='application/json', + data=json.dumps(dict( + sport_id=2, + )), + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + + data = json.loads(response.data.decode()) + + assert response.status_code == 200 + assert 'success' in data['status'] + assert len(data['data']['activities']) == 1 + + assert 'creation_date' in data['data']['activities'][0] + assert 'Tue, 13 Mar 2018 12:44:45 GMT' == data['data']['activities'][0]['activity_date'] # noqa + assert 1 == data['data']['activities'][0]['user_id'] + assert 2 == data['data']['activities'][0]['sport_id'] + assert '0:04:10' == data['data']['activities'][0]['duration'] + assert data['data']['activities'][0]['ascent'] == 0.4 + assert data['data']['activities'][0]['ave_speed'] == 4.6 + assert data['data']['activities'][0]['descent'] == 23.4 + assert data['data']['activities'][0]['distance'] == 0.32 + assert data['data']['activities'][0]['max_alt'] == 998.0 + assert data['data']['activities'][0]['max_speed'] == 5.09 + assert data['data']['activities'][0]['min_alt'] == 975.0 + assert data['data']['activities'][0]['moving'] == '0:04:10' + assert data['data']['activities'][0]['pauses'] is None + assert data['data']['activities'][0]['with_gpx'] is True + + +def test_edit_an_activity_with_gpx_invalid_payload(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + add_sport('running') + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + client.post( + '/api/activities', + data=dict( + file=(BytesIO(str.encode(gpx_file)), 'example.gpx'), + data='{"sport_id": 1}' + ), + headers=dict( + content_type='multipart/form-data', + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + response = client.patch( + '/api/activities/1', + content_type='application/json', + data=json.dumps(dict()), + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + + data = json.loads(response.data.decode()) + + assert response.status_code == 400 + assert 'error' in data['status'] + assert 'Invalid payload.' in data['message'] + + +def test_edit_an_activity_with_gpx_incorrect_data(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + client.post( + '/api/activities', + data=dict( + file=(BytesIO(str.encode(gpx_file)), 'example.gpx'), + data='{"sport_id": 1}' + ), + headers=dict( + content_type='multipart/form-data', + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + response = client.patch( + '/api/activities/1', + content_type='application/json', + data=json.dumps(dict( + sport_id=2, + )), + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + + data = json.loads(response.data.decode()) + + assert response.status_code == 500 + assert 'error' in data['status'] + assert 'Error. Please try again or contact the administrator.' in data['message'] # noqa + + +def test_edit_an_activity_wo_gpx(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + add_activity( + user_id=1, + sport_id=1, + activity_date=datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), + distance=10, + duration=datetime.timedelta(seconds=1024) + ) + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.patch( + '/api/activities/1', + content_type='application/json', + data=json.dumps(dict( + sport_id=1, + duration=3600, + activity_date='2018-05-15 14:05', + distance=10 + )), + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + + data = json.loads(response.data.decode()) + + assert response.status_code == 200 + assert 'success' in data['status'] + assert len(data['data']['activities']) == 1 + assert 'creation_date' in data['data']['activities'][0] + assert data['data']['activities'][0]['activity_date'] == 'Tue, 15 May 2018 14:05:00 GMT' # noqa + assert data['data']['activities'][0]['user_id'] == 1 + assert data['data']['activities'][0]['sport_id'] == 1 + assert data['data']['activities'][0]['duration'] == '1:00:00' + assert data['data']['activities'][0]['ascent'] is None + assert data['data']['activities'][0]['ave_speed'] == 10.0 + assert data['data']['activities'][0]['descent'] is None + assert data['data']['activities'][0]['distance'] == 10.0 + assert data['data']['activities'][0]['max_alt'] is None + assert data['data']['activities'][0]['max_speed'] == 10.0 + assert data['data']['activities'][0]['min_alt'] is None + assert data['data']['activities'][0]['moving'] == '1:00:00' + assert data['data']['activities'][0]['pauses'] is None + assert data['data']['activities'][0]['with_gpx'] is False + + +def test_edit_an_activity_wo_gpx_partial(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + add_activity( + user_id=1, + sport_id=1, + activity_date=datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), + distance=10, + duration=datetime.timedelta(seconds=1024) + ) + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.patch( + '/api/activities/1', + content_type='application/json', + data=json.dumps(dict( + sport_id=1, + distance=10 + )), + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + + data = json.loads(response.data.decode()) + + assert response.status_code == 200 + assert 'success' in data['status'] + assert len(data['data']['activities']) == 1 + assert 'creation_date' in data['data']['activities'][0] + assert data['data']['activities'][0]['activity_date'] == 'Mon, 01 Jan 2018 00:00:00 GMT' # noqa + assert data['data']['activities'][0]['user_id'] == 1 + assert data['data']['activities'][0]['sport_id'] == 1 + assert data['data']['activities'][0]['duration'] == '0:17:04' + assert data['data']['activities'][0]['ascent'] is None + assert data['data']['activities'][0]['ave_speed'] == 35.16 + assert data['data']['activities'][0]['descent'] is None + assert data['data']['activities'][0]['distance'] == 10.0 + assert data['data']['activities'][0]['max_alt'] is None + assert data['data']['activities'][0]['max_speed'] == 35.16 + assert data['data']['activities'][0]['min_alt'] is None + assert data['data']['activities'][0]['moving'] is None # no calculate + assert data['data']['activities'][0]['pauses'] is None + assert data['data']['activities'][0]['with_gpx'] is False + + +def test_edit_an_activity_wo_gpx_invalid_payload(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + add_activity( + user_id=1, + sport_id=1, + activity_date=datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), + distance=10, + duration=datetime.timedelta(seconds=1024) + ) + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.patch( + '/api/activities/1', + content_type='application/json', + data=json.dumps(dict()), + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + + data = json.loads(response.data.decode()) + + assert response.status_code == 400 + assert 'error' in data['status'] + assert 'Invalid payload.' in data['message'] + + +def test_edit_an_activity_wo_gpx_incorrect_data(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + add_activity( + user_id=1, + sport_id=1, + activity_date=datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), + distance=10, + duration=datetime.timedelta(seconds=1024) + ) + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.patch( + '/api/activities/1', + content_type='application/json', + data=json.dumps(dict( + sport_id=1, + duration=3600, + activity_date='15/2018', + distance=10 + )), + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + + data = json.loads(response.data.decode()) + + assert response.status_code == 500 + assert 'error' in data['status'] + assert 'Error. Please try again or contact the administrator.' \ + in data['message'] + + +def test_edit_an_activity_no_activity(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.patch( + '/api/activities/1', + content_type='application/json', + data=json.dumps(dict( + sport_id=1, + duration=3600, + activity_date='2018-05-15 14:05', + distance=10 + )), + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + + data = json.loads(response.data.decode()) + + assert response.status_code == 404 + assert 'not found' in data['status'] + assert len(data['data']['activities']) == 0 diff --git a/mpwo_api/mpwo_api/tests/test_activities_3_delete.py b/mpwo_api/mpwo_api/tests/test_activities_3_delete.py new file mode 100644 index 00000000..86dd432f --- /dev/null +++ b/mpwo_api/mpwo_api/tests/test_activities_3_delete.py @@ -0,0 +1,153 @@ +import datetime +import json +import os +from io import BytesIO + +from mpwo_api.tests.utils import ( + add_activity, add_sport, add_user, get_gpx_filepath +) +from mpwo_api.tests.utils_gpx import gpx_file + + +def test_delete_an_activity_with_gpx(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + client.post( + '/api/activities', + data=dict( + file=(BytesIO(str.encode(gpx_file)), 'example.gpx'), + data='{"sport_id": 1}' + ), + headers=dict( + content_type='multipart/form-data', + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + response = client.delete( + '/api/activities/1', + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + + assert response.status_code == 204 + + +def test_delete_an_activity_wo_gpx(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + add_activity( + user_id=1, + sport_id=1, + activity_date=datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), + distance=10, + duration=datetime.timedelta(seconds=1024) + ) + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.delete( + '/api/activities/1', + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + + assert response.status_code == 204 + + +def test_delete_an_activity_no_activityy(app): + add_user('test', 'test@test.com', '12345678') + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.delete( + '/api/activities/9999', + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + + data = json.loads(response.data.decode()) + + assert response.status_code == 404 + assert 'not found' in data['status'] + + +def test_delete_an_activity_with_gpx_invalid_file(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + client.post( + '/api/activities', + data=dict( + file=(BytesIO(str.encode(gpx_file)), 'example.gpx'), + data='{"sport_id": 1}' + ), + headers=dict( + content_type='multipart/form-data', + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + + gpx_filepath = get_gpx_filepath(1) + os.remove(gpx_filepath) + + response = client.delete( + '/api/activities/1', + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + + data = json.loads(response.data.decode()) + + assert response.status_code == 500 + assert 'error' in data['status'] + assert 'Error. Please try again or contact the administrator.' \ + in data['message'] diff --git a/mpwo_api/mpwo_api/tests/test_sports.py b/mpwo_api/mpwo_api/tests/test_sports.py index 6d9f8b34..b70d0483 100644 --- a/mpwo_api/mpwo_api/tests/test_sports.py +++ b/mpwo_api/mpwo_api/tests/test_sports.py @@ -256,10 +256,11 @@ def test_delete_a_sport_with_an_activity(app): add_admin() add_sport('cycling') add_activity( - 1, - 1, - datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), - datetime.timedelta(seconds=1024) + user_id=1, + sport_id=1, + activity_date=datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), + distance=10, + duration=datetime.timedelta(seconds=1024) ) client = app.test_client() diff --git a/mpwo_api/mpwo_api/tests/utils.py b/mpwo_api/mpwo_api/tests/utils.py index c9670ad8..ec8d530c 100644 --- a/mpwo_api/mpwo_api/tests/utils.py +++ b/mpwo_api/mpwo_api/tests/utils.py @@ -43,11 +43,12 @@ def add_sport(label): return sport -def add_activity(user_id, sport_id, activity_date, duration): +def add_activity(user_id, sport_id, activity_date, distance, duration): activity = Activity( user_id=user_id, sport_id=sport_id, activity_date=activity_date, + distance=distance, duration=duration) db.session.add(activity) db.session.commit() diff --git a/mpwo_api/server.py b/mpwo_api/server.py index 8c5c2071..396238d8 100644 --- a/mpwo_api/server.py +++ b/mpwo_api/server.py @@ -1,3 +1,5 @@ +import shutil + from mpwo_api import create_app, db from mpwo_api.activities.models import Sport from mpwo_api.users.models import User @@ -12,6 +14,8 @@ def drop_db(): db.drop_all() db.session.commit() print('Database dropped.') + shutil.rmtree(app.config['UPLOAD_FOLDER'], ignore_errors=True) + print('Uploaded files deleted.') @app.cli.command() diff --git a/mpwo_client/src/components/Activities/ActivityAddOrEdit.jsx b/mpwo_client/src/components/Activities/ActivityAddOrEdit.jsx index 9782ec32..71167436 100644 --- a/mpwo_client/src/components/Activities/ActivityAddOrEdit.jsx +++ b/mpwo_client/src/components/Activities/ActivityAddOrEdit.jsx @@ -46,13 +46,9 @@ export default class ActivityAddEdit extends React.Component {
{activity ? ( activity.with_gpx ? ( - 'You can\'t modify this activity.' + - ' Please delete and re-import gpx file.' + ) : ( - + ) ) : (
diff --git a/mpwo_client/src/components/Activities/ActivityDisplay.jsx b/mpwo_client/src/components/Activities/ActivityDisplay.jsx index e8afe30c..619fc985 100644 --- a/mpwo_client/src/components/Activities/ActivityDisplay.jsx +++ b/mpwo_client/src/components/Activities/ActivityDisplay.jsx @@ -54,14 +54,12 @@ class ActivityDisplay extends React.Component { {sports.filter(sport => sport.id === activity.sport_id) .map(sport => sport.label)} -{' '} {activity.activity_date}{' '} - {!activity.with_gpx && ( - -