API - add route to get application statistics
This commit is contained in:
parent
0c7cefba13
commit
628f2f3e27
@ -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
|
||||||
|
@ -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">"data"</span><span class="p">:</span> <span class="p">{</span>
|
||||||
|
<span class="nt">"activities"</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
|
||||||
|
<span class="nt">"sports"</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
|
||||||
|
<span class="nt">"users"</span><span class="p">:</span> <span class="mi">2</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>
|
||||||
|
</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>
|
||||||
|
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
BIN
docs/objects.inv
BIN
docs/objects.inv
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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']
|
||||||
|
Loading…
Reference in New Issue
Block a user