4826 lines
145 KiB
Python
4826 lines
145 KiB
Python
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import print_function
|
|
from __future__ import unicode_literals
|
|
from __future__ import unicode_literals, absolute_import
|
|
from rowers.views.statements import *
|
|
|
|
import collections
|
|
from jinja2 import Template,Environment,FileSystemLoader
|
|
|
|
def floatformat(x,prec=2):
|
|
return '{x}'.format(x=round(x,prec))
|
|
|
|
|
|
env = Environment(loader = FileSystemLoader(["rowers/templates"]))
|
|
env.filters['floatformat'] = floatformat
|
|
|
|
|
|
from django.contrib.staticfiles import finders
|
|
from rowers.forms import analysischoices
|
|
|
|
# generic Analysis view -
|
|
|
|
defaultoptions = {
|
|
'includereststrokes': False,
|
|
'workouttypes':['rower','dynamic','slides'],
|
|
'waterboattype': mytypes.waterboattype,
|
|
'rankingonly': False,
|
|
'function':'boxplot'
|
|
}
|
|
|
|
|
|
@user_passes_test(ispromember, login_url="/rowers/paidplans",
|
|
message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
|
|
redirect_field_name=None)
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def analysis_new(request,userid=0,function='boxplot',teamid=0):
|
|
r = getrequestrower(request, userid=userid)
|
|
user = r.user
|
|
userid = user.id
|
|
|
|
try:
|
|
theteam = Team.objects.get(id=teamid)
|
|
except Team.DoesNotExist:
|
|
theteam = None
|
|
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
else:
|
|
options=defaultoptions
|
|
|
|
options['userid'] = userid
|
|
try:
|
|
workouttypes = options['workouttypes']
|
|
except KeyError:
|
|
workouttypes = ['rower','dynamic','slides']
|
|
|
|
try:
|
|
modalities = options['modalities']
|
|
modality = modalities[0]
|
|
except KeyError:
|
|
modalities = [m[0] for m in mytypes.workouttypes_ordered.items()]
|
|
modality = 'all'
|
|
|
|
|
|
|
|
try:
|
|
rankingonly = options['rankingonly']
|
|
except KeyError:
|
|
rankingonly = False
|
|
|
|
try:
|
|
includereststrokes = options['includereststrokes']
|
|
except KeyError:
|
|
includereststrokes = False
|
|
|
|
if 'startdate' in request.session:
|
|
startdate = iso8601.parse_date(request.session['startdate'])
|
|
else:
|
|
startdate=timezone.now()-datetime.timedelta(days=42)
|
|
|
|
if function not in [c[0] for c in analysischoices]:
|
|
function = 'boxplot'
|
|
|
|
if 'enddate' in request.session:
|
|
enddate = iso8601.parse_date(request.session['enddate'])
|
|
else:
|
|
enddate = timezone.now()
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
waterboattype = mytypes.waterboattype
|
|
|
|
if request.method == 'POST':
|
|
thediv = get_call()
|
|
dateform = DateRangeForm(request.POST)
|
|
if dateform.is_valid():
|
|
startdate = dateform.cleaned_data['startdate']
|
|
enddate = dateform.cleaned_data['enddate']
|
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
|
request.session['startdate'] = startdatestring
|
|
request.session['enddate'] = enddatestring
|
|
optionsform = AnalysisOptionsForm(request.POST)
|
|
if optionsform.is_valid():
|
|
for key, value in optionsform.cleaned_data.items():
|
|
options[key] = value
|
|
|
|
modality = optionsform.cleaned_data['modality']
|
|
waterboattype = optionsform.cleaned_data['waterboattype']
|
|
if modality == 'all':
|
|
modalities = [m[0] for m in mytypes.workouttypes]
|
|
else:
|
|
modalities = [modality]
|
|
if modality != 'water':
|
|
waterboattype = [b[0] for b in mytypes.boattypes]
|
|
|
|
|
|
if 'rankingonly' in optionsform.cleaned_data:
|
|
rankingonly = optionsform.cleaned_data['rankingonly']
|
|
else:
|
|
rankingonly = False
|
|
|
|
options['modalities'] = modalities
|
|
options['waterboattype'] = waterboattype
|
|
|
|
chartform = AnalysisChoiceForm(request.POST)
|
|
if chartform.is_valid():
|
|
for key, value in chartform.cleaned_data.items():
|
|
options[key] = value
|
|
|
|
|
|
form = WorkoutMultipleCompareForm(request.POST)
|
|
if form.is_valid():
|
|
cd = form.cleaned_data
|
|
selectedworkouts = cd['workouts']
|
|
ids = [int(w.id) for w in selectedworkouts]
|
|
options['ids'] = ids
|
|
else:
|
|
ids = []
|
|
options['ids'] = ids
|
|
else:
|
|
thediv = ''
|
|
dateform = DateRangeForm(initial={
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
})
|
|
|
|
|
|
|
|
negtypes = []
|
|
for b in mytypes.boattypes:
|
|
if b[0] not in waterboattype:
|
|
negtypes.append(b[0])
|
|
|
|
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
negtypes = []
|
|
for b in mytypes.boattypes:
|
|
if b[0] not in waterboattype:
|
|
negtypes.append(b[0])
|
|
|
|
|
|
|
|
if theteam is not None and (theteam.viewing == 'allmembers' or theteam.manager == request.user):
|
|
workouts = Workout.objects.filter(team=theteam,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate,
|
|
workouttype__in=modalities,
|
|
)
|
|
elif theteam is not None and theteam.viewing == 'coachonly':
|
|
workouts = Workout.objects.filter(team=theteam,user=r,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate,
|
|
workouttype__in=modalities,
|
|
)
|
|
else:
|
|
workouts = Workout.objects.filter(user=r,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate,
|
|
workouttype__in=modalities,
|
|
)
|
|
|
|
workouts = workouts.order_by(
|
|
"-date", "-starttime"
|
|
).exclude(boattype__in=negtypes)
|
|
|
|
|
|
if rankingonly:
|
|
workouts = workouts.exclude(rankingpiece=False)
|
|
|
|
query = request.GET.get('q')
|
|
if query:
|
|
query_list = query.split()
|
|
workouts = workouts.filter(
|
|
reduce(operator.and_,
|
|
(Q(name__icontains=q) for q in query_list)) |
|
|
reduce(operator.and_,
|
|
(Q(notes__icontains=q) for q in query_list))
|
|
)
|
|
searchform = SearchForm(initial={'q':query})
|
|
else:
|
|
searchform = SearchForm()
|
|
|
|
if request.method != 'POST':
|
|
form = WorkoutMultipleCompareForm()
|
|
chartform = AnalysisChoiceForm(initial={'function':function})
|
|
selectedworkouts = Workout.objects.none()
|
|
else:
|
|
selectedworkouts = Workout.objects.filter(id__in=ids)
|
|
|
|
form.fields["workouts"].queryset = workouts | selectedworkouts
|
|
|
|
optionsform = AnalysisOptionsForm(initial={
|
|
'modality':modality,
|
|
'waterboattype':waterboattype,
|
|
'rankingonly':rankingonly,
|
|
})
|
|
|
|
|
|
|
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
|
request.session['startdate'] = startdatestring
|
|
request.session['enddate'] = enddatestring
|
|
request.session['options'] = options
|
|
|
|
|
|
breadcrumbs = [
|
|
{
|
|
'url':'/rowers/analysis',
|
|
'name':'Analysis'
|
|
},
|
|
{
|
|
'url':reverse('analysis_new',kwargs={'userid':userid}),
|
|
'name': 'Analysis Select'
|
|
},
|
|
]
|
|
return render(request, 'user_analysis_select.html',
|
|
{'workouts': workouts,
|
|
'dateform':dateform,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'rower':r,
|
|
'breadcrumbs':breadcrumbs,
|
|
'theuser':user,
|
|
'the_div':thediv,
|
|
'form':form,
|
|
'active':'nav-analysis',
|
|
'chartform':chartform,
|
|
'searchform':searchform,
|
|
'optionsform':optionsform,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
def trendflexdata(workouts, options,userid=0):
|
|
|
|
includereststrokes = options['includereststrokes']
|
|
palette = options['palette']
|
|
groupby = options['groupby']
|
|
binsize = options['binsize']
|
|
xparam = options['xparam']
|
|
yparam = options['yparam']
|
|
spmmin = options['spmmin']
|
|
spmmax = options['spmmax']
|
|
workmin = options['workmin']
|
|
workmax = options['workmax']
|
|
ploterrorbars = options['ploterrorbars']
|
|
ids = options['ids']
|
|
workstrokesonly = not includereststrokes
|
|
|
|
labeldict = {
|
|
int(w.id): w.__str__() for w in workouts
|
|
}
|
|
|
|
fieldlist,fielddict = dataprep.getstatsfields()
|
|
fieldlist = [xparam,yparam,groupby,
|
|
'workoutid','spm','driveenergy',
|
|
'workoutstate']
|
|
|
|
# prepare data frame
|
|
datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)
|
|
|
|
if xparam == groupby:
|
|
datadf['groupby'] = datadf[xparam]
|
|
groupy = 'groupby'
|
|
|
|
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
|
|
|
|
|
|
datadf = dataprep.filter_df(datadf,'spm',spmmin,
|
|
largerthan=True)
|
|
datadf = dataprep.filter_df(datadf,'spm',spmmax,
|
|
largerthan=False)
|
|
|
|
datadf = dataprep.filter_df(datadf,'driveenergy',workmin,
|
|
largerthan=True)
|
|
datadf = dataprep.filter_df(datadf,'driveneergy',workmax,
|
|
largerthan=False)
|
|
|
|
|
|
datadf.dropna(axis=0,how='any',inplace=True)
|
|
|
|
|
|
datemapping = {
|
|
w.id:w.date for w in workouts
|
|
}
|
|
|
|
datadf['date'] = datadf['workoutid']
|
|
datadf['date'].replace(datemapping,inplace=True)
|
|
|
|
today = datetime.date.today()
|
|
|
|
try:
|
|
datadf['days ago'] = list(map(lambda x : x.days, datadf.date - today))
|
|
except TypeError:
|
|
datadf['days ago'] = 0
|
|
|
|
|
|
if groupby != 'date':
|
|
try:
|
|
bins = np.arange(datadf[groupby].min()-binsize,
|
|
datadf[groupby].max()+binsize,
|
|
binsize)
|
|
groups = datadf.groupby(pd.cut(datadf[groupby],bins,labels=False))
|
|
except ValueError:
|
|
return ('','Error: not enough data')
|
|
else:
|
|
bins = np.arange(datadf['days ago'].min()-binsize,
|
|
datadf['days ago'].max()+binsize,
|
|
binsize,
|
|
)
|
|
groups = datadf.groupby(pd.cut(datadf['days ago'], bins,
|
|
labels=False))
|
|
|
|
|
|
xvalues = groups.mean()[xparam]
|
|
yvalues = groups.mean()[yparam]
|
|
xerror = groups.std()[xparam]
|
|
yerror = groups.std()[yparam]
|
|
groupsize = groups.count()[xparam]
|
|
|
|
mask = groupsize <= min([0.01*groupsize.sum(),0.2*groupsize.mean()])
|
|
xvalues.loc[mask] = np.nan
|
|
|
|
yvalues.loc[mask] = np.nan
|
|
xerror.loc[mask] = np.nan
|
|
yerror.loc[mask] = np.nan
|
|
groupsize.loc[mask] = np.nan
|
|
|
|
xvalues.dropna(inplace=True)
|
|
yvalues.dropna(inplace=True)
|
|
xerror.dropna(inplace=True)
|
|
yerror.dropna(inplace=True)
|
|
groupsize.dropna(inplace=True)
|
|
|
|
if len(groupsize) == 0:
|
|
messages.error(request,'No data in selection')
|
|
url = reverse(user_multiflex_select)
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
groupsize = 30.*np.sqrt(groupsize/float(groupsize.max()))
|
|
|
|
df = pd.DataFrame({
|
|
xparam:xvalues,
|
|
yparam:yvalues,
|
|
'x':xvalues,
|
|
'y':yvalues,
|
|
'xerror':xerror,
|
|
'yerror':yerror,
|
|
'groupsize':groupsize,
|
|
})
|
|
|
|
|
|
if yparam == 'pace':
|
|
df['y'] = dataprep.paceformatsecs(df['y']/1.0e3)
|
|
|
|
aantal = len(df)
|
|
|
|
if groupby != 'date':
|
|
try:
|
|
df['groupval'] = groups.mean()[groupby]
|
|
df['groupval'].loc[mask] = np.nan
|
|
|
|
groupcols = df['groupval']
|
|
except ValueError:
|
|
df['groupval'] = groups.mean()[groupby].fillna(value=0)
|
|
df['groupval'].loc[mask] = np.nan
|
|
groupcols = df['groupval']
|
|
except KeyError:
|
|
messages.error(request,'Data selection error')
|
|
url = reverse(user_multiflex_select)
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
try:
|
|
dates = groups.min()[groupby]
|
|
dates.loc[mask] = np.nan
|
|
dates.dropna(inplace=True)
|
|
df['groupval'] = [x.strftime("%Y-%m-%d") for x in dates]
|
|
df['groupval'].loc[mask] = np.nan
|
|
groupcols = 100.*np.arange(aantal)/float(aantal)
|
|
except AttributeError:
|
|
df['groupval'] = groups.mean()['days ago'].fillna(value=0)
|
|
groupcols = 100.*np.arange(aantal)/float(aantal)
|
|
|
|
|
|
groupcols = (groupcols-groupcols.min())/(groupcols.max()-groupcols.min())
|
|
|
|
if aantal == 1:
|
|
groupcols = np.array([1.])
|
|
|
|
|
|
colors = range_to_color_hex(groupcols,palette=palette)
|
|
|
|
df['color'] = colors
|
|
|
|
clegendx = np.arange(0,1.2,.2)
|
|
legcolors = range_to_color_hex(clegendx,palette=palette)
|
|
if groupby != 'date':
|
|
clegendy = df['groupval'].min()+clegendx*(df['groupval'].max()-df['groupval'].min())
|
|
else:
|
|
clegendy = df.index.min()+clegendx*(df.index.max()-df.index.min())
|
|
|
|
|
|
|
|
colorlegend = zip(range(6),clegendy,legcolors)
|
|
|
|
|
|
if userid == 0:
|
|
extratitle = ''
|
|
else:
|
|
u = User.objects.get(id=userid)
|
|
extratitle = ' '+u.first_name+' '+u.last_name
|
|
|
|
|
|
|
|
script,div = interactive_multiflex(df,xparam,yparam,
|
|
groupby,
|
|
extratitle=extratitle,
|
|
ploterrorbars=ploterrorbars,
|
|
binsize=binsize,
|
|
colorlegend=colorlegend,
|
|
spmmin=spmmin,spmmax=spmmax,
|
|
workmin=workmin,workmax=workmax)
|
|
|
|
scripta= script.split('\n')[2:-1]
|
|
script = ''.join(scripta)
|
|
|
|
return(script,div)
|
|
|
|
def flexalldata(workouts, options):
|
|
includereststrokes = options['includereststrokes']
|
|
xparam = options['xaxis']
|
|
yparam1 = options['yaxis1']
|
|
yparam2 = options['yaxis2']
|
|
promember=True
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
res = interactive_cum_flex_chart2(workouts, xparam=xparam,
|
|
yparam1=yparam1,
|
|
yparam2=yparam2,
|
|
promember=promember,
|
|
workstrokesonly=workstrokesonly,
|
|
)
|
|
script = res[0]
|
|
div = res[1]
|
|
|
|
scripta = script.split('\n')[2:-1]
|
|
script = ''.join(scripta)
|
|
|
|
return(script,div)
|
|
|
|
def histodata(workouts, options):
|
|
includereststrokes = options['includereststrokes']
|
|
plotfield = options['plotfield']
|
|
function = options['function']
|
|
spmmin = options['spmmin']
|
|
spmmax = options['spmmax']
|
|
workmin = options['workmin']
|
|
workmax = options['workmax']
|
|
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
script, div = interactive_histoall(workouts,plotfield,includereststrokes,
|
|
spmmin=spmmin,spmmax=spmmax,workmin=workmin,workmax=workmax)
|
|
|
|
|
|
scripta = script.split('\n')[2:-1]
|
|
script = ''.join(scripta)
|
|
|
|
return(script,div)
|
|
|
|
def statsdata(workouts, options):
|
|
includereststrokes = options['includereststrokes']
|
|
spmmin = options['spmmin']
|
|
spmmax = options['spmmax']
|
|
workmin = options['workmin']
|
|
workmax = options['workmax']
|
|
ids = options['ids']
|
|
userid = options['userid']
|
|
plotfield = options['plotfield']
|
|
function = options['function']
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
ids = [w.id for w in workouts]
|
|
|
|
datamapping = {
|
|
w.id:w.date for w in workouts
|
|
}
|
|
|
|
fieldlist,fielddict = dataprep.getstatsfields()
|
|
# prepare data frame
|
|
datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)
|
|
|
|
|
|
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
|
|
|
|
# Create stats
|
|
stats = {}
|
|
# fielddict.pop('workoutstate')
|
|
# fielddict.pop('workoutid')
|
|
|
|
|
|
for field,verbosename in fielddict.items():
|
|
try:
|
|
thedict = {
|
|
'mean':datadf[field].mean(),
|
|
'min': datadf[field].min(),
|
|
'std': datadf[field].std(),
|
|
'max': datadf[field].max(),
|
|
'median': datadf[field].median(),
|
|
'firstq':datadf[field].quantile(q=0.25),
|
|
'thirdq':datadf[field].quantile(q=0.75),
|
|
'verbosename':verbosename,
|
|
}
|
|
stats[field] = thedict
|
|
except KeyError:
|
|
pass
|
|
|
|
# Create a dict with correlation values
|
|
cor = datadf.corr(method='spearman')
|
|
cor.fillna(value=0,inplace=True)
|
|
cordict = {}
|
|
for field1,verbosename1 in fielddict.items():
|
|
thedict = {}
|
|
for field2,verbosename2 in fielddict.items():
|
|
try:
|
|
thedict[verbosename2] = cor.loc[field1,field2]
|
|
except KeyError:
|
|
thedict[verbosename2] = 0
|
|
|
|
cordict[verbosename1] = thedict
|
|
|
|
context = {
|
|
'stats':stats,
|
|
'cordict':cordict,
|
|
}
|
|
|
|
htmly = env.get_template('statsdiv.html')
|
|
html_content = htmly.render(context)
|
|
|
|
return('',html_content)
|
|
|
|
def comparisondata(workouts,options):
|
|
includereststrokes = options['includereststrokes']
|
|
xparam = options['xaxis']
|
|
yparam1 = options['yaxis1']
|
|
plottype = options['plottype']
|
|
promember=True
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
ids = [w.id for w in workouts]
|
|
|
|
labeldict = {
|
|
int(w.id): w.__str__() for w in workouts
|
|
}
|
|
|
|
res = interactive_multiple_compare_chart(ids,xparam,yparam1,
|
|
promember=promember,
|
|
plottype=plottype,
|
|
labeldict=labeldict)
|
|
|
|
script = res[0]
|
|
div = res[1]
|
|
|
|
scripta = script.split('\n')[2:-1]
|
|
script = ''.join(scripta)
|
|
|
|
return(script,div)
|
|
|
|
|
|
def boxplotdata(workouts,options):
|
|
|
|
includereststrokes = options['includereststrokes']
|
|
spmmin = options['spmmin']
|
|
spmmax = options['spmmax']
|
|
workmin = options['workmin']
|
|
workmax = options['workmax']
|
|
ids = options['ids']
|
|
userid = options['userid']
|
|
plotfield = options['plotfield']
|
|
function = options['function']
|
|
|
|
workstrokesonly = not includereststrokes
|
|
labeldict = {
|
|
int(w.id): w.__str__() for w in workouts
|
|
}
|
|
|
|
|
|
datemapping = {
|
|
w.id:w.date for w in workouts
|
|
}
|
|
|
|
|
|
|
|
fieldlist,fielddict = dataprep.getstatsfields()
|
|
fieldlist = [plotfield,'workoutid','spm','driveenergy',
|
|
'workoutstate']
|
|
|
|
ids = [w.id for w in workouts]
|
|
|
|
# prepare data frame
|
|
datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)
|
|
|
|
|
|
|
|
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
|
|
|
|
datadf = dataprep.filter_df(datadf,'spm',spmmin,
|
|
largerthan=True)
|
|
datadf = dataprep.filter_df(datadf,'spm',spmmax,
|
|
largerthan=False)
|
|
datadf = dataprep.filter_df(datadf,'driveenergy',workmin,
|
|
largerthan=True)
|
|
datadf = dataprep.filter_df(datadf,'driveneergy',workmax,
|
|
largerthan=False)
|
|
|
|
datadf.dropna(axis=0,how='any',inplace=True)
|
|
|
|
|
|
datadf['workoutid'].replace(datemapping,inplace=True)
|
|
datadf.rename(columns={"workoutid":"date"},inplace=True)
|
|
datadf['date'] = pd.to_datetime(datadf['date'],errors='coerce')
|
|
datadf = datadf.dropna(subset=['date'])
|
|
datadf = datadf.sort_values(['date'])
|
|
|
|
if userid == 0:
|
|
extratitle = ''
|
|
else:
|
|
u = User.objects.get(id=userid)
|
|
extratitle = ' '+u.first_name+' '+u.last_name
|
|
|
|
|
|
|
|
script,div = interactive_boxchart(datadf,plotfield,
|
|
extratitle=extratitle,
|
|
spmmin=spmmin,spmmax=spmmax,workmin=workmin,workmax=workmax)
|
|
|
|
scripta = script.split('\n')[2:-1]
|
|
script = ''.join(scripta)
|
|
|
|
return(script,div)
|
|
|
|
@user_passes_test(ispromember,login_url="/rowers/paidplans",
|
|
message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
|
|
redirect_field_name=None)
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def analysis_view_data(request,userid=0):
|
|
if not request.is_ajax():
|
|
url = reverse('analysis_new')
|
|
return HttpResponseRedirect(url)
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
else:
|
|
options = defaultoptions
|
|
|
|
|
|
if userid==0:
|
|
userid = request.user.id
|
|
|
|
workouts = []
|
|
|
|
ids = options['ids']
|
|
function = options['function']
|
|
|
|
if not ids:
|
|
return JSONResponse({
|
|
"script":'',
|
|
"div":'No data found'
|
|
})
|
|
|
|
for id in ids:
|
|
try:
|
|
workouts.append(Workout.objects.get(id=id))
|
|
except Workout.DoesNotExist:
|
|
pass
|
|
|
|
if function == 'boxplot':
|
|
script, div = boxplotdata(workouts,options)
|
|
elif function == 'trendflex':
|
|
script, div = trendflexdata(workouts, options,userid=userid)
|
|
elif function == 'histo':
|
|
script, div = histodata(workouts, options)
|
|
elif function == 'flexall':
|
|
script,div = flexalldata(workouts,options)
|
|
elif function == 'stats':
|
|
script,div = statsdata(workouts,options)
|
|
elif function == 'compare':
|
|
script,div = comparisondata(workouts,options)
|
|
else:
|
|
script = ''
|
|
div = 'Unknown analysis functions'
|
|
|
|
|
|
return JSONResponse({
|
|
"script":script,
|
|
"div":div,
|
|
})
|
|
|
|
|
|
# Histogram for a date/time range
|
|
@user_passes_test(ispromember,login_url="/rowers/paidplans",
|
|
message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
|
|
redirect_field_name=None)
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def histo(request,userid=0,
|
|
startdate=timezone.now()-datetime.timedelta(days=365),
|
|
enddate=timezone.now(),
|
|
deltadays=-1,
|
|
enddatestring=timezone.now().strftime("%Y-%m-%d"),
|
|
startdatestring=(timezone.now()-datetime.timedelta(days=30)).strftime("%Y-%m-%d"),
|
|
options={
|
|
'includereststrokes':False,
|
|
'workouttypes':[i[0] for i in mytypes.workouttypes],
|
|
'waterboattype':mytypes.waterboattype,
|
|
'rankingonly': False,
|
|
'histoparam':'power'
|
|
}):
|
|
|
|
r = getrequestrower(request,userid=userid)
|
|
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:
|
|
waterboattype = mytypes.waterboattype
|
|
|
|
|
|
if 'rankingonly' in request.session:
|
|
rankingonly = request.session['rankingonly']
|
|
else:
|
|
rankingonly = False
|
|
|
|
if 'modalities' in request.session:
|
|
modalities = request.session['modalities']
|
|
if len(modalities) > 1:
|
|
modality = 'all'
|
|
else:
|
|
modality = modalities[0]
|
|
else:
|
|
modalities = [m[0] for m in mytypes.workouttypes]
|
|
modality = 'all'
|
|
|
|
|
|
try:
|
|
rankingonly = options['rankingonly']
|
|
except KeyError:
|
|
rankingonly = False
|
|
|
|
try:
|
|
includereststrokes = options['includereststrokes']
|
|
except KeyError:
|
|
includereststrokes = False
|
|
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
waterboattype = mytypes.waterboattype
|
|
|
|
|
|
if startdatestring != "":
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
|
|
if enddatestring != "":
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
|
|
# get all indoor rows of in date range
|
|
|
|
# process form
|
|
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']
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
|
if modalityform.is_valid():
|
|
modality = modalityform.cleaned_data['modality']
|
|
waterboattype = modalityform.cleaned_data['waterboattype']
|
|
rankingonly = modalityform.cleaned_data['rankingonly']
|
|
if modality == 'all':
|
|
modalities = [m[0] for m in mytypes.workouttypes]
|
|
else:
|
|
modalities = [modality]
|
|
|
|
if modality != 'water':
|
|
waterboattype = [b[0] for b in mytypes.boattypes]
|
|
|
|
|
|
request.session['modalities'] = modalities
|
|
request.session['waterboattype'] = waterboattype
|
|
request.session['rankingonly'] = rankingonly
|
|
form = DateRangeForm(initial={
|
|
'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,
|
|
'enddate': enddate,
|
|
})
|
|
includereststrokes = False
|
|
|
|
workstrokesonly = not includereststrokes
|
|
modalityform = TrendFlexModalForm(
|
|
initial={
|
|
'modality':modality,
|
|
'waterboattype':waterboattype,
|
|
'rankingonly':rankingonly,
|
|
}
|
|
)
|
|
histoform = HistoForm(initial={
|
|
'includereststrokes':False,
|
|
'histoparam':histoparam
|
|
})
|
|
|
|
negtypes = []
|
|
for b in mytypes.boattypes:
|
|
if b[0] not in waterboattype:
|
|
negtypes.append(b[0])
|
|
|
|
|
|
|
|
script = ''
|
|
div = get_call()
|
|
js_resources = ''
|
|
css_resources = ''
|
|
|
|
|
|
|
|
|
|
options = {
|
|
'modality': modality,
|
|
'theuser': theuser.id,
|
|
'waterboattype':waterboattype,
|
|
'startdatestring':startdatestring,
|
|
'enddatestring':enddatestring,
|
|
'rankingonly':rankingonly,
|
|
'includereststrokes':includereststrokes,
|
|
'histoparam':histoparam,
|
|
}
|
|
|
|
request.session['options'] = options
|
|
|
|
promember=0
|
|
mayedit=0
|
|
if not request.user.is_anonymous:
|
|
result = request.user.is_authenticated and ispromember(request.user)
|
|
if result:
|
|
promember = 1
|
|
|
|
|
|
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,
|
|
'rower':r,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'form':form,
|
|
'optionsform':modalityform,
|
|
'histoform':histoform,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
# The Flex plot for a large selection of workouts
|
|
@login_required()
|
|
def cum_flex_data(
|
|
request,
|
|
options={
|
|
'includereststrokes':False,
|
|
'rankingonly':False,
|
|
'modality':'all',
|
|
'waterboattype':mytypes.waterboattype,
|
|
'theuser':0,
|
|
'xparam':'spm',
|
|
'yparam1':'power',
|
|
'yparam2':'None',
|
|
'enddatestring':timezone.now().strftime("%Y-%m-%d"),
|
|
'startdatestring':(timezone.now()-datetime.timedelta(days=30)).strftime("%Y-%m-%d"),
|
|
'deltadays':-1,
|
|
}):
|
|
|
|
def_options = options
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
|
|
|
|
modality = keyvalue_get_default('modality',options,def_options)
|
|
rankingonly = keyvalue_get_default('rankingonly',options,def_options)
|
|
includereststrokes = keyvalue_get_default('includereststrokes',options,def_options)
|
|
waterboattype = keyvalue_get_default('waterboattype',options,def_options)
|
|
workstrokesonly = not includereststrokes
|
|
theuser = keyvalue_get_default('theuser',options,def_options)
|
|
xparam = keyvalue_get_default('xparam',options,def_options)
|
|
yparam1 = keyvalue_get_default('yparam1',options,def_options)
|
|
yparam2 = keyvalue_get_default('yparam2',options,def_options)
|
|
startdatestring = keyvalue_get_default('startdatestring',options,def_options)
|
|
enddatestring = keyvalue_get_default('enddatestring',options,def_options)
|
|
|
|
|
|
if modality == 'all':
|
|
modalities = [m[0] for m in mytypes.workouttypes]
|
|
else:
|
|
modalities = [modality]
|
|
|
|
try:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
except ParseError:
|
|
startdate = timezone.now()-datetime.timedelta(days=7)
|
|
|
|
try:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
except ParseError:
|
|
enddate = timezone.now()
|
|
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
promember=0
|
|
if theuser == 0:
|
|
theuser = request.user.id
|
|
|
|
if not request.user.is_anonymous:
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
|
|
r2 = getrower(theuser)
|
|
|
|
if rankingonly:
|
|
rankingpiece = [True,]
|
|
else:
|
|
rankingpiece = [True,False]
|
|
|
|
allworkouts = Workout.objects.filter(user=r2,
|
|
workouttype__in=modalities,
|
|
boattype__in=waterboattype,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate,
|
|
rankingpiece__in=rankingpiece)
|
|
|
|
if allworkouts:
|
|
res = interactive_cum_flex_chart2(allworkouts,xparam=xparam,
|
|
yparam1=yparam1,
|
|
yparam2=yparam2,
|
|
promember=promember,
|
|
workstrokesonly=workstrokesonly,
|
|
)
|
|
script = res[0]
|
|
div = res[1]
|
|
else:
|
|
script = ''
|
|
div = '<p>No pieces uploaded for this date range.</p>'
|
|
|
|
scripta = script.split('\n')[2:-1]
|
|
script = ''.join(scripta)
|
|
|
|
data = {
|
|
"script":script,
|
|
"div":div,
|
|
}
|
|
|
|
return JSONResponse(data)
|
|
|
|
# The Flex plot for a large selection of workouts
|
|
@login_required()
|
|
def histo_data(
|
|
request,
|
|
options={
|
|
'includereststrokes':False,
|
|
'rankingonly':False,
|
|
'modality':'all',
|
|
'waterboattype':mytypes.waterboattype,
|
|
'theuser':0,
|
|
'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
|
|
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
|
|
modality = keyvalue_get_default('modality',options,def_options)
|
|
rankingonly = keyvalue_get_default('rankingonly',options,def_options)
|
|
includereststrokes = keyvalue_get_default('includereststrokes',options,def_options)
|
|
waterboattype = keyvalue_get_default('waterboattype',options,def_options)
|
|
workstrokesonly = not includereststrokes
|
|
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]
|
|
else:
|
|
modalities = [modality]
|
|
|
|
try:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
except ParseError:
|
|
startdate = timezone.now()-datetime.timedelta(days=7)
|
|
|
|
try:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
except ParseError:
|
|
enddate = timezone.now()
|
|
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
promember=0
|
|
if theuser == 0:
|
|
theuser = request.user.id
|
|
|
|
if not request.user.is_anonymous:
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
|
|
r2 = getrower(theuser)
|
|
|
|
if rankingonly:
|
|
rankingpiece = [True,]
|
|
else:
|
|
rankingpiece = [True,False]
|
|
|
|
allworkouts = Workout.objects.filter(user=r2,
|
|
workouttype__in=modalities,
|
|
boattype__in=waterboattype,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate,
|
|
rankingpiece__in=rankingpiece)
|
|
|
|
if allworkouts:
|
|
res = interactive_histoall(allworkouts,histoparam,includereststrokes)
|
|
script = res[0]
|
|
div = res[1]
|
|
else:
|
|
script = ''
|
|
div = '<p>No pieces uploaded for this date range.</p>'
|
|
|
|
scripta = script.split('\n')[2:-1]
|
|
script = ''.join(scripta)
|
|
|
|
data = {
|
|
"script":script,
|
|
"div":div,
|
|
}
|
|
|
|
return JSONResponse(data)
|
|
|
|
|
|
|
|
|
|
@login_required()
|
|
def cum_flex(request,theuser=0,
|
|
xparam='spm',
|
|
yparam1='power',
|
|
yparam2='None',
|
|
startdate=timezone.now()-datetime.timedelta(days=10),
|
|
enddate=timezone.now(),
|
|
deltadays=-1,
|
|
enddatestring=timezone.now().strftime("%Y-%m-%d"),
|
|
startdatestring=(timezone.now()-datetime.timedelta(days=30)).strftime("%Y-%m-%d"),
|
|
options={
|
|
'includereststrokes':False,
|
|
'workouttypes':[i[0] for i in mytypes.workouttypes],
|
|
'waterboattype':mytypes.waterboattype,
|
|
'rankingonly':False,
|
|
}):
|
|
|
|
|
|
r = getrequestrower(request,userid=theuser)
|
|
theuser = r.user
|
|
|
|
if 'waterboattype' in request.session:
|
|
waterboattype = request.session['waterboattype']
|
|
else:
|
|
waterboattype = mytypes.waterboattype
|
|
|
|
|
|
if 'rankingonly' in request.session:
|
|
rankingonly = request.session['rankingonly']
|
|
else:
|
|
rankingonly = False
|
|
|
|
if 'modalities' in request.session:
|
|
modalities = request.session['modalities']
|
|
if len(modalities) > 1:
|
|
modality = 'all'
|
|
else:
|
|
modality = modalities[0]
|
|
else:
|
|
modalities = [m[0] for m in mytypes.workouttypes]
|
|
modality = 'all'
|
|
|
|
|
|
try:
|
|
rankingonly = options['rankingonly']
|
|
except KeyError:
|
|
rankingonly = False
|
|
|
|
try:
|
|
includereststrokes = options['includereststrokes']
|
|
except KeyError:
|
|
includereststrokes = False
|
|
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
waterboattype = mytypes.waterboattype
|
|
|
|
|
|
if startdatestring != "":
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
|
|
if enddatestring != "":
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
|
|
# get all indoor rows of in date range
|
|
|
|
# process form
|
|
if request.method == 'POST':
|
|
form = DateRangeForm(request.POST)
|
|
modalityform = TrendFlexModalForm(request.POST)
|
|
flexaxesform = FlexAxesForm(request,request.POST)
|
|
if form.is_valid():
|
|
startdate = form.cleaned_data['startdate']
|
|
enddate = form.cleaned_data['enddate']
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
|
if modalityform.is_valid():
|
|
modality = modalityform.cleaned_data['modality']
|
|
waterboattype = modalityform.cleaned_data['waterboattype']
|
|
rankingonly = modalityform.cleaned_data['rankingonly']
|
|
if modality == 'all':
|
|
modalities = [m[0] for m in mytypes.workouttypes]
|
|
else:
|
|
modalities = [modality]
|
|
|
|
if modality != 'water':
|
|
waterboattype = [b[0] for b in mytypes.boattypes]
|
|
|
|
|
|
request.session['modalities'] = modalities
|
|
request.session['waterboattype'] = waterboattype
|
|
request.session['rankingonly'] = rankingonly
|
|
form = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
if flexaxesform.is_valid():
|
|
xparam = flexaxesform.cleaned_data['xaxis']
|
|
yparam1 = flexaxesform.cleaned_data['yaxis1']
|
|
yparam2 = flexaxesform.cleaned_data['yaxis2']
|
|
else:
|
|
form = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
includereststrokes = False
|
|
|
|
workstrokesonly = not includereststrokes
|
|
modalityform = TrendFlexModalForm(
|
|
initial={
|
|
'modality':modality,
|
|
'waterboattype':waterboattype,
|
|
'rankingonly':rankingonly,
|
|
}
|
|
)
|
|
initial = {
|
|
'xaxis':xparam,
|
|
'yaxis1':yparam1,
|
|
'yaxis2':yparam2
|
|
}
|
|
flexaxesform = FlexAxesForm(request,initial=initial)
|
|
|
|
negtypes = []
|
|
for b in mytypes.boattypes:
|
|
if b[0] not in waterboattype:
|
|
negtypes.append(b[0])
|
|
|
|
|
|
|
|
script = ''
|
|
div = get_call()
|
|
js_resources = ''
|
|
css_resources = ''
|
|
|
|
|
|
|
|
|
|
options = {
|
|
'xparam': xparam,
|
|
'yparam1': yparam1,
|
|
'yparam2': yparam2,
|
|
'modality': modality,
|
|
'theuser': theuser.id,
|
|
'waterboattype':waterboattype,
|
|
'startdatestring':startdatestring,
|
|
'enddatestring':enddatestring,
|
|
'rankingonly':rankingonly,
|
|
'includereststrokes':includereststrokes,
|
|
}
|
|
|
|
request.session['options'] = options
|
|
|
|
promember=0
|
|
mayedit=0
|
|
if not request.user.is_anonymous:
|
|
result = request.user.is_authenticated and ispromember(request.user)
|
|
if result:
|
|
promember = 1
|
|
|
|
|
|
request.session['options'] = options
|
|
|
|
|
|
return render(request, 'cum_flex.html',
|
|
{'interactiveplot':script,
|
|
'the_div':div,
|
|
'js_res': js_resources,
|
|
'css_res':css_resources,
|
|
'id':theuser,
|
|
'rower':r,
|
|
'active':'nav-analysis',
|
|
'theuser':theuser,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'form':form,
|
|
'optionsform':modalityform,
|
|
'xparam':xparam,
|
|
'yparam1':yparam1,
|
|
'yparam2':yparam2,
|
|
'promember':promember,
|
|
'teams':get_my_teams(request.user),
|
|
'flexaxesform':flexaxesform,
|
|
})
|
|
|
|
|
|
def planrequired_view(request):
|
|
messages.info(request,"This functionality requires Coach or Self-Coach membership")
|
|
|
|
return HttpResponseRedirect(reverse('paidplans_view'))
|
|
|
|
@user_passes_test(isplanmember,login_url="/rowers/paidplans",
|
|
message="This functionality requires a Coach or Self-Coach plan",
|
|
redirect_field_name=None)
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def fitnessmetric_view(request,userid=0,mode='rower',
|
|
startdate=timezone.now()-timezone.timedelta(days=365),
|
|
enddate=timezone.now()):
|
|
|
|
|
|
therower = getrequestrower(request,userid=userid)
|
|
theuser = therower.user
|
|
|
|
|
|
if request.method == 'POST':
|
|
form = FitnessMetricForm(request.POST)
|
|
if form.is_valid():
|
|
startdate = form.cleaned_data['startdate']
|
|
enddate = form.cleaned_data['enddate']
|
|
mode = form.cleaned_data['mode']
|
|
else:
|
|
form = FitnessMetricForm()
|
|
|
|
fitnessmetrics = PowerTimeFitnessMetric.objects.filter(
|
|
user=theuser,
|
|
date__gte=startdate,
|
|
date__lte=enddate)
|
|
|
|
script,thediv = fitnessmetric_chart(
|
|
fitnessmetrics,theuser,
|
|
workoutmode=mode,startdate=startdate,
|
|
enddate=enddate,
|
|
)
|
|
|
|
breadcrumbs = [
|
|
{
|
|
'url':'/rowers/analysis',
|
|
'name':'Analysis'
|
|
},
|
|
{
|
|
'url':reverse('fitnessmetric_view'),
|
|
'name': 'Power Progress'
|
|
}
|
|
]
|
|
|
|
|
|
return render(request,'fitnessmetric.html',
|
|
{
|
|
'rower':therower,
|
|
'active':'nav-analysis',
|
|
'chartscript':script,
|
|
'breadcrumbs':breadcrumbs,
|
|
'the_div':thediv,
|
|
'mode':mode,
|
|
'form':form,
|
|
})
|
|
|
|
|
|
# Show ranking distances including predicted paces
|
|
@login_required()
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def rankings_view(request,userid=0,
|
|
startdate=timezone.now()-datetime.timedelta(days=365),
|
|
enddate=timezone.now(),
|
|
deltadays=-1,
|
|
startdatestring="",
|
|
enddatestring=""):
|
|
|
|
if deltadays>0:
|
|
startdate = enddate-datetime.timedelta(days=int(deltadays))
|
|
|
|
if startdatestring != "":
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
|
|
if enddatestring != "":
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
|
|
|
|
promember=0
|
|
r = getrequestrower(request,userid=userid)
|
|
theuser = r.user.id
|
|
|
|
if r.birthdate:
|
|
age = calculate_age(r.birthdate)
|
|
worldclasspower = int(c2stuff.getagegrouprecord(
|
|
age,
|
|
sex=r.sex,
|
|
weightcategory=r.weightcategory,
|
|
))
|
|
else:
|
|
worldclasspower = None
|
|
|
|
result = request.user.is_authenticated and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
|
|
# get all indoor rows in date range
|
|
|
|
# process form
|
|
if request.method == 'POST' and "daterange" in request.POST:
|
|
dateform = DateRangeForm(request.POST)
|
|
deltaform = DeltaDaysForm(request.POST)
|
|
if dateform.is_valid():
|
|
startdate = dateform.cleaned_data['startdate']
|
|
enddate = dateform.cleaned_data['enddate']
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
elif request.method == 'POST' and "datedelta" in request.POST:
|
|
deltaform = DeltaDaysForm(request.POST)
|
|
if deltaform.is_valid():
|
|
deltadays = deltaform.cleaned_data['deltadays']
|
|
if deltadays:
|
|
enddate = timezone.now()
|
|
startdate = enddate-datetime.timedelta(days=deltadays)
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
dateform = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
else:
|
|
dateform = DateRangeForm()
|
|
deltaform = DeltaDaysForm()
|
|
|
|
else:
|
|
dateform = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
deltaform = DeltaDaysForm()
|
|
|
|
# get all 2k (if any) - this rower, in date range
|
|
try:
|
|
r = getrower(theuser)
|
|
except Rower.DoesNotExist:
|
|
allergworkouts = []
|
|
r=0
|
|
|
|
|
|
try:
|
|
uu = User.objects.get(id=theuser)
|
|
except User.DoesNotExist:
|
|
uu = ''
|
|
|
|
|
|
# test to fix bug
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
#enddate = enddate+datetime.timedelta(days=1)
|
|
|
|
thedistances = []
|
|
theworkouts = []
|
|
thesecs = []
|
|
|
|
rankingdistances.sort()
|
|
rankingdurations.sort()
|
|
|
|
for rankingdistance in rankingdistances:
|
|
|
|
workouts = Workout.objects.filter(
|
|
user=r,distance=rankingdistance,
|
|
workouttype__in=['rower','dynamic','slides'],
|
|
rankingpiece=True,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate
|
|
).order_by('duration')
|
|
if workouts:
|
|
thedistances.append(rankingdistance)
|
|
theworkouts.append(workouts[0])
|
|
|
|
timesecs = 3600*workouts[0].duration.hour
|
|
timesecs += 60*workouts[0].duration.minute
|
|
timesecs += workouts[0].duration.second
|
|
timesecs += 1.e-6*workouts[0].duration.microsecond
|
|
|
|
thesecs.append(timesecs)
|
|
|
|
for rankingduration in rankingdurations:
|
|
|
|
workouts = Workout.objects.filter(
|
|
user=r,duration=rankingduration,
|
|
workouttype='rower',
|
|
rankingpiece=True,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate
|
|
).order_by('-distance')
|
|
if workouts:
|
|
thedistances.append(workouts[0].distance)
|
|
theworkouts.append(workouts[0])
|
|
|
|
timesecs = 3600*workouts[0].duration.hour
|
|
timesecs += 60*workouts[0].duration.minute
|
|
timesecs += workouts[0].duration.second
|
|
timesecs += 1.e-5*workouts[0].duration.microsecond
|
|
|
|
thesecs.append(timesecs)
|
|
|
|
thedistances = np.array(thedistances)
|
|
thesecs = np.array(thesecs)
|
|
|
|
thevelos = thedistances/thesecs
|
|
theavpower = 2.8*(thevelos**3)
|
|
|
|
|
|
# create interactive plot
|
|
if len(thedistances) !=0 :
|
|
res = interactive_cpchart(
|
|
r,thedistances,thesecs,theavpower,
|
|
theworkouts,promember=promember
|
|
)
|
|
script = res[0]
|
|
div = res[1]
|
|
paulslope = res[2]
|
|
paulintercept = res[3]
|
|
p1 = res[4]
|
|
message = res[5]
|
|
else:
|
|
script = ''
|
|
div = '<p>No ranking pieces found.</p>'
|
|
paulslope = 1
|
|
paulintercept = 1
|
|
p1 = [1,1,1,1]
|
|
message = ""
|
|
|
|
|
|
if request.method == 'POST' and "piece" in request.POST:
|
|
form = PredictedPieceForm(request.POST)
|
|
if form.is_valid():
|
|
value = form.cleaned_data['value']
|
|
hourvalue,value = divmod(value,60)
|
|
if hourvalue >= 24:
|
|
hourvalue = 23
|
|
pieceunit = form.cleaned_data['pieceunit']
|
|
if pieceunit == 'd':
|
|
rankingdistances.append(value)
|
|
else:
|
|
rankingdurations.append(datetime.time(minute=int(value),hour=int(hourvalue)))
|
|
else:
|
|
form = PredictedPieceForm()
|
|
|
|
rankingdistances.sort()
|
|
rankingdurations.sort()
|
|
|
|
|
|
predictions = []
|
|
cpredictions = []
|
|
|
|
|
|
for rankingdistance in rankingdistances:
|
|
# Paul's model
|
|
p = paulslope*np.log10(rankingdistance)+paulintercept
|
|
velo = 500./p
|
|
t = rankingdistance/velo
|
|
pwr = 2.8*(velo**3)
|
|
a = {'distance':rankingdistance,
|
|
'duration':timedeltaconv(t),
|
|
'pace':timedeltaconv(p),
|
|
'power':int(pwr)}
|
|
predictions.append(a)
|
|
|
|
# CP model -
|
|
pwr2 = p1[0]/(1+t/p1[2])
|
|
pwr2 += p1[1]/(1+t/p1[3])
|
|
|
|
if pwr2 <= 0:
|
|
pwr2 = 50.
|
|
|
|
velo2 = (pwr2/2.8)**(1./3.)
|
|
|
|
if np.isnan(velo2) or velo2 <= 0:
|
|
velo2 = 1.0
|
|
|
|
t2 = rankingdistance/velo2
|
|
|
|
pwr3 = p1[0]/(1+t2/p1[2])
|
|
pwr3 += p1[1]/(1+t2/p1[3])
|
|
|
|
if pwr3 <= 0:
|
|
pwr3 = 50.
|
|
|
|
velo3 = (pwr3/2.8)**(1./3.)
|
|
if np.isnan(velo3) or velo3 <= 0:
|
|
velo3 = 1.0
|
|
|
|
t3 = rankingdistance/velo3
|
|
p3 = 500./velo3
|
|
|
|
a = {'distance':rankingdistance,
|
|
'duration':timedeltaconv(t3),
|
|
'pace':timedeltaconv(p3),
|
|
'power':int(pwr3)}
|
|
cpredictions.append(a)
|
|
|
|
|
|
|
|
|
|
for rankingduration in rankingdurations:
|
|
t = 3600.*rankingduration.hour
|
|
t += 60.*rankingduration.minute
|
|
t += rankingduration.second
|
|
t += rankingduration.microsecond/1.e6
|
|
|
|
# Paul's model
|
|
ratio = paulintercept/paulslope
|
|
|
|
u = ((2**(2+ratio))*(5.**(3+ratio))*t*np.log(10))/paulslope
|
|
|
|
d = 500*t*np.log(10.)
|
|
d = d/(paulslope*lambertw(u))
|
|
d = d.real
|
|
|
|
velo = d/t
|
|
p = 500./velo
|
|
pwr = 2.8*(velo**3)
|
|
a = {'distance':int(d),
|
|
'duration':timedeltaconv(t),
|
|
'pace':timedeltaconv(p),
|
|
'power':int(pwr)}
|
|
predictions.append(a)
|
|
|
|
# CP model
|
|
pwr = p1[0]/(1+t/p1[2])
|
|
pwr += p1[1]/(1+t/p1[3])
|
|
|
|
if pwr <= 0:
|
|
pwr = 50.
|
|
|
|
velo = (pwr/2.8)**(1./3.)
|
|
|
|
if np.isnan(velo) or velo <=0:
|
|
velo = 1.0
|
|
|
|
d = t*velo
|
|
p = 500./velo
|
|
a = {'distance':int(d),
|
|
'duration':timedeltaconv(t),
|
|
'pace':timedeltaconv(p),
|
|
'power':int(pwr)}
|
|
cpredictions.append(a)
|
|
|
|
|
|
messages.error(request,message)
|
|
return render(request, 'rankings.html',
|
|
{'rankingworkouts':theworkouts,
|
|
'interactiveplot':script,
|
|
'the_div':div,
|
|
'predictions':predictions,
|
|
'cpredictions':cpredictions,
|
|
'nrdata':len(thedistances),
|
|
'form':form,
|
|
'rower':r,
|
|
'active':'nav-analysis',
|
|
'dateform':dateform,
|
|
'deltaform':deltaform,
|
|
'worldclasspower':worldclasspower,
|
|
'id': theuser,
|
|
'theuser':uu,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
@login_required()
|
|
def ajax_agegrouprecords(request,
|
|
age=25,
|
|
sex='female',
|
|
weightcategory='hwt',
|
|
userid=0):
|
|
|
|
wcdurations = []
|
|
wcpower = []
|
|
durations = [1,4,30,60]
|
|
distances = [100,500,1000,2000,5000,6000,10000,21097,42195]
|
|
|
|
df = pd.DataFrame(
|
|
list(
|
|
C2WorldClassAgePerformance.objects.filter(
|
|
sex=sex,
|
|
weightcategory=weightcategory
|
|
).values()
|
|
)
|
|
)
|
|
|
|
jsondf = df.to_json()
|
|
|
|
job = myqueue(queue,
|
|
handle_getagegrouprecords,
|
|
jsondf,distances,durations,age,sex,weightcategory,
|
|
)
|
|
|
|
|
|
|
|
return JSONResponse(
|
|
{
|
|
'job':job.id
|
|
}
|
|
)
|
|
|
|
|
|
# Show ranking distances including predicted paces
|
|
@login_required()
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def rankings_view2(request,userid=0,
|
|
startdate=timezone.now()-datetime.timedelta(days=365),
|
|
enddate=timezone.now(),
|
|
deltadays=-1,
|
|
startdatestring="",
|
|
enddatestring=""):
|
|
|
|
if deltadays>0:
|
|
startdate = enddate-datetime.timedelta(days=int(deltadays))
|
|
|
|
if startdatestring != "":
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
|
|
if enddatestring != "":
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
if userid == 0:
|
|
userid = request.user.id
|
|
else:
|
|
lastupdated = "01-01-1900"
|
|
|
|
|
|
promember=0
|
|
r = getrequestrower(request,userid=userid)
|
|
theuser = r.user
|
|
|
|
wcdurations = []
|
|
wcpower = []
|
|
|
|
lastupdated = "01-01-1900"
|
|
userid = 0
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
try:
|
|
wcdurations = options['wcdurations']
|
|
wcpower = options['wcpower']
|
|
lastupdated = options['lastupdated']
|
|
except KeyError:
|
|
pass
|
|
try:
|
|
userid = options['userid']
|
|
except KeyError:
|
|
userid = 0
|
|
else:
|
|
options = {}
|
|
|
|
|
|
|
|
lastupdatedtime = arrow.get(lastupdated).timestamp
|
|
current_time = arrow.utcnow().timestamp
|
|
|
|
deltatime_seconds = current_time - lastupdatedtime
|
|
recalc = False
|
|
if str(userid) != str(theuser) or deltatime_seconds > 3600:
|
|
recalc = True
|
|
options['lastupdated'] = arrow.utcnow().isoformat()
|
|
else:
|
|
recalc = False
|
|
|
|
options['userid'] = theuser.id
|
|
|
|
if r.birthdate:
|
|
age = calculate_age(r.birthdate)
|
|
else:
|
|
worldclasspower = None
|
|
age = 0
|
|
|
|
agerecords = CalcAgePerformance.objects.filter(
|
|
age = age,
|
|
sex = r.sex,
|
|
weightcategory = r.weightcategory)
|
|
|
|
if len(agerecords) == 0:
|
|
recalc = True
|
|
wcpower = []
|
|
wcduration = []
|
|
else:
|
|
wcdurations = []
|
|
wcpower = []
|
|
for record in agerecords:
|
|
wcdurations.append(record.duration)
|
|
wcpower.append(record.power)
|
|
|
|
options['wcpower'] = wcpower
|
|
options['wcdurations'] = wcdurations
|
|
if theuser:
|
|
options['userid'] = theuser.id
|
|
|
|
request.session['options'] = options
|
|
|
|
|
|
result = request.user.is_authenticated and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
|
|
# get all indoor rows in date range
|
|
|
|
# process form
|
|
if request.method == 'POST' and "daterange" in request.POST:
|
|
dateform = DateRangeForm(request.POST)
|
|
deltaform = DeltaDaysForm(request.POST)
|
|
if dateform.is_valid():
|
|
startdate = dateform.cleaned_data['startdate']
|
|
enddate = dateform.cleaned_data['enddate']
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
elif request.method == 'POST' and "datedelta" in request.POST:
|
|
deltaform = DeltaDaysForm(request.POST)
|
|
if deltaform.is_valid():
|
|
deltadays = deltaform.cleaned_data['deltadays']
|
|
if deltadays:
|
|
enddate = timezone.now()
|
|
startdate = enddate-datetime.timedelta(days=deltadays)
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
dateform = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
else:
|
|
dateform = DateRangeForm()
|
|
deltaform = DeltaDaysForm()
|
|
|
|
else:
|
|
dateform = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
deltaform = DeltaDaysForm()
|
|
|
|
# get all 2k (if any) - this rower, in date range
|
|
try:
|
|
r = getrower(theuser)
|
|
except Rower.DoesNotExist:
|
|
allergworkouts = []
|
|
r=0
|
|
|
|
|
|
uu = theuser
|
|
|
|
|
|
# test to fix bug
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
#enddate = enddate+datetime.timedelta(days=1)
|
|
|
|
|
|
thedistances = []
|
|
theworkouts = []
|
|
thesecs = []
|
|
|
|
|
|
|
|
rankingdistances.sort()
|
|
rankingdurations.sort()
|
|
|
|
for rankingdistance in rankingdistances:
|
|
|
|
workouts = Workout.objects.filter(
|
|
user=r,distance=rankingdistance,
|
|
workouttype__in=['rower','dynamic','slides'],
|
|
rankingpiece=True,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate).order_by('duration')
|
|
if workouts:
|
|
thedistances.append(rankingdistance)
|
|
theworkouts.append(workouts[0])
|
|
|
|
timesecs = 3600*workouts[0].duration.hour
|
|
timesecs += 60*workouts[0].duration.minute
|
|
timesecs += workouts[0].duration.second
|
|
timesecs += 1.e-6*workouts[0].duration.microsecond
|
|
|
|
thesecs.append(timesecs)
|
|
|
|
for rankingduration in rankingdurations:
|
|
|
|
workouts = Workout.objects.filter(
|
|
user=r,duration=rankingduration,
|
|
workouttype='rower',
|
|
rankingpiece=True,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate).order_by('-distance')
|
|
if workouts:
|
|
thedistances.append(workouts[0].distance)
|
|
theworkouts.append(workouts[0])
|
|
|
|
timesecs = 3600*workouts[0].duration.hour
|
|
timesecs += 60*workouts[0].duration.minute
|
|
timesecs += workouts[0].duration.second
|
|
timesecs += 1.e-5*workouts[0].duration.microsecond
|
|
|
|
thesecs.append(timesecs)
|
|
|
|
thedistances = np.array(thedistances)
|
|
thesecs = np.array(thesecs)
|
|
|
|
thevelos = thedistances/thesecs
|
|
theavpower = 2.8*(thevelos**3)
|
|
|
|
|
|
# create interactive plot
|
|
if len(thedistances) !=0 :
|
|
res = interactive_cpchart(
|
|
r,thedistances,thesecs,theavpower,
|
|
theworkouts,promember=promember,
|
|
wcdurations=wcdurations,wcpower=wcpower
|
|
)
|
|
script = res[0]
|
|
div = res[1]
|
|
paulslope = res[2]
|
|
paulintercept = res[3]
|
|
p1 = res[4]
|
|
message = res[5]
|
|
try:
|
|
testcalc = pd.Series(res[6])*3
|
|
except TypeError:
|
|
age = 0
|
|
|
|
else:
|
|
script = ''
|
|
div = '<p>No ranking pieces found.</p>'
|
|
paulslope = 1
|
|
paulintercept = 1
|
|
p1 = [1,1,1,1]
|
|
message = ""
|
|
|
|
|
|
if request.method == 'POST' and "piece" in request.POST:
|
|
form = PredictedPieceForm(request.POST)
|
|
if form.is_valid():
|
|
value = form.cleaned_data['value']
|
|
hourvalue,value = divmod(value,60)
|
|
if hourvalue >= 24:
|
|
hourvalue = 23
|
|
pieceunit = form.cleaned_data['pieceunit']
|
|
if pieceunit == 'd':
|
|
rankingdistances.append(value)
|
|
else:
|
|
rankingdurations.append(datetime.time(minute=int(value),hour=int(hourvalue)))
|
|
else:
|
|
form = PredictedPieceForm()
|
|
|
|
rankingdistances.sort()
|
|
rankingdurations.sort()
|
|
|
|
|
|
predictions = []
|
|
cpredictions = []
|
|
|
|
|
|
for rankingdistance in rankingdistances:
|
|
# Paul's model
|
|
p = paulslope*np.log10(rankingdistance)+paulintercept
|
|
velo = 500./p
|
|
t = rankingdistance/velo
|
|
pwr = 2.8*(velo**3)
|
|
a = {'distance':rankingdistance,
|
|
'duration':timedeltaconv(t),
|
|
'pace':timedeltaconv(p),
|
|
'power':int(pwr)}
|
|
predictions.append(a)
|
|
|
|
# CP model -
|
|
pwr2 = p1[0]/(1+t/p1[2])
|
|
pwr2 += p1[1]/(1+t/p1[3])
|
|
|
|
if pwr2 <= 0:
|
|
pwr2 = 50.
|
|
|
|
velo2 = (pwr2/2.8)**(1./3.)
|
|
|
|
if np.isnan(velo2) or velo2 <= 0:
|
|
velo2 = 1.0
|
|
|
|
t2 = rankingdistance/velo2
|
|
|
|
pwr3 = p1[0]/(1+t2/p1[2])
|
|
pwr3 += p1[1]/(1+t2/p1[3])
|
|
|
|
if pwr3 <= 0:
|
|
pwr3 = 50.
|
|
|
|
velo3 = (pwr3/2.8)**(1./3.)
|
|
if np.isnan(velo3) or velo3 <= 0:
|
|
velo3 = 1.0
|
|
|
|
t3 = rankingdistance/velo3
|
|
p3 = 500./velo3
|
|
|
|
a = {'distance':rankingdistance,
|
|
'duration':timedeltaconv(t3),
|
|
'pace':timedeltaconv(p3),
|
|
'power':int(pwr3)}
|
|
cpredictions.append(a)
|
|
|
|
|
|
|
|
|
|
for rankingduration in rankingdurations:
|
|
t = 3600.*rankingduration.hour
|
|
t += 60.*rankingduration.minute
|
|
t += rankingduration.second
|
|
t += rankingduration.microsecond/1.e6
|
|
|
|
# Paul's model
|
|
ratio = paulintercept/paulslope
|
|
|
|
u = ((2**(2+ratio))*(5.**(3+ratio))*t*np.log(10))/paulslope
|
|
|
|
d = 500*t*np.log(10.)
|
|
d = d/(paulslope*lambertw(u))
|
|
d = d.real
|
|
|
|
velo = d/t
|
|
p = 500./velo
|
|
pwr = 2.8*(velo**3)
|
|
try:
|
|
a = {'distance':int(d),
|
|
'duration':timedeltaconv(t),
|
|
'pace':timedeltaconv(p),
|
|
'power':int(pwr)}
|
|
predictions.append(a)
|
|
except:
|
|
pass
|
|
|
|
# CP model
|
|
pwr = p1[0]/(1+t/p1[2])
|
|
pwr += p1[1]/(1+t/p1[3])
|
|
|
|
if pwr <= 0:
|
|
pwr = 50.
|
|
|
|
velo = (pwr/2.8)**(1./3.)
|
|
|
|
if np.isnan(velo) or velo <=0:
|
|
velo = 1.0
|
|
|
|
d = t*velo
|
|
p = 500./velo
|
|
a = {'distance':int(d),
|
|
'duration':timedeltaconv(t),
|
|
'pace':timedeltaconv(p),
|
|
'power':int(pwr)}
|
|
cpredictions.append(a)
|
|
|
|
if recalc:
|
|
wcdurations = []
|
|
wcpower = []
|
|
durations = [1,4,30,60]
|
|
distances = [100,500,1000,2000,5000,6000,10000,21097,42195]
|
|
|
|
df = pd.DataFrame(
|
|
list(
|
|
C2WorldClassAgePerformance.objects.filter(
|
|
sex=r.sex,
|
|
weightcategory=r.weightcategory
|
|
).values()
|
|
)
|
|
)
|
|
|
|
jsondf = df.to_json()
|
|
|
|
job = myqueue(queue,
|
|
handle_getagegrouprecords,
|
|
jsondf,distances,durations,age,r.sex,r.weightcategory)
|
|
try:
|
|
request.session['async_tasks'] += [(job.id,'agegrouprecords')]
|
|
except KeyError:
|
|
request.session['async_tasks'] = [(job.id,'agegrouprecords')]
|
|
|
|
|
|
|
|
messages.error(request,message)
|
|
return render(request, 'rankings.html',
|
|
{'rankingworkouts':theworkouts,
|
|
'interactiveplot':script,
|
|
'the_div':div,
|
|
'predictions':predictions,
|
|
'cpredictions':cpredictions,
|
|
'nrdata':len(thedistances),
|
|
'form':form,
|
|
'dateform':dateform,
|
|
'deltaform':deltaform,
|
|
'id': theuser,
|
|
'theuser':uu,
|
|
'rower':r,
|
|
'active':'nav-analysis',
|
|
'age':age,
|
|
'sex':r.sex,
|
|
'recalc':recalc,
|
|
'weightcategory':r.weightcategory,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
|
|
# Show ranking distances including predicted paces
|
|
@user_passes_test(ispromember,login_url="/rowers/paidplans",
|
|
message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
|
|
redirect_field_name=None)
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def otwrankings_view(request,userid=0,
|
|
startdate=timezone.now()-datetime.timedelta(days=365),
|
|
enddate=timezone.now(),
|
|
startdatestring="",
|
|
enddatestring=""):
|
|
|
|
if startdatestring != "":
|
|
try:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
except ParseError:
|
|
pass
|
|
|
|
if enddatestring != "":
|
|
try:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
except ParseError:
|
|
pass
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
r = getrequestrower(request,userid=userid)
|
|
promember = True
|
|
|
|
theuser = r.user
|
|
|
|
# get all OTW rows in date range
|
|
|
|
# process form
|
|
if request.method == 'POST':
|
|
dateform = DateRangeForm(request.POST)
|
|
if dateform.is_valid():
|
|
startdate = dateform.cleaned_data['startdate']
|
|
enddate = dateform.cleaned_data['enddate']
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
form = PredictedPieceFormNoDistance(request.POST)
|
|
if form.is_valid():
|
|
value = form.cleaned_data['value']
|
|
else:
|
|
value = None
|
|
trankingdurations = form.cleaned_data['trankingdurations']
|
|
trankingdurations = [
|
|
datetime.datetime.strptime(d,"%H:%M:%S").time() for d in trankingdurations
|
|
]
|
|
if value:
|
|
hourvalue,tvalue = divmod(value,60)
|
|
hourvalue = int(hourvalue)
|
|
minutevalue = int(tvalue)
|
|
tvalue = int(60*(tvalue-minutevalue))
|
|
if hourvalue >= 24:
|
|
hourvalue = 23
|
|
trankingdurations.append(datetime.time(
|
|
minute=minutevalue,
|
|
hour=hourvalue,
|
|
second=tvalue
|
|
))
|
|
|
|
else:
|
|
form = PredictedPieceFormNoDistance()
|
|
dateform = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
workouttypes = ['rower','slides','dynamic']
|
|
trankingdurations = rankingdurations
|
|
|
|
# get all 2k (if any) - this rower, in date range
|
|
try:
|
|
r = Rower.objects.get(user=theuser)
|
|
request.session['rowerid'] = r.id
|
|
except Rower.DoesNotExist:
|
|
raise Http404("Rower doesn't exist")
|
|
|
|
|
|
|
|
uu = theuser
|
|
|
|
|
|
# test to fix bug
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
#enddate = enddate+datetime.timedelta(days=1)
|
|
|
|
|
|
|
|
thedistances = []
|
|
theworkouts = []
|
|
thesecs = []
|
|
|
|
theworkouts = Workout.objects.filter(
|
|
user=r,rankingpiece=True,
|
|
workouttype='water',
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate
|
|
).order_by(
|
|
"-startdatetime"
|
|
)
|
|
|
|
|
|
delta,cpvalue,avgpower = dataprep.fetchcp(r,theworkouts)
|
|
|
|
runningjob = 0
|
|
|
|
taskstatus = get_stored_tasks_status(request)
|
|
for task in taskstatus:
|
|
if task['func_name'] == 'updatecpwater':
|
|
if 'success' in task['status'].lower() or 'finished' in task['status'].lower():
|
|
runningjob = 1
|
|
messages.info(request,'CP chart data have been updated')
|
|
remove_asynctask(request,task['id'])
|
|
elif 'fail' in task['status'].lower():
|
|
runningjob = 0
|
|
try:
|
|
remove_asynctask(request,task[id])
|
|
messages.error(request,'Oh, your task failed')
|
|
except KeyError:
|
|
pass
|
|
elif 'started' in task['status'].lower():
|
|
messages.info(request,'Busy updating CP chart data')
|
|
runningjob = 1
|
|
elif 'queued' in task['status'].lower() or 'pending' in task['status'].lower():
|
|
messages.info(request,'Getting ready to update CP chart data')
|
|
runningjob = 1
|
|
|
|
|
|
|
|
if not runningjob:
|
|
job = dataprep.runcpupdate(
|
|
r,type='water',
|
|
startdate=startdate,
|
|
enddate=enddate
|
|
)
|
|
request.session['job_id'] = job.id
|
|
try:
|
|
request.session['async_tasks'] += [(job.id,'updatecpwater')]
|
|
except KeyError:
|
|
request.session['async_tasks'] = [(job.id,'updatecpwater')]
|
|
messages.info(request,'New calculation queued. Page will reload automatically. You can check the status of your calculations <a href="/rowers/jobs-status/" target="_blank">here</a>')
|
|
|
|
powerdf = pd.DataFrame({
|
|
'Delta':delta,
|
|
'CP':cpvalue,
|
|
})
|
|
|
|
if powerdf.empty:
|
|
messages.info(request,'Your calculations are running in the background. Page will reload automatically. You can check the status of your calculations <a href="/rowers/jobs-status/" target="_blank">here</a>')
|
|
|
|
powerdf = powerdf[powerdf['CP']>0]
|
|
powerdf.dropna(axis=0,inplace=True)
|
|
powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True)
|
|
powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True)
|
|
|
|
|
|
rowername = r.user.first_name+" "+r.user.last_name
|
|
# create interactive plot
|
|
if len(powerdf) !=0 :
|
|
res = interactive_otwcpchart(powerdf,promember=promember,rowername=rowername)
|
|
script = res[0]
|
|
div = res[1]
|
|
p1 = res[2]
|
|
ratio = res[3]
|
|
r.p0 = p1[0]
|
|
r.p1 = p1[1]
|
|
r.p2 = p1[2]
|
|
r.p3 = p1[3]
|
|
r.cpratio = ratio
|
|
r.save()
|
|
paulslope = 1
|
|
paulintercept = 1
|
|
message = res[4]
|
|
else:
|
|
script = ''
|
|
div = '<p>No ranking pieces found.</p>'
|
|
paulslope = 1
|
|
paulintercept = 1
|
|
p1 = [1,1,1,1]
|
|
message = ""
|
|
|
|
|
|
|
|
|
|
cpredictions = []
|
|
|
|
for rankingduration in trankingdurations:
|
|
t = 3600.*rankingduration.hour
|
|
t += 60.*rankingduration.minute
|
|
t += rankingduration.second
|
|
t += rankingduration.microsecond/1.e6
|
|
|
|
|
|
# CP model
|
|
pwr = p1[0]/(1+t/p1[2])
|
|
pwr += p1[1]/(1+t/p1[3])
|
|
|
|
|
|
if pwr <= 0:
|
|
pwr = 50.
|
|
|
|
|
|
if not np.isnan(pwr):
|
|
try:
|
|
pwr2 = pwr*ratio
|
|
except:
|
|
pwr2 = pwr
|
|
|
|
a = {
|
|
'duration':timedeltaconv(t),
|
|
'power':int(pwr),
|
|
'upper':int(pwr2)}
|
|
cpredictions.append(a)
|
|
|
|
|
|
|
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
|
request.session['startdate'] = startdatestring
|
|
request.session['enddate'] = enddatestring
|
|
|
|
messages.error(request,message)
|
|
return render(request, 'otwrankings.html',
|
|
{'rankingworkouts':theworkouts,
|
|
'interactiveplot':script,
|
|
'the_div':div,
|
|
'cpredictions':cpredictions,
|
|
'rower':r,
|
|
'active':'nav-analysis',
|
|
'avgpower':avgpower,
|
|
'form':form,
|
|
'dateform':dateform,
|
|
'id': theuser,
|
|
'theuser':uu,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'teams':get_my_teams(request.user),
|
|
'workouttype':'water',
|
|
})
|
|
|
|
@login_required()
|
|
def otecp_toadmin_view(request,theuser=0,
|
|
startdate=timezone.now()-datetime.timedelta(days=365),
|
|
enddate=timezone.now(),
|
|
startdatestring="",
|
|
enddatestring="",
|
|
):
|
|
|
|
if startdatestring != "":
|
|
try:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
except ParseError:
|
|
pass
|
|
|
|
if enddatestring != "":
|
|
try:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
except ParseError:
|
|
pass
|
|
|
|
if theuser == 0:
|
|
theuser = request.user.id
|
|
|
|
u = User.objects.get(id=theuser)
|
|
r = Rower.objects.get(user=u)
|
|
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
|
|
theworkouts = Workout.objects.filter(
|
|
user=r,rankingpiece=True,
|
|
workouttype__in=[
|
|
'rower',
|
|
'dynamic',
|
|
'slides'
|
|
],
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate
|
|
).order_by("-startdatetime")
|
|
|
|
|
|
delta,cpvalue,avgpower = dataprep.fetchcp(
|
|
r,theworkouts,table='cpergdata'
|
|
)
|
|
|
|
powerdf = pd.DataFrame({
|
|
'Delta':delta,
|
|
'CP':cpvalue,
|
|
})
|
|
|
|
csvfilename = 'CP_data_user_{id}.csv'.format(
|
|
id = theuser
|
|
)
|
|
|
|
powerdf = powerdf[powerdf['CP']>0]
|
|
powerdf.dropna(axis=0,inplace=True)
|
|
powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True)
|
|
powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True)
|
|
powerdf.to_csv(csvfilename)
|
|
|
|
res = myqueue(queuehigh,
|
|
handle_sendemailfile,
|
|
'Sander',
|
|
'Roosendaal',
|
|
'roosendaalsander@gmail.com',
|
|
csvfilename,
|
|
delete=True)
|
|
|
|
successmessage = "The CSV file was sent to the site admin per email"
|
|
messages.info(request,successmessage)
|
|
response = HttpResponseRedirect('/rowers/list-workouts/')
|
|
|
|
return response
|
|
|
|
@login_required()
|
|
def otwcp_toadmin_view(request,theuser=0,
|
|
startdate=timezone.now()-datetime.timedelta(days=365),
|
|
enddate=timezone.now(),
|
|
startdatestring="",
|
|
enddatestring="",
|
|
):
|
|
|
|
if startdatestring != "":
|
|
try:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
except ParseError:
|
|
pass
|
|
|
|
if enddatestring != "":
|
|
try:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
except ParseError:
|
|
pass
|
|
|
|
if theuser == 0:
|
|
theuser = request.user.id
|
|
|
|
u = User.objects.get(id=theuser)
|
|
r = Rower.objects.get(user=u)
|
|
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
|
|
theworkouts = Workout.objects.filter(
|
|
user=r,rankingpiece=True,
|
|
workouttype='water',
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate
|
|
).order_by("-startdatetime")
|
|
|
|
|
|
delta,cpvalue,avgpower = dataprep.fetchcp(
|
|
r,theworkouts,table='cpdata'
|
|
)
|
|
|
|
powerdf = pd.DataFrame({
|
|
'Delta':delta,
|
|
'CP':cpvalue,
|
|
})
|
|
|
|
csvfilename = 'CP_data_user_{id}.csv'.format(
|
|
id = theuser
|
|
)
|
|
|
|
powerdf = powerdf[powerdf['CP']>0]
|
|
powerdf.dropna(axis=0,inplace=True)
|
|
powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True)
|
|
powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True)
|
|
powerdf.to_csv(csvfilename)
|
|
|
|
res = myqueue(queuehigh,
|
|
handle_sendemailfile,
|
|
'Sander',
|
|
'Roosendaal',
|
|
'roosendaalsander@gmail.com',
|
|
csvfilename,
|
|
delete=True)
|
|
|
|
successmessage = "The CSV file was sent to the site admin per email"
|
|
messages.info(request,successmessage)
|
|
response = HttpResponseRedirect('/rowers/list-workouts/')
|
|
|
|
return response
|
|
|
|
# Show ranking distances including predicted paces
|
|
@user_passes_test(ispromember,login_url="/rowers/paidplans",
|
|
message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
|
|
redirect_field_name=None)
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def oterankings_view(request,userid=0,
|
|
startdate=timezone.now()-datetime.timedelta(days=365),
|
|
enddate=timezone.now(),
|
|
startdatestring="",
|
|
enddatestring=""):
|
|
|
|
if startdatestring != "":
|
|
try:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
except ParseError:
|
|
pass
|
|
|
|
if enddatestring != "":
|
|
try:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
except ParseError:
|
|
pass
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
r = getrequestrower(request,userid=userid)
|
|
theuser = r.user
|
|
|
|
|
|
promember=0
|
|
|
|
# get all OTW rows in date range
|
|
|
|
# process form
|
|
if request.method == 'POST':
|
|
dateform = DateRangeForm(request.POST)
|
|
if dateform.is_valid():
|
|
startdate = dateform.cleaned_data['startdate']
|
|
enddate = dateform.cleaned_data['enddate']
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
workouttypeform = OteWorkoutTypeForm(request.POST)
|
|
if workouttypeform.is_valid():
|
|
workouttypes = workouttypeform.cleaned_data['workouttypes']
|
|
form = PredictedPieceForm(request.POST)
|
|
if form.is_valid():
|
|
value = form.cleaned_data['value']
|
|
pieceunit = form.cleaned_data['pieceunit']
|
|
else:
|
|
value = None
|
|
|
|
try:
|
|
trankingdistances = form.cleaned_data['trankingdistances']
|
|
except KeyError:
|
|
trankingdistances = []
|
|
|
|
trankingdistances = [int(d) for d in trankingdistances]
|
|
|
|
try:
|
|
trankingdurations = form.cleaned_data['trankingdurations']
|
|
except KeyError:
|
|
trankingdurations = []
|
|
|
|
trankingdurations = [
|
|
datetime.datetime.strptime(d,"%H:%M:%S").time() for d in trankingdurations
|
|
]
|
|
if value:
|
|
hourvalue,tvalue = divmod(value,60)
|
|
hourvalue = int(hourvalue)
|
|
minutevalue = int(tvalue)
|
|
tvalue = int(60*(tvalue-minutevalue))
|
|
if hourvalue >= 24:
|
|
hourvalue = 23
|
|
if pieceunit == 'd':
|
|
trankingdistances.append(value)
|
|
else:
|
|
trankingdurations.append(datetime.time(
|
|
minute=minutevalue,
|
|
hour=hourvalue,
|
|
second=tvalue
|
|
))
|
|
else:
|
|
form = PredictedPieceForm()
|
|
dateform = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
workouttypeform = OteWorkoutTypeForm()
|
|
workouttypes = ['rower','slides','dynamic']
|
|
trankingdistances = rankingdistances
|
|
trankingdurations = rankingdurations
|
|
|
|
# get all 2k (if any) - this rower, in date range
|
|
try:
|
|
r = Rower.objects.get(user=theuser)
|
|
request.session['rowerid'] = r.id
|
|
except Rower.DoesNotExist:
|
|
allergworkouts = []
|
|
raise Http404("Rower doesn't exist")
|
|
|
|
|
|
uu = theuser
|
|
|
|
|
|
|
|
# test to fix bug
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
|
|
|
|
|
|
thedistances = []
|
|
theworkouts = []
|
|
thesecs = []
|
|
|
|
theworkouts = Workout.objects.filter(
|
|
user=r,rankingpiece=True,
|
|
workouttype__in=workouttypes,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate
|
|
).order_by("-startdatetime")
|
|
|
|
|
|
delta,cpvalue,avgpower = dataprep.fetchcp(
|
|
r,theworkouts,table='cpergdata'
|
|
)
|
|
|
|
runningjob = 0
|
|
|
|
taskstatus = get_stored_tasks_status(request)
|
|
for task in taskstatus:
|
|
if task['func_name'] == 'updatecp':
|
|
if 'success' in task['status'].lower() or 'finished' in task['status'].lower():
|
|
runningjob = 1
|
|
messages.info(request,'CP chart data have been updated')
|
|
remove_asynctask(request,task['id'])
|
|
elif 'fail' in task['status'].lower():
|
|
runningjob = 0
|
|
try:
|
|
remove_asynctask(request,task[id])
|
|
messages.error(request,'Oh, your task failed')
|
|
except KeyError:
|
|
pass
|
|
elif 'started' in task['status'].lower():
|
|
messages.info(request,'Busy updating CP chart data')
|
|
runningjob = 1
|
|
elif 'queued' in task['status'].lower():
|
|
messages.info(request,'Getting ready to update CP chart data')
|
|
runningjob = 1
|
|
|
|
|
|
if not runningjob:
|
|
job = dataprep.runcpupdate(
|
|
r,type='rower',
|
|
startdate=startdate,
|
|
enddate=enddate
|
|
)
|
|
request.session['job_id'] = job.id
|
|
try:
|
|
request.session['async_tasks'] += [(job.id,'updatecp')]
|
|
except KeyError:
|
|
request.session['async_tasks'] = [(job.id,'updatecp')]
|
|
messages.info(request,'New calculation queued. Page will reload automatically. You can check the status of your calculations <a href="/rowers/jobs-status/" target="_blank">here</a>')
|
|
|
|
powerdf = pd.DataFrame({
|
|
'Delta':delta,
|
|
'CP':cpvalue,
|
|
})
|
|
|
|
if powerdf.empty:
|
|
messages.info(request,'Your calculations are running in the background. Page will reload automatically. You can check the status of your calculations <a href="/rowers/jobs-status/" target="_blank">here</a>')
|
|
|
|
powerdf = powerdf[powerdf['CP']>0]
|
|
powerdf.dropna(axis=0,inplace=True)
|
|
powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True)
|
|
powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True)
|
|
|
|
rowername = r.user.first_name+" "+r.user.last_name
|
|
# create interactive plot
|
|
if len(powerdf) !=0 :
|
|
res = interactive_otwcpchart(powerdf,promember=promember,rowername=rowername)
|
|
script = res[0]
|
|
div = res[1]
|
|
p1 = res[2]
|
|
ratio = res[3]
|
|
r.ep0 = p1[0]
|
|
r.ep1 = p1[1]
|
|
r.ep2 = p1[2]
|
|
r.ep3 = p1[3]
|
|
r.ecpratio = ratio
|
|
r.save()
|
|
paulslope = 1
|
|
paulintercept = 1
|
|
message = res[4]
|
|
else:
|
|
ratio = 1
|
|
script = ''
|
|
div = '<p>No ranking pieces found.</p>'
|
|
paulslope = 1
|
|
paulintercept = 1
|
|
p1 = [1,1,1,1]
|
|
message = ""
|
|
|
|
|
|
|
|
|
|
|
|
cpredictions = []
|
|
|
|
|
|
|
|
for rankingduration in trankingdurations:
|
|
t = 3600.*rankingduration.hour
|
|
t += 60.*rankingduration.minute
|
|
t += rankingduration.second
|
|
t += rankingduration.microsecond/1.e6
|
|
|
|
|
|
# CP model
|
|
pwr = p1[0]/(1+t/p1[2])
|
|
pwr += p1[1]/(1+t/p1[3])
|
|
|
|
velo = (pwr/2.8)**(1./3.)
|
|
p = 500./velo
|
|
d = t*velo
|
|
|
|
if pwr <= 0:
|
|
pwr = 50.
|
|
|
|
|
|
if not np.isnan(pwr):
|
|
try:
|
|
pwr2 = pwr*ratio
|
|
except:
|
|
pwr2 = pwr
|
|
|
|
a = collections.OrderedDict()
|
|
a['duration'] = timedeltaconv(t)
|
|
a['distance'] = int(d)
|
|
a['pace'] = timedeltaconv(p)
|
|
a['power'] = int(pwr)
|
|
a['upper'] = int(pwr2)
|
|
|
|
cpredictions.append(a)
|
|
|
|
|
|
# initiation - get 10 min power, then use Paul's law
|
|
|
|
t_10 = 600.
|
|
power_10 = p1[0]/(1+t_10/p1[2])
|
|
power_10 += p1[1]/(1+t_10/p1[3])
|
|
|
|
velo_10 = (power_10/2.8)**(1./3.)
|
|
pace_10 = 500./velo_10
|
|
distance_10 = t_10*velo_10
|
|
|
|
paulslope = 5.
|
|
|
|
for rankingdistance in trankingdistances:
|
|
|
|
delta = paulslope * np.log(rankingdistance/distance_10)/np.log(2)
|
|
|
|
|
|
p = pace_10+delta
|
|
velo = 500./p
|
|
t = rankingdistance/velo
|
|
|
|
pwr2 = p1[0]/(1+t/p1[2])
|
|
pwr2 += p1[1]/(1+t/p1[3])
|
|
try:
|
|
pwr2 *= ratio
|
|
except UnboundLocalError:
|
|
pass
|
|
|
|
if pwr2 <= 0:
|
|
pwr2 = 50.
|
|
|
|
velo2 = (pwr2/2.8)**(1./3.)
|
|
|
|
if np.isnan(velo2) or velo2 <= 0:
|
|
velo2 = 1.0
|
|
|
|
t2 = rankingdistance/velo2
|
|
|
|
pwr3 = p1[0]/(1+t2/p1[2])
|
|
pwr3 += p1[1]/(1+t2/p1[3])
|
|
pwr3 *= ratio
|
|
|
|
|
|
if pwr3 <= 0:
|
|
pwr3 = 50.
|
|
|
|
velo3 = (pwr3/2.8)**(1./3.)
|
|
if np.isnan(velo3) or velo3 <= 0:
|
|
velo3 = 1.0
|
|
|
|
t3 = rankingdistance/velo3
|
|
p3 = 500./velo3
|
|
|
|
a = {
|
|
'distance':rankingdistance,
|
|
'duration':timedeltaconv(t3),
|
|
'power':'--',
|
|
'upper':int(pwr3),
|
|
'pace':timedeltaconv(p3)}
|
|
|
|
cpredictions.append(a)
|
|
|
|
# del form.fields["pieceunit"]
|
|
|
|
|
|
messages.error(request,message)
|
|
return render(request, 'oterankings.html',
|
|
{'rankingworkouts':theworkouts,
|
|
'interactiveplot':script,
|
|
'the_div':div,
|
|
'rower':r,
|
|
'active':'nav-analysis',
|
|
'cpredictions':cpredictions,
|
|
'avgpower':avgpower,
|
|
'form':form,
|
|
'dateform':dateform,
|
|
'workouttypeform':workouttypeform,
|
|
'id': theuser,
|
|
'theuser':uu,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'teams':get_my_teams(request.user),
|
|
'workouttype':'rower',
|
|
})
|
|
|
|
|
|
|
|
# Multi Flex Chart with Grouping
|
|
@user_passes_test(ispromember,login_url="/rowers/paidplans",
|
|
message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
|
|
redirect_field_name=None)
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def user_multiflex_select(request,
|
|
startdatestring="",
|
|
enddatestring="",
|
|
message='',
|
|
successmessage='',
|
|
startdate=timezone.now()-datetime.timedelta(days=30),
|
|
enddate=timezone.now(),
|
|
userid=0):
|
|
|
|
r = getrequestrower(request,userid=userid)
|
|
user = r.user
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
else:
|
|
options = {}
|
|
|
|
try:
|
|
palette = request.session['palette']
|
|
except KeyError:
|
|
palette = 'monochrome_blue'
|
|
|
|
try:
|
|
includereststrokes = request.session['includereststrokes']
|
|
except KeyError:
|
|
includereststrokes = False
|
|
|
|
try:
|
|
ploterrorbars = request.session['ploterrorbars']
|
|
except:
|
|
ploterrorbars = False
|
|
|
|
if 'startdate' in request.session:
|
|
startdate = iso8601.parse_date(request.session['startdate'])
|
|
|
|
|
|
if 'enddate' in request.session:
|
|
enddate = iso8601.parse_date(request.session['enddate'])
|
|
|
|
try:
|
|
waterboattype = request.session['waterboattype']
|
|
except KeyError:
|
|
waterboattype = mytypes.waterboattype
|
|
else:
|
|
waterboattype = mytypes.waterboattype
|
|
|
|
if 'rankingonly' in request.session:
|
|
rankingonly = request.session['rankingonly']
|
|
else:
|
|
rankingonly = False
|
|
|
|
|
|
if 'modalities' in request.session:
|
|
modalities = request.session['modalities']
|
|
if len(modalities) > 1:
|
|
modality = 'all'
|
|
else:
|
|
modality = modalities[0]
|
|
else:
|
|
modalities = [m[0] for m in mytypes.workouttypes]
|
|
modality = 'all'
|
|
|
|
if request.method == 'POST':
|
|
dateform = DateRangeForm(request.POST)
|
|
if dateform.is_valid():
|
|
startdate = dateform.cleaned_data['startdate']
|
|
enddate = dateform.cleaned_data['enddate']
|
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
|
request.session['startdate'] = startdatestring
|
|
request.session['enddate'] = enddatestring
|
|
modalityform = TrendFlexModalForm(request.POST)
|
|
if modalityform.is_valid():
|
|
modality = modalityform.cleaned_data['modality']
|
|
waterboattype = modalityform.cleaned_data['waterboattype']
|
|
rankingonly = modalityform.cleaned_data['rankingonly']
|
|
if modality == 'all':
|
|
modalities = [m[0] for m in mytypes.workouttypes]
|
|
else:
|
|
modalities = [modality]
|
|
|
|
if modality != 'water':
|
|
waterboattype = [b[0] for b in mytypes.boattypes]
|
|
|
|
|
|
request.session['modalities'] = modalities
|
|
request.session['waterboattype'] = waterboattype
|
|
request.session['rankingonly'] = rankingonly
|
|
else:
|
|
dateform = DateRangeForm(initial={
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
})
|
|
|
|
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
#enddate = enddate+datetime.timedelta(days=1)
|
|
|
|
if startdatestring:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
if enddatestring:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
|
|
negtypes = []
|
|
for b in mytypes.boattypes:
|
|
if b[0] not in waterboattype:
|
|
negtypes.append(b[0])
|
|
|
|
if rankingonly:
|
|
rankingpiece = [True]
|
|
else:
|
|
rankingpiece = [True,False]
|
|
|
|
workouts = Workout.objects.filter(
|
|
user=r,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate,
|
|
workouttype__in=modalities,
|
|
rankingpiece__in=rankingpiece
|
|
).order_by(
|
|
"-date", "-starttime"
|
|
).exclude(
|
|
boattype__in=negtypes
|
|
)
|
|
|
|
query = request.GET.get('q')
|
|
if query:
|
|
query_list = query.split()
|
|
workouts = workouts.filter(
|
|
reduce(operator.and_,
|
|
(Q(name__icontains=q) for q in query_list)) |
|
|
reduce(operator.and_,
|
|
(Q(notes__icontains=q) for q in query_list))
|
|
)
|
|
searchform = SearchForm(initial={'q':query})
|
|
else:
|
|
searchform = SearchForm()
|
|
|
|
form = WorkoutMultipleCompareForm()
|
|
form.fields["workouts"].queryset = workouts
|
|
|
|
chartform = MultiFlexChoiceForm(initial={
|
|
'palette':palette,
|
|
'ploterrorbars':ploterrorbars,
|
|
'includereststrokes':includereststrokes,
|
|
})
|
|
|
|
modalityform = TrendFlexModalForm(initial={
|
|
'modality':modality,
|
|
'waterboattype':waterboattype,
|
|
'rankingonly':rankingonly,
|
|
})
|
|
|
|
messages.info(request,successmessage)
|
|
messages.error(request,message)
|
|
|
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
|
request.session['startdate'] = startdatestring
|
|
request.session['enddate'] = enddatestring
|
|
|
|
request.session['waterboattype'] = waterboattype
|
|
request.session['rankingonly'] = rankingonly
|
|
request.session['modalities'] = modalities
|
|
|
|
|
|
breadcrumbs = [
|
|
{
|
|
'url':'/rowers/analysis',
|
|
'name':'Analysis'
|
|
},
|
|
{
|
|
'url':reverse(user_multiflex_select,kwargs={'userid':userid}),
|
|
'name': 'Compare Select'
|
|
},
|
|
{
|
|
'url':reverse('multi_compare_view'),
|
|
'name': 'Comparison Chart'
|
|
}
|
|
]
|
|
|
|
return render(request, 'user_multiflex_select.html',
|
|
{'workouts': workouts,
|
|
'dateform':dateform,
|
|
'breadcrumbs':breadcrumbs,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'theuser':user,
|
|
'rower':r,
|
|
'form':form,
|
|
'chartform':chartform,
|
|
'searchform':searchform,
|
|
'modalityform':modalityform,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
@user_passes_test(ispromember,login_url="/rowers/paidplans",
|
|
message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
|
|
redirect_field_name=None)
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def multiflex_data(request,userid=0,
|
|
options={
|
|
'includereststrokes':False,
|
|
'ploterrorbars':False,
|
|
'userid':0,
|
|
'palette': 'monochrome_blue',
|
|
'groupby': 'spm',
|
|
'binsize': 1,
|
|
'xparam': 'hr',
|
|
'yparam': 'pace',
|
|
'spmmin': 15,
|
|
'spmmax': 55,
|
|
'workmin': 400,
|
|
'workmax': 1500,
|
|
'ids': [],
|
|
'ploterrorbars':False,
|
|
}):
|
|
|
|
def_options = options
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
|
|
try:
|
|
includereststrokes = options['includereststrokes']
|
|
except KeyError:
|
|
includereststrokes = False
|
|
|
|
try:
|
|
ploterrorbars = options['ploterrorbars']
|
|
except KeyError:
|
|
ploterrorbars = False
|
|
|
|
try:
|
|
palette = request.session['palette']
|
|
except KeyError:
|
|
palette = 'monochrome_blue'
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
if userid==0:
|
|
userid = request.user.id
|
|
|
|
|
|
palette = keyvalue_get_default('palette',options, def_options)
|
|
groupby = keyvalue_get_default('groupby',options, def_options)
|
|
binsize = keyvalue_get_default('binsize',options, def_options)
|
|
xparam = keyvalue_get_default('xparam',options, def_options)
|
|
yparam = keyvalue_get_default('yparam',options, def_options)
|
|
spmmin = keyvalue_get_default('spmmin',options, def_options)
|
|
spmmax = keyvalue_get_default('spmmax',options, def_options)
|
|
workmin = keyvalue_get_default('workmin',options, def_options)
|
|
workmax = keyvalue_get_default('workmax',options, def_options)
|
|
ids = keyvalue_get_default('ids',options, def_options)
|
|
|
|
workouts = []
|
|
|
|
for id in ids:
|
|
try:
|
|
workouts.append(Workout.objects.get(id=id))
|
|
except Workout.DoesNotExist:
|
|
pass
|
|
|
|
labeldict = {
|
|
int(w.id): w.__str__() for w in workouts
|
|
}
|
|
|
|
fieldlist,fielddict = dataprep.getstatsfields()
|
|
fieldlist = [xparam,yparam,groupby,
|
|
'workoutid','spm','driveenergy',
|
|
'workoutstate']
|
|
|
|
# prepare data frame
|
|
datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)
|
|
|
|
if xparam == groupby:
|
|
datadf['groupby'] = datadf[xparam]
|
|
groupy = 'groupby'
|
|
|
|
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
|
|
|
|
|
|
datadf = dataprep.filter_df(datadf,'spm',spmmin,
|
|
largerthan=True)
|
|
datadf = dataprep.filter_df(datadf,'spm',spmmax,
|
|
largerthan=False)
|
|
|
|
datadf = dataprep.filter_df(datadf,'driveenergy',workmin,
|
|
largerthan=True)
|
|
datadf = dataprep.filter_df(datadf,'driveneergy',workmax,
|
|
largerthan=False)
|
|
|
|
|
|
datadf.dropna(axis=0,how='any',inplace=True)
|
|
|
|
|
|
datemapping = {
|
|
w.id:w.date for w in workouts
|
|
}
|
|
|
|
datadf['date'] = datadf['workoutid']
|
|
datadf['date'].replace(datemapping,inplace=True)
|
|
|
|
today = datetime.date.today()
|
|
try:
|
|
datadf['days ago'] = map(lambda x : x.days, datadf.date - today)
|
|
except TypeError:
|
|
datadf['days ago'] = 0
|
|
|
|
if groupby != 'date':
|
|
try:
|
|
bins = np.arange(datadf[groupby].min()-binsize,
|
|
datadf[groupby].max()+binsize,
|
|
binsize)
|
|
groups = datadf.groupby(pd.cut(datadf[groupby],bins,labels=False))
|
|
except ValueError:
|
|
messages.error(
|
|
request,
|
|
"Unable to compete. Probably not enough data selected"
|
|
)
|
|
url = reverse(user_multiflex_select)
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
bins = np.arange(datadf['days ago'].min()-binsize,
|
|
datadf['days ago'].max()+binsize,
|
|
binsize,
|
|
)
|
|
groups = datadf.groupby(pd.cut(datadf['days ago'], bins,
|
|
labels=False))
|
|
|
|
|
|
xvalues = groups.mean()[xparam]
|
|
yvalues = groups.mean()[yparam]
|
|
xerror = groups.std()[xparam]
|
|
yerror = groups.std()[yparam]
|
|
groupsize = groups.count()[xparam]
|
|
|
|
mask = groupsize <= min([0.01*groupsize.sum(),0.2*groupsize.mean()])
|
|
xvalues.loc[mask] = np.nan
|
|
|
|
yvalues.loc[mask] = np.nan
|
|
xerror.loc[mask] = np.nan
|
|
yerror.loc[mask] = np.nan
|
|
groupsize.loc[mask] = np.nan
|
|
|
|
xvalues.dropna(inplace=True)
|
|
yvalues.dropna(inplace=True)
|
|
xerror.dropna(inplace=True)
|
|
yerror.dropna(inplace=True)
|
|
groupsize.dropna(inplace=True)
|
|
|
|
if len(groupsize) == 0:
|
|
messages.error(request,'No data in selection')
|
|
url = reverse(user_multiflex_select)
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
groupsize = 30.*np.sqrt(groupsize/float(groupsize.max()))
|
|
|
|
df = pd.DataFrame({
|
|
xparam:xvalues,
|
|
yparam:yvalues,
|
|
'x':xvalues,
|
|
'y':yvalues,
|
|
'xerror':xerror,
|
|
'yerror':yerror,
|
|
'groupsize':groupsize,
|
|
})
|
|
|
|
|
|
if yparam == 'pace':
|
|
df['y'] = dataprep.paceformatsecs(df['y']/1.0e3)
|
|
|
|
aantal = len(df)
|
|
|
|
if groupby != 'date':
|
|
try:
|
|
df['groupval'] = groups.mean()[groupby]
|
|
df['groupval'].loc[mask] = np.nan
|
|
|
|
groupcols = df['groupval']
|
|
except ValueError:
|
|
df['groupval'] = groups.mean()[groupby].fillna(value=0)
|
|
df['groupval'].loc[mask] = np.nan
|
|
groupcols = df['groupval']
|
|
except KeyError:
|
|
messages.error(request,'Data selection error')
|
|
url = reverse(user_multiflex_select)
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
try:
|
|
dates = groups.min()[groupby]
|
|
dates.loc[mask] = np.nan
|
|
dates.dropna(inplace=True)
|
|
df['groupval'] = [x.strftime("%Y-%m-%d") for x in dates]
|
|
df['groupval'].loc[mask] = np.nan
|
|
groupcols = 100.*np.arange(aantal)/float(aantal)
|
|
except AttributeError:
|
|
df['groupval'] = groups.mean()['days ago'].fillna(value=0)
|
|
groupcols = 100.*np.arange(aantal)/float(aantal)
|
|
|
|
|
|
groupcols = (groupcols-groupcols.min())/(groupcols.max()-groupcols.min())
|
|
|
|
if aantal == 1:
|
|
groupcols = np.array([1.])
|
|
|
|
|
|
colors = range_to_color_hex(groupcols,palette=palette)
|
|
|
|
df['color'] = colors
|
|
|
|
clegendx = np.arange(0,1.2,.2)
|
|
legcolors = range_to_color_hex(clegendx,palette=palette)
|
|
if groupby != 'date':
|
|
clegendy = df['groupval'].min()+clegendx*(df['groupval'].max()-df['groupval'].min())
|
|
else:
|
|
clegendy = df.index.min()+clegendx*(df.index.max()-df.index.min())
|
|
|
|
|
|
|
|
colorlegend = zip(range(6),clegendy,legcolors)
|
|
|
|
|
|
if userid == 0:
|
|
extratitle = ''
|
|
else:
|
|
u = User.objects.get(id=userid)
|
|
extratitle = ' '+u.first_name+' '+u.last_name
|
|
|
|
|
|
|
|
script,div = interactive_multiflex(df,xparam,yparam,
|
|
groupby,
|
|
extratitle=extratitle,
|
|
ploterrorbars=ploterrorbars,
|
|
binsize=binsize,
|
|
colorlegend=colorlegend,
|
|
spmmin=spmmin,spmmax=spmmax,
|
|
workmin=workmin,workmax=workmax)
|
|
|
|
scripta= script.split('\n')[2:-1]
|
|
script = ''.join(scripta)
|
|
|
|
|
|
return JSONResponse({
|
|
"script":script,
|
|
"div":div,
|
|
})
|
|
|
|
|
|
@user_passes_test(ispromember,login_url="/rowers/paidplans",
|
|
message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
|
|
redirect_field_name=None)
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def multiflex_view(request,userid=0,
|
|
options={
|
|
'includereststrokes':False,
|
|
'ploterrorbars':False,
|
|
}):
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
|
|
try:
|
|
includereststrokes = options['includereststrokes']
|
|
except KeyError:
|
|
includereststrokes = False
|
|
|
|
try:
|
|
ploterrorbars = options['ploterrorbars']
|
|
except KeyError:
|
|
ploterrorbars = False
|
|
|
|
try:
|
|
palette = request.session['palette']
|
|
except KeyError:
|
|
palette = 'monochrome_blue'
|
|
|
|
if 'startdate' in request.session:
|
|
startdate = iso8601.parse_date(request.session['startdate'])
|
|
|
|
|
|
if 'enddate' in request.session:
|
|
enddate = iso8601.parse_date(request.session['enddate'])
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
if userid==0:
|
|
userid = request.user.id
|
|
|
|
if request.method == 'POST' and 'workouts' in request.POST:
|
|
form = WorkoutMultipleCompareForm(request.POST)
|
|
chartform = MultiFlexChoiceForm(request.POST)
|
|
if form.is_valid() and chartform.is_valid():
|
|
cd = form.cleaned_data
|
|
workouts = cd['workouts']
|
|
xparam = chartform.cleaned_data['xparam']
|
|
yparam = chartform.cleaned_data['yparam']
|
|
includereststrokes = chartform.cleaned_data['includereststrokes']
|
|
ploterrorbars = chartform.cleaned_data['ploterrorbars']
|
|
|
|
workstrokesonly = not includereststrokes
|
|
palette = chartform.cleaned_data['palette']
|
|
|
|
groupby = chartform.cleaned_data['groupby']
|
|
binsize = chartform.cleaned_data['binsize']
|
|
if binsize <= 0:
|
|
binsize = 1
|
|
if groupby == 'pace':
|
|
binsize *= 1000
|
|
|
|
spmmin = chartform.cleaned_data['spmmin']
|
|
spmmax = chartform.cleaned_data['spmmax']
|
|
workmin = chartform.cleaned_data['workmin']
|
|
workmax = chartform.cleaned_data['workmax']
|
|
|
|
ids = [int(w.id) for w in workouts]
|
|
request.session['ids'] = ids
|
|
|
|
else:
|
|
return HttpResponse("Form is not valid")
|
|
elif request.method == 'POST' and 'ids' in request.session:
|
|
chartform = MultiFlexChoiceForm(request.POST)
|
|
if chartform.is_valid():
|
|
xparam = chartform.cleaned_data['xparam']
|
|
yparam = chartform.cleaned_data['yparam']
|
|
includereststrokes = chartform.cleaned_data['includereststrokes']
|
|
ploterrorbars = chartform.cleaned_data['ploterrorbars']
|
|
request.session['ploterrorbars'] = ploterrorbars
|
|
request.session['includereststrokes'] = includereststrokes
|
|
workstrokesonly = not includereststrokes
|
|
palette = chartform.cleaned_data['palette']
|
|
|
|
groupby = chartform.cleaned_data['groupby']
|
|
binsize = chartform.cleaned_data['binsize']
|
|
if binsize <= 0:
|
|
binsize = 1
|
|
if groupby == 'pace':
|
|
binsize *= 1000.
|
|
|
|
spmmin = chartform.cleaned_data['spmmin']
|
|
spmmax = chartform.cleaned_data['spmmax']
|
|
workmin = chartform.cleaned_data['workmin']
|
|
workmax = chartform.cleaned_data['workmax']
|
|
|
|
ids = request.session['ids']
|
|
request.session['ids'] = ids
|
|
workouts = dataprep.get_workouts(ids,userid)
|
|
if not workouts:
|
|
message = 'Error: Workouts in session storage do not belong to this user.'
|
|
messages.error(request,message)
|
|
url = reverse(user_multiflex_select,
|
|
kwargs={
|
|
'userid':userid,
|
|
}
|
|
)
|
|
return HttpResponseRedirect(url)
|
|
|
|
# workouts = [Workout.objects.get(id=id) for id in ids]
|
|
|
|
|
|
else:
|
|
return HttpResponse("invalid form")
|
|
else:
|
|
url = reverse(user_multiflex_select)
|
|
return HttpResponseRedirect(url)
|
|
|
|
div = get_call()
|
|
|
|
options['includereststrokes'] = includereststrokes
|
|
options['ploterrorbars'] = ploterrorbars
|
|
options['userid'] = userid
|
|
options['palette'] = palette
|
|
options['groupby'] = groupby
|
|
options['binsize'] = binsize
|
|
options['xparam'] = xparam
|
|
options['yparam'] = yparam
|
|
options['spmmin'] = spmmin
|
|
options['spmmax'] = spmmax
|
|
options['workmin'] = workmin
|
|
options['workmax'] = workmax
|
|
options['idso'] = ids
|
|
|
|
|
|
request.session['options'] = options
|
|
|
|
r = getrequestrower(request,userid=userid)
|
|
|
|
breadcrumbs = [
|
|
{
|
|
'url':'/rowers/analysis',
|
|
'name':'Analysis'
|
|
},
|
|
{
|
|
'url':reverse(user_multiflex_select,kwargs={'userid':userid}),
|
|
'name': 'Trend Flex Select'
|
|
},
|
|
{
|
|
'url':reverse(multiflex_view),
|
|
'name': 'Trend Flex Chart'
|
|
}
|
|
]
|
|
|
|
|
|
return render(request,'multiflex.html',
|
|
{'interactiveplot':'',
|
|
'active':'nav-analysis',
|
|
'rower':r,
|
|
'breadcrumbs':breadcrumbs,
|
|
'the_div':div,
|
|
'active':'nav-analysis',
|
|
'chartform':chartform,
|
|
'userid':userid,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
|
|
# Box plots
|
|
@user_passes_test(ispromember,login_url="/rowers/paidplans",
|
|
message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
|
|
redirect_field_name=None)
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def user_boxplot_select(request,
|
|
startdatestring="",
|
|
enddatestring="",
|
|
message='',
|
|
successmessage='',
|
|
startdate=timezone.now()-datetime.timedelta(days=30),
|
|
enddate=timezone.now(),
|
|
options={
|
|
'includereststrokes':False,
|
|
'workouttypes':['rower','dynamic','slides'],
|
|
'waterboattype':mytypes.waterboattype,
|
|
'rankingonly':False,
|
|
},
|
|
userid=0):
|
|
|
|
r = getrequestrower(request,userid=userid)
|
|
user = r.user
|
|
userid = user.id
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
|
|
|
|
try:
|
|
workouttypes = options['workouttypes']
|
|
except KeyError:
|
|
workouttypes = ['rower','dynamic','slides']
|
|
|
|
try:
|
|
rankingonly = options['rankingonly']
|
|
except KeyError:
|
|
rankingonly = False
|
|
|
|
try:
|
|
includereststrokes = options['includereststrokes']
|
|
except KeyError:
|
|
includereststrokes = False
|
|
|
|
if 'startdate' in request.session:
|
|
startdate = iso8601.parse_date(request.session['startdate'])
|
|
|
|
|
|
if 'enddate' in request.session:
|
|
enddate = iso8601.parse_date(request.session['enddate'])
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
waterboattype = mytypes.waterboattype
|
|
|
|
|
|
if startdatestring != "":
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
|
|
if enddatestring != "":
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
|
|
if request.method == 'POST':
|
|
dateform = DateRangeForm(request.POST)
|
|
if dateform.is_valid():
|
|
startdate = dateform.cleaned_data['startdate']
|
|
enddate = dateform.cleaned_data['enddate']
|
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
|
request.session['startdate'] = startdatestring
|
|
request.session['enddate'] = enddatestring
|
|
optionsform = TrendFlexModalForm(request.POST)
|
|
if optionsform.is_valid():
|
|
modality = optionsform.cleaned_data['modality']
|
|
waterboattype = optionsform.cleaned_data['waterboattype']
|
|
if modality == 'all':
|
|
modalities = [m[0] for m in mytypes.workouttypes]
|
|
else:
|
|
modalities = [modality]
|
|
if modality != 'water':
|
|
waterboattype = [b[0] for b in mytypes.boattypes]
|
|
|
|
|
|
if 'rankingonly' in optionsform.cleaned_data:
|
|
rankingonly = optionsform.cleaned_data['rankingonly']
|
|
else:
|
|
rankingonly = False
|
|
|
|
request.session['modalities'] = modalities
|
|
request.session['waterboattype'] = waterboattype
|
|
else:
|
|
dateform = DateRangeForm(initial={
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
})
|
|
|
|
if 'modalities' in request.session:
|
|
modalities = request.session['modalities']
|
|
if len(modalities) > 1:
|
|
modality = 'all'
|
|
else:
|
|
modality = modalities[0]
|
|
else:
|
|
modalities = [m[0] for m in mytypes.workouttypes]
|
|
modality = 'all'
|
|
|
|
|
|
|
|
|
|
negtypes = []
|
|
for b in mytypes.boattypes:
|
|
if b[0] not in waterboattype:
|
|
negtypes.append(b[0])
|
|
|
|
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
#enddate = enddate+datetime.timedelta(days=1)
|
|
|
|
if startdatestring:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
if enddatestring:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
negtypes = []
|
|
for b in mytypes.boattypes:
|
|
if b[0] not in waterboattype:
|
|
negtypes.append(b[0])
|
|
|
|
|
|
workouts = Workout.objects.filter(user=r,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate,
|
|
workouttype__in=modalities,
|
|
).order_by(
|
|
"-date", "-starttime"
|
|
).exclude(boattype__in=negtypes)
|
|
# workouttypes = [w for w in workouttypes if w not in mytypes.otwtypes]
|
|
|
|
if rankingonly:
|
|
workouts = workouts.exclude(rankingpiece=False)
|
|
|
|
query = request.GET.get('q')
|
|
if query:
|
|
query_list = query.split()
|
|
workouts = workouts.filter(
|
|
reduce(operator.and_,
|
|
(Q(name__icontains=q) for q in query_list)) |
|
|
reduce(operator.and_,
|
|
(Q(notes__icontains=q) for q in query_list))
|
|
)
|
|
searchform = SearchForm(initial={'q':query})
|
|
else:
|
|
searchform = SearchForm()
|
|
|
|
form = WorkoutMultipleCompareForm()
|
|
form.fields["workouts"].queryset = workouts
|
|
|
|
chartform = BoxPlotChoiceForm()
|
|
optionsform = TrendFlexModalForm(initial={
|
|
'modality':modality,
|
|
'waterboattype':waterboattype,
|
|
'rankingonly':rankingonly,
|
|
})
|
|
|
|
messages.info(request,successmessage)
|
|
messages.error(request,message)
|
|
|
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
|
request.session['startdate'] = startdatestring
|
|
request.session['enddate'] = enddatestring
|
|
|
|
|
|
breadcrumbs = [
|
|
{
|
|
'url':'/rowers/analysis',
|
|
'name':'Analysis'
|
|
},
|
|
{
|
|
'url':reverse(user_boxplot_select,kwargs={'userid':userid}),
|
|
'name': 'BoxPlot Select'
|
|
},
|
|
]
|
|
return render(request, 'user_boxplot_select.html',
|
|
{'workouts': workouts,
|
|
'dateform':dateform,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'rower':r,
|
|
'breadcrumbs':breadcrumbs,
|
|
'theuser':user,
|
|
'form':form,
|
|
'active':'nav-analysis',
|
|
'chartform':chartform,
|
|
'searchform':searchform,
|
|
'optionsform':optionsform,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
@user_passes_test(ispromember,login_url="/rowers/paidplans",
|
|
message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
|
|
redirect_field_name=None)
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def boxplot_view_data(request,userid=0,
|
|
options={
|
|
'includereststrokes':False,
|
|
'spmmin':15,
|
|
'spmmax':55,
|
|
'workmin':0,
|
|
'workmax':1500,
|
|
'ids':[],
|
|
'userid':0,
|
|
'plotfield':'spm',
|
|
}):
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
|
|
try:
|
|
includereststrokes = options['includereststrokes']
|
|
spmmin = options['spmmin']
|
|
spmmax = options['spmmax']
|
|
workmin = options['workmin']
|
|
workmax = options['workmax']
|
|
ids = options['ids']
|
|
userid = options['userid']
|
|
plotfield = options['plotfield']
|
|
except KeyError:
|
|
includereststrokes = False
|
|
spmmin = 15
|
|
spmmax = 55
|
|
workmin = 0
|
|
workmax = 55
|
|
ids = []
|
|
userid = 0
|
|
plotfield = 'spm'
|
|
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
if userid==0:
|
|
userid = request.user.id
|
|
|
|
workouts = []
|
|
|
|
|
|
if not ids:
|
|
return JSONResponse({
|
|
"script":'',
|
|
"div":'No data found'
|
|
})
|
|
|
|
for id in ids:
|
|
try:
|
|
workouts.append(Workout.objects.get(id=id))
|
|
except Workout.DoesNotExist:
|
|
pass
|
|
|
|
labeldict = {
|
|
int(w.id): w.__str__() for w in workouts
|
|
}
|
|
|
|
|
|
datemapping = {
|
|
w.id:w.date for w in workouts
|
|
}
|
|
|
|
|
|
|
|
fieldlist,fielddict = dataprep.getstatsfields()
|
|
fieldlist = [plotfield,'workoutid','spm','driveenergy',
|
|
'workoutstate']
|
|
|
|
# prepare data frame
|
|
datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)
|
|
|
|
|
|
|
|
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
|
|
|
|
datadf = dataprep.filter_df(datadf,'spm',spmmin,
|
|
largerthan=True)
|
|
datadf = dataprep.filter_df(datadf,'spm',spmmax,
|
|
largerthan=False)
|
|
datadf = dataprep.filter_df(datadf,'driveenergy',workmin,
|
|
largerthan=True)
|
|
datadf = dataprep.filter_df(datadf,'driveneergy',workmax,
|
|
largerthan=False)
|
|
|
|
datadf.dropna(axis=0,how='any',inplace=True)
|
|
|
|
|
|
datadf['workoutid'].replace(datemapping,inplace=True)
|
|
datadf.rename(columns={"workoutid":"date"},inplace=True)
|
|
datadf = datadf.sort_values(['date'])
|
|
|
|
if userid == 0:
|
|
extratitle = ''
|
|
else:
|
|
u = User.objects.get(id=userid)
|
|
extratitle = ' '+u.first_name+' '+u.last_name
|
|
|
|
|
|
|
|
script,div = interactive_boxchart(datadf,plotfield,
|
|
extratitle=extratitle,
|
|
spmmin=spmmin,spmmax=spmmax,workmin=workmin,workmax=workmax)
|
|
|
|
scripta = script.split('\n')[2:-1]
|
|
script = ''.join(scripta)
|
|
|
|
|
|
return JSONResponse({
|
|
"script":script,
|
|
"div":div,
|
|
})
|
|
|
|
@user_passes_test(ispromember,login_url="/rowers/paidplans",
|
|
message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
|
|
redirect_field_name=None)
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def boxplot_view(request,userid=0,
|
|
options={
|
|
'includereststrokes':False,
|
|
'rankingonly':False,
|
|
}):
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
else:
|
|
options = {}
|
|
|
|
try:
|
|
includereststrokes = options['includereststrokes']
|
|
except KeyError:
|
|
includereststrokes = False
|
|
options['includereststrokes'] = False
|
|
|
|
try:
|
|
rankingonly = options['rankingonly']
|
|
except KeyError:
|
|
rankingonly = False
|
|
options['rankingonly'] = False
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
if userid==0:
|
|
userid = request.user.id
|
|
|
|
|
|
if request.method == 'POST' and 'workouts' in request.POST:
|
|
form = WorkoutMultipleCompareForm(request.POST)
|
|
chartform = BoxPlotChoiceForm(request.POST)
|
|
if form.is_valid() and chartform.is_valid():
|
|
cd = form.cleaned_data
|
|
workouts = cd['workouts']
|
|
plotfield = chartform.cleaned_data['yparam']
|
|
includereststrokes = chartform.cleaned_data['includereststrokes']
|
|
request.session['includereststrokes'] = includereststrokes
|
|
workstrokesonly = not includereststrokes
|
|
|
|
spmmin = chartform.cleaned_data['spmmin']
|
|
spmmax = chartform.cleaned_data['spmmax']
|
|
workmin = chartform.cleaned_data['workmin']
|
|
workmax = chartform.cleaned_data['workmax']
|
|
|
|
ids = [int(w.id) for w in workouts]
|
|
request.session['ids'] = ids
|
|
|
|
else:
|
|
url = reverse(user_boxplot_select,kwargs={'userid':userid})
|
|
return HttpResponseRedirect(url)
|
|
elif request.method == 'POST' and 'ids' in request.session:
|
|
chartform = BoxPlotChoiceForm(request.POST)
|
|
if chartform.is_valid():
|
|
plotfield = chartform.cleaned_data['yparam']
|
|
includereststrokes = chartform.cleaned_data['includereststrokes']
|
|
spmmin = chartform.cleaned_data['spmmin']
|
|
spmmax = chartform.cleaned_data['spmmax']
|
|
workmin = chartform.cleaned_data['workmin']
|
|
workmax = chartform.cleaned_data['workmax']
|
|
request.session['includereststrokes'] = includereststrokes
|
|
workstrokesonly = not includereststrokes
|
|
ids = request.session['ids']
|
|
request.session['ids'] = ids
|
|
|
|
|
|
else:
|
|
url = reverse(user_boxplot_select,kwargs={'userid':userid})
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
url = reverse(user_boxplot_select,kwargs={'userid':userid})
|
|
return HttpResponseRedirect(url)
|
|
|
|
div = get_call()
|
|
|
|
|
|
options['spmmin'] = spmmin
|
|
options['spmmax'] = spmmax
|
|
options['workmin'] = workmin
|
|
options['workmax'] = workmax
|
|
options['ids'] = ids
|
|
options['userid'] = userid
|
|
options['plotfield'] = plotfield
|
|
options['rankingonly'] = rankingonly
|
|
|
|
|
|
request.session['options'] = options
|
|
|
|
r = getrequestrower(request,userid=userid)
|
|
breadcrumbs = [
|
|
{
|
|
'url':'/rowers/Analysis',
|
|
'name':'Analysis'
|
|
},
|
|
{
|
|
'url':reverse(user_boxplot_select,kwargs={'userid':userid}),
|
|
'name': 'BoxPlot Select'
|
|
},
|
|
{
|
|
'url':reverse(boxplot_view,kwargs={'userid':userid}),
|
|
'name': 'BoxPlot Select'
|
|
},
|
|
]
|
|
|
|
return render(request,'boxplot.html',
|
|
{'interactiveplot':'',
|
|
'the_div':div,
|
|
'rower':r,
|
|
'breadcrumbs':breadcrumbs,
|
|
'active':'nav-analysis',
|
|
'chartform':chartform,
|
|
'userid':userid,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
|
|
# Cumulative stats page
|
|
@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",redirect_field_name=None)
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def cumstats(request,userid=0,
|
|
startdate=timezone.now()-datetime.timedelta(days=30),
|
|
enddate=timezone.now(),
|
|
deltadays=-1,
|
|
startdatestring="",
|
|
enddatestring="",
|
|
options={
|
|
'includereststrokes':False,
|
|
'workouttypes':['rower','dynamic','slides'],
|
|
'waterboattype':mytypes.waterboattype,
|
|
'rankingonly':False,
|
|
}):
|
|
|
|
r = getrequestrower(request,userid=userid)
|
|
theuser = r.user
|
|
|
|
if 'waterboattype' in request.session:
|
|
waterboattype = request.session['waterboattype']
|
|
else:
|
|
waterboattype = mytypes.waterboattype
|
|
|
|
|
|
if 'rankingonly' in request.session:
|
|
rankingonly = request.session['rankingonly']
|
|
else:
|
|
rankingonly = False
|
|
|
|
if 'modalities' in request.session:
|
|
modalities = request.session['modalities']
|
|
if len(modalities) > 1:
|
|
modality = 'all'
|
|
else:
|
|
modality = modalities[0]
|
|
else:
|
|
modalities = [m[0] for m in mytypes.workouttypes]
|
|
modality = 'all'
|
|
|
|
|
|
try:
|
|
rankingonly = options['rankingonly']
|
|
except KeyError:
|
|
rankingonly = False
|
|
|
|
try:
|
|
includereststrokes = options['includereststrokes']
|
|
except KeyError:
|
|
includereststrokes = False
|
|
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
waterboattype = mytypes.waterboattype
|
|
|
|
|
|
if startdatestring != "":
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
|
|
if enddatestring != "":
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
|
|
# get all indoor rows of in date range
|
|
|
|
# process form
|
|
if request.method == 'POST':
|
|
form = DateRangeForm(request.POST)
|
|
modalityform = TrendFlexModalForm(request.POST)
|
|
if form.is_valid():
|
|
startdate = form.cleaned_data['startdate']
|
|
enddate = form.cleaned_data['enddate']
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
|
if modalityform.is_valid():
|
|
modality = modalityform.cleaned_data['modality']
|
|
waterboattype = modalityform.cleaned_data['waterboattype']
|
|
rankingonly = modalityform.cleaned_data['rankingonly']
|
|
if modality == 'all':
|
|
modalities = [m[0] for m in mytypes.workouttypes]
|
|
else:
|
|
modalities = [modality]
|
|
|
|
if modality != 'water':
|
|
waterboattype = [b[0] for b in mytypes.boattypes]
|
|
|
|
|
|
request.session['modalities'] = modalities
|
|
request.session['waterboattype'] = waterboattype
|
|
request.session['rankingonly'] = rankingonly
|
|
form = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
else:
|
|
form = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
includereststrokes = False
|
|
|
|
workstrokesonly = not includereststrokes
|
|
modalityform = TrendFlexModalForm(
|
|
initial={
|
|
'modality':modality,
|
|
'waterboattype':waterboattype,
|
|
'rankingonly':rankingonly,
|
|
}
|
|
)
|
|
|
|
negtypes = []
|
|
for b in mytypes.boattypes:
|
|
if b[0] not in waterboattype:
|
|
negtypes.append(b[0])
|
|
|
|
|
|
|
|
script = ''
|
|
div = get_call()
|
|
js_resources = ''
|
|
css_resources = ''
|
|
|
|
options = {
|
|
'modality': modality,
|
|
'userid': theuser.id,
|
|
'waterboattype':waterboattype,
|
|
'startdatestring':startdatestring,
|
|
'enddatestring':enddatestring,
|
|
'rankingonly':rankingonly,
|
|
'includereststrokes':includereststrokes,
|
|
}
|
|
|
|
|
|
request.session['options'] = options
|
|
|
|
|
|
if modality == 'all':
|
|
modalities = [m[0] for m in mytypes.workouttypes]
|
|
else:
|
|
modalities = [modality]
|
|
|
|
try:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
except ParseError:
|
|
startdate = timezone.now()-datetime.timedelta(days=7)
|
|
|
|
try:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
except ParseError:
|
|
enddate = timezone.now()
|
|
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
promember=0
|
|
if theuser == 0:
|
|
theuser = request.user.id
|
|
|
|
if not request.user.is_anonymous:
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
|
|
r2 = getrower(theuser)
|
|
|
|
if rankingonly:
|
|
rankingpiece = [True,]
|
|
else:
|
|
rankingpiece = [True,False]
|
|
|
|
allworkouts = Workout.objects.filter(
|
|
user=r2,
|
|
workouttype__in=modalities,
|
|
boattype__in=waterboattype,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate,
|
|
rankingpiece__in=rankingpiece
|
|
).order_by("-date", "-starttime")
|
|
|
|
ids = [int(workout.id) for workout in allworkouts]
|
|
|
|
datemapping = {
|
|
w.id:w.date for w in allworkouts
|
|
}
|
|
|
|
|
|
|
|
fieldlist,fielddict = dataprep.getstatsfields()
|
|
|
|
# prepare data frame
|
|
datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)
|
|
|
|
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
|
|
|
|
request.session['rowerid'] = r.id
|
|
|
|
if datadf.empty:
|
|
stats = {}
|
|
cordict = {}
|
|
|
|
response = render(request,
|
|
'cumstats.html',
|
|
{
|
|
'stats':stats,
|
|
'teams':get_my_teams(request.user),
|
|
'options':options,
|
|
'active':'nav-analysis',
|
|
'rower':r,
|
|
'id':theuser,
|
|
'theuser':theuser,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'form':form,
|
|
'optionsform':modalityform,
|
|
'cordict':cordict,
|
|
})
|
|
|
|
request.session['options'] = options
|
|
|
|
return response
|
|
|
|
|
|
|
|
# Create stats
|
|
stats = {}
|
|
try:
|
|
fielddict.pop('pace')
|
|
except KeyError:
|
|
pass
|
|
|
|
for field,verbosename in fielddict.items():
|
|
thedict = {
|
|
'mean':datadf[field].mean(),
|
|
'min': datadf[field].min(),
|
|
'std': datadf[field].std(),
|
|
'max': datadf[field].max(),
|
|
'median': datadf[field].median(),
|
|
'firstq':datadf[field].quantile(q=0.25),
|
|
'thirdq':datadf[field].quantile(q=0.75),
|
|
'verbosename':verbosename,
|
|
}
|
|
stats[field] = thedict
|
|
|
|
# Create a dict with correlation values
|
|
cor = datadf.corr(method='spearman')
|
|
cor.fillna(value=0,inplace=True)
|
|
cordict = {}
|
|
for field1,verbosename1 in fielddict.items():
|
|
thedict = {}
|
|
for field2,verbosename2 in fielddict.items():
|
|
try:
|
|
thedict[verbosename2] = cor.loc[field1,field2]
|
|
except KeyError:
|
|
thedict[verbosename2] = 0
|
|
|
|
cordict[verbosename1] = thedict
|
|
|
|
# set options form correctly
|
|
initial = {}
|
|
initial['includereststrokes'] = includereststrokes
|
|
initial['waterboattype'] = waterboattype
|
|
initial['rankingonly'] = rankingonly
|
|
|
|
|
|
response = render(request,
|
|
'cumstats.html',
|
|
{
|
|
'stats':stats,
|
|
'teams':get_my_teams(request.user),
|
|
'active':'nav-analysis',
|
|
'rower':r,
|
|
'options':options,
|
|
'id':theuser,
|
|
'theuser':theuser,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'form':form,
|
|
'optionsform':modalityform,
|
|
'cordict':cordict,
|
|
})
|
|
|
|
request.session['options'] = options
|
|
|
|
return response
|
|
|
|
|
|
def agegroupcpview(request,age,normalize=0):
|
|
script,div = interactive_agegroupcpchart(age,normalized=normalize)
|
|
|
|
response = render(request,'agegroupcp.html',
|
|
{
|
|
'active': 'nav-analysis',
|
|
'interactiveplot':script,
|
|
'the_div':div,
|
|
}
|
|
)
|
|
|
|
return response
|
|
|
|
def agegrouprecordview(request,sex='male',weightcategory='hwt',
|
|
distance=2000,duration=None):
|
|
if not duration:
|
|
df = pd.DataFrame(
|
|
list(
|
|
C2WorldClassAgePerformance.objects.filter(
|
|
distance=distance,
|
|
sex=sex,
|
|
weightcategory=weightcategory
|
|
).values()
|
|
)
|
|
)
|
|
else:
|
|
duration = int(duration)*60
|
|
df = pd.DataFrame(
|
|
list(
|
|
C2WorldClassAgePerformance.objects.filter(
|
|
duration=duration,
|
|
sex=sex,
|
|
weightcategory=weightcategory
|
|
).values()
|
|
)
|
|
)
|
|
|
|
|
|
script,div = interactive_agegroup_plot(df,sex=sex,distance=distance,
|
|
duration=duration,
|
|
weightcategory=weightcategory)
|
|
|
|
return render(request, 'agegroupchart.html',
|
|
{
|
|
'interactiveplot':script,
|
|
'active':'nav-analysis',
|
|
'the_div':div,
|
|
})
|
|
|
|
# alert overview view
|
|
#@user_passes_test(ispromember, login_url="/rowers/paidplans",
|
|
# message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
|
|
# redirect_field_name=None)
|
|
@login_required
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def alerts_view(request,userid=0):
|
|
r = getrequestrower(request,userid=userid)
|
|
|
|
alerts = Alert.objects.filter(rower=r).order_by('next_run')
|
|
|
|
stats = []
|
|
|
|
for alert in alerts:
|
|
stats.append(alert_get_stats(alert))
|
|
|
|
|
|
breadcrumbs = [
|
|
{
|
|
'url':'/rowers/analysis',
|
|
'name': 'Analysis'
|
|
},
|
|
{
|
|
'url': reverse('alerts_view'),
|
|
'name': 'Alerts',
|
|
},
|
|
]
|
|
|
|
return render(request,'alerts.html',
|
|
{
|
|
'breadcrumbs':breadcrumbs,
|
|
'alerts':alerts,
|
|
'rower':r,
|
|
'stats':stats,
|
|
})
|
|
|
|
# alert create view
|
|
@user_passes_test(ispromember, login_url="/rowers/paidplans",
|
|
message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
|
|
redirect_field_name=None)
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def alert_create_view(request,userid=0):
|
|
r = getrequestrower(request,userid=userid)
|
|
FilterFormSet = formset_factory(ConditionEditForm, formset=BaseConditionFormSet,extra=1)
|
|
filter_formset = FilterFormSet()
|
|
|
|
if request.method == 'POST':
|
|
form = AlertEditForm(request.POST)
|
|
measuredform = ConditionEditForm(request.POST)
|
|
filter_formset = FilterFormSet(request.POST)
|
|
if form.is_valid() and measuredform.is_valid() and filter_formset.is_valid():
|
|
ad = form.cleaned_data
|
|
measured = measuredform.cleaned_data
|
|
|
|
period = ad['period']
|
|
emailalert = ad['emailalert']
|
|
reststrokes = ad['reststrokes']
|
|
workouttype = ad['workouttype']
|
|
boattype = ad['boattype']
|
|
name = ad['name']
|
|
|
|
filters = []
|
|
|
|
for filter_form in filter_formset:
|
|
metric = filter_form.cleaned_data.get('metric')
|
|
condition = filter_form.cleaned_data.get('condition')
|
|
value1 = filter_form.cleaned_data.get('value1')
|
|
value2 = filter_form.cleaned_data.get('value2')
|
|
|
|
filters.append(
|
|
{
|
|
'metric':metric,
|
|
'condition':condition,
|
|
'value1':value1,
|
|
'value2':value2,
|
|
}
|
|
)
|
|
|
|
result,message = create_alert(request.user,r,measured,period=period,emailalert=emailalert,
|
|
reststrokes=reststrokes,workouttype=workouttype,
|
|
boattype=boattype,
|
|
filter = filters,
|
|
name=name)
|
|
|
|
if result:
|
|
messages.info(request,message)
|
|
|
|
url = reverse('alert_edit_view',kwargs={'id':result})
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
form = AlertEditForm()
|
|
measuredform = ConditionEditForm()
|
|
|
|
breadcrumbs = [
|
|
{
|
|
'url':'/rowers/analysis',
|
|
'name': 'Analysis'
|
|
},
|
|
{
|
|
'url': reverse('alerts_view'),
|
|
'name': 'Alerts',
|
|
},
|
|
{
|
|
'url': reverse('alert_create_view'),
|
|
'name': 'Create'
|
|
}
|
|
]
|
|
|
|
return render(request,'alert_create.html',
|
|
{
|
|
'breadcrumbs':breadcrumbs,
|
|
'formset': filter_formset,
|
|
'rower':r,
|
|
'form':form,
|
|
'measuredform':measuredform,
|
|
})
|
|
|
|
# alert report view
|
|
@login_required()
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def alert_report_view(request,id=0,userid=0,nperiod=0):
|
|
r = getrequestrower(request,userid=userid)
|
|
if userid == 0:
|
|
userid = request.user.id
|
|
|
|
alert = Alert.objects.get(id=id)
|
|
nperiod = int(nperiod)
|
|
|
|
try:
|
|
alert = Alert.objects.get(id=id)
|
|
except Alert.DoesNotExist:
|
|
raise Http404("This alert doesn't exist")
|
|
|
|
|
|
if not checkalertowner(alert,request.user):
|
|
raise PermissionDenied('You are not allowed to edit this Alert')
|
|
|
|
stats = alert_get_stats(alert,nperiod=nperiod)
|
|
|
|
if request.is_ajax():
|
|
return JSONResponse({
|
|
"stats":stats,
|
|
})
|
|
|
|
breadcrumbs = [
|
|
{
|
|
'url':'/rowers/analysis',
|
|
'name': 'Analysis'
|
|
},
|
|
{
|
|
'url':reverse('alerts_view'),
|
|
'name':'Alerts',
|
|
},
|
|
{
|
|
'url': reverse('alert_edit_view',
|
|
kwargs={'userid':userid,'id':alert.id}),
|
|
'name': alert.name,
|
|
},
|
|
{
|
|
'url': reverse('alert_report_view',
|
|
kwargs={'userid':userid,'id':alert.id}),
|
|
'name': 'Report',
|
|
},
|
|
]
|
|
return render(request,'alert_stats.html',
|
|
{
|
|
'breadcrumbs':breadcrumbs,
|
|
'stats':stats,
|
|
'rower':r,
|
|
'alert':alert,
|
|
'nperiod':nperiod,
|
|
})
|
|
|
|
# alert edit view
|
|
@user_passes_test(ispromember, login_url="/rowers/paidplans",
|
|
message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
|
|
redirect_field_name=None)
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def alert_edit_view(request,id=0,userid=0):
|
|
r = getrequestrower(request,userid=userid)
|
|
|
|
try:
|
|
alert = Alert.objects.get(id=id)
|
|
except Alert.DoesNotExist:
|
|
raise Http404("This alert doesn't exist")
|
|
|
|
|
|
if alert.manager != request.user:
|
|
raise PermissionDenied('You are not allowed to edit this Alert')
|
|
|
|
FilterFormSet = formset_factory(ConditionEditForm, formset=BaseConditionFormSet,extra=0)
|
|
if len(alert.filter.all()) == 0:
|
|
FilterFormSet = formset_factory(ConditionEditForm, formset=BaseConditionFormSet, extra=1)
|
|
|
|
filter_data = [{'metric':m.metric,
|
|
'value1':m.value1,
|
|
'value2':m.value2,
|
|
'condition':m.condition}
|
|
for m in alert.filter.all()]
|
|
|
|
if request.method == 'POST':
|
|
form = AlertEditForm(request.POST)
|
|
measuredform = ConditionEditForm(request.POST)
|
|
filter_formset = FilterFormSet(request.POST)
|
|
if form.is_valid() and measuredform.is_valid() and filter_formset.is_valid():
|
|
ad = form.cleaned_data
|
|
measured = measuredform.cleaned_data
|
|
|
|
period = ad['period']
|
|
emailalert = ad['emailalert']
|
|
reststrokes = ad['reststrokes']
|
|
workouttype = ad['workouttype']
|
|
boattype = ad['boattype']
|
|
name = ad['name']
|
|
|
|
m = alert.measured
|
|
m.metric = measured['metric']
|
|
m.value1 = measured['value1']
|
|
m.value2 = measured['value2']
|
|
m.condition = measured['condition']
|
|
m.save()
|
|
|
|
alert.period = period
|
|
alert.emailalert = emailalert
|
|
alert.reststrokes = reststrokes
|
|
alert.workouttype = workouttype
|
|
alert.boattype = boattype
|
|
alert.name = name
|
|
alert.save()
|
|
|
|
filters = []
|
|
|
|
for filter_form in filter_formset:
|
|
metric = filter_form.cleaned_data.get('metric')
|
|
condition = filter_form.cleaned_data.get('condition')
|
|
value1 = filter_form.cleaned_data.get('value1')
|
|
value2 = filter_form.cleaned_data.get('value2')
|
|
|
|
filters.append(
|
|
{
|
|
'metric':metric,
|
|
'condition':condition,
|
|
'value1':value1,
|
|
'value2':value2,
|
|
}
|
|
)
|
|
|
|
res = alert_add_filters(alert, filters)
|
|
messages.info(request,'Alert was changed')
|
|
|
|
else:
|
|
form = AlertEditForm(instance=alert)
|
|
measuredform = ConditionEditForm(instance=alert.measured)
|
|
filter_formset = FilterFormSet(initial=filter_data)
|
|
|
|
|
|
|
|
breadcrumbs = [
|
|
{
|
|
'url':'/rowers/analysis',
|
|
'name': 'Analysis'
|
|
},
|
|
{
|
|
'url':reverse('alerts_view'),
|
|
'name':'Alerts',
|
|
},
|
|
{
|
|
'url': reverse('alert_edit_view',
|
|
kwargs={'userid':userid,'id':alert.id}),
|
|
'name': alert.name,
|
|
},
|
|
]
|
|
|
|
|
|
return render(request,'alert_edit.html',
|
|
{
|
|
'breadcrumbs':breadcrumbs,
|
|
'rower':r,
|
|
'form':form,
|
|
'measuredform':measuredform,
|
|
'formset':filter_formset,
|
|
'alert':alert,
|
|
})
|
|
|
|
# alert delete view
|
|
|
|
class AlertDelete(DeleteView):
|
|
login_requird = True
|
|
model = Alert
|
|
template_name = 'alert_delete_confirm.html'
|
|
|
|
# extra parameters
|
|
def get_context_data(self, **kwargs):
|
|
context = super(AlertDelete, self).get_context_data(**kwargs)
|
|
|
|
if 'userid' in kwargs:
|
|
userid = kwargs['userid']
|
|
else:
|
|
userid = 0
|
|
|
|
context['rower'] = getrequestrower(self.request,userid=userid)
|
|
context['alert'] = self.object
|
|
|
|
breadcrumbs = [
|
|
{
|
|
'url':'/rowers/analysis',
|
|
'name': 'Analysis'
|
|
},
|
|
{
|
|
'url':reverse('alerts_view'),
|
|
'name':'Alerts',
|
|
},
|
|
{
|
|
'url': reverse('alert_edit_view',
|
|
kwargs={'userid':userid,'id':self.object.pk}),
|
|
'name': 'Alert',
|
|
},
|
|
{
|
|
'url': reverse('alert_delete_view',kwargs={'pk':self.object.pk}),
|
|
'name': 'Delete'
|
|
}
|
|
]
|
|
|
|
context['breadcrumbs'] = breadcrumbs
|
|
|
|
return context
|
|
|
|
def get_success_url(self):
|
|
return reverse('alerts_view')
|
|
|
|
def get_object(self, *args, **kwargs):
|
|
obj = super(AlertDelete, self).get_object(*args, **kwargs)
|
|
|
|
if obj.manager != self.request.user:
|
|
raise PermissionDenied("You are not allowed to delete this Alert")
|
|
|
|
# some checks
|
|
|
|
return obj
|
|
|
|
@user_passes_test(ispromember, login_url="/rowers/paidplans",
|
|
message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
|
|
redirect_field_name=None)
|
|
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
def history_view(request,userid=0):
|
|
r = getrequestrower(request,userid=userid)
|
|
|
|
usertimezone = pytz.timezone(r.defaulttimezone)
|
|
|
|
if request.GET.get('startdate'):
|
|
startdate,enddate = get_dates_timeperiod(request)
|
|
activity_startdate = datetime.datetime(
|
|
startdate.year,startdate.month,startdate.day
|
|
).replace(hour=0,minute=0,second=0).astimezone(usertimezone)
|
|
activity_enddate = datetime.datetime(
|
|
enddate.year,enddate.month,enddate.day
|
|
).replace(hour=23,minute=59,second=59).astimezone(usertimezone)
|
|
else:
|
|
activity_enddate = timezone.now()
|
|
activity_enddate = activity_enddate.replace(hour=23,minute=59,second=59).astimezone(usertimezone)
|
|
activity_startdate = activity_enddate-datetime.timedelta(days=15)
|
|
activity_startdate = activity_startdate.replace(hour=0,minute=0,second=0).astimezone(usertimezone)
|
|
|
|
typeselect = 'All'
|
|
if request.GET.get('workouttype'):
|
|
typeselect = request.GET.get('workouttype')
|
|
|
|
if typeselect not in mytypes.checktypes:
|
|
typeselect = 'All'
|
|
|
|
form = HistorySelectForm(initial={'startdate':activity_startdate,
|
|
'enddate':activity_enddate,
|
|
'workouttype':typeselect})
|
|
|
|
g_workouts = Workout.objects.filter(
|
|
user=r,
|
|
startdatetime__gte=activity_startdate,
|
|
startdatetime__lte=activity_enddate,
|
|
duplicate=False,
|
|
privacy='visible'
|
|
).order_by("-startdatetime")
|
|
|
|
ids = [w.id for w in g_workouts]
|
|
|
|
columns = ['hr','power','time']
|
|
|
|
df = getsmallrowdata_db(columns,ids=ids)
|
|
try:
|
|
df['deltat'] = df['time'].diff()
|
|
except KeyError:
|
|
pass
|
|
df = dataprep.clean_df_stats(df,workstrokesonly=True,
|
|
ignoreadvanced=True)
|
|
|
|
|
|
totalmeters,totalhours, totalminutes = get_totals(g_workouts)
|
|
totalminutes = "{totalminutes:02d}".format(totalminutes=totalminutes)
|
|
|
|
# meters, duration per workout type
|
|
wtypes = list(set([w.workouttype for w in g_workouts]))
|
|
|
|
typechoices = [("All","All")]
|
|
for wtype in wtypes:
|
|
typechoices.append((wtype,mytypes.workouttypes_ordered[wtype]))
|
|
|
|
form.fields['workouttype'].choices = typechoices
|
|
|
|
listofdicts = []
|
|
|
|
for wtype in wtypes:
|
|
a_workouts = g_workouts.filter(workouttype=wtype)
|
|
wmeters, whours, wminutes = get_totals(a_workouts)
|
|
ddict = {}
|
|
ddict['wtype'] = mytypes.workouttypes_ordered[wtype]
|
|
ddict['distance'] = wmeters
|
|
ddict['duration'] = "{whours}:{wminutes:02d}".format(
|
|
whours=whours,
|
|
wminutes=wminutes
|
|
)
|
|
ddf = getsmallrowdata_db(columns,ids=[w.id for w in a_workouts])
|
|
try:
|
|
ddf['deltat'] = ddf['time'].diff()
|
|
except KeyError:
|
|
pass
|
|
ddf = dataprep.clean_df_stats(ddf,workstrokesonly=True,
|
|
ignoreadvanced=True)
|
|
|
|
ddict['hrmean'] = int(wavg(ddf,'hr','deltat'))
|
|
ddict['hrmax'] = ddf['hr'].max().astype(int)
|
|
ddict['powermean'] = int(wavg(df,'power','deltat'))
|
|
ddict['powermax'] = ddf['power'].max().astype(int)
|
|
ddict['nrworkouts'] = a_workouts.count()
|
|
listofdicts.append(ddict)
|
|
|
|
|
|
# interactive hr pie chart
|
|
if typeselect == 'All':
|
|
totalscript,totaldiv = interactive_hr_piechart(df,r,'All Workouts')
|
|
else:
|
|
a_workouts = g_workouts.filter(workouttype=typeselect)
|
|
ddf = getsmallrowdata_db(columns,ids=[w.id for w in a_workouts])
|
|
try:
|
|
ddf['deltat'] = ddf['time'].diff()
|
|
except KeyError:
|
|
pass
|
|
ddf = dataprep.clean_df_stats(ddf,workstrokesonly=True,
|
|
ignoreadvanced=True)
|
|
totalscript, totaldiv = interactive_hr_piechart(ddf,r,mytypes.workouttypes_ordered[typeselect])
|
|
|
|
# interactive power pie chart
|
|
|
|
totalsdict = {}
|
|
totalsdict['duration'] = "{totalhours}:{totalminutes}".format(
|
|
totalhours=totalhours,
|
|
totalminutes=totalminutes
|
|
)
|
|
|
|
totalsdict['distance'] = totalmeters
|
|
try:
|
|
totalsdict['powermean'] = int(wavg(df,'power','deltat'))
|
|
totalsdict['powermax'] = df['power'].max().astype(int)
|
|
except KeyError:
|
|
totalsdict['powermean'] = 0
|
|
totalsdict['powermax'] = 0
|
|
try:
|
|
totalsdict['hrmean'] = int(wavg(df,'hr','deltat'))
|
|
totalsdict['hrmax'] = df['hr'].max().astype(int)
|
|
except KeyError:
|
|
totalsdict['hrmean'] = 0
|
|
totalsdict['hrmax'] = 0
|
|
|
|
totalsdict['nrworkouts'] = g_workouts.count()
|
|
|
|
breadcrumbs = [
|
|
{
|
|
'url':'rowers/analysis',
|
|
'name':'Analysis',
|
|
},
|
|
{
|
|
'url':reverse('history_view'),
|
|
'name': 'History',
|
|
},
|
|
]
|
|
|
|
lastseven = timezone.now()-datetime.timedelta(days=7)
|
|
lastfourteen = timezone.now()-datetime.timedelta(days=14)
|
|
last28 = timezone.now()-datetime.timedelta(days=28)
|
|
today = timezone.now()
|
|
|
|
firstmay = datetime.datetime(year=today.year,month=5,day=1).astimezone(usertimezone)
|
|
if firstmay>today:
|
|
firstmay = datetime.datetime(year=today.year-1,month=5,day=1)
|
|
|
|
return render(request,'history.html',
|
|
{
|
|
'rower':r,
|
|
'breadcrumbs':breadcrumbs,
|
|
'active':'nav-analysis',
|
|
'totalsdict':totalsdict,
|
|
'typedicts':listofdicts,
|
|
'totalscript':totalscript,
|
|
'totaldiv':totaldiv,
|
|
'form':form,
|
|
'startdate':activity_startdate,
|
|
'enddate':activity_enddate,
|
|
'lastseven':lastseven,
|
|
'lastfourteen':lastfourteen,
|
|
'last28':last28,
|
|
'today':today,
|
|
'workouttype':typeselect,
|
|
'firstmay':firstmay,
|
|
})
|