Merge branch 'develop' into feature/django2
This commit is contained in:
@@ -1007,7 +1007,10 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
|
||||
|
||||
f = rowdatadf['TimeStamp (sec)'].diff().mean()
|
||||
if f != 0:
|
||||
windowsize = 2*(int(10./(f)))+1
|
||||
try:
|
||||
windowsize = 2*(int(10./(f)))+1
|
||||
except ValueError:
|
||||
windowsize = 1
|
||||
else:
|
||||
windowsize = 1
|
||||
if windowsize <= 3:
|
||||
|
||||
@@ -25,6 +25,12 @@ import datetime
|
||||
from django.forms import formset_factory
|
||||
from rowers.utils import landingpages
|
||||
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):
|
||||
|
||||
@@ -710,6 +716,12 @@ class DataFrameColumnsForm(forms.Form):
|
||||
cols = forms.MultipleChoiceField(choices=colchoices,
|
||||
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
|
||||
class TrendFlexModalForm(forms.Form):
|
||||
modality = forms.ChoiceField(choices=workouttypes,
|
||||
@@ -722,7 +734,8 @@ class TrendFlexModalForm(forms.Form):
|
||||
label='Only Ranking Pieces',
|
||||
required=False)
|
||||
|
||||
|
||||
|
||||
|
||||
# This form sets options for the summary stats page
|
||||
class StatsOptionsForm(forms.Form):
|
||||
includereststrokes = forms.BooleanField(initial=False,label='Include Rest Strokes',required=False)
|
||||
@@ -796,12 +809,6 @@ class PlannedSessionMultipleCloneForm(forms.Form):
|
||||
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):
|
||||
yparam = forms.ChoiceField(choices=parchoices,initial='spm',
|
||||
@@ -1181,24 +1188,39 @@ class FlexOptionsForm(forms.Form):
|
||||
('line','Line Plot'),
|
||||
('scatter','Scatter Plot'),
|
||||
)
|
||||
plottype = forms.ChoiceField(choices=plotchoices,initial='scatter',
|
||||
plottype = forms.ChoiceField(choices=plotchoices,initial='line',
|
||||
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):
|
||||
axchoices = (
|
||||
axchoices = list(
|
||||
(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 = (
|
||||
(ax[0], ax[1]) for ax in axes if ax[0] not in ['cumdist','distance','time']
|
||||
)
|
||||
yaxchoices = list((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(
|
||||
choices=axchoices,label='X-Axis',required=True)
|
||||
|
||||
@@ -43,7 +43,7 @@ from django.contrib.auth.decorators import login_required
|
||||
# from .models import Profile
|
||||
from rowingdata import rowingdata, make_cumvalues
|
||||
import pandas as pd
|
||||
from rowers.models import Rower,Workout,checkworkoutuser
|
||||
from rowers.models import Rower,Workout,checkworkoutuser,TombStone
|
||||
import rowers.mytypes as mytypes
|
||||
from rowsandall_app.settings import (
|
||||
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET,
|
||||
|
||||
@@ -17,6 +17,7 @@ from math import pi
|
||||
from django.utils import timezone
|
||||
|
||||
from bokeh.palettes import Dark2_8 as palette
|
||||
from bokeh.models.glyphs import MultiLine
|
||||
import itertools
|
||||
from bokeh.plotting import figure, ColumnDataSource, Figure,curdoc
|
||||
from bokeh.models import CustomJS,Slider, TextInput,BoxAnnotation
|
||||
@@ -64,6 +65,7 @@ activate(settings.TIME_ZONE)
|
||||
thetimezone = get_current_timezone()
|
||||
|
||||
from scipy.stats import linregress,percentileofscore
|
||||
from scipy.spatial import ConvexHull,Delaunay
|
||||
from scipy import optimize
|
||||
from scipy.signal import savgol_filter
|
||||
from scipy.interpolate import griddata
|
||||
@@ -164,7 +166,8 @@ def tailwind(bearing,vwind,winddir):
|
||||
from rowers.dataprep import nicepaceformat,niceformat
|
||||
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:
|
||||
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 = 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])
|
||||
plot.y_range = yrange1
|
||||
plot.sizing_mode = 'scale_width'
|
||||
@@ -221,6 +215,18 @@ def interactive_boxchart(datadf,fieldname,extratitle=''):
|
||||
plot.plot_width=920
|
||||
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)
|
||||
|
||||
return script,div
|
||||
@@ -331,45 +337,12 @@ def interactive_activitychart(workouts,startdate,enddate,stack='type'):
|
||||
p.toolbar_location = None
|
||||
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)
|
||||
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'
|
||||
|
||||
ids = [int(w.id) for w in theworkouts]
|
||||
@@ -399,63 +372,296 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
|
||||
if rowdata.empty:
|
||||
return "","No Valid Data Available","",""
|
||||
|
||||
|
||||
# quick linear regression
|
||||
# peakforce = slope*peakforceangle + intercept
|
||||
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:
|
||||
catchav = 0
|
||||
catch25 = 0
|
||||
catch75 = 0
|
||||
catch05 = 0
|
||||
catch95 = 0
|
||||
|
||||
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:
|
||||
finishav = 0
|
||||
finish25 = 0
|
||||
finish75 = 0
|
||||
finish05 = 0
|
||||
finish95 = 0
|
||||
|
||||
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:
|
||||
washav = 0
|
||||
wash25 = 0
|
||||
wash75 = 0
|
||||
wash05 = 0
|
||||
wash95 = 0
|
||||
|
||||
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:
|
||||
slipav = 0
|
||||
|
||||
slip25 = 0
|
||||
slip75 = 0
|
||||
slip05 = 0
|
||||
slip95 = 0
|
||||
|
||||
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:
|
||||
peakforceav = 0
|
||||
peakforce25 = 0
|
||||
peakforce75 = 0
|
||||
peakforce05 = 0
|
||||
peakforce95 = 0
|
||||
|
||||
|
||||
try:
|
||||
averageforceav = rowdata['averageforce'].mean()
|
||||
averageforceav = rowdata['averageforce'].median()
|
||||
except KeyError:
|
||||
averageforceav = 0
|
||||
|
||||
|
||||
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:
|
||||
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,
|
||||
catchav+slipav,
|
||||
slipav,
|
||||
peakforceangleav,
|
||||
finishav-washav,
|
||||
washav,
|
||||
finishav]
|
||||
|
||||
thresholdforce = 100 if 'x' in boattype else 200
|
||||
#thresholdforce /= 4.45 # N to lbs
|
||||
y = [0,thresholdforce,
|
||||
peakforceav,
|
||||
thresholdforce,0]
|
||||
|
||||
|
||||
|
||||
source = ColumnDataSource(
|
||||
data = dict(
|
||||
x = x,
|
||||
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(
|
||||
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,
|
||||
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',
|
||||
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.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),
|
||||
background_fill_alpha=.7,
|
||||
background_fill_color='white',
|
||||
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),
|
||||
background_fill_alpha=.7,
|
||||
background_fill_color='white',
|
||||
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),
|
||||
background_fill_alpha=0.7,
|
||||
background_fill_color='white',
|
||||
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),
|
||||
background_fill_alpha=0.7,
|
||||
background_fill_color='white',
|
||||
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),
|
||||
background_fill_alpha=0.7,
|
||||
background_fill_color='white',
|
||||
text_color='red',
|
||||
)
|
||||
|
||||
sliplabel = Label(x=370,y=280,x_units='screen',y_units='screen',
|
||||
text="Slip: {slipav:6.2f}".format(slipav=slipav),
|
||||
sliplabel = Label(x=425,y=310,x_units='screen',y_units='screen',
|
||||
text="Slip: {slipav:6.2f}".format(slipav=slipav-catchav),
|
||||
background_fill_alpha=0.7,
|
||||
background_fill_color='white',
|
||||
text_color='red',
|
||||
)
|
||||
|
||||
washlabel = Label(x=360,y=250,x_units='screen',y_units='screen',
|
||||
text="Wash: {washav:6.2f}".format(washav=washav),
|
||||
washlabel = Label(x=415,y=280,x_units='screen',y_units='screen',
|
||||
text="Wash: {washav:6.2f}".format(washav=finishav-washav),
|
||||
background_fill_alpha=0.7,
|
||||
background_fill_color='white',
|
||||
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(peakforceanglelabel)
|
||||
plot.add_layout(avflabel)
|
||||
@@ -549,6 +838,10 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
|
||||
plot.add_layout(sliplabel)
|
||||
plot.add_layout(washlabel)
|
||||
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.yaxis.axis_label = "Force (N)"
|
||||
@@ -564,6 +857,8 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
|
||||
callback = CustomJS(args = dict(
|
||||
source=source,
|
||||
source2=source2,
|
||||
sourceslipwash=sourceslipwash,
|
||||
sourcepoints=sourcepoints,
|
||||
avf=avf,
|
||||
avflabel=avflabel,
|
||||
catchlabel=catchlabel,
|
||||
@@ -572,12 +867,30 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
|
||||
washlabel=washlabel,
|
||||
peakflabel=peakflabel,
|
||||
peakforceanglelabel=peakforceanglelabel,
|
||||
annolabel=annolabel,
|
||||
sliderlabel=sliderlabel,
|
||||
lengthlabel=lengthlabel,
|
||||
efflengthlabel=efflengthlabel,
|
||||
plottype=plottype,
|
||||
sourcemultiline=sourcemultiline,
|
||||
sourcemultiline2=sourcemultiline2
|
||||
), code="""
|
||||
var data = source.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 y = data['y']
|
||||
|
||||
var xslip = dataslipwash['xslip']
|
||||
|
||||
var spm1 = data2['spm']
|
||||
var distance1 = data2['distance']
|
||||
var driveenergy1 = data2['driveenergy']
|
||||
@@ -592,6 +905,10 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
|
||||
var peakforce = data2['peakforce']
|
||||
var averageforce = data2['averageforce']
|
||||
|
||||
var peakforcepoints = datapoints['peakforce']
|
||||
var peakforceanglepoints = datapoints['peakforceangle']
|
||||
|
||||
var annotation = annotation.value
|
||||
var minspm = minspm.value
|
||||
var maxspm = maxspm.value
|
||||
var mindist = mindist.value
|
||||
@@ -599,6 +916,10 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
|
||||
var minwork = minwork.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 finishav = 0
|
||||
var slipav = 0
|
||||
@@ -608,11 +929,23 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
|
||||
var peakforceav = 0
|
||||
var count = 0
|
||||
|
||||
datapoints['peakforceangle'] = []
|
||||
datapoints['peakforce'] = []
|
||||
multilines['x'] = []
|
||||
multilines['y'] = []
|
||||
|
||||
for (i=0; i<c.length; i++) {
|
||||
if (spm1[i]>=minspm && spm1[i]<=maxspm) {
|
||||
if (distance1[i]>=mindist && distance1[i]<=maxdist) {
|
||||
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]
|
||||
finishav += finish[i]
|
||||
slipav += slip[i]
|
||||
@@ -637,6 +970,12 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
|
||||
data['x'] = [catchav,catchav+slipav,peakforceangleav,finishav-washav,finishav]
|
||||
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
|
||||
avflabel.text = 'Favg: '+averageforceav.toFixed(2)
|
||||
catchlabel.text = 'Catch: '+catchav.toFixed(2)
|
||||
@@ -645,11 +984,25 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
|
||||
washlabel.text = 'Wash: '+washav.toFixed(2)
|
||||
peakflabel.text = 'Fpeak: '+peakforceav.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.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,
|
||||
title="Min SPM",callback=callback)
|
||||
callback.args["minspm"] = slider_spm_min
|
||||
@@ -670,16 +1023,17 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
|
||||
|
||||
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)
|
||||
callback.args["mindist"] = slider_dist_min
|
||||
|
||||
slider_dist_max = Slider(start=0,end=distmax,value=distmax,
|
||||
step=1,
|
||||
step=50,
|
||||
title="Max Distance",callback=callback)
|
||||
callback.args["maxdist"] = slider_dist_max
|
||||
|
||||
layout = layoutrow([layoutcolumn([slider_spm_min,
|
||||
layout = layoutrow([layoutcolumn([annotation,
|
||||
slider_spm_min,
|
||||
slider_spm_max,
|
||||
slider_dist_min,
|
||||
slider_dist_max,
|
||||
@@ -860,12 +1214,13 @@ def fitnessmetric_chart(fitnessmetrics,user,workoutmode='rower',startdate=None,
|
||||
|
||||
return [script,div]
|
||||
|
||||
def interactive_histoall(theworkouts):
|
||||
def interactive_histoall(theworkouts,histoparam,includereststrokes):
|
||||
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
|
||||
|
||||
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)
|
||||
|
||||
@@ -873,16 +1228,16 @@ def interactive_histoall(theworkouts):
|
||||
return "","No Valid Data Available","",""
|
||||
|
||||
try:
|
||||
histopwr = rowdata['power'].values
|
||||
histopwr = rowdata[histoparam].values
|
||||
except KeyError:
|
||||
return "","No power data","",""
|
||||
return "","No data","",""
|
||||
if len(histopwr) == 0:
|
||||
return "","No valid data available","",""
|
||||
|
||||
# throw out nans
|
||||
histopwr = histopwr[~np.isinf(histopwr)]
|
||||
histopwr = histopwr[histopwr > 25]
|
||||
histopwr = histopwr[histopwr < 1000]
|
||||
histopwr = histopwr[histopwr > yaxminima[histoparam]]
|
||||
histopwr = histopwr[histopwr < yaxmaxima[histoparam]]
|
||||
|
||||
plot = Figure(tools=TOOLS,plot_width=900,
|
||||
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='left',right='right',source=source)
|
||||
|
||||
plot.xaxis.axis_label = "Power (W)"
|
||||
plot.xaxis.axis_label = axlabels[histoparam]
|
||||
plot.yaxis.axis_label = "% of strokes"
|
||||
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.tooltips = OrderedDict([
|
||||
('Power(W)','@left{int}'),
|
||||
(axlabels[histoparam],'@left{int}'),
|
||||
('% of strokes','@hist_norm'),
|
||||
('Cumulative %','@histsum{int}'),
|
||||
])
|
||||
@@ -957,12 +1312,36 @@ def interactive_histoall(theworkouts):
|
||||
axis_label="Cumulative % of strokes"),'right')
|
||||
|
||||
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:
|
||||
script, div = components(plot)
|
||||
script, div = components(layout)
|
||||
except ValueError:
|
||||
script = ''
|
||||
div = ''
|
||||
|
||||
|
||||
|
||||
return [script,div]
|
||||
|
||||
def course_map(course):
|
||||
@@ -2370,7 +2749,8 @@ def interactive_chart(id=0,promember=0,intervaldata = {}):
|
||||
|
||||
def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='',
|
||||
ploterrorbars=False,
|
||||
title=None,binsize=1,colorlegend=[]):
|
||||
title=None,binsize=1,colorlegend=[],
|
||||
spmmin=0,spmmax=0,workmin=0,workmax=0):
|
||||
|
||||
if datadf.empty:
|
||||
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:
|
||||
box = BoxAnnotation(bottom=125+20*nr,left=100,top=145+20*nr,
|
||||
right=120,
|
||||
box = BoxAnnotation(bottom=75+20*nr,left=50,top=95+20*nr,
|
||||
right=70,
|
||||
bottom_units='screen',
|
||||
top_units='screen',
|
||||
left_units='screen',
|
||||
@@ -2524,7 +2904,7 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='',
|
||||
fill_color=color,
|
||||
fill_alpha=1.0,
|
||||
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',
|
||||
text = "{gvalue:3.0f}".format(gvalue=gvalue),
|
||||
background_fill_alpha=1.0,
|
||||
@@ -2534,7 +2914,7 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='',
|
||||
plot.add_layout(legendlabel)
|
||||
|
||||
if colorlegend:
|
||||
legendlabel = Label(x=372,y=300,x_units='screen',
|
||||
legendlabel = Label(x=322,y=250,x_units='screen',
|
||||
y_units='screen',
|
||||
text = 'group legend',
|
||||
text_color='black',
|
||||
@@ -2552,15 +2932,27 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='',
|
||||
else:
|
||||
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',
|
||||
text="Bin size {binsize:3.1f}".format(binsize=binsize),
|
||||
background_fill_alpha=0.7,
|
||||
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(sliderlabel)
|
||||
|
||||
yrange1 = Range1d(start=yaxmin,end=yaxmax)
|
||||
plot.y_range = yrange1
|
||||
@@ -2946,12 +3338,12 @@ def interactive_cum_flex_chart2(theworkouts,promember=0,
|
||||
except KeyError:
|
||||
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)
|
||||
callback.args["mindist"] = slider_dist_min
|
||||
|
||||
slider_dist_max = Slider(start=0,end=distmax,value=distmax,
|
||||
step=1,
|
||||
step=50,
|
||||
title="Max Distance",callback=callback)
|
||||
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="",
|
||||
callback=callback)
|
||||
callback=callback)
|
||||
callback.args["annotation"] = annotation
|
||||
|
||||
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):
|
||||
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)
|
||||
callback.args["mindist"] = slider_dist_min
|
||||
|
||||
slider_dist_max = Slider(start=0,end=distmax,value=distmax,
|
||||
step=1,
|
||||
step=50,
|
||||
title="Max Distance",callback=callback)
|
||||
callback.args["maxdist"] = slider_dist_max
|
||||
|
||||
|
||||
@@ -106,7 +106,8 @@ def make_new_workout_from_email(rower, datafile, name, cntr=0,testing=False):
|
||||
|
||||
if testing:
|
||||
print('Fileformat = ',fileformat)
|
||||
|
||||
|
||||
|
||||
if fileformat == 'unknown':
|
||||
# extension = datafilename[-4:].lower()
|
||||
# fcopy = "media/"+datafilename[:-4]+"_copy"+extension
|
||||
|
||||
@@ -234,6 +234,8 @@ class Command(BaseCommand):
|
||||
]
|
||||
except IOError:
|
||||
rowers = []
|
||||
except Message.DoesNotExist:
|
||||
attachment.delete()
|
||||
for rower in rowers:
|
||||
if extension == 'zip':
|
||||
try:
|
||||
|
||||
@@ -285,7 +285,7 @@ axesnew = [
|
||||
(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}
|
||||
|
||||
|
||||
@@ -97,6 +97,12 @@ import arrow
|
||||
|
||||
from rowers.utils import get_strava_stream
|
||||
|
||||
def safetimedelta(x):
|
||||
try:
|
||||
return timedelta(seconds=x)
|
||||
except ValueError:
|
||||
return timedelta(seconds=0)
|
||||
|
||||
siteurl = SITE_URL
|
||||
|
||||
# testing task
|
||||
@@ -292,7 +298,7 @@ def handle_check_race_course(self,
|
||||
rowdata = rowdata[rowdata['time']>splitsecond]
|
||||
# we may want to expand the time (interpolate)
|
||||
rowdata['dt'] = rowdata['time'].apply(
|
||||
lambda x: timedelta(seconds=x)
|
||||
lambda x: safetimedelta(seconds=x)
|
||||
)
|
||||
rowdata = rowdata.resample('100ms',on='dt').mean()
|
||||
rowdata = rowdata.interpolate()
|
||||
@@ -618,7 +624,7 @@ def handle_calctrimp(id,
|
||||
|
||||
df2['time'] = df2[' ElapsedTime (sec)']
|
||||
df2['time'] = df2['time'].apply(
|
||||
lambda x:timedelta(seconds=x)
|
||||
lambda x:safetimedelta(seconds=x)
|
||||
)
|
||||
|
||||
duration = df['TimeStamp (sec)'].max()-df['TimeStamp (sec)'].min()
|
||||
@@ -1659,11 +1665,10 @@ def handle_makeplot(f1, f2, t, hrdata, plotnr, imagename,
|
||||
|
||||
try:
|
||||
haspower = row.df[' Power (watts)'].mean() > 50
|
||||
except TypeError:
|
||||
haspower = True
|
||||
except KeyError:
|
||||
except (TypeError, KeyError):
|
||||
haspower = False
|
||||
|
||||
|
||||
nr_rows = len(row.df)
|
||||
if (plotnr in [1, 2, 4, 5, 8, 11, 9, 12]) and (nr_rows > 1200):
|
||||
bin = int(nr_rows / 1200.)
|
||||
@@ -1700,14 +1705,13 @@ def handle_makeplot(f1, f2, t, hrdata, plotnr, imagename,
|
||||
t += ' - Power Distribution'
|
||||
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)
|
||||
# plt.imsave(fname='static/plots/'+imagename)
|
||||
plt.close(fig1)
|
||||
fig1.clf()
|
||||
gc.collect()
|
||||
canvas.print_figure('static/plots/' + imagename)
|
||||
plt.close(fig1)
|
||||
fig1.clf()
|
||||
gc.collect()
|
||||
|
||||
return imagename
|
||||
|
||||
|
||||
@@ -11,18 +11,6 @@
|
||||
|
||||
|
||||
<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">
|
||||
<h2>Compare Workouts</h2>
|
||||
{% if team %}
|
||||
@@ -52,14 +40,14 @@
|
||||
</p>
|
||||
</li>
|
||||
<li class="rounder">
|
||||
<h2>Power Histogram</h2>
|
||||
<h2>Histogram</h2>
|
||||
<a href="/rowers/histo/">
|
||||
<div class="vignet">
|
||||
<img src="/static/img/histogram.png" alt="Power Histogram">
|
||||
</div>
|
||||
</a>
|
||||
<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>
|
||||
</li>
|
||||
<li class="rounder">
|
||||
@@ -85,29 +73,6 @@
|
||||
BETA: Box Chart Statistics of stroke metrics over a date range
|
||||
</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>
|
||||
<li class="rounder">
|
||||
<h2>Trend Flex</h2>
|
||||
<a href="/rowers/user-multiflex-select/">
|
||||
@@ -131,6 +96,41 @@
|
||||
Monitoring power duration evidence from all your workouts. Feel free to explore.
|
||||
</p>
|
||||
</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>
|
||||
|
||||
|
||||
|
||||
@@ -23,23 +23,22 @@
|
||||
|
||||
<ul class="main-content">
|
||||
<li class="grid_4">
|
||||
{% if user.is_authenticated and mayedit %}
|
||||
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
|
||||
{% csrf_token %}
|
||||
{% 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 %}
|
||||
<div id="theplot" class="flexplot">
|
||||
{{ the_div|safe }}
|
||||
</div>
|
||||
|
||||
</li>
|
||||
<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>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
|
||||
</div>
|
||||
|
||||
<h1>Histogram View</h1>
|
||||
|
||||
<ul class="main-content">
|
||||
|
||||
<li class="grid_4">
|
||||
@@ -100,6 +102,11 @@
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
</li>
|
||||
<li>
|
||||
<table>
|
||||
{{ histoform.as_table }}
|
||||
</table>
|
||||
</li>
|
||||
<li>
|
||||
{% csrf_token %}
|
||||
<input class="button green small" value="Submit" type="Submit">
|
||||
|
||||
@@ -5,20 +5,30 @@
|
||||
<li class="has-children" id="fitness">
|
||||
<input type="checkbox" name="group-fitness" id="group-fitness" checked>
|
||||
<label for="group-fitness">
|
||||
<i class="fas fa-watch-fitness fa-fw"></i> Fitness</label>
|
||||
<i class="fas fa-watch-fitness fa-fw"></i> Fitness
|
||||
</label>
|
||||
<ul>
|
||||
<li id="compare">
|
||||
{% if team %}
|
||||
<a href="/rowers/team-compare-select/team/{{ team.id }}/">
|
||||
<i class="fas fa-balance-scale fa-fw"></i> Compare
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="/rowers/team-compare-select/team/0/">
|
||||
<i class="fas fa-balance-scale fa-fw"></i> Compare
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
<li id="fitness-powerprogress">
|
||||
<a href="/rowers/fitness-progress/">
|
||||
<i class="far fa-watch-fitness fa-fw"></i> Power Progress
|
||||
</a>
|
||||
</li>
|
||||
<li id="fitness-ranking">
|
||||
<a href="/rowers/ote-bests2/">
|
||||
<i class="fas fa-star fa-fw"></i> Ranking Pieces
|
||||
</a>
|
||||
</li>
|
||||
<li id="compare">
|
||||
{% if team %}
|
||||
<a href="/rowers/team-compare-select/team/{{ team.id }}/"><i class="fas fa-balance-scale fa-fw"></i> Compare</a>
|
||||
{% else %}
|
||||
<a href="/rowers/team-compare-select/team/0/"><i class="fas fa-balance-scale fa-fw"></i> Compare</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
<li id="fitness-otecp">
|
||||
<a href="/rowers/ote-ranking/">
|
||||
<i class="fas fa-user-chart fa-fw"></i> CP Chart OTE
|
||||
@@ -29,11 +39,6 @@
|
||||
<i class="far fa-user-chart fa-fw"></i> CP Chart OTW
|
||||
</a>
|
||||
</li>
|
||||
<li id="fitness-powerprogress">
|
||||
<a href="/rowers/fitness-progress/">
|
||||
<i class="far fa-watch-fitness fa-fw"></i> Power Progress
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="has-children" id="stats">
|
||||
@@ -57,9 +62,9 @@
|
||||
<i class="fal fa-table fa-fw"></i> Statistics
|
||||
</a>
|
||||
</li>
|
||||
<li id="stats-histopower">
|
||||
<li id="stats-histo">
|
||||
<a href="/rowers/histo/">
|
||||
<i class="fas fa-chart-bar"></i> Power Histogram
|
||||
<i class="fas fa-chart-bar"></i> Histogram
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -20,11 +20,17 @@ def histo(request,theuser=0,
|
||||
'workouttypes':[i[0] for i in mytypes.workouttypes],
|
||||
'waterboattype':mytypes.waterboattype,
|
||||
'rankingonly': False,
|
||||
'histoparam':'power'
|
||||
}):
|
||||
|
||||
r = getrequestrower(request,userid=theuser)
|
||||
theuser = r.user
|
||||
|
||||
|
||||
if 'histoparam' in request.session:
|
||||
histoparam = request.session['histoparam']
|
||||
else:
|
||||
histoparam = 'power'
|
||||
|
||||
if 'waterboattype' in request.session:
|
||||
waterboattype = request.session['waterboattype']
|
||||
else:
|
||||
@@ -81,6 +87,7 @@ def histo(request,theuser=0,
|
||||
if request.method == 'POST':
|
||||
form = DateRangeForm(request.POST)
|
||||
modalityform = TrendFlexModalForm(request.POST)
|
||||
histoform = HistoForm(request.POST)
|
||||
if form.is_valid():
|
||||
startdate = form.cleaned_data['startdate']
|
||||
enddate = form.cleaned_data['enddate']
|
||||
@@ -110,6 +117,11 @@ def histo(request,theuser=0,
|
||||
'startdate': startdate,
|
||||
'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:
|
||||
form = DateRangeForm(initial={
|
||||
'startdate': startdate,
|
||||
@@ -125,6 +137,10 @@ def histo(request,theuser=0,
|
||||
'rankingonly':rankingonly,
|
||||
}
|
||||
)
|
||||
histoform = HistoForm(initial={
|
||||
'includereststrokes':False,
|
||||
'histoparam':histoparam
|
||||
})
|
||||
|
||||
negtypes = []
|
||||
for b in mytypes.boattypes:
|
||||
@@ -149,6 +165,7 @@ def histo(request,theuser=0,
|
||||
'enddatestring':enddatestring,
|
||||
'rankingonly':rankingonly,
|
||||
'includereststrokes':includereststrokes,
|
||||
'histoparam':histoparam,
|
||||
}
|
||||
|
||||
request.session['options'] = options
|
||||
@@ -163,9 +180,21 @@ def histo(request,theuser=0,
|
||||
|
||||
request.session['options'] = options
|
||||
|
||||
breadcrumbs = [
|
||||
{
|
||||
'url':'/rowers/analysis',
|
||||
'name':'Analysis'
|
||||
},
|
||||
{
|
||||
'url':reverse('histo'),
|
||||
'name': 'Histogram'
|
||||
}
|
||||
]
|
||||
|
||||
return render(request, 'histo.html',
|
||||
{'interactiveplot':script,
|
||||
'the_div':div,
|
||||
'breadcrumbs':breadcrumbs,
|
||||
'id':theuser,
|
||||
'active':'nav-analysis',
|
||||
'theuser':theuser,
|
||||
@@ -174,6 +203,7 @@ def histo(request,theuser=0,
|
||||
'enddate':enddate,
|
||||
'form':form,
|
||||
'optionsform':modalityform,
|
||||
'histoform':histoform,
|
||||
'teams':get_my_teams(request.user),
|
||||
})
|
||||
|
||||
@@ -295,6 +325,7 @@ def histo_data(
|
||||
'enddatestring':timezone.now().strftime("%Y-%m-%d"),
|
||||
'startdatestring':(timezone.now()-datetime.timedelta(days=30)).strftime("%Y-%m-%d"),
|
||||
'deltadays':-1,
|
||||
'histoparam':'power',
|
||||
}):
|
||||
|
||||
def_options = options
|
||||
@@ -311,6 +342,7 @@ def histo_data(
|
||||
theuser = keyvalue_get_default('theuser',options,def_options)
|
||||
startdatestring = keyvalue_get_default('startdatestring',options,def_options)
|
||||
enddatestring = keyvalue_get_default('enddatestring',options,def_options)
|
||||
histoparam = keyvalue_get_default('histoparam',options,def_options)
|
||||
|
||||
if modality == 'all':
|
||||
modalities = [m[0] for m in mytypes.workouttypes]
|
||||
@@ -358,7 +390,7 @@ def histo_data(
|
||||
rankingpiece__in=rankingpiece)
|
||||
|
||||
if allworkouts:
|
||||
res = interactive_histoall(allworkouts)
|
||||
res = interactive_histoall(allworkouts,histoparam,includereststrokes)
|
||||
script = res[0]
|
||||
div = res[1]
|
||||
else:
|
||||
@@ -2593,7 +2625,9 @@ def multiflex_data(request,userid=0,
|
||||
extratitle=extratitle,
|
||||
ploterrorbars=ploterrorbars,
|
||||
binsize=binsize,
|
||||
colorlegend=colorlegend)
|
||||
colorlegend=colorlegend,
|
||||
spmmin=spmmin,spmmax=spmmax,
|
||||
workmin=workmin,workmax=workmax)
|
||||
|
||||
scripta= script.split('\n')[2:-1]
|
||||
script = ''.join(scripta)
|
||||
@@ -3085,7 +3119,8 @@ def boxplot_view_data(request,userid=0,
|
||||
|
||||
|
||||
script,div = interactive_boxchart(datadf,plotfield,
|
||||
extratitle=extratitle)
|
||||
extratitle=extratitle,
|
||||
spmmin=spmmin,spmmax=spmmax,workmin=workmin,workmax=workmax)
|
||||
|
||||
scripta = script.split('\n')[2:-1]
|
||||
script = ''.join(scripta)
|
||||
|
||||
@@ -48,6 +48,7 @@ from django.http import (
|
||||
)
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from rowers.forms import (
|
||||
ForceCurveOptionsForm,HistoForm,
|
||||
LoginForm,DocumentsForm,UploadOptionsForm,ImageForm,CourseForm,
|
||||
TeamUploadOptionsForm,WorkFlowLeftPanelForm,WorkFlowMiddlePanelForm,
|
||||
WorkFlowLeftPanelElement,WorkFlowMiddlePanelElement,
|
||||
|
||||
@@ -26,15 +26,24 @@ def workout_forcecurve_view(request,id=0,workstrokesonly=False):
|
||||
if not promember:
|
||||
return HttpResponseRedirect("/rowers/about/")
|
||||
|
||||
if request.method == 'POST' and 'workstrokesonly' in request.POST:
|
||||
workstrokesonly = request.POST['workstrokesonly']
|
||||
if workstrokesonly == 'True':
|
||||
workstrokesonly = True
|
||||
if request.method == 'POST':
|
||||
form = ForceCurveOptionsForm(request.POST)
|
||||
if form.is_valid():
|
||||
includereststrokes = form.cleaned_data['includereststrokes']
|
||||
plottype = form.cleaned_data['plottype']
|
||||
workstrokesonly = not includereststrokes
|
||||
else:
|
||||
workstrokesonly = False
|
||||
workstrokesonly = True
|
||||
plottype = 'line'
|
||||
else:
|
||||
form = ForceCurveOptionsForm()
|
||||
plottype = 'line'
|
||||
|
||||
script,div,js_resources,css_resources = interactive_forcecurve([row],
|
||||
workstrokesonly=workstrokesonly)
|
||||
script,div,js_resources,css_resources = interactive_forcecurve(
|
||||
[row],
|
||||
workstrokesonly=workstrokesonly,
|
||||
plottype=plottype,
|
||||
)
|
||||
|
||||
breadcrumbs = [
|
||||
{
|
||||
@@ -53,12 +62,13 @@ def workout_forcecurve_view(request,id=0,workstrokesonly=False):
|
||||
]
|
||||
|
||||
r = getrower(request.user)
|
||||
|
||||
|
||||
return render(request,
|
||||
'forcecurve_single.html',
|
||||
{
|
||||
'the_script':script,
|
||||
'rower':r,
|
||||
'form':form,
|
||||
'workout':row,
|
||||
'breadcrumbs':breadcrumbs,
|
||||
'active':'nav-workouts',
|
||||
@@ -67,7 +77,6 @@ def workout_forcecurve_view(request,id=0,workstrokesonly=False):
|
||||
'css_res':css_resources,
|
||||
'id':id,
|
||||
'mayedit':mayedit,
|
||||
'workstrokesonly': not workstrokesonly,
|
||||
'teams':get_my_teams(request.user),
|
||||
})
|
||||
|
||||
@@ -102,7 +111,7 @@ def workout_histo_view(request,id=0):
|
||||
if not promember:
|
||||
return HttpResponseRedirect("/rowers/about/")
|
||||
|
||||
res = interactive_histoall([w])
|
||||
res = interactive_histoall([w],'power',False)
|
||||
script = res[0]
|
||||
div = res[1]
|
||||
|
||||
@@ -4834,8 +4843,8 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
|
||||
s = cd["intervalstring"]
|
||||
try:
|
||||
rowdata.updateinterval_string(s)
|
||||
except (ParseException,err):
|
||||
messages.error(request,'Parsing error in column '+str(err.col))
|
||||
except:
|
||||
messages.error(request,'Parsing error')
|
||||
intervalstats = rowdata.allstats()
|
||||
itime,idist,itype = rowdata.intervalstats_values()
|
||||
nrintervals = len(idist)
|
||||
|
||||
Reference in New Issue
Block a user