API & Client - display only active sports when adding an activity

This commit is contained in:
Sam 2019-09-23 14:09:26 +02:00
parent a9cbe220ac
commit 8a4b114af8
13 changed files with 413 additions and 101 deletions

View File

@ -135,6 +135,9 @@
</pre></div>
</div>
<p><strong>Example response</strong>:</p>
<ul class="simple">
<li><p>for non admin user :</p></li>
</ul>
<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>
@ -142,42 +145,93 @@
<span class="nt">&quot;data&quot;</span><span class="p">:</span> <span class="p">{</span>
<span class="nt">&quot;sports&quot;</span><span class="p">:</span> <span class="p">[</span>
<span class="p">{</span>
<span class="nt">&quot;_can_be_disabled&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="nt">&quot;img&quot;</span><span class="p">:</span> <span class="s2">&quot;/img/sports/cycling-sport.png&quot;</span><span class="p">,</span>
<span class="nt">&quot;is_active&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nt">&quot;label&quot;</span><span class="p">:</span> <span class="s2">&quot;Cycling (Sport)&quot;</span>
<span class="p">},</span>
<span class="p">{</span>
<span class="nt">&quot;_can_be_disabled&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
<span class="nt">&quot;img&quot;</span><span class="p">:</span> <span class="s2">&quot;/img/sports/cycling-transport.png&quot;</span><span class="p">,</span>
<span class="nt">&quot;is_active&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nt">&quot;label&quot;</span><span class="p">:</span> <span class="s2">&quot;Cycling (Transport)&quot;</span>
<span class="p">},</span>
<span class="p">{</span>
<span class="nt">&quot;_can_be_disabled&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
<span class="nt">&quot;img&quot;</span><span class="p">:</span> <span class="s2">&quot;/img/sports/hiking.png&quot;</span><span class="p">,</span>
<span class="nt">&quot;is_active&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nt">&quot;label&quot;</span><span class="p">:</span> <span class="s2">&quot;Hiking&quot;</span>
<span class="p">},</span>
<span class="p">{</span>
<span class="nt">&quot;_can_be_disabled&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span>
<span class="nt">&quot;img&quot;</span><span class="p">:</span> <span class="s2">&quot;/img/sports/mountain-biking.png&quot;</span><span class="p">,</span>
<span class="nt">&quot;is_active&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nt">&quot;label&quot;</span><span class="p">:</span> <span class="s2">&quot;Mountain Biking&quot;</span>
<span class="p">},</span>
<span class="p">{</span>
<span class="nt">&quot;_can_be_disabled&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span>
<span class="nt">&quot;img&quot;</span><span class="p">:</span> <span class="s2">&quot;/img/sports/running.png&quot;</span><span class="p">,</span>
<span class="nt">&quot;is_active&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nt">&quot;label&quot;</span><span class="p">:</span> <span class="s2">&quot;Running&quot;</span>
<span class="p">},</span>
<span class="p">{</span>
<span class="nt">&quot;_can_be_disabled&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">6</span><span class="p">,</span>
<span class="nt">&quot;img&quot;</span><span class="p">:</span> <span class="s2">&quot;/img/sports/walking.png&quot;</span><span class="p">,</span>
<span class="nt">&quot;is_active&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nt">&quot;label&quot;</span><span class="p">:</span> <span class="s2">&quot;Walking&quot;</span>
<span class="p">}</span>
<span class="p">]</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>
<ul class="simple">
<li><p>for admin user :</p></li>
</ul>
<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;sports&quot;</span><span class="p">:</span> <span class="p">[</span>
<span class="p">{</span>
<span class="nt">&quot;has_activities&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="nt">&quot;img&quot;</span><span class="p">:</span> <span class="s2">&quot;/img/sports/cycling-sport.png&quot;</span><span class="p">,</span>
<span class="nt">&quot;is_active&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nt">&quot;label&quot;</span><span class="p">:</span> <span class="s2">&quot;Cycling (Sport)&quot;</span>
<span class="p">},</span>
<span class="p">{</span>
<span class="nt">&quot;has_activities&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
<span class="nt">&quot;img&quot;</span><span class="p">:</span> <span class="s2">&quot;/img/sports/cycling-transport.png&quot;</span><span class="p">,</span>
<span class="nt">&quot;is_active&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nt">&quot;label&quot;</span><span class="p">:</span> <span class="s2">&quot;Cycling (Transport)&quot;</span>
<span class="p">},</span>
<span class="p">{</span>
<span class="nt">&quot;has_activities&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
<span class="nt">&quot;img&quot;</span><span class="p">:</span> <span class="s2">&quot;/img/sports/hiking.png&quot;</span><span class="p">,</span>
<span class="nt">&quot;is_active&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nt">&quot;label&quot;</span><span class="p">:</span> <span class="s2">&quot;Hiking&quot;</span>
<span class="p">},</span>
<span class="p">{</span>
<span class="nt">&quot;has_activities&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span>
<span class="nt">&quot;img&quot;</span><span class="p">:</span> <span class="s2">&quot;/img/sports/mountain-biking.png&quot;</span><span class="p">,</span>
<span class="nt">&quot;is_active&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nt">&quot;label&quot;</span><span class="p">:</span> <span class="s2">&quot;Mountain Biking&quot;</span>
<span class="p">},</span>
<span class="p">{</span>
<span class="nt">&quot;has_activities&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span>
<span class="nt">&quot;img&quot;</span><span class="p">:</span> <span class="s2">&quot;/img/sports/running.png&quot;</span><span class="p">,</span>
<span class="nt">&quot;is_active&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nt">&quot;label&quot;</span><span class="p">:</span> <span class="s2">&quot;Running&quot;</span>
<span class="p">},</span>
<span class="p">{</span>
<span class="nt">&quot;has_activities&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">6</span><span class="p">,</span>
<span class="nt">&quot;img&quot;</span><span class="p">:</span> <span class="s2">&quot;/img/sports/walking.png&quot;</span><span class="p">,</span>
<span class="nt">&quot;is_active&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
@ -225,7 +279,7 @@
</div>
<p><strong>Example response</strong>:</p>
<ul class="simple">
<li><p>success</p></li>
<li><p>success for non admin user :</p></li>
</ul>
<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>
@ -234,7 +288,28 @@
<span class="nt">&quot;data&quot;</span><span class="p">:</span> <span class="p">{</span>
<span class="nt">&quot;sports&quot;</span><span class="p">:</span> <span class="p">[</span>
<span class="p">{</span>
<span class="nt">&quot;_can_be_disabled&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="nt">&quot;img&quot;</span><span class="p">:</span> <span class="s2">&quot;/img/sports/cycling-sport.png&quot;</span><span class="p">,</span>
<span class="nt">&quot;is_active&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nt">&quot;label&quot;</span><span class="p">:</span> <span class="s2">&quot;Cycling (Sport)&quot;</span>
<span class="p">}</span>
<span class="p">]</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>
<ul class="simple">
<li><p>success for admin user :</p></li>
</ul>
<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;sports&quot;</span><span class="p">:</span> <span class="p">[</span>
<span class="p">{</span>
<span class="nt">&quot;has_activities&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="nt">&quot;img&quot;</span><span class="p">:</span> <span class="s2">&quot;/img/sports/cycling-sport.png&quot;</span><span class="p">,</span>
<span class="nt">&quot;is_active&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
@ -290,7 +365,8 @@
<dl class="patch">
<dt id="patch--api-sports-(int-sport_id)">
<code class="sig-name descname">PATCH </code><code class="sig-name descname">/api/sports/</code><span class="sig-paren">(</span><em class="property">int: </em><em class="sig-param">sport_id</em><span class="sig-paren">)</span><a class="headerlink" href="#patch--api-sports-(int-sport_id)" title="Permalink to this definition"></a></dt>
<dd><p>Update a sport</p>
<dd><p>Update a sport
Authenticated user must be an admin</p>
<p><strong>Example request</strong>:</p>
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">PATCH</span> <span class="nn">/api/sports/1</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>
@ -307,7 +383,7 @@
<span class="nt">&quot;data&quot;</span><span class="p">:</span> <span class="p">{</span>
<span class="nt">&quot;sports&quot;</span><span class="p">:</span> <span class="p">[</span>
<span class="p">{</span>
<span class="nt">&quot;_can_be_disabled&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="nt">&quot;has_activities&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="nt">&quot;img&quot;</span><span class="p">:</span> <span class="s2">&quot;/img/sports/cycling-sport.png&quot;</span><span class="p">,</span>
<span class="nt">&quot;is_active&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
@ -360,6 +436,7 @@
<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>
<li><p><a class="reference external" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5">404 Not Found</a> sport 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>

File diff suppressed because one or more lines are too long

View File

@ -74,16 +74,16 @@ class Sport(db.Model):
def __init__(self, label):
self.label = label
def serialize(self):
return {
def serialize(self, is_admin=False):
serialized_sport = {
'id': self.id,
'label': self.label,
'img': self.img,
'is_active': self.is_active,
'_can_be_disabled': not (
len(self.activities) > 0 and self.is_active
),
}
if is_admin:
serialized_sport['has_activities'] = len(self.activities) > 0
return serialized_sport
class Activity(db.Model):

View File

@ -2,6 +2,7 @@ from fittrackee_api import appLog, db
from flask import Blueprint, jsonify, request
from sqlalchemy import exc
from ..users.models import User
from ..users.utils import authenticate, authenticate_as_admin
from .models import Sport
@ -23,6 +24,8 @@ def get_sports(auth_user_id):
**Example response**:
- for non admin user :
.. sourcecode:: http
HTTP/1.1 200 OK
@ -32,42 +35,93 @@ def get_sports(auth_user_id):
"data": {
"sports": [
{
"_can_be_disabled": false,
"id": 1,
"img": "/img/sports/cycling-sport.png",
"is_active": true,
"label": "Cycling (Sport)"
},
{
"_can_be_disabled": false,
"id": 2,
"img": "/img/sports/cycling-transport.png",
"is_active": true,
"label": "Cycling (Transport)"
},
{
"_can_be_disabled": false,
"id": 3,
"img": "/img/sports/hiking.png",
"is_active": true,
"label": "Hiking"
},
{
"_can_be_disabled": false,
"id": 4,
"img": "/img/sports/mountain-biking.png",
"is_active": true,
"label": "Mountain Biking"
},
{
"_can_be_disabled": false,
"id": 5,
"img": "/img/sports/running.png",
"is_active": true,
"label": "Running"
},
{
"_can_be_disabled": false,
"id": 6,
"img": "/img/sports/walking.png",
"is_active": true,
"label": "Walking"
}
]
},
"status": "success"
}
- for admin user :
.. sourcecode:: http
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": {
"sports": [
{
"has_activities": true,
"id": 1,
"img": "/img/sports/cycling-sport.png",
"is_active": true,
"label": "Cycling (Sport)"
},
{
"has_activities": false,
"id": 2,
"img": "/img/sports/cycling-transport.png",
"is_active": true,
"label": "Cycling (Transport)"
},
{
"has_activities": false,
"id": 3,
"img": "/img/sports/hiking.png",
"is_active": true,
"label": "Hiking"
},
{
"has_activities": false,
"id": 4,
"img": "/img/sports/mountain-biking.png",
"is_active": true,
"label": "Mountain Biking"
},
{
"has_activities": false,
"id": 5,
"img": "/img/sports/running.png",
"is_active": true,
"label": "Running"
},
{
"has_activities": false,
"id": 6,
"img": "/img/sports/walking.png",
"is_active": true,
@ -90,10 +144,11 @@ def get_sports(auth_user_id):
"""
user = User.query.filter_by(id=int(auth_user_id)).first()
sports = Sport.query.order_by(Sport.id).all()
response_object = {
'status': 'success',
'data': {'sports': [sport.serialize() for sport in sports]},
'data': {'sports': [sport.serialize(user.admin) for sport in sports]},
}
return jsonify(response_object), 200
@ -101,7 +156,8 @@ def get_sports(auth_user_id):
@sports_blueprint.route('/sports/<int:sport_id>', methods=['GET'])
@authenticate
def get_sport(auth_user_id, sport_id):
"""Get a sport
"""
Get a sport
**Example request**:
@ -112,7 +168,7 @@ def get_sport(auth_user_id, sport_id):
**Example response**:
- success
- success for non admin user :
.. sourcecode:: http
@ -123,7 +179,28 @@ def get_sport(auth_user_id, sport_id):
"data": {
"sports": [
{
"_can_be_disabled": false,
"id": 1,
"img": "/img/sports/cycling-sport.png",
"is_active": true,
"label": "Cycling (Sport)"
}
]
},
"status": "success"
}
- success for admin user :
.. sourcecode:: http
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": {
"sports": [
{
"has_activities": false,
"id": 1,
"img": "/img/sports/cycling-sport.png",
"is_active": true,
@ -162,11 +239,12 @@ def get_sport(auth_user_id, sport_id):
"""
user = User.query.filter_by(id=int(auth_user_id)).first()
sport = Sport.query.filter_by(id=sport_id).first()
if sport:
response_object = {
'status': 'success',
'data': {'sports': [sport.serialize()]},
'data': {'sports': [sport.serialize(user.admin)]},
}
code = 200
else:
@ -178,7 +256,9 @@ def get_sport(auth_user_id, sport_id):
@sports_blueprint.route('/sports/<int:sport_id>', methods=['PATCH'])
@authenticate_as_admin
def update_sport(auth_user_id, sport_id):
"""Update a sport
"""
Update a sport
Authenticated user must be an admin
**Example request**:
@ -200,7 +280,7 @@ def update_sport(auth_user_id, sport_id):
"data": {
"sports": [
{
"_can_be_disabled": false,
"has_activities": false,
"id": 1,
"img": "/img/sports/cycling-sport.png",
"is_active": false,
@ -238,6 +318,7 @@ def update_sport(auth_user_id, sport_id):
- Provide a valid auth token.
- Signature expired. Please log in again.
- Invalid token. Please log in again.
:statuscode 403: You do not have permissions.
:statuscode 404: sport not found
:statuscode 500:
@ -250,24 +331,13 @@ def update_sport(auth_user_id, sport_id):
try:
sport = Sport.query.filter_by(id=sport_id).first()
if sport:
if not (
not sport_data.get('is_active')
and sport.is_active
and len(sport.activities) > 0
):
sport.is_active = sport_data.get('is_active')
db.session.commit()
response_object = {
'status': 'success',
'data': {'sports': [sport.serialize()]},
}
code = 200
else:
response_object = {
'status': 'fail',
'message': 'Sport can not be disabled, activities exist.',
}
code = 400
sport.is_active = sport_data.get('is_active')
db.session.commit()
response_object = {
'status': 'success',
'data': {'sports': [sport.serialize(True)]},
}
code = 200
else:
response_object = {'status': 'not found', 'data': {'sports': []}}
code = 404

View File

@ -107,6 +107,15 @@ def sport_1_cycling():
return sport
@pytest.fixture()
def sport_1_cycling_inactive():
sport = Sport(label='Cycling')
sport.is_active = False
db.session.add(sport)
db.session.commit()
return sport
@pytest.fixture()
def sport_2_running():
sport = Sport(label='Running')

View File

@ -5,16 +5,29 @@ expected_sport_1_cycling_result = {
'label': 'Cycling',
'img': None,
'is_active': True,
'_can_be_disabled': True,
}
expected_sport_1_cycling_admin_result = expected_sport_1_cycling_result.copy()
expected_sport_1_cycling_admin_result['has_activities'] = False
expected_sport_2_running_result = {
'id': 2,
'label': 'Running',
'img': None,
'is_active': True,
'_can_be_disabled': True,
}
expected_sport_2_running_admin_result = expected_sport_2_running_result.copy()
expected_sport_2_running_admin_result['has_activities'] = False
expected_sport_1_cycling_inactive_result = {
'id': 1,
'label': 'Cycling',
'img': None,
'is_active': False,
}
expected_sport_1_cycling_inactive_admin_result = (
expected_sport_1_cycling_inactive_result.copy()
)
expected_sport_1_cycling_inactive_admin_result['has_activities'] = False
def test_get_all_sports(app, user_1, sport_1_cycling, sport_2_running):
@ -41,6 +54,63 @@ def test_get_all_sports(app, user_1, sport_1_cycling, sport_2_running):
assert data['data']['sports'][1] == expected_sport_2_running_result
def test_get_all_sports_with_inactive_one(
app, user_1, sport_1_cycling_inactive, sport_2_running
):
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/sports',
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 len(data['data']['sports']) == 2
assert (
data['data']['sports'][0] == expected_sport_1_cycling_inactive_result
)
assert data['data']['sports'][1] == expected_sport_2_running_result
def test_get_all_sports_admin(
app, user_1_admin, sport_1_cycling_inactive, sport_2_running
):
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/sports',
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 len(data['data']['sports']) == 2
assert (
data['data']['sports'][0]
== expected_sport_1_cycling_inactive_admin_result
)
assert data['data']['sports'][1] == expected_sport_2_running_admin_result
def test_get_a_sport(app, user_1, sport_1_cycling):
client = app.test_client()
resp_login = client.post(
@ -85,6 +155,59 @@ def test_get_a_sport_invalid(app, user_1):
assert len(data['data']['sports']) == 0
def test_get_a_inactive_sport(app, user_1, sport_1_cycling_inactive):
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/sports/1',
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 len(data['data']['sports']) == 1
assert (
data['data']['sports'][0] == expected_sport_1_cycling_inactive_result
)
def test_get_a_inactive_sport_as_admin(
app, user_1_admin, sport_1_cycling_inactive
):
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/sports/1',
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 len(data['data']['sports']) == 1
assert (
data['data']['sports'][0]
== expected_sport_1_cycling_inactive_admin_result
)
def test_update_a_sport(app, user_1_admin, sport_1_cycling):
client = app.test_client()
resp_login = client.post(
@ -108,6 +231,7 @@ def test_update_a_sport(app, user_1_admin, sport_1_cycling):
assert len(data['data']['sports']) == 1
assert data['data']['sports'][0]['is_active'] is False
assert data['data']['sports'][0]['has_activities'] is False
response = client.patch(
'/api/sports/1',
@ -125,9 +249,10 @@ def test_update_a_sport(app, user_1_admin, sport_1_cycling):
assert len(data['data']['sports']) == 1
assert data['data']['sports'][0]['is_active'] is True
assert data['data']['sports'][0]['has_activities'] is False
def test_disable_a_sport_with_activities(
def test_update_a_sport_with_activities(
app, user_1_admin, sport_1_cycling, activity_cycling_user_1
):
client = app.test_client()
@ -147,9 +272,30 @@ def test_disable_a_sport_with_activities(
)
data = json.loads(response.data.decode())
assert response.status_code == 400
assert 'fail' in data['status']
assert 'Sport can not be disabled, activities exist.' in data['message']
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['sports']) == 1
assert data['data']['sports'][0]['is_active'] is False
assert data['data']['sports'][0]['has_activities'] is True
response = client.patch(
'/api/sports/1',
content_type='application/json',
data=json.dumps(dict(is_active=True)),
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 len(data['data']['sports']) == 1
assert data['data']['sports'][0]['is_active'] is True
assert data['data']['sports'][0]['has_activities'] is True
def test_update_a_sport_not_admin(app, user_1, sport_1_cycling):

View File

@ -1,24 +1,29 @@
def test_sport_model(app, sport_1_cycling):
assert 1 == sport_1_cycling.id
assert 'Cycling' == sport_1_cycling.label
assert '<Sport \'Cycling\'>' == str(sport_1_cycling)
def assert_sport_model(sport, is_admin=False):
assert 1 == sport.id
assert 'Cycling' == sport.label
assert '<Sport \'Cycling\'>' == str(sport)
serialized_sport = sport_1_cycling.serialize()
serialized_sport = sport.serialize(is_admin)
assert 1 == serialized_sport['id']
assert 'Cycling' == serialized_sport['label']
assert serialized_sport['is_active'] is True
assert serialized_sport['_can_be_disabled'] is True
return serialized_sport
def test_sport_model(app, sport_1_cycling):
serialized_sport = assert_sport_model(sport_1_cycling)
assert 'has_activities' not in serialized_sport
def test_sport_model_with_activity(
app, sport_1_cycling, user_1, activity_cycling_user_1
):
assert 1 == sport_1_cycling.id
assert 'Cycling' == sport_1_cycling.label
assert '<Sport \'Cycling\'>' == str(sport_1_cycling)
serialized_sport = assert_sport_model(sport_1_cycling)
assert 'has_activities' not in serialized_sport
serialized_sport = sport_1_cycling.serialize()
assert 1 == serialized_sport['id']
assert 'Cycling' == serialized_sport['label']
assert serialized_sport['is_active'] is True
assert serialized_sport['_can_be_disabled'] is False
def test_sport_model_with_activity_admin(
app, sport_1_cycling, user_1, activity_cycling_user_1
):
serialized_sport = assert_sport_model(sport_1_cycling, True)
assert serialized_sport['has_activities'] is True

View File

@ -11,7 +11,7 @@ import { translateSports } from '../../../utils/activities'
function FormWithGpx(props) {
const { activity, loading, onAddActivity, onEditActivity, sports, t } = props
const sportId = activity ? activity.sport_id : ''
const translatedSports = translateSports(sports, t)
const translatedSports = translateSports(sports, t, true)
// prettier-ignore
const zipTooltip =
`${t('activities:no folder inside')}, ${gpxLimit} ${

View File

@ -10,7 +10,7 @@ import { formatActivityDate, translateSports } from '../../../utils/activities'
function FormWithoutGpx(props) {
const { activity, onAddOrEdit, sports, t } = props
const translatedSports = translateSports(sports, t)
const translatedSports = translateSports(sports, t, true)
let activityDate,
activityTime,
sportId = ''

View File

@ -62,23 +62,26 @@ class AdminSports extends React.Component {
)}
</td>
<td>
{sport._can_be_disabled ? (
<input
type="submit"
className={`btn btn-${
sport.is_active ? 'dark' : 'primary'
} btn-sm`}
value={
sport.is_active
? t('administration:Disable')
: t('administration:Enable')
}
onClick={() =>
updateSport(sport.id, !sport.is_active)
}
/>
) : (
<input
type="submit"
className={`btn btn-${
sport.is_active ? 'dark' : 'primary'
} btn-sm`}
value={
sport.is_active
? t('administration:Disable')
: t('administration:Enable')
}
onClick={() =>
updateSport(sport.id, !sport.is_active)
}
/>
{sport.has_activities && (
<span className="admin-message">
<i
className="fa fa-warning custom-fa"
aria-hidden="true"
/>
{t('administration:activities exist')}
</span>
)}

View File

@ -167,13 +167,14 @@ label {
}
.admin-items {
list-style-type: square;
list-style-type: square;
}
.admin-message {
color: #7c7c7d;
font-size: 0.9em;
font-style: italic;
font-size: 0.9em;
font-style: italic;
margin-left: 10px;
}
.card {

View File

@ -64,18 +64,6 @@ class NavBar extends React.PureComponent {
</Link>
</li>
)}
{isAuthenticated && (
<li className="nav-item">
<Link
className="nav-link"
to={{
pathname: '/activities/add',
}}
>
<strong>{t('common:Add workout')}</strong>
</Link>
</li>
)}
{admin && (
<li className="nav-item">
<Link
@ -88,6 +76,18 @@ class NavBar extends React.PureComponent {
</Link>
</li>
)}
{isAuthenticated && (
<li className="nav-item">
<Link
className="nav-link"
to={{
pathname: '/activities/add',
}}
>
<strong>{t('common:Add workout')}</strong>
</Link>
</li>
)}
</ul>
{/* prettier-ignore */}
<ul

View File

@ -93,8 +93,9 @@ const sortSports = (a, b) => {
return sportALabel > sportBLabel ? 1 : sportALabel < sportBLabel ? -1 : 0
}
export const translateSports = (sports, t) =>
export const translateSports = (sports, t, onlyActive = false) =>
sports
.filter(sport => (onlyActive ? sport.is_active : true))
.map(sport => ({
...sport,
label: t(`sports:${sport.label}`),