API - add route to get application statistics

This commit is contained in:
Sam 2019-09-23 20:30:34 +02:00
parent 0c7cefba13
commit 628f2f3e27
8 changed files with 204 additions and 5 deletions

View File

@ -4,4 +4,5 @@ Statistics
.. autoflask:: fittrackee_api:create_app() .. autoflask:: fittrackee_api:create_app()
:endpoints: :endpoints:
stats.get_activities_by_sport, stats.get_activities_by_sport,
stats.get_activities_by_time stats.get_activities_by_time,
stats.get_application_stats

View File

@ -333,6 +333,54 @@
</dl> </dl>
</dd></dl> </dd></dl>
<dl class="get">
<dt id="get--api-stats-all">
<code class="sig-name descname">GET </code><code class="sig-name descname">/api/stats/all</code><a class="headerlink" href="#get--api-stats-all" title="Permalink to this definition"></a></dt>
<dd><p>Get all application statistics</p>
<p><strong>Example requests</strong>:</p>
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">GET</span> <span class="nn">/api/stats/all</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
</pre></div>
</div>
<p><strong>Example responses</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">&quot;data&quot;</span><span class="p">:</span> <span class="p">{</span>
<span class="nt">&quot;activities&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
<span class="nt">&quot;sports&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
<span class="nt">&quot;users&quot;</span><span class="p">:</span> <span class="mi">2</span>
<span class="p">},</span>
<span class="nt">&quot;status&quot;</span><span class="p">:</span> <span class="s2">&quot;success&quot;</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>
</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.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.4">403 Forbidden</a> You do not have permissions.</p></li>
</ul>
</dd>
</dl>
</dd></dl>
</div> </div>

View File

@ -199,6 +199,11 @@
<td> <td>
<a href="api/stats.html#get--api-stats-(int-user_id)-by_time"><code class="xref">GET /api/stats/(int:user_id)/by_time</code></a></td><td> <a href="api/stats.html#get--api-stats-(int-user_id)-by_time"><code class="xref">GET /api/stats/(int:user_id)/by_time</code></a></td><td>
<em></em></td></tr> <em></em></td></tr>
<tr>
<td></td>
<td>
<a href="api/stats.html#get--api-stats-all"><code class="xref">GET /api/stats/all</code></a></td><td>
<em></em></td></tr>
<tr> <tr>
<td></td> <td></td>
<td> <td>

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -4,4 +4,5 @@ Statistics
.. autoflask:: fittrackee_api:create_app() .. autoflask:: fittrackee_api:create_app()
:endpoints: :endpoints:
stats.get_activities_by_sport, stats.get_activities_by_sport,
stats.get_activities_by_time stats.get_activities_by_time,
stats.get_application_stats

View File

@ -1,10 +1,11 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from fittrackee_api import appLog from fittrackee_api import appLog, db
from flask import Blueprint, jsonify, request from flask import Blueprint, jsonify, request
from sqlalchemy import func
from ..users.models import User from ..users.models import User
from ..users.utils import authenticate from ..users.utils import authenticate, authenticate_as_admin
from .models import Activity, Sport from .models import Activity, Sport
from .utils import get_datetime_with_tz from .utils import get_datetime_with_tz
from .utils_format import convert_timedelta_to_integer from .utils_format import convert_timedelta_to_integer
@ -319,3 +320,62 @@ def get_activities_by_sport(auth_user_id, user_id):
""" """
return get_activities(user_id, 'by_sport') return get_activities(user_id, 'by_sport')
@stats_blueprint.route('/stats/all', methods=['GET'])
@authenticate_as_admin
def get_application_stats(auth_user_id):
"""
Get all application statistics
**Example requests**:
.. sourcecode:: http
GET /api/stats/all HTTP/1.1
**Example responses**:
.. sourcecode:: http
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": {
"activities": 3,
"sports": 3,
"users": 2
},
"status": "success"
}
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:reqheader Authorization: OAuth 2.0 Bearer Token
:statuscode 200: success
:statuscode 401:
- Provide a valid auth token.
- Signature expired. Please log in again.
- Invalid token. Please log in again.
:statuscode 403: You do not have permissions.
"""
nb_activities = Activity.query.filter().count()
nb_users = User.query.filter().count()
nb_sports = (
db.session.query(func.count(Activity.sport_id))
.group_by(Activity.sport_id)
.count()
)
response_object = {
'status': 'success',
'data': {
'activities': nb_activities,
'sports': nb_sports,
'users': nb_users,
},
}
return jsonify(response_object), 200

View File

@ -947,3 +947,87 @@ def test_get_stats_by_sport_all_activities_error(
'Error. Please try again or contact the administrator.' 'Error. Please try again or contact the administrator.'
in data['message'] in data['message']
) )
def test_get_app_stats_without_activities(app, user_1_admin):
client = app.test_client()
resp_login = client.post(
'/api/auth/login',
data=json.dumps(dict(email='admin@example.com', password='12345678')),
content_type='application/json',
)
response = client.get(
'/api/stats/all',
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['data'] == {'activities': 0, 'sports': 0, 'users': 1}
def test_get_app_stats_with_activities(
app,
user_1_admin,
user_2,
user_3,
sport_1_cycling,
sport_2_running,
activity_cycling_user_1,
activity_cycling_user_2,
activity_running_user_1,
):
client = app.test_client()
resp_login = client.post(
'/api/auth/login',
data=json.dumps(dict(email='admin@example.com', password='12345678')),
content_type='application/json',
)
response = client.get(
'/api/stats/all',
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['data'] == {'activities': 3, 'sports': 2, 'users': 3}
def test_get_app_stats_no_admin(
app,
user_1,
user_2,
user_3,
sport_1_cycling,
sport_2_running,
activity_cycling_user_1,
activity_cycling_user_2,
activity_running_user_1,
):
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/stats/all',
headers=dict(
Authorization='Bearer '
+ json.loads(resp_login.data.decode())['auth_token']
),
)
data = json.loads(response.data.decode())
assert response.status_code == 403
assert 'success' not in data['status']
assert 'error' in data['status']
assert 'You do not have permissions.' in data['message']