API - get gpx and chart data for an activity segment - #14
This commit is contained in:
@ -16,7 +16,9 @@ from .utils import (
|
||||
get_datetime_with_tz, process_files
|
||||
)
|
||||
from .utils_format import convert_in_duration
|
||||
from .utils_gpx import get_chart_data
|
||||
from .utils_gpx import (
|
||||
ActivityGPXException, extract_segment_from_gpx_file, get_chart_data
|
||||
)
|
||||
|
||||
activities_blueprint = Blueprint('activities', __name__)
|
||||
|
||||
@ -345,7 +347,7 @@ def get_activity(auth_user_id, activity_id):
|
||||
return jsonify(response_object), code
|
||||
|
||||
|
||||
def get_activity_data(auth_user_id, activity_id, data_type):
|
||||
def get_activity_data(auth_user_id, activity_id, data_type, segment_id=None):
|
||||
"""Get data from an activity gpx file"""
|
||||
activity = Activity.query.filter_by(id=activity_id).first()
|
||||
content = ''
|
||||
@ -364,10 +366,21 @@ def get_activity_data(auth_user_id, activity_id, data_type):
|
||||
try:
|
||||
absolute_gpx_filepath = get_absolute_file_path(activity.gpx)
|
||||
if data_type == 'chart':
|
||||
content = get_chart_data(absolute_gpx_filepath)
|
||||
content = get_chart_data(absolute_gpx_filepath, segment_id)
|
||||
else: # data_type == 'gpx'
|
||||
with open(absolute_gpx_filepath, encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
if segment_id is not None:
|
||||
content = extract_segment_from_gpx_file(
|
||||
content,
|
||||
segment_id
|
||||
)
|
||||
except ActivityGPXException as e:
|
||||
appLog.error(e.message)
|
||||
response_object = {'status': e.status,
|
||||
'message': e.message}
|
||||
code = 404 if e.status == 'not found' else 500
|
||||
return jsonify(response_object), code
|
||||
except Exception as e:
|
||||
appLog.error(e)
|
||||
response_object = {'status': 'error',
|
||||
@ -505,6 +518,125 @@ def get_activity_chart_data(auth_user_id, activity_id):
|
||||
return get_activity_data(auth_user_id, activity_id, 'chart')
|
||||
|
||||
|
||||
@activities_blueprint.route(
|
||||
'/activities/<int:activity_id>/gpx/segment/<int:segment_id>',
|
||||
methods=['GET']
|
||||
)
|
||||
@authenticate
|
||||
def get_segment_gpx(auth_user_id, activity_id, segment_id):
|
||||
"""
|
||||
Get gpx file for an activity segment displayed on map with Leaflet
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/activities/3/gpx/segment/0 HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"gpx": "gpx file content"
|
||||
},
|
||||
"message": "",
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
:param integer auth_user_id: authenticate user id (from JSON Web Token)
|
||||
:param integer activity_id: activity id
|
||||
:param integer segment_id: segment id
|
||||
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 200: success
|
||||
:statuscode 400: no gpx file for this activity
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 404: activity not found
|
||||
:statuscode 500:
|
||||
|
||||
"""
|
||||
return get_activity_data(auth_user_id, activity_id, 'gpx', segment_id)
|
||||
|
||||
|
||||
@activities_blueprint.route(
|
||||
'/activities/<int:activity_id>/chart_data/segment/<int:segment_id>',
|
||||
methods=['GET']
|
||||
)
|
||||
@authenticate
|
||||
def get_segment_chart_data(auth_user_id, activity_id, segment_id):
|
||||
"""
|
||||
Get chart data from an activity gpx file, to display it with Recharts
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/activities/3/chart/segment/0 HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"chart_data": [
|
||||
{
|
||||
"distance": 0,
|
||||
"duration": 0,
|
||||
"elevation": 279.4,
|
||||
"latitude": 51.5078118,
|
||||
"longitude": -0.1232004,
|
||||
"speed": 8.63,
|
||||
"time": "Fri, 14 Jul 2017 13:44:03 GMT"
|
||||
},
|
||||
{
|
||||
"distance": 7.5,
|
||||
"duration": 7380,
|
||||
"elevation": 280,
|
||||
"latitude": 51.5079733,
|
||||
"longitude": -0.1234538,
|
||||
"speed": 6.39,
|
||||
"time": "Fri, 14 Jul 2017 15:47:03 GMT"
|
||||
}
|
||||
]
|
||||
},
|
||||
"message": "",
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
:param integer auth_user_id: authenticate user id (from JSON Web Token)
|
||||
:param integer activity_id: activity id
|
||||
:param integer segment_id: segment id
|
||||
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 200: success
|
||||
:statuscode 400: no gpx file for this activity
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 404: activity not found
|
||||
:statuscode 500:
|
||||
|
||||
"""
|
||||
return get_activity_data(auth_user_id, activity_id, 'chart', segment_id)
|
||||
|
||||
|
||||
@activities_blueprint.route('/activities/map/<map_id>', methods=['GET'])
|
||||
def get_map(map_id):
|
||||
"""
|
||||
|
@ -121,7 +121,22 @@ def get_gpx_info(gpx_file, update_map_data=True, update_weather_data=True):
|
||||
return gpx_data, map_data, weather_data
|
||||
|
||||
|
||||
def get_chart_data(gpx_file):
|
||||
def get_gpx_segments(track_segments, segment_id=None):
|
||||
if segment_id is not None:
|
||||
if segment_id > (len(track_segments) - 1):
|
||||
raise ActivityGPXException(
|
||||
'not found',
|
||||
f'No segment with id \'{segment_id}\'',
|
||||
None
|
||||
)
|
||||
segments = [track_segments[segment_id]]
|
||||
else:
|
||||
segments = track_segments
|
||||
|
||||
return segments
|
||||
|
||||
|
||||
def get_chart_data(gpx_file, segment_id=None):
|
||||
gpx = open_gpx_file(gpx_file)
|
||||
if gpx is None:
|
||||
return None
|
||||
@ -131,7 +146,10 @@ def get_chart_data(gpx_file):
|
||||
previous_point = None
|
||||
previous_distance = 0
|
||||
|
||||
for segment_idx, segment in enumerate(gpx.tracks[0].segments):
|
||||
track_segments = gpx.tracks[0].segments
|
||||
segments = get_gpx_segments(track_segments, segment_id)
|
||||
|
||||
for segment_idx, segment in enumerate(segments):
|
||||
for point_idx, point in enumerate(segment.points):
|
||||
if segment_idx == 0 and point_idx == 0:
|
||||
first_point = point
|
||||
@ -161,3 +179,29 @@ def get_chart_data(gpx_file):
|
||||
previous_distance = distance
|
||||
|
||||
return chart_data
|
||||
|
||||
|
||||
def extract_segment_from_gpx_file(content, segment_id):
|
||||
gpx_content = gpxpy.parse(content)
|
||||
if len(gpx_content.tracks) == 0:
|
||||
return None
|
||||
|
||||
track_segment = get_gpx_segments(
|
||||
gpx_content.tracks[0].segments,
|
||||
segment_id
|
||||
)
|
||||
|
||||
gpx = gpxpy.gpx.GPX()
|
||||
gpx_track = gpxpy.gpx.GPXTrack()
|
||||
gpx.tracks.append(gpx_track)
|
||||
gpx_segment = gpxpy.gpx.GPXTrackSegment()
|
||||
gpx_track.segments.append(gpx_segment)
|
||||
|
||||
for point_idx, point in enumerate(track_segment[0].points):
|
||||
gpx_segment.points.append(
|
||||
gpxpy.gpx.GPXTrackPoint(
|
||||
point.latitude,
|
||||
point.longitude,
|
||||
elevation=point.elevation))
|
||||
|
||||
return gpx.to_xml()
|
||||
|
@ -274,6 +274,21 @@ def activity_assertion(app, user_1, sport_1_cycling, gpx_file, with_segments):
|
||||
assert '' in data['message']
|
||||
assert len(data['data']['gpx']) != ''
|
||||
|
||||
response = client.get(
|
||||
'/api/activities/1/gpx/segment/0',
|
||||
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 '' in data['message']
|
||||
assert len(data['data']['gpx']) != ''
|
||||
|
||||
response = client.get(
|
||||
f'/api/activities/map/{map_id}',
|
||||
headers=dict(
|
||||
@ -419,6 +434,36 @@ def test_get_chart_data_activty_with_gpx(
|
||||
assert data['message'] == ''
|
||||
assert data['data']['chart_data'] != ''
|
||||
|
||||
response = client.get(
|
||||
'/api/activities/1/chart_data/segment/0',
|
||||
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'] != ''
|
||||
|
||||
response = client.get(
|
||||
'/api/activities/1/chart_data/segment/999999',
|
||||
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 data['message'] == 'No segment with id \'999999\''
|
||||
assert 'data' not in data
|
||||
|
||||
|
||||
def test_get_chart_data_activty_with_gpx_different_user(
|
||||
app, user_1, user_2, sport_1_cycling, gpx_file
|
||||
@ -467,6 +512,34 @@ def test_get_chart_data_activty_with_gpx_different_user(
|
||||
assert 'error' in data['status']
|
||||
assert 'You do not have permissions.' in data['message']
|
||||
|
||||
response = client.get(
|
||||
'/api/activities/1/chart_data/segment/0',
|
||||
headers=dict(
|
||||
Authorization='Bearer ' + json.loads(
|
||||
resp_login.data.decode()
|
||||
)['auth_token']
|
||||
)
|
||||
)
|
||||
data = json.loads(response.data.decode())
|
||||
|
||||
assert response.status_code == 403
|
||||
assert 'error' in data['status']
|
||||
assert 'You do not have permissions.' in data['message']
|
||||
|
||||
response = client.get(
|
||||
'/api/activities/1/chart_data/segment/999999',
|
||||
headers=dict(
|
||||
Authorization='Bearer ' + json.loads(
|
||||
resp_login.data.decode()
|
||||
)['auth_token']
|
||||
)
|
||||
)
|
||||
data = json.loads(response.data.decode())
|
||||
|
||||
assert response.status_code == 403
|
||||
assert 'error' in data['status']
|
||||
assert 'You do not have permissions.' in data['message']
|
||||
|
||||
|
||||
def test_add_an_activity_with_gpx_without_name(
|
||||
app, user_1, sport_1_cycling, gpx_file_wo_name
|
||||
|
Reference in New Issue
Block a user