API - get sports with authenticated user preferences

This commit is contained in:
Sam 2021-11-12 12:33:25 +01:00
parent 8237345edd
commit c05aba92a9
8 changed files with 409 additions and 44 deletions

View File

@ -147,34 +147,52 @@
<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;color&quot;</span><span class="p">:</span> <span class="kc">null</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;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;is_active_for_user&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="nt">&quot;stopped_speed_threshold&quot;</span><span class="p">:</span> <span class="mi">1</span>
<span class="p">},</span> <span class="p">},</span>
<span class="p">{</span> <span class="p">{</span>
<span class="nt">&quot;color&quot;</span><span class="p">:</span> <span class="kc">null</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;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;is_active_for_user&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="nt">&quot;stopped_speed_threshold&quot;</span><span class="p">:</span> <span class="mi">1</span>
<span class="p">},</span> <span class="p">},</span>
<span class="p">{</span> <span class="p">{</span>
<span class="nt">&quot;color&quot;</span><span class="p">:</span> <span class="kc">null</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;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;is_active_for_user&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="nt">&quot;stopped_speed_threshold&quot;</span><span class="p">:</span> <span class="mf">0.1</span>
<span class="p">},</span> <span class="p">},</span>
<span class="p">{</span> <span class="p">{</span>
<span class="nt">&quot;color&quot;</span><span class="p">:</span> <span class="kc">null</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;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;is_active_for_user&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="nt">&quot;stopped_speed_threshold&quot;</span><span class="p">:</span> <span class="mi">1</span>
<span class="p">},</span> <span class="p">},</span>
<span class="p">{</span> <span class="p">{</span>
<span class="nt">&quot;color&quot;</span><span class="p">:</span> <span class="kc">null</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;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;is_active_for_user&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="nt">&quot;stopped_speed_threshold&quot;</span><span class="p">:</span> <span class="mf">0.1</span>
<span class="p">},</span> <span class="p">},</span>
<span class="p">{</span> <span class="p">{</span>
<span class="nt">&quot;color&quot;</span><span class="p">:</span> <span class="kc">null</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;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;Walking&quot;</span> <span class="nt">&quot;is_active_for_user&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="nt">&quot;stopped_speed_threshold&quot;</span><span class="p">:</span> <span class="mf">0.1</span>
<span class="p">}</span> <span class="p">}</span>
<span class="p">]</span> <span class="p">]</span>
<span class="p">},</span> <span class="p">},</span>
@ -192,40 +210,58 @@
<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;color&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;has_workouts&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nt">&quot;has_workouts&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;id&quot;</span><span class="p">:</span> <span class="mi">1</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;is_active_for_user&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="nt">&quot;stopped_speed_threshold&quot;</span><span class="p">:</span> <span class="mi">1</span>
<span class="p">},</span> <span class="p">},</span>
<span class="p">{</span> <span class="p">{</span>
<span class="nt">&quot;color&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;has_workouts&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nt">&quot;has_workouts&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;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;is_active_for_user&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="nt">&quot;stopped_speed_threshold&quot;</span><span class="p">:</span> <span class="mi">1</span>
<span class="p">},</span> <span class="p">},</span>
<span class="p">{</span> <span class="p">{</span>
<span class="nt">&quot;color&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;has_workouts&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nt">&quot;has_workouts&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;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;is_active_for_user&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="nt">&quot;stopped_speed_threshold&quot;</span><span class="p">:</span> <span class="mf">0.1</span>
<span class="p">},</span> <span class="p">},</span>
<span class="p">{</span> <span class="p">{</span>
<span class="nt">&quot;color&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;has_workouts&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nt">&quot;has_workouts&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;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;is_active_for_user&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="nt">&quot;stopped_speed_threshold&quot;</span><span class="p">:</span> <span class="mi">1</span>
<span class="p">},</span> <span class="p">},</span>
<span class="p">{</span> <span class="p">{</span>
<span class="nt">&quot;color&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;has_workouts&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nt">&quot;has_workouts&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;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;is_active_for_user&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="nt">&quot;stopped_speed_threshold&quot;</span><span class="p">:</span> <span class="mf">0.1</span>
<span class="p">},</span> <span class="p">},</span>
<span class="p">{</span> <span class="p">{</span>
<span class="nt">&quot;color&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;has_workouts&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nt">&quot;has_workouts&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;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;Walking&quot;</span> <span class="nt">&quot;is_active_for_user&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="nt">&quot;stopped_speed_threshold&quot;</span><span class="p">:</span> <span class="mf">0.1</span>
<span class="p">}</span> <span class="p">}</span>
<span class="p">]</span> <span class="p">]</span>
<span class="p">},</span> <span class="p">},</span>
@ -278,9 +314,12 @@
<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;color&quot;</span><span class="p">:</span> <span class="kc">null</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;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;is_active_for_user&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="nt">&quot;stopped_speed_threshold&quot;</span><span class="p">:</span> <span class="mi">1</span>
<span class="p">}</span> <span class="p">}</span>
<span class="p">]</span> <span class="p">]</span>
<span class="p">},</span> <span class="p">},</span>
@ -298,10 +337,13 @@
<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;color&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;has_workouts&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nt">&quot;has_workouts&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;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;is_active_for_user&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="nt">&quot;stopped_speed_threshold&quot;</span><span class="p">:</span> <span class="mi">1</span>
<span class="p">}</span> <span class="p">}</span>
<span class="p">]</span> <span class="p">]</span>
<span class="p">},</span> <span class="p">},</span>
@ -371,10 +413,13 @@ Authenticated user must be an admin</p>
<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;color&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;has_workouts&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nt">&quot;has_workouts&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;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>
<span class="nt">&quot;label&quot;</span><span class="p">:</span> <span class="s2">&quot;Cycling (Sport)&quot;</span> <span class="nt">&quot;is_active_for_user&quot;</span><span class="p">:</span> <span class="kc">false</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="nt">&quot;stopped_speed_threshold&quot;</span><span class="p">:</span> <span class="mi">1</span>
<span class="p">}</span> <span class="p">}</span>
<span class="p">]</span> <span class="p">]</span>
<span class="p">},</span> <span class="p">},</span>

File diff suppressed because one or more lines are too long

View File

@ -96,3 +96,17 @@ def user_sport_1_preference(
db.session.add(user_sport) db.session.add(user_sport)
db.session.commit() db.session.commit()
return user_sport return user_sport
@pytest.fixture()
def user_admin_sport_1_preference(
user_1_admin: User, sport_1_cycling: Sport
) -> UserSportPreference:
user_sport = UserSportPreference(
user_id=user_1_admin.id,
sport_id=sport_1_cycling.id,
stopped_speed_threshold=sport_1_cycling.stopped_speed_threshold,
)
db.session.add(user_sport)
db.session.commit()
return user_sport

View File

@ -2,7 +2,8 @@ import json
from flask import Flask from flask import Flask
from fittrackee.users.models import User from fittrackee import db
from fittrackee.users.models import User, UserSportPreference
from fittrackee.workouts.models import Sport, Workout from fittrackee.workouts.models import Sport, Workout
from ..api_test_case import ApiTestCaseMixin from ..api_test_case import ApiTestCaseMixin
@ -11,6 +12,9 @@ expected_sport_1_cycling_result = {
'id': 1, 'id': 1,
'label': 'Cycling', 'label': 'Cycling',
'is_active': True, 'is_active': True,
'is_active_for_user': True,
'color': None,
'stopped_speed_threshold': 1,
} }
expected_sport_1_cycling_admin_result = expected_sport_1_cycling_result.copy() expected_sport_1_cycling_admin_result = expected_sport_1_cycling_result.copy()
expected_sport_1_cycling_admin_result['has_workouts'] = False expected_sport_1_cycling_admin_result['has_workouts'] = False
@ -19,6 +23,9 @@ expected_sport_2_running_result = {
'id': 2, 'id': 2,
'label': 'Running', 'label': 'Running',
'is_active': True, 'is_active': True,
'is_active_for_user': True,
'color': None,
'stopped_speed_threshold': 0.1,
} }
expected_sport_2_running_admin_result = expected_sport_2_running_result.copy() expected_sport_2_running_admin_result = expected_sport_2_running_result.copy()
expected_sport_2_running_admin_result['has_workouts'] = False expected_sport_2_running_admin_result['has_workouts'] = False
@ -27,6 +34,9 @@ expected_sport_1_cycling_inactive_result = {
'id': 1, 'id': 1,
'label': 'Cycling', 'label': 'Cycling',
'is_active': False, 'is_active': False,
'is_active_for_user': False,
'color': None,
'stopped_speed_threshold': 1,
} }
expected_sport_1_cycling_inactive_admin_result = ( expected_sport_1_cycling_inactive_admin_result = (
expected_sport_1_cycling_inactive_result.copy() expected_sport_1_cycling_inactive_result.copy()
@ -108,6 +118,39 @@ class TestGetSports(ApiTestCaseMixin):
data['data']['sports'][1] == expected_sport_2_running_admin_result data['data']['sports'][1] == expected_sport_2_running_admin_result
) )
def test_it_gets_sports_with_auth_user_preferences(
self,
app: Flask,
user_1_admin: User,
sport_1_cycling: Sport,
sport_2_running: Sport,
user_admin_sport_1_preference: UserSportPreference,
) -> None:
user_admin_sport_1_preference.color = '#000000'
user_admin_sport_1_preference.stopped_speed_threshold = 0.5
user_admin_sport_1_preference.is_active = False
db.session.commit()
client, auth_token = self.get_test_client_and_auth_token(
app, as_admin=True
)
response = client.get(
'/api/sports',
headers=dict(Authorization=f'Bearer {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]['color'] == '#000000'
assert data['data']['sports'][0]['stopped_speed_threshold'] == 0.5
assert data['data']['sports'][0]['is_active_for_user'] is False
assert (
data['data']['sports'][1] == expected_sport_2_running_admin_result
)
class TestGetSport(ApiTestCaseMixin): class TestGetSport(ApiTestCaseMixin):
def test_it_gets_a_sport( def test_it_gets_a_sport(
@ -126,6 +169,26 @@ class TestGetSport(ApiTestCaseMixin):
assert len(data['data']['sports']) == 1 assert len(data['data']['sports']) == 1
assert data['data']['sports'][0] == expected_sport_1_cycling_result assert data['data']['sports'][0] == expected_sport_1_cycling_result
def test_it_gets_a_sport_with_preferences(
self,
app: Flask,
user_1: User,
sport_1_cycling: Sport,
user_sport_1_preference: UserSportPreference,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(app)
response = client.get(
'/api/sports/1',
headers=dict(Authorization=f'Bearer {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_result
def test_it_returns_404_if_sport_does_not_exist( def test_it_returns_404_if_sport_does_not_exist(
self, app: Flask, user_1: User self, app: Flask, user_1: User
) -> None: ) -> None:
@ -202,6 +265,7 @@ class TestUpdateSport(ApiTestCaseMixin):
assert 'success' in data['status'] assert 'success' in data['status']
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]['is_active_for_user'] is False
assert data['data']['sports'][0]['has_workouts'] is False assert data['data']['sports'][0]['has_workouts'] is False
def test_it_enables_a_sport( def test_it_enables_a_sport(
@ -224,6 +288,7 @@ class TestUpdateSport(ApiTestCaseMixin):
assert 'success' in data['status'] assert 'success' in data['status']
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]['is_active_for_user'] is True
assert data['data']['sports'][0]['has_workouts'] is False assert data['data']['sports'][0]['has_workouts'] is False
def test_it_disables_a_sport_with_workouts( def test_it_disables_a_sport_with_workouts(
@ -249,6 +314,7 @@ class TestUpdateSport(ApiTestCaseMixin):
assert 'success' in data['status'] assert 'success' in data['status']
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]['is_active_for_user'] is False
assert data['data']['sports'][0]['has_workouts'] is True assert data['data']['sports'][0]['has_workouts'] is True
def test_it_enables_a_sport_with_workouts( def test_it_enables_a_sport_with_workouts(
@ -275,8 +341,63 @@ class TestUpdateSport(ApiTestCaseMixin):
assert 'success' in data['status'] assert 'success' in data['status']
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]['is_active_for_user'] is True
assert data['data']['sports'][0]['has_workouts'] is True assert data['data']['sports'][0]['has_workouts'] is True
def test_it_disables_a_sport_with_preferences(
self,
app: Flask,
user_1_admin: User,
sport_1_cycling: Sport,
user_admin_sport_1_preference: UserSportPreference,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, as_admin=True
)
response = client.patch(
'/api/sports/1',
content_type='application/json',
data=json.dumps(dict(is_active=False)),
headers=dict(Authorization=f'Bearer {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 False
assert data['data']['sports'][0]['is_active_for_user'] is False
assert data['data']['sports'][0]['is_active_for_user'] is False
assert data['data']['sports'][0]['has_workouts'] is False
def test_it_enables_a_sport_with_preferences(
self,
app: Flask,
user_1_admin: User,
sport_1_cycling: Sport,
user_admin_sport_1_preference: UserSportPreference,
) -> None:
sport_1_cycling.is_active = False
client, auth_token = self.get_test_client_and_auth_token(
app, as_admin=True
)
response = client.patch(
'/api/sports/1',
content_type='application/json',
data=json.dumps(dict(is_active=True)),
headers=dict(Authorization=f'Bearer {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]['is_active_for_user'] is True
assert data['data']['sports'][0]['has_workouts'] is False
def test_returns_error_if_user_has_no_admin_rights( def test_returns_error_if_user_has_no_admin_rights(
self, app: Flask, user_1: User, sport_1_cycling: Sport self, app: Flask, user_1: User, sport_1_cycling: Sport
) -> None: ) -> None:

View File

@ -2,7 +2,8 @@ from typing import Dict, Optional
from flask import Flask from flask import Flask
from fittrackee.users.models import User from fittrackee import db
from fittrackee.users.models import User, UserSportPreference
from fittrackee.workouts.models import Sport, Workout from fittrackee.workouts.models import Sport, Workout
@ -15,10 +16,13 @@ class TestSportModel:
assert 'Cycling' == sport.label assert 'Cycling' == sport.label
assert '<Sport \'Cycling\'>' == str(sport) assert '<Sport \'Cycling\'>' == str(sport)
serialized_sport = sport.serialize(is_admin) serialized_sport = sport.serialize(is_admin=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['is_active_for_user'] is True
assert serialized_sport['color'] is None
assert serialized_sport['stopped_speed_threshold'] == 1
return serialized_sport return serialized_sport
def test_sport_model(self, app: Flask, sport_1_cycling: Sport) -> None: def test_sport_model(self, app: Flask, sport_1_cycling: Sport) -> None:
@ -44,3 +48,87 @@ class TestSportModel:
) -> None: ) -> None:
serialized_sport = self.assert_sport_model(sport_1_cycling, True) serialized_sport = self.assert_sport_model(sport_1_cycling, True)
assert serialized_sport['has_workouts'] is True assert serialized_sport['has_workouts'] is True
class TestSportModelWithPreferences:
def test_sport_model_with_color_preference(
self,
app: Flask,
user_1: User,
sport_1_cycling: Sport,
user_sport_1_preference: UserSportPreference,
) -> None:
user_sport_1_preference.color = '#00000'
serialized_sport = sport_1_cycling.serialize(
sport_preferences=user_sport_1_preference.serialize()
)
assert serialized_sport['id'] == 1
assert serialized_sport['label'] == 'Cycling'
assert serialized_sport['is_active'] is True
assert serialized_sport['is_active_for_user'] is True
assert serialized_sport['color'] == '#00000'
assert serialized_sport['stopped_speed_threshold'] == 1
assert 'has_workouts' not in serialized_sport
def test_sport_model_with_is_active_preference(
self,
app: Flask,
user_1: User,
sport_1_cycling: Sport,
user_sport_1_preference: UserSportPreference,
) -> None:
user_sport_1_preference.is_active = False
serialized_sport = sport_1_cycling.serialize(
sport_preferences=user_sport_1_preference.serialize()
)
assert serialized_sport['id'] == 1
assert serialized_sport['label'] == 'Cycling'
assert serialized_sport['is_active'] is True
assert serialized_sport['is_active_for_user'] is False
assert serialized_sport['color'] is None
assert serialized_sport['stopped_speed_threshold'] == 1
assert 'has_workouts' not in serialized_sport
def test_inactive_sport_model_with_is_active_preference(
self,
app: Flask,
user_1: User,
sport_1_cycling: Sport,
user_sport_1_preference: UserSportPreference,
) -> None:
sport_1_cycling.is_active = False
user_sport_1_preference.is_active = True
serialized_sport = sport_1_cycling.serialize(
sport_preferences=user_sport_1_preference.serialize()
)
assert serialized_sport['id'] == 1
assert serialized_sport['label'] == 'Cycling'
assert serialized_sport['is_active'] is False
assert serialized_sport['is_active_for_user'] is False
assert serialized_sport['color'] is None
assert serialized_sport['stopped_speed_threshold'] == 1
assert 'has_workouts' not in serialized_sport
def test_sport_model_with_threshold_preference(
self,
app: Flask,
user_1: User,
sport_1_cycling: Sport,
user_sport_1_preference: UserSportPreference,
) -> None:
user_sport_1_preference.stopped_speed_threshold = 0.5
db.session.commit()
serialized_sport = sport_1_cycling.serialize(
sport_preferences=user_sport_1_preference.serialize()
)
assert serialized_sport['id'] == 1
assert serialized_sport['label'] == 'Cycling'
assert serialized_sport['is_active'] is True
assert serialized_sport['is_active_for_user'] is True
assert serialized_sport['color'] is None
assert serialized_sport['stopped_speed_threshold'] == 0.5
assert 'has_workouts' not in serialized_sport

View File

@ -40,11 +40,6 @@ class User(BaseModel):
'Record', lazy=True, backref=db.backref('user', lazy='joined') 'Record', lazy=True, backref=db.backref('user', lazy='joined')
) )
language = db.Column(db.String(50), nullable=True) language = db.Column(db.String(50), nullable=True)
sport_preferences = db.relationship(
'UserSportPreference',
lazy=True,
backref=db.backref('user', lazy='joined'),
)
def __repr__(self) -> str: def __repr__(self) -> str:
return f'<User {self.username!r}>' return f'<User {self.username!r}>'

View File

@ -85,11 +85,30 @@ class Sport(BaseModel):
def __init__(self, label: str) -> None: def __init__(self, label: str) -> None:
self.label = label self.label = label
def serialize(self, is_admin: Optional[bool] = False) -> Dict: def serialize(
self,
is_admin: Optional[bool] = False,
sport_preferences: Optional[Dict] = None,
) -> Dict:
serialized_sport = { serialized_sport = {
'id': self.id, 'id': self.id,
'label': self.label, 'label': self.label,
'is_active': self.is_active, 'is_active': self.is_active,
'is_active_for_user': (
self.is_active
if sport_preferences is None
else (sport_preferences['is_active'] and self.is_active)
),
'color': (
None
if sport_preferences is None
else sport_preferences['color']
),
'stopped_speed_threshold': (
self.stopped_speed_threshold
if sport_preferences is None
else sport_preferences['stopped_speed_threshold']
),
} }
if is_admin: if is_admin:
serialized_sport['has_workouts'] = len(self.workouts) > 0 serialized_sport['has_workouts'] = len(self.workouts) > 0

View File

@ -11,7 +11,7 @@ from fittrackee.responses import (
handle_error_and_return_response, handle_error_and_return_response,
) )
from fittrackee.users.decorators import authenticate, authenticate_as_admin from fittrackee.users.decorators import authenticate, authenticate_as_admin
from fittrackee.users.models import User from fittrackee.users.models import User, UserSportPreference
from .models import Sport from .models import Sport
@ -44,34 +44,52 @@ def get_sports(auth_user_id: int) -> Dict:
"data": { "data": {
"sports": [ "sports": [
{ {
"color": null,
"id": 1, "id": 1,
"is_active": true, "is_active": true,
"label": "Cycling (Sport)" "is_active_for_user": true,
"label": "Cycling (Sport)",
"stopped_speed_threshold": 1
}, },
{ {
"color": null,
"id": 2, "id": 2,
"is_active": true, "is_active": true,
"label": "Cycling (Transport)" "is_active_for_user": true,
"label": "Cycling (Transport)",
"stopped_speed_threshold": 1
}, },
{ {
"color": null,
"id": 3, "id": 3,
"is_active": true, "is_active": true,
"label": "Hiking" "is_active_for_user": true,
"label": "Hiking",
"stopped_speed_threshold": 0.1
}, },
{ {
"color": null,
"id": 4, "id": 4,
"is_active": true, "is_active": true,
"label": "Mountain Biking" "is_active_for_user": true,
"label": "Mountain Biking",
"stopped_speed_threshold": 1
}, },
{ {
"color": null,
"id": 5, "id": 5,
"is_active": true, "is_active": true,
"label": "Running" "is_active_for_user": true,
"label": "Running",
"stopped_speed_threshold": 0.1
}, },
{ {
"color": null,
"id": 6, "id": 6,
"is_active": true, "is_active": true,
"label": "Walking" "is_active_for_user": true,
"label": "Walking",
"stopped_speed_threshold": 0.1
} }
] ]
}, },
@ -89,40 +107,58 @@ def get_sports(auth_user_id: int) -> Dict:
"data": { "data": {
"sports": [ "sports": [
{ {
"color": null,
"has_workouts": true, "has_workouts": true,
"id": 1, "id": 1,
"is_active": true, "is_active": true,
"label": "Cycling (Sport)" "is_active_for_user": true,
"label": "Cycling (Sport)",
"stopped_speed_threshold": 1
}, },
{ {
"color": null,
"has_workouts": false, "has_workouts": false,
"id": 2, "id": 2,
"is_active": true, "is_active": true,
"label": "Cycling (Transport)" "is_active_for_user": true,
"label": "Cycling (Transport)",
"stopped_speed_threshold": 1
}, },
{ {
"color": null,
"has_workouts": false, "has_workouts": false,
"id": 3, "id": 3,
"is_active": true, "is_active": true,
"label": "Hiking" "is_active_for_user": true,
"label": "Hiking",
"stopped_speed_threshold": 0.1
}, },
{ {
"color": null,
"has_workouts": false, "has_workouts": false,
"id": 4, "id": 4,
"is_active": true, "is_active": true,
"label": "Mountain Biking" "is_active_for_user": true,
"label": "Mountain Biking",
"stopped_speed_threshold": 1
}, },
{ {
"color": null,
"has_workouts": false, "has_workouts": false,
"id": 5, "id": 5,
"is_active": true, "is_active": true,
"label": "Running" "is_active_for_user": true,
"label": "Running",
"stopped_speed_threshold": 0.1
}, },
{ {
"color": null,
"has_workouts": false, "has_workouts": false,
"id": 6, "id": 6,
"is_active": true, "is_active": true,
"label": "Walking" "is_active_for_user": true,
"label": "Walking",
"stopped_speed_threshold": 0.1
} }
] ]
}, },
@ -142,9 +178,22 @@ def get_sports(auth_user_id: int) -> Dict:
""" """
user = User.query.filter_by(id=int(auth_user_id)).first() 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()
sports_data = []
for sport in sports:
sport_preferences = UserSportPreference.query.filter_by(
user_id=user.id, sport_id=sport.id
).first()
sports_data.append(
sport.serialize(
is_admin=user.admin,
sport_preferences=sport_preferences.serialize()
if sport_preferences
else None,
)
)
return { return {
'status': 'success', 'status': 'success',
'data': {'sports': [sport.serialize(user.admin) for sport in sports]}, 'data': {'sports': sports_data},
} }
@ -174,9 +223,12 @@ def get_sport(auth_user_id: int, sport_id: int) -> Union[Dict, HttpResponse]:
"data": { "data": {
"sports": [ "sports": [
{ {
"color": null,
"id": 1, "id": 1,
"is_active": true, "is_active": true,
"label": "Cycling (Sport)" "is_active_for_user": true,
"label": "Cycling (Sport)",
"stopped_speed_threshold": 1
} }
] ]
}, },
@ -194,10 +246,13 @@ def get_sport(auth_user_id: int, sport_id: int) -> Union[Dict, HttpResponse]:
"data": { "data": {
"sports": [ "sports": [
{ {
"color": null,
"has_workouts": false, "has_workouts": false,
"id": 1, "id": 1,
"is_active": true, "is_active": true,
"label": "Cycling (Sport)" "is_active_for_user": true,
"label": "Cycling (Sport)",
"stopped_speed_threshold": 1
} }
] ]
}, },
@ -234,9 +289,21 @@ def get_sport(auth_user_id: int, sport_id: int) -> Union[Dict, HttpResponse]:
user = User.query.filter_by(id=int(auth_user_id)).first() 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:
sport_preferences = UserSportPreference.query.filter_by(
user_id=user.id, sport_id=sport.id
).first()
return { return {
'status': 'success', 'status': 'success',
'data': {'sports': [sport.serialize(user.admin)]}, 'data': {
'sports': [
sport.serialize(
is_admin=user.admin,
sport_preferences=sport_preferences.serialize()
if sport_preferences
else None,
)
]
},
} }
return DataNotFoundErrorResponse('sports') return DataNotFoundErrorResponse('sports')
@ -270,10 +337,13 @@ def update_sport(
"data": { "data": {
"sports": [ "sports": [
{ {
"color": null,
"has_workouts": false, "has_workouts": false,
"id": 1, "id": 1,
"is_active": false, "is_active": false,
"label": "Cycling (Sport)" "is_active_for_user": false,
"label": "Cycling (Sport)",
"stopped_speed_threshold": 1
} }
] ]
}, },
@ -317,15 +387,28 @@ def update_sport(
return InvalidPayloadErrorResponse() return InvalidPayloadErrorResponse()
try: try:
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 not sport: if not sport:
return DataNotFoundErrorResponse('sports') return DataNotFoundErrorResponse('sports')
sport.is_active = sport_data.get('is_active') sport.is_active = sport_data.get('is_active')
db.session.commit() db.session.commit()
sport_preferences = UserSportPreference.query.filter_by(
user_id=user.id, sport_id=sport.id
).first()
return { return {
'status': 'success', 'status': 'success',
'data': {'sports': [sport.serialize(True)]}, 'data': {
'sports': [
sport.serialize(
is_admin=user.admin,
sport_preferences=sport_preferences.serialize()
if sport_preferences
else None,
)
]
},
} }
except (exc.IntegrityError, exc.OperationalError, ValueError) as e: except (exc.IntegrityError, exc.OperationalError, ValueError) as e: