Merge branch 'release/v13.18'
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
# cache
|
||||
/django_cache/
|
||||
testcert.crt
|
||||
testcert.key
|
||||
|
||||
# Compiled python modules.
|
||||
*.pyc
|
||||
|
||||
+2
-1
@@ -57,7 +57,8 @@ class RowerInline(admin.StackedInline):
|
||||
'stravatoken','stravatokenexpirydate','stravarefreshtoken',
|
||||
'stravaexportas','strava_auto_export',
|
||||
'strava_auto_import',
|
||||
'runkeepertoken','runkeeper_auto_export',)}),
|
||||
'runkeepertoken','runkeeper_auto_export',
|
||||
'garmintoken','garminrefreshtoken')}),
|
||||
('Team',
|
||||
{'fields':('friends','privacy','team')}),
|
||||
)
|
||||
|
||||
@@ -157,10 +157,6 @@ def add_stroke_data(user,c2id,workoutid,startdatetime,csvfilename,
|
||||
return 1
|
||||
|
||||
def get_c2_workouts(rower):
|
||||
|
||||
if not ispromember(rower.user):
|
||||
return 0
|
||||
|
||||
try:
|
||||
thetoken = c2_open(rower.user)
|
||||
except NoTokenError:
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
from rowers.imports import *
|
||||
import datetime
|
||||
import requests
|
||||
from requests_oauthlib import OAuth1,OAuth1Session
|
||||
from requests import Request, Session
|
||||
import rowers.mytypes as mytypes
|
||||
from rowers.mytypes import otwtypes
|
||||
from rowers.rower_rules import is_workout_user,ispromember
|
||||
from iso8601 import ParseError
|
||||
|
||||
import pandas as pd
|
||||
|
||||
import numpy
|
||||
import json
|
||||
from json.decoder import JSONDecodeError
|
||||
|
||||
from rowsandall_app.settings import (
|
||||
GARMIN_CLIENT_KEY, GARMIN_REDIRECT_URI, GARMIN_CLIENT_SECRET
|
||||
)
|
||||
|
||||
from rowers.tasks import handle_c2_import_stroke_data, handle_c2_sync
|
||||
import django_rq
|
||||
queue = django_rq.get_queue('default')
|
||||
queuelow = django_rq.get_queue('low')
|
||||
queuehigh = django_rq.get_queue('low')
|
||||
from rowers.utils import myqueue
|
||||
from rowers.models import C2WorldClassAgePerformance,Rower,Workout,TombStone
|
||||
|
||||
from django.core.exceptions import PermissionDenied
|
||||
|
||||
from rowers.utils import custom_exception_handler,NoTokenError
|
||||
from rowingdata import rowingdata
|
||||
|
||||
oauth_data = {
|
||||
'client_id': GARMIN_CLIENT_KEY,
|
||||
'client_secret': GARMIN_CLIENT_SECRET,
|
||||
'redirect_uri': GARMIN_REDIRECT_URI,
|
||||
'authorization_uri': "https://connectapi.garmin.com/oauth-service/oauth/request_token",
|
||||
'content_type': 'application/x-www-form-urlencoded',
|
||||
'tokenname': 'garmintoken',
|
||||
'refreshtokenname': 'garminrefreshtoken',
|
||||
'expirydatename': 'garmintokenexpirydate',
|
||||
'bearer_auth': True,
|
||||
'base_url': "https://connect.garmin.com/oauthConfirm",
|
||||
'scope':'write',
|
||||
'headers': 'Authorization: OAuth oauth_version="1.0"'
|
||||
}
|
||||
|
||||
columns = {
|
||||
'startTimeInSeconds':'TimeStamp (sec)',
|
||||
'latitudeInDegree':' latitude',
|
||||
'longitudeInDegree':' longitude',
|
||||
'heartRate':' HRCur (bpm)',
|
||||
'speedMetersPerSecond':' AverageBoatSpeed (m/s)',
|
||||
'totalDistanceInMeters':' Horizontal (meters)',
|
||||
'clockDurationInSeconds':' ElapsedTime (sec)',
|
||||
'powerInWatts':' Power (watts)',
|
||||
'bikeCadenceInRPM':' Cadence (stokes/min)',
|
||||
}
|
||||
|
||||
def garmin_authorize():
|
||||
redirect_uri = oauth_data['redirect_uri']
|
||||
client_secret = oauth_data['client_secret']
|
||||
client_id = oauth_data['client_id']
|
||||
base_uri = oauth_data['base_url']
|
||||
|
||||
|
||||
garmin = OAuth1Session(oauth_data['client_id'],
|
||||
client_secret=oauth_data['client_secret'],
|
||||
)
|
||||
fetch_response = garmin.fetch_request_token(oauth_data['authorization_uri'])
|
||||
resource_owner_key = fetch_response.get('oauth_token')
|
||||
resource_owner_secret = fetch_response.get('oauth_token_secret')
|
||||
|
||||
authorization_url = garmin.authorization_url(base_uri)
|
||||
return authorization_url,resource_owner_key,resource_owner_secret
|
||||
|
||||
def garmin_processcallback(redirect_response,resource_owner_key,resource_owner_secret):
|
||||
garmin = OAuth1Session(oauth_data['client_id'],
|
||||
client_secret=oauth_data['client_secret'],
|
||||
)
|
||||
oauth_response = garmin.parse_authorization_response(redirect_response)
|
||||
|
||||
verifier = oauth_response.get('oauth_verifier')
|
||||
token = oauth_response.get('oauth_token')
|
||||
access_token_url = 'https://connectapi.garmin.com/oauth-service/oauth/access_token'
|
||||
|
||||
# Using OAuth1Session
|
||||
garmin = OAuth1Session(oauth_data['client_id'],
|
||||
client_secret=oauth_data['client_secret'],
|
||||
resource_owner_key=resource_owner_key,
|
||||
resource_owner_secret=resource_owner_secret,
|
||||
verifier=verifier,)
|
||||
oauth_tokens = garmin.fetch_access_token(access_token_url)
|
||||
|
||||
garmintoken = oauth_tokens.get('oauth_token')
|
||||
garminrefreshtoken = oauth_tokens.get('oauth_token_secret')
|
||||
|
||||
return garmintoken,garminrefreshtoken
|
||||
|
||||
def garmin_open(user):
|
||||
r = Rower.objects.get(user=user)
|
||||
token = Rower.garmintoken
|
||||
|
||||
if (token == '') or (token is None):
|
||||
raise NoTokenError("User has no garmin token")
|
||||
|
||||
return token
|
||||
|
||||
def get_garmin_workout_list(user):
|
||||
r = Rower.objects.get(user=user)
|
||||
if (r.garmintoken == '') or (r.stravatoken is None):
|
||||
s = "Token doesn't exist. Need to authorize"
|
||||
return custom_exception_handler(401,s)
|
||||
|
||||
garmin = OAuth1Session(oauth_data['client_id'],
|
||||
client_secret=oauth_data['client_secret'],
|
||||
resource_owner_key=r.garmintoken,
|
||||
resource_owner_secret=r.garminrefreshtoken,
|
||||
)
|
||||
|
||||
url = 'https://healthapi.garmin.com/wellness-api/rest/activities?uploadStartTimeInSeconds=1593113760&uploadEndTimeInSeconds=1593279360'
|
||||
|
||||
result = garmin.get(url)
|
||||
|
||||
return result
|
||||
|
||||
def garmin_getworkout(garminid,r,activity):
|
||||
starttime = activity['startTimeInSeconds']
|
||||
startdatetime = arrow.get(starttime)
|
||||
durationseconds = activity['durationInSeconds']
|
||||
duration = dataprep.totaltime_sec_to_string(durationseconds)
|
||||
activitytype = activity['activityType']
|
||||
name = 'Imported from Garmin'
|
||||
date = startdatetime.date()
|
||||
try:
|
||||
distance = activity['distanceInMeters']
|
||||
except KeyError:
|
||||
distance = 0
|
||||
try:
|
||||
averagehr = activity['averageHeartRateInBeatsPerMinute']
|
||||
maxhr = activity['maxHeartRateInBeatsPerMinute']
|
||||
except KeyError:
|
||||
averagehr = 0
|
||||
maxhr = 0
|
||||
try:
|
||||
w = Workout.objects.get(uploadedtogarmin=garminid)
|
||||
except Workout.DoesNotExist:
|
||||
newcsvfile='media/garmin{code}_{importid}.csv'
|
||||
w = Workout(user=r,csvfilename=newcsvfile)
|
||||
w.startdatetime = datetime.datetime(
|
||||
year=startdatetime.year,
|
||||
month=startdatetime.month,
|
||||
day=startdatetime.day,
|
||||
hour=startdatetime.hour,
|
||||
minute=startdatetime.minute,
|
||||
second=startdatetime.second,
|
||||
tzinfo=startdatetime.tzinfo)
|
||||
w.starttime = startdatetime.time()
|
||||
try:
|
||||
w.duration = datetime.datetime.strptime(duration,"%H:%M:%S.%f").time()
|
||||
except ValueError:
|
||||
w.duration = datetime.datetime.strptime(duration,"%H:%M:%S")
|
||||
try:
|
||||
w.workouttype = mytypes.garminmappinginv[activitytype]
|
||||
except KeyError:
|
||||
w.workouttype = 'other'
|
||||
w.name = name
|
||||
w.date = date
|
||||
w.distance = distance
|
||||
w.uploadedtogarmin = garminid
|
||||
|
||||
w.save()
|
||||
|
||||
|
||||
return w
|
||||
|
||||
def garmin_workouts_from_details(activities):
|
||||
for activity in activities:
|
||||
garmintoken = activity['userAccessToken']
|
||||
try:
|
||||
r = Rower.objects.get(garmintoken=garmintoken)
|
||||
garminid = activity['summaryId'][:-7]
|
||||
summary = activity['summary']
|
||||
w = garmin_getworkout(garminid,r,summary)
|
||||
samples = activity['samples']
|
||||
df = pd.DataFrame(samples)
|
||||
df.rename(columns=columns,inplace=True)
|
||||
try:
|
||||
pace = 500./df[' AverageBoatSpeed (m/s)']
|
||||
except KeyError:
|
||||
pace = 0
|
||||
df[' AverageBoatSpeed (m/s)'] = 0
|
||||
df[' Stroke500mPace (sec/500m)'] = pace
|
||||
try:
|
||||
spm = df[' Cadence (stokes/min)']
|
||||
except KeyError:
|
||||
df[' Cadence (stokes/min)'] = 0
|
||||
df['cum_dist'] = df[' Horizontal (meters)']
|
||||
try:
|
||||
power = df[' Power (watts)']
|
||||
except KeyError:
|
||||
df[' Power (watts)'] = 0
|
||||
df[' AverageDriveForce (lbs)'] = 0
|
||||
df[' DriveLength (meters)'] = 0
|
||||
df[' PeakDriveForce (lbs)'] = 1
|
||||
df[' DriveTime (ms)'] = 0
|
||||
|
||||
rowdata = rowingdata(df=df)
|
||||
rowdata.write_csv(w.csvfilename,gzip=True)
|
||||
data = dataprep.dataprep(rowdata.df,id=w.id)
|
||||
summary = rowdata.allstats()
|
||||
w.summary=summary
|
||||
w.uploadedtogarmin = garminid
|
||||
w.save()
|
||||
except Rower.DoesNotExist:
|
||||
pass
|
||||
|
||||
return 1
|
||||
|
||||
def garmin_workouts_from_summaries(activities):
|
||||
for activity in activities:
|
||||
garmintoken = activity['userAccessToken']
|
||||
try:
|
||||
r = Rower.objects.get(garmintoken=garmintoken)
|
||||
id = activity['summaryId']
|
||||
w = garmin_getworkout(id,r,activity)
|
||||
except Rower.DoesNotExist:
|
||||
pass
|
||||
|
||||
return 1
|
||||
@@ -296,7 +296,7 @@ class Command(BaseCommand):
|
||||
message.delete()
|
||||
|
||||
# Strava
|
||||
rowers = Rower.objects.filter(strava_auto_import=True).exclude(rowerplan='basic')
|
||||
rowers = Rower.objects.filter(strava_auto_import=True)
|
||||
for r in rowers:
|
||||
stravastuff.get_strava_workouts(r)
|
||||
|
||||
|
||||
+8
-2
@@ -837,6 +837,10 @@ class Rower(models.Model):
|
||||
polaruserid = models.IntegerField(default=0)
|
||||
polar_auto_import = models.BooleanField(default=False)
|
||||
|
||||
garmintoken = models.CharField(default='',max_length=200,blank=True,null=True)
|
||||
garminrefreshtoken = models.CharField(default='',max_length=1000,
|
||||
blank=True,null=True)
|
||||
|
||||
stravatoken = models.CharField(default='',max_length=200,blank=True,null=True)
|
||||
stravatokenexpirydate = models.DateTimeField(blank=True,null=True)
|
||||
stravarefreshtoken = models.CharField(default='',max_length=1000,
|
||||
@@ -2797,6 +2801,7 @@ class Workout(models.Model):
|
||||
uploadedtounderarmour = models.BigIntegerField(default=0)
|
||||
uploadedtotp = models.BigIntegerField(default=0)
|
||||
uploadedtorunkeeper = models.BigIntegerField(default=0)
|
||||
uploadedtogarmin = models.BigIntegerField(default=0)
|
||||
forceunit = models.CharField(default='lbs',
|
||||
choices = (
|
||||
('lbs','lbs'),
|
||||
@@ -2837,12 +2842,13 @@ class Workout(models.Model):
|
||||
boattype = self.boattype
|
||||
workouttype = self.workouttype
|
||||
|
||||
|
||||
if workouttype != 'water':
|
||||
stri = u'{d} {n} {dist}m {duration:%H:%M:%S} {workouttype} {ownerfirst} {ownerlast}'.format(
|
||||
stri = u'{d} {n} {dist}m {duration} {workouttype} {ownerfirst} {ownerlast}'.format(
|
||||
d = date.strftime('%Y-%m-%d'),
|
||||
n = name,
|
||||
dist = distance,
|
||||
duration = duration,
|
||||
duration = duration.strftime("%H:%M:%S"),
|
||||
workouttype = workouttype,
|
||||
ownerfirst = ownerfirst,
|
||||
ownerlast = ownerlast,
|
||||
|
||||
+231
-186
@@ -42,207 +42,252 @@ workouttypes_ordered = collections.OrderedDict({
|
||||
|
||||
workouttypes = tuple((key, value) for key, value in workouttypes_ordered.items())
|
||||
|
||||
def Reverse(tuples):
|
||||
new_tup = tuples[::-1]
|
||||
return new_tup
|
||||
|
||||
stravacollection = (
|
||||
('water','Rowing'),
|
||||
('rower','Rowing'),
|
||||
('skierg','NordicSki'),
|
||||
('Bike','Ride'),
|
||||
('bikeerg','Ride'),
|
||||
('dynamic','Rowing'),
|
||||
('slides','Rowing'),
|
||||
('paddle','StandUpPaddling'),
|
||||
('snow','NordicSki'),
|
||||
('coastal','Rowing'),
|
||||
('c-boat','Rowing'),
|
||||
('churchboat','Rowing'),
|
||||
('Ride','Ride'),
|
||||
('Run','Run'),
|
||||
('NordicSki','NordicSki'),
|
||||
('Swim','Swim'),
|
||||
('Hike','Hike'),
|
||||
('Walk','Walk'),
|
||||
('Canoeing','Canoeing'),
|
||||
('Crossfit','Crossfit'),
|
||||
('StandUpPaddling','StandUpPaddling'),
|
||||
('IceSkate','IceSkate'),
|
||||
('WeightTraining','WeightTraining'),
|
||||
('InlineSkate','InlineSkate'),
|
||||
('Kayaking','Kayaking'),
|
||||
('Workout','Workout'),
|
||||
('Yoga','Yoga'),
|
||||
('other','Workout'),
|
||||
)
|
||||
|
||||
stravamapping = collections.OrderedDict({
|
||||
'water':'Rowing',
|
||||
'rower':'Rowing',
|
||||
'skierg':'NordicSki',
|
||||
'Bike':'Ride',
|
||||
'bikeerg':'Ride',
|
||||
'dynamic':'Rowing',
|
||||
'slides':'Rowing',
|
||||
'paddle':'StandUpPaddling',
|
||||
'snow':'NordicSki',
|
||||
'coastal':'Rowing',
|
||||
'c-boat':'Rowing',
|
||||
'churchboat':'Rowing',
|
||||
'Ride':'Ride',
|
||||
'Run':'Run',
|
||||
'NordicSki':'NordicSki',
|
||||
'Swim':'Swim',
|
||||
'Hike':'Hike',
|
||||
'Walk':'Walk',
|
||||
'Canoeing':'Canoeing',
|
||||
'Crossfit':'Crossfit',
|
||||
'StandUpPaddling':'StandUpPaddling',
|
||||
'IceSkate':'IceSkate',
|
||||
'WeightTraining':'WeightTraining',
|
||||
'InlineSkate':'InlineSkate',
|
||||
'Kayaking':'Kayaking',
|
||||
'Workout':'Workout',
|
||||
'Yoga':'Yoga',
|
||||
'other':'Workout',
|
||||
stravamapping = {key:value for key,value in Reverse(stravacollection)}
|
||||
|
||||
})
|
||||
garmincollection = (
|
||||
('water','ROWING'),
|
||||
('rower','INDOOR_ROWING'),
|
||||
('skierg','CROSS_COUNTRY_SKIING'),
|
||||
('Bike','ROAD_BIKING'),
|
||||
('bikeerg','INDOOR_CYCLING'),
|
||||
('dynamic','INDOOR_ROWING'),
|
||||
('slides','INDOOR_ROWING'),
|
||||
('paddle','PADDLING'),
|
||||
('snow','CROSS_COUNTRY_SKIING'),
|
||||
('coastal','ROWING'),
|
||||
('c-boat','ROWING'),
|
||||
('churchboat','ROWING'),
|
||||
('Ride','ROAD_BIKING'),
|
||||
('Run','RUNNING'),
|
||||
('NordicSki','CROSS_COUNTRY_SKIING'),
|
||||
('Swim','SWIMMING'),
|
||||
('Hike','HIKING'),
|
||||
('Walk','WALKING'),
|
||||
('Canoeing','PADDLING'),
|
||||
('Crossfit','FITNESS_EQUIPMENT'),
|
||||
('StandUpPaddling','STAND_UP_PADDLEBOARDING'),
|
||||
('IceSkate','SKATING'),
|
||||
('WeightTraining','STRENGTH_TRAINING'),
|
||||
('InlineSkate','INLINE_SKATING'),
|
||||
('Kayaking','PADDLING'),
|
||||
('Workout','OTHER'),
|
||||
('Yoga','OTHER'),
|
||||
('other','OTHER'),
|
||||
)
|
||||
|
||||
stmapping = collections.OrderedDict({
|
||||
'water':'Rowing',
|
||||
'rower':'Rowing',
|
||||
'skierg':'Skiing:Nordic',
|
||||
'Bike':'Cycling',
|
||||
'bikeerg':'Cycling',
|
||||
'dynamic':'Rowing',
|
||||
'slides':'Rowing',
|
||||
'paddle':'Other:Paddling',
|
||||
'snow':'Skiing:Nordic',
|
||||
'coastal':'Rowing',
|
||||
'c-boat':'Rowing',
|
||||
'churchboat':'Rowing',
|
||||
'Ride':'Cycling',
|
||||
'Run':'Running',
|
||||
'NordicSki':'Skiing:Nordic',
|
||||
'Swim':'Swimming',
|
||||
'Hike':'Hiking',
|
||||
'RollerSki':'Other:RollerSki',
|
||||
'Walk':'Other:Walk',
|
||||
'Canoeing':'Other:Canoeing',
|
||||
'Crossfit':'Other:Crossfit',
|
||||
'StandUpPaddling':'Other:StandUpPaddling',
|
||||
'IceSkate':'Skating',
|
||||
'WeightTraining':'Other:WeightTraining',
|
||||
'InlineSkate':'Skating:InlineSkate',
|
||||
'Kayaking':'Other:Kayaking',
|
||||
'Workout':'Other:Workout',
|
||||
'Yoga':'Other',
|
||||
'other':'Other',
|
||||
garminmapping = {key:value for key,value in Reverse(garmincollection)}
|
||||
|
||||
})
|
||||
stcollection = (
|
||||
('water','Rowing'),
|
||||
('rower','Rowing'),
|
||||
('skierg','Skiing:Nordic'),
|
||||
('Bike','Cycling'),
|
||||
('bikeerg','Cycling'),
|
||||
('dynamic','Rowing'),
|
||||
('slides','Rowing'),
|
||||
('paddle','Other:Paddling'),
|
||||
('snow','Skiing:Nordic'),
|
||||
('coastal','Rowing'),
|
||||
('c-boat','Rowing'),
|
||||
('churchboat','Rowing'),
|
||||
('Ride','Cycling'),
|
||||
('Run','Running'),
|
||||
('NordicSki','Skiing:Nordic'),
|
||||
('Swim','Swimming'),
|
||||
('Hike','Hiking'),
|
||||
('RollerSki','Other:RollerSki'),
|
||||
('Walk','Other:Walk'),
|
||||
('Canoeing','Other:Canoeing'),
|
||||
('Crossfit','Other:Crossfit'),
|
||||
('StandUpPaddling','Other:StandUpPaddling'),
|
||||
('IceSkate','Skating'),
|
||||
('WeightTraining','Other:WeightTraining'),
|
||||
('InlineSkate','Skating:InlineSkate'),
|
||||
('Kayaking','Other:Kayaking'),
|
||||
('Workout','Other:Workout'),
|
||||
('Yoga','Other'),
|
||||
('other','Other'),
|
||||
)
|
||||
|
||||
rkmapping = collections.OrderedDict({
|
||||
'water':'Rowing',
|
||||
'rower':'Rowing',
|
||||
'skierg':'Cross-Country Skiing',
|
||||
'Bike':'Cycling',
|
||||
'bikeerg':'Cycling',
|
||||
'dynamic':'Rowing',
|
||||
'slides':'Rowing',
|
||||
'paddle':'Other:Paddling',
|
||||
'snow':'Cross-Country Skiing',
|
||||
'coastal':'Rowing',
|
||||
'c-boat':'Rowing',
|
||||
'churchboat':'Rowing',
|
||||
'Ride':'Cycling',
|
||||
'Run':'Running',
|
||||
'NordicSki':'Cross-Country Skiing',
|
||||
'Swim':'Swimming',
|
||||
'Hike':'Hiking',
|
||||
'Walk':'Walking',
|
||||
'Canoeing':'Other',
|
||||
'Crossfit':'CrossFit',
|
||||
'StandUpPaddling':'Other',
|
||||
'IceSkate':'Skating',
|
||||
'WeightTraining':'Other',
|
||||
'InlineSkate':'Skating',
|
||||
'Kayaking':'Other',
|
||||
'Workout':'Other',
|
||||
'other':'Other',
|
||||
'Yoga':'Other',
|
||||
})
|
||||
stmapping = {key:value for key,value in Reverse(stcollection)}
|
||||
|
||||
polarmapping = collections.OrderedDict({
|
||||
'water':'Rowing',
|
||||
'rower':'Rowing',
|
||||
'skierg':'Skiing',
|
||||
'Bike':'Cycling',
|
||||
'bikeerg':'Cycling',
|
||||
'dynamic':'Rowing',
|
||||
'slides':'Rowing',
|
||||
'paddle':'Other Outdoor',
|
||||
'snow':'Skiing',
|
||||
'coastal':'Rowing',
|
||||
'c-boat':'Rowing',
|
||||
'churchboat':'Rowing',
|
||||
'Ride':'Cycling',
|
||||
'Run':'Running',
|
||||
'NordicSki':'Skiing',
|
||||
'Swim':'Swimming',
|
||||
'Hike':'Hiking',
|
||||
'Walk':'Walking',
|
||||
'Canoeing':'Canoeing',
|
||||
'Crossfit':'Crossfit',
|
||||
'StandUpPaddling':'Other Outdoor',
|
||||
'IceSkate':'Skating',
|
||||
'WeightTraining':'Strength training',
|
||||
'InlineSkate':'Skating',
|
||||
'Kayaking':'Kayaking',
|
||||
'Workout':'Other Indoor',
|
||||
'other':'Other Indoor',
|
||||
'Yoga':'Yoga',
|
||||
})
|
||||
rkcollection = (
|
||||
('water','Rowing'),
|
||||
('rower','Rowing'),
|
||||
('skierg','Cross-Country Skiing'),
|
||||
('Bike','Cycling'),
|
||||
('bikeerg','Cycling'),
|
||||
('dynamic','Rowing'),
|
||||
('slides','Rowing'),
|
||||
('paddle','Other:Paddling'),
|
||||
('snow','Cross-Country Skiing'),
|
||||
('coastal','Rowing'),
|
||||
('c-boat','Rowing'),
|
||||
('churchboat','Rowing'),
|
||||
('Ride','Cycling'),
|
||||
('Run','Running'),
|
||||
('NordicSki','Cross-Country Skiing'),
|
||||
('Swim','Swimming'),
|
||||
('Hike','Hiking'),
|
||||
('Walk','Walking'),
|
||||
('Canoeing','Other'),
|
||||
('Crossfit','CrossFit'),
|
||||
('StandUpPaddling','Other'),
|
||||
('IceSkate','Skating'),
|
||||
('WeightTraining','Other'),
|
||||
('InlineSkate','Skating'),
|
||||
('Kayaking','Other'),
|
||||
('Workout','Other'),
|
||||
('other','Other'),
|
||||
('Yoga','Other'),
|
||||
)
|
||||
|
||||
tpmapping = collections.OrderedDict({
|
||||
'water':'rowing',
|
||||
'rower':'rowing',
|
||||
'skierg':'xc-ski',
|
||||
'Bike':'bike',
|
||||
'Bikeerg':'bike',
|
||||
'dynamic':'rowing',
|
||||
'slides':'rowing',
|
||||
'paddle':'other',
|
||||
'snow':'xc-ski',
|
||||
'coastal':'rowing',
|
||||
'c-boat':'rowing',
|
||||
'churchboat':'rowing',
|
||||
'Ride':'cycling',
|
||||
'Run':'run',
|
||||
'NordicSki':'xc-ski',
|
||||
'Swim':'swim',
|
||||
'Hike':'other',
|
||||
'Walk':'walk',
|
||||
'Canoeing':'other',
|
||||
'Crossfit':'other',
|
||||
'StandUpPaddling':'other',
|
||||
'IceSkate':'other',
|
||||
'WeightTraining':'strength',
|
||||
'InlineSkate':'other',
|
||||
'Kayaking':'other',
|
||||
'Workout':'other',
|
||||
'other':'other',
|
||||
'Yoga':'other',
|
||||
})
|
||||
rkmapping = {key:value for key,value in Reverse(rkcollection)}
|
||||
|
||||
c2mapping = collections.OrderedDict({
|
||||
'water':'water',
|
||||
'rower':'rower',
|
||||
'skierg':'skierg',
|
||||
'Bike':'bike',
|
||||
'bikeerg':'bike',
|
||||
'dynamic':'dynamic',
|
||||
'slides':'slides',
|
||||
'paddle':'paddle',
|
||||
'snow':'snow',
|
||||
'coastal':'water',
|
||||
'c-boat':'water',
|
||||
'churchboat':'water',
|
||||
'Ride':'bike',
|
||||
'Run':None,
|
||||
'NordicSki':'snow',
|
||||
'Swim':None,
|
||||
'Hike':None,
|
||||
'Walk':None,
|
||||
'Canoeing':'paddle',
|
||||
'Crossfit':None,
|
||||
'StandUpPaddling':None,
|
||||
'IceSkate':None,
|
||||
'WeightTraining':None,
|
||||
'InlineSkate':None,
|
||||
'Kayaking':None,
|
||||
'Workout':None,
|
||||
'other':None,
|
||||
'Yoga':None,
|
||||
polarcollection = (
|
||||
('water','Rowing'),
|
||||
('rower','Rowing'),
|
||||
('skierg','Skiing'),
|
||||
('Bike','Cycling'),
|
||||
('bikeerg','Cycling'),
|
||||
('dynamic','Rowing'),
|
||||
('slides','Rowing'),
|
||||
('paddle','Other Outdoor'),
|
||||
('snow','Skiing'),
|
||||
('coastal','Rowing'),
|
||||
('c-boat','Rowing'),
|
||||
('churchboat','Rowing'),
|
||||
('Ride','Cycling'),
|
||||
('Run','Running'),
|
||||
('NordicSki','Skiing'),
|
||||
('Swim','Swimming'),
|
||||
('Hike','Hiking'),
|
||||
('Walk','Walking'),
|
||||
('Canoeing','Canoeing'),
|
||||
('Crossfit','Crossfit'),
|
||||
('StandUpPaddling','Other Outdoor'),
|
||||
('IceSkate','Skating'),
|
||||
('WeightTraining','Strength training'),
|
||||
('InlineSkate','Skating'),
|
||||
('Kayaking','Kayaking'),
|
||||
('Workout','Other Indoor'),
|
||||
('other','Other Indoor'),
|
||||
('Yoga','Yoga'),
|
||||
)
|
||||
|
||||
})
|
||||
polarmapping = {key:value for key,value in Reverse(polarcollection)}
|
||||
|
||||
c2mappinginv = {value:key for key,value in reversed(c2mapping.items()) if value is not None}
|
||||
tpcollection = (
|
||||
('water','rowing'),
|
||||
('rower','rowing'),
|
||||
('skierg','xc-ski'),
|
||||
('Bike','bike'),
|
||||
('Bikeerg','bike'),
|
||||
('dynamic','rowing'),
|
||||
('slides','rowing'),
|
||||
('paddle','other'),
|
||||
('snow','xc-ski'),
|
||||
('coastal','rowing'),
|
||||
('c-boat','rowing'),
|
||||
('churchboat','rowing'),
|
||||
('Ride','cycling'),
|
||||
('Run','run'),
|
||||
('NordicSki','xc-ski'),
|
||||
('Swim','swim'),
|
||||
('Hike','other'),
|
||||
('Walk','walk'),
|
||||
('Canoeing','other'),
|
||||
('Crossfit','other'),
|
||||
('StandUpPaddling','other'),
|
||||
('IceSkate','other'),
|
||||
('WeightTraining','strength'),
|
||||
('InlineSkate','other'),
|
||||
('Kayaking','other'),
|
||||
('Workout','other'),
|
||||
('other','other'),
|
||||
('Yoga','other'),
|
||||
)
|
||||
|
||||
stravamappinginv = {value:key for key,value in reversed(stravamapping.items()) if value is not None}
|
||||
tpmapping = {key:value for key,value in Reverse(tpcollection)}
|
||||
|
||||
stmappinginv = {value:key for key,value in reversed(stmapping.items()) if value is not None}
|
||||
c2collection = (
|
||||
('water','water'),
|
||||
('rower','rower'),
|
||||
('skierg','skierg'),
|
||||
('Bike','bike'),
|
||||
('bikeerg','bike'),
|
||||
('dynamic','dynamic'),
|
||||
('slides','slides'),
|
||||
('paddle','paddle'),
|
||||
('snow','snow'),
|
||||
('coastal','water'),
|
||||
('c-boat','water'),
|
||||
('churchboat','water'),
|
||||
('Ride','bike'),
|
||||
('Run',None),
|
||||
('NordicSki','snow'),
|
||||
('Swim',None),
|
||||
('Hike',None),
|
||||
('Walk',None),
|
||||
('Canoeing','paddle'),
|
||||
('Crossfit',None),
|
||||
('StandUpPaddling',None),
|
||||
('IceSkate',None),
|
||||
('WeightTraining',None),
|
||||
('InlineSkate',None),
|
||||
('Kayaking',None),
|
||||
('Workout',None),
|
||||
('other',None),
|
||||
('Yoga',None),
|
||||
)
|
||||
|
||||
rkmappinginv = {value:key for key,value in reversed(rkmapping.items()) if value is not None}
|
||||
c2mapping = {key:value for key,value in Reverse(c2collection)}
|
||||
c2mappinginv = {value:key for key,value in Reverse(c2collection) if value is not None}
|
||||
|
||||
polarmappinginv = {value:key for key,value in reversed(polarmapping.items()) if value is not None}
|
||||
stravamappinginv = {value:key for key,value in Reverse(stravacollection) if value is not None}
|
||||
|
||||
stmappinginv = {value:key for key,value in Reverse(stcollection) if value is not None}
|
||||
|
||||
rkmappinginv = {value:key for key,value in Reverse(rkcollection) if value is not None}
|
||||
|
||||
polarmappinginv = {value:key for key,value in Reverse(polarcollection) if value is not None}
|
||||
|
||||
garminmappinginv = {value:key for key, value in Reverse(garmincollection) if value is not None}
|
||||
|
||||
otwtypes = (
|
||||
'water',
|
||||
|
||||
@@ -126,10 +126,6 @@ def get_strava_workout_list(user,limit_n=0):
|
||||
|
||||
# gets all new Strava workouts for a rower
|
||||
def get_strava_workouts(rower):
|
||||
|
||||
if not ispromember(rower.user):
|
||||
return 0
|
||||
|
||||
try:
|
||||
thetoken = strava_open(rower.user)
|
||||
except NoTokenError:
|
||||
|
||||
@@ -220,6 +220,17 @@
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
<li id="export-garmin">
|
||||
{% if workout.uploadedtogarmin %}
|
||||
<a href="https://connect.garmin.com/modern/activity/{{ workout.uploadedtogarmin }}">
|
||||
Garmin <i class="fas fa-check"></i>
|
||||
</a>
|
||||
{% elif user.rower.garmintoken == None or user.rower.garmintoken == '' %}
|
||||
<a href="/rowers/me/garminauthorize/">
|
||||
Connect to Garmin
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
<li id="export-csv">
|
||||
<a href="/rowers/workout/{{ workout.id|encode }}/emailcsv/">
|
||||
CSV
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Automatic Synchronization with other fitness sites</td>
|
||||
<td> </td>
|
||||
<td>✔</td>
|
||||
<td>✔</td>
|
||||
<td>✔</td>
|
||||
<td>✔</td>
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
{% block main %}
|
||||
<h1>Import and Export Settings for {{ rower.user.first_name }} {{ rower.user.last_name }}</h1>
|
||||
|
||||
{% if user.rower.rowerplan == 'basic' %}
|
||||
The auto import and export settings only work on <a href="/rowers/paidplans/">a paid plan</a>.
|
||||
{% endif %}
|
||||
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
@@ -23,8 +19,12 @@ The auto import and export settings only work on <a href="/rowers/paidplans/">a
|
||||
<input type="submit" value="Save">
|
||||
</form>
|
||||
</p>
|
||||
<p>
|
||||
Garmin Connnect has no manual sync, so connecting your account to your Garmin account will
|
||||
automatically auto-sync workouts from Garmin to Rowsandall (but not in the other direction).
|
||||
</p>
|
||||
<p>Click on one of the icons below to connect to the service of your
|
||||
choice or to renew the authorization</p>
|
||||
choice or to renew the authorization.</p>
|
||||
<p><a href="/rowers/me/stravaauthorize/"><img src="/static/img/ConnectWithStrava.png" alt="connect with strava" width="120"></a></p>
|
||||
<p><a href="/rowers/me/c2authorize/"><img src="/static/img/blueC2logo.png" alt="connect with Concept2" width="120"></a></p>
|
||||
<p><a href="/rowers/me/sporttracksauthorize/"><img src="/static/img/sporttracks-button.png" alt="connect with SportTracks" width="120"></a></p>
|
||||
@@ -34,6 +34,8 @@ The auto import and export settings only work on <a href="/rowers/paidplans/">a
|
||||
alt="connect with Polar" width="130"></a></p>
|
||||
<p><a href="/rowers/me/tpauthorize/"><img src="/static/img/TP_logo_horz_2_color.png"
|
||||
alt="connect with Polar" width="130"></a></p>
|
||||
<p><a href="/rowers/me/garminauthorize"><img src="/static/img/garmin_badge_130.png"
|
||||
alt="connect with Garmin" with="130"></a></p>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3,12 +3,98 @@ from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import transaction
|
||||
|
||||
#from __future__ import print_function
|
||||
from .statements import *
|
||||
nu = datetime.datetime.now()
|
||||
|
||||
|
||||
import rowers
|
||||
from rowers import dataprep
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(TESTING=True)
|
||||
class GarminObjects(DjangoTestCase):
|
||||
def setUp(self):
|
||||
self.c = Client()
|
||||
self.u = User.objects.create_user('john',
|
||||
'sander@ds.ds',
|
||||
'koeinsloot')
|
||||
|
||||
self.u.first_name = 'John'
|
||||
self.u.last_name = 'Sander'
|
||||
self.u.save()
|
||||
self.r = Rower.objects.create(user=self.u,gdproptin=True,surveydone=True,
|
||||
gdproptindate=timezone.now()
|
||||
)
|
||||
self.r.garmintoken = 'dfdzf'
|
||||
self.r.garminrefreshtoken = 'fsls'
|
||||
self.r.save()
|
||||
self.c.login(username='john',password='koeinsloot')
|
||||
|
||||
self.nu = datetime.datetime.now()
|
||||
|
||||
def tearDown(self):
|
||||
ws = Workout.objects.filter(user=self.r)
|
||||
for w in ws:
|
||||
w.delete()
|
||||
|
||||
def test_garmin_push_summaries(self):
|
||||
data = json.load(open('rowers/tests/testdata/garminsummarydata.txt','r'))
|
||||
response = self.c.post('/rowers/garmin/summaries/',json.dumps(data),
|
||||
content_type="application/json")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
#response = self.c.get('/rowers/workout/'+encoded1+'/', follow=True)
|
||||
#self.assertEqual(response.status_code, 200)
|
||||
|
||||
ws = Workout.objects.filter(user=self.r)
|
||||
self.assertEqual(ws.count(),3)
|
||||
|
||||
def test_garmin_push_details3(self):
|
||||
data = json.load(open('rowers/tests/testdata/garmindetail3.txt','r'))
|
||||
response = self.c.post('/rowers/garmin/activities/',json.dumps(data),
|
||||
content_type='application/json')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
ws = Workout.objects.filter(user=self.r)
|
||||
self.assertEqual(ws.count(),1)
|
||||
|
||||
data,w = dataprep.getrowdata_db(id=ws[0].id)
|
||||
|
||||
self.assertEqual(len(data),515)
|
||||
|
||||
def test_garmin_push_details2(self):
|
||||
data = json.load(open('rowers/tests/testdata/garmindetail2.txt','r'))
|
||||
response = self.c.post('/rowers/garmin/activities/',json.dumps(data),
|
||||
content_type='application/json')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
ws = Workout.objects.filter(user=self.r)
|
||||
self.assertEqual(ws.count(),3)
|
||||
|
||||
data,w = dataprep.getrowdata_db(id=ws[0].id)
|
||||
self.assertEqual(len(data),451)
|
||||
|
||||
def test_garmin_push_details1(self):
|
||||
data = json.load(open('rowers/tests/testdata/garmindetail1.txt','r'))
|
||||
response = self.c.post('/rowers/garmin/activities/',json.dumps(data),
|
||||
content_type='application/json')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = self.c.get('/rowers/workout/'+encoded1+'/', follow=True)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
ws = Workout.objects.filter(user=self.r)
|
||||
self.assertEqual(ws.count(),2)
|
||||
|
||||
data,w = dataprep.getrowdata_db(id=ws[0].id)
|
||||
self.assertEqual(len(data),2)
|
||||
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(TESTING=True)
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
[ { "userId": "25858854-f086-4026-83ac-a3bfc97dcbb1",
|
||||
"userAccessToken": "dfdzf",
|
||||
"summaryId" : "14098044-detail",
|
||||
"summary" : {
|
||||
"durationInSeconds" : 4828,
|
||||
"startTimeInSeconds" : 1593691200,
|
||||
"startTimeOffsetInSeconds" : -18000,
|
||||
"activityType" : "WALKING",
|
||||
"averageHeartRateInBeatsPerMinute" : 90,
|
||||
"averageRunCadenceInStepsPerMinute" : 38.0,
|
||||
"averageSpeedInMetersPerSecond" : 0.8794985,
|
||||
"averagePaceInMinutesPerKilometer" : 18.247978,
|
||||
"activeKilocalories" : 233,
|
||||
"distanceInMeters" : 4103.25,
|
||||
"maxHeartRateInBeatsPerMinute" : 124,
|
||||
"maxPaceInMinutesPerKilometer" : 3.2660666,
|
||||
"maxRunCadenceInStepsPerMinute" : 120.0,
|
||||
"maxSpeedInMetersPerSecond" : 4.289388,
|
||||
"steps" : 1623,
|
||||
"totalElevationGainInMeters" : 20.09
|
||||
},
|
||||
"samples" : [ {
|
||||
"startTimeInSeconds" : 1593691200,
|
||||
"speedMetersPerSecond" : 0.0,
|
||||
"totalDistanceInMeters" : 25.0,
|
||||
"timerDurationInSeconds" : 23,
|
||||
"clockDurationInSeconds" : 30,
|
||||
"movingDurationInSeconds" : 0
|
||||
}, {
|
||||
"startTimeInSeconds" : 1593691200,
|
||||
"speedMetersPerSecond" : 0.0,
|
||||
"totalDistanceInMeters" : 27.0,
|
||||
"timerDurationInSeconds" : 23,
|
||||
"clockDurationInSeconds" : 27,
|
||||
"movingDurationInSeconds" : 0
|
||||
} ]
|
||||
}, {
|
||||
"userId": "25858854-f086-4026-83ac-a3bfc97dcbb1",
|
||||
"userAccessToken": "dfdzf",
|
||||
"summaryId" : "14033650-detail",
|
||||
"summary" : {
|
||||
"durationInSeconds" : 4778,
|
||||
"startTimeInSeconds" : 1593777600,
|
||||
"startTimeOffsetInSeconds" : -18000,
|
||||
"activityType" : "WALKING",
|
||||
"averageHeartRateInBeatsPerMinute" : 86,
|
||||
"averageRunCadenceInStepsPerMinute" : 30.0,
|
||||
"averageSpeedInMetersPerSecond" : 0.6164423,
|
||||
"averagePaceInMinutesPerKilometer" : 18.036388,
|
||||
"activeKilocalories" : 182,
|
||||
"distanceInMeters" : 3742.63,
|
||||
"maxHeartRateInBeatsPerMinute" : 120,
|
||||
"maxPaceInMinutesPerKilometer" : 3.5400813,
|
||||
"maxRunCadenceInStepsPerMinute" : 118.0,
|
||||
"maxSpeedInMetersPerSecond" : 4.5140486,
|
||||
"steps" : 1623,
|
||||
"totalElevationGainInMeters" : 20.9
|
||||
},
|
||||
"samples" : [ {
|
||||
"startTimeInSeconds" : 1593691200,
|
||||
"speedMetersPerSecond" : 1.0,
|
||||
"totalDistanceInMeters" : 27.0,
|
||||
"timerDurationInSeconds" : 27,
|
||||
"clockDurationInSeconds" : 27,
|
||||
"movingDurationInSeconds" : 0
|
||||
}, {
|
||||
"startTimeInSeconds" : 1593691200,
|
||||
"speedMetersPerSecond" : 1.0,
|
||||
"totalDistanceInMeters" : 24.0,
|
||||
"timerDurationInSeconds" : 23,
|
||||
"clockDurationInSeconds" : 29,
|
||||
"movingDurationInSeconds" : 0
|
||||
} ]
|
||||
} ]
|
||||
+4
File diff suppressed because one or more lines are too long
+2
File diff suppressed because one or more lines are too long
+43
@@ -0,0 +1,43 @@
|
||||
{"activities":
|
||||
[
|
||||
{
|
||||
"userId": "25858854-f086-4026-83ac-a3bfc97dcbb1",
|
||||
"userAccessToken": "dfdzf",
|
||||
"summaryId" : "5180795946",
|
||||
"durationInSeconds" : 640,
|
||||
"startTimeInSeconds" : 1593758587,
|
||||
"startTimeOffsetInSeconds" : 7200,
|
||||
"activityType" :
|
||||
"INDOOR_ROWING",
|
||||
"averageHeartRateInBeatsPerMinute" : 96,
|
||||
"activeKilocalories" : 55,
|
||||
"deviceName" : "vivoactive3",
|
||||
"maxHeartRateInBeatsPerMinute" : 105},
|
||||
{
|
||||
"userId": "25858854-f086-4026-83ac-a3bfc97dcbb1",
|
||||
"userAccessToken": "dfdzf",
|
||||
"summaryId" : "5180798644",
|
||||
"durationInSeconds" : 3959,
|
||||
"startTimeInSeconds" : 1593759504,
|
||||
"startTimeOffsetInSeconds" : 7200,
|
||||
"activityType" : "STRENGTH_TRAINING",
|
||||
"averageHeartRateInBeatsPerMinute" : 97,
|
||||
"activeKilocalories" : 296,
|
||||
"deviceName" : "vivoactive3",
|
||||
"maxHeartRateInBeatsPerMinute" : 132,
|
||||
"steps" : 630},
|
||||
{
|
||||
"userId": "25858854-f086-4026-83ac-a3bfc97dcbb1",
|
||||
"userAccessToken": "dfdzf",
|
||||
"summaryId" : "5180799021",
|
||||
"durationInSeconds" : 309,
|
||||
"startTimeInSeconds" : 1593763505,
|
||||
"startTimeOffsetInSeconds" : 7200,
|
||||
"activityType" : "INDOOR_CYCLING",
|
||||
"averageHeartRateInBeatsPerMinute" : 109,
|
||||
"activeKilocalories" : 38,
|
||||
"deviceName" : "vivoactive3",
|
||||
"maxHeartRateInBeatsPerMinute" : 119
|
||||
}
|
||||
]
|
||||
}
|
||||
+6
-6
@@ -535,7 +535,7 @@ def do_sync(w,options, quick=False):
|
||||
pass
|
||||
|
||||
|
||||
if ('upload_to_C2' in options and options['upload_to_C2']) or (w.user.c2_auto_export and ispromember(w.user.user)):
|
||||
if ('upload_to_C2' in options and options['upload_to_C2']) or (w.user.c2_auto_export):
|
||||
try:
|
||||
message,id = c2stuff.workout_c2_upload(w.user.user,w,asynchron=True)
|
||||
except NoTokenError:
|
||||
@@ -544,7 +544,7 @@ def do_sync(w,options, quick=False):
|
||||
except:
|
||||
pass
|
||||
|
||||
if ('upload_to_Strava' in options and upload_to_strava) or (w.user.strava_auto_export and ispromember(w.user.user)):
|
||||
if ('upload_to_Strava' in options and upload_to_strava) or (w.user.strava_auto_export):
|
||||
try:
|
||||
message,id = stravastuff.workout_strava_upload(
|
||||
w.user.user,w,quick=quick,asynchron=True,
|
||||
@@ -563,7 +563,7 @@ def do_sync(w,options, quick=False):
|
||||
|
||||
|
||||
|
||||
if ('upload_to_SportTracks' in options and options['upload_to_SportTracks']) or (w.user.sporttracks_auto_export and ispromember(w.user.user)):
|
||||
if ('upload_to_SportTracks' in options and options['upload_to_SportTracks']) or (w.user.sporttracks_auto_export):
|
||||
try:
|
||||
message,id = sporttracksstuff.workout_sporttracks_upload(
|
||||
w.user.user,w,asynchron=True,
|
||||
@@ -573,7 +573,7 @@ def do_sync(w,options, quick=False):
|
||||
id = 0
|
||||
|
||||
|
||||
if ('upload_to_RunKeeper' in options and options['upload_to_RunKeeper']) or (w.user.runkeeper_auto_export and ispromember(w.user.user)):
|
||||
if ('upload_to_RunKeeper' in options and options['upload_to_RunKeeper']) or (w.user.runkeeper_auto_export):
|
||||
try:
|
||||
message,id = runkeeperstuff.workout_runkeeper_upload(
|
||||
w.user.user,w,asynchron=True,
|
||||
@@ -582,7 +582,7 @@ def do_sync(w,options, quick=False):
|
||||
message = "Please connect to Runkeeper first"
|
||||
id = 0
|
||||
|
||||
if ('upload_to_MapMyFitness' in options and options['upload_to_MapMyFitness']) or (w.user.mapmyfitness_auto_export and ispromember(w.user.user)):
|
||||
if ('upload_to_MapMyFitness' in options and options['upload_to_MapMyFitness']) or (w.user.mapmyfitness_auto_export):
|
||||
try:
|
||||
message,id = underarmourstuff.workout_ua_upload(
|
||||
w.user.user,w
|
||||
@@ -592,7 +592,7 @@ def do_sync(w,options, quick=False):
|
||||
id = 0
|
||||
|
||||
|
||||
if ('upload_to_TrainingPeaks' in options and options['upload_to_TrainingPeaks']) or (w.user.trainingpeaks_auto_export and ispromember(w.user.user)):
|
||||
if ('upload_to_TrainingPeaks' in options and options['upload_to_TrainingPeaks']) or (w.user.trainingpeaks_auto_export):
|
||||
try:
|
||||
message,id = tpstuff.workout_tp_upload(
|
||||
w.user.user,w
|
||||
|
||||
@@ -415,6 +415,8 @@ urlpatterns = [
|
||||
re_path(r'^workout/(?P<pk>\b[0-9A-Fa-f]+\b)/delete/$',login_required(
|
||||
views.WorkoutDelete.as_view()),
|
||||
name='workout_delete'),
|
||||
re_path(r'^garmin/summaries/',views.garmin_summaries_view,name='garmin_summaries_view'),
|
||||
re_path(r'^garmin/activities/',views.garmin_details_view,name='garmin_details_view'),
|
||||
# re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/delete/$',login_required(
|
||||
# views.workout_code_delete_view),name='workout_code_delete'),
|
||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/smoothenpace/$',views.workout_smoothenpace_view,name='workout_smoothenpace_view'),
|
||||
@@ -532,6 +534,7 @@ urlpatterns = [
|
||||
re_path(r'^me/polarauthorize/$',views.rower_polar_authorize,name='rower_polar_authorize'),
|
||||
re_path(r'^me/revokeapp/(?P<id>\d+)/$',views.rower_revokeapp_view,name='rower_revokeapp_view'),
|
||||
re_path(r'^me/stravaauthorize/$',views.rower_strava_authorize,name='rower_strava_authorize'),
|
||||
re_path(r'^me/garminauthorize/$',views.rower_garmin_authorize,name='rower_garmin_authorize'),
|
||||
re_path(r'^me/sporttracksauthorize/$',views.rower_sporttracks_authorize,name='rower_sporttracks_authorize'),
|
||||
re_path(r'^me/underarmourauthorize/$',views.rower_underarmour_authorize,name='rower_underarmour_authorize'),
|
||||
re_path(r'^me/tpauthorize/$',views.rower_tp_authorize,name='rower_tp_authorize'),
|
||||
|
||||
@@ -401,6 +401,14 @@ def rower_c2_authorize(request):
|
||||
url += "&scope="+scope
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
# Garmin authorization
|
||||
@login_required()
|
||||
def rower_garmin_authorize(request):
|
||||
authorization_url,token,secret = garmin_stuff.garmin_authorize()
|
||||
request.session['garmin_owner_key'] = token
|
||||
request.session['garmin_owner_secret'] = secret
|
||||
return HttpResponseRedirect(authorization_url)
|
||||
|
||||
# Strava Authorization
|
||||
@login_required()
|
||||
def rower_strava_authorize(request):
|
||||
@@ -705,7 +713,23 @@ def rower_process_polarcallback(request):
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
|
||||
# process Garmin callback
|
||||
@login_required()
|
||||
def rower_process_garmincallback(request):
|
||||
r = getrower(request.user)
|
||||
absoluteurl = request.build_absolute_uri()
|
||||
|
||||
key = request.session['garmin_owner_key']
|
||||
secret = request.session['garmin_owner_secret']
|
||||
garmintoken,garminrefreshtoken = garmin_stuff.garmin_processcallback(absoluteurl,key,secret)
|
||||
r.garmintoken = garmintoken
|
||||
r.garminrefreshtoken = garminrefreshtoken
|
||||
r.save()
|
||||
|
||||
successmessage = "Tokens stored. Good to go"
|
||||
messages.info(request,successmessage)
|
||||
url = reverse('workouts_view')
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
# Process Strava Callback
|
||||
@login_required()
|
||||
@@ -984,6 +1008,38 @@ def workout_stravaimport_view(request,message="",userid=0):
|
||||
|
||||
return HttpResponse(res)
|
||||
|
||||
# For push notifications from Garmin
|
||||
@csrf_exempt
|
||||
def garmin_summaries_view(request):
|
||||
if request.method != 'POST':
|
||||
raise Http404("Not allowed")
|
||||
|
||||
# POST request
|
||||
data = json.loads(request.body)
|
||||
activities = data['activities']
|
||||
result = garmin_stuff.garmin_workouts_from_summaries(activities)
|
||||
|
||||
if result:
|
||||
return HttpResponse(status=200)
|
||||
|
||||
return HttpResponse(status=400)
|
||||
|
||||
@csrf_exempt
|
||||
def garmin_details_view(request):
|
||||
if request.method != 'POST':
|
||||
raise Http404("not allowed")
|
||||
|
||||
# POST request
|
||||
data = json.loads(request.body)
|
||||
result = garmin_stuff.garmin_workouts_from_details(data)
|
||||
|
||||
|
||||
if result:
|
||||
return HttpResponse(status=200)
|
||||
|
||||
return HttpResponse(status=400)
|
||||
|
||||
|
||||
# The page where you select which RunKeeper workout to import
|
||||
@login_required()
|
||||
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
||||
|
||||
@@ -157,6 +157,7 @@ from rowers.sporttracksstuff import sporttracks_open
|
||||
from rowers.tpstuff import tp_open
|
||||
from iso8601 import ParseError
|
||||
import rowers.stravastuff as stravastuff
|
||||
import rowers.garmin_stuff as garmin_stuff
|
||||
from rowers.stravastuff import strava_open
|
||||
import rowers.polarstuff as polarstuff
|
||||
import rowers.sporttracksstuff as sporttracksstuff
|
||||
|
||||
@@ -282,6 +282,10 @@ STRAVA_CLIENT_ID = CFG['strava_client_id']
|
||||
STRAVA_CLIENT_SECRET = CFG['strava_client_secret']
|
||||
STRAVA_REDIRECT_URI = CFG['strava_callback']
|
||||
|
||||
# Garmin
|
||||
GARMIN_CLIENT_KEY = CFG["garmin_client_key"]
|
||||
GARMIN_CLIENT_SECRET = CFG['garmin_client_secret']
|
||||
GARMIN_REDIRECT_URI = CFG['garmin_callback']
|
||||
|
||||
# SportTracks
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ TEMPLATES[0]['OPTIONS']['debug'] = DEBUG
|
||||
ALLOWED_HOSTS = ['localhost','127.0.0.1']
|
||||
|
||||
# INSTALLED_APPS += ['debug_toolbar',]
|
||||
#INSTALLED_APPS += ["sslserver"]
|
||||
|
||||
# MIDDLEWARE_CLASSES += ['debug_toolbar.middleware.DebugToolbarMiddleware',]
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ urlpatterns += [
|
||||
# re_path(r'^admin/rq/',include('django_rq_dashboard.urls')),
|
||||
re_path(r'^call\_back',rowersviews.rower_process_callback),
|
||||
re_path(r'^stravacall\_back',rowersviews.rower_process_stravacallback),
|
||||
re_path(r'^garmin\_callback',rowersviews.rower_process_garmincallback),
|
||||
re_path(r'^sporttracks\_callback',rowersviews.rower_process_sporttrackscallback),
|
||||
re_path(r'^underarmour\_callback',rowersviews.rower_process_underarmourcallback),
|
||||
re_path(r'^polarflowcallback',rowersviews.rower_process_polarcallback),
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 262 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
Reference in New Issue
Block a user