Private
Public Access
1
0

Merge branch 'release/v15.7.0'

This commit is contained in:
Sander Roosendaal
2021-03-10 11:10:00 +01:00
8 changed files with 207 additions and 3 deletions

View File

@@ -184,7 +184,7 @@ ratelim==0.1.6
redis==3.5.3
requests==2.23.0
requests-oauthlib==1.2.0
rowingdata==3.1.8
rowingdata==3.2.7
rowingphysics==0.5.0
rq==0.13.0
rules==2.1

View File

@@ -1531,6 +1531,15 @@ class TrainingTargetForm(ModelForm):
).distinct().order_by("user__last_name","user__first_name")
class InstantPlan(models.Model):
uuid = models.UUIDField(primary_key=False,editable=True,default=uuid.uuid4)
owner = models.ForeignKey(User,on_delete=models.SET_NULL,null=True)
name = models.CharField(max_length=150,blank=True)
goal = models.CharField(max_length=150,blank=True)
description = models.TextField(max_length=300,blank=True)
duration = models.IntegerField(default=6)
target = models.TextField(max_length=300,blank=True)
hoursperweek = models.IntegerField(default=4)
@python_2_unicode_compatible

View File

@@ -30,6 +30,8 @@ import pandas as pd
from rowingdata import rowingdata as rrdata
from rowingdata import rower as rrower
from rowers.opaque import encoder
def to_pace(pace):
minutes, seconds = divmod(pace,60)
seconds, rest = divmod(seconds, 1)
@@ -455,7 +457,7 @@ def add_workouts_plannedsession(ws,ps,r):
w.plannedsession = ps
w.save()
result += 1
comments.append('Attached workout %i to session' % w.id)
comments.append('Attached workout %s to session' % encoder.encode_hex(w.id))
if ps.sessiontype == 'coursetest':
record = CourseTestResult(
userid=w.user.id,

View File

@@ -0,0 +1,43 @@
{% extends "newbase.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}{{ plan.name }}{% endblock %}
{% block main %}
<h1>{{ plan.name }}</h1>
<ul class="main-content">
<li class="grid_4">
<table width="100%" class="listtable">
<thead>
<tr>
<th>Week</th>
<th>Day</th>
<th>Workouts</th>
</tr>
</thead>
<tbody>
{% for day in trainingdays %}
<tr>
<td>{{ day.week }}</td>
<td>{{ day.order }}</td>
<td>
{% for workout in day.workouts %}
<h3>{{ workout.workoutName }}</h3>
{{ workout|steptostring|safe }}
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</li>
</ul>
{% endblock %}
{% block sidebar %}
{% include 'menu_plan.html' %}
{% endblock %}

View File

@@ -0,0 +1,24 @@
{% extends "newbase.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}Rowsandall Training Plans{% endblock %}
{% block main %}
<h1>Training Plans</h1>
<ul class="main-content">
{% for plan in trainingdict %}
<li>
<p><a href="/rowers/plans/{{ plan.ID }}">{{ plan.name }}</a></p>
<p>{{ plan.plan|lookup:"duration"}}</p>
</li>
{% endfor %}
</ul>
{% endblock %}
{% block sidebar %}
{% include 'menu_plan.html' %}
{% endblock %}

View File

@@ -26,12 +26,13 @@ from rowers.rower_rules import is_coach_user, is_workout_user, isplanmember,ispr
from rowers.mytypes import (
otwtypes,adaptivetypes,sexcategories,weightcategories,workouttypes,
)
from rowers.utils import NoTokenError
from rowers.utils import NoTokenError, step_to_string
import rowers.payments as payments
from rowers.opaque import encoder
from rowers.plannedsessions import ps_dict_get_description_html
import arrow
@@ -70,6 +71,11 @@ favanalysisicons = {
'cp':'fa-user-chart',
}
@register.filter
def steptostring(steps):
res = ps_dict_get_description_html(steps)
return res
# for verbose version of fav analysis
@register.filter
def verbose(s):

View File

@@ -740,6 +740,9 @@ urlpatterns = [
re_path(r'^test\_callback',views.rower_process_testcallback,name='rower_process_testcallback'),
re_path(r'^createplan/$',views.rower_create_trainingplan,name='rower_create_trainingplan'),
re_path(r'^createplan/user/(?P<id>\d+)/$',views.rower_create_trainingplan,name='rower_create_trainingplan'),
re_path(r'^plans/$', views.rower_select_instantplan, name='rower_select_instantplan'),
re_path(r'^plans/(?P<id>[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12})/$',
views.rower_view_instantplan, name='rower_view_instantplan'),
re_path(r'^deleteplan/(?P<pk>\d+)/$',login_required(
views.TrainingPlanDelete.as_view()),name='trainingplan_delete_view'),
re_path(r'^deletemicrocycle/(?P<pk>\d+)/$',login_required(

View File

@@ -2425,6 +2425,123 @@ class PlannedSessionDelete(DeleteView):
return obj
@user_passes_test(can_plan,login_url="/rowers/paidplans",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def rower_view_instantplan(request,id='',userid=0):
r = getrequestrower(request,userid=userid)
if not id:
raise Http404("Plan does not exist")
authorizationstring = 'Bearer '+settings.WORKOUTS_FIT_TOKEN
url = settings.WORKOUTS_FIT_URL+"/trainingplan/"+id
headers = {'Authorization':authorizationstring}
response = requests.get(url=url,headers=headers)
if response.status_code != 200:
messages.error(request,"Could not connect to the training plan server")
return HttpResponseRedirect(reverse('rower_select_instantplan'))
plan = response.json()
trainingdays = plan['plan']['trainingDays']
trainingdays2 = []
nextday = trainingdays.pop(0)
for i in range(plan['plan']['duration']):
if nextday['order'] == i+1:
nextday['week'] = (divmod(i,7)[0])+1
trainingdays2.append(nextday)
try:
nextday = trainingdays.pop(0)
except IndexError:
continue
else:
trainingdays2.append(
{
'order':i+1,
'week': (divmod(i,7)[0])+1,
'workouts':[]
}
)
breadcrumbs = [
{
'url':reverse('plannedsessions_view'),
'name': 'Sessions'
},
{
'url':reverse(rower_create_trainingplan,
kwargs={'id':userid}),
'name': 'Manage Plans and Targets'
},
{
'url':reverse('rower_select_instantplan'),
'name': 'Select Existing Plans'
},
{
'url':reverse('rower_view_instantplan',kwargs={
'id':id,
# 'userid':userid,
}),
'name':plan['name']
}
]
return render(request,
'instantplan.html',
{
'rower':r,
'active':'nav-plan',
'plan':plan,
'trainingdays':trainingdays2,
})
@user_passes_test(can_plan,login_url="/rowers/paidplans",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def rower_select_instantplan(request,id=0):
r = getrequestrower(request,userid=id)
themanager = getrower(request.user)
# get and present available plans
authorizationstring = 'Bearer '+settings.WORKOUTS_FIT_TOKEN
url = settings.WORKOUTS_FIT_URL+"/trainingplan/"
headers = {'Authorization':authorizationstring}
trainingdict = {}
response = requests.get(url=url, headers=headers)
if response.status_code != 200:
messages.error(request,"Could not connect to the training plan server")
else:
trainingdict = response.json()['plans']
breadcrumbs = [
{
'url':reverse('plannedsessions_view'),
'name': 'Sessions'
},
{
'url':reverse(rower_create_trainingplan,
kwargs={'id':id}),
'name': 'Manage Plans and Targets'
},
{
'url':reverse('rower_select_instantplan'),
'name': 'Select Existing Plans'
}
]
return render(request,
'instantplans.html',
{
'rower':r,
'active':'nav-plan',
'trainingdict':trainingdict,
})
@user_passes_test(can_plan,login_url="/rowers/paidplans",
message="This functionality requires a Coach or Self-Coach plan",