Merge branch 'release/v15.7.0'
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
43
rowers/templates/instantplan.html
Normal file
43
rowers/templates/instantplan.html
Normal 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 %}
|
||||
24
rowers/templates/instantplans.html
Normal file
24
rowers/templates/instantplans.html
Normal 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 %}
|
||||
@@ -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):
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user