Merge branch 'develop' into feature/stravaapi
This commit is contained in:
@@ -254,6 +254,8 @@ urlpatterns = [
|
|||||||
name='strokedatajson_v3'),
|
name='strokedatajson_v3'),
|
||||||
re_path(r'^api/TCX/workouts/$', views.strokedata_tcx,
|
re_path(r'^api/TCX/workouts/$', views.strokedata_tcx,
|
||||||
name='strokedata_tcx'),
|
name='strokedata_tcx'),
|
||||||
|
re_path(r'^api/FIT/workouts/$', views.strokedata_fit,
|
||||||
|
name='strokedata_fit'),
|
||||||
re_path(r'^api/rowingdata/workouts/$', views.strokedata_rowingdata,
|
re_path(r'^api/rowingdata/workouts/$', views.strokedata_rowingdata,
|
||||||
name='strokedata_rowingdata'),
|
name='strokedata_rowingdata'),
|
||||||
re_path(r'^api/rowingdata/$', views.strokedata_rowingdata_apikey,
|
re_path(r'^api/rowingdata/$', views.strokedata_rowingdata_apikey,
|
||||||
|
|||||||
@@ -20,10 +20,25 @@ from datetime import datetime as dt
|
|||||||
|
|
||||||
import rowingdata.tcxtools as tcxtools
|
import rowingdata.tcxtools as tcxtools
|
||||||
from rowingdata import TCXParser, rowingdata
|
from rowingdata import TCXParser, rowingdata
|
||||||
|
from rowingdata import FITParser as FP
|
||||||
import arrow
|
import arrow
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
|
# create a FITParser which parses the application/octet-stream and creates a fit file
|
||||||
|
class FITParser(BaseParser):
|
||||||
|
media_type = "application/octet-stream"
|
||||||
|
|
||||||
|
def parse(self, stream, media_type=None, parser_context=None):
|
||||||
|
try:
|
||||||
|
return stream.read()
|
||||||
|
except Exception as e:
|
||||||
|
dologging("apilog.log", "FIT Parser")
|
||||||
|
dolofging("apilog.log", e)
|
||||||
|
raise ValueError(f"Failed to read FIT file: {str(e)}")
|
||||||
|
|
||||||
|
return stream.read()
|
||||||
|
|
||||||
class XMLParser(BaseParser):
|
class XMLParser(BaseParser):
|
||||||
media_type = "application/xml"
|
media_type = "application/xml"
|
||||||
|
|
||||||
@@ -32,10 +47,10 @@ class XMLParser(BaseParser):
|
|||||||
try:
|
try:
|
||||||
s = ET.parse(stream).getroot()
|
s = ET.parse(stream).getroot()
|
||||||
except ET.XMLSyntaxError:
|
except ET.XMLSyntaxError:
|
||||||
return HttpResponse(status=400)
|
raise ValueError("XML Syntax Error")
|
||||||
except Exception as e: # pragma: no cover
|
except Exception as e: # pragma: no cover
|
||||||
dologging("apilog.log",e)
|
dologging("apilog.log",e)
|
||||||
return HttpResponse(status=500)
|
raise ValueError(f"Failed to parse XML file: {str(e)}")
|
||||||
return s
|
return s
|
||||||
|
|
||||||
# Stroke data form to test API upload
|
# Stroke data form to test API upload
|
||||||
@@ -530,6 +545,83 @@ def strokedata_rowingdata_apikey(request):
|
|||||||
response.status_code = 201
|
response.status_code = 201
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
@api_view(["POST"])
|
||||||
|
@permission_required('rower.is_not_freecoach', fn=get_user_by_userid, raise_exception=True)
|
||||||
|
@permission_classes([IsAuthenticated])
|
||||||
|
@parser_classes([FITParser])
|
||||||
|
def strokedata_fit(request):
|
||||||
|
"""
|
||||||
|
Handle a POST request to upload a binary FIT file and save it locally.
|
||||||
|
"""
|
||||||
|
if request.method != 'POST':
|
||||||
|
return HttpResponseBadRequest("Only POST requests are allowed.")
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
fit_data = request.data
|
||||||
|
|
||||||
|
# Ensure the media directory exists
|
||||||
|
media_dir = 'media'
|
||||||
|
os.makedirs(media_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# Generate a unique filename for the FIT file
|
||||||
|
fit_filename = os.path.join(media_dir, f'{uuid4().hex[:16]}.fit')
|
||||||
|
|
||||||
|
# Save the FIT file locally
|
||||||
|
with open(fit_filename, 'wb') as fit_file:
|
||||||
|
fit_file.write(fit_data)
|
||||||
|
except Exception as e:
|
||||||
|
return JsonResponse({
|
||||||
|
"status": "error",
|
||||||
|
"message": f"An error occurred while saving the FIT file: {str(e)}"
|
||||||
|
}, status=500)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Parse the FIT file
|
||||||
|
row = FP(fit_filename)
|
||||||
|
|
||||||
|
rowdata = rowingdata(df=row.df)
|
||||||
|
duration = totaltime_sec_to_string(rowdata.duration)
|
||||||
|
title = "ActiveSpeed water"
|
||||||
|
|
||||||
|
w = Workout.objects.create(user=request.user.rower,
|
||||||
|
duration=duration,
|
||||||
|
name=title,)
|
||||||
|
|
||||||
|
uploadoptions = {
|
||||||
|
'secret': UPLOAD_SERVICE_SECRET,
|
||||||
|
'user': request.user.id,
|
||||||
|
'file': fit_filename,
|
||||||
|
'workouttype': 'water',
|
||||||
|
'boattype': '1x',
|
||||||
|
'title': title,
|
||||||
|
'rpe': 0,
|
||||||
|
'notes': '',
|
||||||
|
'workoutid': w.id,
|
||||||
|
'offline': False,
|
||||||
|
}
|
||||||
|
|
||||||
|
url = UPLOAD_SERVICE_URL
|
||||||
|
|
||||||
|
_ = myqueue(queuehigh,
|
||||||
|
handle_request_post,
|
||||||
|
url,
|
||||||
|
uploadoptions)
|
||||||
|
|
||||||
|
return JsonResponse(
|
||||||
|
{"status": "success",
|
||||||
|
"workout public id": encoder.encode_hex(w.id),
|
||||||
|
"workout id": w.id,
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
dologging('apilog.log','FIT API endpoint')
|
||||||
|
dologging('apilog.log',e)
|
||||||
|
_ = myqueue(queuehigh, handle_sendemail_unrecognized, fit_filename, "fit parser")
|
||||||
|
return HttpResponse(status=500)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
#@login_required()
|
#@login_required()
|
||||||
|
|||||||
Reference in New Issue
Block a user