diff --git a/rowers/dataprep.py b/rowers/dataprep.py index 12744d7f..906b66fe 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -15,11 +15,23 @@ from pytz import timezone as tz,utc from django.utils.timezone import get_current_timezone thetimezone = get_current_timezone() +from rowingdata import ( + TCXParser,RowProParser,ErgDataParser,TCXParserNoHR, + BoatCoachParser,RowPerfectParser,BoatCoachAdvancedParser, + MysteryParser, + painsledDesktopParser,speedcoachParser,ErgStickParser, + SpeedCoach2Parser,FITParser,fitsummarydata, + make_cumvalues, + summarydata,get_file_type, + ) +import os import pandas as pd import numpy as np import itertools +from tasks import handle_sendemail_unrecognized + from django.conf import settings from sqlalchemy import create_engine import sqlalchemy as sa @@ -212,10 +224,46 @@ def new_workout_from_file(r,f2, workouttype='rower', title='Workout', notes=''): - + message = None fileformat = get_file_type(f2) summary = '' - # handle non-Painsled + 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': + os.remove(f2) + 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." + return (0,message) + + # Some people try to upload RowPro summary logs + if fileformat == 'rowprolog': + os.remove(f2) + message = "This RowPro logbook summary does not contain stroke data. Please use the Stroke Data CSV file for the individual workout in your log." + return (0,message) + + # 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" + if settings.DEBUG: + res = handle_sendemail_unrecognized.delay(f2, + request.user.email) + + else: + res = queuehigh.enqueue(handle_sendemail_unrecognized, + f2,request.user.email) + return (0,'message') + + # handle non-Painsled by converting it to painsled compatible CSV if (fileformat != 'csv'): # handle RowPro: if (fileformat == 'rp'): @@ -232,10 +280,18 @@ def new_workout_from_file(r,f2, if (fileformat == 'tcxnohr'): row = TCXParserNoHR(f2) + # handle RowPerfect + if (fileformat == 'rowperfect3'): + row = RowPerfectParser(f2) + # handle ErgData if (fileformat == 'ergdata'): row = ErgDataParser(f2) + # handle Mike + if (fileformat == 'bcmike'): + row = BoatCoachAdvancedParser(f2) + # handle BoatCoach if (fileformat == 'boatcoach'): row = BoatCoachParser(f2) @@ -251,7 +307,10 @@ def new_workout_from_file(r,f2, # handle speed coach GPS 2 if (fileformat == 'speedcoach2'): row = SpeedCoach2Parser(f2) - summary = row.allstats() + try: + summary = row.allstats() + except: + pass # handle ErgStick @@ -266,24 +325,30 @@ def new_workout_from_file(r,f2, summary = s.summarytext - f_to_be_deleted = f2 - # should delete file - f2 = f2[:-4]+'o.csv' - row.write_csv(f2,gzip=True) + 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') + #os.remove(f2) + try: + os.remove(f_to_be_deleted) + except: + os.remove(f_to_be_deleted+'.gz') + + powerperc = 100*np.array([r.pw_ut2, + r.pw_ut1, + r.pw_at, + r.pw_tr,r.pw_an])/r.ftp # 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,ftp=r.ftp) + hrtr=r.tr,hran=r.an,ftp=r.ftp, + powerperc=powerperc,powerzones=r.powerzones) row = rdata(f2,rower=rr) if row == 0: - return HttpResponse("Error: CSV Data File Not Found") + return (0,'Error: CSV data file not found') # auto smoothing pace = row.df[' Stroke500mPace (sec/500m)'].values @@ -336,6 +401,10 @@ def new_workout_from_file(r,f2, totaltime = totaltime+row.df.ix[0,' ElapsedTime (sec)'] hours = int(totaltime/3600.) + if hours>23: + message = 'Warning: The workout duration was longer than 23 hours' + hours = 23 + minutes = int((totaltime - 3600.*hours)/60.) seconds = int(totaltime - 3600.*hours - 60.*minutes) tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds)) @@ -362,9 +431,10 @@ def new_workout_from_file(r,f2, w.save() # put stroke data in database - res = dataprep(row.df,id=w.id,bands=True,barchart=True,otwpower=True,empower=True) + res = dataprep(row.df,id=w.id,bands=True, + barchart=True,otwpower=True,empower=True) - return True + return (w.id,message) # Compare the data from the CSV file and the database # Currently only calculates number of strokes. To be expanded with diff --git a/rowers/teams.py b/rowers/teams.py index 4ebc24e1..0e896db5 100644 --- a/rowers/teams.py +++ b/rowers/teams.py @@ -31,6 +31,8 @@ from rowers.tasks import ( inviteduration = 14 # days def update_team(t,name,manager,private,notes): + if t.manager != manager: + return (0,'You are not the manager of this team') try: t.name = name t.manager = manager diff --git a/rowers/urls.py b/rowers/urls.py index 2ee4b855..0d88ebe3 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -154,7 +154,7 @@ urlpatterns = [ url(r'^workout/(?P\d+)/export/c/(?P\w+.*)/s/(?P\w+.*)$',views.workout_edit_view), url(r'^workout/(?P\d+)/edit/c/(?P.+.*)$',views.workout_edit_view), url(r'^workout/(?P\d+)/edit/s/(?P.+.*)$',views.workout_edit_view), - url(r'^workout/(\d+)/edit$',views.workout_edit_view), + url(r'^workout/(?P\d+)/edit$',views.workout_edit_view), url(r'^workout/(?P\d+)/advanced/c/(?P.+.*)$',views.workout_advanced_view), url(r'^workout/(?P\d+)/advanced/s/(?P.+.*)$',views.workout_advanced_view), url(r'^workout/(?P\d+)/geeky$',views.workout_geeky_view), diff --git a/rowers/views.py b/rowers/views.py index 7cee538d..89ad45d6 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -4053,6 +4053,7 @@ def workout_getc2workout_view(request,c2id): # This is the main view for processing uploaded files @login_required() def workout_upload_view(request,message=""): + r = Rower.objects.get(user=request.user) if request.method == 'POST': form = DocumentsForm(request.POST,request.FILES) optionsform = UploadOptionsForm(request.POST) @@ -4070,283 +4071,82 @@ def workout_upload_view(request,message=""): f1 = res[0] # file name f2 = res[1] # file name incl media directory - - # 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': - os.remove(f2) - 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." + + id,message = dataprep.new_workout_from_file(r,f2, + workouttype=workouttype, + title = t, + notes='') + if not id: url = reverse(workout_upload_view, args=[str(message)]) response = HttpResponseRedirect(url) return response - # Some people try to upload RowPro summary logs - if fileformat == 'rowprolog': - os.remove(f2) - 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 + else: + if message: + url = reverse(workout_edit_view, + kwargs = { + 'id':id, + 'message':message, + }) + else: + url = reverse(workout_edit_view, + kwargs = { + 'id':id, + }) + + response = HttpResponseRedirect(url) + w = Workout.objects.get(id=id) - # 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, - args=[str(message)]) - response = HttpResponseRedirect(url) - if settings.DEBUG: - res = handle_sendemail_unrecognized.delay(f2, - request.user.email) - - else: - res = queuehigh.enqueue(handle_sendemail_unrecognized, - f2,request.user.email) - - return response - - summary = '' - # handle non-Painsled by converting it to painsled - # compatible CSV - try: - if (fileformat != 'csv'): - # handle RowPro: - if (fileformat == 'rp'): - row = RowProParser(f2) - # handle TCX - if (fileformat == 'tcx'): - row = TCXParser(f2) - - # handle Mystery - if (fileformat == 'mystery'): - row = MysteryParser(f2) - - # handle RowPerfect - if (fileformat == 'rowperfect3'): - row = RowPerfectParser(f2) - - # handle TCX no HR - if (fileformat == 'tcxnohr'): - row = TCXParserNoHR(f2) - - # handle ErgData - if (fileformat == 'ergdata'): - row = ErgDataParser(f2) - - # handle Mike - if (fileformat == 'bcmike'): - row = BoatCoachAdvancedParser(f2) - - # handle BoatCoach - if (fileformat == 'boatcoach'): - row = BoatCoachParser(f2) - - # handle painsled desktop - if (fileformat == 'painsleddesktop'): - row = painsledDesktopParser(f2) - - # handle speed coach GPS - if (fileformat == 'speedcoach'): - row = speedcoachParser(f2) - - # handle speed coach GPS 2 - if (fileformat == 'speedcoach2'): - row = SpeedCoach2Parser(f2) - try: - summary = row.allstats() - except: - pass - - # handle ErgStick - if (fileformat == 'ergstick'): - row = ErgStickParser(f2) - - # 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) - - try: - os.remove(f_to_be_deleted) - except: - os.remove(f_to_be_deleted+'.gz') - - # make Workout object and put in database + if (make_plot): + imagename = f1[:-4]+'.png' + fullpathimagename = 'static/plots/'+imagename + u = request.user r = Rower.objects.get(user=request.user) - powerperc = 100*np.array([r.pw_ut2, + powerperc = 100*np.array([r.pw_ut2, r.pw_ut1, r.pw_at, r.pw_tr,r.pw_an])/r.ftp - rr = rrower(hrmax=r.max,hrut2=r.ut2, - hrut1=r.ut1,hrat=r.at, - hrtr=r.tr,hran=r.an,ftp=r.ftp, - powerperc=powerperc,powerzones=r.powerzones) - row = rdata(f2,rower=rr) - if row == 0: - return HttpResponse("Error: CSV Data File Not Found") + hrpwrdata = { + 'hrmax':r.max, + 'hrut2':r.ut2, + 'hrut1':r.ut1, + 'hrat':r.at, + 'hrtr':r.tr, + 'hran':r.an, + 'ftp':r.ftp, + 'powerperc':serialize_list(powerperc), + 'powerzones':serialize_list(r.powerzones), + } - # auto smoothing - pace = row.df[' Stroke500mPace (sec/500m)'].values - velo = 500./pace - - f = row.df['TimeStamp (sec)'].diff().mean() - windowsize = 2*(int(10./(f)))+1 - if not 'originalvelo' in row.df: - row.df['originalvelo'] = velo + # make plot - asynchronous task + plotnrs = { + 'timeplot':1, + 'distanceplot':2, + 'pieplot':3, + } - if windowsize > 3 and windowsize