diff --git a/rowers/dataroutines.py b/rowers/dataroutines.py index c828d047..abfe2fde 100644 --- a/rowers/dataroutines.py +++ b/rowers/dataroutines.py @@ -1406,7 +1406,12 @@ def get_title_from_fit(filename): return title -def get_workouttype_from_fit(filename, workouttype='water'): +def get_workouttype_from_fit(filename, workouttype=None): + if workouttype is None: + workouttype = 'water' + workouttype_orig = '' + else: + workouttype_orig = workouttype try: fitfile = FitFile(filename, check_crc=False) except FitHeaderError: # pragma: no cover @@ -1435,6 +1440,8 @@ def get_workouttype_from_fit(filename, workouttype='water'): workouttype = mytypes.fitmappinginv[fittype] except KeyError: pass + if workouttype_orig in ['water', 'rower']: + workouttype = workouttype_orig return workouttype diff --git a/rowers/integrations/intervals.py b/rowers/integrations/intervals.py index 56c5da64..36da54d5 100644 --- a/rowers/integrations/intervals.py +++ b/rowers/integrations/intervals.py @@ -1,10 +1,12 @@ from .integrations import SyncIntegration, NoTokenError, create_or_update_syncrecord, get_known_ids from rowers.models import Rower, User, Workout, TombStone, PlannedSession from rowingdata import rowingdata +from rowingdata import FITParser as FP +from rowingdata.otherparsers import FitSummaryData from rowers.rower_rules import user_is_not_basic, user_is_coachee from rowers import mytypes - +import shutil from rowers.rower_rules import is_workout_user, ispromember from rowers.utils import myqueue, dologging, custom_exception_handler from rowers.tasks import handle_intervals_getworkout @@ -22,7 +24,8 @@ import rowers.dataprep as dataprep from rowers.opaque import encoder from rowsandall_app.settings import ( - INTERVALS_CLIENT_ID, INTERVALS_REDIRECT_URI, INTERVALS_CLIENT_SECRET, SITE_URL + INTERVALS_CLIENT_ID, INTERVALS_REDIRECT_URI, INTERVALS_CLIENT_SECRET, SITE_URL, + UPLOAD_SERVICE_SECRET, UPLOAD_SERVICE_URL ) import django_rq @@ -272,12 +275,103 @@ class IntervalsIntegration(SyncIntegration): return workouts - + + def update_workout(self, id, *args, **kwargs) -> int: + _ = self.open() + r = self.rower + + headers = { + 'Authorization': 'Bearer ' + r.intervals_token, + } + url = self.oauth_data['base_url'] + 'activity/' + str(id) + response = requests.get(url, headers=headers) + if response.status_code != 200: + dologging('intervals.icu.log', response.text) + return 0 + + data = response.json() + ws = Workout.objects.filter(uploadedtointervals=id) + + for w in ws: + try: + w.name = data['name'] + except KeyError: + pass + try: + w.notes = data['description'] + except KeyError: + pass + try: + w.workouttype = mytypes.intervalsmappinginv[data['type']] + except KeyError: + pass + w.save() + + # we stop here now + return 1 + + url = self.oauth_data['base_url'] + 'activity/' + str(id) + '/fit-file' + response = requests.get(url, headers=headers) + if response.status_code != 200: + dologging('intervals.icu.log', response.text) + return 0 + + try: + fit_data = response.content + fit_filename = 'media/intervals_' + str(id) + '.fit' + with open(fit_filename, 'wb') as f: + f.write(fit_data) + except: + return 0 + + try: + row = FP(fit_filename) + rowdata = rowingdata(df=row.df) + rowsummary = FitSummaryData(fit_filename) + except Exception as e: + dologging('intervals.icu.log', e) + return 0 + + for w in ws: + # copy fit_file to random file name using shutil + temp_filename = 'media/' + str(uuid4()) + '.fit' + try: + shutil.copy(fit_filename, temp_filename) + + + uploadoptions = { + 'secret': UPLOAD_SERVICE_SECRET, + 'user': self.rower.user.id, + 'boattype': '1x', + 'workouttype': w.workouttype, + 'file': temp_filename, + 'intervalsid': id, + 'id': w.id, + } + url = UPLOAD_SERVICE_URL + response = requests.post(url, data=uploadoptions) + except FileNotFoundError: + return 0 + except Exception as e: + dologging('intervals.icu.log', e) + + # remove fit_file + try: + os.remove(fit_filename) + except: + pass + + return 1 def get_workout(self, id, *args, **kwargs) -> int: _ = self.open() r = self.rower + # check if workout with this id already exists + known_interval_ids = get_known_ids(r, 'intervalsid') + if id in known_interval_ids: + return self.update_workout(id) + record = create_or_update_syncrecord(r, None, intervalsid=id) _ = myqueue(queuehigh, @@ -583,8 +677,8 @@ class IntervalsIntegration(SyncIntegration): id = record['id'] try: ws = Workout.objects.filter(uploadedtointervals=id) - if w.user == self.rower: - for w in ws: + for w in ws: + if w.user == self.rower: w.delete() except Workout.DoesNotExist: pass @@ -592,3 +686,17 @@ class IntervalsIntegration(SyncIntegration): pass return 1 + + def update_activities(self, event, *args, **kwargs): + try: + record = event["activity"] + except KeyError: + records = [] + + try: + id = record['id'] + result = self.update_workout(id) + except KeyError: + pass + + return 1 diff --git a/rowers/tasks.py b/rowers/tasks.py index 0382f924..866d25bc 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -3582,7 +3582,6 @@ def handle_intervals_getworkout(rower, intervalstoken, workoutid, debug=False, * except KeyError: workouttype = 'water' - url = "https://intervals.icu/api/v1/activity/{workoutid}/fit-file".format(workoutid=workoutid) response = requests.get(url, headers=headers) diff --git a/rowers/tests/testdata/testdata.tcx.gz b/rowers/tests/testdata/testdata.tcx.gz index d443a633..82547966 100644 Binary files a/rowers/tests/testdata/testdata.tcx.gz and b/rowers/tests/testdata/testdata.tcx.gz differ diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py index 30cd7b7b..dd2cd567 100644 --- a/rowers/views/importviews.py +++ b/rowers/views/importviews.py @@ -942,7 +942,6 @@ def intervals_webhook_view(request): webhook_type = None - for event in events: try: athlete_id = event['athlete_id'] @@ -964,6 +963,9 @@ def intervals_webhook_view(request): if webhook_type.lower() == 'activity_uploaded': integration.import_activities(event) + if webhook_type.lower() == 'activity_updated': + integration.update_activities(event) + if webhook_type.lower() == 'activity_deleted': integration.delete_activities(event) diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 82aa5433..b2ffab6e 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -1131,20 +1131,10 @@ def workouts_join_select(request, r = getrequestrower(request, userid=userid) - if 'waterboattype' in request.session: - waterboattype = request.session['waterboattype'] - else: - waterboattype = mytypes.waterboattype+mytypes.ergtype + waterboattype = mytypes.waterboattype+mytypes.ergtype - if 'modalities' in request.session: - modalities = request.session['modalities'] - if len(modalities) > 1: # pragma: no cover - modality = 'all' - else: - modality = modalities[0] - else: - modalities = [m[0] for m in mytypes.workouttypes] - modality = 'all' + modalities = [m[0] for m in mytypes.workouttypes] + modality = 'all' if request.method == 'POST': dateform = DateRangeForm(request.POST) @@ -1154,8 +1144,6 @@ def workouts_join_select(request, enddate = dateform.cleaned_data['enddate'] startdatestring = startdate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d') - request.session['startdate'] = startdatestring - request.session['enddate'] = enddatestring if modalityform.is_valid(): modality = modalityform.cleaned_data['modality'] waterboattype = modalityform.cleaned_data['waterboattype'] @@ -1164,11 +1152,7 @@ def workouts_join_select(request, else: modalities = [modality] - if modality != 'water': # pragma: no cover - waterboattype = [b[0] for b in mytypes.boattypes] - request.session['modalities'] = modalities - request.session['waterboattype'] = waterboattype else: dateform = DateRangeForm(initial={ 'startdate': startdate, @@ -1211,6 +1195,7 @@ def workouts_join_select(request, startdatetime__lte=enddate, workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes) + query = request.GET.get('q') if query: # pragma: no cover query_list = query.split() @@ -4954,6 +4939,7 @@ def workout_upload_api(request): message = {'status': 'false', 'message': 'invalid credentials'} return JSONResponse(status=403, data=message) + form = DocumentsForm(post_data) optionsform = TeamUploadOptionsForm(post_data) rowerform = TeamInviteForm(post_data)