import somewhat working (workout type not)
This commit is contained in:
@@ -109,7 +109,7 @@ class SyncIntegration(metaclass=ABCMeta):
|
||||
if 'grant_type' in self.oauth_data:
|
||||
if self.oauth_data['grant_type']:
|
||||
post_data['grant_type'] = self.oauth_data['grant_type']
|
||||
if 'strava' in self.oauth_data['autorization_uri']:
|
||||
if 'strava' in self.oauth_data['authorization_uri']:
|
||||
post_data['grant_type'] = "authorization_code"
|
||||
|
||||
if 'json' in self.oauth_data['content_type']:
|
||||
|
||||
@@ -6,6 +6,7 @@ from rowers import mytypes
|
||||
|
||||
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
|
||||
|
||||
import urllib
|
||||
import gzip
|
||||
@@ -26,13 +27,25 @@ queue = django_rq.get_queue('default', default_timeout=3600)
|
||||
queuelow = django_rq.get_queue('low', default_timeout=3600)
|
||||
queuehigh = django_rq.get_queue('high', default_timeout=3600)
|
||||
|
||||
|
||||
def seconds_to_duration(seconds):
|
||||
hours = seconds // 3600
|
||||
minutes = (seconds % 3600) // 60
|
||||
remaining_seconds = seconds % 60
|
||||
|
||||
# Format as "H:MM:SS" or "MM:SS" if no hours
|
||||
if hours > 0:
|
||||
return f"{int(hours)}:{int(minutes):02}:{int(remaining_seconds):02}"
|
||||
else:
|
||||
return f"{int(minutes)}:{int(remaining_seconds):02}"
|
||||
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
|
||||
intervals_authorize_url = 'https://intervals.icu/oauth/authorize?'
|
||||
intervals_token_url = 'https://intervals.icu/oauth/token'
|
||||
intervals_token_url = 'https://intervals.icu/api/oauth/token'
|
||||
|
||||
class IntervalsIntegration(SyncIntegration):
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -47,14 +60,33 @@ class IntervalsIntegration(SyncIntegration):
|
||||
'expirydatename': 'intervals_exp',
|
||||
'refreshtokenname': 'intervals_r',
|
||||
'bearer_auth': True,
|
||||
'base_uri': 'https://intervals.icu/api/v1/',
|
||||
'base_url': 'https://intervals.icu/api/v1/',
|
||||
'grant_type': 'refresh_token',
|
||||
'headers': headers,
|
||||
'scope': 'ACTIVITY:WRITE'
|
||||
'scope': 'ACTIVITY:WRITE, LIBRARY:READ',
|
||||
}
|
||||
|
||||
def get_token(self, code, *args, **kwargs):
|
||||
return super(IntervalsIntegration, self).get_token(code, *args, **kwargs)
|
||||
post_data = {
|
||||
'client_id': str(self.oauth_data['client_id']),
|
||||
'client_secret': self.oauth_data['client_secret'],
|
||||
'code': code,
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
intervals_token_url,
|
||||
data=post_data,
|
||||
)
|
||||
|
||||
if response.status_code not in [200, 201]:
|
||||
dologging('intervals.icu.log',response.text)
|
||||
return [0,"Failed to get token. ",0]
|
||||
|
||||
token_json = response.json()
|
||||
access_token = token_json['access_token']
|
||||
athlete = token_json['athlete']
|
||||
|
||||
return [access_token, athlete, '']
|
||||
|
||||
def get_name(self):
|
||||
return 'Intervals'
|
||||
@@ -63,8 +95,8 @@ class IntervalsIntegration(SyncIntegration):
|
||||
return 'intervals'
|
||||
|
||||
def open(self, *args, **kwargs):
|
||||
dologging('intervals.icu.log', "Getting token for user {id}".format(id=self.rower.id))
|
||||
token = super(IntervalsIntegration).open(*args, **kwargs)
|
||||
# dologging('intervals.icu.log', "Getting token for user {id}".format(id=self.rower.id))
|
||||
token = super(IntervalsIntegration, self).open(*args, **kwargs)
|
||||
return token
|
||||
|
||||
def createworkoutdata(self, w, *args, **kwargs) -> str:
|
||||
@@ -73,13 +105,63 @@ class IntervalsIntegration(SyncIntegration):
|
||||
def workout_export(self, workout, *args, **kwargs) -> str:
|
||||
return NotImplemented
|
||||
|
||||
def get_workouts(workout, *args, **kwargs) -> int:
|
||||
return NotImplemented
|
||||
def get_workout_list(self, *args, **kwargs) -> int:
|
||||
url = self.oauth_data['base_url'] + 'athlete/0/activities?'
|
||||
startdate = timezone.now() - timedelta(days=365)
|
||||
enddate = timezone.now() + timedelta(days=1)
|
||||
url += 'oldest=' + startdate.strftime('%Y-%m-%d') + '&newest=' + enddate.strftime('%Y-%m-%d')
|
||||
headers = {
|
||||
'accept': '*/*',
|
||||
'authorization': 'Bearer ' + self.open(),
|
||||
}
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
if response.status_code != 200:
|
||||
dologging('intervals.icu.log', response.text)
|
||||
return []
|
||||
|
||||
data = response.json()
|
||||
known_interval_ids = get_known_ids(self.rower, 'intervalsid')
|
||||
|
||||
workouts = []
|
||||
|
||||
for item in data:
|
||||
i = item['id']
|
||||
r = item['type']
|
||||
d = item['distance']
|
||||
ttot = seconds_to_duration(item['moving_time'])
|
||||
s = item['start_date']
|
||||
s2 = ''
|
||||
c = item['name']
|
||||
if i in known_interval_ids:
|
||||
nnn = ''
|
||||
else:
|
||||
nnn = 'NEW'
|
||||
|
||||
keys = ['id','distance','duration','starttime',
|
||||
'rowtype','source','name','new']
|
||||
|
||||
values = [i, d, ttot, s, r, s2, c, nnn]
|
||||
|
||||
ress = dict(zip(keys, values))
|
||||
workouts.append(ress)
|
||||
|
||||
return workouts
|
||||
|
||||
|
||||
def get_workout(self, id, *args, **kwargs) -> int:
|
||||
return NotImplemented
|
||||
_ = self.open()
|
||||
r = self.rower
|
||||
|
||||
def get_workout_list(self, *args, **kwargs) -> list:
|
||||
record = create_or_update_syncrecord(r, None, intervalsid=id)
|
||||
|
||||
_ = myqueue(queuehigh,
|
||||
handle_intervals_getworkout,
|
||||
self.rower,
|
||||
self.rower.intervals_token,
|
||||
id)
|
||||
|
||||
def get_workouts(workout, *args, **kwargs) -> list:
|
||||
return NotImplemented
|
||||
|
||||
def make_authorization_url(self, *args, **kwargs):
|
||||
|
||||
@@ -1242,8 +1242,7 @@ class Rower(models.Model):
|
||||
|
||||
intervals_token = models.CharField(
|
||||
default='', max_length=200, blank=True, null=True)
|
||||
intervals_exp = models.DateTimeField(blank=True, null=True)
|
||||
intervals_r = models.CharField(default='', max_length=200, blank=True, null=True)
|
||||
intervals_owner_id = models.CharField(default='', max_length=200,blank=True, null=True)
|
||||
|
||||
privacychoices = (
|
||||
('visible', 'Visible'),
|
||||
@@ -3696,6 +3695,7 @@ class Workout(models.Model):
|
||||
uploadedtogarmin = models.BigIntegerField(default=0)
|
||||
uploadedtorp3 = models.BigIntegerField(default=0)
|
||||
uploadedtonk = models.BigIntegerField(default=0)
|
||||
uploadedtointervals = models.BigIntegerField(default=0)
|
||||
forceunit = models.CharField(default='lbs',
|
||||
choices=(
|
||||
('lbs', 'lbs'),
|
||||
@@ -3851,6 +3851,7 @@ class SyncRecord(models.Model):
|
||||
c2id = models.BigIntegerField(unique=True,null=True,default=None)
|
||||
tpid = models.BigIntegerField(unique=True,null=True,default=None)
|
||||
rp3id = models.BigIntegerField(unique=True,null=True,default=None)
|
||||
intervalsid = models.BigIntegerField(unique=True, null=True, default=None)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.workout:
|
||||
@@ -3866,7 +3867,7 @@ class SyncRecord(models.Model):
|
||||
|
||||
str2 = ''
|
||||
|
||||
for field in ['stravaid', 'sporttracksid', 'nkid', 'c2id', 'tpid']:
|
||||
for field in ['stravaid', 'sporttracksid', 'nkid', 'c2id', 'tpid', 'intervalsid']:
|
||||
value = getattr(self, field, None)
|
||||
if value is not None:
|
||||
str2 += '{w}: {v},'.format(
|
||||
|
||||
@@ -24,6 +24,7 @@ from rowers.courseutils import (
|
||||
InvalidTrajectoryError
|
||||
)
|
||||
from rowers.emails import send_template_email
|
||||
from rowers.mytypes import fitmappinginv
|
||||
from rowers.nkimportutils import (
|
||||
get_nk_summary, get_nk_allstats, get_nk_intervalstats, getdict, strokeDataToDf,
|
||||
add_workout_from_data
|
||||
@@ -59,6 +60,8 @@ import rowingdata
|
||||
from rowingdata import make_cumvalues, make_cumvalues_array
|
||||
from uuid import uuid4
|
||||
from rowingdata import rowingdata as rdata
|
||||
from rowingdata import FITParser as FP
|
||||
from rowingdata.otherparsers import FitSummaryData
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
@@ -3485,6 +3488,72 @@ def handle_nk_async_workout(alldata, userid, nktoken, nkid, delaysec, defaulttim
|
||||
|
||||
return workoutid
|
||||
|
||||
@app.task
|
||||
def handle_intervals_getworkout(rower, intervalstoken, workoutid, debug=False, **kwargs):
|
||||
authorizationstring = str('Bearer '+intervalstoken)
|
||||
headers = {
|
||||
'authorization': authorizationstring,
|
||||
}
|
||||
|
||||
url = "https://intervals.icu/api/v1/activity/{}".format(workoutid)
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
if response.status_code != 200:
|
||||
return 0
|
||||
|
||||
data = response.json()
|
||||
try:
|
||||
title = data['name']
|
||||
except KeyError:
|
||||
title = 'Intervals workout'
|
||||
|
||||
try:
|
||||
workouttype = fitmappinginv[data['type']]
|
||||
print(data['type'])
|
||||
except KeyError:
|
||||
workouttype = 'water'
|
||||
|
||||
url = "https://intervals.icu/api/v1/activity/{workoutid}/fit-file".format(workoutid=workoutid)
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
|
||||
if response.status_code != 200:
|
||||
return 0
|
||||
|
||||
try:
|
||||
fit_data = response.content
|
||||
fit_filename = 'media/'+f'{uuid4().hex[:16]}.fit'
|
||||
with open(fit_filename, 'wb') as fit_file:
|
||||
fit_file.write(fit_data)
|
||||
except Exception as e:
|
||||
return 0
|
||||
|
||||
try:
|
||||
row = FP(fit_filename)
|
||||
rowdata = rowingdata.rowingdata(df=row.df)
|
||||
rowsummary = FitSummaryData(fit_filename)
|
||||
duration = totaltime_sec_to_string(rowdata.duration)
|
||||
distance = rowdata.df[" Horizontal (meters)"].iloc[-1]
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return 0
|
||||
|
||||
uploadoptions = {
|
||||
'secret': UPLOAD_SERVICE_SECRET,
|
||||
'user': rower.user.id,
|
||||
'boattype': '1x',
|
||||
'workouttype': workouttype,
|
||||
'file': fit_filename,
|
||||
'title': title,
|
||||
'rpe': 0,
|
||||
'notes': '',
|
||||
'offline': False,
|
||||
}
|
||||
|
||||
url = UPLOAD_SERVICE_URL
|
||||
handle_request_post(url, uploadoptions)
|
||||
|
||||
return 1
|
||||
|
||||
@app.task
|
||||
def handle_c2_getworkout(userid, c2token, c2id, defaulttimezone, debug=False, **kwargs):
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
<li id="sporttracks"><a href="/rowers/workout/sporttracksimport/">SportTracks</a></li>
|
||||
<li id="polar"><a href="/rowers/workout/polarimport/">Polar</a></li>
|
||||
<li id="rp3"><a href="/rowers/workout/rp3import/">RP3</a></li>
|
||||
<li id="intervals"><a href="/rowers/workout/intervalsimport/">Intervals.icu</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul> <!-- cd-accordion-menu -->
|
||||
|
||||
@@ -123,6 +123,8 @@
|
||||
alt="connect with RP3" width="130"></a></p>
|
||||
<p><a href="/rowers/me/rojaboauthorize"><img src="/static/img/rojabo.png"
|
||||
alt="connect with Rojabo" width="130"></a></p>
|
||||
<p><a href="/rowers/me/intervalsauthorize"><img src="/static/img/intervals_icu.png"
|
||||
alt="connect with intervals.icu" height="30"></a></p>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ importauthorizeviews = {
|
||||
'nk': 'rower_integration_authorize',
|
||||
'rp3': 'rower_integration_authorize',
|
||||
'garmin': 'rower_garmin_authorize',
|
||||
'intervals': 'rower_integration_authorize',
|
||||
}
|
||||
|
||||
|
||||
@@ -173,6 +174,37 @@ def rower_process_twittercallback(request): # pragma: no cover
|
||||
|
||||
# Process Polar Callback
|
||||
|
||||
@login_required()
|
||||
def rower_process_intervalscallback(request):
|
||||
integration = importsources['intervals'](request.user)
|
||||
r = getrower(request.user)
|
||||
try:
|
||||
code = request.GET['code']
|
||||
res = integration.get_token(code)
|
||||
except MultiValueDictKeyError:
|
||||
message = "The resource owner or authorization server denied the request"
|
||||
messages.error(request, message)
|
||||
|
||||
url = reverse('rower_exportsettings_view')
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
access_token = res[0]
|
||||
athlete = res[1]
|
||||
if access_token == 0:
|
||||
message = res[1]
|
||||
message += 'Connection to intervals.icu failed.'
|
||||
messages.error(request, message)
|
||||
url = reverse('rower_exportsettings_view')
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
r.intervals_token = access_token
|
||||
r.intervals_owner_id = athlete['id']
|
||||
r.save()
|
||||
|
||||
successmessage = "Tokens stored. Good to go. Please check your import/export settings"
|
||||
messages.info(request, successmessage)
|
||||
url = reverse('rower_exportsettings_view')
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
@login_required()
|
||||
def rower_process_polarcallback(request):
|
||||
|
||||
Reference in New Issue
Block a user