first version actual/executed chart for plan
This commit is contained in:
@@ -232,6 +232,37 @@ def interactive_boxchart(datadf,fieldname,extratitle='',
|
||||
|
||||
return script,div
|
||||
|
||||
|
||||
def interactive_planchart(data,startdate,enddate):
|
||||
source = ColumnDataSource(data)
|
||||
|
||||
hv.extension('bokeh')
|
||||
|
||||
|
||||
yaxmaximum = data['executed'].max()
|
||||
if data['planned'].max() > yaxmaximum:
|
||||
yaxmaximum = data['planned'].max()
|
||||
|
||||
yrange1 = Range1d(start=0,end=1.1*yaxmaximum)
|
||||
|
||||
|
||||
tidy_df = data.melt(id_vars=['startdate'],value_vars=['planned','executed'])
|
||||
bars = hv.Bars(tidy_df,['startdate','variable'],['value'])
|
||||
bars.opts(
|
||||
opts.Bars(show_legend=True,tools=['tap','hover'],legend_position='bottom',show_frame=True))
|
||||
|
||||
p = hv.render(bars)
|
||||
|
||||
p.plot_width=550
|
||||
p.plot_height=350
|
||||
p.y_range = yrange1
|
||||
p.toolbar_location = 'above'
|
||||
p.sizing_mode = 'scale_width'
|
||||
|
||||
script,div = components(p)
|
||||
|
||||
return script,div
|
||||
|
||||
def interactive_activitychart(workouts,startdate,enddate,stack='type'):
|
||||
|
||||
dates = []
|
||||
@@ -352,7 +383,7 @@ def interactive_activitychart(workouts,startdate,enddate,stack='type'):
|
||||
|
||||
p.plot_width=550
|
||||
p.plot_height=350
|
||||
p.toolbar_location = None
|
||||
p.toolbar_location = 'above'
|
||||
p.sizing_mode = 'scale_width'
|
||||
url = "http://rowsandall.com/rowers/workout/@duration/"
|
||||
taptool = p.select(type=TapTool)
|
||||
|
||||
@@ -1981,7 +1981,7 @@ class TrainingMicroCycle(models.Model):
|
||||
if self.startdate < self.plan.startdate:
|
||||
self.startdate = self.plan.startdate
|
||||
|
||||
othercycles = TrainingMicroCycle.objects.filter(
|
||||
othercycles = TrainingMicroCycle.objects.filter(
|
||||
plan=self.plan).exclude(pk=self.pk).order_by("-startdate")
|
||||
|
||||
for othercycle in othercycles:
|
||||
|
||||
@@ -22,11 +22,14 @@ queue = django_rq.get_queue('default')
|
||||
queuelow = django_rq.get_queue('low')
|
||||
queuehigh = django_rq.get_queue('low')
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from rowers.models import (
|
||||
Rower, Workout,Team,
|
||||
GeoCourse, TrainingMicroCycle,TrainingMesoCycle,TrainingMacroCycle,
|
||||
TrainingPlan,PlannedSession,VirtualRaceResult,CourseTestResult,
|
||||
get_course_timezone, IndoorVirtualRaceResult,VirtualRace
|
||||
get_course_timezone, IndoorVirtualRaceResult,VirtualRace,createmacrofillers,
|
||||
createmesofillers,createmicrofillers,
|
||||
)
|
||||
|
||||
from rowers.courses import get_time_course
|
||||
@@ -47,6 +50,170 @@ from rowers.tasks import (
|
||||
)
|
||||
from rowers.utils import totaltime_sec_to_string
|
||||
|
||||
def checkscores(r,macrocycles):
|
||||
for m in macrocycles:
|
||||
createmesofillers(m)
|
||||
m.plantime = 0
|
||||
m.actualtime = 0
|
||||
m.plandistance = 0
|
||||
m.actualdistance = 0
|
||||
m.planrscore = 0
|
||||
m.actualrscore = 0
|
||||
m.plantrimp = 0
|
||||
m.actualtrimp = 0
|
||||
|
||||
|
||||
mesocycles = TrainingMesoCycle.objects.filter(
|
||||
plan=m,
|
||||
type='userdefined').order_by("startdate")
|
||||
|
||||
for me in mesocycles:
|
||||
me.plantime = 0
|
||||
me.actualtime = 0
|
||||
me.plandistance = 0
|
||||
me.actualdistance = 0
|
||||
me.planrscore = 0
|
||||
me.actualrscore = 0
|
||||
me.plantrimp = 0
|
||||
me.actualtrimp = 0
|
||||
|
||||
microcycles = TrainingMicroCycle.objects.filter(
|
||||
plan=me,
|
||||
type='userdefined').order_by("startdate")
|
||||
|
||||
for mm in microcycles:
|
||||
sps = get_sessions(r,startdate=mm.startdate,enddate=mm.enddate)
|
||||
|
||||
# sps = PlannedSession.objects.filter(
|
||||
# rower = r,
|
||||
# startdate__lte=mm.enddate,
|
||||
# enddate__gte=mm.startdate)
|
||||
|
||||
|
||||
mm.plantime = 0
|
||||
mm.actualtime = 0
|
||||
mm.plandistance = 0
|
||||
mm.actualdistance = 0
|
||||
mm.planrscore = 0
|
||||
mm.actualrscore = 0
|
||||
mm.plantrimp = 0
|
||||
mm.actualtrimp = 0
|
||||
|
||||
|
||||
if mm.type == 'userdefined':
|
||||
for ps in sps:
|
||||
ratio, status, cdate = is_session_complete(r,ps)
|
||||
if ps.sessionmode == 'time':
|
||||
mm.plantime += ps.sessionvalue
|
||||
mm.actualtime += int(ps.sessionvalue*ratio)
|
||||
elif ps.sessionmode == 'distance' and ps.sessiontype != 'race':
|
||||
mm.plandistance += ps.sessionvalue
|
||||
mm.actualdistance += int(ps.sessionvalue*ratio)
|
||||
elif ps.sessionmode == 'rScore':
|
||||
mm.planrscore += ps.sessionvalue
|
||||
mm.actualrscore += int(ps.sessionvalue*ratio)
|
||||
elif ps.sessionmode == 'TRIMP':
|
||||
mm.plantrimp += ps.sessionvalue
|
||||
mm.actualtrimp += int(ps.sessionvalue*ratio)
|
||||
|
||||
mm.save()
|
||||
|
||||
me.plantime += mm.plantime
|
||||
me.actualtime += mm.actualtime
|
||||
me.plandistance += mm.plandistance
|
||||
me.actualdistance += mm.actualdistance
|
||||
me.planrscore += mm.planrscore
|
||||
me.actualrscore += mm.actualrscore
|
||||
me.plantrimp += mm.plantrimp
|
||||
me.actualtrimp += mm.actualtrimp
|
||||
|
||||
if me.type == 'userdefined':
|
||||
me.save()
|
||||
|
||||
m.plantime += me.plantime
|
||||
m.actualtime += me.actualtime
|
||||
m.plandistance += me.plandistance
|
||||
m.actualdistance += me.actualdistance
|
||||
m.planrscore += me.planrscore
|
||||
m.actualrscore += me.actualrscore
|
||||
m.plantrimp += me.plantrimp
|
||||
m.actualtrimp += me.actualtrimp
|
||||
|
||||
|
||||
|
||||
if m.type == 'userdefined':
|
||||
m.save()
|
||||
|
||||
|
||||
|
||||
def get_execution_report(rower,startdate,enddate,plan=None):
|
||||
if plan:
|
||||
macros = TrainingMacroCycle.objects.filter(plan=plan).order_by("startdate")
|
||||
checkscores(rower,macros)
|
||||
mesos = TrainingMesoCycle.objects.filter(plan__in=macros).order_by("startdate")
|
||||
micros = TrainingMicroCycle.objects.filter(plan__in=mesos).order_by("startdate")
|
||||
else:
|
||||
plans = TrainingPlan.objects.filter(startdate__lte=startdate,enddate__gte=startdate)
|
||||
plans2 = TrainingPlan.objects.filter(enddate__lte=enddate,startdate__lte=enddate)
|
||||
plans = plans | plans2
|
||||
|
||||
plans = plans.exclude(status=False).order_by("-enddate")
|
||||
|
||||
if not plans:
|
||||
# make week cycles here
|
||||
return(0,'no plan found')
|
||||
else:
|
||||
plan = plans[0]
|
||||
macros = TrainingMacroCycle.objects.filter(plan=plan).order_by("startdate")
|
||||
checkscores(rower,macros)
|
||||
mesos = TrainingMesoCycle.objects.filter(plan__in=macros).order_by("startdate")
|
||||
micros = TrainingMicroCycle.objects.filter(plan__in=mesos).order_by("startdate")
|
||||
|
||||
# we've got micros, now get sessions
|
||||
startdates = []
|
||||
planned = []
|
||||
executed = []
|
||||
|
||||
for mm in micros:
|
||||
plannedscore = 0
|
||||
actualscore = 0
|
||||
sps = get_sessions(rower,startdate=mm.startdate,enddate=mm.enddate)
|
||||
for ps in sps:
|
||||
if ps.sessionmode == 'rscore':
|
||||
plannedscore += ps.planrscore
|
||||
actualscore += ps.actualrscore
|
||||
else:
|
||||
ratio, status, cdate = is_session_complete(rower,ps)
|
||||
ws = Workout.objects.filter(user=rower,plannedsession=ps)
|
||||
|
||||
for w in ws:
|
||||
if w.rscore != 0:
|
||||
plannedscore += w.rscore/ratio
|
||||
actualscore += w.rscore
|
||||
elif w.hrtss != 0:
|
||||
plannedscore += w.hrtss/ratio
|
||||
actualscore += w.hrtss
|
||||
else:
|
||||
minutes = w.duration.hour*60+w.duration.minute
|
||||
plannedscore += minutes/ratio
|
||||
actualscore += minutes
|
||||
|
||||
actualscore = int(actualscore)
|
||||
plannedscore = int(plannedscore)
|
||||
|
||||
startdates += [mm.startdate]
|
||||
planned += [plannedscore]
|
||||
executed += [actualscore]
|
||||
|
||||
|
||||
data = pd.DataFrame({
|
||||
'startdate':startdates,
|
||||
'planned':planned,
|
||||
'executed':executed,
|
||||
})
|
||||
|
||||
return(data,'ok')
|
||||
|
||||
def get_indoorraces(workout):
|
||||
races1 = VirtualRace.objects.filter(
|
||||
sessiontype='indoorrace',
|
||||
|
||||
35
rowers/templates/trainingplan_chart.html
Normal file
35
rowers/templates/trainingplan_chart.html
Normal file
@@ -0,0 +1,35 @@
|
||||
{% extends "newbase.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Rowsandall Training Plans{% endblock %}
|
||||
|
||||
|
||||
{% block main %}
|
||||
<script src="https://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
|
||||
<script src="https://cdn.pydata.org/bokeh/release/bokeh-widgets-1.0.4.min.js"></script>
|
||||
<script async="true" type="text/javascript">
|
||||
Bokeh.set_log_level("info");
|
||||
</script>
|
||||
|
||||
|
||||
<h1>Training Plan - {{ plan.name }}</h1>
|
||||
|
||||
{{ the_script | safe }}
|
||||
|
||||
<ul class="main-content">
|
||||
<li class="grid_4">
|
||||
<div id="theplot" class="flexplot">
|
||||
{{ the_div|safe }}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
{% endblock %}
|
||||
|
||||
{% block sidebar %}
|
||||
{% include 'menu_plan.html' %}
|
||||
{% endblock %}
|
||||
@@ -587,6 +587,10 @@ urlpatterns = [
|
||||
name='rower_trainingplan_view'),
|
||||
re_path(r'^plan/(?P<id>\d+)/macro/(?P<thismacroid>\d+)/user/(?P<userid>\d+)/$',views.rower_trainingplan_view,
|
||||
name='rower_trainingplan_view'),
|
||||
re_path(r'^plan/(?P<id>\d+)/execution/$',views.rower_trainingplan_execution_view,
|
||||
name='rower_trainingplan_execution_view'),
|
||||
re_path(r'^plan/(?P<id>\d+)/execution/user/(?P<userid>\d+)/$',views.rower_trainingplan_execution_view,
|
||||
name='rower_trainingplan_execution_view'),
|
||||
re_path(r'^macrocycle/(?P<pk>\d+)/$',login_required(
|
||||
views.TrainingMacroCycleUpdate.as_view()),
|
||||
name='macrocycle_update_view'),
|
||||
|
||||
@@ -1053,7 +1053,7 @@ def plannedsessions_coach_view(request,
|
||||
}
|
||||
)
|
||||
|
||||
from rowers.plannedsessions import cratiocolors
|
||||
from rowers.plannedsessions import cratiocolors,checkscores
|
||||
|
||||
@login_required()
|
||||
def plannedsessions_view(request,
|
||||
@@ -2329,6 +2329,63 @@ class MacroCycleDelete(DeleteView):
|
||||
return obj
|
||||
|
||||
|
||||
@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans",
|
||||
message="This functionality requires a Coach or Self-Coach plan",
|
||||
redirect_field_name=None)
|
||||
def rower_trainingplan_execution_view(request,
|
||||
id=0,
|
||||
userid=0):
|
||||
|
||||
startdate,enddate = get_dates_timeperiod(request)
|
||||
|
||||
try:
|
||||
plan = TrainingPlan.objects.get(id=id)
|
||||
except TrainingPlan.DoesNotExist:
|
||||
raise Http4040("Training Plan Does Not Exist")
|
||||
|
||||
r = getrequestrower(request,userid=userid)
|
||||
|
||||
if not checkaccessuser(request.user,plan.manager):
|
||||
if request.user.rower not in plan.rowers.all():
|
||||
raise PermissionDenied("Access denied")
|
||||
|
||||
data,message = get_execution_report(r,plan.startdate,plan.enddate,plan=plan)
|
||||
|
||||
script, div = interactive_planchart(data,plan.startdate,plan.enddate)
|
||||
|
||||
breadcrumbs = [
|
||||
{
|
||||
'url':reverse(plannedsessions_view,
|
||||
kwargs={'userid':userid}),
|
||||
'name': 'Plan'
|
||||
},
|
||||
{
|
||||
'url':reverse(rower_trainingplan_view,
|
||||
kwargs={'userid':userid,
|
||||
'id':id}),
|
||||
'name': plan.name
|
||||
},
|
||||
{
|
||||
'url':reverse(rower_trainingplan_execution_view,
|
||||
kwargs={'userid':userid,
|
||||
'id':id}),
|
||||
'name': 'Execution'
|
||||
}
|
||||
]
|
||||
|
||||
return render(request,'trainingplan_chart.html',
|
||||
{
|
||||
'plan':plan,
|
||||
'todays_date': timezone.now().date(),
|
||||
'active': 'nav-plan',
|
||||
'breadcrumbs':breadcrumbs,
|
||||
'rower':r,
|
||||
'the_script':script,
|
||||
'the_div':div
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans",
|
||||
message="This functionality requires a Coach or Self-Coach plan",
|
||||
redirect_field_name=None)
|
||||
@@ -2359,99 +2416,7 @@ def rower_trainingplan_view(request,
|
||||
plan=plan,
|
||||
type='userdefined').order_by("startdate")
|
||||
|
||||
|
||||
for m in macrocycles:
|
||||
createmesofillers(m)
|
||||
m.plantime = 0
|
||||
m.actualtime = 0
|
||||
m.plandistance = 0
|
||||
m.actualdistance = 0
|
||||
m.planrscore = 0
|
||||
m.actualrscore = 0
|
||||
m.plantrimp = 0
|
||||
m.actualtrimp = 0
|
||||
|
||||
|
||||
mesocycles = TrainingMesoCycle.objects.filter(
|
||||
plan=m,
|
||||
type='userdefined').order_by("startdate")
|
||||
|
||||
for me in mesocycles:
|
||||
me.plantime = 0
|
||||
me.actualtime = 0
|
||||
me.plandistance = 0
|
||||
me.actualdistance = 0
|
||||
me.planrscore = 0
|
||||
me.actualrscore = 0
|
||||
me.plantrimp = 0
|
||||
me.actualtrimp = 0
|
||||
|
||||
microcycles = TrainingMicroCycle.objects.filter(
|
||||
plan=me,
|
||||
type='userdefined').order_by("startdate")
|
||||
|
||||
for mm in microcycles:
|
||||
sps = get_sessions(r,startdate=mm.startdate,enddate=mm.enddate)
|
||||
|
||||
# sps = PlannedSession.objects.filter(
|
||||
# rower = r,
|
||||
# startdate__lte=mm.enddate,
|
||||
# enddate__gte=mm.startdate)
|
||||
|
||||
|
||||
mm.plantime = 0
|
||||
mm.actualtime = 0
|
||||
mm.plandistance = 0
|
||||
mm.actualdistance = 0
|
||||
mm.planrscore = 0
|
||||
mm.actualrscore = 0
|
||||
mm.plantrimp = 0
|
||||
mm.actualtrimp = 0
|
||||
|
||||
|
||||
if mm.type == 'userdefined':
|
||||
for ps in sps:
|
||||
ratio, status, cdate = is_session_complete(r,ps)
|
||||
if ps.sessionmode == 'time':
|
||||
mm.plantime += ps.sessionvalue
|
||||
mm.actualtime += int(ps.sessionvalue*ratio)
|
||||
elif ps.sessionmode == 'distance' and ps.sessiontype != 'race':
|
||||
mm.plandistance += ps.sessionvalue
|
||||
mm.actualdistance += int(ps.sessionvalue*ratio)
|
||||
elif ps.sessionmode == 'rScore':
|
||||
mm.planrscore += ps.sessionvalue
|
||||
mm.actualrscore += int(ps.sessionvalue*ratio)
|
||||
elif ps.sessionmode == 'TRIMP':
|
||||
mm.plantrimp += ps.sessionvalue
|
||||
mm.actualtrimp += int(ps.sessionvalue*ratio)
|
||||
|
||||
mm.save()
|
||||
|
||||
me.plantime += mm.plantime
|
||||
me.actualtime += mm.actualtime
|
||||
me.plandistance += mm.plandistance
|
||||
me.actualdistance += mm.actualdistance
|
||||
me.planrscore += mm.planrscore
|
||||
me.actualrscore += mm.actualrscore
|
||||
me.plantrimp += mm.plantrimp
|
||||
me.actualtrimp += mm.actualtrimp
|
||||
|
||||
if me.type == 'userdefined':
|
||||
me.save()
|
||||
|
||||
m.plantime += me.plantime
|
||||
m.actualtime += me.actualtime
|
||||
m.plandistance += me.plandistance
|
||||
m.actualdistance += me.actualdistance
|
||||
m.planrscore += me.planrscore
|
||||
m.actualrscore += me.actualrscore
|
||||
m.plantrimp += me.plantrimp
|
||||
m.actualtrimp += me.actualtrimp
|
||||
|
||||
|
||||
|
||||
if m.type == 'userdefined':
|
||||
m.save()
|
||||
checkscores(r,macrocycles)
|
||||
|
||||
createmacrofillers(plan)
|
||||
macrocycles = TrainingMacroCycle.objects.filter(plan=plan).order_by("startdate")
|
||||
|
||||
Reference in New Issue
Block a user