API - get gpx and chart data for an activity segment - #14
This commit is contained in:
		@@ -11,4 +11,6 @@ Activities
 | 
				
			|||||||
    activities.delete_activity,
 | 
					    activities.delete_activity,
 | 
				
			||||||
    activities.get_map,
 | 
					    activities.get_map,
 | 
				
			||||||
    activities.get_activity_gpx,
 | 
					    activities.get_activity_gpx,
 | 
				
			||||||
    activities.get_activity_chart_data
 | 
					    activities.get_activity_chart_data,
 | 
				
			||||||
 | 
					    activities.get_segment_gpx,
 | 
				
			||||||
 | 
					    activities.get_segment_chart_data
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -958,6 +958,129 @@
 | 
				
			|||||||
</dl>
 | 
					</dl>
 | 
				
			||||||
</dd></dl>
 | 
					</dd></dl>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<dl class="get">
 | 
				
			||||||
 | 
					<dt id="get--api-activities-(int-activity_id)-gpx-segment-(int-segment_id)">
 | 
				
			||||||
 | 
					<code class="sig-name descname">GET </code><code class="sig-name descname">/api/activities/</code><span class="sig-paren">(</span><em class="property">int: </em><em class="sig-param">activity_id</em><span class="sig-paren">)</span><code class="sig-name descname">/gpx/segment/</code><span class="sig-paren">(</span><em class="property">int: </em><em class="sig-param">segment_id</em><span class="sig-paren">)</span><a class="headerlink" href="#get--api-activities-(int-activity_id)-gpx-segment-(int-segment_id)" title="Permalink to this definition">¶</a></dt>
 | 
				
			||||||
 | 
					<dd><p>Get gpx file for an activity segment displayed on map with Leaflet</p>
 | 
				
			||||||
 | 
					<p><strong>Example request</strong>:</p>
 | 
				
			||||||
 | 
					<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">GET</span> <span class="nn">/api/activities/3/gpx/segment/0</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
				
			||||||
 | 
					<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
				
			||||||
 | 
					</pre></div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<p><strong>Example response</strong>:</p>
 | 
				
			||||||
 | 
					<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">200</span> <span class="ne">OK</span>
 | 
				
			||||||
 | 
					<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<span class="p">{</span>
 | 
				
			||||||
 | 
					  <span class="nt">"data"</span><span class="p">:</span> <span class="p">{</span>
 | 
				
			||||||
 | 
					    <span class="nt">"gpx"</span><span class="p">:</span> <span class="s2">"gpx file content"</span>
 | 
				
			||||||
 | 
					  <span class="p">},</span>
 | 
				
			||||||
 | 
					  <span class="nt">"message"</span><span class="p">:</span> <span class="s2">""</span><span class="p">,</span>
 | 
				
			||||||
 | 
					  <span class="nt">"status"</span><span class="p">:</span> <span class="s2">"success"</span>
 | 
				
			||||||
 | 
					<span class="p">}</span>
 | 
				
			||||||
 | 
					</pre></div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<dl class="field-list simple">
 | 
				
			||||||
 | 
					<dt class="field-odd">Parameters</dt>
 | 
				
			||||||
 | 
					<dd class="field-odd"><ul class="simple">
 | 
				
			||||||
 | 
					<li><p><strong>auth_user_id</strong> (<em>integer</em>) – authenticate user id (from JSON Web Token)</p></li>
 | 
				
			||||||
 | 
					<li><p><strong>activity_id</strong> (<em>integer</em>) – activity id</p></li>
 | 
				
			||||||
 | 
					<li><p><strong>segment_id</strong> (<em>integer</em>) – segment id</p></li>
 | 
				
			||||||
 | 
					</ul>
 | 
				
			||||||
 | 
					</dd>
 | 
				
			||||||
 | 
					<dt class="field-even">Request Headers</dt>
 | 
				
			||||||
 | 
					<dd class="field-even"><ul class="simple">
 | 
				
			||||||
 | 
					<li><p><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a> – OAuth 2.0 Bearer Token</p></li>
 | 
				
			||||||
 | 
					</ul>
 | 
				
			||||||
 | 
					</dd>
 | 
				
			||||||
 | 
					<dt class="field-odd">Status Codes</dt>
 | 
				
			||||||
 | 
					<dd class="field-odd"><ul class="simple">
 | 
				
			||||||
 | 
					<li><p><a class="reference external" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a> – success</p></li>
 | 
				
			||||||
 | 
					<li><p><a class="reference external" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a> – no gpx file for this activity</p></li>
 | 
				
			||||||
 | 
					<li><p><a class="reference external" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a> – <ul>
 | 
				
			||||||
 | 
					<li><p>Provide a valid auth token.</p></li>
 | 
				
			||||||
 | 
					<li><p>Signature expired. Please log in again.</p></li>
 | 
				
			||||||
 | 
					<li><p>Invalid token. Please log in again.</p></li>
 | 
				
			||||||
 | 
					</ul>
 | 
				
			||||||
 | 
					</p></li>
 | 
				
			||||||
 | 
					<li><p><a class="reference external" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5">404 Not Found</a> – activity not found</p></li>
 | 
				
			||||||
 | 
					<li><p><a class="reference external" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1">500 Internal Server Error</a> – </p></li>
 | 
				
			||||||
 | 
					</ul>
 | 
				
			||||||
 | 
					</dd>
 | 
				
			||||||
 | 
					</dl>
 | 
				
			||||||
 | 
					</dd></dl>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<dl class="get">
 | 
				
			||||||
 | 
					<dt id="get--api-activities-(int-activity_id)-chart_data-segment-(int-segment_id)">
 | 
				
			||||||
 | 
					<code class="sig-name descname">GET </code><code class="sig-name descname">/api/activities/</code><span class="sig-paren">(</span><em class="property">int: </em><em class="sig-param">activity_id</em><span class="sig-paren">)</span><code class="sig-name descname">/chart_data/segment/</code><span class="sig-paren">(</span><em class="property">int: </em><em class="sig-param">segment_id</em><span class="sig-paren">)</span><a class="headerlink" href="#get--api-activities-(int-activity_id)-chart_data-segment-(int-segment_id)" title="Permalink to this definition">¶</a></dt>
 | 
				
			||||||
 | 
					<dd><p>Get chart data from an activity gpx file, to display it with Recharts</p>
 | 
				
			||||||
 | 
					<p><strong>Example request</strong>:</p>
 | 
				
			||||||
 | 
					<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">GET</span> <span class="nn">/api/activities/3/chart/segment/0</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
				
			||||||
 | 
					<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
				
			||||||
 | 
					</pre></div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<p><strong>Example response</strong>:</p>
 | 
				
			||||||
 | 
					<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">200</span> <span class="ne">OK</span>
 | 
				
			||||||
 | 
					<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<span class="p">{</span>
 | 
				
			||||||
 | 
					  <span class="nt">"data"</span><span class="p">:</span> <span class="p">{</span>
 | 
				
			||||||
 | 
					    <span class="nt">"chart_data"</span><span class="p">:</span> <span class="p">[</span>
 | 
				
			||||||
 | 
					      <span class="p">{</span>
 | 
				
			||||||
 | 
					        <span class="nt">"distance"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
 | 
				
			||||||
 | 
					        <span class="nt">"duration"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
 | 
				
			||||||
 | 
					        <span class="nt">"elevation"</span><span class="p">:</span> <span class="mf">279.4</span><span class="p">,</span>
 | 
				
			||||||
 | 
					        <span class="nt">"latitude"</span><span class="p">:</span> <span class="mf">51.5078118</span><span class="p">,</span>
 | 
				
			||||||
 | 
					        <span class="nt">"longitude"</span><span class="p">:</span> <span class="mf">-0.1232004</span><span class="p">,</span>
 | 
				
			||||||
 | 
					        <span class="nt">"speed"</span><span class="p">:</span> <span class="mf">8.63</span><span class="p">,</span>
 | 
				
			||||||
 | 
					        <span class="nt">"time"</span><span class="p">:</span> <span class="s2">"Fri, 14 Jul 2017 13:44:03 GMT"</span>
 | 
				
			||||||
 | 
					      <span class="p">},</span>
 | 
				
			||||||
 | 
					      <span class="p">{</span>
 | 
				
			||||||
 | 
					        <span class="nt">"distance"</span><span class="p">:</span> <span class="mf">7.5</span><span class="p">,</span>
 | 
				
			||||||
 | 
					        <span class="nt">"duration"</span><span class="p">:</span> <span class="mi">7380</span><span class="p">,</span>
 | 
				
			||||||
 | 
					        <span class="nt">"elevation"</span><span class="p">:</span> <span class="mi">280</span><span class="p">,</span>
 | 
				
			||||||
 | 
					        <span class="nt">"latitude"</span><span class="p">:</span> <span class="mf">51.5079733</span><span class="p">,</span>
 | 
				
			||||||
 | 
					        <span class="nt">"longitude"</span><span class="p">:</span> <span class="mf">-0.1234538</span><span class="p">,</span>
 | 
				
			||||||
 | 
					        <span class="nt">"speed"</span><span class="p">:</span> <span class="mf">6.39</span><span class="p">,</span>
 | 
				
			||||||
 | 
					        <span class="nt">"time"</span><span class="p">:</span> <span class="s2">"Fri, 14 Jul 2017 15:47:03 GMT"</span>
 | 
				
			||||||
 | 
					      <span class="p">}</span>
 | 
				
			||||||
 | 
					    <span class="p">]</span>
 | 
				
			||||||
 | 
					  <span class="p">},</span>
 | 
				
			||||||
 | 
					  <span class="nt">"message"</span><span class="p">:</span> <span class="s2">""</span><span class="p">,</span>
 | 
				
			||||||
 | 
					  <span class="nt">"status"</span><span class="p">:</span> <span class="s2">"success"</span>
 | 
				
			||||||
 | 
					<span class="p">}</span>
 | 
				
			||||||
 | 
					</pre></div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<dl class="field-list simple">
 | 
				
			||||||
 | 
					<dt class="field-odd">Parameters</dt>
 | 
				
			||||||
 | 
					<dd class="field-odd"><ul class="simple">
 | 
				
			||||||
 | 
					<li><p><strong>auth_user_id</strong> (<em>integer</em>) – authenticate user id (from JSON Web Token)</p></li>
 | 
				
			||||||
 | 
					<li><p><strong>activity_id</strong> (<em>integer</em>) – activity id</p></li>
 | 
				
			||||||
 | 
					<li><p><strong>segment_id</strong> (<em>integer</em>) – segment id</p></li>
 | 
				
			||||||
 | 
					</ul>
 | 
				
			||||||
 | 
					</dd>
 | 
				
			||||||
 | 
					<dt class="field-even">Request Headers</dt>
 | 
				
			||||||
 | 
					<dd class="field-even"><ul class="simple">
 | 
				
			||||||
 | 
					<li><p><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a> – OAuth 2.0 Bearer Token</p></li>
 | 
				
			||||||
 | 
					</ul>
 | 
				
			||||||
 | 
					</dd>
 | 
				
			||||||
 | 
					<dt class="field-odd">Status Codes</dt>
 | 
				
			||||||
 | 
					<dd class="field-odd"><ul class="simple">
 | 
				
			||||||
 | 
					<li><p><a class="reference external" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a> – success</p></li>
 | 
				
			||||||
 | 
					<li><p><a class="reference external" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a> – no gpx file for this activity</p></li>
 | 
				
			||||||
 | 
					<li><p><a class="reference external" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a> – <ul>
 | 
				
			||||||
 | 
					<li><p>Provide a valid auth token.</p></li>
 | 
				
			||||||
 | 
					<li><p>Signature expired. Please log in again.</p></li>
 | 
				
			||||||
 | 
					<li><p>Invalid token. Please log in again.</p></li>
 | 
				
			||||||
 | 
					</ul>
 | 
				
			||||||
 | 
					</p></li>
 | 
				
			||||||
 | 
					<li><p><a class="reference external" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5">404 Not Found</a> – activity not found</p></li>
 | 
				
			||||||
 | 
					<li><p><a class="reference external" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1">500 Internal Server Error</a> – </p></li>
 | 
				
			||||||
 | 
					</ul>
 | 
				
			||||||
 | 
					</dd>
 | 
				
			||||||
 | 
					</dl>
 | 
				
			||||||
 | 
					</dd></dl>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -138,11 +138,21 @@
 | 
				
			|||||||
       <td>
 | 
					       <td>
 | 
				
			||||||
       <a href="api/activities.html#get--api-activities-(int-activity_id)-chart_data"><code class="xref">GET /api/activities/(int:activity_id)/chart_data</code></a></td><td>
 | 
					       <a href="api/activities.html#get--api-activities-(int-activity_id)-chart_data"><code class="xref">GET /api/activities/(int:activity_id)/chart_data</code></a></td><td>
 | 
				
			||||||
       <em></em></td></tr>
 | 
					       <em></em></td></tr>
 | 
				
			||||||
 | 
					     <tr>
 | 
				
			||||||
 | 
					       <td></td>
 | 
				
			||||||
 | 
					       <td>
 | 
				
			||||||
 | 
					       <a href="api/activities.html#get--api-activities-(int-activity_id)-chart_data-segment-(int-segment_id)"><code class="xref">GET /api/activities/(int:activity_id)/chart_data/segment/(int:segment_id)</code></a></td><td>
 | 
				
			||||||
 | 
					       <em></em></td></tr>
 | 
				
			||||||
     <tr>
 | 
					     <tr>
 | 
				
			||||||
       <td></td>
 | 
					       <td></td>
 | 
				
			||||||
       <td>
 | 
					       <td>
 | 
				
			||||||
       <a href="api/activities.html#get--api-activities-(int-activity_id)-gpx"><code class="xref">GET /api/activities/(int:activity_id)/gpx</code></a></td><td>
 | 
					       <a href="api/activities.html#get--api-activities-(int-activity_id)-gpx"><code class="xref">GET /api/activities/(int:activity_id)/gpx</code></a></td><td>
 | 
				
			||||||
       <em></em></td></tr>
 | 
					       <em></em></td></tr>
 | 
				
			||||||
 | 
					     <tr>
 | 
				
			||||||
 | 
					       <td></td>
 | 
				
			||||||
 | 
					       <td>
 | 
				
			||||||
 | 
					       <a href="api/activities.html#get--api-activities-(int-activity_id)-gpx-segment-(int-segment_id)"><code class="xref">GET /api/activities/(int:activity_id)/gpx/segment/(int:segment_id)</code></a></td><td>
 | 
				
			||||||
 | 
					       <em></em></td></tr>
 | 
				
			||||||
     <tr>
 | 
					     <tr>
 | 
				
			||||||
       <td></td>
 | 
					       <td></td>
 | 
				
			||||||
       <td>
 | 
					       <td>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								docs/objects.inv
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/objects.inv
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -11,4 +11,6 @@ Activities
 | 
				
			|||||||
    activities.delete_activity,
 | 
					    activities.delete_activity,
 | 
				
			||||||
    activities.get_map,
 | 
					    activities.get_map,
 | 
				
			||||||
    activities.get_activity_gpx,
 | 
					    activities.get_activity_gpx,
 | 
				
			||||||
    activities.get_activity_chart_data
 | 
					    activities.get_activity_chart_data,
 | 
				
			||||||
 | 
					    activities.get_segment_gpx,
 | 
				
			||||||
 | 
					    activities.get_segment_chart_data
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,9 @@ from .utils import (
 | 
				
			|||||||
    get_datetime_with_tz, process_files
 | 
					    get_datetime_with_tz, process_files
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from .utils_format import convert_in_duration
 | 
					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__)
 | 
					activities_blueprint = Blueprint('activities', __name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -345,7 +347,7 @@ def get_activity(auth_user_id, activity_id):
 | 
				
			|||||||
    return jsonify(response_object), code
 | 
					    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"""
 | 
					    """Get data from an activity gpx file"""
 | 
				
			||||||
    activity = Activity.query.filter_by(id=activity_id).first()
 | 
					    activity = Activity.query.filter_by(id=activity_id).first()
 | 
				
			||||||
    content = ''
 | 
					    content = ''
 | 
				
			||||||
@@ -364,10 +366,21 @@ def get_activity_data(auth_user_id, activity_id, data_type):
 | 
				
			|||||||
        try:
 | 
					        try:
 | 
				
			||||||
            absolute_gpx_filepath = get_absolute_file_path(activity.gpx)
 | 
					            absolute_gpx_filepath = get_absolute_file_path(activity.gpx)
 | 
				
			||||||
            if data_type == 'chart':
 | 
					            if data_type == 'chart':
 | 
				
			||||||
                content = get_chart_data(absolute_gpx_filepath)
 | 
					                content = get_chart_data(absolute_gpx_filepath, segment_id)
 | 
				
			||||||
            else:  # data_type == 'gpx'
 | 
					            else:  # data_type == 'gpx'
 | 
				
			||||||
                with open(absolute_gpx_filepath, encoding='utf-8') as f:
 | 
					                with open(absolute_gpx_filepath, encoding='utf-8') as f:
 | 
				
			||||||
                    content = f.read()
 | 
					                    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:
 | 
					        except Exception as e:
 | 
				
			||||||
            appLog.error(e)
 | 
					            appLog.error(e)
 | 
				
			||||||
            response_object = {'status': 'error',
 | 
					            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')
 | 
					    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'])
 | 
					@activities_blueprint.route('/activities/map/<map_id>', methods=['GET'])
 | 
				
			||||||
def get_map(map_id):
 | 
					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
 | 
					    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)
 | 
					    gpx = open_gpx_file(gpx_file)
 | 
				
			||||||
    if gpx is None:
 | 
					    if gpx is None:
 | 
				
			||||||
        return None
 | 
					        return None
 | 
				
			||||||
@@ -131,7 +146,10 @@ def get_chart_data(gpx_file):
 | 
				
			|||||||
    previous_point = None
 | 
					    previous_point = None
 | 
				
			||||||
    previous_distance = 0
 | 
					    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):
 | 
					        for point_idx, point in enumerate(segment.points):
 | 
				
			||||||
            if segment_idx == 0 and point_idx == 0:
 | 
					            if segment_idx == 0 and point_idx == 0:
 | 
				
			||||||
                first_point = point
 | 
					                first_point = point
 | 
				
			||||||
@@ -161,3 +179,29 @@ def get_chart_data(gpx_file):
 | 
				
			|||||||
            previous_distance = distance
 | 
					            previous_distance = distance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return chart_data
 | 
					    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 '' in data['message']
 | 
				
			||||||
    assert len(data['data']['gpx']) != ''
 | 
					    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(
 | 
					    response = client.get(
 | 
				
			||||||
        f'/api/activities/map/{map_id}',
 | 
					        f'/api/activities/map/{map_id}',
 | 
				
			||||||
        headers=dict(
 | 
					        headers=dict(
 | 
				
			||||||
@@ -419,6 +434,36 @@ def test_get_chart_data_activty_with_gpx(
 | 
				
			|||||||
    assert data['message'] == ''
 | 
					    assert data['message'] == ''
 | 
				
			||||||
    assert data['data']['chart_data'] != ''
 | 
					    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(
 | 
					def test_get_chart_data_activty_with_gpx_different_user(
 | 
				
			||||||
        app, user_1, user_2, sport_1_cycling, gpx_file
 | 
					        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 'error' in data['status']
 | 
				
			||||||
    assert 'You do not have permissions.' in data['message']
 | 
					    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(
 | 
					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
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user