Private
Public Access
1
0

Merge branch 'develop' into feature/django2

This commit is contained in:
Sander Roosendaal
2019-04-05 19:44:16 +02:00
16 changed files with 687 additions and 207 deletions

View File

@@ -1007,7 +1007,10 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
f = rowdatadf['TimeStamp (sec)'].diff().mean() f = rowdatadf['TimeStamp (sec)'].diff().mean()
if f != 0: if f != 0:
windowsize = 2*(int(10./(f)))+1 try:
windowsize = 2*(int(10./(f)))+1
except ValueError:
windowsize = 1
else: else:
windowsize = 1 windowsize = 1
if windowsize <= 3: if windowsize <= 3:

View File

@@ -25,6 +25,12 @@ import datetime
from django.forms import formset_factory from django.forms import formset_factory
from rowers.utils import landingpages from rowers.utils import landingpages
from rowers.metrics import axes from rowers.metrics import axes
from rowers.metrics import axlabels
formaxlabels = axlabels.copy()
formaxlabels.pop('None')
parchoices = list(sorted(formaxlabels.items(), key = lambda x:x[1]))
class FlexibleDecimalField(forms.DecimalField): class FlexibleDecimalField(forms.DecimalField):
@@ -710,6 +716,12 @@ class DataFrameColumnsForm(forms.Form):
cols = forms.MultipleChoiceField(choices=colchoices, cols = forms.MultipleChoiceField(choices=colchoices,
label='Table Columns') label='Table Columns')
class HistoForm(forms.Form):
includereststrokes = forms.BooleanField(initial=False,label='Include Rest Strokes',required=False)
histoparam = forms.ChoiceField(choices=parchoices,initial='power',
label='Metric')
# form to select modality and boat type for trend flex # form to select modality and boat type for trend flex
class TrendFlexModalForm(forms.Form): class TrendFlexModalForm(forms.Form):
modality = forms.ChoiceField(choices=workouttypes, modality = forms.ChoiceField(choices=workouttypes,
@@ -722,7 +734,8 @@ class TrendFlexModalForm(forms.Form):
label='Only Ranking Pieces', label='Only Ranking Pieces',
required=False) required=False)
# This form sets options for the summary stats page # This form sets options for the summary stats page
class StatsOptionsForm(forms.Form): class StatsOptionsForm(forms.Form):
includereststrokes = forms.BooleanField(initial=False,label='Include Rest Strokes',required=False) includereststrokes = forms.BooleanField(initial=False,label='Include Rest Strokes',required=False)
@@ -796,12 +809,6 @@ class PlannedSessionMultipleCloneForm(forms.Form):
label='Planned Sessions' label='Planned Sessions'
) )
from rowers.metrics import axlabels
formaxlabels = axlabels.copy()
formaxlabels.pop('None')
parchoices = list(sorted(formaxlabels.items(), key = lambda x:x[1]))
class BoxPlotChoiceForm(forms.Form): class BoxPlotChoiceForm(forms.Form):
yparam = forms.ChoiceField(choices=parchoices,initial='spm', yparam = forms.ChoiceField(choices=parchoices,initial='spm',
@@ -1181,24 +1188,39 @@ class FlexOptionsForm(forms.Form):
('line','Line Plot'), ('line','Line Plot'),
('scatter','Scatter Plot'), ('scatter','Scatter Plot'),
) )
plottype = forms.ChoiceField(choices=plotchoices,initial='scatter', plottype = forms.ChoiceField(choices=plotchoices,initial='line',
label='Chart Type') label='Chart Type')
class ForceCurveOptionsForm(forms.Form):
includereststrokes = forms.BooleanField(initial=False, required = False,
label='Include Rest Strokes')
plotchoices = (
('line','Force Curve Collection Plot'),
('scatter','Peak Force Scatter Plot'),
('none','Only aggregrate data')
)
plottype = forms.ChoiceField(choices=plotchoices,initial='line',
label='Individual Stroke Chart Type')
class FlexAxesForm(forms.Form): class FlexAxesForm(forms.Form):
axchoices = ( axchoices = list(
(ax[0],ax[1]) for ax in axes if ax[0] not in ['cumdist','None'] (ax[0],ax[1]) for ax in axes if ax[0] not in ['cumdist','None']
) )
axchoices = dict((x,y) for x,y in axchoices)
axchoices = list(sorted(axchoices.items(), key = lambda x:x[1]))
yaxchoices = ( yaxchoices = list((ax[0],ax[1]) for ax in axes if ax[0] not in ['cumdist','distance','time'])
(ax[0], ax[1]) for ax in axes if ax[0] not in ['cumdist','distance','time'] yaxchoices = dict((x,y) for x,y in yaxchoices)
) yaxchoices = list(sorted(yaxchoices.items(), key = lambda x:x[1]))
yaxchoices2 = (
(ax[0], ax[1]) for ax in axes if ax[0] not in ['cumdist','distance','time']
)
yaxchoices2 = list(
(ax[0],ax[1]) for ax in axes if ax[0] not in ['cumdist','distance','time']
)
yaxchoices2 = dict((x,y) for x,y in yaxchoices2)
yaxchoices2 = list(sorted(yaxchoices2.items(), key = lambda x:x[1]))
xaxis = forms.ChoiceField( xaxis = forms.ChoiceField(
choices=axchoices,label='X-Axis',required=True) choices=axchoices,label='X-Axis',required=True)

View File

@@ -43,7 +43,7 @@ from django.contrib.auth.decorators import login_required
# from .models import Profile # from .models import Profile
from rowingdata import rowingdata, make_cumvalues from rowingdata import rowingdata, make_cumvalues
import pandas as pd import pandas as pd
from rowers.models import Rower,Workout,checkworkoutuser from rowers.models import Rower,Workout,checkworkoutuser,TombStone
import rowers.mytypes as mytypes import rowers.mytypes as mytypes
from rowsandall_app.settings import ( from rowsandall_app.settings import (
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET,

View File

@@ -17,6 +17,7 @@ from math import pi
from django.utils import timezone from django.utils import timezone
from bokeh.palettes import Dark2_8 as palette from bokeh.palettes import Dark2_8 as palette
from bokeh.models.glyphs import MultiLine
import itertools import itertools
from bokeh.plotting import figure, ColumnDataSource, Figure,curdoc from bokeh.plotting import figure, ColumnDataSource, Figure,curdoc
from bokeh.models import CustomJS,Slider, TextInput,BoxAnnotation from bokeh.models import CustomJS,Slider, TextInput,BoxAnnotation
@@ -64,6 +65,7 @@ activate(settings.TIME_ZONE)
thetimezone = get_current_timezone() thetimezone = get_current_timezone()
from scipy.stats import linregress,percentileofscore from scipy.stats import linregress,percentileofscore
from scipy.spatial import ConvexHull,Delaunay
from scipy import optimize from scipy import optimize
from scipy.signal import savgol_filter from scipy.signal import savgol_filter
from scipy.interpolate import griddata from scipy.interpolate import griddata
@@ -164,7 +166,8 @@ def tailwind(bearing,vwind,winddir):
from rowers.dataprep import nicepaceformat,niceformat from rowers.dataprep import nicepaceformat,niceformat
from rowers.dataprep import timedeltaconv from rowers.dataprep import timedeltaconv
def interactive_boxchart(datadf,fieldname,extratitle=''): def interactive_boxchart(datadf,fieldname,extratitle='',
spmmin=0,spmmax=0,workmin=0,workmax=0):
if datadf.empty: if datadf.empty:
return '','It looks like there are no data matching your filter' return '','It looks like there are no data matching your filter'
@@ -186,15 +189,6 @@ def interactive_boxchart(datadf,fieldname,extratitle=''):
plot = hv.render(boxwhiskers) plot = hv.render(boxwhiskers)
#plot = BoxPlot(datadf, values=fieldname, label='date',
# legend=False,
# title=axlabels[fieldname]+' '+extratitle,
# outliers=False,
# tools=TOOLS,
# toolbar_location="above",
# toolbar_sticky=False,
# x_mapper_type='datetime',plot_width=920)
yrange1 = Range1d(start=yaxminima[fieldname],end=yaxmaxima[fieldname]) yrange1 = Range1d(start=yaxminima[fieldname],end=yaxmaxima[fieldname])
plot.y_range = yrange1 plot.y_range = yrange1
plot.sizing_mode = 'scale_width' plot.sizing_mode = 'scale_width'
@@ -221,6 +215,18 @@ def interactive_boxchart(datadf,fieldname,extratitle=''):
plot.plot_width=920 plot.plot_width=920
plot.plot_height=600 plot.plot_height=600
slidertext = 'SPM: {:.0f}-{:.0f}, WpS: {:.0f}-{:.0f}'.format(
spmmin,spmmax,workmin,workmax
)
sliderlabel = Label(x=50,y=20,x_units='screen',y_units='screen',
text=slidertext,
background_fill_alpha=0.7,
background_fill_color='white',
text_color='black',text_font_size='10pt',
)
plot.add_layout(sliderlabel)
script, div = components(plot) script, div = components(plot)
return script,div return script,div
@@ -331,45 +337,12 @@ def interactive_activitychart(workouts,startdate,enddate,stack='type'):
p.toolbar_location = None p.toolbar_location = None
p.sizing_mode = 'scale_width' p.sizing_mode = 'scale_width'
# p = hv.Bars(df,values='duration',
# label = CatAttr(columns=['date'], sort=False),
# xlabel='Date',
# ylabel='Time',
# title='Activity {d1} to {d2}'.format(
# d1 = startdate.strftime("%Y-%m-%d"),
# d2 = enddate.strftime("%Y-%m-%d"),
# ),
# stack=stack,
# plot_width=350,
# plot_height=250,
# toolbar_location = None,
# )
# for legend in p.legend:
# new_items = []
# for legend_item in legend.items:
# it = legend_item.label['value']
# tot = df[df[stack]==it].duration.sum()
# if tot != 0:
# new_items.append(legend_item)
# legend.items = new_items
# p.legend.location = "top_left"
# p.legend.background_fill_alpha = 0.7
#p.sizing_mode = 'scale_width'
#p.sizing_mode = 'stretch_both'
#p.yaxis.axis_label = 'Minutes'
script,div = components(p) script,div = components(p)
return script,div return script,div
def interactive_forcecurve(theworkouts,workstrokesonly=False): def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'):
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
ids = [int(w.id) for w in theworkouts] ids = [int(w.id) for w in theworkouts]
@@ -399,63 +372,296 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
if rowdata.empty: if rowdata.empty:
return "","No Valid Data Available","","" return "","No Valid Data Available","",""
# quick linear regression
# peakforce = slope*peakforceangle + intercept
try: try:
catchav = rowdata['catch'].mean() slope, intercept, r,p,stderr = linregress(rowdata['peakforceangle'],rowdata['peakforce'])
except KeyError:
slope = 0
intercept = 0
try:
covariancematrix = np.cov(rowdata['peakforceangle'],y=rowdata['peakforce'])
eig_vals, eig_vecs = np.linalg.eig(covariancematrix)
a = rowdata['peakforceangle']-rowdata['peakforceangle'].median()
F = rowdata['peakforce']-rowdata['peakforce'].median()
Rinv = eig_vecs
R = np.linalg.inv(Rinv)
x = R[0,0]*a+R[0,1]*F
y = R[1,0]*a+R[1,1]*F
x05 = x.quantile(q=0.01)
x25 = x.quantile(q=0.15)
x75 = x.quantile(q=0.85)
x95 = x.quantile(q=0.99)
y05 = y.quantile(q=0.01)
y25 = y.quantile(q=0.15)
y75 = y.quantile(q=0.85)
y95 = y.quantile(q=0.99)
a25 = Rinv[0,0]*x25 + rowdata['peakforceangle'].median()
F25 = Rinv[1,0]*x25 + rowdata['peakforce'].median()
a25b = Rinv[0,1]*y25 + rowdata['peakforceangle'].median()
F25b = Rinv[1,1]*y25 + rowdata['peakforce'].median()
a75 = Rinv[0,0]*x75 + rowdata['peakforceangle'].median()
F75 = Rinv[1,0]*x75 + rowdata['peakforce'].median()
a75b = Rinv[0,1]*y75 + rowdata['peakforceangle'].median()
F75b = Rinv[1,1]*y75 + rowdata['peakforce'].median()
a05 = Rinv[0,0]*x05 + rowdata['peakforceangle'].median()
F05 = Rinv[1,0]*x05 + rowdata['peakforce'].median()
a05b = Rinv[0,1]*y05 + rowdata['peakforceangle'].median()
F05b = Rinv[1,1]*y05 + rowdata['peakforce'].median()
a95 = Rinv[0,0]*x95 + rowdata['peakforceangle'].median()
F95 = Rinv[1,0]*x95 + rowdata['peakforce'].median()
a95b = Rinv[0,1]*y95 + rowdata['peakforceangle'].median()
F95b = Rinv[1,1]*y95 + rowdata['peakforce'].median()
except KeyError:
a25 = 0
F25 = 0
a25b = 0
F25b = 0
a75 = 0
F75 = 0
a75b = 0
F75b = 0
a05 = 0
F05 = 0
a05b = 0
F05b = 0
a95 = 0
F95 = 0
a95b = 0
F95b = 0
try:
catchav = rowdata['catch'].median()
catch25 = rowdata['catch'].quantile(q=0.25)
catch75 = rowdata['catch'].quantile(q=0.75)
catch05 = rowdata['catch'].quantile(q=0.05)
catch95 = rowdata['catch'].quantile(q=0.95)
except KeyError: except KeyError:
catchav = 0 catchav = 0
catch25 = 0
catch75 = 0
catch05 = 0
catch95 = 0
try: try:
finishav = rowdata['finish'].mean() finishav = rowdata['finish'].median()
finish25 = rowdata['finish'].quantile(q=0.25)
finish75 = rowdata['finish'].quantile(q=0.75)
finish05 = rowdata['finish'].quantile(q=0.05)
finish95 = rowdata['finish'].quantile(q=0.95)
except KeyError: except KeyError:
finishav = 0 finishav = 0
finish25 = 0
finish75 = 0
finish05 = 0
finish95 = 0
try: try:
washav = rowdata['wash'].mean() washav = (rowdata['finish']-rowdata['wash']).median()
wash25 = (rowdata['finish']-rowdata['wash']).quantile(q=0.25)
wash75 = (rowdata['finish']-rowdata['wash']).quantile(q=0.75)
wash05 = (rowdata['finish']-rowdata['wash']).quantile(q=0.05)
wash95 = (rowdata['finish']-rowdata['wash']).quantile(q=0.95)
except KeyError: except KeyError:
washav = 0 washav = 0
wash25 = 0
wash75 = 0
wash05 = 0
wash95 = 0
try: try:
slipav = rowdata['slip'].mean() slipav = (rowdata['slip']+rowdata['catch']).median()
slip25 = (rowdata['slip']+rowdata['catch']).quantile(q=0.25)
slip75 = (rowdata['slip']+rowdata['catch']).quantile(q=0.75)
slip05 = (rowdata['slip']+rowdata['catch']).quantile(q=0.05)
slip95 = (rowdata['slip']+rowdata['catch']).quantile(q=0.95)
except KeyError: except KeyError:
slipav = 0 slipav = 0
slip25 = 0
slip75 = 0
slip05 = 0
slip95 = 0
try: try:
peakforceav = rowdata['peakforce'].mean() peakforceav = rowdata['peakforce'].median()
peakforce25 = rowdata['peakforce'].quantile(q=0.25)
peakforce75 = rowdata['peakforce'].quantile(q=0.75)
peakforce05 = rowdata['peakforce'].quantile(q=0.05)
peakforce95 = rowdata['peakforce'].quantile(q=0.95)
except KeyError: except KeyError:
peakforceav = 0 peakforceav = 0
peakforce25 = 0
peakforce75 = 0
peakforce05 = 0
peakforce95 = 0
try: try:
averageforceav = rowdata['averageforce'].mean() averageforceav = rowdata['averageforce'].median()
except KeyError: except KeyError:
averageforceav = 0 averageforceav = 0
try: try:
peakforceangleav = rowdata['peakforceangle'].mean() peakforceangleav = rowdata['peakforceangle'].median()
peakforceangle05 = rowdata['peakforceangle'].quantile(q=0.05)
peakforceangle25 = rowdata['peakforceangle'].quantile(q=0.25)
peakforceangle75 = rowdata['peakforceangle'].quantile(q=0.75)
peakforceangle95 = rowdata['peakforceangle'].quantile(q=0.95)
except KeyError: except KeyError:
peakforceangleav = 0 peakforceangleav = 0
peakforceangle25 = 0
peakforceangle75 = 0
peakforceangle05 = 0
peakforceangle95 = 0
#thresholdforce /= 4.45 # N to lbs
thresholdforce = 100 if 'x' in boattype else 200
points2575 = [
(catch25,0), #0
(slip25,thresholdforce), #1
(a75,F75),#4
(a25b,F25b), #9
(a25,F25), #2
(wash75,thresholdforce), #5
(finish75,0), #6
(finish25,0), #7
(wash25,thresholdforce), #8
(a75b,F75b), #3
(slip75,thresholdforce), #10
(catch75,0), #11
]
points0595 = [
(catch05,0), #0
(slip05,thresholdforce), #1
(a95,F95),#4
(a05b,F05b), #9
(a05,F05), #2
(wash95,thresholdforce), #5
(finish95,0), #6
(finish05,0), #7
(wash05,thresholdforce), #8
(a95b,F95b), #3
(slip95,thresholdforce), #10
(catch95,0), #11
]
angles2575 = []
forces2575 = []
for x,y in points2575:
angles2575.append(x)
forces2575.append(y)
angles0595 = []
forces0595 = []
for x,y in points0595:
angles0595.append(x)
forces0595.append(y)
x = [catchav, x = [catchav,
catchav+slipav, slipav,
peakforceangleav, peakforceangleav,
finishav-washav, washav,
finishav] finishav]
thresholdforce = 100 if 'x' in boattype else 200
#thresholdforce /= 4.45 # N to lbs
y = [0,thresholdforce, y = [0,thresholdforce,
peakforceav, peakforceav,
thresholdforce,0] thresholdforce,0]
source = ColumnDataSource( source = ColumnDataSource(
data = dict( data = dict(
x = x, x = x,
y = y, y = y,
)) ))
sourceslipwash = ColumnDataSource(
data = dict(
xslip = [slipav,washav],
yslip = [thresholdforce,thresholdforce]
)
)
sourcetrend = ColumnDataSource(
data = dict(
x = [peakforceangle25,peakforceangle75],
y = [peakforce25,peakforce75]
)
)
sourcefit = ColumnDataSource(
data = dict(
x = np.array([peakforceangle25,peakforceangle75]),
y = slope*np.array([peakforceangle25,peakforceangle75])+intercept
)
)
source2 = ColumnDataSource( source2 = ColumnDataSource(
rowdata rowdata
) )
if plottype == 'scatter':
sourcepoints = ColumnDataSource(
data = dict(
peakforceangle = rowdata['peakforceangle'],
peakforce = rowdata['peakforce']
)
)
else:
sourcepoints = ColumnDataSource(
data = dict(
peakforceangle = [],
peakforce = []
))
sourcerange = ColumnDataSource(
data = dict(
x2575 = angles2575,
y2575 = forces2575,
x0595 = angles0595,
y0595 = forces0595,
)
)
plot = Figure(tools=TOOLS, plot = Figure(tools=TOOLS,
toolbar_sticky=False,toolbar_location="above") toolbar_sticky=False,toolbar_location="above")
@@ -489,59 +695,142 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
avf = Span(location=averageforceav,dimension='width',line_color='blue', avf = Span(location=averageforceav,dimension='width',line_color='blue',
line_dash=[6,6],line_width=2) line_dash=[6,6],line_width=2)
plot.patch('x0595','y0595',source=sourcerange,color="red",alpha=0.05)
plot.patch('x2575','y2575',source=sourcerange,color="red",alpha=0.2)
plot.line('x','y',source=source,color="red")
plot.circle('xslip','yslip',source=sourceslipwash,color="red")
plot.circle('peakforceangle','peakforce',source=sourcepoints,color='black',alpha=0.1)
if plottype == 'line':
multilinedatax = []
multilinedatay = []
for i in range(len(rowdata)):
try:
x = [
rowdata['catch'].values[i],
rowdata['slip'].values[i]+rowdata['catch'].values[i],
rowdata['peakforceangle'].values[i],
rowdata['finish'].values[i]-rowdata['wash'].values[i],
rowdata['finish'].values[i]
]
y = [
0,
thresholdforce,
rowdata['peakforce'].values[i],
thresholdforce,
0]
except KeyError:
x = [0,0]
y = [0,0]
multilinedatax.append(x)
multilinedatay.append(y)
sourcemultiline = ColumnDataSource(dict(
x=multilinedatax,
y=multilinedatay,
))
sourcemultiline2 = ColumnDataSource(dict(
x=multilinedatax,
y=multilinedatay,
))
glyph = MultiLine(xs='x',ys='y',line_color='black',line_alpha=0.05)
plot.add_glyph(sourcemultiline,glyph)
else:
sourcemultiline = ColumnDataSource(dict(
x=[],y=[]))
sourcemultiline2 = ColumnDataSource(dict(
x=[],y=[]))
plot.line('x','y',source=source,color="red") plot.line('x','y',source=source,color="red")
plot.add_layout(avf) plot.add_layout(avf)
peakflabel = Label(x=355,y=430,x_units='screen',y_units='screen', peakflabel = Label(x=410,y=460,x_units='screen',y_units='screen',
text="Fpeak: {peakforceav:6.2f}".format(peakforceav=peakforceav), text="Fpeak: {peakforceav:6.2f}".format(peakforceav=peakforceav),
background_fill_alpha=.7, background_fill_alpha=.7,
background_fill_color='white', background_fill_color='white',
text_color='blue', text_color='blue',
) )
avflabel = Label(x=365,y=400,x_units='screen',y_units='screen', avflabel = Label(x=420,y=430,x_units='screen',y_units='screen',
text="Favg: {averageforceav:6.2f}".format(averageforceav=averageforceav), text="Favg: {averageforceav:6.2f}".format(averageforceav=averageforceav),
background_fill_alpha=.7, background_fill_alpha=.7,
background_fill_color='white', background_fill_color='white',
text_color='blue', text_color='blue',
) )
catchlabel = Label(x=360,y=370,x_units='screen',y_units='screen', catchlabel = Label(x=415,y=400,x_units='screen',y_units='screen',
text="Catch: {catchav:6.2f}".format(catchav=catchav), text="Catch: {catchav:6.2f}".format(catchav=catchav),
background_fill_alpha=0.7, background_fill_alpha=0.7,
background_fill_color='white', background_fill_color='white',
text_color='red', text_color='red',
) )
peakforceanglelabel = Label(x=320,y=340,x_units='screen',y_units='screen', peakforceanglelabel = Label(x=375,y=370,x_units='screen',y_units='screen',
text="Peak angle: {peakforceangleav:6.2f}".format(peakforceangleav=peakforceangleav), text="Peak angle: {peakforceangleav:6.2f}".format(peakforceangleav=peakforceangleav),
background_fill_alpha=0.7, background_fill_alpha=0.7,
background_fill_color='white', background_fill_color='white',
text_color='red', text_color='red',
) )
finishlabel = Label(x=355,y=310,x_units='screen',y_units='screen', finishlabel = Label(x=410,y=340,x_units='screen',y_units='screen',
text="Finish: {finishav:6.2f}".format(finishav=finishav), text="Finish: {finishav:6.2f}".format(finishav=finishav),
background_fill_alpha=0.7, background_fill_alpha=0.7,
background_fill_color='white', background_fill_color='white',
text_color='red', text_color='red',
) )
sliplabel = Label(x=370,y=280,x_units='screen',y_units='screen', sliplabel = Label(x=425,y=310,x_units='screen',y_units='screen',
text="Slip: {slipav:6.2f}".format(slipav=slipav), text="Slip: {slipav:6.2f}".format(slipav=slipav-catchav),
background_fill_alpha=0.7, background_fill_alpha=0.7,
background_fill_color='white', background_fill_color='white',
text_color='red', text_color='red',
) )
washlabel = Label(x=360,y=250,x_units='screen',y_units='screen', washlabel = Label(x=415,y=280,x_units='screen',y_units='screen',
text="Wash: {washav:6.2f}".format(washav=washav), text="Wash: {washav:6.2f}".format(washav=finishav-washav),
background_fill_alpha=0.7, background_fill_alpha=0.7,
background_fill_color='white', background_fill_color='white',
text_color='red', text_color='red',
) )
lengthlabel = Label(x=405,y=250, x_units='screen',y_units='screen',
text="Length: {length:6.2f}".format(length=finishav-catchav),
background_fill_alpha=0.7,
background_fill_color='white',
text_color='green'
)
efflengthlabel = Label(x=340,y=220, x_units='screen',y_units='screen',
text="Effective Length: {length:6.2f}".format(length=washav-slipav),
background_fill_alpha=0.7,
background_fill_color='white',
text_color='green'
)
annolabel = Label(x=50,y=450,x_units='screen',y_units='screen',
text='',
background_fill_alpha=0.7,
background_fill_color='white',
text_color='black',
)
sliderlabel = Label(x=10,y=470,x_units='screen',y_units='screen',
text='',
background_fill_alpha=0.7,
background_fill_color='white',
text_color='black',text_font_size='10pt',
)
plot.add_layout(peakflabel) plot.add_layout(peakflabel)
plot.add_layout(peakforceanglelabel) plot.add_layout(peakforceanglelabel)
plot.add_layout(avflabel) plot.add_layout(avflabel)
@@ -549,6 +838,10 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
plot.add_layout(sliplabel) plot.add_layout(sliplabel)
plot.add_layout(washlabel) plot.add_layout(washlabel)
plot.add_layout(finishlabel) plot.add_layout(finishlabel)
plot.add_layout(annolabel)
plot.add_layout(sliderlabel)
plot.add_layout(lengthlabel)
plot.add_layout(efflengthlabel)
plot.xaxis.axis_label = "Angle" plot.xaxis.axis_label = "Angle"
plot.yaxis.axis_label = "Force (N)" plot.yaxis.axis_label = "Force (N)"
@@ -564,6 +857,8 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
callback = CustomJS(args = dict( callback = CustomJS(args = dict(
source=source, source=source,
source2=source2, source2=source2,
sourceslipwash=sourceslipwash,
sourcepoints=sourcepoints,
avf=avf, avf=avf,
avflabel=avflabel, avflabel=avflabel,
catchlabel=catchlabel, catchlabel=catchlabel,
@@ -572,12 +867,30 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
washlabel=washlabel, washlabel=washlabel,
peakflabel=peakflabel, peakflabel=peakflabel,
peakforceanglelabel=peakforceanglelabel, peakforceanglelabel=peakforceanglelabel,
annolabel=annolabel,
sliderlabel=sliderlabel,
lengthlabel=lengthlabel,
efflengthlabel=efflengthlabel,
plottype=plottype,
sourcemultiline=sourcemultiline,
sourcemultiline2=sourcemultiline2
), code=""" ), code="""
var data = source.data var data = source.data
var data2 = source2.data var data2 = source2.data
var dataslipwash = sourceslipwash.data
var datapoints = sourcepoints.data
var multilines = sourcemultiline.data
var multilines2 = sourcemultiline2.data
var plottype = plottype
var multilinesx = multilines2['x']
var multilinesy = multilines2['y']
var x = data['x'] var x = data['x']
var y = data['y'] var y = data['y']
var xslip = dataslipwash['xslip']
var spm1 = data2['spm'] var spm1 = data2['spm']
var distance1 = data2['distance'] var distance1 = data2['distance']
var driveenergy1 = data2['driveenergy'] var driveenergy1 = data2['driveenergy']
@@ -592,6 +905,10 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
var peakforce = data2['peakforce'] var peakforce = data2['peakforce']
var averageforce = data2['averageforce'] var averageforce = data2['averageforce']
var peakforcepoints = datapoints['peakforce']
var peakforceanglepoints = datapoints['peakforceangle']
var annotation = annotation.value
var minspm = minspm.value var minspm = minspm.value
var maxspm = maxspm.value var maxspm = maxspm.value
var mindist = mindist.value var mindist = mindist.value
@@ -599,6 +916,10 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
var minwork = minwork.value var minwork = minwork.value
var maxwork = maxwork.value var maxwork = maxwork.value
sliderlabel.text = 'SPM: '+minspm.toFixed(0)+'-'+maxspm.toFixed(0)
sliderlabel.text += ', Dist: '+mindist.toFixed(0)+'-'+maxdist.toFixed(0)
sliderlabel.text += ', WpS: '+minwork.toFixed(0)+'-'+maxwork.toFixed(0)
var catchav = 0 var catchav = 0
var finishav = 0 var finishav = 0
var slipav = 0 var slipav = 0
@@ -608,11 +929,23 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
var peakforceav = 0 var peakforceav = 0
var count = 0 var count = 0
datapoints['peakforceangle'] = []
datapoints['peakforce'] = []
multilines['x'] = []
multilines['y'] = []
for (i=0; i<c.length; i++) { for (i=0; i<c.length; i++) {
if (spm1[i]>=minspm && spm1[i]<=maxspm) { if (spm1[i]>=minspm && spm1[i]<=maxspm) {
if (distance1[i]>=mindist && distance1[i]<=maxdist) { if (distance1[i]>=mindist && distance1[i]<=maxdist) {
if (driveenergy1[i]>=minwork && driveenergy1[i]<=maxwork) { if (driveenergy1[i]>=minwork && driveenergy1[i]<=maxwork) {
if (plottype=='scatter') {
datapoints['peakforceangle'].push(peakforceangle[i])
datapoints['peakforce'].push(peakforce[i])
}
if (plottype=='line') {
multilines['x'].push(multilinesx[i])
multilines['y'].push(multilinesy[i])
}
catchav += c[i] catchav += c[i]
finishav += finish[i] finishav += finish[i]
slipav += slip[i] slipav += slip[i]
@@ -637,6 +970,12 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
data['x'] = [catchav,catchav+slipav,peakforceangleav,finishav-washav,finishav] data['x'] = [catchav,catchav+slipav,peakforceangleav,finishav-washav,finishav]
data['y'] = [0,thresholdforce,peakforceav,thresholdforce,0] data['y'] = [0,thresholdforce,peakforceav,thresholdforce,0]
dataslipwash['xslip'] = [catchav+slipav,finishav-washav]
dataslipwash['yslip'] = [thresholdforce,thresholdforce]
var length = finishav-catchav
var efflength = length-slipav-washav
avf.location = averageforceav avf.location = averageforceav
avflabel.text = 'Favg: '+averageforceav.toFixed(2) avflabel.text = 'Favg: '+averageforceav.toFixed(2)
catchlabel.text = 'Catch: '+catchav.toFixed(2) catchlabel.text = 'Catch: '+catchav.toFixed(2)
@@ -645,11 +984,25 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
washlabel.text = 'Wash: '+washav.toFixed(2) washlabel.text = 'Wash: '+washav.toFixed(2)
peakflabel.text = 'Fpeak: '+peakforceav.toFixed(2) peakflabel.text = 'Fpeak: '+peakforceav.toFixed(2)
peakforceanglelabel.text = 'Peak angle: '+peakforceangleav.toFixed(2) peakforceanglelabel.text = 'Peak angle: '+peakforceangleav.toFixed(2)
annolabel.text = annotation
lengthlabel.text = 'Length: '+length.toFixed(2)
efflengthlabel.text = 'Effective Length: '+efflength.toFixed(2)
console.log(count);
console.log(multilines['x'].length);
console.log(multilines['y'].length);
// source.trigger('change'); // source.trigger('change');
source.change.emit(); source.change.emit();
sourceslipwash.change.emit()
sourcepoints.change.emit();
sourcemultiline.change.emit();
""") """)
annotation = TextInput(title="Type your plot notes here", value="",
callback=callback)
callback.args["annotation"] = annotation
slider_spm_min = Slider(start=15.0, end=55,value=15.0, step=.1, slider_spm_min = Slider(start=15.0, end=55,value=15.0, step=.1,
title="Min SPM",callback=callback) title="Min SPM",callback=callback)
callback.args["minspm"] = slider_spm_min callback.args["minspm"] = slider_spm_min
@@ -670,16 +1023,17 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
distmax = 100+100*int(rowdata['distance'].max()/100.) distmax = 100+100*int(rowdata['distance'].max()/100.)
slider_dist_min = Slider(start=0,end=distmax,value=0,step=1, slider_dist_min = Slider(start=0,end=distmax,value=0,step=50,
title="Min Distance",callback=callback) title="Min Distance",callback=callback)
callback.args["mindist"] = slider_dist_min callback.args["mindist"] = slider_dist_min
slider_dist_max = Slider(start=0,end=distmax,value=distmax, slider_dist_max = Slider(start=0,end=distmax,value=distmax,
step=1, step=50,
title="Max Distance",callback=callback) title="Max Distance",callback=callback)
callback.args["maxdist"] = slider_dist_max callback.args["maxdist"] = slider_dist_max
layout = layoutrow([layoutcolumn([slider_spm_min, layout = layoutrow([layoutcolumn([annotation,
slider_spm_min,
slider_spm_max, slider_spm_max,
slider_dist_min, slider_dist_min,
slider_dist_max, slider_dist_max,
@@ -860,12 +1214,13 @@ def fitnessmetric_chart(fitnessmetrics,user,workoutmode='rower',startdate=None,
return [script,div] return [script,div]
def interactive_histoall(theworkouts): def interactive_histoall(theworkouts,histoparam,includereststrokes):
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
ids = [int(w.id) for w in theworkouts] ids = [int(w.id) for w in theworkouts]
rowdata = dataprep.getsmallrowdata_db(['power'],ids=ids,doclean=True) workstrokesonly = not includereststrokes
rowdata = dataprep.getsmallrowdata_db([histoparam],ids=ids,doclean=True,workstrokesonly=workstrokesonly)
rowdata.dropna(axis=0,how='any',inplace=True) rowdata.dropna(axis=0,how='any',inplace=True)
@@ -873,16 +1228,16 @@ def interactive_histoall(theworkouts):
return "","No Valid Data Available","","" return "","No Valid Data Available","",""
try: try:
histopwr = rowdata['power'].values histopwr = rowdata[histoparam].values
except KeyError: except KeyError:
return "","No power data","","" return "","No data","",""
if len(histopwr) == 0: if len(histopwr) == 0:
return "","No valid data available","","" return "","No valid data available","",""
# throw out nans # throw out nans
histopwr = histopwr[~np.isinf(histopwr)] histopwr = histopwr[~np.isinf(histopwr)]
histopwr = histopwr[histopwr > 25] histopwr = histopwr[histopwr > yaxminima[histoparam]]
histopwr = histopwr[histopwr < 1000] histopwr = histopwr[histopwr < yaxmaxima[histoparam]]
plot = Figure(tools=TOOLS,plot_width=900, plot = Figure(tools=TOOLS,plot_width=900,
toolbar_sticky=False, toolbar_sticky=False,
@@ -935,7 +1290,7 @@ def interactive_histoall(theworkouts):
# plot.quad(top='hist_norm',bottom=0,left=edges[:-1],right=edges[1:]) # plot.quad(top='hist_norm',bottom=0,left=edges[:-1],right=edges[1:])
plot.quad(top='hist_norm',bottom=0,left='left',right='right',source=source) plot.quad(top='hist_norm',bottom=0,left='left',right='right',source=source)
plot.xaxis.axis_label = "Power (W)" plot.xaxis.axis_label = axlabels[histoparam]
plot.yaxis.axis_label = "% of strokes" plot.yaxis.axis_label = "% of strokes"
plot.y_range = Range1d(0,1.05*max(hist_norm)) plot.y_range = Range1d(0,1.05*max(hist_norm))
@@ -943,7 +1298,7 @@ def interactive_histoall(theworkouts):
hover = plot.select(dict(type=HoverTool)) hover = plot.select(dict(type=HoverTool))
hover.tooltips = OrderedDict([ hover.tooltips = OrderedDict([
('Power(W)','@left{int}'), (axlabels[histoparam],'@left{int}'),
('% of strokes','@hist_norm'), ('% of strokes','@hist_norm'),
('Cumulative %','@histsum{int}'), ('Cumulative %','@histsum{int}'),
]) ])
@@ -957,12 +1312,36 @@ def interactive_histoall(theworkouts):
axis_label="Cumulative % of strokes"),'right') axis_label="Cumulative % of strokes"),'right')
plot.sizing_mode = 'scale_width' plot.sizing_mode = 'scale_width'
annolabel = Label(x=50,y=450,x_units='screen',y_units='screen',
text='',
background_fill_alpha=0.7,
background_fill_color='white',
text_color='black',
)
plot.add_layout(annolabel)
callback = CustomJS(args = dict(
annolabel=annolabel,
), code="""
var annotation = annotation.value
annolabel.text = annotation
""")
annotation = TextInput(title="Type your plot notes here", value="",
callback=callback)
callback.args["annotation"] = annotation
layout = layoutcolumn([annotation,plot])
try: try:
script, div = components(plot) script, div = components(layout)
except ValueError: except ValueError:
script = '' script = ''
div = '' div = ''
return [script,div] return [script,div]
def course_map(course): def course_map(course):
@@ -2370,7 +2749,8 @@ def interactive_chart(id=0,promember=0,intervaldata = {}):
def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='',
ploterrorbars=False, ploterrorbars=False,
title=None,binsize=1,colorlegend=[]): title=None,binsize=1,colorlegend=[],
spmmin=0,spmmax=0,workmin=0,workmax=0):
if datadf.empty: if datadf.empty:
return ['','<p>No non-zero data in selection</p>'] return ['','<p>No non-zero data in selection</p>']
@@ -2515,8 +2895,8 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='',
for nr, gvalue, color in colorlegend: for nr, gvalue, color in colorlegend:
box = BoxAnnotation(bottom=125+20*nr,left=100,top=145+20*nr, box = BoxAnnotation(bottom=75+20*nr,left=50,top=95+20*nr,
right=120, right=70,
bottom_units='screen', bottom_units='screen',
top_units='screen', top_units='screen',
left_units='screen', left_units='screen',
@@ -2524,7 +2904,7 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='',
fill_color=color, fill_color=color,
fill_alpha=1.0, fill_alpha=1.0,
line_color=color) line_color=color)
legendlabel = Label(x=121,y=128+20*nr,x_units='screen', legendlabel = Label(x=71,y=78+20*nr,x_units='screen',
y_units='screen', y_units='screen',
text = "{gvalue:3.0f}".format(gvalue=gvalue), text = "{gvalue:3.0f}".format(gvalue=gvalue),
background_fill_alpha=1.0, background_fill_alpha=1.0,
@@ -2534,7 +2914,7 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='',
plot.add_layout(legendlabel) plot.add_layout(legendlabel)
if colorlegend: if colorlegend:
legendlabel = Label(x=372,y=300,x_units='screen', legendlabel = Label(x=322,y=250,x_units='screen',
y_units='screen', y_units='screen',
text = 'group legend', text = 'group legend',
text_color='black', text_color='black',
@@ -2552,15 +2932,27 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='',
else: else:
plot.yaxis.axis_label = axlabels[yparam] plot.yaxis.axis_label = axlabels[yparam]
binlabel = Label(x=100,y=100,x_units='screen', binlabel = Label(x=50,y=50,x_units='screen',
y_units='screen', y_units='screen',
text="Bin size {binsize:3.1f}".format(binsize=binsize), text="Bin size {binsize:3.1f}".format(binsize=binsize),
background_fill_alpha=0.7, background_fill_alpha=0.7,
background_fill_color='white', background_fill_color='white',
text_color='black', text_color='black',text_font_size='10pt',
) )
slidertext = 'SPM: {:.0f}-{:.0f}, WpS: {:.0f}-{:.0f}'.format(
spmmin,spmmax,workmin,workmax
)
sliderlabel = Label(x=50,y=20,x_units='screen',y_units='screen',
text=slidertext,
background_fill_alpha=0.7,
background_fill_color='white',
text_color='black',text_font_size='10pt',
)
plot.add_layout(binlabel) plot.add_layout(binlabel)
plot.add_layout(sliderlabel)
yrange1 = Range1d(start=yaxmin,end=yaxmax) yrange1 = Range1d(start=yaxmin,end=yaxmax)
plot.y_range = yrange1 plot.y_range = yrange1
@@ -2946,12 +3338,12 @@ def interactive_cum_flex_chart2(theworkouts,promember=0,
except KeyError: except KeyError:
distmax = 1000. distmax = 1000.
slider_dist_min = Slider(start=0,end=distmax,value=0,step=1, slider_dist_min = Slider(start=0,end=distmax,value=0,step=50,
title="Min Distance",callback=callback) title="Min Distance",callback=callback)
callback.args["mindist"] = slider_dist_min callback.args["mindist"] = slider_dist_min
slider_dist_max = Slider(start=0,end=distmax,value=distmax, slider_dist_max = Slider(start=0,end=distmax,value=distmax,
step=1, step=50,
title="Max Distance",callback=callback) title="Max Distance",callback=callback)
callback.args["maxdist"] = slider_dist_max callback.args["maxdist"] = slider_dist_max
@@ -3522,7 +3914,7 @@ def interactive_flex_chart2(id=0,promember=0,
""") """)
annotation = TextInput(title="Type your plot notes here", value="", annotation = TextInput(title="Type your plot notes here", value="",
callback=callback) callback=callback)
callback.args["annotation"] = annotation callback.args["annotation"] = annotation
slider_spm_min = Slider(start=15.0, end=55,value=15.0, step=.1, slider_spm_min = Slider(start=15.0, end=55,value=15.0, step=.1,
@@ -3548,12 +3940,12 @@ def interactive_flex_chart2(id=0,promember=0,
except (KeyError,ValueError): except (KeyError,ValueError):
distmax = 100 distmax = 100
slider_dist_min = Slider(start=0,end=distmax,value=0,step=1, slider_dist_min = Slider(start=0,end=distmax,value=0,step=50,
title="Min Distance",callback=callback) title="Min Distance",callback=callback)
callback.args["mindist"] = slider_dist_min callback.args["mindist"] = slider_dist_min
slider_dist_max = Slider(start=0,end=distmax,value=distmax, slider_dist_max = Slider(start=0,end=distmax,value=distmax,
step=1, step=50,
title="Max Distance",callback=callback) title="Max Distance",callback=callback)
callback.args["maxdist"] = slider_dist_max callback.args["maxdist"] = slider_dist_max

View File

@@ -106,7 +106,8 @@ def make_new_workout_from_email(rower, datafile, name, cntr=0,testing=False):
if testing: if testing:
print('Fileformat = ',fileformat) print('Fileformat = ',fileformat)
if fileformat == 'unknown': if fileformat == 'unknown':
# extension = datafilename[-4:].lower() # extension = datafilename[-4:].lower()
# fcopy = "media/"+datafilename[:-4]+"_copy"+extension # fcopy = "media/"+datafilename[:-4]+"_copy"+extension

View File

@@ -234,6 +234,8 @@ class Command(BaseCommand):
] ]
except IOError: except IOError:
rowers = [] rowers = []
except Message.DoesNotExist:
attachment.delete()
for rower in rowers: for rower in rowers:
if extension == 'zip': if extension == 'zip':
try: try:

View File

@@ -285,7 +285,7 @@ axesnew = [
(name,d['verbose_name'],d['ax_min'],d['ax_max'],d['type']) for name,d in rowingmetrics (name,d['verbose_name'],d['ax_min'],d['ax_max'],d['type']) for name,d in rowingmetrics
] ]
axes = tuple(axesnew+[('None','None',0,1,'basic')]) axes = tuple(axesnew+[('None','<None>',0,1,'basic')])
axlabels = {ax[0]:ax[1] for ax in axes} axlabels = {ax[0]:ax[1] for ax in axes}

View File

@@ -97,6 +97,12 @@ import arrow
from rowers.utils import get_strava_stream from rowers.utils import get_strava_stream
def safetimedelta(x):
try:
return timedelta(seconds=x)
except ValueError:
return timedelta(seconds=0)
siteurl = SITE_URL siteurl = SITE_URL
# testing task # testing task
@@ -292,7 +298,7 @@ def handle_check_race_course(self,
rowdata = rowdata[rowdata['time']>splitsecond] rowdata = rowdata[rowdata['time']>splitsecond]
# we may want to expand the time (interpolate) # we may want to expand the time (interpolate)
rowdata['dt'] = rowdata['time'].apply( rowdata['dt'] = rowdata['time'].apply(
lambda x: timedelta(seconds=x) lambda x: safetimedelta(seconds=x)
) )
rowdata = rowdata.resample('100ms',on='dt').mean() rowdata = rowdata.resample('100ms',on='dt').mean()
rowdata = rowdata.interpolate() rowdata = rowdata.interpolate()
@@ -618,7 +624,7 @@ def handle_calctrimp(id,
df2['time'] = df2[' ElapsedTime (sec)'] df2['time'] = df2[' ElapsedTime (sec)']
df2['time'] = df2['time'].apply( df2['time'] = df2['time'].apply(
lambda x:timedelta(seconds=x) lambda x:safetimedelta(seconds=x)
) )
duration = df['TimeStamp (sec)'].max()-df['TimeStamp (sec)'].min() duration = df['TimeStamp (sec)'].max()-df['TimeStamp (sec)'].min()
@@ -1659,11 +1665,10 @@ def handle_makeplot(f1, f2, t, hrdata, plotnr, imagename,
try: try:
haspower = row.df[' Power (watts)'].mean() > 50 haspower = row.df[' Power (watts)'].mean() > 50
except TypeError: except (TypeError, KeyError):
haspower = True
except KeyError:
haspower = False haspower = False
nr_rows = len(row.df) nr_rows = len(row.df)
if (plotnr in [1, 2, 4, 5, 8, 11, 9, 12]) and (nr_rows > 1200): if (plotnr in [1, 2, 4, 5, 8, 11, 9, 12]) and (nr_rows > 1200):
bin = int(nr_rows / 1200.) bin = int(nr_rows / 1200.)
@@ -1700,14 +1705,13 @@ def handle_makeplot(f1, f2, t, hrdata, plotnr, imagename,
t += ' - Power Distribution' t += ' - Power Distribution'
fig1 = row.get_power_piechart(t) fig1 = row.get_power_piechart(t)
canvas = FigureCanvas(fig1) if fig1:
canvas = FigureCanvas(fig1)
# plt.savefig('static/plots/'+imagename,format='png') canvas.print_figure('static/plots/' + imagename)
canvas.print_figure('static/plots/' + imagename) plt.close(fig1)
# plt.imsave(fname='static/plots/'+imagename) fig1.clf()
plt.close(fig1) gc.collect()
fig1.clf()
gc.collect()
return imagename return imagename

View File

@@ -11,18 +11,6 @@
<ul class="main-content"> <ul class="main-content">
<li class="rounder">
<h2>Ranking Pieces</h2>
<a href="/rowers/ote-bests2/">
<div class="vignet">
<img src="/static/img/rankingpiece.png"
alt="Ranking Piece">
</div>
</a>
<p>
Analyze your Concept2 ranking pieces over a date range and predict your pace on other pieces.
</p>
</li>
<li class="rounder"> <li class="rounder">
<h2>Compare Workouts</h2> <h2>Compare Workouts</h2>
{% if team %} {% if team %}
@@ -52,14 +40,14 @@
</p> </p>
</li> </li>
<li class="rounder"> <li class="rounder">
<h2>Power Histogram</h2> <h2>Histogram</h2>
<a href="/rowers/histo/"> <a href="/rowers/histo/">
<div class="vignet"> <div class="vignet">
<img src="/static/img/histogram.png" alt="Power Histogram"> <img src="/static/img/histogram.png" alt="Power Histogram">
</div> </div>
</a> </a>
<p> <p>
Plot a power histogram of all your strokes over a date range. Plot a histogram chart of one metric for all your strokes over a date range.
</p> </p>
</li> </li>
<li class="rounder"> <li class="rounder">
@@ -85,29 +73,6 @@
BETA: Box Chart Statistics of stroke metrics over a date range BETA: Box Chart Statistics of stroke metrics over a date range
</p> </p>
</li> </li>
<li class="rounder">
<h2>OTW Critical Power</h2>
<a href="/rowers/otw-bests/">
<div class="vignet">
<img src="/static/img/otwcp.png" alt="OTW Critical Power">
</div>
</a>
<p>
Analyse power vs piece duration to make predictions. For On-The-Water rowing.
</p>
</li>
<li class="rounder">
<h2>OTE Critical Power</h2>
<a href="/rowers/ote-ranking/">
<div class="vignet">
<img src="/static/img/otecp.png" alt="OTE Critical Power">
</div>
</a>
<p>
Analyse power vs piece duration to make predictions, for erg pieces.
</p>
</li>
<li class="rounder"> <li class="rounder">
<h2>Trend Flex</h2> <h2>Trend Flex</h2>
<a href="/rowers/user-multiflex-select/"> <a href="/rowers/user-multiflex-select/">
@@ -131,6 +96,41 @@
Monitoring power duration evidence from all your workouts. Feel free to explore. Monitoring power duration evidence from all your workouts. Feel free to explore.
</p> </p>
</li> </li>
<li class="rounder">
<h2>Ranking Pieces</h2>
<a href="/rowers/ote-bests2/">
<div class="vignet">
<img src="/static/img/rankingpiece.png"
alt="Ranking Piece">
</div>
</a>
<p>
Analyze your Concept2 ranking pieces over a date range and predict your pace on other pieces.
</p>
</li>
<li class="rounder">
<h2>OTW Critical Power</h2>
<a href="/rowers/otw-bests/">
<div class="vignet">
<img src="/static/img/otwcp.png" alt="OTW Critical Power">
</div>
</a>
<p>
Analyse power vs piece duration to make predictions. For On-The-Water rowing.
</p>
</li>
<li class="rounder">
<h2>OTE Critical Power</h2>
<a href="/rowers/ote-ranking/">
<div class="vignet">
<img src="/static/img/otecp.png" alt="OTE Critical Power">
</div>
</a>
<p>
Analyse power vs piece duration to make predictions, for erg pieces.
</p>
</li>
</ul> </ul>

View File

@@ -23,23 +23,22 @@
<ul class="main-content"> <ul class="main-content">
<li class="grid_4"> <li class="grid_4">
{% if user.is_authenticated and mayedit %} <div id="theplot" class="flexplot">
<form enctype="multipart/form-data" action="{{ formloc }}" method="post"> {{ the_div|safe }}
{% csrf_token %} </div>
{% if workstrokesonly %}
<input type="hidden" name="workstrokesonly" value="True">
<input class="button blue small" value="Remove Rest Strokes" type="Submit">
{% else %}
<input class="button blue small" type="hidden" name="workstrokesonly" value="False">
<input class="button blue small" value="Include Rest Strokes" type="Submit">
</form>
{% endif %}
<span class="tooltiptext">If your data source allows, this will show or hide strokes taken during rest intervals.</span>
{% endif %}
</li> </li>
<li class="grid_4"> <li class="grid_4">
{{ the_div|safe }} <form enctype="multipart/form-data" action="" method="post">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<p>
<input name="chartform" type="submit"
value="Update Chart">
</p>
</form>
</li> </li>
</ul> </ul>

View File

@@ -75,6 +75,8 @@
</div> </div>
<h1>Histogram View</h1>
<ul class="main-content"> <ul class="main-content">
<li class="grid_4"> <li class="grid_4">
@@ -100,6 +102,11 @@
{{ form.as_table }} {{ form.as_table }}
</table> </table>
</li> </li>
<li>
<table>
{{ histoform.as_table }}
</table>
</li>
<li> <li>
{% csrf_token %} {% csrf_token %}
<input class="button green small" value="Submit" type="Submit"> <input class="button green small" value="Submit" type="Submit">

View File

@@ -5,20 +5,30 @@
<li class="has-children" id="fitness"> <li class="has-children" id="fitness">
<input type="checkbox" name="group-fitness" id="group-fitness" checked> <input type="checkbox" name="group-fitness" id="group-fitness" checked>
<label for="group-fitness"> <label for="group-fitness">
<i class="fas fa-watch-fitness fa-fw"></i>&nbsp;Fitness</label> <i class="fas fa-watch-fitness fa-fw"></i>&nbsp;Fitness
</label>
<ul> <ul>
<li id="compare">
{% if team %}
<a href="/rowers/team-compare-select/team/{{ team.id }}/">
<i class="fas fa-balance-scale fa-fw"></i>&nbsp;Compare
</a>
{% else %}
<a href="/rowers/team-compare-select/team/0/">
<i class="fas fa-balance-scale fa-fw"></i>&nbsp;Compare
</a>
{% endif %}
</li>
<li id="fitness-powerprogress">
<a href="/rowers/fitness-progress/">
<i class="far fa-watch-fitness fa-fw"></i>&nbsp;Power Progress
</a>
</li>
<li id="fitness-ranking"> <li id="fitness-ranking">
<a href="/rowers/ote-bests2/"> <a href="/rowers/ote-bests2/">
<i class="fas fa-star fa-fw"></i>&nbsp;Ranking Pieces <i class="fas fa-star fa-fw"></i>&nbsp;Ranking Pieces
</a> </a>
</li> </li>
<li id="compare">
{% if team %}
<a href="/rowers/team-compare-select/team/{{ team.id }}/"><i class="fas fa-balance-scale fa-fw"></i>&nbsp;Compare</a>
{% else %}
<a href="/rowers/team-compare-select/team/0/"><i class="fas fa-balance-scale fa-fw"></i>&nbsp;Compare</a>
{% endif %}
</li>
<li id="fitness-otecp"> <li id="fitness-otecp">
<a href="/rowers/ote-ranking/"> <a href="/rowers/ote-ranking/">
<i class="fas fa-user-chart fa-fw"></i>&nbsp;CP Chart OTE <i class="fas fa-user-chart fa-fw"></i>&nbsp;CP Chart OTE
@@ -29,11 +39,6 @@
<i class="far fa-user-chart fa-fw"></i>&nbsp;CP Chart OTW <i class="far fa-user-chart fa-fw"></i>&nbsp;CP Chart OTW
</a> </a>
</li> </li>
<li id="fitness-powerprogress">
<a href="/rowers/fitness-progress/">
<i class="far fa-watch-fitness fa-fw"></i>&nbsp;Power Progress
</a>
</li>
</ul> </ul>
</li> </li>
<li class="has-children" id="stats"> <li class="has-children" id="stats">
@@ -57,9 +62,9 @@
<i class="fal fa-table fa-fw"></i>&nbsp;Statistics <i class="fal fa-table fa-fw"></i>&nbsp;Statistics
</a> </a>
</li> </li>
<li id="stats-histopower"> <li id="stats-histo">
<a href="/rowers/histo/"> <a href="/rowers/histo/">
<i class="fas fa-chart-bar"></i>&nbsp;Power Histogram <i class="fas fa-chart-bar"></i>&nbsp;Histogram
</a> </a>
</li> </li>
</ul> </ul>

View File

@@ -20,11 +20,17 @@ def histo(request,theuser=0,
'workouttypes':[i[0] for i in mytypes.workouttypes], 'workouttypes':[i[0] for i in mytypes.workouttypes],
'waterboattype':mytypes.waterboattype, 'waterboattype':mytypes.waterboattype,
'rankingonly': False, 'rankingonly': False,
'histoparam':'power'
}): }):
r = getrequestrower(request,userid=theuser) r = getrequestrower(request,userid=theuser)
theuser = r.user theuser = r.user
if 'histoparam' in request.session:
histoparam = request.session['histoparam']
else:
histoparam = 'power'
if 'waterboattype' in request.session: if 'waterboattype' in request.session:
waterboattype = request.session['waterboattype'] waterboattype = request.session['waterboattype']
else: else:
@@ -81,6 +87,7 @@ def histo(request,theuser=0,
if request.method == 'POST': if request.method == 'POST':
form = DateRangeForm(request.POST) form = DateRangeForm(request.POST)
modalityform = TrendFlexModalForm(request.POST) modalityform = TrendFlexModalForm(request.POST)
histoform = HistoForm(request.POST)
if form.is_valid(): if form.is_valid():
startdate = form.cleaned_data['startdate'] startdate = form.cleaned_data['startdate']
enddate = form.cleaned_data['enddate'] enddate = form.cleaned_data['enddate']
@@ -110,6 +117,11 @@ def histo(request,theuser=0,
'startdate': startdate, 'startdate': startdate,
'enddate': enddate, 'enddate': enddate,
}) })
if histoform.is_valid():
includereststrokes = histoform.cleaned_data['includereststrokes']
histoparam = histoform.cleaned_data['histoparam']
request.session['histoparam'] = histoparam
request.session['includereststrokes'] = includereststrokes
else: else:
form = DateRangeForm(initial={ form = DateRangeForm(initial={
'startdate': startdate, 'startdate': startdate,
@@ -125,6 +137,10 @@ def histo(request,theuser=0,
'rankingonly':rankingonly, 'rankingonly':rankingonly,
} }
) )
histoform = HistoForm(initial={
'includereststrokes':False,
'histoparam':histoparam
})
negtypes = [] negtypes = []
for b in mytypes.boattypes: for b in mytypes.boattypes:
@@ -149,6 +165,7 @@ def histo(request,theuser=0,
'enddatestring':enddatestring, 'enddatestring':enddatestring,
'rankingonly':rankingonly, 'rankingonly':rankingonly,
'includereststrokes':includereststrokes, 'includereststrokes':includereststrokes,
'histoparam':histoparam,
} }
request.session['options'] = options request.session['options'] = options
@@ -163,9 +180,21 @@ def histo(request,theuser=0,
request.session['options'] = options request.session['options'] = options
breadcrumbs = [
{
'url':'/rowers/analysis',
'name':'Analysis'
},
{
'url':reverse('histo'),
'name': 'Histogram'
}
]
return render(request, 'histo.html', return render(request, 'histo.html',
{'interactiveplot':script, {'interactiveplot':script,
'the_div':div, 'the_div':div,
'breadcrumbs':breadcrumbs,
'id':theuser, 'id':theuser,
'active':'nav-analysis', 'active':'nav-analysis',
'theuser':theuser, 'theuser':theuser,
@@ -174,6 +203,7 @@ def histo(request,theuser=0,
'enddate':enddate, 'enddate':enddate,
'form':form, 'form':form,
'optionsform':modalityform, 'optionsform':modalityform,
'histoform':histoform,
'teams':get_my_teams(request.user), 'teams':get_my_teams(request.user),
}) })
@@ -295,6 +325,7 @@ def histo_data(
'enddatestring':timezone.now().strftime("%Y-%m-%d"), 'enddatestring':timezone.now().strftime("%Y-%m-%d"),
'startdatestring':(timezone.now()-datetime.timedelta(days=30)).strftime("%Y-%m-%d"), 'startdatestring':(timezone.now()-datetime.timedelta(days=30)).strftime("%Y-%m-%d"),
'deltadays':-1, 'deltadays':-1,
'histoparam':'power',
}): }):
def_options = options def_options = options
@@ -311,6 +342,7 @@ def histo_data(
theuser = keyvalue_get_default('theuser',options,def_options) theuser = keyvalue_get_default('theuser',options,def_options)
startdatestring = keyvalue_get_default('startdatestring',options,def_options) startdatestring = keyvalue_get_default('startdatestring',options,def_options)
enddatestring = keyvalue_get_default('enddatestring',options,def_options) enddatestring = keyvalue_get_default('enddatestring',options,def_options)
histoparam = keyvalue_get_default('histoparam',options,def_options)
if modality == 'all': if modality == 'all':
modalities = [m[0] for m in mytypes.workouttypes] modalities = [m[0] for m in mytypes.workouttypes]
@@ -358,7 +390,7 @@ def histo_data(
rankingpiece__in=rankingpiece) rankingpiece__in=rankingpiece)
if allworkouts: if allworkouts:
res = interactive_histoall(allworkouts) res = interactive_histoall(allworkouts,histoparam,includereststrokes)
script = res[0] script = res[0]
div = res[1] div = res[1]
else: else:
@@ -2593,7 +2625,9 @@ def multiflex_data(request,userid=0,
extratitle=extratitle, extratitle=extratitle,
ploterrorbars=ploterrorbars, ploterrorbars=ploterrorbars,
binsize=binsize, binsize=binsize,
colorlegend=colorlegend) colorlegend=colorlegend,
spmmin=spmmin,spmmax=spmmax,
workmin=workmin,workmax=workmax)
scripta= script.split('\n')[2:-1] scripta= script.split('\n')[2:-1]
script = ''.join(scripta) script = ''.join(scripta)
@@ -3085,7 +3119,8 @@ def boxplot_view_data(request,userid=0,
script,div = interactive_boxchart(datadf,plotfield, script,div = interactive_boxchart(datadf,plotfield,
extratitle=extratitle) extratitle=extratitle,
spmmin=spmmin,spmmax=spmmax,workmin=workmin,workmax=workmax)
scripta = script.split('\n')[2:-1] scripta = script.split('\n')[2:-1]
script = ''.join(scripta) script = ''.join(scripta)

View File

@@ -48,6 +48,7 @@ from django.http import (
) )
from django.contrib.auth import authenticate, login, logout from django.contrib.auth import authenticate, login, logout
from rowers.forms import ( from rowers.forms import (
ForceCurveOptionsForm,HistoForm,
LoginForm,DocumentsForm,UploadOptionsForm,ImageForm,CourseForm, LoginForm,DocumentsForm,UploadOptionsForm,ImageForm,CourseForm,
TeamUploadOptionsForm,WorkFlowLeftPanelForm,WorkFlowMiddlePanelForm, TeamUploadOptionsForm,WorkFlowLeftPanelForm,WorkFlowMiddlePanelForm,
WorkFlowLeftPanelElement,WorkFlowMiddlePanelElement, WorkFlowLeftPanelElement,WorkFlowMiddlePanelElement,

View File

@@ -26,15 +26,24 @@ def workout_forcecurve_view(request,id=0,workstrokesonly=False):
if not promember: if not promember:
return HttpResponseRedirect("/rowers/about/") return HttpResponseRedirect("/rowers/about/")
if request.method == 'POST' and 'workstrokesonly' in request.POST: if request.method == 'POST':
workstrokesonly = request.POST['workstrokesonly'] form = ForceCurveOptionsForm(request.POST)
if workstrokesonly == 'True': if form.is_valid():
workstrokesonly = True includereststrokes = form.cleaned_data['includereststrokes']
plottype = form.cleaned_data['plottype']
workstrokesonly = not includereststrokes
else: else:
workstrokesonly = False workstrokesonly = True
plottype = 'line'
else:
form = ForceCurveOptionsForm()
plottype = 'line'
script,div,js_resources,css_resources = interactive_forcecurve([row], script,div,js_resources,css_resources = interactive_forcecurve(
workstrokesonly=workstrokesonly) [row],
workstrokesonly=workstrokesonly,
plottype=plottype,
)
breadcrumbs = [ breadcrumbs = [
{ {
@@ -53,12 +62,13 @@ def workout_forcecurve_view(request,id=0,workstrokesonly=False):
] ]
r = getrower(request.user) r = getrower(request.user)
return render(request, return render(request,
'forcecurve_single.html', 'forcecurve_single.html',
{ {
'the_script':script, 'the_script':script,
'rower':r, 'rower':r,
'form':form,
'workout':row, 'workout':row,
'breadcrumbs':breadcrumbs, 'breadcrumbs':breadcrumbs,
'active':'nav-workouts', 'active':'nav-workouts',
@@ -67,7 +77,6 @@ def workout_forcecurve_view(request,id=0,workstrokesonly=False):
'css_res':css_resources, 'css_res':css_resources,
'id':id, 'id':id,
'mayedit':mayedit, 'mayedit':mayedit,
'workstrokesonly': not workstrokesonly,
'teams':get_my_teams(request.user), 'teams':get_my_teams(request.user),
}) })
@@ -102,7 +111,7 @@ def workout_histo_view(request,id=0):
if not promember: if not promember:
return HttpResponseRedirect("/rowers/about/") return HttpResponseRedirect("/rowers/about/")
res = interactive_histoall([w]) res = interactive_histoall([w],'power',False)
script = res[0] script = res[0]
div = res[1] div = res[1]
@@ -4834,8 +4843,8 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
s = cd["intervalstring"] s = cd["intervalstring"]
try: try:
rowdata.updateinterval_string(s) rowdata.updateinterval_string(s)
except (ParseException,err): except:
messages.error(request,'Parsing error in column '+str(err.col)) messages.error(request,'Parsing error')
intervalstats = rowdata.allstats() intervalstats = rowdata.allstats()
itime,idist,itype = rowdata.intervalstats_values() itime,idist,itype = rowdata.intervalstats_values()
nrintervals = len(idist) nrintervals = len(idist)

View File

@@ -608,7 +608,7 @@
#theplot .bk-plot-layout { #theplot .bk-plot-layout {
position: auto; position: auto;
width: 100%; width: 90%;
height: auto; height: auto;
display: inline; display: inline;
} }