From 18c59d0fe47fde2e41ba72ad4c432e38a72cc9e2 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 5 Apr 2021 06:37:28 +0200 Subject: [PATCH] imports nk incl summary stats --- rowers/dataprep.py | 1 - rowers/models.py | 3 + rowers/nkstuff.py | 143 ++++++++++++++++++++++++++-- rowers/templates/menu_workout.html | 7 ++ rowers/templatetags/rowerfilters.py | 7 ++ rowers/uploads.py | 7 ++ rowers/urls.py | 3 +- rowers/views/importviews.py | 6 ++ rowers/views/workoutviews.py | 15 +++ 9 files changed, 180 insertions(+), 12 deletions(-) diff --git a/rowers/dataprep.py b/rowers/dataprep.py index 5eca407c..55da70ec 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -1856,7 +1856,6 @@ parsers = { def parsenonpainsled(fileformat,f2,summary,startdatetime=''): try: row = parsers[fileformat](f2) - print(row,'jet') if startdatetime != '': row.rowdatetime = arrow.get(startdatetime).datetime hasrecognized = True diff --git a/rowers/models.py b/rowers/models.py index 616a6216..9256b262 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -1002,6 +1002,7 @@ class Rower(models.Model): nkrefreshtoken = models.TextField(default='',max_length=1000, blank=True,null=True) nk_owner_id = models.BigIntegerField(default=0) + nk_auto_import = models.BooleanField(default=False) trainingpeaks_auto_export = models.BooleanField(default=False) @@ -3268,6 +3269,7 @@ class TombStone(models.Model): uploadedtounderarmour = models.BigIntegerField(default=0) uploadedtotp = models.BigIntegerField(default=0) uploadedtorunkeeper = models.BigIntegerField(default=0) + uploadedtonk = models.BigIntegerField(default=0) @receiver(models.signals.pre_delete,sender=Workout) def create_tombstone_on_delete(sender, instance, **kwargs): @@ -3278,6 +3280,7 @@ def create_tombstone_on_delete(sender, instance, **kwargs): uploadedtounderarmour = instance.uploadedtounderarmour, uploadedtotp = instance.uploadedtotp, uploadedtorunkeeper = instance.uploadedtorunkeeper, + uploadedtonk = instance.uploadedtonk ) t.save() diff --git a/rowers/nkstuff.py b/rowers/nkstuff.py index 930b898b..f55aeef5 100644 --- a/rowers/nkstuff.py +++ b/rowers/nkstuff.py @@ -210,8 +210,11 @@ def add_workout_from_data(user,nkid,data,strokedata,source='nk',splitdata=None, elapsedTime = data["elapsedTime"] totalDistanceGps = data["totalDistanceGps"] totalDistanceImp = data["totalDistanceImp"] - intervals = data["intervals"] + intervals = data["intervals"] # add intervals oarlockSessions = data["oarlockSessions"] + deviceId = data["deviceId"] # you could get the firmware version + + summary = get_nk_allstats(data,strokedata) # oarlock inboard, length, boat name if oarlockSessions: @@ -220,6 +223,11 @@ def add_workout_from_data(user,nkid,data,strokedata,source='nk',splitdata=None, oarLength = oarlocksession["oarLength"] # cm oarInboardLength = oarlocksession["oarInboardLength"] # cm seatNumber = oarlocksession["seatNumber"] + else: + boatName = '' + oarLength = 289 + oarInboardLength = 88 + seatNumber = 1 workouttype = "water" boattype = "1x" @@ -232,6 +240,9 @@ def add_workout_from_data(user,nkid,data,strokedata,source='nk',splitdata=None, 'workouttype': workouttype, 'boattype': boattype, 'nkid':nkid, + 'inboard': oarInboardLength/100., + 'oarlength': oarLength/100., + 'summary':summary, } session = requests.session() @@ -248,11 +259,125 @@ def add_workout_from_data(user,nkid,data,strokedata,source='nk',splitdata=None, except KeyError: workoutid = 1 + # evt update workout summary # return return workoutid,"" + +def get_nk_intervalstats(workoutdata,strokedata): + intervals = workoutdata['intervals'] + separator = "|" + stri = "Workout Details\n" + stri += "#-{sep}SDist{sep}-Split-{sep}-SPace-{sep}-Pwr-{sep}-SPM--{sep}-AvgHR-{sep}DPS-\n".format( + sep=separator) + + i = 0 + + for interval in intervals: + id = interval['id'] + sdist = interval['totalDistanceGps'] + avgpace = interval['avgPaceGps']/1000. + avgpacetd = timedelta(seconds=avgpace) + newpacestring = dataprep.strfdelta(avgpacetd) + + elapsedSeconds = interval['elapsedTime']/1000. # secs + elapsedTime = datetime.utcfromtimestamp(elapsedSeconds) + + newsplitstring = "%s.%i" % (elapsedTime.strftime("%M:%S"), elapsedTime.microsecond/100000) + + pwr = interval['avgPower'] + avghr = interval['avgHeartRate'] + spm = interval['avgStrokeRate'] + dps = interval['distStrokeGps'] + + stri += "{i:0>2}{sep}{sdist:0>5}{sep}{split}{sep}{space}{sep} {pwr:0>3} {sep}".format( + i=i + 1, + sdist=int(float(sdist)), + split=newsplitstring, + space=newpacestring, + pwr=int(pwr), + sep=separator, + ) + stri += " {spm} {sep} {avghr:0>3} {sep}{dps:0>4.1f}\n".format( + sep=separator, + avghr=avghr, + spm=spm, + dps=dps, + ) + + i += 1 + + return stri + +def get_nk_summary(workoutdata,strokedata): + + name = workoutdata['name'] + + avgpace = workoutdata['avgPaceGps']/1000. # secs + avgpacetd = timedelta(seconds=avgpace) + avgpacestring = dataprep.strfdelta(avgpacetd) + + elapsedSeconds = workoutdata['elapsedTime']/1000. # secs + elapsedTime = datetime.utcfromtimestamp(elapsedSeconds) + + timestring = "%s.%i" % (elapsedTime.strftime("%H:%M:%S"), elapsedTime.microsecond/100000) + + dist = workoutdata['totalDistanceGps'] + spm = workoutdata['avgStrokeRate'] + avghr = workoutdata['avgHeartRate'] + avgdps = workoutdata['distStrokeGps'] + maxhr = strokedata['heartRate'].max() + pwr = workoutdata['avgPower'] + + sep = "|" + + + stri1 = "Workout Summary - " + name + "\n" + stri1 += "--{sep}Total{sep}--Total---{sep}--Avg--{sep}-Avg-{sep}-Avg--{sep}-Avg-{sep}-Max-{sep}-Avg\n".format( + sep=sep) + stri1 += "--{sep}Dist-{sep}--Time----{sep}-Pace--{sep}-Pwr-{sep}-SPM--{sep}-HR--{sep}-HR--{sep}-DPS\n".format( + sep=sep) + + + + stri1 += "--{sep}{dist:0>5.0f}{sep}".format( + sep=sep, + dist=dist, + ) + + stri1 += timestring + sep + avgpacestring + + stri1 += "{sep}{avgpower:0>5.1f}".format( + sep=sep, + avgpower=pwr, + ) + + stri1 += "{sep} {avgsr:2.1f} {sep}{avghr:0>5.1f}{sep}".format( + avgsr=spm, + sep=sep, + avghr=avghr + ) + + stri1 += "{maxhr:0>5.1f}{sep}{avgdps:0>4.1f}\n".format( + sep=sep, + maxhr=maxhr, + avgdps=avgdps + ) + + return stri1 + + + + return stri1 + +def get_nk_allstats(data,workoutdata): + stri = get_nk_summary(data, workoutdata) + \ + get_nk_intervalstats(data, workoutdata) + return stri + + def get_workout(user,nkid): r = Rower.objects.get(user=user) if (r.nktoken == '') or (r.nktoken is None): @@ -277,9 +402,11 @@ def get_workout(user,nkid): # get strokes url = NK_API_LOCATION+"api/v1/sessions/strokes" response = requests.get(url,headers=headers,params=params) - if response != 200: - # error handling - pass + + + if response.status_code != 200: + # error handling and logging + return {},pd.DataFrame() jsonData = response.json() @@ -314,8 +441,8 @@ def get_workout(user,nkid): response = requests.get(url, headers=headers,params=params) if response.status_code != 200: - # error handling - pass + # error handling and logging + return {},df jsondata = response.json() workoutdata = {} @@ -323,8 +450,4 @@ def get_workout(user,nkid): if str(w['id']) == str(nkid): workoutdata = w - # not to_csv and run upload API! - #strokedata = df.to_json(orient='records') - #df.to_csv('~/Downloads/nk_logbook.csv') - return workoutdata, df diff --git a/rowers/templates/menu_workout.html b/rowers/templates/menu_workout.html index 8c0d8385..51aefb8b 100644 --- a/rowers/templates/menu_workout.html +++ b/rowers/templates/menu_workout.html @@ -155,6 +155,13 @@ {% endif %} +
  • + {% if workout.uploadedtonk %} + + NK LiNK + + {% endif %} +
  • {% if workout.uploadedtostrava %} diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py index 89f5c1c6..af9fc57e 100644 --- a/rowers/templatetags/rowerfilters.py +++ b/rowers/templatetags/rowerfilters.py @@ -106,6 +106,13 @@ def adaptive(s): return u +@register.filter +def nkviewerlink(workout): + url = "https://viewer-stage.nkrowlink.com/workout-analysis/{nkid}".format(nkid=workout.uploadedtonk) + + return url + + @register.filter def boatclass(s): u = s diff --git a/rowers/uploads.py b/rowers/uploads.py index adf2688f..23546796 100644 --- a/rowers/uploads.py +++ b/rowers/uploads.py @@ -541,6 +541,13 @@ def do_sync(w,options, quick=False): except KeyError: pass + try: + if options['nkid'] != 0 and options['nkid'] != '': + w.uploadedtonk = options['nkid'] + w.save() + except KeyError: + pass + try: if options['garminid'] != 0 and options['garminid'] != '': w.uploadedtogarmin = options['garminid'] diff --git a/rowers/urls.py b/rowers/urls.py index d92e3167..ad8dfe03 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -568,6 +568,7 @@ urlpatterns = [ re_path(r'^workout/c2import/all/$',views.workout_getc2workout_all,name='workout_getc2workout_all'), re_path(r'^workout/c2import/all/(?P\d+)/$',views.workout_getc2workout_all,name='workout_getc2workout_all'), re_path(r'^workout/nkimport/$',views.workout_nkimport_view,name='workout_nkimport_view'), + re_path(r'^workout/nkimport/all/$',views.workout_getnkworkout_all,name='workout_getnkworkout_all'), re_path(r'^workout/rp3import/(?P\d+)/$',views.workout_getrp3importview, name='workout_getrp3importview'), re_path(r'^workout/rp3import/all/$',views.workout_getrp3workout_all,name='workout_getrp3workout_all'), @@ -731,7 +732,7 @@ urlpatterns = [ re_path(r'^legal', TemplateView.as_view(template_name='legal.html'),name='legal'), re_path(r'^register/$',views.rower_register_view,name='rower_register_view'), re_path(r'^coachregister/$',views.freecoach_register_view,name='freecoach_register_view'), - path('activate///',views.useractivate, name='useractivate'), + path('activate///',views.useractivate, name='useractivate'), re_path(r'^register/thankyou/$', TemplateView.as_view(template_name='registerthankyou.html'), name='registerthankyou'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/workflow/$',views.workout_workflow_view, name='workout_workflow_view'), diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py index e1516914..42f78a20 100644 --- a/rowers/views/importviews.py +++ b/rowers/views/importviews.py @@ -815,6 +815,12 @@ def rower_process_nkcallback(request): url = reverse('rower_exportsettings_view') return HttpResponseRedirect(url) +@login_required() +def workout_getnkworkout_all(request): + messages.error(request,"Future Functionality, coming soon!") + url = reverse('workout_nkimport_view') + return HttpResponseRedirect(url) + @login_required() @permission_required('rower.is_coach',fn=get_user_by_userid, raise_exception=True) def workout_nkimport_view(request,userid=0): diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index ca46185e..b1f1ef56 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -5033,6 +5033,21 @@ def workout_upload_api(request): plottype = r.staticchartonupload res, jobid = uploads.make_plot(r,w,f1,f2,plottype,t) + try: + oarlength = post_data['oarlength'] + inboard = post_data['inboard'] + w.inboard = inboard + w.oarlength = oarlength + w.save() + except KeyError: + pass + + try: + summary = post_data['summary'] + w.summary = summary + w.save() + except KeyError: + pass uploads.do_sync(w,post_data,quick=True)