diff --git a/rowers/views/apiviews.py b/rowers/views/apiviews.py index 06745200..fc1226f8 100644 --- a/rowers/views/apiviews.py +++ b/rowers/views/apiviews.py @@ -37,6 +37,138 @@ def strokedataform(request, id=0): 'workout': w, }) # pragma: no cover +def api_get_dataframe(startdatetime, df): + try: + time = df['time']/1.e3 + except KeyError: # pragma: no cover + try: + time = df['t']/10. + except KeyError: + return 400, "Missing time", pd.DataFrame() + + try: + spm = df['spm'] + except KeyError: # pragma: no cover + return 400, "Missing spm", pd.DataFrame() + try: + distance = df['distance'] + except KeyError: # pragma: no cover + try: + distance = df['d']/10. + except KeyError: + return 400, "Missing distance", pd.DataFrame() + + try: + pace = df['pace']/1.e3 + except KeyError: # pragma: no cover + try: + pace = df['p']/10. + except KeyError: + return 400, "Missing pace", pd.DataFrame() + + + try: + power = df['power'] + except KeyError: # pragma: no cover + power = 0*time + try: + drivelength = df['drivelength'] + except KeyError: + drivelength = 0*time + try: + dragfactor = df['dragfactor'] + except KeyError: + dragfactor = 0*time + try: + drivetime = df['drivetime'] + except KeyError: + drivetime = 0*time + try: + strokerecoverytime = df['strokerecoverytime'] + except KeyError: + strokerecoverytime = 0*time + try: + averagedriveforce = df['averagedriveforce'] + except KeyError: + averagedriveforce = 0*time + try: + peakdriveforce = df['peakdriveforce'] + except KeyError: + peakdriveforce = 0*time + try: + wash = df['wash'] + except KeyError: + wash = 0*time + try: + catch = df['catch'] + except KeyError: + catch = 0*time + try: + finish = df['finish'] + except KeyError: + finish = 0*time + try: + peakforceangle = df['peakforceangle'] + except KeyError: + peakforceangle = 0*time + try: + driveenergy = df['driveenergy'] + except KeyError: + driveenergy = 60.*power/spm + try: + slip = df['slip'] + except KeyError: + slip = 0*time + try: + lapidx = df['lapidx'] + except KeyError: + lapidx = 0*time + try: + hr = df['hr'] + except KeyError: # pragma: no cover + hr = 0*df['time'] + + try: + latitude = df['latitude'] + except KeyError: + latitude = 0*df['time'] + + try: + longitude = df['longitude'] + except KeyError: + longitude = 0*df['time'] + + starttime = totimestamp(startdatetime)+time[0] + unixtime = starttime+time + + dologging('apilog.log',"(strokedatajson_v2/3 POST - data parsed)") + + data = pd.DataFrame({'TimeStamp (sec)': unixtime, + ' Horizontal (meters)': distance, + ' Cadence (stokes/min)': spm, + ' HRCur (bpm)': hr, + ' DragFactor': dragfactor, + ' Stroke500mPace (sec/500m)': pace, + ' Power (watts)': power, + ' DriveLength (meters)': drivelength, + ' DriveTime (ms)': drivetime, + ' StrokeRecoveryTime (ms)': strokerecoverytime, + ' AverageDriveForce (lbs)': averagedriveforce, + ' PeakDriveForce (lbs)': peakdriveforce, + ' lapIdx': lapidx, + ' ElapsedTime (sec)': time, + 'catch': catch, + 'slip': slip, + 'finish': finish, + 'wash': wash, + 'driveenergy': driveenergy, + 'peakforceangle': peakforceangle, + ' latitude': latitude, + ' longitude': longitude, + }) + + return 200, "", data + @login_required() def strokedataform_v2(request, id=0): @@ -69,10 +201,135 @@ def strokedataform_v2(request, id=0): }) # pragma: no cover +@csrf_exempt +@login_required() +@api_view(["POST"]) +@permission_classes([IsAuthenticated]) +def strokedatajson_v3(request): + """ + POST: Add Stroke data to workout + GET: Get stroke data of workout + This v2 API works on stroke based data dict: + {"startdatetime":"2023-01-16 12:34:45", + "totalDistance":11233, + "elapsedTime":"65:23.10", + "summary":"Some summary", + "timezone":"UTC", + "workouttype":"rower", + "boattype":"1x", + "notes":"some notes", + "title":"Workout title", + "rpe":4, + "data": + [ + {"hr": 110, "p": 3600, "spm": 53, "d": 6, "t": 12, "latitude": 52.343, "longitude": -6.342}, + {"hr": 111, "p": 3600, "spm": 53, "d": 6, "t": 12, "latitude": 52.343, "longitude": -6.342}, + {"hr": 111, "p": 3600, "spm": 64, "d": 6, "t": 22, "latitude": 52.343, "longitude": -6.342}, + {"hr": 110, "p": 3600, "spm": 16, "d": 14, "t": 55, "latitude": 52.343, "longitude": -6.342}, + {"hr": 110, "p": 3600, "spm": 16, "d": 14, "t": 82, "latitude": 52.343, "longitude": -6.342}, + {"hr": 107, "p": 3600, "spm": 12, "d": 22, "t": 109, "latitude": 52.343, "longitude": -6.342}, + {"hr": 107, "p": 3600, "spm": 12, "d": 22, "t": 133, "latitude": 52.343, "longitude": -6.342}, + {"hr": 108, "p": 3600, "spm": 12, "d": 32, "t": 157, "latitude": 52.343, "longitude": -6.342}, + {"hr": 108, "p": 3577, "spm": 12, "d": 32, "t": 157, "latitude": 52.343, "longitude": -6.342}, + {"hr": 108, "p": 3411, "spm": 12, "d": 32, "t": 157, "latitude": 52.343, "longitude": -6.342}, + {"hr": 108, "p": 2649, "spm": 12, "d": 32, "t": 157, "latitude": 52.343, "longitude": -6.342}, + {"hr": 108, "p": 3099, "spm": 12, "d": 32, "t": 157, "latitude": 52.343, "longitude": -6.342}, + {"hr": 108, "p": 3600, "spm": 12, "d": 32, "t": 157, "latitude": 52.343, "longitude": -6.342}, + {"hr": 100, "p": 3600, "spm": 44, "d": 115, "t": 292, "latitude": 52.343, "longitude": -6.342}, + {"hr": 99, "p": 3600, "spm": 27, "d": 129, "t": 305, "latitude": 52.343, "longitude": -6.342}, + {"hr": 97, "p": 3600, "spm": 34, "d": 161, "t": 330, "latitude": 52.343, "longitude": -6.342}, + {"hr": 96, "p": 3600, "spm": 25, "d": 177, "t": 344, "latitude": 52.343, "longitude": -6.342}, + {"hr": 96, "p": 3494, "spm": 43, "d": 196, "t": 357, "latitude": 52.343, "longitude": -6.342}, + {"hr": 98, "p": 2927, "spm": 26, "d": 235, "t": 377, "latitude": 52.343, "longitude": -6.342}, + {"hr": 102, "p": 2718, "spm": 27, "d": 380, "t": 455, "latitude": 52.343, "longitude": -6.342}, + {"hr": 102, "p": 2753, "spm": 9, "d": 398, "t": 472, "latitude": 52.343, "longitude": -6.342}, + {"hr": 102, "p": 2864, "spm": 61, "d": 406, "t": 477, "latitude": 52.343, "longitude": -6.342}, + {"hr": 101, "p": 2780, "spm": 15, "d": 484, "t": 515, "latitude": 52.343, "longitude": -6.342}, + {"hr": 101, "p": 2365, "spm": 16, "d": 583, "t": 554, "latitude": 52.343, "longitude": -6.342}, + {"hr": 103, "p": 1965, "spm": 16, "d": 681, "t": 592, "latitude": 52.343, "longitude": -6.342}, + ] + } + """ + + if request.method != 'POST': + return HttpResponse("Not allowed", status=409) + + dologging('apilog.log', request.user.username+" (strokedatajson_v3 POST)") + + title = request.data.get('title','') + try: + elapsedTime = request.data['elapsedTime'] + except KeyError: + return HttpResponse("Missing Elapsed Time", status=400) + try: + totalDistance = request.data['totalDistance'] + except KeyError: + return HttpResponse("Missing Total Distance", status=400) + summary = request.data.get('summary','') + timezone = request.data.get('timezone','UTC') + workouttype = request.data.get('workouttype','rower') + boattype = request.data.get('boattype','1x') + notes = request.data.get('notes','') + rpe = request.data.get('rpe',0) + + df = pd.DataFrame() + + try: + df = pd.DataFrame(request.data['data']) + except KeyError: + try: + df = pd.DataFrame(request.data['strokedata']) + except: + return HttpResponse("No JSON Object could be decoded", status=400) + + df.index = df.index.astype(int) + df.sort_index(inplace=True) + + status, comment, data = api_get_dataframe(startdatetime, df) + + if status != 200: + return HttpResponse(comment, status=status) + + + csvfilename = 'media/{code}.csv'.format(code=uuid4().hex[:16]) + _ = data.to_csv(csvfilename+'.gz', index_label='index', compression='gzip') + + uploadoptions = { + 'secret': UPLOAD_SERVICE_SECRET, + 'user': userid, + 'file': csvfilename, + 'title': title, + 'workouttype': workouttype, + 'boattype': boattype, + 'summary': summary, + 'elapsedTime': elapsedTime/1000., # in seconds + 'totalDistance': totalDistance, + 'rpe': rpe, + 'notes': notes, + 'timezone': timezone, + } + 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 HttpResponse(response.text, response.status_code) + + try: + workoutid = response.json()['id'] + except KeyError: + workoutid = 1 + + return JsonResponse( + {"workout public id": encoder.encode_hex(workoutid), + "status": "success", + }) + + # Process the POSTed stroke data according to the API definition # Return the GET stroke data according to the API definition - - @csrf_exempt @login_required() @api_view(["GET", "POST"]) @@ -161,135 +418,10 @@ def strokedatajson_v2(request, id): df.index = df.index.astype(int) df.sort_index(inplace=True) - try: - time = df['time']/1.e3 - except KeyError: # pragma: no cover - try: - time = df['t']/10. - except KeyError: - return HttpResponse("Missing time", status=400) - - try: - spm = df['spm'] - except KeyError: # pragma: no cover - return HttpResponse("Missing spm", status=400) - - try: - distance = df['distance'] - except KeyError: # pragma: no cover - try: - distance = df['d']/10. - except KeyError: - return HttpResponse("Missing distance", status=400) - - try: - pace = df['pace']/1.e3 - except KeyError: # pragma: no cover - try: - pace = df['p']/10. - except KeyError: - return HttpResponse("Missing pace", status=400) - - try: - power = df['power'] - except KeyError: # pragma: no cover - power = 0*time - try: - drivelength = df['drivelength'] - except KeyError: - drivelength = 0*time - try: - dragfactor = df['dragfactor'] - except KeyError: - dragfactor = 0*time - try: - drivetime = df['drivetime'] - except KeyError: - drivetime = 0*time - try: - strokerecoverytime = df['strokerecoverytime'] - except KeyError: - strokerecoverytime = 0*time - try: - averagedriveforce = df['averagedriveforce'] - except KeyError: - averagedriveforce = 0*time - try: - peakdriveforce = df['peakdriveforce'] - except KeyError: - peakdriveforce = 0*time - try: - wash = df['wash'] - except KeyError: - wash = 0*time - try: - catch = df['catch'] - except KeyError: - catch = 0*time - try: - finish = df['finish'] - except KeyError: - finish = 0*time - try: - peakforceangle = df['peakforceangle'] - except KeyError: - peakforceangle = 0*time - try: - driveenergy = df['driveenergy'] - except KeyError: - driveenergy = 60.*power/spm - try: - slip = df['slip'] - except KeyError: - slip = 0*time - try: - lapidx = df['lapidx'] - except KeyError: - lapidx = 0*time - try: - hr = df['hr'] - except KeyError: # pragma: no cover - hr = 0*df['time'] - - try: - latitude = df['latitude'] - except KeyError: - latitude = 0*df['time'] - - try: - longitude = df['longitude'] - except KeyError: - longitude = 0*df['time'] - - starttime = totimestamp(row.startdatetime)+time[0] - unixtime = starttime+time - - dologging('apilog.log',"(strokedatajson_v2 POST - data parsed)") - - data = pd.DataFrame({'TimeStamp (sec)': unixtime, - ' Horizontal (meters)': distance, - ' Cadence (stokes/min)': spm, - ' HRCur (bpm)': hr, - ' DragFactor': dragfactor, - ' Stroke500mPace (sec/500m)': pace, - ' Power (watts)': power, - ' DriveLength (meters)': drivelength, - ' DriveTime (ms)': drivetime, - ' StrokeRecoveryTime (ms)': strokerecoverytime, - ' AverageDriveForce (lbs)': averagedriveforce, - ' PeakDriveForce (lbs)': peakdriveforce, - ' lapIdx': lapidx, - ' ElapsedTime (sec)': time, - 'catch': catch, - 'slip': slip, - 'finish': finish, - 'wash': wash, - 'driveenergy': driveenergy, - 'peakforceangle': peakforceangle, - ' latitude': latitude, - ' longitude': longitude, - }) - + status, comment, data = api_get_dataframe(row.startdatetime, df) + if status != 200: + return HttpResponse(comment, status=status) + r = getrower(request.user) timestr = row.startdatetime.strftime("%Y%m%d-%H%M%S")