diff --git a/rowers/dataprep.py b/rowers/dataprep.py index 1891625b..e51e6631 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -26,7 +26,7 @@ from rowingdata import ( MysteryParser, BoatCoachOTWParser, painsledDesktopParser, speedcoachParser, ErgStickParser, SpeedCoach2Parser, FITParser, fitsummarydata, - make_cumvalues, + make_cumvalues,cumcpdata, summarydata, get_file_type, ) @@ -41,7 +41,7 @@ import itertools import math from tasks import ( handle_sendemail_unrecognized, handle_sendemail_breakthrough, - handle_sendemail_hard, handle_updatecp + handle_sendemail_hard, handle_updatecp,handle_updateergcp ) from django.conf import settings @@ -426,20 +426,22 @@ def paceformatsecs(values): return out -def getcpdata_sql(rower_id): +def getcpdata_sql(rower_id,table='cpdata'): engine = create_engine(database_url, echo=False) - query = sa.text('SELECT delta,cp from cpdata WHERE user={rower_id};'.format( - rower_id=rower_id + query = sa.text('SELECT * from {table} WHERE user={rower_id};'.format( + rower_id=rower_id, + table=table, )) connection = engine.raw_connection() df = pd.read_sql_query(query, engine) return df -def deletecpdata_sql(rower_id): +def deletecpdata_sql(rower_id,table='cpdata'): engine = create_engine(database_url, echo=False) - query = sa.text('DELETE from cpdata WHERE user={rower_id};'.format( - rower_id=rower_id + query = sa.text('DELETE from {table} WHERE user={rower_id};'.format( + rower_id=rower_id, + table=table, )) with engine.connect() as conn, conn.begin(): try: @@ -451,7 +453,7 @@ def deletecpdata_sql(rower_id): -def updatecpdata_sql(rower_id,delta,cp): +def updatecpdata_sql(rower_id,delta,cp,table='cpdata',distance=[]): deletecpdata_sql(rower_id) df = pd.DataFrame( { @@ -461,9 +463,12 @@ def updatecpdata_sql(rower_id,delta,cp): } ) + if not distance.empty: + df['distance'] = distance + engine = create_engine(database_url, echo=False) with engine.connect() as conn, conn.begin(): - df.to_sql('cpdata', engine, if_exists='append', index=False) + df.to_sql(table, engine, if_exists='append', index=False) conn.close() engine.dispose() @@ -484,7 +489,19 @@ def runcpupdate(rower): else: res = queue.enqueue(handle_updatecp,rower.id,theids) - +def fetchcperg(rower,theworkouts): + theids = [int(w.id) for w in theworkouts] + thefilenames = [w.csvfilename for w in theworkouts] + cpdf = getcpdata_sql(rower.id,table='ergcpdata') + + if settings.DEBUG: + res = handle_updateergcp.delay(rower.id,thefilenames,debug=True) + else: + res = queue.enqueue(handle_updateergcp,rower.id,thefilenames) + + return cpdf + + def fetchcp(rower,theworkouts): # get all power data from database (plus workoutid) theids = [int(w.id) for w in theworkouts] diff --git a/rowers/dataprepnodjango.py b/rowers/dataprepnodjango.py index 67ded4e6..767ace9f 100644 --- a/rowers/dataprepnodjango.py +++ b/rowers/dataprepnodjango.py @@ -548,14 +548,30 @@ def read_df_sql(id,debug=False): engine.dispose() return df -def deletecpdata_sql(rower_id,debug=False): +def getcpdata_sql(rower_id,table='cpdata',debug=False): if debug: engine = create_engine(database_url_debug, echo=False) else: engine = create_engine(database_url, echo=False) - query = sa.text('DELETE from cpdata WHERE user={rower_id};'.format( - rower_id=rower_id + query = sa.text('SELECT * from {table} WHERE user={rower_id};'.format( + rower_id=rower_id, + table=table, + )) + connection = engine.raw_connection() + df = pd.read_sql_query(query, engine) + + return df + +def deletecpdata_sql(rower_id,table='cpdata',debug=False): + if debug: + engine = create_engine(database_url_debug, echo=False) + else: + engine = create_engine(database_url, echo=False) + + query = sa.text('DELETE from {table} WHERE user={rower_id};'.format( + rower_id=rower_id, + table=table, )) with engine.connect() as conn, conn.begin(): try: @@ -567,8 +583,8 @@ def deletecpdata_sql(rower_id,debug=False): -def updatecpdata_sql(rower_id,delta,cp,debug=False): - deletecpdata_sql(rower_id,debug=debug) +def updatecpdata_sql(rower_id,delta,cp,table='cpdata',distance=pd.Series([]),debug=False): + deletecpdata_sql(rower_id,table=table,debug=debug) df = pd.DataFrame( { 'delta':delta, @@ -577,17 +593,22 @@ def updatecpdata_sql(rower_id,delta,cp,debug=False): } ) + if not distance.empty: + df['distance'] = distance + if debug: engine = create_engine(database_url_debug, echo=False) else: engine = create_engine(database_url, echo=False) with engine.connect() as conn, conn.begin(): - df.to_sql('cpdata', engine, if_exists='append', index=False) + df.to_sql(table, engine, if_exists='append', index=False) conn.close() engine.dispose() + + def smalldataprep(therows,xparam,yparam1,yparam2): diff --git a/rowers/forms.py b/rowers/forms.py index 34b14e4d..4923a517 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -152,6 +152,11 @@ class WorkFlowMiddlePanelElement(forms.Form): # The form to indicate additional actions to be performed immediately # after a successful upload + +nextpages = list(landingpages) +nextpages.append(('workout_upload_view','Upload Another File')) +nextpages = tuple(nextpages) + class UploadOptionsForm(forms.Form): plotchoices = ( ('timeplot','Time Plot'), @@ -181,9 +186,9 @@ class UploadOptionsForm(forms.Form): makeprivate = forms.BooleanField(initial=False,required=False, label='Make Workout Private') - landingpage = forms.ChoiceField(choices=landingpages, + landingpage = forms.ChoiceField(choices=nextpages, initial='workout_edit_view', - label='Landing Page') + label='After Upload, go to') class Meta: fields = ['make_plot','plottype','upload_toc2','makeprivate'] diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index dff5c227..29edb78c 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -41,6 +41,7 @@ import pandas as pd from pytz import timezone as tz,utc from django.utils.timezone import get_current_timezone from django.utils.timezone import activate +from django.utils import timezone activate(settings.TIME_ZONE) thetimezone = get_current_timezone() @@ -1065,10 +1066,9 @@ def interactive_otwcpchart(powerdf,promember=0): return [script,div,p1,ratio,message] -def interactive_cpchart(thedistances,thesecs,theavpower, +def interactive_cpchart(rower,thedistances,thesecs,theavpower, theworkouts,promember=0): - message = 0 # plot tools if (promember==1): @@ -1086,7 +1086,7 @@ def interactive_cpchart(thedistances,thesecs,theavpower, p = pd.Series(500./velo) p2 = p.fillna(method='ffill').apply(lambda x: timedeltaconv(x)) - + source = ColumnDataSource( data = dict( dist = thedistances, @@ -1108,6 +1108,7 @@ def interactive_cpchart(thedistances,thesecs,theavpower, paulslope = 5.0/np.log10(2.0) paulintercept = p[0]-paulslope*np.log10(thedistances[0]) + fitx = pd.Series(np.arange(100)*2*max(np.log10(thedistances))/100.) fitp = paulslope*fitx+paulintercept @@ -1198,16 +1199,14 @@ def interactive_cpchart(thedistances,thesecs,theavpower, plot.xaxis.axis_label = "Duration (seconds)" plot.yaxis.axis_label = "Power (W)" - therows = [] - for workout in theworkouts: - f1 = workout.csvfilename - rowdata = rdata(f1) - if rowdata != 0: - therows.append(rowdata) - cpdata = cumcpdata(therows) + cpdata = dataprep.fetchcperg(rower, theworkouts) - velo = cpdata['Distance']/cpdata['Delta'] + if cpdata.empty: + message = 'Calculations are running in the background. Please refresh this page to see updated results' + return ['','',paulslope,paulintercept,p1,message] + + velo = cpdata['distance']/cpdata['delta'] p = 500./velo @@ -1215,12 +1214,12 @@ def interactive_cpchart(thedistances,thesecs,theavpower, source2 = ColumnDataSource( data = dict( - duration = cpdata['Delta'], - power = cpdata['CP'], + duration = cpdata['delta'], + power = cpdata['cp'], tim = niceformat( - cpdata['Delta'].fillna(method='ffill').apply(lambda x: timedeltaconv(x)) + cpdata['delta'].fillna(method='ffill').apply(lambda x: timedeltaconv(x)) ), - dist = cpdata['Distance'], + dist = cpdata['distance'], pace = nicepaceformat(p2), ) ) @@ -1252,6 +1251,7 @@ def interactive_cpchart(thedistances,thesecs,theavpower, script, div = components(plot) + return [script,div,paulslope,paulintercept,p1,message] def interactive_windchart(id=0,promember=0): diff --git a/rowers/models.py b/rowers/models.py index 2c84d272..1b233404 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -653,6 +653,18 @@ class cpdata(models.Model): index_together = ['user'] app_label = 'rowers' +# Storing data for the OTW CP chart +class ergcpdata(models.Model): + delta = models.IntegerField(default=0) + cp = models.FloatField(default=0) + distance = models.FloatField(default=0) + user = models.IntegerField(default=0) + + class Meta: + db_table = 'ergcpdata' + index_together = ['user'] + app_label = 'rowers' + # A wrapper around the png files class GraphImage(models.Model): filename = models.CharField(default='',max_length=150,blank=True,null=True) diff --git a/rowers/tasks.py b/rowers/tasks.py index 9ffe5da8..372dc1cb 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -400,7 +400,24 @@ def handle_otwsetpower(f1, boattype, weightvalue, return 1 -# This function generates all the static (PNG image) plots + +@app.task +def handle_updateergcp(rower_id,workoutfilenames,debug=False): + therows = [] + for f1 in workoutfilenames: + rowdata = rdata(f1) + if rowdata != 0: + therows.append(rowdata) + + cpdata = rowingdata.cumcpdata(therows) + cpdata.columns = cpdata.columns.str.lower() + + updatecpdata_sql(rower_id,cpdata['delta'],cpdata['cp'], + table='ergcpdata',distance=cpdata['distance'], + debug=debug) + + return 1 + @app.task def handle_updatecp(rower_id,workoutids,debug=False): diff --git a/rowers/urls.py b/rowers/urls.py index a218c9b3..d33c32b6 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -177,7 +177,7 @@ urlpatterns = [ url(r'^graph/(?P\d+)/deleteconfirm$',views.graph_delete_confirm_view), url(r'^graph/(?P\d+)/delete$',views.graph_delete_view), url(r'^workout/upload/team/$',views.team_workout_upload_view), - url(r'^workout/upload/$',views.workout_upload_view), + url(r'^workout/upload/$',views.workout_upload_view,name='workout_upload_view'), url(r'^workout/(?P\d+)/histo$',views.workout_histo_view), url(r'^workout/(?P\d+)/task$',views.workout_test_task_view), url(r'^workout/(?P\d+)/forcecurve$',views.workout_forcecurve_view), diff --git a/rowers/views.py b/rowers/views.py index e40aedfb..ad43fcf3 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -2733,6 +2733,7 @@ def rankings_view(request,theuser=0, rankingdurations.append(datetime.time(minute=1)) rankingdurations.append(datetime.time(minute=4)) rankingdurations.append(datetime.time(minute=30)) + rankingdurations.append(datetime.time(hour=1,minute=15)) rankingdurations.append(datetime.time(hour=1)) thedistances = [] @@ -2787,8 +2788,10 @@ def rankings_view(request,theuser=0, # create interactive plot if len(thedistances) !=0 : - res = interactive_cpchart(thedistances,thesecs,theavpower, - theworkouts,promember=promember) + res = interactive_cpchart( + r,thedistances,thesecs,theavpower, + theworkouts,promember=promember + ) script = res[0] div = res[1] paulslope = res[2] @@ -7803,10 +7806,14 @@ def workout_upload_view(request, else: messages.error(request,message) - url = reverse(landingpage, - kwargs = { - 'id':w.id, - }) + if landingpage != 'workout_upload_view': + url = reverse(landingpage, + kwargs = { + 'id':w.id, + }) + else: + url = reverse(landingpage) + return HttpResponseRedirect(url) else: response = render(request,