From f18357923778cdbe4f6fdc9fb95c5562d12bf1cf Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 31 Oct 2019 17:17:03 +0100 Subject: [PATCH] improved data cleaning --- rowers/dataprep.py | 34 ++- rowers/views/workoutviews.py | 538 ++++++++++++++++++----------------- 2 files changed, 302 insertions(+), 270 deletions(-) diff --git a/rowers/dataprep.py b/rowers/dataprep.py index e62498b1..3f062027 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -353,7 +353,6 @@ def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True, ignoreadvanced=False): # clean data remove zeros and negative values - # bring metrics which have negative values to positive domain if len(datadf)==0: return datadf @@ -378,11 +377,30 @@ def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True, except (KeyError,TypeError) as e: pass + try: datadf = datadf.clip(lower=0) except TypeError: pass + # protect advanced metrics columns + advancedcols = [ + 'rhythm', + 'power', + 'drivelength', + 'forceratio', + 'drivespeed', + 'driveenergy', + 'catch', + 'finish', + 'averageforce', + 'peakforce', + 'slip', + 'wash', + 'peakforceangle', + 'effectiveangle', + ] + datadf.replace(to_replace=0, value=np.nan, inplace=True) # datadf = datadf.map_partitions(lambda df:df.replace(to_replace=0,value=np.nan)) @@ -392,6 +410,7 @@ def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True, except (TypeError,KeyError) as e: pass + # return from positive domain to negative try: datadf['catch'] = -datadf['catch'] @@ -422,12 +441,14 @@ def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True, except (KeyError,TypeError): pass + try: mask = datadf['efficiency'] > 200. datadf.mask(mask,inplace=True) except (KeyError,TypeError): pass + try: mask = datadf['spm'] < 10 datadf.mask(mask,inplace=True) @@ -459,15 +480,23 @@ def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True, except (KeyError,TypeError): pass + try: mask = datadf['wash'] > 1 datadf.loc[mask, 'wash'] = np.nan except (KeyError,TypeError): pass + # try to guess ignoreadvanced + if not ignoreadvanced: + for metric in advancedcols: + sum = datadf[metric].std() + if sum == 0 or np.isnan(sum): + ignoreadvanced = True + if not ignoreadvanced: try: - mask = datadf['rhythm'] < 5 + mask = datadf['rhythm'] < 0 datadf.mask(mask,inplace=True) except (KeyError,TypeError): pass @@ -532,6 +561,7 @@ def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True, except (KeyError,TypeError): pass + workoutstateswork = [1, 4, 5, 8, 9, 6, 7] workoutstatesrest = [3] workoutstatetransition = [0, 2, 10, 11, 12, 13] diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index f8c0efbf..4fd22411 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -58,7 +58,7 @@ def workout_forcecurve_view(request,id=0,workstrokesonly=False): 'url':reverse('workout_forcecurve_view',kwargs={'id':id}), 'name': 'Empower Force Curve' } - + ] r = getrower(request.user) @@ -110,7 +110,7 @@ def workout_histo_view(request,id=0): if not promember: return HttpResponseRedirect("/rowers/about/") - + res = interactive_histoall([w],'power',False) script = res[0] div = res[1] @@ -128,7 +128,7 @@ def workout_histo_view(request,id=0): 'url':reverse('workout_histo_view',kwargs={'id':id}), 'name': 'Histogram' } - + ] @@ -163,7 +163,7 @@ def addmanual_view(request): 'name': 'Add Manual Entry' }, ] - + if request.method == 'POST': # Form was submitted form = WorkoutForm(request.POST) @@ -190,7 +190,7 @@ def addmanual_view(request): ps = form.cleaned_data['plannedsession'] except KeyError: ps = None - + try: boattype = request.POST['boattype'] except KeyError: @@ -208,7 +208,7 @@ def addmanual_view(request): duplicate = form.cleaned_data['duplicate'] except KeyError: duplicate = False - + if private: privacy = 'private' else: @@ -223,7 +223,7 @@ def addmanual_view(request): ) - + id,message = dataprep.create_row_df(r, distance, duration,startdatetime, @@ -257,7 +257,7 @@ def addmanual_view(request): w.save() if ps: add_workouts_plannedsession([w],ps,w.user) - + messages.info(request,'New workout created') url = reverse( @@ -280,7 +280,7 @@ def addmanual_view(request): 'timezone':r.defaulttimezone, 'duration':datetime.timedelta(minutes=2), 'distance':500, - + } form = WorkoutForm(initial=initial) metricsform = MetricsForm() @@ -302,14 +302,14 @@ def fitness_metric_view(request,mode='rower',days=42): if not ms: url = reverse('workouts_view') return HttpResponseRedirect(url) - + max_workout_id = max([m.last_workout for m in ms]) last_update_date = max([m.date.strftime('%Y-%m-%d') for m in ms]) - + now_date = timezone.now().strftime('%Y-%m-%d') - + if mode == 'rower': workouts = Workout.objects.filter( user=r, @@ -332,7 +332,7 @@ def fitness_metric_view(request,mode='rower',days=42): handle_updatefitnessmetric, request.user.id,mode,theids, ) - + return HttpResponse("job queued") @@ -355,7 +355,7 @@ def workout_update_cp_view(request,id=0): r = getrower(request.user) dataprep.runcpupdate(r) - + if row.workouttype in mytypes.otwtypes: url = reverse('otwrankings_view') else: @@ -394,7 +394,7 @@ def workout_recalcsummary_view(request,id=0): kwargs = { 'id':id, }) - + return HttpResponseRedirect(url) @@ -410,7 +410,7 @@ def workouts_join_view(request): if result: promember=1 - + if request.method == 'POST' and 'workouts' in request.POST: form = WorkoutMultipleCompareForm(request.POST) paramform = WorkoutJoinParamForm(request.POST) @@ -427,7 +427,7 @@ def workouts_join_view(request): id,message = dataprep.join_workouts(r,ids, title=workout_name, setprivate=set_private) - + if message: messages.error(request,message) @@ -435,12 +435,12 @@ def workouts_join_view(request): kwargs = { 'id':encoder.encode_hex(id), }) - + return HttpResponseRedirect(url) - + else: return HttpResponse("form is not valid") - + else: url = reverse('workouts_join_select') return HttpResponseRedirect(url) @@ -468,7 +468,7 @@ def workouts_join_select(request, else: waterboattype = mytypes.waterboattype - + if 'modalities' in request.session: modalities = request.session['modalities'] if len(modalities) > 1: @@ -478,7 +478,7 @@ def workouts_join_select(request, else: modalities = [m[0] for m in mytypes.workouttypes] modality = 'all' - + if request.method == 'POST': dateform = DateRangeForm(request.POST) modalityform = TrendFlexModalForm(request.POST) @@ -500,7 +500,7 @@ def workouts_join_select(request, if modality != 'water': waterboattype = [b[0] for b in mytypes.boattypes] - + request.session['modalities'] = modalities request.session['waterboattype'] = waterboattype else: @@ -509,13 +509,13 @@ def workouts_join_select(request, '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)) #enddate = enddate+datetime.timedelta(days=1) @@ -534,7 +534,7 @@ def workouts_join_select(request, theteam = Team.objects.get(id=teamid) except Team.DoesNotExist: theteam = 0 - + if r.rowerplan == 'basic' and theteam==0: raise PermissionDenied("Access denied") @@ -549,7 +549,7 @@ def workouts_join_select(request, startdatetime__lte=enddate, workouttype__in=modalities).order_by("-date","-starttime").exclude(boattype__in=negtypes) - + else: theteam = None workouts = Workout.objects.filter(user=r, @@ -577,7 +577,7 @@ def workouts_join_select(request, theid = theteam.id else: theid = 0 - + joinparamform = WorkoutJoinParamForm() modalityform = TrendFlexModalForm(initial={ 'modality':modality, @@ -587,7 +587,7 @@ def workouts_join_select(request, messages.info(request,successmessage) messages.error(request,message) - + return render(request, 'workout_join_select.html', {'workouts': workouts, 'dateform':dateform, @@ -631,7 +631,7 @@ def team_comparison_select(request, rankingonly = request.session['rankingonly'] else: rankingonly = False - + if 'modalities' in request.session: modalities = request.session['modalities'] if len(modalities) > 1: @@ -641,7 +641,7 @@ def team_comparison_select(request, else: modalities = [m[0] for m in mytypes.workouttypes] modality = 'all' - + if request.method == 'POST': dateform = DateRangeForm(request.POST) if dateform.is_valid(): @@ -669,7 +669,7 @@ def team_comparison_select(request, rankingonly = modalityform.cleaned_data['rankingonly'] else: rankingonly = False - + request.session['modalities'] = modalities request.session['waterboattype'] = waterboattype else: @@ -683,14 +683,14 @@ def team_comparison_select(request, 'rankingonly':rankingonly, }) - - + + 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) @@ -709,7 +709,7 @@ def team_comparison_select(request, theteam = Team.objects.get(id=teamid) except Team.DoesNotExist: theteam = 0 - + if theteam and (theteam.viewing == 'allmembers' or theteam.manager == request.user): workouts = Workout.objects.filter(team=theteam, startdatetime__gte=startdate, @@ -721,7 +721,7 @@ def team_comparison_select(request, startdatetime__lte=enddate, workouttype__in=modalities).order_by("-date","-starttime").exclude(boattype__in=negtypes) - + else: theteam = None workouts = Workout.objects.filter(user=r, @@ -754,24 +754,24 @@ def team_comparison_select(request, workouts = firstworkoutquery | workouts else: firstworkout = None - + form = WorkoutMultipleCompareForm() form.fields["workouts"].queryset = workouts if id: form.fields["workouts"].initial = [firstworkout] - - + + if theteam: theid = theteam.id else: theid = 0 - + chartform = ChartParamChoiceForm(initial={'teamid':0}) messages.info(request,successmessage) messages.error(request,message) - + if id: breadcrumbs = [ { @@ -797,7 +797,7 @@ def team_comparison_select(request, 'url':reverse('team_comparison_select',kwargs={'teamid':teamid}), 'name': 'Compare Select' }, - + ] return render(request, 'team_compare_select.html', @@ -857,15 +857,15 @@ def virtualevent_compare_view(request,id=0): }) messages.info(request,'There are no results to display') - + return HttpResponseRedirect(url) - + if request.method == 'GET': xparam = race.sessionmode if race.sessionmode in ['distance','time'] else 'time' yparam = 'pace' plottype = 'line' - + request.session['ids'] = workoutids @@ -904,7 +904,7 @@ def virtualevent_compare_view(request,id=0): workoutids = request.session['ids'] except KeyError: pass - + request.session['ids'] = workoutids workouts = [] for id in workoutids: @@ -919,8 +919,8 @@ def virtualevent_compare_view(request,id=0): } else: raise HttpResponse('Only GET and POST allowed',status=405) - - + + res = interactive_multiple_compare_chart(workoutids,xparam,yparam, promember=promember, plottype=plottype, @@ -968,12 +968,12 @@ def virtualevent_compare_view(request,id=0): 'chartform':chartform, 'teams':[] }) - + @login_required() def plannedsession_compare_view(request,id=0,userid=0): r = getrequestrower(request,userid=userid) - + try: ps = PlannedSession.objects.get(id=id) except PlannedSession.DoesNotExist: @@ -995,7 +995,7 @@ def plannedsession_compare_view(request,id=0,userid=0): workouts = Workout.objects.filter(plannedsession=ps) ids = [int(w.id) for w in workouts] - + labeldict = { int(w.id): w.__str__() for w in workouts } @@ -1090,7 +1090,7 @@ def multi_compare_view(request,id=0,userid=0): 'teamid':teamid } ) - + else: url = reverse('team_comparison_select', kwargs={ @@ -1153,8 +1153,8 @@ def multi_compare_view(request,id=0,userid=0): 'name': 'Compare' } ] - - + + return render(request,'multicompare.html', {'interactiveplot':script, 'the_div':div, @@ -1168,7 +1168,7 @@ def multi_compare_view(request,id=0,userid=0): }) -# List Workouts +# List Workouts @login_required() def workouts_view(request,message='',successmessage='', teamid=0,rankingonly=False,rowerid=0,userid=0): @@ -1212,7 +1212,7 @@ def workouts_view(request,message='',successmessage='', enddate = startdate startdate = s - + startdatestring = startdate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d') @@ -1231,7 +1231,7 @@ def workouts_view(request,message='',successmessage='', g_startdate = activity_startdate g_enddate = activity_enddate - + if teamid: try: theteam = Team.objects.get(id=teamid) @@ -1297,7 +1297,7 @@ def workouts_view(request,message='',successmessage='', g_enddate = timezone.now() g_startdate = (timezone.now()-timedelta(days=15)) - + if rankingonly: workouts = workouts.exclude(rankingpiece=False) @@ -1308,8 +1308,8 @@ def workouts_view(request,message='',successmessage='', # ids = [w.id for w in workouts] # df = dataprep.getsmallrowdata_db(['time','power'],ids=ids) # polarization = dataprep.polarization_index(df,r) - - + + if query: query_list = query.split() workouts = workouts.filter( @@ -1351,7 +1351,7 @@ def workouts_view(request,message='',successmessage='', g_enddate, stack=stack) - + messages.info(request,successmessage) messages.error(request,message) @@ -1361,7 +1361,7 @@ def workouts_view(request,message='',successmessage='', 'name':'Workouts' }, ] - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') + timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') return render(request, 'list_workouts.html', {'workouts': workouts, 'active': 'nav-workouts', @@ -1417,7 +1417,7 @@ def workout_fusion_list(request,id=0,message='',successmessage='', startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) #enddate = enddate+datetime.timedelta(days=1) - + if enddate < startdate: s = enddate enddate = startdate @@ -1425,7 +1425,7 @@ def workout_fusion_list(request,id=0,message='',successmessage='', if id: theid = encoder.decode_hex(id) - + workouts = Workout.objects.filter(user=r, startdatetime__gte=startdate, startdatetime__lte=enddate).order_by("-date", "-starttime").exclude(id=theid) @@ -1442,7 +1442,7 @@ def workout_fusion_list(request,id=0,message='',successmessage='', searchform = SearchForm(initial={'q':query}) else: searchform = SearchForm() - + paginator = Paginator(workouts,15) # show 25 workouts per page page = request.GET.get('page') @@ -1453,10 +1453,10 @@ def workout_fusion_list(request,id=0,message='',successmessage='', except EmptyPage: workouts = paginator.page(paginator.num_pages) row = get_workout(id) - + messages.info(request,successmessage) messages.error(request,message) - + breadcrumbs = [ { 'url':'/rowers/list-workouts/', @@ -1470,9 +1470,9 @@ def workout_fusion_list(request,id=0,message='',successmessage='', 'url':reverse('workout_fusion_list',kwargs={'id':id}), 'name': 'Sensor Fusion' } - + ] - + return render(request, 'fusion_list.html', {'id':id, 'workout':row, @@ -1488,21 +1488,21 @@ def workout_fusion_list(request,id=0,message='',successmessage='', 'enddate':enddate, 'teams':get_my_teams(request.user), }) - + # Basic view of workout def workout_view(request,id=0): request.session['referer'] = absolute(request)['PATH'] - + if not request.user.is_anonymous: rower = getrower(request.user) else: rower = None - + try: row = Workout.objects.get(id=encoder.decode_hex(id)) except Workout.DoesNotExist: - raise Http404("Workout doesn't exist") - + raise Http404("Workout doesn't exist") + comments = WorkoutComment.objects.filter(workout=row) aantalcomments = len(comments) @@ -1510,7 +1510,7 @@ def workout_view(request,id=0): if row.privacy == 'private' and not checkworkoutuser(request.user,row): raise PermissionDenied("Access denied") - + g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime") for i in g: try: @@ -1520,9 +1520,9 @@ def workout_view(request,id=0): i.save() except: pass - - + + # create interactive plot res = interactive_chart(encoder.decode_hex(id)) script = res[0] @@ -1548,7 +1548,7 @@ def workout_view(request,id=0): mapscript,mapdiv = leaflet_chart(rowdata.df[' latitude'], rowdata.df[' longitude'], row.name) - + else: mapscript = "" @@ -1563,14 +1563,14 @@ def workout_view(request,id=0): 'url':reverse('workout_view',kwargs={'id':id}), 'name': row.name, } - + ] u = row.user.user recordsindoor = IndoorVirtualRaceResult.objects.filter(workoutid= row.id) records = VirtualRaceResult.objects.filter(workoutid= row.id) - + return render(request, 'workout_view.html', {'workout':row, 'rower':rower, @@ -1587,7 +1587,7 @@ def workout_view(request,id=0): 'mapdiv':mapdiv, 'teams':get_my_teams(request.user), 'the_div':div}) - + # Resets stroke data to raw data (pace) @user_passes_test(ispromember,login_url="/rowers/paidplans", @@ -1627,7 +1627,7 @@ def workout_undo_smoothenpace_view( return HttpResponseRedirect(url) - + # Data smoothing of pace data @user_passes_test(ispromember,login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher", @@ -1638,7 +1638,7 @@ def workout_smoothenpace_view(request,id=0,message="",successmessage=""): previousurl = request.META.get('HTTP_REFERER') r = getrower(request.user) - + if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) @@ -1701,7 +1701,7 @@ def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""): 'url':reverse('workout_crewnerd_summary_view',kwargs={'id':id}), 'name': 'CrewNerd Summary' } - + ] if request.method == 'POST': @@ -1747,7 +1747,7 @@ def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""): 'id':row.id}) else: form = CNsummaryForm() - + return render(request, "cn_form.html", {'form':form, @@ -1757,7 +1757,7 @@ def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""): 'breadcrumbs':breadcrumbs, 'teams':get_my_teams(request.user), 'id':row.id}) - + # Get weather for given location and date/time @user_passes_test(ispromember,login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher", @@ -1787,7 +1787,7 @@ def workout_downloadwind_view(request,id=0, rowdata.write_csv(f1,gzip=True) # get wind - + try: avglat = rowdata.df[' latitude'].mean() avglon = rowdata.df[' longitude'].mean() @@ -1808,13 +1808,13 @@ def workout_downloadwind_view(request,id=0, except TypeError: if message is not None and row.notes is not None: row.notes += message - + row.save() rowdata.add_wind(windspeed,windbearing) rowdata.write_csv(f1,gzip=True) messages.info(request,message) - + kwargs = { 'id':id} @@ -1867,7 +1867,7 @@ def workout_downloadmetar_view(request,id=0, avgtime = int(rowdata.df['TimeStamp (sec)'].mean()-rowdata.df.loc[:,'TimeStamp (sec)'].iloc[0]) startdatetime = dateutil.parser.parse("{}, {}".format(row.date, row.starttime)) - + starttimeunix = arrow.get(row.startdatetime).timestamp #starttimeunix = int(mktime(startdatetime.utctimetuple())) avgtime = starttimeunix +avgtime @@ -1883,12 +1883,12 @@ def workout_downloadmetar_view(request,id=0, row.notes += message except TypeError: pass - + row.save() rowdata.add_wind(windspeed,windbearing) rowdata.write_csv(f1,gzip=True) messages.info(request,message) - + kwargs = { 'id':id} @@ -1926,7 +1926,7 @@ def workout_wind_view(request,id=0,message="",successmessage=""): 'url':reverse('workout_wind_view',kwargs={'id':id}), 'name': 'Wind' } - + ] if (checkworkoutuser(request.user,row)==False): @@ -1956,13 +1956,13 @@ def workout_wind_view(request,id=0,message="",successmessage=""): if hascoordinates and not latitude.std(): hascoordinates = 0 - + try: bearing = rowdata.df.loc[:,'bearing'].values except KeyError: rowdata.add_bearing() rowdata.write_csv(f1,gzip=True) - + if hascoordinates: avglat = rowdata.df[' latitude'].mean() @@ -1973,14 +1973,14 @@ def workout_wind_view(request,id=0,message="",successmessage=""): else: airportcode = 'UNKNOWN' airportdistance = 0 - + if request.method == 'POST': # process form form = UpdateWindForm(request.POST) if form.is_valid(): - + vwind1 = form.cleaned_data['vwind1'] vwind2 = form.cleaned_data['vwind2'] dist1 = form.cleaned_data['dist1'] @@ -1997,7 +1997,7 @@ def workout_wind_view(request,id=0,message="",successmessage=""): rowdata.write_csv(f1,gzip=True) - + else: message = "Invalid Form" messages.error(request,message) @@ -2006,7 +2006,7 @@ def workout_wind_view(request,id=0,message="",successmessage=""): } url = reverse('workout_wind_view',kwargs=kwargs) response = HttpResponseRedirect(url) - + else: form = UpdateWindForm() @@ -2027,7 +2027,7 @@ def workout_wind_view(request,id=0,message="",successmessage=""): messages.info(request,successmessage) messages.error(request,message) - + return render(request, 'windedit.html', {'workout':row, @@ -2042,7 +2042,7 @@ def workout_wind_view(request,id=0,message="",successmessage=""): 'the_div':div, 'gmap':gmscript, 'gmapdiv':gmdiv}) - + # Show form to update River stream data (for river dwellers) @user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher",redirect_field_name=None) @@ -2072,7 +2072,7 @@ def workout_stream_view(request,id=0,message="",successmessage=""): form = UpdateStreamForm(request.POST) if form.is_valid(): - + dist1 = form.cleaned_data['dist1'] dist2 = form.cleaned_data['dist2'] stream1 = form.cleaned_data['stream1'] @@ -2084,7 +2084,7 @@ def workout_stream_view(request,id=0,message="",successmessage=""): rowdata.write_csv(f1,gzip=True) - + else: message = "Invalid Form" messages.error(request,message) @@ -2092,7 +2092,7 @@ def workout_stream_view(request,id=0,message="",successmessage=""): 'id':id} url = reverse('workout_wind_view',kwargs=kwargs) response = HttpResponseRedirect(url) - + else: form = UpdateStreamForm() @@ -2114,7 +2114,7 @@ def workout_stream_view(request,id=0,message="",successmessage=""): 'url':reverse('workout_stream_view',kwargs={'id':id}), 'name': 'Stream' } - + ] messages.info(request,successmessage) @@ -2129,7 +2129,7 @@ def workout_stream_view(request,id=0,message="",successmessage=""): 'interactiveplot':script, 'form':form, 'the_div':div}) - + # Form to set average crew weight and boat type, then run power calcs @user_passes_test(ispromember, login_url="/rowers/paidplans",redirect_field_name=None) def workout_otwsetpower_view(request,id=0,message="",successmessage=""): @@ -2218,10 +2218,10 @@ def workout_otwsetpower_view(request,id=0,message="",successmessage=""): url = request.session['referer'] except KeyError: url = reverse('workout_edit_view',kwargs=kwargs) - + response = HttpResponseRedirect(url) return response - + else: message = "Invalid Form" messages.error(request,message) @@ -2229,7 +2229,7 @@ def workout_otwsetpower_view(request,id=0,message="",successmessage=""): 'id':id} url = reverse('workout_otwsetpower_view',kwargs=kwargs) response = HttpResponseRedirect(url) - + else: form = AdvancedWorkoutForm(instance=w) @@ -2246,10 +2246,10 @@ def workout_otwsetpower_view(request,id=0,message="",successmessage=""): 'url':reverse('workout_otwsetpower_view',kwargs={'id':id}), 'name': 'OTW Power' } - + ] - + messages.error(request,message) messages.info(request,successmessage) return render(request, @@ -2286,7 +2286,7 @@ def instroke_view(request,id=0): 'url':reverse('instroke_view',kwargs={'id':id}), 'name': 'In-Stroke Metrics' } - + ] # form = WorkoutForm(instance=row) @@ -2363,11 +2363,11 @@ def instroke_chart(request,id=0,metric=''): creationdatetime=timezone.now(), filename=fullpathimagename, width=width,height=height) - + i.save() else: messages.error(request,'You have reached the maximum number of static images for this workout. Delete an image first') - + r = getrower(request.user) url = reverse(r.defaultlandingpage, @@ -2401,7 +2401,7 @@ def workout_data_view(request, id=0): 'url':reverse('workout_data_view',kwargs={'id':id}), 'name': 'Data Explorer' } - + ] @@ -2416,11 +2416,11 @@ def workout_data_view(request, id=0): 'id','time','hr_an','hr_at','hr_bottom','hr_max', 'hr_tr','hr_ut1','hr_ut2','x_right', ] - + to_be_dropped = [c for c in to_be_dropped if c in columns] - + datadf.drop(labels=to_be_dropped,inplace=True,axis=1) - + cols = ['ftime','cumdist','fpace','spm', 'hr','power','driveenergy','drivelength','averageforce', @@ -2430,14 +2430,14 @@ def workout_data_view(request, id=0): tcols = ['ftime','cumdist','fpace','spm','hr','power'] - + datadf = datadf[cols] datadf.loc[:,'hr'] = datadf['hr'].astype('int') datadf.loc[:,'power'] = datadf['power'].astype('int') datadf.loc[:,'distance'] = datadf['distance'].astype('int') datadf.loc[:,'spm'] = 10*datadf['spm'].astype('int')/10. - + if request.method == 'POST': form = DataFrameColumnsForm(request.POST) if form.is_valid(): @@ -2447,7 +2447,7 @@ def workout_data_view(request, id=0): form = DataFrameColumnsForm(initial = {'cols':tcols}) datadf = datadf[tcols] - + for col in cols: try: if datadf[col].mean() == 0 and datadf[col].std() == 0: @@ -2457,7 +2457,7 @@ def workout_data_view(request, id=0): # pd.set_option('display.width', 1000) #pd.set_option('colheader_justify', 'left') - + htmltable = datadf.to_html( bold_rows=True, show_dimensions=True,border=1, @@ -2472,10 +2472,10 @@ def workout_data_view(request, id=0): 'teams':get_my_teams(request.user), 'workout': w, 'breadcrumbs': breadcrumbs, - + } ) - + # Stats page @login_required() @@ -2503,7 +2503,7 @@ def workout_stats_view(request,id=0,message="",successmessage=""): 'url':reverse('workout_stats_view',kwargs={'id':id}), 'name': 'Stats' } - + ] workstrokesonly = True @@ -2516,18 +2516,21 @@ def workout_stats_view(request,id=0,message="",successmessage=""): if (checkworkoutuserview(request.user,row)==False): raise PermissionDenied('Access Denied') - datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly) - + datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly, + ignoreadvanced=False) + + if datadf.empty: datadf,row = dataprep.getrowdata_db(id=encoder.decode_hex(id)) - datadf = dataprep.clean_df_stats(datadf,workstrokesonly=False) + datadf = dataprep.clean_df_stats(datadf,workstrokesonly=False, + ignoreadvanced=True) workstrokesonly=False if datadf.empty: return HttpResponse("CSV data file not found") #datadf['deltat'] = datadf['time'].diff() - - + + workoutstateswork = [1,4,5,8,9,6,7] workoutstatesrest = [3] workoutstatetransition = [0,2,10,11,12,13] @@ -2543,7 +2546,7 @@ def workout_stats_view(request,id=0,message="",successmessage=""): fielddict.pop('pace') except KeyError: pass - + for field,verbosename in fielddict.items(): thedict = { 'mean':datadf[field].mean(), @@ -2585,14 +2588,14 @@ def workout_stats_view(request,id=0,message="",successmessage=""): 'value':int(tss), 'unit':'' } - + if not np.isnan(normp): otherstats['np'] = { 'verbose_name':'rPower', 'value':int(10*normp)/10., 'unit':'Watt' } - + # HR Drift tmax = datadf['time'].max() tmin = datadf['time'].min() @@ -2602,7 +2605,7 @@ def workout_stats_view(request,id=0,message="",successmessage=""): hr1 = datadf.loc[mask1,'hr'].mean() hr2 = datadf.loc[mask2,'hr'].mean() - + pwr1 = datadf.loc[mask1,'power'].mean() pwr2 = datadf.loc[mask2,'power'].mean() @@ -2648,7 +2651,7 @@ def workout_stats_view(request,id=0,message="",successmessage=""): 'cordict':cordict, 'otherstats':otherstats, }) - + # Change default landing page @@ -2663,7 +2666,7 @@ def workflow_default_view(request): r.save() url = reverse('workout_workflow_config2_view') - + return HttpResponseRedirect(url) @@ -2676,13 +2679,13 @@ def workout_workflow_config2_view(request,userid=0): workoutid = request.session['lastworkout'] except KeyError: workoutid = 0 - - + + r = getrequestrower(request,userid=userid,notpermanent=True) MiddlePanelFormSet = formset_factory(WorkFlowMiddlePanelElement,extra=1) LeftPanelFormSet = formset_factory(WorkFlowLeftPanelElement,extra=1) - + if request.method == 'POST': wasmiddle = [1 for key,value in request.POST.items() if 'middlepanel' in key.lower()] @@ -2721,11 +2724,11 @@ def workout_workflow_config2_view(request,userid=0): r.save() except IntegrityError: messages.error(request,'Something went wrong') - + leftpanelform_data = [{'panel':panel} for panel in r.workflowleftpanel] - + middlepanelform_data = [{'panel':panel} for panel in r.workflowmiddlepanel] @@ -2733,8 +2736,8 @@ def workout_workflow_config2_view(request,userid=0): prefix='leftpanel') middlepanel_formset = MiddlePanelFormSet(initial=middlepanelform_data, prefix='middlepanel') - - + + tmplt = 'workflowconfig2.html' return render(request,tmplt, @@ -2754,7 +2757,7 @@ def workout_workflow_view(request,id): request.session['lastworkout'] = id request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE row = get_workout_permittedview(request.user,id) - + r = getrower(request.user) result = request.user.is_authenticated and ispromember(request.user) if result: @@ -2780,10 +2783,10 @@ def workout_workflow_view(request,id): mapscript = '' mapdiv = '' - + statcharts = GraphImage.objects.filter(workout=row) - + middleTemplates = [] for t in r.workflowmiddlepanel: @@ -2801,7 +2804,7 @@ def workout_workflow_view(request,id): except template.TemplateDoesNotExist: pass - + breadcrumbs = [ { 'url':'/rowers/list-workouts/', @@ -2815,7 +2818,7 @@ def workout_workflow_view(request,id): 'url':reverse('workout_workflow_view',kwargs={'id':id}), 'name': 'View' } - + ] return render(request, @@ -2837,12 +2840,12 @@ def workout_workflow_view(request,id): # The famous flex chart @login_required() def workout_flexchart3_view(request,*args,**kwargs): - + try: id = kwargs['id'] except KeyError: raise Http404("Invalid workout number") - + if 'promember' in kwargs: promember = kwargs['promember'] else: @@ -2852,7 +2855,7 @@ def workout_flexchart3_view(request,*args,**kwargs): favoritenr = int(request.GET['favoritechart']) except: favoritenr = -1 - + row = get_workout(id) promember=0 @@ -2881,7 +2884,7 @@ def workout_flexchart3_view(request,*args,**kwargs): favoritenr=0 except AssertionError: favoritenr=0 - + if 'xparam' in kwargs: xparam = kwargs['xparam'] else: @@ -2897,7 +2900,7 @@ def workout_flexchart3_view(request,*args,**kwargs): yparam1 = favorites[favoritenr].yparam1 else: yparam1 = 'pace' - + if 'yparam2' in kwargs: yparam2 = kwargs['yparam2'] if yparam2 == '': @@ -2922,7 +2925,7 @@ def workout_flexchart3_view(request,*args,**kwargs): else: favoritechartnotes = '' favoritenr = 0 - + if 'plottype' in kwargs: plottype = kwargs['plottype'] else: @@ -2938,7 +2941,7 @@ def workout_flexchart3_view(request,*args,**kwargs): workstrokesonly = not favorites[favoritenr].reststrokes else: workstrokesonly = False - + if request.method == 'POST' and 'savefavorite' in request.POST: if not request.user.is_anonymous: workstrokesonly = request.POST['workstrokesonlysave'] @@ -2955,7 +2958,7 @@ def workout_flexchart3_view(request,*args,**kwargs): plottype=plottype,workouttype=workouttype, reststrokes=reststrokes) f.save() - + except KeyError: messages.error(request,'We cannot save the ad hoc metrics in a favorite chart') @@ -2967,7 +2970,7 @@ def workout_flexchart3_view(request,*args,**kwargs): plottype = cd['plottype'] workstrokesonly = not includereststrokes - + flexaxesform = FlexAxesForm(request,request.POST) if flexaxesform.is_valid(): @@ -3027,12 +3030,12 @@ def workout_flexchart3_view(request,*args,**kwargs): js_resources = "" css_resources = "" - + axchoicesbasic = {ax[0]:ax[1] for ax in axes if ax[4]=='basic'} axchoicespro = {ax[0]:ax[1] for ax in axes if ax[4]=='pro'} noylist = ["time","distance"] axchoicesbasic.pop("cumdist") - + if row.workouttype in mytypes.otwtypes: for name,d in rowingmetrics: if d['mode'] == 'erg': @@ -3044,7 +3047,7 @@ def workout_flexchart3_view(request,*args,**kwargs): axchoicespro.pop(name) from rowers.metrics import nometrics - + rowdata = rdata(row.csvfilename) try: rowdata.set_instroke_metrics() @@ -3070,7 +3073,7 @@ def workout_flexchart3_view(request,*args,**kwargs): # extrametrics.pop(metric) # except KeyError: # pass - + initial = { 'xaxis':xparam, 'yaxis1':yparam1, @@ -3101,10 +3104,10 @@ def workout_flexchart3_view(request,*args,**kwargs): 'url':reverse('workout_flexchart3_view',kwargs=kwargs), 'name': 'Flex Chart' } - + ] - + return render(request, 'flexchart3otw.html', {'the_script':script, @@ -3134,7 +3137,7 @@ def workout_flexchart3_view(request,*args,**kwargs): 'favoritenr':favoritenr, 'maxfav':maxfav, }) - + # The interactive plot with wind corrected pace for OTW outings @@ -3155,7 +3158,7 @@ def workout_otwpowerplot_view(request,id=0,message="",successmessage=""): 'url':reverse('workout_otwpowerplot_view',kwargs={'id':id}), 'name': 'Interactive OTW Power Plot' } - + ] # check if user is owner of this workout @@ -3214,7 +3217,7 @@ def workout_unsubscribe_view(request,id=0): form = WorkoutCommentForm() successmessage = 'You have been unsubscribed from new comment notifications for this workout' - + messages.info(request,successmessage) return render(request, @@ -3225,12 +3228,12 @@ def workout_unsubscribe_view(request,id=0): 'form':form, }) - + # list of comments to a workout @login_required() def workout_comment_view(request,id=0): w = get_workout(id) - + if w.privacy == 'private' and w.user.user != request.user: return HttpResponseForbidden("Permission error") @@ -3266,7 +3269,7 @@ def workout_comment_view(request,id=0): comment = comment, url = url, ) - if request.user != r.user: + if request.user != r.user: a_messages.info(r.user,message.encode('ascii','ignore')) res = myqueue(queuehigh, @@ -3278,7 +3281,7 @@ def workout_comment_view(request,id=0): comment,w.name,w.id, emailbounced = r.emailbounced ) - + commenters = {oc.user for oc in comments if oc.notification} for u in commenters: a_messages.info(u,message) @@ -3302,7 +3305,7 @@ def workout_comment_view(request,id=0): kwargs = { 'id':id}) return HttpResponseRedirect(url) - + form = WorkoutCommentForm() g = GraphImage.objects.filter(workout=w).order_by("-creationdatetime") @@ -3330,9 +3333,9 @@ def workout_comment_view(request,id=0): 'url':reverse('workout_comment_view',kwargs={'id':id}), 'name': 'Comments' } - + ] - + return render(request, 'workout_comments.html', @@ -3347,7 +3350,7 @@ def workout_comment_view(request,id=0): }) - + # The basic edit page @login_required() def workout_edit_view(request,id=0,message="",successmessage=""): @@ -3397,7 +3400,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""): ps = form.cleaned_data['plannedsession'] except KeyError: ps = None - + try: boattype = request.POST['boattype'] except KeyError: @@ -3479,16 +3482,16 @@ def workout_edit_view(request,id=0,message="",successmessage=""): if ps: add_workouts_plannedsession([row],ps,row.user) - + # change data in csv file - + r = rdata(row.csvfilename) if dragchanged: try: r.change_drag(newdragfactor) except AttributeError: pass - + if r == 0: return HttpResponse("Error: CSV Data File Not Found") r.rowdatetime = startdatetime @@ -3498,14 +3501,14 @@ def workout_edit_view(request,id=0,message="",successmessage=""): if rankingpiece: dataprep.runcpupdate(row.user,type=row.workouttype) - + messages.info(request,successmessage) url = reverse('workout_edit_view', kwargs = { 'id':encoder.encode_hex(row.id), }) response = HttpResponseRedirect(url) - + #else: # form not POSTed form = WorkoutForm(instance=row) @@ -3568,7 +3571,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""): 'url':reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(row.id)}), 'name': 'Edit' } - + ] if row.workouttype in mytypes.otetypes: @@ -3616,10 +3619,10 @@ def workout_map_view(request,id=0): 'url':reverse('workout_map_view',kwargs={'id':id}), 'name': 'Map' } - + ] - + # create interactive plot f1 = w.csvfilename u = w.user.user @@ -3645,7 +3648,7 @@ def workout_map_view(request,id=0): else: mapscript = "" mapdiv = "" - + mayedit=0 if not request.user.is_anonymous: r = getrower(request.user) @@ -3665,8 +3668,8 @@ def workout_map_view(request,id=0): 'mayedit':mayedit, }) - - + + # Image upload @login_required() @@ -3692,7 +3695,7 @@ def workout_uploadimage_view(request,id): 'url':reverse('workout_uploadimage_view',kwargs={'id':id}), 'name': 'Upload Image' } - + ] if not checkworkoutuser(request.user,w): @@ -3700,7 +3703,7 @@ def workout_uploadimage_view(request,id): images = GraphImage.objects.filter(workout=w) - + if len(images) >= 6: message = "You have reached the maximum number of static images for this workout" messages.error(request,message) @@ -3710,7 +3713,7 @@ def workout_uploadimage_view(request,id): }) return HttpResponseRedirect(url) - + if request.method == 'POST': form = ImageForm(request.POST,request.FILES) @@ -3738,7 +3741,7 @@ def workout_uploadimage_view(request,id): filename = path_and_filename, width=width,height=height) i.save() - + url = reverse(r.defaultlandingpage, kwargs = {'id':id}) if is_ajax: @@ -3771,8 +3774,8 @@ def workout_uploadimage_view(request,id): else: return {'result':0} - - + + # Generic chart creation @login_required() def workout_add_chart_view(request,id,plotnr=1): @@ -3781,11 +3784,11 @@ def workout_add_chart_view(request,id,plotnr=1): r = getrower(request.user) plotnr = int(plotnr) - + if (checkworkoutuser(request.user,w)==False): raise PermissionDenied("You are not allowed add plots to this workout") else: - f1 = w.csvfilename[6:-4] + f1 = w.csvfilename[6:-4] timestr = strftime("%Y%m%d-%H%M%S") imagename = f1+timestr+'.png' u = w.user.user @@ -3828,13 +3831,13 @@ def workout_toggle_ranking(request,id=0): content_type='application/json') return response - else: + else: url = reverse('workouts_view') response = HttpResponseRedirect(url) return response - - + + # This is the main view for processing uploaded files @login_required() def workout_upload_view(request, @@ -3955,7 +3958,7 @@ def workout_upload_view(request, upload_to_tp = False - + response = {} if request.method == 'POST': @@ -3976,7 +3979,7 @@ def workout_upload_view(request, return JSONResponse({'result':0,'url':0}) else: return HttpResponseRedirect(url) - + t = form.cleaned_data['title'] workouttype = form.cleaned_data['workouttype'] boattype = form.cleaned_data['boattype'] @@ -3985,7 +3988,7 @@ def workout_upload_view(request, 'workouttype':workouttype, 'boattype': boattype, } - + notes = form.cleaned_data['notes'] offline = form.cleaned_data['offline'] @@ -4055,7 +4058,7 @@ def workout_upload_view(request, a = MessageAttachment(message=msg,document=f3) a.save() os.remove(f2) - + messages.info( request, "The file was too large to process in real time. It will be processed in a background process. You will receive an email when it is ready") @@ -4065,7 +4068,7 @@ def workout_upload_view(request, else: response = HttpResponseRedirect(url) return response - + if not id: messages.error(request,message) url = reverse('workout_upload_view') @@ -4122,7 +4125,7 @@ def workout_upload_view(request, messages.info(request,message) else: messages.error(request,message) - + if (upload_to_strava): try: message,id = stravastuff.workout_strava_upload( @@ -4135,7 +4138,7 @@ def workout_upload_view(request, messages.info(request,message) else: messages.error(request,message) - + if (upload_to_st): try: message,id = sporttracksstuff.workout_sporttracks_upload( @@ -4148,7 +4151,7 @@ def workout_upload_view(request, messages.info(request,message) else: messages.error(request,message) - + if (upload_to_rk): try: message,id = runkeeperstuff.workout_runkeeper_upload( @@ -4200,7 +4203,7 @@ def workout_upload_view(request, ) if records: - + result,comments,errors,jobid = add_workout_indoorrace( [w],race,r,recordid=records[0].id ) @@ -4215,7 +4218,7 @@ def workout_upload_view(request, for er in errors: messages.error(request,er) - + if landingpage != 'workout_upload_view': url = reverse(landingpage, kwargs = { @@ -4261,7 +4264,7 @@ def workout_upload_view(request, if r.mapmyfitness_auto_export and isprorower(r): uploadoptions['upload_to_MapMyFitness'] = True - + form = DocumentsForm(initial=docformoptions) optionsform = UploadOptionsForm(initial=uploadoptions, request=request) @@ -4275,7 +4278,7 @@ def workout_upload_view(request, else: return {'result':0} - + # This is the main view for processing uploaded files @user_passes_test(iscoachmember,login_url="/rowers/paidplans",redirect_field_name=None, message="This functionality requires a Coach plan or higher") @@ -4304,7 +4307,7 @@ def team_workout_upload_view(request,message="", myteams = Team.objects.filter(manager=request.user) - + make_plot = uploadoptions['make_plot'] plottype = uploadoptions['plottype'] @@ -4312,7 +4315,7 @@ def team_workout_upload_view(request,message="", if request.method == 'POST': form = DocumentsForm(request.POST,request.FILES) optionsform = TeamUploadOptionsForm(request.POST) - + rowerform = TeamInviteForm(request.POST) rowerform.fields.pop('email') rowers = Rower.objects.filter( @@ -4322,7 +4325,7 @@ def team_workout_upload_view(request,message="", ).distinct() if r.rowerplan == 'freecoach': rowers = rowers.exclude(rowerplan='basic') - + rowerform.fields['user'].queryset = User.objects.filter(rower__in=rowers).distinct() if form.is_valid(): f = request.FILES.get('file',False) @@ -4407,12 +4410,12 @@ def team_workout_upload_view(request,message="", "The file was too large to process in real time. It will be processed in a background process. The user will receive an email when it is ready" ) - + url = reverse('team_workout_upload_view') response = HttpResponseRedirect(url) return response - - + + if not id: messages.error(request,message) url = reverse('team_workout_upload_view') @@ -4430,19 +4433,19 @@ def team_workout_upload_view(request,message="", messages.info(request,successmessage) url = reverse('team_workout_upload_view') - + response = HttpResponseRedirect(url) w = Workout.objects.get(id=id) r = getrower(request.user) if (make_plot): id,jobid = uploads.make_plot(r,w,f1,f2,plottype,t) - + else: - + response = render(request, 'team_document_form.html', {'form':form, @@ -4467,7 +4470,7 @@ def team_workout_upload_view(request,message="", ).distinct() if r.rowerplan == 'freecoach': rowers = rowers.exclude(rowerplan='basic') - + rowerform.fields['user'].queryset = User.objects.filter(rower__in=rowers).distinct() @@ -4517,7 +4520,7 @@ def graphs_view(request): g = paginator.page(1) except EmptyPage: g = paginator.page(paginator.num_pages) - + return render(request, 'list_graphs.html', {'graphs': g, 'searchform':searchform, @@ -4539,7 +4542,7 @@ def graph_show_view(request,id): g.save() except: pass - + w = Workout.objects.get(id=g.workout.id) r = Rower.objects.get(id=w.user.id) @@ -4556,10 +4559,10 @@ def graph_show_view(request,id): 'url':reverse('graph_show_view',kwargs={'id':id}), 'name': 'Chart' } - + ] - + return render(request,'show_graph.html', {'graph':g, 'teams':get_my_teams(request.user), @@ -4567,12 +4570,12 @@ def graph_show_view(request,id): 'breadcrumbs':breadcrumbs, 'active':'nav-workouts', 'rower':r,}) - + except GraphImage.DoesNotExist: raise Http404("This graph doesn't exist") except Workout.DoesNotExist: raise Http404("This workout doesn't exist") - + # Restore original stroke data and summary @login_required() def workout_summary_restore_view(request,id,message="",successmessage=""): @@ -4591,7 +4594,7 @@ def workout_summary_restore_view(request,id,message="",successmessage=""): ftp = float(r.ftp) if row.workouttype in mytypes.otwtypes: ftp = ftp*(100.-r.otwslack)/100. - + rr = rrower(hrmax=r.max,hrut2=r.ut2, hrut1=r.ut1,hrat=r.at, hrtr=r.tr,hran=r.an,ftp=ftp, @@ -4613,13 +4616,13 @@ def workout_summary_restore_view(request,id,message="",successmessage=""): div = res[1] except ValueError: pass - + messages.info(request,'Original Interval Data Restored') url = reverse('workout_summary_edit_view', kwargs={ 'id':encoder.encode_hex(row.id), - } + } ) return HttpResponseRedirect(url) @@ -4631,7 +4634,7 @@ def workout_split_view(request,id=0): row = get_workout_permitted(request.user,id) r = row.user - + breadcrumbs = [ { 'url':'/rowers/list-workouts/', @@ -4646,7 +4649,7 @@ def workout_split_view(request,id=0): kwargs={'id':id}), 'name': 'Chart' } - + ] if request.method == 'POST': form = WorkoutSplitForm(request.POST) @@ -4663,7 +4666,7 @@ def workout_split_view(request,id=0): ) except IndexError: messages.error(request,"Something went wrong in Split") - + for message in mesgs: messages.info(request,message) @@ -4676,7 +4679,7 @@ def workout_split_view(request,id=0): teamids = list(set(mgrids) & set(rwrids)) if len(teamids) > 0: teamid = teamids[0] - + url = reverse('workouts_view', kwargs={ 'teamid':int(teamid), @@ -4693,7 +4696,7 @@ def workout_split_view(request,id=0): rowname = rowname.decode('utf8') except: pass - + qdict = {'q':rowname} url+='?'+urllib.parse.urlencode(qdict) @@ -4729,9 +4732,9 @@ def workout_fusion_view(request,id1=0,id2=1): id2 = encoder.decode_hex(id2) except: pass - + r = getrower(request.user) - + try: w1 = Workout.objects.get(id=id1) w2 = Workout.objects.get(id=id2) @@ -4755,7 +4758,7 @@ def workout_fusion_view(request,id1=0,id2=1): # Create DataFrame df,forceunit = dataprep.datafusion(id1,id2,columns,timeoffset) - + idnew,message = dataprep.new_workout_from_df(r,df, title='Fused data', parent=w1, @@ -4772,9 +4775,9 @@ def workout_fusion_view(request,id1=0,id2=1): }) return HttpResponseRedirect(url) - else: + else: form = FusionMetricChoiceForm(instance=w2) - + breadcrumbs = [ { 'url':'/rowers/list-workouts/', @@ -4797,9 +4800,9 @@ def workout_fusion_view(request,id1=0,id2=1): }), 'name': encoder.encode_hex(w2.id) } - + ] - + return render(request, 'fusion.html', {'form':form, 'teams':get_my_teams(request.user), @@ -4831,7 +4834,7 @@ def workout_summary_edit_view(request,id,message="",successmessage="" 'url':reverse('workout_summary_edit_view',kwargs={'id':id}), 'name': 'Edit Intervals' } - + ] s = "" @@ -4847,7 +4850,7 @@ def workout_summary_edit_view(request,id,message="",successmessage="" ftp = float(r.ftp) if row.workouttype in mytypes.otwtypes: ftp = ftp*(100.-r.otwslack)/100. - + rr = rrower(hrmax=r.max,hrut2=r.ut2, hrut1=r.ut1,hrat=r.at, hrtr=r.tr,hran=r.an,ftp=ftp, @@ -4862,7 +4865,7 @@ def workout_summary_edit_view(request,id,message="",successmessage="" return HttpResponse("Error: CSV Data File Not Found") nrintervals = len(idist) - + savebutton = 'nosavebutton' formvalues = {} form = SummaryStringForm() @@ -4901,10 +4904,10 @@ def workout_summary_edit_view(request,id,message="",successmessage="" data['selector'] = 'pace' powerorpace = 'pace' - - + + powerupdateform = PowerIntervalUpdateForm(initial=data) - + # We have submitted the mini language interpreter if request.method == 'POST' and "intervalstring" in request.POST: form = SummaryStringForm(request.POST) @@ -4979,7 +4982,7 @@ def workout_summary_edit_view(request,id,message="",successmessage="" rowdata.write_csv(f1,gzip=True) dataprep.update_strokedata(encoder.decode_hex(id),rowdata.df) - + messages.info(request,"Updated interval data saved") data = { 'power': power, @@ -4997,7 +5000,7 @@ def workout_summary_edit_view(request,id,message="",successmessage="" 'value_work':work, } - + # we are saving the results obtained from the mini language interpreter elif request.method == 'POST' and "savestringform" in request.POST: s = request.POST["savestringform"] @@ -5005,7 +5008,7 @@ def workout_summary_edit_view(request,id,message="",successmessage="" rowdata.updateinterval_string(s) except (ParseException,err): messages.error(request,'Parsing error in column '+str(err.col)) - + intervalstats = rowdata.allstats() itime,idist,itype = rowdata.intervalstats_values() nrintervals = len(idist) @@ -5019,7 +5022,7 @@ def workout_summary_edit_view(request,id,message="",successmessage="" ) except TypeError: pass - + row.save() rowdata.write_csv(f1,gzip=True) dataprep.update_strokedata(encoder.decode_hex(id),rowdata.df) @@ -5068,7 +5071,7 @@ def workout_summary_edit_view(request,id,message="",successmessage="" debug=False,smoothwindow=15.) except: messages.error(request,'Error updating Work per Stroke') - + intervalstats = rowdata.allstats() itime,idist,itype = rowdata.intervalstats_values() @@ -5082,10 +5085,10 @@ def workout_summary_edit_view(request,id,message="",successmessage="" } powerupdateform = PowerIntervalUpdateForm(initial=cd) form = SummaryStringForm() - + form = SummaryStringForm() - + # we are saving the results obtained from the detailed form elif request.method == 'POST' and "savedetailform" in request.POST: savebutton = 'savedetailform' @@ -5104,7 +5107,7 @@ def workout_summary_edit_view(request,id,message="",successmessage="" t = datetime.datetime.strptime(request.POST['intervalt_%s' % i],"%H:%M:%S.%f") except ValueError: t = datetime.datetime.strptime(request.POST['intervalt_%s' % i],"%H:%M:%S") - + timesecs = 3600*t.hour+60*t.minute+t.second+t.microsecond/1.e6 itime += [timesecs] idist += [int(request.POST['intervald_%s' % i])] @@ -5134,7 +5137,7 @@ def workout_summary_edit_view(request,id,message="",successmessage="" row.notes += "\n"+s except TypeError: pass - + row.save() rowdata.write_csv(f1,gzip=True) dataprep.update_strokedata(encoder.decode_hex(id),rowdata.df) @@ -5273,23 +5276,23 @@ class GraphDelete(DeleteView): { 'url':reverse('graph_delete',kwargs={'pk':str(self.object.pk)}), 'name': 'Delete' } - + ] - + context['active'] = 'nav-workouts' context['rower'] = getrower(self.request.user) context['breadcrumbs'] = breadcrumbs return context - - + + def get_success_url(self): w = self.object.workout try: w = Workout.objects.get(id=w.id) except Workout.DoesNotExist: return reverse('workouts_view') - + return reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(w.id)}) def get_object(self, *args, **kwargs): @@ -5309,7 +5312,7 @@ def workout_code_delete_view(request,id=0): url = reverse('workout_delete',kwargs={'pk':id}) return HttpResponseRedirect(url) - + class WorkoutDelete(DeleteView): login_required = True model = Workout @@ -5331,7 +5334,7 @@ class WorkoutDelete(DeleteView): { 'url':reverse('workout_delete',kwargs={'pk':str(self.object.pk)}), 'name': 'Delete' } - + ] mayedit=0 @@ -5343,17 +5346,17 @@ class WorkoutDelete(DeleteView): promember=1 if self.request.user == self.object.user.user: mayedit=1 - + context['active'] = 'nav-workouts' context['rower'] = getrower(self.request.user) context['breadcrumbs'] = breadcrumbs context['workout'] = self.object context['mayedit'] = mayedit context['promember'] = promember - + return context - - + + def get_success_url(self): return reverse('workouts_view') @@ -5372,4 +5375,3 @@ class WorkoutDelete(DeleteView): raise PermissionDenied('You are not allowed to delete this workout') return obj -