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 import simplejson from jinja2 import Template,Environment,FileSystemLoader def floatformat(x,prec=2): # pragma: no cover return '{x}'.format(x=round(x,prec)) import time 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,id=''): r = getrequestrower(request, userid=userid) user = r.user userid = user.id worldclass = False firstworkout = None if id: firstworkout = get_workout(id) if not is_workout_team(request.user,firstworkout): # pragma: no cover raise PermissionDenied("You are not allowed to use this workout") firstworkoutquery = Workout.objects.filter(id=encoder.decode_hex(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: # pragma: no cover 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: worldclass = options['cpoverlay'] except KeyError: worldclass = False try: rankingonly = options['rankingonly'] except KeyError: # pragma: no cover rankingonly = False try: includereststrokes = options['includereststrokes'] except KeyError: # pragma: no cover 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]: # pragma: no cover 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: # pragma: no cover 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: # pragma: no cover rankingonly = False options['modalities'] = modalities options['waterboattype'] = waterboattype try: worldclass = options['cpoverlay'] except KeyError: worldclass = False options['cpoverlay'] = worldclass 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: # pragma: no cover 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: # pragma: no cover negtypes.append(b[0]) startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) if enddate < startdate: # pragma: no cover s = enddate enddate = startdate startdate = s # make sure the dates are not naive try: startdate = pytz.utc.localize(startdate) except (ValueError, AttributeError): # pragma: no cover pass try: enddate = pytz.utc.localize(enddate) except (ValueError, AttributeError): # pragma: no cover pass negtypes = [] for b in mytypes.boattypes: if b[0] not in waterboattype: # pragma: no cover negtypes.append(b[0]) if theteam is not None and (theteam.viewing == 'allmembers' or theteam.manager == request.user): # pragma: no cover workouts = Workout.objects.filter(team=theteam, startdatetime__gte=startdate, startdatetime__lte=enddate, workouttype__in=modalities, ) elif theteam is not None and theteam.viewing == 'coachonly': # pragma: no cover 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, ) if firstworkout: workouts = firstworkoutquery | workouts workouts = workouts.order_by( "-date", "-starttime" ).exclude(boattype__in=negtypes) if rankingonly: # pragma: no cover workouts = workouts.exclude(rankingpiece=False) query = request.POST.get('q') if query: # pragma: no cover query_list = query.split() try: 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}) except TypeError: searchform = SearchForm() else: searchform = SearchForm() if request.method != 'POST': form = WorkoutMultipleCompareForm() if id: form.fields["workouts"].initial = [firstworkout] 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, }) if r.birthdate: age = calculate_age(r.birthdate) else: age = 0 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, 'worldclass':worldclass, 'age':age, 'sex':r.sex, 'weightcategory':r.weightcategory, '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'] cpfit = options['cpfit'] piece = options['piece'] 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: # pragma: no cover 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 = timezone.now() 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, AttributeError): # pragma: no cover return ('','Error: not enough data') else: # pragma: no cover 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: # pragma: no cover 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.loc[mask,'groupval'] = np.nan groupcols = df['groupval'] except (ValueError, AttributeError): # pragma: no cover df['groupval'] = groups.mean()[groupby].fillna(value=0) df.loc[mask,'groupval'] = np.nan groupcols = df['groupval'] except KeyError: # pragma: no cover messages.error(request,'Data selection error') url = reverse(user_multiflex_select) return HttpResponseRedirect(url) else: # pragma: no cover 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: # pragma: no cover 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: # pragma: no cover clegendy = df.index.min()+clegendx*(df.index.max()-df.index.min()) colorlegend = zip(range(6),clegendy,legcolors) if userid == 0: extratitle = '' else: # pragma: no cover 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 cpdata(workouts, options): userid = options['userid'] cpfit = options['cpfit'] cpoverlay = options['cpoverlay'] u = User.objects.get(id=userid) r = u.rower ids = [w.id for w in workouts] delta, cpvalue, avgpower,workoutnames,urls = dataprep.fetchcp_new(r,workouts) powerdf = pd.DataFrame({ 'Delta':delta, 'CP':cpvalue, 'workout':workoutnames, 'url':urls, }) if powerdf.empty: # pragma: no cover return('','
No valid data found
') 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 wcdurations = [] wcpower = [] if len(powerdf) !=0 : datefirst = pd.Series(w.date for w in workouts).min() datelast = pd.Series(w.date for w in workouts).max() title = 'CP chart for {name}, from {d1} to {d2}'.format( name = rowername, d1 = datefirst, d2 = datelast, ) wtype = 'water' if workouts[0].workouttype in mytypes.otetypes: # pragma: no cover wtype = 'erg' if workouts[0].workouttype == 'bikeerg': # pragma: no cover # for Mike wtype = 'erg' if cpoverlay: if r.birthdate: age = calculate_age(r.birthdate) else: # pragma: no cover worldclasspower = None age = 0 agerecords = CalcAgePerformance.objects.filter( age = age, sex = r.sex, weightcategory = r.weightcategory ) if len(agerecords) == 0: wcpower = [] wcdurations = [] else: # pragma: no cover wcdurations = [] wcpower = [] for record in agerecords: recordpower = record.power if wtype == 'water': recordpower = record.power*(100.-r.otwslack)/100. wcdurations.append(record.duration) wcpower.append(recordpower) res = interactive_otwcpchart(powerdf,promember=True,rowername=rowername,r=r, cpfit=cpfit,title=title,type=wtype, cpoverlay=cpoverlay, wcdurations=wcdurations,wcpower=wcpower) script = res[0] div = res[1] p1 = res[2] ratio = res[3] paulslope = 1 paulintercept = 1 message = res[4] else: # pragma: no cover script = '' div = 'No ranking pieces found.
' paulslope = 1 paulintercept = 1 p1 = [1,1,1,1] ratio = 1 message = "" scripta = script.split('\n')[2:-1] script = ''.join(scripta) minutes = options['piece'] if minutes != 0: # minutes = 77 try: hourvalue,tvalue = divmod(minutes,60) except: # pragma: no cover hourvalue = 0 tvalue = minutes # hourvalue = 1, tvalue = 17 try: hourvalue = int(hourvalue) except TypeError: # pragma: no cover hourvalue = 0 try: minutevalue = int(tvalue) except TypeError: # pragma: no cover minutevalue = 0 tvalue = int(60*(tvalue-minutevalue)) if hourvalue >= 24: # pragma: no cover hourvalue = 23 pieceduration = datetime.time( minute = minutevalue, hour = hourvalue, second = tvalue, ) pieceseconds = 3600.*pieceduration.hour+60.*pieceduration.minute+pieceduration.second # CP model pwr = p1[0]/(1+pieceseconds/p1[2]) pwr += p1[1]/(1+pieceseconds/p1[3]) if pwr <= 0: # pragma: no cover pwr = 50. if not np.isnan(pwr): try: pwr2 = pwr*ratio except: # pragma: no cover pwr2 = pwr duration = timedeltaconv(pieceseconds) power = int(pwr) upper = int(pwr2) else: # pragma: no cover duration = timedeltaconv(0) power = 0 upper = 0 htmly = env.get_template('otwcp.html') html_content = htmly.render({ 'script':script, 'the_div':div, 'duration':duration, 'power':power, 'upper':upper, }) return (script,html_content) 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) try: datadf['pace'] = datadf['pace']/1000. except KeyError: # pragma: no cover pass # 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: # pragma: no cover 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: # pragma: no cover 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, workstrokesonly=workstrokesonly, 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: # pragma: no cover 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): is_ajax = request_is_ajax(request) if settings.TESTING: is_ajax = True if not is_ajax: # pragma: no cover url = reverse('analysis_new') return HttpResponseRedirect(url) if 'options' in request.session: options = request.session['options'] else: # pragma: no cover options = defaultoptions if userid==0: userid = request.user.id workouts = [] try: ids = options['ids'] except KeyError: # pragma: no cover return JSONResponse({ "script":'', "div":'No data found' }) function = options['function'] if not ids: # pragma: no cover return JSONResponse({ "script":'', "div":'No data found' }) for id in ids: try: workouts.append(Workout.objects.get(id=id)) except Workout.DoesNotExist: # pragma: no cover pass if function == 'boxplot': script, div = boxplotdata(workouts,options) elif function == 'trendflex': # pragma: no cover script, div = trendflexdata(workouts, options,userid=userid) elif function == 'histo': # pragma: no cover script, div = histodata(workouts, options) elif function == 'flexall': # pragma: no cover script,div = flexalldata(workouts,options) elif function == 'stats': # pragma: no cover script,div = statsdata(workouts,options) elif function == 'compare': # pragma: no cover script,div = comparisondata(workouts,options) elif function == 'cp': # pragma: no cover script, div = cpdata(workouts, options) else: # pragma: no cover script = '' div = 'Unknown analysis functions' return JSONResponse({ "script":script, "div":div, }) def planrequired_view(request): messages.info(request,"This functionality requires Coach or Self-Coach membership") return HttpResponseRedirect(reverse('paidplans_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) def create_marker_workouts_view(request,userid=0, startdate=timezone.now()-timezone.timedelta(days=42), enddate=timezone.now()): therower = getrequestrower(request,userid=userid) theuser = therower.user workouts = Workout.objects.filter(user=theuser.rower,date__gte=startdate, date__lte=enddate, workouttype__in=mytypes.rowtypes, duplicate=False).order_by('date') for workout in workouts: w2 = dataprep.check_marker(workout) url = reverse('goldmedalscores_view', kwargs={ 'userid':request.user.id, }) return HttpResponseRedirect(url) @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) def goldmedalscores_view(request,userid=0, startdate=timezone.now()-timezone.timedelta(days=365), enddate=timezone.now()): is_ajax = request_is_ajax(request) therower = getrequestrower(request,userid=userid) theuser = therower.user if request.method == 'POST': form = DateRangeForm(request.POST) if form.is_valid(): startdate = form.cleaned_data['startdate'] enddate = form.cleaned_data['enddate'] if startdate > enddate: # pragma: no cover s = enddate enddate = startdate startdate = s else: form = DateRangeForm(initial={ 'startdate':startdate, 'enddate':enddate, }) script, div, ids = goldmedalscorechart( theuser,startdate=startdate,enddate=enddate, ) bestworkouts = Workout.objects.filter(id__in=ids).order_by('-date') breadcrumbs = [ { 'url':'/rower/analysis', 'name':'Analysis', }, { 'url':reverse(goldmedalscores_view), 'name': 'Gold Medal Scores' } ] if is_ajax: # pragma: no cover response = json.dumps({ 'script':script, 'div':div, }) return(HttpResponse(response,content_type='application/json')) return render(request,'goldmedalscores.html', { 'rower':therower, 'active':'nav-analysis', 'chartscript':script, 'breadcrumbs':breadcrumbs, 'the_div':div, 'form':form, 'bestworkouts':bestworkouts, }) @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 trainingzones_view(request,userid=0): is_ajax = request_is_ajax(request) r = getrequestrower(request,userid=userid) enddate = timezone.now() startdate = enddate-datetime.timedelta(days=42) zones = 'hr' date_agg = 'week' yaxis = 'percentage' form = TrainingZonesForm({ 'startdate':startdate, 'enddate':enddate, 'zones':zones, 'dates':date_agg, 'yaxis':yaxis, }) if request.method == 'POST': # pragma: no cover form = TrainingZonesForm(request.POST) if form.is_valid(): startdate = form.cleaned_data['startdate'] enddate = form.cleaned_data['enddate'] zones = form.cleaned_data['zones'] date_agg = form.cleaned_data['dates'] yaxis = form.cleaned_data['yaxis'] if date_agg == 'week': startdate = startdate - datetime.timedelta(days = startdate.weekday()) else: # pragma: no cover startdate = startdate - datetime.timedelta(days = (startdate.day-1)) form = TrainingZonesForm(initial={ 'startdate':startdate, 'enddate':enddate, 'zones':zones, 'dates':date_agg, 'yaxis':yaxis, }) script = '' div = get_call() breadcrumbs = [ { 'url':'/rowers/analysis', 'name':'Analysis' }, { 'url':reverse('trainingzones_view'), 'name': 'Training Zones' } ] return render(request,'trainingzones.html', { 'active':'nav-analysis', 'breadcrumbs':breadcrumbs, 'rower':r, 'the_script':script, 'the_div':div, 'form':form, 'startdate':startdate, 'enddate':enddate, 'zones':zones, 'dates': date_agg, 'yaxis': yaxis, } ) @login_required() def trainingzones_view_data(request,userid=0): r = getrequestrower(request,userid=userid) startdate = timezone.now()-datetime.timedelta(days=365) enddate = timezone.now() zones = 'hr' date_agg = 'week' yaxis = 'percentage' zones = request.GET.get('zones',zones) date_agg = request.GET.get('dates',date_agg) yaxis = request.GET.get('yaxis',yaxis) if request.GET.get('startdate'): startdate = datetime.datetime.strptime(request.GET.get('startdate'),"%Y-%m-%d") startdate = arrow.get(startdate).datetime if request.GET.get('enddate'): enddate = datetime.datetime.strptime(request.GET.get('enddate'),"%Y-%m-%d") enddate = arrow.get(enddate).datetime data = get_zones_report(r,startdate,enddate,trainingzones=zones,date_agg=date_agg,yaxis=yaxis) script, div = interactive_zoneschart(r,data,startdate,enddate,trainingzones=zones,date_agg=date_agg,yaxis=yaxis) 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 performancemanager_view(request,userid=0,mode='rower', startdate=timezone.now()-timezone.timedelta(days=365), enddate=timezone.now()): is_ajax = request_is_ajax(request) therower = getrequestrower(request,userid=userid) theuser = therower.user kfitness = therower.kfit kfatigue = therower.kfatigue fitnesstest = 20 metricchoice = 'hrtss' modelchoice = 'tsb' usegoldmedalstandard = False doform = therower.showfresh dofatigue = therower.showfit if request.method == 'POST': form = PerformanceManagerForm(request.POST) if form.is_valid(): startdate = form.cleaned_data['startdate'] enddate = form.cleaned_data['enddate'] metricchoice = form.cleaned_data['metricchoice'] dofatigue = form.cleaned_data['dofatigue'] doform = form.cleaned_data['doform'] therower.showfresh = doform therower.showfatigue = dofatigue therower.save() else: form = PerformanceManagerForm(initial={ 'doform':doform, 'dofatigue':dofatigue, }) script, thediv, endfitness, endfatigue, endform, ids = performance_chart( theuser,startdate=startdate,enddate=enddate, kfitness = kfitness, kfatigue = kfatigue, metricchoice = metricchoice, doform = doform, dofatigue = dofatigue, showtests = True, ) ids = pd.Series(ids,dtype='int').dropna().values bestworkouts = Workout.objects.filter(id__in=ids).order_by('-date') breadcrumbs = [ { 'url':'/rowers/analysis', 'name':'Analysis' }, { 'url':reverse('performancemanager_view'), 'name': 'Performance Manager' } ] if is_ajax: # pragma: no cover response = json.dumps({ 'script':script, 'div':thediv, 'endform':int(endform), 'endfitness': int(endfitness), 'endfatigue': int(endfatigue), }) return(HttpResponse(response,content_type='application/json')) return render(request,'performancemanager.html', { 'rower':therower, 'active':'nav-analysis', 'chartscript':script, 'breadcrumbs':breadcrumbs, 'the_div':thediv, 'mode':mode, 'form':form, 'endfitness':int(endfitness), 'endfatigue':int(endfatigue), 'endform':int(endform), 'bestworkouts':bestworkouts, }) @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: # pragma: no cover startdate = enddate-datetime.timedelta(days=int(deltadays)) if startdatestring != "": # pragma: no cover startdate = iso8601.parse_date(startdatestring) if enddatestring != "": # pragma: no cover enddate = iso8601.parse_date(enddatestring) if enddate < startdate: # pragma: no cover s = enddate enddate = startdate startdate = s if userid == 0: userid = request.user.id else: lastupdated = "1900-01-01" promember=0 r = getrequestrower(request,userid=userid) theuser = r.user wcdurations = [] wcpower = [] lastupdated = "1900-01-01" 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: # pragma: no cover 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: # pragma: no cover 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 = [] wcdurations = [] 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: # pragma: no cover s = enddate enddate = startdate startdate = s elif request.method == 'POST' and "datedelta" in request.POST: # pragma: no cover 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: # pragma: no cover 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) startdate = arrow.get(startdate).datetime enddate = arrow.get(enddate).datetime 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],dtype='float')*3 except TypeError: # pragma: no cover age = 0 else: script = '' div = 'No ranking pieces found.
' paulslope = 1 paulintercept = 1 p1 = [1,1,1,1] message = "" if request.method == 'POST' and "piece" in request.POST: # pragma: no cover 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) try: pwr = int(pwr) except (ValueError, AttributeError): # pragma: no cover pwr = 0 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: # pragma: no cover pwr2 = 50. velo2 = (pwr2/2.8)**(1./3.) if np.isnan(velo2) or velo2 <= 0: # pragma: no cover velo2 = 1.0 t2 = rankingdistance/velo2 pwr3 = p1[0]/(1+t2/p1[2]) pwr3 += p1[1]/(1+t2/p1[3]) if pwr3 <= 0: # pragma: no cover pwr3 = 50. velo3 = (pwr3/2.8)**(1./3.) if np.isnan(velo3) or velo3 <= 0: # pragma: no cover 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: # pragma: no cover pass # CP model pwr = p1[0]/(1+t/p1[2]) pwr += p1[1]/(1+t/p1[3]) if pwr <= 0: # pragma: no cover pwr = 50. velo = (pwr/2.8)**(1./3.) if np.isnan(velo) or velo <=0: # pragma: no cover 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), }) @login_required() def otecp_toadmin_view(request,theuser=0, startdate=timezone.now()-datetime.timedelta(days=365), enddate=timezone.now(), startdatestring="", enddatestring="", ): # pragma: no cover if startdatestring != "": # pragma: no cover try: startdate = iso8601.parse_date(startdatestring) except ParseError: pass if enddatestring != "": # pragma: no cover try: enddate = iso8601.parse_date(enddatestring) except ParseError: pass if theuser == 0: # pragma: no cover 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="", ): # pragma: no cover 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 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: # pragma: no cover 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: # pragma: no cover userid = request.user.id alert = Alert.objects.get(id=id) nperiod = int(nperiod) try: alert = Alert.objects.get(id=id) except Alert.DoesNotExist: # pragma: no cover raise Http404("This alert doesn't exist") if not checkalertowner(alert,request.user): # pragma: no cover raise PermissionDenied('You are not allowed to edit this Alert') stats = alert_get_stats(alert,nperiod=nperiod) is_ajax = request_is_ajax(request) if not 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', }, ] # pragma: no cover return render(request,'alert_stats.html', { 'breadcrumbs':breadcrumbs, 'stats':stats, 'rower':r, 'alert':alert, 'nperiod':nperiod, }) # pragma: no cover # 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: # pragma: no cover raise Http404("This alert doesn't exist") if alert.manager != request.user: # pragma: no cover raise PermissionDenied('You are not allowed to edit this Alert') FilterFormSet = formset_factory(ConditionEditForm, formset=BaseConditionFormSet,extra=0) if len(alert.filter.all()) == 0: # pragma: no cover 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_required = 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: # pragma: no cover 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: # pragma: no cover 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) time_min = datetime.time(hour=0,minute=0,second=0) time_max = datetime.time(hour=23,minute=59,second=59) if request.GET.get('startdate'): startdate,enddate = get_dates_timeperiod(request) sstartdate = startdate senddate = enddate activity_startdate = usertimezone.localize(timezone.datetime.combine(startdate,time_min)) activity_enddate = usertimezone.localize(timezone.datetime.combine(enddate,time_max)) else: activity_enddate = timezone.now() activity_enddate = usertimezone.localize(timezone.datetime.combine(activity_enddate.date(),time_max)) startdate = timezone.now()-datetime.timedelta(days=14) activity_startdate = usertimezone.localize(timezone.datetime.combine(startdate.date(),time_min)) sstartdate = activity_startdate.date senddate = activity_enddate.date startdate = sstartdate enddate = senddate typeselect = 'All' if request.GET.get('workouttype'): typeselect = request.GET.get('workouttype') yaxis = request.GET.get('yaxis','duration') if typeselect not in mytypes.checktypes: typeselect = 'All' form = HistorySelectForm(initial={'startdate':activity_startdate, 'enddate':activity_enddate, 'workouttype':typeselect, 'yaxis':yaxis}) 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'] tscript,tdiv = interactive_workouttype_piechart(g_workouts) totalmeters,totalhours, totalminutes, totalseconds = get_totals(g_workouts) # meters, duration per workout type wtypes = list(set([w.workouttype for w in g_workouts])) typechoices = [("All","All")] for wtype in wtypes: if wtype in mytypes.checktypes: 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,wseconds = get_totals(a_workouts) ddict = {} ddict['id'] = wtype try: ddict['wtype'] = mytypes.workouttypes_ordered[wtype] except KeyError: # pragma: no cover ddict['wtype'] = wtype ddict['distance'] = wmeters ddict['duration'] = "{whours}:{wminutes:02d}:{wseconds:02d}".format( whours=whours, wminutes=wminutes, wseconds=wseconds, ) ddict['nrworkouts'] = a_workouts.count() listofdicts.append(ddict) # interactive hr pie chart totalscript = '' totaldiv = get_call() totalsdict = {} totalsdict['duration'] = "{totalhours:02d}:{totalminutes:02d}:{totalseconds:02d}".format( totalhours=totalhours, totalminutes=totalminutes, totalseconds=totalseconds, ) totalsdict['distance'] = totalmeters 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() lastyear = datetime.datetime(year=today.year-1,month=today.month,day=today.day) firstmay = datetime.datetime(year=today.year,month=5,day=1).astimezone(usertimezone) if firstmay>today: # pragma: no cover firstmay = datetime.datetime(year=today.year-1,month=5,day=1) return render(request,'history.html', { 'tscript':tscript, 'tdiv':tdiv, '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, 'lastyear':lastyear, 'today':today, 'workouttype':typeselect, 'yaxis':yaxis, 'firstmay':firstmay, 'sstartdate':sstartdate, 'senddate':senddate, }) @login_required() def history_view_data(request,userid=0): r = getrequestrower(request,userid=userid) usertimezone = pytz.timezone(r.defaulttimezone) time_min = datetime.time(hour=0,minute=0,second=0) time_max = datetime.time(hour=23,minute=59,second=59) startdate = timezone.now()-datetime.timedelta(days=14) enddate = timezone.now() activity_enddate = usertimezone.localize(timezone.datetime.combine(enddate.date(),time_max)) activity_startdate = usertimezone.localize(timezone.datetime.combine(startdate.date(),time_min)) if request.GET.get('startdate'): startdate = datetime.datetime.strptime(request.GET.get('startdate'),"%Y-%m-%d") activity_startdate = usertimezone.localize(timezone.datetime.combine(startdate,time_min)) if request.GET.get('enddate'): enddate = datetime.datetime.strptime(request.GET.get('enddate'),"%Y-%m-%d") activity_enddate = usertimezone.localize(timezone.datetime.combine(enddate,time_max)) typeselect = 'All' if request.GET.get('workouttype'): typeselect = request.GET.get('workouttype') if typeselect not in mytypes.checktypes: typeselect = 'All' yaxis = request.GET.get('yaxis','duration') if yaxis.lower() not in ['duration','rscore','trimp']: # pragma: no cover yaxis = 'duration' 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().clip(lower=0) except KeyError: pass df = dataprep.clean_df_stats(df,workstrokesonly=True, ignoreadvanced=True,ignorehr=False) totalmeters,totalhours, totalminutes,totalseconds = get_totals(g_workouts) # meters, duration per workout type wtypes = list(set([w.workouttype for w in g_workouts])) typechoices = [("All","All")] for wtype in wtypes: if wtype in mytypes.checktypes: typechoices.append((wtype,mytypes.workouttypes_ordered[wtype])) listofdicts = [] for wtype in wtypes: a_workouts = g_workouts.filter(workouttype=wtype) wmeters, whours, wminutes,wseconds = get_totals(a_workouts) ddict = {} try: # pragma: no cover ddict['wtype'] = mytypes.workouttypes_ordered[wtype] except KeyError: # pragma: no cover ddict['wtype'] = wtype ddict['id'] = wtype ddict['distance'] = wmeters ddict['duration'] = "{whours}:{wminutes:02d}:{wseconds:02d}".format( whours=whours, wminutes=wminutes,wseconds=wseconds, ) ddf = getsmallrowdata_db(columns,ids=[w.id for w in a_workouts]) try: ddf['deltat'] = ddf['time'].diff().clip(lower=0) except KeyError: # pragma: no cover pass ddf = dataprep.clean_df_stats(ddf,workstrokesonly=False, ignoreadvanced=True) ddict['hrmean'] = int(wavg(ddf,'hr','deltat')) try: ddict['hrmax'] = ddf['hr'].max().astype(int) except (KeyError, ValueError, AttributeError): # pragma: no cover ddict['hrmax'] = 0 ddict['powermean'] = int(wavg(ddf,'power','deltat')) try: ddict['powermax'] = ddf['power'].max().astype(int) except KeyError: ddict['powermax'] = 0 ddict['nrworkouts'] = a_workouts.count() listofdicts.append(ddict) 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: # pragma: no cover totalsdict['hrmean'] = 0 totalsdict['hrmax'] = 0 totalsdict['nrworkouts'] = g_workouts.count() # activity chart activity_script, activity_div = interactive_activitychart2(g_workouts,startdate,enddate,yaxis=yaxis) # interactive hr pie chart if typeselect == 'All': totalseconds = 3600*totalhours+60*totalminutes+totalseconds totalscript,totaldiv = interactive_hr_piechart(df,r,'All Workouts', totalseconds=totalseconds) else: a_workouts = g_workouts.filter(workouttype=typeselect) meters, hours,minutes,seconds = get_totals(a_workouts) totalseconds = 3600*hours+60*minutes+seconds ddf = getsmallrowdata_db(columns,ids=[w.id for w in a_workouts]) try: ddf['deltat'] = ddf['time'].diff().clip(lower=0) except KeyError: pass ddf = dataprep.clean_df_stats(ddf,workstrokesonly=True, ignoreadvanced=True) totalscript, totaldiv = interactive_hr_piechart( ddf,r,mytypes.workouttypes_ordered[typeselect], totalseconds=totalseconds) return JSONResponse({ 'script':totalscript, 'div':totaldiv, 'totalsdict':totalsdict, 'listofdicts':listofdicts, 'activities_script':activity_script, 'activities_chart':activity_div, })