=mina && x1[i]<=maxa && z1[i]<=maxz && z1[i]>=minz) {
- data2['x'].push(x1[i])
- data2['y'].push(y1[i])
- data2['z'].push(z1[i])
- }
- }
-
- source2.trigger('change');
- """)
-
- s1 = Slider(start=0.0, end=5,value=0.0, step=.1,title="Min X Value",callback=callback)
- callback.args["mina"] = s1
-
- s2 = Slider(start=0.0, end=5,value=5.0, step=.1,title="Max X Value",callback=callback)
- callback.args["maxa"] = s2
-
- s3 = Slider(start=0.0, end=25,value=0.0, step=.1,title="Min Z Value",callback=callback)
- callback.args["minz"] = s3
-
- s4 = Slider(start=0.0, end=25,value=25.0, step=.1,title="Max Z Value",callback=callback)
- callback.args["maxz"] = s4
-
- hover = plot.select(dict(type=HoverTool))
-
- hover.tooltips = OrderedDict([
- ('X value','@x'),
- ('Y value','@y'),
- ('Z value','@z'),
- ])
-
- hover.mode = 'mouse'
-
- layout = layoutrow([layoutcolumn([s1,s2,s3,s4]),plot])
-# widgetbox(s)
- script, div = components(layout)
- js_resources = INLINE.render_js()
- css_resources = INLINE.render_css()
- return render(request,
- 'test.html',
- {'the_script': script,
- 'the_div': div,
- 'js_res': js_resources,
- 'css_res':css_resources,
- })
-
-#@user_passes_test(promember,login_url="/",redirect_field_name=None)
+# The interactive plot with the colored Heart rate zones
def workout_biginteractive_view(request,id=0,message="",successmessage=""):
row = Workout.objects.get(id=id)
# check if user is owner of this workout
@@ -3013,6 +2875,7 @@ def workout_biginteractive_view(request,id=0,message="",successmessage=""):
'promember':promember,
'mayedit':mayedit})
+# The interactive plot with wind corrected pace for OTW outings
def workout_otwpowerplot_view(request,id=0,message="",successmessage=""):
row = Workout.objects.get(id=id)
# check if user is owner of this workout
@@ -3048,6 +2911,7 @@ def workout_otwpowerplot_view(request,id=0,message="",successmessage=""):
'the_div':div,
'mayedit':mayedit})
+# the page where you can chose where to export this workout
@login_required()
def workout_export_view(request,id=0, message="", successmessage=""):
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
@@ -3071,6 +2935,7 @@ def workout_export_view(request,id=0, message="", successmessage=""):
'successmessage':successmessage,
})
+# The basic edit page
@login_required()
def workout_edit_view(request,id=0,message="",successmessage=""):
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
@@ -3111,7 +2976,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
row.boattype = boattype
row.save()
# change data in csv file
- # startdatetime = dateutil.parser.parse("{}, {}".format(date,starttime))
+
r = rdata(row.csvfilename)
if r == 0:
return HttpResponse("Error: CSV Data File Not Found")
@@ -3227,6 +3092,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
})
return HttpResponseRedirect(url)
+# Create the chart image with wind corrected pace (OTW)
@user_passes_test(promember,login_url="/",redirect_field_name=None)
def workout_add_otw_powerplot_view(request,id):
w = Workout.objects.get(id=id)
@@ -3271,7 +3137,7 @@ def workout_add_otw_powerplot_view(request,id):
url = "/rowers/workout/"+str(w.id)+"/edit"
return HttpResponseRedirect(url)
-
+# Create the Heart rate zone pie chart
@login_required()
def workout_add_piechart_view(request,id):
w = Workout.objects.get(id=id)
@@ -3315,7 +3181,8 @@ def workout_add_piechart_view(request,id):
url = "/rowers/workout/"+str(w.id)+"/edit"
return HttpResponseRedirect(url)
-
+
+# Create the Power zone pie chart
@login_required()
def workout_add_power_piechart_view(request,id):
w = Workout.objects.get(id=id)
@@ -3351,15 +3218,14 @@ def workout_add_power_piechart_view(request,id):
res = queue.enqueue(handle_makeplot,f1,w.csvfilename,
w.name,hrpwrdata,plotnr,imagename)
-# i = GraphImage(workout=w,creationdatetime=datetime.datetime.now(),
-# filename=fullpathimagename)
i = GraphImage(workout=w,creationdatetime=timezone.now(),
filename=fullpathimagename)
i.save()
url = "/rowers/workout/"+str(w.id)+"/edit"
return HttpResponseRedirect(url)
-
+
+# Create the time based summary chart
@login_required()
def workout_add_timeplot_view(request,id):
w = Workout.objects.get(id=id)
@@ -3404,7 +3270,7 @@ def workout_add_timeplot_view(request,id):
url = "/rowers/workout/"+str(w.id)+"/edit"
return HttpResponseRedirect(url)
-
+# Create the distance based summary chart
@login_required()
def workout_add_distanceplot_view(request,id):
w = Workout.objects.get(id=id)
@@ -3442,13 +3308,12 @@ def workout_add_distanceplot_view(request,id):
i = GraphImage(workout=w,creationdatetime=timezone.now(),
filename=fullpathimagename)
-# i = GraphImage(workout=w,creationdatetime=datetime.datetime.now(),
-# filename=fullpathimagename)
i.save()
url = "/rowers/workout/"+str(w.id)+"/edit"
return HttpResponseRedirect(url)
+# Create the advanced parameters distance overview chart
@user_passes_test(promember,login_url="/",redirect_field_name=None)
def workout_add_distanceplot2_view(request,id):
w = Workout.objects.get(id=id)
@@ -3485,14 +3350,14 @@ def workout_add_distanceplot2_view(request,id):
i = GraphImage(workout=w,creationdatetime=timezone.now(),
filename=fullpathimagename)
-# i = GraphImage(workout=w,creationdatetime=datetime.datetime.now(),
-# filename=fullpathimagename)
+
i.save()
url = "/rowers/workout/"+str(w.id)+"/edit"
return HttpResponseRedirect(url)
+# Create the advanced parameters time based overview chart
@user_passes_test(promember,login_url="/",redirect_field_name=None)
def workout_add_timeplot2_view(request,id):
w = Workout.objects.get(id=id)
@@ -3529,13 +3394,13 @@ def workout_add_timeplot2_view(request,id):
i = GraphImage(workout=w,creationdatetime=timezone.now(),
filename=fullpathimagename)
-# i = GraphImage(workout=w,creationdatetime=datetime.datetime.now(),
-# filename=fullpathimagename)
+
i.save()
url = "/rowers/workout/"+str(w.id)+"/edit"
return HttpResponseRedirect(url)
+# The page where you select which Strava workout to import
@login_required()
def workout_stravaimport_view(request,message=""):
res = stravastuff.get_strava_workout_list(request.user)
@@ -3564,6 +3429,7 @@ def workout_stravaimport_view(request,message=""):
return HttpResponse(res)
+# The page where you select which SportTracks page to import
@login_required()
def workout_sporttracksimport_view(request,message=""):
res = sporttracksstuff.get_sporttracks_workout_list(request.user)
@@ -3602,6 +3468,7 @@ def workout_sporttracksimport_view(request,message=""):
return HttpResponse(res)
+# List of workouts on Concept2 logbook. This view only used for debugging
@login_required()
def c2listdebug_view(request,message=""):
try:
@@ -3644,7 +3511,7 @@ def c2listdebug_view(request,message=""):
{'workouts':workouts,
'message':message})
-
+# List of workouts available on Concept2 logbook - for import
@login_required()
def workout_c2import_view(request,message=""):
try:
@@ -3685,6 +3552,7 @@ def workout_c2import_view(request,message=""):
{'workouts':workouts,
'message':message})
+# Import a workout from Strava
@login_required()
def workout_getstravaworkout_view(request,stravaid):
res = stravastuff.get_strava_workout(request.user,stravaid)
@@ -3698,6 +3566,7 @@ def workout_getstravaworkout_view(request,stravaid):
url = "/rowers/workout/"+str(id)+"/edit"
return HttpResponseRedirect(url)
+# Imports a workout from SportTracks
@login_required()
def workout_getsporttracksworkout_view(request,sporttracksid):
res = sporttracksstuff.get_sporttracks_workout(request.user,sporttracksid)
@@ -3710,6 +3579,7 @@ def workout_getsporttracksworkout_view(request,sporttracksid):
url = "/rowers/workout/"+str(id)+"/edit"
return HttpResponseRedirect(url)
+# Imports a workout from Concept2
@login_required()
def workout_getc2workout_view(request,c2id):
try:
@@ -3727,6 +3597,7 @@ def workout_getc2workout_view(request,c2id):
if 'intervals' in data['workout']:
splitdata = data['workout']['intervals']
+ # Check if workout has stroke data, and get the stroke data
if data['stroke_data']:
res2 = c2stuff.get_c2_workout_strokes(request.user,c2id)
else:
@@ -3737,12 +3608,16 @@ def workout_getc2workout_view(request,c2id):
})
return HttpResponseRedirect(url)
+ # We have stroke data
if res2.status_code == 200:
strokedata = pd.DataFrame.from_dict(res2.json()['data'])
+ # create the workout
id = add_workout_from_strokedata(request.user,c2id,data,strokedata,
source='c2')
w = Workout.objects.get(id=id)
w.uploadedtoc2=c2id
+ # If we have split data, update the stroke data so they
+ # match exactly (some users are anal about this)
if splitdata:
try:
w.summary,sa,results = c2stuff.summaryfromsplitdata(splitdata,data,w.csvfilename)
@@ -3758,7 +3633,7 @@ def workout_getc2workout_view(request,c2id):
w.save()
from rowingdata.trainingparser import getlist
- # set stroke data
+ # set stroke data in CSV file
if sa:
values = getlist(sa)
units = getlist(sa,sel='unit')
@@ -3800,7 +3675,7 @@ def workout_getc2workout_view(request,c2id):
})
return HttpResponseRedirect(url)
-
+# This is the main view for processing uploaded files
@login_required()
def workout_upload_view(request,message=""):
if request.method == 'POST':
@@ -3810,7 +3685,6 @@ def workout_upload_view(request,message=""):
f = request.FILES['file']
res = handle_uploaded_file(f)
t = form.cleaned_data['title']
- # fileformat = form.cleaned_data['fileformat']
workouttype = form.cleaned_data['workouttype']
notes = form.cleaned_data['notes']
@@ -3822,15 +3696,18 @@ def workout_upload_view(request,message=""):
f1 = res[0] # file name
f2 = res[1] # file name incl media directory
- # new
+ # get file type (ErgData, NK, BoatCoach, etc
fileformat = get_file_type(f2)
if len(fileformat)==3 and fileformat[0]=='zip':
f_to_be_deleted = f2
with zipfile.ZipFile(f2) as z:
+ # for now, we're getting only the first file
+ # from the NK zip file (issue #69 on bitbucket)
f2 = z.extract(z.namelist()[0],path='media/')
fileformat = fileformat[2]
os.remove(f_to_be_deleted)
+ # Some people try to upload Concept2 logbook summaries
if fileformat == 'c2log':
message = "This C2 logbook summary does not contain stroke data. Please download the Export Stroke Data file from the workout details on the C2 logbook."
url = reverse(workout_upload_view,
@@ -3838,13 +3715,18 @@ def workout_upload_view(request,message=""):
response = HttpResponseRedirect(url)
return response
+ # Some people try to upload RowPro summary logs
if fileformat == 'rowprolog':
message = "This RowPro logbook summary does not contain stroke data. Please use the Stroke Data CSV file for the individual workout in your log."
url = reverse(workout_upload_view,
args=[str(message)])
response = HttpResponseRedirect(url)
return response
-
+
+ # Sometimes people try an unsupported file type.
+ # Send an email to info@rowsandall.com with the file attached
+ # for me to check if it is a bug, or a new file type
+ # worth supporting
if fileformat == 'unknown':
message = "We couldn't recognize the file type"
url = reverse(workout_upload_view,
@@ -3861,7 +3743,8 @@ def workout_upload_view(request,message=""):
return response
summary = ''
- # handle non-Painsled
+ # handle non-Painsled by converting it to painsled
+ # compatible CSV
try:
if (fileformat != 'csv'):
# handle RowPro:
@@ -3914,23 +3797,25 @@ def workout_upload_view(request,message=""):
# handle FIT
if (fileformat == 'fit'):
row = FITParser(f2)
+ # The FIT files have nice lap/split summaries
+ # so we make use of it
s = fitsummarydata(f2)
s.setsummary()
summary = s.summarytext
-
+ # Save the Painsled compatible CSV file and delete
+ # the uploaded file
f_to_be_deleted = f2
# should delete file
f2 = f2[:-4]+'o.csv'
row.write_csv(f2,gzip=True)
- #os.remove(f2)
try:
os.remove(f_to_be_deleted)
except:
os.remove(f_to_be_deleted+'.gz')
- # make workout and put in database
+ # make Workout object and put in database
r = Rower.objects.get(user=request.user)
rr = rrower(hrmax=r.max,hrut2=r.ut2,
hrut1=r.ut1,hrat=r.at,
@@ -3973,7 +3858,6 @@ def workout_upload_view(request,message=""):
if workouttype == 'rower' or workouttype == 'dynamic' or workouttype == 'slides':
try:
row.erg_recalculatepower()
-# row.spm_fromtimestamps()
row.write_csv(f2,gzip=True)
except:
pass
@@ -4020,8 +3904,11 @@ def workout_upload_view(request,message=""):
startdatetime=workoutstartdatetime)
w.save()
+
# put stroke data in database
- res = dataprep.dataprep(row.df,id=w.id,bands=True,barchart=True,otwpower=True,empower=True)
+ res = dataprep.dataprep(row.df,id=w.id,
+ bands=True,barchart=True,
+ otwpower=True,empower=True)
# Make Plot
if (make_plot):
@@ -4049,8 +3936,6 @@ def workout_upload_view(request,message=""):
plotnr = plotnr+3
-# res = handle_makeplot.delay(f1,f2,t,
-# hrpwrdata,plotnr,imagename)
if settings.DEBUG:
res = handle_makeplot.delay(f1,f2,t,
hrpwrdata,plotnr,
@@ -4064,9 +3949,6 @@ def workout_upload_view(request,message=""):
i = GraphImage(workout=w,
creationdatetime=timezone.now(),
filename=fullpathimagename)
-# i = GraphImage(workout=w,
-# creationdatetime=datetime.datetime.now(),
-# filename=fullpathimagename)
i.save()
# upload to C2
@@ -4140,216 +4022,8 @@ def workout_upload_view(request,message=""):
'message':message})
-@login_required()
-def workout_upload_view_debug(request,message=""):
- if request.method == 'POST':
- form = DocumentsForm(request.POST,request.FILES)
- optionsform = UploadOptionsForm(request.POST)
- if form.is_valid():
- f = request.FILES['file']
- res = handle_uploaded_file(f)
- t = form.cleaned_data['title']
- fileformat = form.cleaned_data['fileformat']
- workouttype = form.cleaned_data['workouttype']
- notes = form.cleaned_data['notes']
- make_plot = request.POST.getlist('make_plot')
- plottype = request.POST['plottype']
- upload_to_c2 = request.POST.getlist('upload_to_C2')
-
-
- f1 = res[0] # file name
- f2 = res[1] # file name incl media directory
-
-
- # handle non-Painsled
- if (fileformat != 'csv'):
- # handle RowPro:
- if (fileformat == 'rp'):
- row = RowProParser(f2)
-
- # handle TCX
- if (fileformat == 'tcx'):
- row = TCXParser(f2)
-
- # handle TCX no HR
- if (fileformat == 'tcxnohr'):
- row = TCXParserNoHR(f2)
-
- # handle ErgData
- if (fileformat == 'ergdata'):
- row = ErgDataParser(f2)
-
- # handle BoatCoach
- if (fileformat == 'boatcoach'):
- row = BoatCoachParser(f2)
-
-
- # handle painsled desktop
- if (fileformat == 'painsleddesktop'):
- row = painsledDesktopParser(f2)
-
- # handle speed coach
- if (fileformat == 'speedcoach'):
- row = speedcoachParser(f2)
-
- # handle ErgStick
- if (fileformat == 'ergstick'):
- row = ErgStickParser(f2)
-
- f_to_be_deleted = f2
- # should delete file
- f2 = f2[:-4]+'o.csv'
- row.write_csv(f2,gzip=True)
- try:
- os.remove(f_to_be_deleted)
- except:
- os.remove(f_to_be_deleted+'.gz')
- # remove uncompressed
-
- # make workout and put in database
- r = Rower.objects.get(user=request.user)
- rr = rrower(hrmax=r.max,hrut2=r.ut2,
- hrut1=r.ut1,hrat=r.at,
- hrtr=r.tr,hran=r.an,ftp=r.ftp)
- row = rdata(f2,rower=rr)
-
- if row == 0:
- return HttpResponse("Error: CSV Data File Not Found")
- row.write_csv(f2,gzip=True)
- averagehr = row.df[' HRCur (bpm)'].mean()
- maxhr = row.df[' HRCur (bpm)'].max()
- totaldist = row.df['cum_dist'].max()
- totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min()
- totaltime = totaltime+row.df.ix[0,' ElapsedTime (sec)']
-
-
- hours = int(totaltime/3600.)
- minutes = int((totaltime - 3600.*hours)/60.)
- seconds = int(totaltime - 3600.*hours - 60.*minutes)
- tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds))
-
- summary = row.summary()
- summary += '\n'
- summary += row.intervalstats()
-
- duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths)
-
-
- workoutdate = row.rowdatetime.strftime('%Y-%m-%d')
- workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
-
- # check for duplicate start times
- r = Rower.objects.get(user=request.user)
-
- ws = Workout.objects.filter(starttime=workoutstarttime,
- user=r)
- if (len(ws) != 0):
- print "Warning: This workout probably already exists in the database"
-
-
- w = Workout(user=r,name=t,date=workoutdate,
- workouttype=workouttype,
- duration=duration,distance=totaldist,
- weightcategory=r.weightcategory,
- starttime=workoutstarttime,
- csvfilename=f2,notes=notes,summary=summary,
- averagehr=averagehr,maxhr=maxhr)
-
- w.save()
-
- # Make Plot
- if (make_plot):
- imagename = f1[:-4]+'.png'
- fullpathimagename = 'static/plots/'+imagename
- u = request.user
- hrpwrdata = {
- 'hrmax':r.max,
- 'hrut2':r.ut2,
- 'hrut1':r.ut1,
- 'hrat':r.at,
- 'hrtr':r.tr,
- 'hran':r.an,
- }
-
- # make plot - asynchronous task
- plotnrs = {
- 'timeplot':1,
- 'distanceplot':2,
- 'pieplot':3,
- }
-
- plotnr = plotnrs[plottype]
- if (workouttype=='water'):
- plotnr = plotnr+3
-
-
- # res = handle_makeplot.delay(f1,f2,t,
- # hrpwrdata,plotnr,imagename)
- if settings.DEBUG:
- res = handle_makeplot.delay(f1,f2,t,
- hrpwrdata,plotnr,
- imagename)
- else:
- res = queue.enqueue(handle_makeplot,f1,f2,
- t,hrpwrdata,
- plotnr,imagename)
-
-
- i = GraphImage(workout=w,
- creationdatetime=timezone.now(),
- filename=fullpathimagename)
- i.save()
-
- # upload to C2
- if (upload_to_c2):
- try:
- thetoken = c2_open(request.user)
- except C2NoTokenError:
- return HttpResponseRedirect("/rowers/me/c2authorize/")
-
- try:
- response = c2stuff.workout_c2_upload(request.user,w)
- if (response.status_code != 201):
- if settings.DEBUG:
- return HttpResponse(response)
- else:
- message = "C2 upload failed"
- url = reverse(workout_edit_view,
- kwargs={
- 'message':message,
- 'id':str(w.id),
- })
- return HttpResponseRedirect(url)
- except:
- message = "C2 upload failed"
- url = reverse(workout_edit_view,
- kwargs={
- 'message':message,
- 'id':str(w.id),
- })
- return HttpResponseRedirect(url)
-
- # redirect to workout edit page
- url = "/rowers/workout/"+str(w.id)+"/edit"
- return HttpResponseRedirect(url)
-
- else:
- response = render(request,
- 'document_form.html',
- {'form':form,
- 'optionsform': optionsform,
- 'message':message})
-
- return response
- else:
- form = DocumentsForm()
- optionsform = UploadOptionsForm()
- return render(request, 'document_form.html',
- {'form':form,
- 'optionsform': optionsform,
- 'message':message})
-
+# Ask the user if he really wants to delete the workout
@login_required()
def workout_delete_confirm_view(request, id=0):
try:
@@ -4364,6 +4038,7 @@ def workout_delete_confirm_view(request, id=0):
except Workout.DoesNotExist:
return HttpResponseNotFound("Workout doesn't exist")
+# Really deleting the workout
@login_required()
def workout_delete_view(request,id=0):
try:
@@ -4383,7 +4058,7 @@ def workout_delete_view(request,id=0):
except Workout.DoesNotExist:
return HttpResponseNotFound("Workout doesn't exist")
-
+# Ask the user to confirm that he wants to delete a chart
@login_required()
def graph_delete_confirm_view(request, id=0):
try:
@@ -4399,6 +4074,7 @@ def graph_delete_confirm_view(request, id=0):
except Workout.DoesNotExist:
return HttpResponseNotFound("Workout doesn't exist")
+# Really deleting the chart
@login_required()
def graph_delete_view(request,id=0):
try:
@@ -4420,32 +4096,7 @@ def graph_delete_view(request,id=0):
return HttpResponse("Graph Image doesn't exist")
-@login_required()
-def dashboard_view(request,message="",successmessage=""):
- try:
- request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
-
- r = Rower.objects.get(user=request.user)
- workouts = Workout.objects.filter(user=r).order_by("-date", "-starttime")
- g = GraphImage.objects.filter(workout__in=workouts).order_by("-creationdatetime")
- if (len(g)<=3):
- return render(request,'dashboard.html',
- {'workouts':workouts,
- 'graphs1':g[0:3],
- 'message':message,
- 'successmessage':successmessage})
- else:
- return render(request,'dashboard.html',
- {'workouts':workouts,
- 'graphs1':g[0:3],
- 'graphs2':g[3:6],
- 'message':message,
- 'successmessage':successmessage})
-
- except Rower.DoesNotExist:
- return HttpResponse("User has no rower instance")
-
-
+# A page with all the recent graphs (searchable on workout name)
@login_required()
def graphs_view(request):
try:
@@ -4472,7 +4123,7 @@ def graphs_view(request):
except Rower.DoesNotExist:
return HttpResponse("User has no rower instance")
-
+# Show the chart (png image)
def graph_show_view(request,id):
try:
g = GraphImage.objects.get(id=id)
@@ -4487,6 +4138,7 @@ def graph_show_view(request,id):
except GraphImage.DoesNotExist:
return HttpResponse("This graph doesn't exist")
+# Restore original stroke data and summary
@login_required()
def workout_summary_restore_view(request,id,message="",successmessage=""):
try:
@@ -4534,7 +4186,7 @@ def workout_summary_restore_view(request,id,message="",successmessage=""):
return HttpResponseRedirect(url)
-
+# Edit the splits/summary
@login_required()
def workout_summary_edit_view(request,id,message="",successmessage=""
):
@@ -4722,7 +4374,7 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
'savebutton':savebutton,
})
-
+# Page where user can manage his favorite charts
@user_passes_test(promember,login_url="/rowers/me/edit",redirect_field_name=None)
def rower_favoritecharts_view(request):
message = ''
@@ -4786,6 +4438,8 @@ def rower_favoritecharts_view(request):
return render(request,'favoritecharts.html',context)
+# Page where user can set his details
+# Add email address to form so user can change his email address
@login_required()
def rower_edit_view(request,message=""):
if request.method == 'POST' and "ut2" in request.POST:
@@ -4868,6 +4522,9 @@ def rower_edit_view(request,message=""):
except Rower.DoesNotExist:
return HttpResponse("This user doesn't exist")
+# Revoke an app that you granted access through the API.
+# this views is called when you press a button on the User edit page
+# the button is only there when you have granted access to an app
@login_required()
def rower_revokeapp_view(request,id=0):
tokens = AccessToken.objects.filter(user=request.user,application=id)
@@ -4889,18 +4546,21 @@ def rower_revokeapp_view(request,id=0):
})
-
+# Utility to get stroke data in a JSON response
class JSONResponse(HttpResponse):
def __init__(self, data, **kwargs):
content = JSONRenderer().render(data)
kwargs['content_type'] = 'application/json'
super(JSONResponse, self).__init__(content, **kwargs)
+# Creates unix time stamp from a datetime object
def totimestamp(dt, epoch=datetime.datetime(1970,1,1,tzinfo=tz('UTC'))):
td = dt - epoch
# return td.total_seconds()
return (td.microseconds + (td.seconds + td.days * 86400) * 10**6) / 10**6
-
+# Check if a column of a dataframe has the required (aantal)
+# number of elements. Also checks if the column is a numerical type
+# Replaces any faulty columns with zeros
def trydf(df,aantal,column):
try:
s = df[column]
@@ -4913,6 +4573,7 @@ def trydf(df,aantal,column):
return s
+# Stroke data form to test API upload
@login_required()
def strokedataform(request,id=0):
if request.method == 'GET':
@@ -4931,7 +4592,8 @@ def strokedataform(request,id=0):
'id':id,
})
-
+# Process the POSTed stroke data according to the API definition
+# Return the GET stroke data according to the API definition
from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
@@ -4955,6 +4617,7 @@ def strokedatajson(request,id):
if request.method == 'GET':
+ # currently only returns a subset.
columns = ['spm','time','hr','pace','power','distance']
datadf = dataprep.getsmallrowdata_db(columns,ids=[id])
return JSONResponse(datadf)
diff --git a/rowers/weather.py b/rowers/weather.py
index 272dd7ce..74e2bdd6 100644
--- a/rowers/weather.py
+++ b/rowers/weather.py
@@ -8,9 +8,8 @@ from rowers.models import Rower, Workout
from rowsandall_app.settings import FORECAST_IO_KEY
-
+# Get weather data from the DarkSky API
def get_weather_data(long,lat,unixtime):
-# url = "https://api.forecast.io/forecast/"+FORECAST_IO_KEY+"/"
url = "https://api.darksky.net/forecast/"+FORECAST_IO_KEY+"/"
url += str(long)+","+str(lat)+","+str(unixtime)
@@ -21,6 +20,7 @@ def get_weather_data(long,lat,unixtime):
else:
return 0
+# Get wind data (and translate from knots to m/s)
def get_wind_data(lat,long,unixtime):
data = get_weather_data(lat,long,unixtime)
if data:
@@ -39,6 +39,7 @@ def get_wind_data(lat,long,unixtime):
try:
temperature = data['currently']['temperature']
+ # Temp is given in Fahrenheit, so convert to Celsius for Europeans
temperaturec = (temperature-32.)*(5./9.)
temperaturec = int(10*temperaturec)/10.
except KeyError:
@@ -54,7 +55,8 @@ def get_wind_data(lat,long,unixtime):
windbearing = 0
message = 'Not able to get weather data'
- # apply Hellman's coefficient for neutral air above human inhabitated areas
+ # apply Hellman's coefficient for neutral air above human
+ # inhabitated areas
windspeed = windspeed*(0.1)**0.34
windspeed = 0.01*int(100*windspeed)
diff --git a/rowsandall_app/forms.py b/rowsandall_app/forms.py
index 55b40689..97f5d436 100644
--- a/rowsandall_app/forms.py
+++ b/rowsandall_app/forms.py
@@ -1,33 +1,5 @@
from django import forms
-from rowers.models import Workout
-from rows import validate_file_extension
-
-class ContactForm(forms.Form):
- subject = forms.CharField()
- email = forms.EmailField(required=False)
- message = forms.CharField(widget=forms.Textarea)
-
-class DocumentsForm(forms.Form):
- filetypechoices = (
- ('tcx' , 'TCX'),
- ('csv' , 'Painsled CSV')
- )
- title = forms.CharField(required=False)
- file = forms.FileField(required=True,
- validators=[validate_file_extension])
- workouttype = forms.ChoiceField(required=True,
- choices=Workout.workouttypes,
- initial='rower')
- fileformat = forms.ChoiceField(required=True,
- choices=filetypechoices,
- initial='csv')
- notes = forms.CharField(required=False,
- widget=forms.Textarea)
-
- class Meta:
- fields = ['title','file','workouttype','fileformat']
-
diff --git a/rowsandall_app/models.py b/rowsandall_app/models.py
index 070f1f9f..9ae69826 100644
--- a/rowsandall_app/models.py
+++ b/rowsandall_app/models.py
@@ -1,7 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import models
-from rows import validate_file_extension
-class Document(models.Model):
- docfile = models.FileField(upload_to='documents/%Y/%m/%d',
- validators=[validate_file_extension])
diff --git a/rowsandall_app/rctasks.py b/rowsandall_app/rctasks.py
deleted file mode 100644
index 2546b73e..00000000
--- a/rowsandall_app/rctasks.py
+++ /dev/null
@@ -1,3 +0,0 @@
-
-def addrc(x,y,z):
- return int(x)+int(y)
diff --git a/rowsandall_app/settings_orig.py b/rowsandall_app/settings_orig.py
deleted file mode 100644
index 8e6b74bb..00000000
--- a/rowsandall_app/settings_orig.py
+++ /dev/null
@@ -1,121 +0,0 @@
-"""
-Django settings for rowsandall_app project.
-
-Generated by 'django-admin startproject' using Django 1.9.5.
-
-For more information on this file, see
-https://docs.djangoproject.com/en/1.9/topics/settings/
-
-For the full list of settings and their values, see
-https://docs.djangoproject.com/en/1.9/ref/settings/
-"""
-
-import os
-
-# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
-BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-
-
-# Quick-start development settings - unsuitable for production
-# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
-
-# SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = '&cg#y8h-8s#00ayk#gu)+l43j1j9^9r&qf$3!$x#ov@1houiph'
-
-# SECURITY WARNING: don't run with debug turned on in production!
-DEBUG = True
-
-ALLOWED_HOSTS = []
-
-
-# Application definition
-
-INSTALLED_APPS = [
- 'django.contrib.admin',
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.messages',
- 'django.contrib.staticfiles',
-]
-
-MIDDLEWARE_CLASSES = [
- 'django.middleware.security.SecurityMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.common.CommonMiddleware',
- 'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
- 'django.middleware.clickjacking.XFrameOptionsMiddleware',
-]
-
-ROOT_URLCONF = 'rowsandall_app.urls'
-
-TEMPLATES = [
- {
- 'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': [os.path.join(BASE_DIR,'templates')],
- 'APP_DIRS': True,
- 'OPTIONS': {
- 'context_processors': [
- 'django.template.context_processors.debug',
- 'django.template.context_processors.request',
- 'django.contrib.auth.context_processors.auth',
- 'django.contrib.messages.context_processors.messages',
- ],
- },
- },
-]
-
-WSGI_APPLICATION = 'rowsandall_app.wsgi.application'
-
-
-# Database
-# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
-
-DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.sqlite3',
- 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
- }
-}
-
-
-# Password validation
-# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
-
-AUTH_PASSWORD_VALIDATORS = [
- {
- 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
- },
- {
- 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
- },
- {
- 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
- },
- {
- 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
- },
-]
-
-
-# Internationalization
-# https://docs.djangoproject.com/en/1.9/topics/i18n/
-
-LANGUAGE_CODE = 'en-us'
-
-TIME_ZONE = 'Europe/Prague'
-
-USE_I18N = True
-
-USE_L10N = True
-
-USE_TZ = True
-
-
-# Static files (CSS, JavaScript, Images)
-# https://docs.djangoproject.com/en/1.9/howto/static-files/
-
-STATIC_URL = '/static/'
diff --git a/rowsandall_app/templates/interactiveplot.html b/rowsandall_app/templates/interactiveplot.html
deleted file mode 100644
index d4585020..00000000
--- a/rowsandall_app/templates/interactiveplot.html
+++ /dev/null
@@ -1,3 +0,0 @@
-graph = {{ my_data|safe }};
-
-mpld3.draw_figure("fig01", graph);
\ No newline at end of file
diff --git a/rowsandall_app/urls.py b/rowsandall_app/urls.py
index 3415cd36..b90e14d6 100644
--- a/rowsandall_app/urls.py
+++ b/rowsandall_app/urls.py
@@ -17,9 +17,7 @@ from django.conf.urls import url,include
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
-from rowsandall_app.views import rootview,version,rowingdata,showStaticImage,\
- add,wait,nrowingdata,waitforplot,showplot,interactiveplot
-from rowsandall_app.views import uploadfile
+from rowsandall_app.views import rootview
from django.contrib.auth import views as auth_views
from rowers import views as rowersviews
@@ -28,8 +26,6 @@ urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^$',rootview),
- url(r'^version/$',version),
- url(r'^addresult/(.+.*)/$',wait),
url(r'^login/',auth_views.login, name='login'),
url(r'^logout/',auth_views.logout_then_login,name='logout'),
url(r'^password_change_done/$',auth_views.password_change_done,name='password_change_done'),
@@ -42,12 +38,10 @@ urlpatterns = [
url(r'^password_reset_complete/$',auth_views.password_reset_complete,name='password_reset_complete'),
url(r'^rowers/',include('rowers.urls')),
url(r'^cvkbrno/',include('cvkbrno.urls')),
- url(r'^add/(\d+)/(\d+)/$',add),
url(r'^call\_back',rowersviews.rower_process_callback),
url(r'^stravacall\_back',rowersviews.rower_process_stravacallback),
url(r'^sporttracks\_callback',rowersviews.rower_process_sporttrackscallback),
url(r'^twitter\_callback',rowersviews.rower_process_twittercallback),
- url(r'^interactiveplot',interactiveplot),
url(r'^i18n/', include('django.conf.urls.i18n')),
]
diff --git a/rowsandall_app/views.py b/rowsandall_app/views.py
index 6f351bcf..921e05f6 100644
--- a/rowsandall_app/views.py
+++ b/rowsandall_app/views.py
@@ -1,258 +1,11 @@
-from django.http import HttpResponse,Http404,HttpResponseRedirect
-from django.template import Template, Context
-from django.template.loader import get_template
from django.shortcuts import render, redirect, render_to_response
from django.conf import settings
-from rowsandall_app.forms import ContactForm,DocumentsForm
-from django.core.mail import send_mail
-from django.core.urlresolvers import reverse
-from matplotlib.pyplot import figure, axes, pie, title
-from matplotlib.backends.backend_agg import FigureCanvasAgg
-from matplotlib.ticker import MultipleLocator,FuncFormatter,NullFormatter
-import matplotlib.pyplot as plt
-from rowingdata import rower as rrower
+
from rowingdata import main as rmain
-from rowingdata import rowingdata as rdata
-from rowingdata import TCXParser
-import StringIO
-from django.contrib.auth.decorators import login_required
-
-import django_rq
-queue = django_rq.get_queue('default')
-
-import datetime
-import pandas as pd
-import os
-import numpy as np
-import json
-import mpld3
-
-from rctasks import addrc
-
-from rows import *
-from rowers.tasks import add as addtask
-from rowers.tasks import handle_makeplot
-from celery.result import AsyncResult
-from celery.exceptions import TimeoutError
-from rowers.models import Rower,User,Workout,GraphImage
-
-
-def showStaticImage(request,imagename):
- return render(request, 'image_page.html', {'imagename': imagename})
def rootview(request):
magicsentence = rmain()
return render(request, 'frontpage.html', {'versionstring': magicsentence})
-def version(request):
- magicsentence = rmain()
- return render(request, 'base.html', {'versionstring': magicsentence})
-def rowingdata(request,formloc):
- if request.method == 'POST':
- form = DocumentsForm(request.POST,request.FILES)
-
- if form.is_valid():
- f = request.FILES['file']
- t = request.POST['title']
- res = handle_uploaded_file(f)
- f1 = res[0]
- f2 = res[1]
- row = rdata(f2)
- fig1 = row.get_timeplot_erg(t)
-
- canvas = FigureCanvasAgg(fig1)
- # response = HttpResponse(content_type='image/png')
- # canvas.print_png(response)
- imagename = f1+'.png'
- plt.savefig('static/plots/'+imagename,format='png')
- plt.close(fig1)
- response = render(request,'image_page.html',{'imagename':'plots/'+imagename})
-
- else:
- response = HttpResponse("invalid form")
-
- return response
- else:
- form = DocumentsForm()
- return render(request, 'document_form.html',
- {'form':form, 'formloc': formloc})
-
-@login_required()
-def nrowingdata(request,formloc):
- if request.method == 'POST':
- form = DocumentsForm(request.POST,request.FILES)
-
- if form.is_valid():
- f = request.FILES['file']
- res = handle_uploaded_file(f)
- t = request.POST['title']
- fileformat = request.POST['fileformat']
- workouttype = request.POST['workouttype']
- notes = request.POST['notes']
-
- f1 = res[0] # file name
- f2 = res[1] # file name incl media directory
-
- # handle TCX
- if (fileformat == 'tcx'):
- row = TCXParser(f2)
- f_to_be_deleted = f2
- # should delete file
- f2 = f2+'.csv'
- row.write_csv(f2)
- os.remove(f_to_be_deleted)
-
- imagename = f1+'.png'
- fullpathimagename = 'static/plots/'+imagename
- u = request.user
- r = Rower.objects.get(user=request.user)
- hrdata = {
- 'hrmax':r.max,
- 'hrut2':r.ut2,
- 'hrut1':r.ut1,
- 'hrat':r.at,
- 'hrtr':r.tr,
- 'hran':r.an,
- }
-
- # make plot - asynchronous task
- res = handle_makeplot.delay(f1,f2,t,hrdata)
-
- # make workout and put in database
- rr = rrower(hrmax=r.max,hrut2=r.ut2,
- hrut1=r.ut1,hrat=r.at,
- hrtr=r.tr,hran=r.an)
- row = rdata(f2,rower=rr)
- totaldist = row.df['cum_dist'].max()
- totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min()
- totaltime = totaltime+row.df.ix[0,' ElapsedTime (sec)']
-
-
- hours = int(totaltime/3600.)
- minutes = int((totaltime - 3600.*hours)/60.)
- seconds = int(totaltime - 3600.*hours - 60.*minutes)
-
- duration = "%s:%s:%s" % (hours,minutes,seconds)
-
-
- workoutdate = row.rowdatetime.strftime('%Y-%m-%d')
- workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
-
- # check for duplicate start times
- r = Rower.objects.get(user=request.user)
-
- ws = Workout.objects.filter(starttime=workoutstarttime,
- user=r)
- if (len(ws) != 0):
- print "Warning: This workout probably already exists in the database"
-
- w = Workout(user=r,name=t,date=workoutdate,workouttype=workouttype,
- duration=duration,distance=totaldist,
- weightcategory=r.weightcategory,
- starttime=workoutstarttime,
- csvfilename=f2,notes=notes)
- w.save()
-
- i = GraphImage(workout=w,creationdatetime=datetime.datetime.now(),
- filename=fullpathimagename)
- i.save()
-
- url = reverse(waitforplot,args=[str(res.id)])
- return HttpResponseRedirect(url)
-
- else:
- response = HttpResponse("invalid form")
-
- return response
- else:
- form = DocumentsForm()
- return render(request, 'document_form.html',
- {'form':form, 'formloc': formloc})
-
-
-
-def uploadfile(request,formloc):
- if request.method == 'POST':
- form = DocumentsForm(request.POST, request.FILES)
-
- if form.is_valid():
- result = handle_uploaded_file(request.FILES['file'])
- return HttpResponse("succes! "+result)
- else:
- return HttpResponse("Invalid Form")
- else:
- form = DocumentsForm()
- return render(request, 'document_form.html',
- {'form': form, 'formloc':formloc})
-
-@login_required()
-def add(request,x,y):
- if settings.DEBUG:
- task = addtask.apply_async((int(x),int(y)),countdown=10)
- task_id = AsyncResult(task)
- print task.id
- else:
- task = queue.enqueue(addrc,x,y,0)
- task_id = task.id
-
- url = reverse(wait,args=[str(task_id)])
- return HttpResponseRedirect(url)
-
-
-
-
-def wait(request,task_id=""):
- if settings.DEBUG:
- res = AsyncResult(task_id)
- else:
- res = queue.fetch_job(task_id)
- if (res.status == 'SUCCESS') or (res.status == 'finished'):
- return HttpResponse("Task complete. Result: "+str(res.result))
- else:
- m = "Task status: "+str(res.status)
- return render(request,'waiting_page.html',{'message':m})
- return HttpResponse("Task status: "+str(res.status)+". Hit reload to check again")
-
-
-def waitforplot(request,task_id=""):
- res = AsyncResult(task_id)
- if (res.status == 'SUCCESS'):
- url = reverse(showplot,args=[str(res.result)])
- return HttpResponseRedirect(url)
- else:
- m = "Task status: "+str(res.status)
-
- return render(request,'waiting_page.html',{'message':m})
-
-def showplot(request,imagename=""):
- return render(request,'image_page.html',{'imagename':'plots/'+imagename})
-
-
-def contact(request):
- if request.method == 'POST':
- form = ContactForm(request.POST)
- if form.is_valid():
- cd = form.cleaned_data
- send_mail(
- cd['subject'],
- cd['message'],
- cd.get('email', 'noreply@example.com'),
- ['siteowner@example.com'],
- )
- return HttpResponseRedirect('/contact/thanks/')
- else:
- form = ContactForm()
- return render(request, 'contact_form.html', {'form': form})
-
-def interactiveplot(request):
- x = np.linspace(-10,10,num=1200)
- y = x**2-5*x+4
- fig = plt.figure(figsize=(6,5))
- graph = plt.plot(x,y)
- g = mpld3.fig_to_html(fig)
- # js_data = json.dumps(mpld3.fig_to_dict(fig))
- plt.close()
- # return render(request,'interactiveplot.html',{"my_data":js_data})
- return HttpResponse(g)
diff --git a/rowsandall_app/wsgi.py b/rowsandall_app/wsgi.py
index a6d5107e..62686321 100644
--- a/rowsandall_app/wsgi.py
+++ b/rowsandall_app/wsgi.py
@@ -13,4 +13,4 @@ from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "rowsandall_app.settings")
-application = get_wsgi_application()
+application = get_wsgi_application()