From 34b11f86c22a39309443198ee28a68c781c0c44d Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 11 Dec 2024 23:10:14 +0100 Subject: [PATCH] list of intervals --- rowers/integrations/intervals.py | 45 +++++++++++++++++++- rowers/templates/intervals_list_import.html | 47 +++++++++++++++++++++ rowers/urls.py | 2 + rowers/views/importviews.py | 45 +++++++++++++++++++- 4 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 rowers/templates/intervals_list_import.html diff --git a/rowers/integrations/intervals.py b/rowers/integrations/intervals.py index 4e0ac680..b6498821 100644 --- a/rowers/integrations/intervals.py +++ b/rowers/integrations/intervals.py @@ -63,7 +63,7 @@ class IntervalsIntegration(SyncIntegration): 'base_url': 'https://intervals.icu/api/v1/', 'grant_type': 'refresh_token', 'headers': headers, - 'scope': 'ACTIVITY:WRITE, LIBRARY:READ', + 'scope': 'ACTIVITY:WRITE, LIBRARY:READ, CALENDAR:WRITE', } def get_token(self, code, *args, **kwargs): @@ -297,5 +297,48 @@ class IntervalsIntegration(SyncIntegration): def token_refresh(self, *args, **kwargs): return super(IntervalsIntegration, self).token_refresh(*args, **kwargs) + def get_plannedsessions_list(self, *args, **kwargs): + _ = self.open() + r = self.rower + + headers = { + 'Authorization': 'Bearer ' + r.intervals_token, + } + + # first get the folders - we need the folder id for the next call + url = self.oauth_data['base_url'] + 'athlete/0/folders' + response = requests.get(url, headers=headers) + if response.status_code != 200: + return [] + + data = response.json() + # get all elements in the list where start_date_local is not None + folders = [x for x in data if x['start_date_local']] + for plan in folders: + plan_start_date = arrow.get(plan['start_date_local']).datetime + for session in plan["children"]: + session["date"] = (plan_start_date+timedelta(days=session["day"])).date() + + return folders + + def get_plannedsession(self, id, *args, **kwargs): + _ = self.open() + r = self.rower + + url = self.oauth_data['base_url'] + 'athlete/0/workouts/' + str(id) + headers = { + 'Authorization': 'Bearer ' + r.intervals_token, + } + + response = requests.get(url, headers=headers) + + if response.status_code != 200: + dologging('intervals.icu.log', response.text) + return 0 + + data = response.json() + + return data + diff --git a/rowers/templates/intervals_list_import.html b/rowers/templates/intervals_list_import.html new file mode 100644 index 00000000..ea3b410a --- /dev/null +++ b/rowers/templates/intervals_list_import.html @@ -0,0 +1,47 @@ +{% extends "newbase.html" %} +{% load static %} +{% load rowerfilters %} + +{% block title %}Sessions on intervals.icu{% endblock %} + +{% block main %} +

Sessions on Intervals.icu

+{% if folders %} +
    +
  • +
    + {% csrf_token %} + + + + + + + + + + + + + + {% for folder in folders %} + {% for session in folder.children %} + + + + + + + + + {% endfor %} + {% endfor %} + +
    ImportPlanDateDescriptionTypeTraining Load
    + + {{ folder.name }}{{ session.date }}{{ session.description }}{{ session.type }}{{ session.icu_training_load }}
    +
    +
  • +
+{% endif %} +{% endblock %} diff --git a/rowers/urls.py b/rowers/urls.py index 5a4484da..71bb2228 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -630,6 +630,8 @@ urlpatterns = [ views.workout_undo_smoothenpace_view, name='workout_undo_smoothenpace_view'), re_path(r'^session/rojaboimport/$', views.workout_rojaboimport_view, name='workout_rojaboimport_view'), + re_path(r'^session/intervalsimport/$', views.plannedsession_intervalsimport_view, + name='plannedsession_intervalsimport_view'), re_path(r'^workout/(?P\w+.*)import/$', views.workout_import_view, name='workout_import_view'), re_path(r'^workout/(?P\w+.*)import/(?P\d+)/$', diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py index a02527b0..c145347a 100644 --- a/rowers/views/importviews.py +++ b/rowers/views/importviews.py @@ -681,8 +681,51 @@ def rower_process_testcallback(request): # pragma: no cover return HttpResponse(text) +# view to list planned sessions from intervals.icu +@login_required() +@user_passes_test(isplanmember, login_url="/rowers/paidplans/", + message="This functionality requires a Self-coach plan or higher", + redirect_field_name=None) +def plannedsession_intervalsimport_view(request, message="", userid=0): + r = getrequestrower(request, userid=userid) + if r.user != request.user: + messages.error( + request, 'You can only access your own workouts on Intervals.icu, not those of your athletes') + url = reverse('plannedsession_intervalsimport_view', + kwargs={'userid': request.user.id}) + return HttpResponseRedirect(url) + + integration = importsources['intervals'](request.user) + sessions_list = integration.get_plannedsessions_list() + + if request.method == 'POST': # pragma: no cover + try: + tdict = dict(request.POST.lists()) + print(tdict) + ids = tdict['session'] + sessionids = [int(id) for id in ids] + for sessionid in sessionids: + try: + _ = integration.get_plannedsession(sessionid) + except NoTokenError: + pass + messages.info( + request, + 'Your Intervals.icu planned sessions will be imported in the background.' + ' It may take a few minutes before they appear.') + url = reverse('plannedsessions_view') + return HttpResponseRedirect(url) + except KeyError: + pass + + return render(request, 'intervals_list_import.html', + { + 'folders': sessions_list, + 'rower': r, + 'active': 'nav-plans', + }) + -# The page where you select which Strava workout to import @login_required() @user_passes_test(isplanmember, login_url="/rowers/paidplans/", message="This functionality requires a Self-coach plan or higher",