diff --git a/rowers/nkimportutils.py b/rowers/nkimportutils.py new file mode 100644 index 00000000..db98450e --- /dev/null +++ b/rowers/nkimportutils.py @@ -0,0 +1,244 @@ +import pandas as pd +import datetime +from datetime import timedelta +from uuid import uuid4 + +from rowsandall_app.settings import UPLOAD_SERVICE_SECRET, UPLOAD_SERVICE_URL +from rowsandall_app.settings import NK_API_LOCATION + +import requests + +def strfdelta(tdelta): + try: + minutes, seconds = divmod(tdelta.seconds, 60) + tenths = int(tdelta.microseconds / 1e5) + except AttributeError: + minutes, seconds = divmod(tdelta.view(np.int64), 60e9) + seconds, rest = divmod(seconds, 1e9) + tenths = int(rest / 1e8) + res = "{minutes:0>2}:{seconds:0>2}.{tenths:0>1}".format( + minutes=minutes, + seconds=seconds, + tenths=tenths, + ) + + return res + + +def add_workout_from_data(userid,nkid,data,strokedata,source='nk',splitdata=None, + workoutsource='nklinklogbook'): + + csvfilename = 'media/{code}_{nkid}.csv.gz'.format( + nkid=nkid, + code = uuid4().hex[:16] + ) + + strokedata.to_csv(csvfilename, index_label='index', compression='gzip') + + title = data["name"] + speedInput = data["speedInput"] + elapsedTime = data["elapsedTime"] + totalDistanceGps = data["totalDistanceGps"] + totalDistanceImp = data["totalDistanceImp"] + intervals = data["intervals"] # add intervals + oarlockSessions = data["oarlockSessions"] + deviceId = data["deviceId"] # you could get the firmware version + + summary = get_nk_allstats(data,strokedata) + + speedInput = data['speedInput'] # 0 = GPS; 1 = Impeller + + # oarlock inboard, length, boat name + if oarlockSessions: + oarlocksession = oarlockSessions[0] # should take seatIndex + boatName = oarlocksession["boatName"] + oarLength = oarlocksession["oarLength"] # cm + oarInboardLength = oarlocksession["oarInboardLength"] # cm + seatNumber = oarlocksession["seatNumber"] + else: + boatName = '' + oarLength = 289 + oarInboardLength = 88 + seatNumber = 1 + + workouttype = "water" + boattype = "1x" + + uploadoptions = { + 'secret': UPLOAD_SERVICE_SECRET, + 'user':userid, + 'file': csvfilename, + 'title': title, + 'workouttype': workouttype, + 'boattype': boattype, + 'nkid':nkid, + 'inboard': oarInboardLength/100., + 'oarlength': oarLength/100., + 'summary':summary, + } + + session = requests.session() + newHeaders = {'Content-type': 'application/json', 'Accept': 'text/plain'} + session.headers.update(newHeaders) + + response = session.post(UPLOAD_SERVICE_URL,json=uploadoptions) + + if response.status_code != 200: + return 0,response.text + + try: + workoutid = response.json()['id'] + 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 = strfdelta(avgpacetd) + + elapsedSeconds = interval['elapsedTime']/1000. # secs + elapsedTime = datetime.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 = strfdelta(avgpacetd) + + elapsedSeconds = workoutdata['elapsedTime']/1000. # secs + elapsedTime = datetime.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): +def getdict(x, seatIndex=1): + seatStrokes = pd.DataFrame(x) + try: + seatStrokes = seatStrokes.set_index('seatIndex') + return dict(seatStrokes.loc[seatIndex]) + except KeyError: + pass + + return {} + +def strokeDataToDf(strokeData): + + df = pd.DataFrame.from_dict(strokeData) + + oarlockData = df['oarlockStrokes'] + + oarlockData = oarlockData.apply(lambda x:getdict(x, seatIndex=1)) + df2 = pd.DataFrame.from_records(oarlockData.values) + + #df.set_index('timestamp',inplace=True) + + if not df2.empty: + #df2.set_index('timestamp',inplace=True) + df = df.merge(df2,left_index=True,right_index=True) + df = df.rename(columns={"timestamp_x":"timestamp"}) + + df = df.drop('oarlockStrokes',axis=1) + df.sort_values(by='timestamp',ascending=True,inplace=True) + df.fillna(inplace=True,method='ffill') + + + + return df diff --git a/rowers/nkstuff.py b/rowers/nkstuff.py index 709e9db7..4a3dbe2d 100644 --- a/rowers/nkstuff.py +++ b/rowers/nkstuff.py @@ -22,7 +22,7 @@ queuehigh = django_rq.get_queue('low') from rowers.rower_rules import is_workout_user, ispromember from iso8601 import ParseError -from rowers.utils import myqueue,get_nk_summary, get_nk_allstats, get_nk_intervalstats,getdict +from rowers.utils import myqueue import rowers.mytypes as mytypes import gzip @@ -42,6 +42,7 @@ except ImportError: JSONDecodeError = ValueError from rowers.imports import * +from rowers.nkimportutils import * oauth_data = { 'client_id': NK_CLIENT_ID, @@ -209,7 +210,7 @@ def rower_nk_token_refresh(user): def make_authorization_url(request): return imports_make_authorization_url(oauth_data) -def get_nk_workout_list(user,fake=False): +def get_nk_workout_list(user,fake=False,startTime=0,endTime=0): r = Rower.objects.get(user=user) if (r.nktoken == '') or (r.nktoken is None): @@ -220,10 +221,12 @@ def get_nk_workout_list(user,fake=False): return custom_exception_handler(401,s) else: # ready to fetch. Hurray - endTime = int(arrow.now().timestamp())*1000 - endTime = str(endTime) - startTime = arrow.now()-timedelta(days=30)*1000 - startTime = str(int(startTime.timestamp())) + if not endTime: + endTime = int(arrow.now().timestamp())*1000 + endTime = str(endTime) + if not startTime: + startTime = arrow.now()-timedelta(days=30) + startTime = str(int(startTime.timestamp())*1000) authorizationstring = str('Bearer ' + r.nktoken) headers = {'Authorization': authorizationstring, 'user-agent': 'sanderroosendaal', @@ -244,81 +247,6 @@ def get_nk_workout_list(user,fake=False): # -def add_workout_from_data(user,nkid,data,strokedata,source='nk',splitdata=None, - workoutsource='nklinklogbook'): - - csvfilename = 'media/{code}_{nkid}.csv.gz'.format( - nkid=nkid, - code = uuid4().hex[:16] - ) - - strokedata.to_csv(csvfilename, index_label='index', compression='gzip') - - userid = user.id - - title = data["name"] - speedInput = data["speedInput"] - elapsedTime = data["elapsedTime"] - totalDistanceGps = data["totalDistanceGps"] - totalDistanceImp = data["totalDistanceImp"] - 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: - oarlocksession = oarlockSessions[0] # should take seatIndex - boatName = oarlocksession["boatName"] - oarLength = oarlocksession["oarLength"] # cm - oarInboardLength = oarlocksession["oarInboardLength"] # cm - seatNumber = oarlocksession["seatNumber"] - else: - boatName = '' - oarLength = 289 - oarInboardLength = 88 - seatNumber = 1 - - workouttype = "water" - boattype = "1x" - - uploadoptions = { - 'secret': UPLOAD_SERVICE_SECRET, - 'user':userid, - 'file': csvfilename, - 'title': title, - 'workouttype': workouttype, - 'boattype': boattype, - 'nkid':nkid, - 'inboard': oarInboardLength/100., - 'oarlength': oarLength/100., - 'summary':summary, - } - - session = requests.session() - newHeaders = {'Content-type': 'application/json', 'Accept': 'text/plain'} - session.headers.update(newHeaders) - - response = session.post(UPLOAD_SERVICE_URL,json=uploadoptions) - - if response.status_code != 200: - return 0,response.text - - try: - workoutid = response.json()['id'] - except KeyError: - workoutid = 1 - - - # evt update workout summary - - # return - return workoutid,"" - - - - def get_workout(user,nkid): r = Rower.objects.get(user=user) if (r.nktoken == '') or (r.nktoken is None): @@ -338,8 +266,6 @@ def get_workout(user,nkid): 'Content-Type': 'application/json', } - - # get strokes url = NK_API_LOCATION+"api/v1/sessions/strokes" response = requests.get(url,headers=headers,params=params) @@ -353,22 +279,7 @@ def get_workout(user,nkid): strokeData = jsonData[str(nkid)] - df = pd.DataFrame.from_dict(strokeData) - oarlockData = df['oarlockStrokes'] - - oarlockData = oarlockData.apply(lambda x:getdict(x, seatIndex=1)) - df2 = pd.DataFrame.from_records(oarlockData.values) - - df.set_index('timestamp') - - if not df2.empty: - df2.set_index('timestamp') - df = df.merge(df2,left_index=True,right_index=True) - df = df.rename(columns={"timestamp_x":"timestamp"}) - - df = df.drop('oarlockStrokes',axis=1) - df.sort_values(by='timestamp',ascending=True,inplace=True) - df.fillna(inplace=True,method='ffill') + df = strokeDataToDf(strokeData) # get workout data timestampbegin = df['timestamp'].min() @@ -379,6 +290,7 @@ def get_workout(user,nkid): 'startTime':timestampbegin-1, 'endTime': timestampend+1, } + response = requests.get(url, headers=headers,params=params) if response.status_code != 200: diff --git a/rowers/tasks.py b/rowers/tasks.py index 84957b55..63fa17b1 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -111,7 +111,7 @@ def safetimedelta(x): siteurl = SITE_URL -from rowers.utils import get_nk_summary, get_nk_allstats, get_nk_intervalstats,getdict +from rowers.nkimportutils import get_nk_summary, get_nk_allstats, get_nk_intervalstats,getdict,strokeDataToDf # testing task @@ -2981,22 +2981,8 @@ def handle_nk_async_workout(alldata,userid,nktoken,nkid,delaysec,defaulttimezone jsonData = response.json() strokeData = jsonData[str(nkid)] - df = pd.DataFrame.from_dict(strokeData) - oarlockData = df['oarlockStrokes'] - oarlockData = oarlockData.apply(lambda x:getdict(x, seatIndex=1)) - df2 = pd.DataFrame.from_records(oarlockData.values) - - df.set_index('timestamp') - - if not df2.empty: - df2.set_index('timestamp') - df = df.merge(df2,left_index=True,right_index=True) - df = df.rename(columns={"timestamp_x":"timestamp"}) - - df = df.drop('oarlockStrokes',axis=1) - df.sort_values(by='timestamp',ascending=True,inplace=True) - df.fillna(inplace=True,method='ffill') + df = strokeDataToDf(strokeData) # get workout data timestampbegin = df['timestamp'].min() @@ -3009,7 +2995,7 @@ def handle_nk_async_workout(alldata,userid,nktoken,nkid,delaysec,defaulttimezone df.to_csv(csvfilename, index_label='index', compression='gzip') - + #res = add_workout_from_data(userid,nkid,data,df) title = data["name"] speedInput = data["speedInput"] elapsedTime = data["elapsedTime"] diff --git a/rowers/templates/menu_workout.html b/rowers/templates/menu_workout.html index 51aefb8b..382f8d71 100644 --- a/rowers/templates/menu_workout.html +++ b/rowers/templates/menu_workout.html @@ -335,7 +335,7 @@ Remove Power Data - {% if 'speedcoach2' in workout.workoutsource %} + {% if 'speedcoach2' in workout.workoutsource or 'nklinklogbook' in workout.workoutsource %}