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> </pre></div>
</div> </div>
<p><strong>Example response</strong>:</p> <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> <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="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;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="nt">&quot;sports&quot;</span><span class="p">:</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;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;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;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="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="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;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;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;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="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="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;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;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;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="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="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;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;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;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="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="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;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;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;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="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="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;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;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;is_active&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
@ -225,7 +279,7 @@
</div> </div>
<p><strong>Example response</strong>:</p> <p><strong>Example response</strong>:</p>
<ul class="simple"> <ul class="simple">
<li><p>success</p></li> <li><p>success for non admin user :</p></li>
</ul> </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> <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="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;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="nt">&quot;sports&quot;</span><span class="p">:</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;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;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;is_active&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
@ -290,7 +365,8 @@
<dl class="patch"> <dl class="patch">
<dt id="patch--api-sports-(int-sport_id)"> <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> <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> <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> <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> <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;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="nt">&quot;sports&quot;</span><span class="p">:</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;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;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> <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> <li><p>Invalid token. Please log in again.</p></li>
</ul> </ul>
</p></li> </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.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> <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> </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): def __init__(self, label):
self.label = label self.label = label
def serialize(self): def serialize(self, is_admin=False):
return { serialized_sport = {
'id': self.id, 'id': self.id,
'label': self.label, 'label': self.label,
'img': self.img, 'img': self.img,
'is_active': self.is_active, '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): class Activity(db.Model):

View File

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

View File

@ -107,6 +107,15 @@ def sport_1_cycling():
return sport 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() @pytest.fixture()
def sport_2_running(): def sport_2_running():
sport = Sport(label='Running') sport = Sport(label='Running')

View File

@ -5,16 +5,29 @@ expected_sport_1_cycling_result = {
'label': 'Cycling', 'label': 'Cycling',
'img': None, 'img': None,
'is_active': True, '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 = { expected_sport_2_running_result = {
'id': 2, 'id': 2,
'label': 'Running', 'label': 'Running',
'img': None, 'img': None,
'is_active': True, '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): 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 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): def test_get_a_sport(app, user_1, sport_1_cycling):
client = app.test_client() client = app.test_client()
resp_login = client.post( resp_login = client.post(
@ -85,6 +155,59 @@ def test_get_a_sport_invalid(app, user_1):
assert len(data['data']['sports']) == 0 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): def test_update_a_sport(app, user_1_admin, sport_1_cycling):
client = app.test_client() client = app.test_client()
resp_login = client.post( 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 len(data['data']['sports']) == 1
assert data['data']['sports'][0]['is_active'] is False assert data['data']['sports'][0]['is_active'] is False
assert data['data']['sports'][0]['has_activities'] is False
response = client.patch( response = client.patch(
'/api/sports/1', '/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 len(data['data']['sports']) == 1
assert data['data']['sports'][0]['is_active'] is True 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 app, user_1_admin, sport_1_cycling, activity_cycling_user_1
): ):
client = app.test_client() client = app.test_client()
@ -147,9 +272,30 @@ def test_disable_a_sport_with_activities(
) )
data = json.loads(response.data.decode()) data = json.loads(response.data.decode())
assert response.status_code == 400 assert response.status_code == 200
assert 'fail' in data['status'] assert 'success' in data['status']
assert 'Sport can not be disabled, activities exist.' in data['message']
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): 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): def assert_sport_model(sport, is_admin=False):
assert 1 == sport_1_cycling.id assert 1 == sport.id
assert 'Cycling' == sport_1_cycling.label assert 'Cycling' == sport.label
assert '<Sport \'Cycling\'>' == str(sport_1_cycling) assert '<Sport \'Cycling\'>' == str(sport)
serialized_sport = sport_1_cycling.serialize() serialized_sport = sport.serialize(is_admin)
assert 1 == serialized_sport['id'] assert 1 == serialized_sport['id']
assert 'Cycling' == serialized_sport['label'] assert 'Cycling' == serialized_sport['label']
assert serialized_sport['is_active'] is True 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( def test_sport_model_with_activity(
app, sport_1_cycling, user_1, activity_cycling_user_1 app, sport_1_cycling, user_1, activity_cycling_user_1
): ):
assert 1 == sport_1_cycling.id serialized_sport = assert_sport_model(sport_1_cycling)
assert 'Cycling' == sport_1_cycling.label assert 'has_activities' not in serialized_sport
assert '<Sport \'Cycling\'>' == str(sport_1_cycling)
serialized_sport = sport_1_cycling.serialize()
assert 1 == serialized_sport['id'] def test_sport_model_with_activity_admin(
assert 'Cycling' == serialized_sport['label'] app, sport_1_cycling, user_1, activity_cycling_user_1
assert serialized_sport['is_active'] is True ):
assert serialized_sport['_can_be_disabled'] is False 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) { function FormWithGpx(props) {
const { activity, loading, onAddActivity, onEditActivity, sports, t } = props const { activity, loading, onAddActivity, onEditActivity, sports, t } = props
const sportId = activity ? activity.sport_id : '' const sportId = activity ? activity.sport_id : ''
const translatedSports = translateSports(sports, t) const translatedSports = translateSports(sports, t, true)
// prettier-ignore // prettier-ignore
const zipTooltip = const zipTooltip =
`${t('activities:no folder inside')}, ${gpxLimit} ${ `${t('activities:no folder inside')}, ${gpxLimit} ${

View File

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

View File

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

View File

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

View File

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

View File

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