API: timezone support for activity creation and edition - #11
This commit is contained in:
parent
31d23da473
commit
027fa8f699
@ -300,7 +300,7 @@ def update_activity(auth_user_id, activity_id):
|
||||
try:
|
||||
activity = Activity.query.filter_by(id=activity_id).first()
|
||||
if activity:
|
||||
activity = edit_activity(activity, activity_data)
|
||||
activity = edit_activity(activity, activity_data, auth_user_id)
|
||||
db.session.commit()
|
||||
response_object = {
|
||||
'status': 'success',
|
||||
|
@ -5,12 +5,14 @@ import zipfile
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import gpxpy.gpx
|
||||
import pytz
|
||||
from fittrackee_api import db
|
||||
from flask import current_app
|
||||
from sqlalchemy import exc
|
||||
from staticmap import Line, StaticMap
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from ..users.models import User
|
||||
from .models import Activity, ActivitySegment, Sport
|
||||
|
||||
|
||||
@ -37,8 +39,20 @@ def update_activity_data(activity, gpx_data):
|
||||
def create_activity(
|
||||
auth_user_id, activity_data, gpx_data=None
|
||||
):
|
||||
user = User.query.filter_by(id=auth_user_id).first()
|
||||
|
||||
activity_date = gpx_data['start'] if gpx_data else datetime.strptime(
|
||||
activity_data.get('activity_date'), '%Y-%m-%d %H:%M')
|
||||
activity_date_tz = None
|
||||
# activity date in gpx are directly in UTC
|
||||
if user.timezone and not gpx_data:
|
||||
user_tz = pytz.timezone(user.timezone)
|
||||
activity_date_tz = user_tz.localize(activity_date)
|
||||
if not gpx_data:
|
||||
# make datetime 'naive' like in gpx file
|
||||
activity_date = activity_date_tz.astimezone(pytz.utc)
|
||||
activity_date = activity_date.replace(tzinfo=None)
|
||||
|
||||
duration = gpx_data['duration'] if gpx_data \
|
||||
else timedelta(seconds=activity_data.get('duration'))
|
||||
distance = gpx_data['distance'] if gpx_data \
|
||||
@ -58,7 +72,12 @@ def create_activity(
|
||||
new_activity.title = title
|
||||
else:
|
||||
sport = Sport.query.filter_by(id=new_activity.sport_id).first()
|
||||
new_activity.title = f'{sport.label} - {new_activity.activity_date}'
|
||||
fmt = "%Y-%m-%d %H:%M:%S"
|
||||
activity_datetime = (
|
||||
activity_date_tz.strftime(fmt)
|
||||
if activity_date_tz
|
||||
else new_activity.activity_date.strftime(fmt))
|
||||
new_activity.title = f'{sport.label} - {activity_datetime}'
|
||||
|
||||
if gpx_data:
|
||||
new_activity.gpx = gpx_data['filename']
|
||||
@ -85,15 +104,23 @@ def create_segment(activity_id, segment_data):
|
||||
return new_segment
|
||||
|
||||
|
||||
def edit_activity(activity, activity_data):
|
||||
def edit_activity(activity, activity_data, auth_user_id):
|
||||
if activity_data.get('sport_id'):
|
||||
activity.sport_id = activity_data.get('sport_id')
|
||||
if activity_data.get('title'):
|
||||
activity.title = activity_data.get('title')
|
||||
if not activity.gpx:
|
||||
if activity_data.get('activity_date'):
|
||||
activity.activity_date = datetime.strptime(
|
||||
activity_date = datetime.strptime(
|
||||
activity_data.get('activity_date'), '%Y-%m-%d %H:%M')
|
||||
user = User.query.filter_by(id=auth_user_id).first()
|
||||
if user.timezone:
|
||||
# make datetime 'naive' like in gpx file
|
||||
user_tz = pytz.timezone(user.timezone)
|
||||
activity_date_tz = user_tz.localize(activity_date)
|
||||
activity_date = activity_date_tz.astimezone(pytz.utc)
|
||||
activity_date = activity_date.replace(tzinfo=None)
|
||||
activity.activity_date = activity_date
|
||||
if activity_data.get('duration'):
|
||||
activity.duration = timedelta(
|
||||
seconds=activity_data.get('duration'))
|
||||
|
@ -569,6 +569,49 @@ def test_get_an_activity_wo_gpx(app, user_1, sport_1_cycling):
|
||||
assert_activity_data_wo_gpx(data)
|
||||
|
||||
|
||||
def test_get_an_activity_wo_gpx_with_timezone(app, user_1, sport_1_cycling):
|
||||
user_1.timezone = 'Europe/Paris'
|
||||
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 data['data']['activities'][0]['activity_date'] == 'Tue, 15 May 2018 12:05:00 GMT' # noqa
|
||||
assert data['data']['activities'][0]['title'] == 'Cycling - 2018-05-15 14:05:00' # noqa
|
||||
|
||||
|
||||
def test_add_an_activity_no_gpx_invalid_payload(app, user_1, sport_1_cycling):
|
||||
client = app.test_client()
|
||||
resp_login = client.post(
|
||||
|
@ -306,7 +306,7 @@ def test_edit_an_activity_wo_gpx(
|
||||
data=json.dumps(dict(
|
||||
sport_id=2,
|
||||
duration=3600,
|
||||
activity_date='2018-05-15 14:05',
|
||||
activity_date='2018-05-15 15:05',
|
||||
distance=8,
|
||||
title='Activity test'
|
||||
)),
|
||||
@ -322,7 +322,7 @@ def test_edit_an_activity_wo_gpx(
|
||||
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]['activity_date'] == 'Tue, 15 May 2018 15:05:00 GMT' # noqa
|
||||
assert data['data']['activities'][0]['user_id'] == 1
|
||||
assert data['data']['activities'][0]['sport_id'] == 2
|
||||
assert data['data']['activities'][0]['duration'] == '1:00:00'
|
||||
@ -343,22 +343,159 @@ def test_edit_an_activity_wo_gpx(
|
||||
assert records[0]['sport_id'] == 2
|
||||
assert records[0]['activity_id'] == 1
|
||||
assert records[0]['record_type'] == 'MS'
|
||||
assert records[0]['activity_date'] == 'Tue, 15 May 2018 14:05:00 GMT'
|
||||
assert records[0]['activity_date'] == 'Tue, 15 May 2018 15:05:00 GMT'
|
||||
assert records[0]['value'] == 8.0
|
||||
assert records[1]['sport_id'] == 2
|
||||
assert records[1]['activity_id'] == 1
|
||||
assert records[1]['record_type'] == 'LD'
|
||||
assert records[1]['activity_date'] == 'Tue, 15 May 2018 14:05:00 GMT'
|
||||
assert records[1]['activity_date'] == 'Tue, 15 May 2018 15:05:00 GMT'
|
||||
assert records[1]['value'] == '1:00:00'
|
||||
assert records[2]['sport_id'] == 2
|
||||
assert records[2]['activity_id'] == 1
|
||||
assert records[2]['record_type'] == 'FD'
|
||||
assert records[2]['activity_date'] == 'Tue, 15 May 2018 14:05:00 GMT'
|
||||
assert records[2]['activity_date'] == 'Tue, 15 May 2018 15:05:00 GMT'
|
||||
assert records[2]['value'] == 8.0
|
||||
assert records[3]['sport_id'] == 2
|
||||
assert records[3]['activity_id'] == 1
|
||||
assert records[3]['record_type'] == 'AS'
|
||||
assert records[3]['activity_date'] == 'Tue, 15 May 2018 14:05:00 GMT'
|
||||
assert records[3]['activity_date'] == 'Tue, 15 May 2018 15:05:00 GMT'
|
||||
assert records[3]['value'] == 8.0
|
||||
|
||||
|
||||
def test_edit_an_activity_wo_gpx_timezone(
|
||||
app, user_1, sport_1_cycling, sport_2_running
|
||||
):
|
||||
client = app.test_client()
|
||||
user_1.timezone = 'Europe/Paris'
|
||||
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-14 14:05',
|
||||
distance=7
|
||||
)),
|
||||
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'] == 'Mon, 14 May 2018 12: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]['title'] == 'Cycling - 2018-05-14 14:05:00' # noqa
|
||||
assert data['data']['activities'][0]['ascent'] is None
|
||||
assert data['data']['activities'][0]['ave_speed'] == 7.0
|
||||
assert data['data']['activities'][0]['descent'] is None
|
||||
assert data['data']['activities'][0]['distance'] == 7.0
|
||||
assert data['data']['activities'][0]['max_alt'] is None
|
||||
assert data['data']['activities'][0]['max_speed'] == 7.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
|
||||
|
||||
records = data['data']['activities'][0]['records']
|
||||
assert len(records) == 4
|
||||
assert records[0]['sport_id'] == 1
|
||||
assert records[0]['activity_id'] == 1
|
||||
assert records[0]['record_type'] == 'MS'
|
||||
assert records[0]['activity_date'] == 'Mon, 14 May 2018 12:05:00 GMT'
|
||||
assert records[0]['value'] == 7.0
|
||||
assert records[1]['sport_id'] == 1
|
||||
assert records[1]['activity_id'] == 1
|
||||
assert records[1]['record_type'] == 'LD'
|
||||
assert records[1]['activity_date'] == 'Mon, 14 May 2018 12:05:00 GMT'
|
||||
assert records[1]['value'] == '1:00:00'
|
||||
assert records[2]['sport_id'] == 1
|
||||
assert records[2]['activity_id'] == 1
|
||||
assert records[2]['record_type'] == 'FD'
|
||||
assert records[2]['activity_date'] == 'Mon, 14 May 2018 12:05:00 GMT'
|
||||
assert records[2]['value'] == 7.0
|
||||
assert records[3]['sport_id'] == 1
|
||||
assert records[3]['activity_id'] == 1
|
||||
assert records[3]['record_type'] == 'AS'
|
||||
assert records[3]['activity_date'] == 'Mon, 14 May 2018 12:05:00 GMT'
|
||||
assert records[3]['value'] == 7.0
|
||||
|
||||
response = client.patch(
|
||||
'/api/activities/1',
|
||||
content_type='application/json',
|
||||
data=json.dumps(dict(
|
||||
sport_id=2,
|
||||
duration=3600,
|
||||
activity_date='2018-05-15 15:05',
|
||||
distance=8,
|
||||
title='Activity test'
|
||||
)),
|
||||
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 13:05:00 GMT' # noqa
|
||||
assert data['data']['activities'][0]['user_id'] == 1
|
||||
assert data['data']['activities'][0]['sport_id'] == 2
|
||||
assert data['data']['activities'][0]['duration'] == '1:00:00'
|
||||
assert data['data']['activities'][0]['title'] == 'Activity test' # noqa
|
||||
assert data['data']['activities'][0]['ascent'] is None
|
||||
assert data['data']['activities'][0]['ave_speed'] == 8.0
|
||||
assert data['data']['activities'][0]['descent'] is None
|
||||
assert data['data']['activities'][0]['distance'] == 8.0
|
||||
assert data['data']['activities'][0]['max_alt'] is None
|
||||
assert data['data']['activities'][0]['max_speed'] == 8.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
|
||||
|
||||
records = data['data']['activities'][0]['records']
|
||||
assert len(records) == 4
|
||||
assert records[0]['sport_id'] == 2
|
||||
assert records[0]['activity_id'] == 1
|
||||
assert records[0]['record_type'] == 'MS'
|
||||
assert records[0]['activity_date'] == 'Tue, 15 May 2018 13:05:00 GMT'
|
||||
assert records[0]['value'] == 8.0
|
||||
assert records[1]['sport_id'] == 2
|
||||
assert records[1]['activity_id'] == 1
|
||||
assert records[1]['record_type'] == 'LD'
|
||||
assert records[1]['activity_date'] == 'Tue, 15 May 2018 13:05:00 GMT'
|
||||
assert records[1]['value'] == '1:00:00'
|
||||
assert records[2]['sport_id'] == 2
|
||||
assert records[2]['activity_id'] == 1
|
||||
assert records[2]['record_type'] == 'FD'
|
||||
assert records[2]['activity_date'] == 'Tue, 15 May 2018 13:05:00 GMT'
|
||||
assert records[2]['value'] == 8.0
|
||||
assert records[3]['sport_id'] == 2
|
||||
assert records[3]['activity_id'] == 1
|
||||
assert records[3]['record_type'] == 'AS'
|
||||
assert records[3]['activity_date'] == 'Tue, 15 May 2018 13:05:00 GMT'
|
||||
assert records[3]['value'] == 8.0
|
||||
|
||||
|
||||
|
@ -59,6 +59,7 @@ def register_user():
|
||||
email=email,
|
||||
password=password
|
||||
)
|
||||
new_user.timezone = 'Europe/Paris'
|
||||
db.session.add(new_user)
|
||||
db.session.commit()
|
||||
# generate auth token
|
||||
|
@ -43,6 +43,7 @@ pytest-isort==0.1.0
|
||||
pytest-runner==3.0
|
||||
python-dateutil==2.7.2
|
||||
python-editor==1.0.3
|
||||
pytz==2018.4
|
||||
requests==2.18.4
|
||||
six==1.11.0
|
||||
SQLAlchemy==1.2.7
|
||||
|
Loading…
Reference in New Issue
Block a user