API: get data from gpx to display chart w/ speed and elevation
This commit is contained in:
parent
8b2bbb505f
commit
8bd0d4c26e
@ -8,8 +8,8 @@ from sqlalchemy import exc
|
|||||||
from ..users.utils import authenticate, verify_extension
|
from ..users.utils import authenticate, verify_extension
|
||||||
from .models import Activity, Sport
|
from .models import Activity, Sport
|
||||||
from .utils import (
|
from .utils import (
|
||||||
create_activity, create_segment, edit_activity, get_file_path,
|
create_activity, create_segment, edit_activity, get_chart_data,
|
||||||
get_gpx_info, get_new_file_path
|
get_file_path, get_gpx_info, get_new_file_path
|
||||||
)
|
)
|
||||||
|
|
||||||
activities_blueprint = Blueprint('activities', __name__)
|
activities_blueprint = Blueprint('activities', __name__)
|
||||||
@ -114,6 +114,53 @@ def get_activity_gpx(auth_user_id, activity_id):
|
|||||||
return jsonify(response_object), code
|
return jsonify(response_object), code
|
||||||
|
|
||||||
|
|
||||||
|
@activities_blueprint.route(
|
||||||
|
'/activities/<int:activity_id>/chart_data', methods=['GET']
|
||||||
|
)
|
||||||
|
@authenticate
|
||||||
|
def get_activity_chart_data(auth_user_id, activity_id):
|
||||||
|
"""Get chart data from an activity gpx file"""
|
||||||
|
activity = Activity.query.filter_by(id=activity_id).first()
|
||||||
|
if activity:
|
||||||
|
if not activity.gpx or activity.gpx == '':
|
||||||
|
response_object = {
|
||||||
|
'status': 'fail',
|
||||||
|
'message': f'No gpx file for this activity (id: {activity_id})'
|
||||||
|
}
|
||||||
|
return jsonify(response_object), 400
|
||||||
|
|
||||||
|
try:
|
||||||
|
chart_content = get_chart_data(activity.gpx)
|
||||||
|
except Exception as e:
|
||||||
|
appLog.error(e)
|
||||||
|
response_object = {
|
||||||
|
'status': 'error',
|
||||||
|
'message': 'internal error',
|
||||||
|
'data': {
|
||||||
|
'chart_data': ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jsonify(response_object), 500
|
||||||
|
|
||||||
|
status = 'success'
|
||||||
|
message = ''
|
||||||
|
code = 200
|
||||||
|
else:
|
||||||
|
chart_content = ''
|
||||||
|
status = 'not found'
|
||||||
|
message = f'Activity not found (id: {activity_id})'
|
||||||
|
code = 404
|
||||||
|
|
||||||
|
response_object = {
|
||||||
|
'status': status,
|
||||||
|
'message': message,
|
||||||
|
'data': {
|
||||||
|
'chart_data': chart_content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jsonify(response_object), code
|
||||||
|
|
||||||
|
|
||||||
@activities_blueprint.route('/activities', methods=['POST'])
|
@activities_blueprint.route('/activities', methods=['POST'])
|
||||||
@authenticate
|
@authenticate
|
||||||
def post_activity(auth_user_id):
|
def post_activity(auth_user_id):
|
||||||
|
@ -122,7 +122,7 @@ def get_gpx_data(parsed_gpx, max_speed, start):
|
|||||||
return gpx_data
|
return gpx_data
|
||||||
|
|
||||||
|
|
||||||
def get_gpx_info(gpx_file):
|
def open_gpx_file(gpx_file):
|
||||||
gpx_file = open(gpx_file, 'r')
|
gpx_file = open(gpx_file, 'r')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -131,10 +131,17 @@ def get_gpx_info(gpx_file):
|
|||||||
appLog.error(e)
|
appLog.error(e)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# handle only one track per file
|
|
||||||
if len(gpx.tracks) == 0:
|
if len(gpx.tracks) == 0:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
return gpx
|
||||||
|
|
||||||
|
|
||||||
|
def get_gpx_info(gpx_file):
|
||||||
|
gpx = open_gpx_file(gpx_file)
|
||||||
|
if gpx is None:
|
||||||
|
return None
|
||||||
|
|
||||||
gpx_data = {
|
gpx_data = {
|
||||||
'name': gpx.tracks[0].name,
|
'name': gpx.tracks[0].name,
|
||||||
'segments': []
|
'segments': []
|
||||||
@ -173,6 +180,42 @@ def get_gpx_info(gpx_file):
|
|||||||
return gpx_data
|
return gpx_data
|
||||||
|
|
||||||
|
|
||||||
|
def get_chart_data(gpx_file):
|
||||||
|
gpx = open_gpx_file(gpx_file)
|
||||||
|
if gpx is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
chart_data = []
|
||||||
|
first_point = None
|
||||||
|
previous_point = None
|
||||||
|
previous_distance = 0
|
||||||
|
|
||||||
|
for segment_idx, segment in enumerate(gpx.tracks[0].segments):
|
||||||
|
for point_idx, point in enumerate(segment.points):
|
||||||
|
if segment_idx == 0 and point_idx == 0:
|
||||||
|
first_point = point
|
||||||
|
distance = (point.distance_3d(previous_point)
|
||||||
|
if (point.elevation
|
||||||
|
and previous_point
|
||||||
|
and previous_point.elevation)
|
||||||
|
else point.distance_2d(previous_point)
|
||||||
|
)
|
||||||
|
distance = 0 if distance is None else distance
|
||||||
|
distance += previous_distance
|
||||||
|
speed = round((segment.get_speed(point_idx) / 1000)*3600, 2)
|
||||||
|
chart_data.append({
|
||||||
|
'distance': round(distance / 1000, 2),
|
||||||
|
'duration': point.time_difference(first_point),
|
||||||
|
'elevation': round(point.elevation, 1),
|
||||||
|
'speed': speed,
|
||||||
|
'time': point.time,
|
||||||
|
})
|
||||||
|
previous_point = point
|
||||||
|
previous_distance = distance
|
||||||
|
|
||||||
|
return chart_data
|
||||||
|
|
||||||
|
|
||||||
def get_file_path(auth_user_id, activity_file):
|
def get_file_path(auth_user_id, activity_file):
|
||||||
filename = secure_filename(activity_file.filename)
|
filename = secure_filename(activity_file.filename)
|
||||||
dir_path = os.path.join(
|
dir_path = os.path.join(
|
||||||
|
@ -254,8 +254,23 @@ def test_get_an_activity_no_actvity_no_gpx(app, user_1):
|
|||||||
assert 'Activity not found (id: 11)' in data['message']
|
assert 'Activity not found (id: 11)' in data['message']
|
||||||
assert data['data']['gpx'] == ''
|
assert data['data']['gpx'] == ''
|
||||||
|
|
||||||
|
response = client.get(
|
||||||
|
'/api/activities/11/chart_data',
|
||||||
|
headers=dict(
|
||||||
|
Authorization='Bearer ' + json.loads(
|
||||||
|
resp_login.data.decode()
|
||||||
|
)['auth_token']
|
||||||
|
)
|
||||||
|
)
|
||||||
|
data = json.loads(response.data.decode())
|
||||||
|
|
||||||
def test_get_an_activity_actvity_no_gpx(
|
assert response.status_code == 404
|
||||||
|
assert 'not found' in data['status']
|
||||||
|
assert 'Activity not found (id: 11)' in data['message']
|
||||||
|
assert data['data']['chart_data'] == ''
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_an_activity_activity_no_gpx(
|
||||||
app, user_1, sport_1_cycling, activity_cycling_user_1
|
app, user_1, sport_1_cycling, activity_cycling_user_1
|
||||||
):
|
):
|
||||||
client = app.test_client()
|
client = app.test_client()
|
||||||
@ -281,8 +296,22 @@ def test_get_an_activity_actvity_no_gpx(
|
|||||||
assert 'fail' in data['status']
|
assert 'fail' in data['status']
|
||||||
assert 'No gpx file for this activity (id: 1)' in data['message']
|
assert 'No gpx file for this activity (id: 1)' in data['message']
|
||||||
|
|
||||||
|
response = client.get(
|
||||||
|
'/api/activities/1/chart_data',
|
||||||
|
headers=dict(
|
||||||
|
Authorization='Bearer ' + json.loads(
|
||||||
|
resp_login.data.decode()
|
||||||
|
)['auth_token']
|
||||||
|
)
|
||||||
|
)
|
||||||
|
data = json.loads(response.data.decode())
|
||||||
|
|
||||||
def test_get_an_activity_actvity_invalid_gpx(
|
assert response.status_code == 400
|
||||||
|
assert 'fail' in data['status']
|
||||||
|
assert 'No gpx file for this activity (id: 1)' in data['message']
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_an_activity_activity_invalid_gpx(
|
||||||
app, user_1, sport_1_cycling, activity_cycling_user_1
|
app, user_1, sport_1_cycling, activity_cycling_user_1
|
||||||
):
|
):
|
||||||
activity_cycling_user_1.gpx = "some path"
|
activity_cycling_user_1.gpx = "some path"
|
||||||
@ -309,3 +338,18 @@ def test_get_an_activity_actvity_invalid_gpx(
|
|||||||
assert 'error' in data['status']
|
assert 'error' in data['status']
|
||||||
assert 'internal error' in data['message']
|
assert 'internal error' in data['message']
|
||||||
assert data['data']['gpx'] == ''
|
assert data['data']['gpx'] == ''
|
||||||
|
|
||||||
|
response = client.get(
|
||||||
|
'/api/activities/1/chart_data',
|
||||||
|
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 'internal error' in data['message']
|
||||||
|
assert data['data']['chart_data'] == ''
|
||||||
|
@ -189,6 +189,47 @@ def test_get_an_activity_with_gpx(app, user_1, sport_1_cycling, gpx_file):
|
|||||||
assert len(data['data']['gpx']) != ''
|
assert len(data['data']['gpx']) != ''
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_chart_data_activty_with_gpx(
|
||||||
|
app, user_1, sport_1_cycling, gpx_file
|
||||||
|
):
|
||||||
|
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/chart_data',
|
||||||
|
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 data['message'] == ''
|
||||||
|
assert data['data']['chart_data'] != ''
|
||||||
|
|
||||||
|
|
||||||
def test_add_an_activity_with_gpx_without_name(
|
def test_add_an_activity_with_gpx_without_name(
|
||||||
app, user_1, sport_1_cycling, gpx_file_wo_name
|
app, user_1, sport_1_cycling, gpx_file_wo_name
|
||||||
):
|
):
|
||||||
|
Loading…
Reference in New Issue
Block a user