From 47d9cac21a969f9f5d37802da88b52c99228449e Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 8 Feb 2020 17:22:51 +0100 Subject: [PATCH 01/13] simple function to upload workouts to user --- rowers/views/workoutviews.py | 93 ++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 5c6cb869..6fbcfaf2 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -4384,6 +4384,99 @@ def workout_toggle_ranking(request,id=0): return response +def workout_add(request): + if request.method != 'POST': + raise PermissionDenied("This view cannot be accessed") + + # check credentials here + + form = DocumentsForm(request.POST,request.FILES) + optionsform = TeamUploadOptionsForm(request.POST) + rowerform = TeamInviteForm(request.POST) + rowerform.fields.pop('email') + if form.is_valid(): + f = request.FILES.get('file',False) + if f: + res = handle_uploaded_file(f) + else: + message = {'message':'no file attached','status':'false'} + return JsonResponse(status=400,data=message) + + t = form.cleaned_data['title'] + offline = True + offline = form.cleaned_data['offline'] + boattype = form.cleaned_data['boattype'] + workouttype = form.cleaned_data['workouttype'] + if rowerform.is_valid(): + u = rowerform.cleaned_data['user'] + r = getrower(u) + else: + message = {'status':'false','message':'invalid user'} + return JSonResponse(status=400,data=message) + + notes = form.cleaned_data['notes'] + if optionsform.is_valid(): + make_plot = optionsform.cleaned_data['make_plot'] + plottype = optionsform.cleaned_data['plottype'] + upload_to_c2 = optionsform.cleaned_data['upload_to_C2'] + upload_to_strava = optionsform.cleaned_data['upload_to_Strava'] + upload_to_st = optionsform.cleaned_data['upload_to_SportTracks'] + upload_to_rk = optionsform.cleaned_data['upload_to_RunKeeper'] + upload_to_ua = optionsform.cleaned_data['upload_to_MapMyFitness'] + upload_to_tp = optionsform.cleaned_data['upload_to_TrainingPeaks'] + makeprivate = optionsform.cleaned_data['makeprivate'] + landingpage = optionsform.cleaned_data['landingpage'] + + uploadoptions = { + 'makeprivate':makeprivate, + 'make_plot':make_plot, + 'plottype':plottype, + 'upload_to_C2':upload_to_c2, + 'upload_to_Strava':upload_to_strava, + 'upload_to_SportTracks':upload_to_st, + 'upload_to_RunKeeper':upload_to_rk, + 'upload_to_MapMyFitness':upload_to_ua, + 'upload_to_TrainingPeaks':upload_to_tp, + 'landingpage':landingpage, + 'boattype': boattype, + 'workouttype': workouttype, + } + + f1 = res[0] + f2 = res[1] + + id, message, f2 = dataprep.new_workout_from_file( + r,f2, + workouttype=workouttype, + workoutsource=workoutsource, + boattype=boattype, + makeprivate=makeprivate, + title = t, + notes=notes, + ) + + if <= 0: + message = {'status':'false','message':'unable to process file'} + return JSonResponse(status=400,data=message) + + w = Workout.objects.get(id=id) + + if make_plot: + res, jobid = uploads.make_plot(r,w,f1,f2,plottype,t) + + if upload_to_c2: + try: + message,id = c2stuff.workout_c2_upload(r.user,w) + except NoTokenError: + pass + + # add other export options + + message = {'status': 'true','id':w.id} + return JSONResponse(status=200,data=message) + + + # This is the main view for processing uploaded files @login_required() From 424178ac0ee13b3a2c5005ffebed82c11fe7bacb Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 9 Feb 2020 13:55:43 +0100 Subject: [PATCH 02/13] working --- rowers/dataprep.py | 1 + rowers/forms.py | 20 +++++++++++ rowers/tests/test_emails.py | 47 +++++++++++++++++++++++++ rowers/tests/test_uploads.py | 1 - rowers/urls.py | 2 +- rowers/views/workoutviews.py | 67 +++++++++++++++++++----------------- 6 files changed, 104 insertions(+), 34 deletions(-) diff --git a/rowers/dataprep.py b/rowers/dataprep.py index 3912e8c1..b2ae436f 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -1568,6 +1568,7 @@ def new_workout_from_file(r, f2, if workoutsource is None: workoutsource = fileformat + print(f2,'final name') id, message = save_workout_database( f2, r, notes=notes, diff --git a/rowers/forms.py b/rowers/forms.py index 530c842d..829bbd3a 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -395,6 +395,26 @@ class TeamUploadOptionsForm(forms.Form): initial='timeplot', label='Plot Type') + upload_to_C2 = forms.BooleanField(initial=False,required=False, + label='Export to Concept2 logbook') + upload_to_Strava = forms.BooleanField(initial=False,required=False, + label='Export to Strava') + upload_to_SportTracks = forms.BooleanField(initial=False,required=False, + label='Export to SportTracks') + upload_to_RunKeeper = forms.BooleanField(initial=False,required=False, + label='Export to RunKeeper') + upload_to_MapMyFitness = forms.BooleanField(initial=False, + required=False, + label='Export to MapMyFitness') + upload_to_TrainingPeaks = forms.BooleanField(initial=False, + required=False, + label='Export to TrainingPeaks') + # do_physics = forms.BooleanField(initial=False,required=False,label='Power Estimate (OTW)') + makeprivate = forms.BooleanField(initial=False,required=False, + label='Make Workout Private') + + + class Meta: fields = ['make_plot','plottype'] diff --git a/rowers/tests/test_emails.py b/rowers/tests/test_emails.py index b65bcd9c..06ede1d6 100644 --- a/rowers/tests/test_emails.py +++ b/rowers/tests/test_emails.py @@ -9,6 +9,7 @@ from .statements import * class EmailUpload(TestCase): def setUp(self): redis_connection.publish('tasks','KILL') + self.c = Client() u = User.objects.create_user('john', 'sander@ds.ds', 'koeinsloot') @@ -50,6 +51,52 @@ workout run except (IOError,FileNotFoundError,OSError): pass + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db) + def test_uploadapi(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): + form_data = { + 'title': 'test', + 'workouttype':'rower', + 'boattype': '1x', + 'notes': 'aap noot mies', + 'make_plot': False, + 'upload_to_C2': False, + 'plottype': 'timeplot', + 'file': 'media/mailbox_attachments/colin3.csv', + 'secret': 'potjandorie', + 'user': 1, + } + + url = reverse('workout_upload_api') + response = self.c.post(url,form_data,HTTP_HOST='127.0.0.1:4533') + self.assertEqual(response.status_code,200) + + # should also test if workout is created + w = Workout.objects.get(id=1) + self.assertEqual(w.name,'test') + self.assertEqual(w.notes,'aap noot mies') + + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db) + def test_uploadapi_credentials(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): + form_data = { + 'title': 'test', + 'workouttype':'rower', + 'boattype': '1x', + 'notes': 'aap noot mies', + 'make_plot': False, + 'upload_to_C2': False, + 'plottype': 'timeplot', + 'file': 'media/mailbox_attachments/colin3.csv', + 'secret': 'potjandorie2', + 'user': 1, + } + + url = reverse('workout_upload_api') + response = self.c.post(url,form_data) + self.assertEqual(response.status_code,403) + + @patch('rowers.dataprep.create_engine') @patch('rowers.polarstuff.get_polar_notifications') @patch('rowers.c2stuff.requests.get', side_effect=mocked_requests) diff --git a/rowers/tests/test_uploads.py b/rowers/tests/test_uploads.py index 22f9a4e5..21e03df9 100644 --- a/rowers/tests/test_uploads.py +++ b/rowers/tests/test_uploads.py @@ -245,7 +245,6 @@ class ViewTest(TestCase): - @patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.TCXParser') def test_upload_view_TCX_CN(self, mocked_sqlalchemy, mocked_tcx_parser): diff --git a/rowers/urls.py b/rowers/urls.py index 00b83ac9..625ee831 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -745,7 +745,7 @@ urlpatterns = [ re_path(r'^courses/(?P\d+)/map/$',views.course_map_view,name='course_map_view'), # URLS to be created re_path(r'^help/$',TemplateView.as_view(template_name='help.html'), name='help'), - + re_path(r'^workout/api/upload/',views.workout_upload_api,name='workout_upload_api'), ] if settings.DEBUG: diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 6fbcfaf2..fd2cc78c 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -3,6 +3,9 @@ from __future__ import division from __future__ import print_function from __future__ import unicode_literals +import hashlib +from shutil import copyfile + from rowers.views.statements import * import rowers.teams as teams import rowers.mytypes as mytypes @@ -4384,24 +4387,35 @@ def workout_toggle_ranking(request,id=0): return response -def workout_add(request): +# simple POST API for files on local (e.g. in mailbox) +@csrf_exempt +def workout_upload_api(request): if request.method != 'POST': raise PermissionDenied("This view cannot be accessed") - # check credentials here + # only allow local host + print(request.get_host(),'get_host') - form = DocumentsForm(request.POST,request.FILES) + # check credentials here + secret = request.POST['secret'] + if secret != 'potjandorie': + raise PermissionDenied("Invalid credentials") + + form = DocumentsForm(request.POST) optionsform = TeamUploadOptionsForm(request.POST) rowerform = TeamInviteForm(request.POST) rowerform.fields.pop('email') - if form.is_valid(): - f = request.FILES.get('file',False) - if f: - res = handle_uploaded_file(f) - else: - message = {'message':'no file attached','status':'false'} - return JsonResponse(status=400,data=message) + try: + fstr = request.POST['file'] + f1 = uuid.uuid4().hex[:10]+'-'+time.strftime("%Y%m%d-%H%M%S")+os.path.splitext(fstr)[1] + print(f1) + f2 = 'media/'+f1 + copyfile(fstr,f2) + except KeyError: + message = {'status':'false','message':'no filename given'} + return JSONResponse(status=400,data=message) + if form.is_valid(): t = form.cleaned_data['title'] offline = True offline = form.cleaned_data['offline'] @@ -4425,37 +4439,22 @@ def workout_add(request): upload_to_ua = optionsform.cleaned_data['upload_to_MapMyFitness'] upload_to_tp = optionsform.cleaned_data['upload_to_TrainingPeaks'] makeprivate = optionsform.cleaned_data['makeprivate'] - landingpage = optionsform.cleaned_data['landingpage'] + else: + message = optionsform.errors + return JSonResponse(status=400,data=message) - uploadoptions = { - 'makeprivate':makeprivate, - 'make_plot':make_plot, - 'plottype':plottype, - 'upload_to_C2':upload_to_c2, - 'upload_to_Strava':upload_to_strava, - 'upload_to_SportTracks':upload_to_st, - 'upload_to_RunKeeper':upload_to_rk, - 'upload_to_MapMyFitness':upload_to_ua, - 'upload_to_TrainingPeaks':upload_to_tp, - 'landingpage':landingpage, - 'boattype': boattype, - 'workouttype': workouttype, - } - - f1 = res[0] - f2 = res[1] id, message, f2 = dataprep.new_workout_from_file( r,f2, workouttype=workouttype, - workoutsource=workoutsource, + workoutsource=None, boattype=boattype, makeprivate=makeprivate, title = t, notes=notes, ) - if <= 0: + if id <= 0: message = {'status':'false','message':'unable to process file'} return JSonResponse(status=400,data=message) @@ -4472,8 +4471,12 @@ def workout_add(request): # add other export options - message = {'status': 'true','id':w.id} - return JSONResponse(status=200,data=message) + else: # form invalid + message = form.errors + return JsonResponse(status=400,data=message) + + message = {'status': 'true','id':w.id} + return JSONResponse(status=200,data=message) From 02a37e9657917a37d454aeab6de47a8831963c1f Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 9 Feb 2020 14:13:03 +0100 Subject: [PATCH 03/13] further improvements --- rowers/dataprep.py | 2 -- rowers/rows.py | 26 +++++++++++++++----------- rowers/tests/test_emails.py | 2 +- rowers/views/workoutviews.py | 14 +++++++++----- rowsandall_app/settings.py | 4 ++++ 5 files changed, 29 insertions(+), 19 deletions(-) diff --git a/rowers/dataprep.py b/rowers/dataprep.py index b2ae436f..3843d4ec 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -3,7 +3,6 @@ from __future__ import division from __future__ import print_function from __future__ import unicode_literals - # All the data preparation, data cleaning and data mangling should # be defined here from __future__ import unicode_literals, absolute_import @@ -1568,7 +1567,6 @@ def new_workout_from_file(r, f2, if workoutsource is None: workoutsource = fileformat - print(f2,'final name') id, message = save_workout_database( f2, r, notes=notes, diff --git a/rowers/rows.py b/rowers/rows.py index 6393fa02..fa34cf00 100644 --- a/rowers/rows.py +++ b/rowers/rows.py @@ -7,6 +7,11 @@ import time import gzip import shutil import hashlib + + +import uuid + + from django.core.exceptions import ValidationError def format_pace_tick(x,pos=None): @@ -26,7 +31,7 @@ def format_time_tick(x,pos=None): def format_pace(x,pos=None): if isinf(x) or isnan(x): x=0 - + min=int(x/60) sec=(x-min*60.) @@ -73,14 +78,14 @@ def must_be_csv(value): valid_extensions = ['.csv','.CSV'] if not ext in valid_extensions: raise ValidationError(u'File not supported!') - + def validate_kml(value): import os ext = os.path.splitext(value.name)[1] valid_extensions = ['.kml','.KML'] if not ext in valid_extensions: raise ValidationError(u'File not supported!') - + def handle_uploaded_image(i): from io import StringIO, BytesIO @@ -92,8 +97,8 @@ def handle_uploaded_image(i): image_str += chunk imagefile = BytesIO(image_str) - - + + image = Image.open(i) try: @@ -105,7 +110,7 @@ def handle_uploaded_image(i): except (AttributeError, KeyError, IndexError): # cases: image don't have getexif exif = {'orientation':0} - + if image.mode not in ("L", "RGB"): image = image.convert("RGB") @@ -128,18 +133,17 @@ def handle_uploaded_image(i): filename2 = os.path.join('static/plots/',filename) image.save(filename2,'JPEG') - + return filename,filename2 - + def handle_uploaded_file(f): fname = f.name - timestr = time.strftime("%Y%m%d-%H%M%S") + timestr = uuid.uuid4().hex[:10]+'-'+time.strftime("%Y%m%d-%H%M%S") fname = timestr+'-'+fname fname2 = 'media/'+fname with open(fname2,'wb+') as destination: for chunk in f.chunks(): destination.write(chunk) - - return fname,fname2 + return fname,fname2 diff --git a/rowers/tests/test_emails.py b/rowers/tests/test_emails.py index 06ede1d6..11149be0 100644 --- a/rowers/tests/test_emails.py +++ b/rowers/tests/test_emails.py @@ -63,7 +63,7 @@ workout run 'upload_to_C2': False, 'plottype': 'timeplot', 'file': 'media/mailbox_attachments/colin3.csv', - 'secret': 'potjandorie', + 'secret': settings.UPLOAD_SERVICE_SECRET, 'user': 1, } diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index fd2cc78c..d0078ad5 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -4391,15 +4391,20 @@ def workout_toggle_ranking(request,id=0): @csrf_exempt def workout_upload_api(request): if request.method != 'POST': - raise PermissionDenied("This view cannot be accessed") + message = {'status':'false','message':'this view cannot be accessed through GET'} + return JSONResponse(status=403,data=message) # only allow local host - print(request.get_host(),'get_host') + hostt = request.get_host().split(':') + if hostt[0] not in ['localhost','127.0.0.1']: + message = {'status':'false','message':'permission denied'} + return JSONResponse(status=403,data=message) # check credentials here secret = request.POST['secret'] - if secret != 'potjandorie': - raise PermissionDenied("Invalid credentials") + if secret != settings.UPLOAD_SERVICE_SECRET: + message = {'status':'false','message':'invalid credentials'} + return JSONResponse(status=403,data=message) form = DocumentsForm(request.POST) optionsform = TeamUploadOptionsForm(request.POST) @@ -4408,7 +4413,6 @@ def workout_upload_api(request): try: fstr = request.POST['file'] f1 = uuid.uuid4().hex[:10]+'-'+time.strftime("%Y%m%d-%H%M%S")+os.path.splitext(fstr)[1] - print(f1) f2 = 'media/'+f1 copyfile(fstr,f2) except KeyError: diff --git a/rowsandall_app/settings.py b/rowsandall_app/settings.py index c453669c..372c6f69 100644 --- a/rowsandall_app/settings.py +++ b/rowsandall_app/settings.py @@ -247,6 +247,10 @@ LOGOUT_REDIRECT_URL = '/' # Update Cache with task progress password PROGRESS_CACHE_SECRET = CFG['progress_cache_secret'] +try: + UPLOAD_SERVICE_SECRET = CFG['upload_service_secret'] +except KeyError: + UPLOAD_SERVICE_SECRET = "FoYezZWLSyfAVimumpHEeYsJjsNCerxV" # Concept 2 C2_CLIENT_ID = CFG['c2_client_id'] From 79fef44e1adf286917483b4e78409e66a1f49673 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 9 Feb 2020 14:18:53 +0100 Subject: [PATCH 04/13] further improvements --- rowers/views/workoutviews.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index d0078ad5..515f195f 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -4473,8 +4473,16 @@ def workout_upload_api(request): except NoTokenError: pass + if upload_to_strava: + try: + message,id = stravastuff.workout_strava_upload(r.user,w) + except NoTokenError: + pass + # add other export options + # sporttracks, runkeeper, underarmour, trainingpeaks + else: # form invalid message = form.errors return JsonResponse(status=400,data=message) From 50b253d9a291bdc97097a1a18b03c3e02190c622 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 9 Feb 2020 18:27:49 +0100 Subject: [PATCH 05/13] Email processing now uses new API --- rowers/management/commands/processemail.py | 61 ++++++++-------------- rowers/views/workoutviews.py | 5 +- 2 files changed, 26 insertions(+), 40 deletions(-) diff --git a/rowers/management/commands/processemail.py b/rowers/management/commands/processemail.py index 8720b2cb..45d2a4f4 100644 --- a/rowers/management/commands/processemail.py +++ b/rowers/management/commands/processemail.py @@ -10,6 +10,9 @@ import re import time from time import strftime +import requests +import json + import io from django.core.management.base import BaseCommand @@ -56,6 +59,7 @@ def rdata(file_obj, rower=rrower()): def processattachment(rower, fileobj, title, uploadoptions,testing=False): try: filename = fileobj.name +# filename = os.path.abspath(fileobj.name) except AttributeError: filename = fileobj[6:] if testing: @@ -85,9 +89,19 @@ def processattachment(rower, fileobj, title, uploadoptions,testing=False): therower = rower - workoutid = [ - make_new_workout_from_email(therower, filename, title,testing=testing) - ] + uploadoptions['secret'] = settings.UPLOAD_SERVICE_SECRET + uploadoptions['user'] = therower.user.id + uploadoptions['file'] = 'media/'+filename + uploadoptions['title'] = title + + + url = "http://localhost:8000/rowers/workout/api/upload/" + response = requests.post(url,data=uploadoptions) + if response.status_code == 200: + response_json = response.json() + workoutid = [int(response_json['id'])] + else: + workoutid = [0] if 'raceid' in uploadoptions and workoutid[0] and rower.user.is_staff: @@ -114,41 +128,12 @@ def processattachment(rower, fileobj, title, uploadoptions,testing=False): } ) - if uploadoptions and not 'error' in uploadoptions: - workout = Workout.objects.get(id=workoutid[0]) - uploads.make_private(workout, uploadoptions) - uploads.set_workouttype(workout, uploadoptions) - uploads.do_sync(workout, uploadoptions) - if 'make_plot' in uploadoptions: - plottype = uploadoptions['plottype'] - workoutcsvfilename = workout.csvfilename[6:-4] - timestr = strftime("%Y%m%d-%H%M%S") - imagename = workoutcsvfilename + timestr + '.png' - result,jobid = uploads.make_plot( - workout.user, workout, workoutcsvfilename, - workout.csvfilename, - plottype, title, - imagename=imagename - ) - try: - if workoutid and not testing: - if therower.getemailnotifications and not therower.emailbounced: - email_sent = send_confirm( - therower.user, title, link, - uploadoptions - ) - time.sleep(10) - except: - try: - time.sleep(10) - if workoutid: - if therower.getemailnotifications and not therower.emailbounced: - email_sent = send_confirm( - therower.user, title, link, - uploadoptions - ) - except: - pass + if not testing: + if therower.getemailnotifications and not therower.emailbounced: + email_sent = send_confirm( + therower.user, title, link, + uploadoptions + ) return workoutid diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 515f195f..297b338c 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -4418,11 +4418,12 @@ def workout_upload_api(request): except KeyError: message = {'status':'false','message':'no filename given'} return JSONResponse(status=400,data=message) + except FileNotFoundError: + message = {'status':'false','message':'could not find file'} + return JSONResponse(status=400,data=message) if form.is_valid(): t = form.cleaned_data['title'] - offline = True - offline = form.cleaned_data['offline'] boattype = form.cleaned_data['boattype'] workouttype = form.cleaned_data['workouttype'] if rowerform.is_valid(): From 3909ce31270b1bb7db87a7d081cbdb7aaf2155f6 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 9 Feb 2020 18:40:09 +0100 Subject: [PATCH 06/13] passing email tests - still not doing everything --- rowers/management/commands/processemail.py | 24 ++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/rowers/management/commands/processemail.py b/rowers/management/commands/processemail.py index 45d2a4f4..9f0f5c54 100644 --- a/rowers/management/commands/processemail.py +++ b/rowers/management/commands/processemail.py @@ -96,12 +96,24 @@ def processattachment(rower, fileobj, title, uploadoptions,testing=False): url = "http://localhost:8000/rowers/workout/api/upload/" - response = requests.post(url,data=uploadoptions) - if response.status_code == 200: - response_json = response.json() - workoutid = [int(response_json['id'])] - else: - workoutid = [0] + if not testing: + response = requests.post(url,data=uploadoptions) + if response.status_code == 200: + response_json = response.json() + workoutid = [int(response_json['id'])] + else: + workoutid = [0] + + # this is ugly and needs to be done better + if testing: + workoutid = [ + make_new_workout_from_email(therower, filename, title,testing=testing) + ] + if workoutid[0] and uploadoptions and not 'error' in uploadoptions: + workout = Workout.objects.get(id=workoutid[0]) + uploads.make_private(workout, uploadoptions) + uploads.set_workouttype(workout, uploadoptions) + uploads.do_sync(workout, uploadoptions) if 'raceid' in uploadoptions and workoutid[0] and rower.user.is_staff: From 0f1269de63b8bb83ed0a7884b204146c395ab1f9 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 9 Feb 2020 18:45:37 +0100 Subject: [PATCH 07/13] now also processing gz --- rowers/views/workoutviews.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 297b338c..34fdc80e 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -4412,7 +4412,11 @@ def workout_upload_api(request): rowerform.fields.pop('email') try: fstr = request.POST['file'] - f1 = uuid.uuid4().hex[:10]+'-'+time.strftime("%Y%m%d-%H%M%S")+os.path.splitext(fstr)[1] + nn, ext = os.path.splitext(fstr) + if ext== '.gz': + nn, ext2 = os.path.splitext(nn) + ext = ext2+ext + f1 = uuid.uuid4().hex[:10]+'-'+time.strftime("%Y%m%d-%H%M%S")+ext f2 = 'media/'+f1 copyfile(fstr,f2) except KeyError: From 6d392d7033fbb4e7c4c22d845d7da2b38541162a Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 9 Feb 2020 18:51:13 +0100 Subject: [PATCH 08/13] all stuff done --- rowers/views/workoutviews.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 34fdc80e..d7a2a748 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -4484,9 +4484,29 @@ def workout_upload_api(request): except NoTokenError: pass - # add other export options + if upload_to_st: + try: + message,id = sporttrackstuff.workout_sporttracks_upload(r.user,w) + except NoTokenError: + pass - # sporttracks, runkeeper, underarmour, trainingpeaks + if upload_to_rk: + try: + message,id = runkeeperstuff.workout_runkeeper_upload(r.user,w) + except NoTokenError: + pass + + if upload_to_ua: + try: + message,id = underarmourstuff.workout_ua_upload(r.user,w) + except NoTokenError: + pass + + if upload_to_tp: + try: + message,id = tpstuff.workout_tp_upload(r.user,w) + except NoTokenError: + pass else: # form invalid message = form.errors From de5d030168313b6e2d3ba3aac55a2c10457d4aed Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 11 Feb 2020 11:45:07 +0100 Subject: [PATCH 09/13] adding check for email --- rowers/views/workoutviews.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index d7a2a748..743ecb71 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -4433,6 +4433,11 @@ def workout_upload_api(request): if rowerform.is_valid(): u = rowerform.cleaned_data['user'] r = getrower(u) + elif 'useremail' in request.POST: + us = User.objects.filter(email=request.POST['useremail']) + if len(us): + u = us[0] + r = getrower(u) else: message = {'status':'false','message':'invalid user'} return JSonResponse(status=400,data=message) From 9f6873c40ea25d840242eb5f04d3c38a06b32220 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 12 Feb 2020 18:09:44 +0100 Subject: [PATCH 10/13] changes to accomodate the golang implementation --- rowers/mytypes.py | 5 ++++- rowers/views/workoutviews.py | 28 +++++++++++++++++++++------- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/rowers/mytypes.py b/rowers/mytypes.py index 6a10e967..e8b7aa9b 100644 --- a/rowers/mytypes.py +++ b/rowers/mytypes.py @@ -5,6 +5,9 @@ from __future__ import unicode_literals from six import iteritems import collections + + + workouttypes_ordered = collections.OrderedDict({ 'water':'Standard Racing Shell', 'rower':'Indoor Rower', @@ -289,7 +292,7 @@ workoutsources = ( ('ergstick','ergstick'), ('fit','fit'), ('unknown','unknown')) - + boattypes = ( ('1x', '1x (single)'), ('2x', '2x (double)'), diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 743ecb71..39a88629 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -4394,6 +4394,15 @@ def workout_upload_api(request): message = {'status':'false','message':'this view cannot be accessed through GET'} return JSONResponse(status=403,data=message) + + # test if JSON + try: + json_data = json.loads(request.body) + secret = json_data['secret'] + post_data = json_data + except KeyError: + post_data = request.POST + # only allow local host hostt = request.get_host().split(':') if hostt[0] not in ['localhost','127.0.0.1']: @@ -4401,17 +4410,22 @@ def workout_upload_api(request): return JSONResponse(status=403,data=message) # check credentials here - secret = request.POST['secret'] + try: + secret = post_data['secret'] + except KeyError: + message = {'status': 'false', 'message':'missing credentials'} + return JSONResponse(status=400,data=message) if secret != settings.UPLOAD_SERVICE_SECRET: message = {'status':'false','message':'invalid credentials'} return JSONResponse(status=403,data=message) - form = DocumentsForm(request.POST) - optionsform = TeamUploadOptionsForm(request.POST) - rowerform = TeamInviteForm(request.POST) + form = DocumentsForm(post_data) + optionsform = TeamUploadOptionsForm(post_data) + rowerform = TeamInviteForm(post_data) rowerform.fields.pop('email') try: - fstr = request.POST['file'] + fstr = post_data['file'] + print(fstr) nn, ext = os.path.splitext(fstr) if ext== '.gz': nn, ext2 = os.path.splitext(nn) @@ -4433,8 +4447,8 @@ def workout_upload_api(request): if rowerform.is_valid(): u = rowerform.cleaned_data['user'] r = getrower(u) - elif 'useremail' in request.POST: - us = User.objects.filter(email=request.POST['useremail']) + elif 'useremail' in post_data: + us = User.objects.filter(email=post_data['useremail']) if len(us): u = us[0] r = getrower(u) From 84ee537e20f7b18f4531ca80c73cdd2373355642 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 13 Feb 2020 22:22:56 +0100 Subject: [PATCH 11/13] updated settings --- rowsandall_app/settings.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/rowsandall_app/settings.py b/rowsandall_app/settings.py index 372c6f69..e87a4a78 100644 --- a/rowsandall_app/settings.py +++ b/rowsandall_app/settings.py @@ -361,8 +361,13 @@ CACHE_MIDDLEWARE_SECONDS = 900 EMAIL_BACKEND = 'django_ses.SESBackend' -AWS_SES_REGION_NAME = 'eu-west-1' -AWS_SES_REGION_ENDPOINT = 'email.eu-west-1.amazonaws.com' +AWS_SES_REGION_NAME = CFG['aws_smtp'] +AWS_SES_REGION_ENDPOINT = CFG['aws_smtp'] + +AWS_SES_ACCESS_KEY_ID = CFG['aws_access_key_id'] +AWS_SES_SECRET_ACCESS_KEY = CFG['aws_secret_access_key'] +AWS_ACCESS_KEY_ID = CFG['aws_access_key_id'] +AWS_SECRET_ACCESS_KEY = CFG['aws_secret_access_key'] EMAIL_HOST = CFG['aws_smtp'] EMAIL_PORT = CFG['aws_port'] From 338b50452bbdf8484207bc27887dcafa9401a1ca Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 15 Feb 2020 13:59:15 +0100 Subject: [PATCH 12/13] fixing to accomodate golang service --- rowers/views/workoutviews.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 39a88629..38771622 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -4425,7 +4425,6 @@ def workout_upload_api(request): rowerform.fields.pop('email') try: fstr = post_data['file'] - print(fstr) nn, ext = os.path.splitext(fstr) if ext== '.gz': nn, ext2 = os.path.splitext(nn) @@ -4452,6 +4451,9 @@ def workout_upload_api(request): if len(us): u = us[0] r = getrower(u) + else: + message = {'status':'false','message':'could not find user'} + return JSonResponse(status=400,data=message) else: message = {'status':'false','message':'invalid user'} return JSonResponse(status=400,data=message) From f0c6d0ca70331d6610ad8281a558d1a3e9b99d79 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 15 Feb 2020 14:15:36 +0100 Subject: [PATCH 13/13] bug fix --- rowers/views/workoutviews.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 38771622..daaf1406 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -12,6 +12,7 @@ import rowers.mytypes as mytypes import numpy from urllib.parse import urlparse, parse_qs +from json.decoder import JSONDecodeError def default(o): if isinstance(o, numpy.int64): return int(o) @@ -4400,7 +4401,7 @@ def workout_upload_api(request): json_data = json.loads(request.body) secret = json_data['secret'] post_data = json_data - except KeyError: + except (KeyError,JSONDecodeError): post_data = request.POST # only allow local host