Private
Public Access
1
0

Merge branch 'release/v7.01'

This commit is contained in:
Sander Roosendaal
2018-06-27 07:51:10 +02:00
21 changed files with 454 additions and 463 deletions
+6 -31
View File
@@ -34,7 +34,7 @@ import sys
import urllib import urllib
from requests import Request, Session from requests import Request, Session
from utils import myqueue,uniqify,isprorower from utils import myqueue,uniqify,isprorower, custom_exception_handler, NoTokenError
from rowers.types import otwtypes from rowers.types import otwtypes
@@ -46,32 +46,7 @@ queue = django_rq.get_queue('default')
queuelow = django_rq.get_queue('low') queuelow = django_rq.get_queue('low')
queuehigh = django_rq.get_queue('low') queuehigh = django_rq.get_queue('low')
# Custom error class - to raise a NoTokenError
class C2NoTokenError(Exception):
def __init__(self,value):
self.value=value
def __str__(self):
return repr(self.value)
# Custom exception handler, returns a 401 HTTP message
# with exception details in the json data
def custom_exception_handler(exc,message):
response = {
"errors": [
{
"code": str(exc),
"detail": message,
}
]
}
res = HttpResponse(message)
res.status_code = 401
res.json = json.dumps(response)
return res
# Checks if user has Concept2 tokens, resets tokens if they are # Checks if user has Concept2 tokens, resets tokens if they are
# expired. # expired.
@@ -79,16 +54,16 @@ def c2_open(user):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
if (r.c2token == '') or (r.c2token is None): if (r.c2token == '') or (r.c2token is None):
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
raise C2NoTokenError("User has no token") raise NoTokenError("User has no token")
else: else:
if (timezone.now()>r.tokenexpirydate): if (timezone.now()>r.tokenexpirydate):
res = rower_c2_token_refresh(user) res = rower_c2_token_refresh(user)
if res == None: if res == None:
raise C2NoTokenError("User has no token") raise NoTokenError("User has no token")
if res[0] != None: if res[0] != None:
thetoken = res[0] thetoken = res[0]
else: else:
raise C2NoTokenError("User has no token") raise NoTokenError("User has no token")
else: else:
thetoken = r.c2token thetoken = r.c2token
@@ -122,7 +97,7 @@ def get_c2_workouts(rower):
try: try:
thetoken = c2_open(rower.user) thetoken = c2_open(rower.user)
except C2NoTokenError: except NoTokenError:
return 0 return 0
res = get_c2_workout_list(rower.user,page=1) res = get_c2_workout_list(rower.user,page=1)
@@ -693,7 +668,7 @@ def workout_c2_upload(user,w):
if (checkworkoutuser(user,w)): if (checkworkoutuser(user,w)):
c2userid = get_userid(r.c2token) c2userid = get_userid(r.c2token)
if not c2userid: if not c2userid:
raise C2NoTokenError raise NoTokenError
data = createc2workoutdata(w) data = createc2workoutdata(w)
if data == 0: if data == 0:
-24
View File
@@ -46,30 +46,6 @@ from utils import geo_distance
from rowers.courseutils import coursetime_paths, coursetime_first,time_in_path from rowers.courseutils import coursetime_paths, coursetime_first,time_in_path
def get_course_timezone(course):
polygons = GeoPolygon.objects.filter(course = course)
points = GeoPoint.objects.filter(polygon = polygons[0])
lat = points[0].latitude
lon = points[0].longitude
tf = TimezoneFinder()
try:
timezone_str = tf.timezone_at(lng=lon,lat=lat)
except ValueError:
timezone_str = 'UTC'
if timezone_str is None:
timezone_str = tf.closest_timezone_at(lng=lon,lat=lat)
if timezone_str is None:
timezone_str = 'UTC'
return timezone_str
def crewnerdcourse(doc): def crewnerdcourse(doc):
courses = [] courses = []
for course in doc: for course in doc:
+18
View File
@@ -0,0 +1,18 @@
from django.conf import settings
user = settings.DATABASES['default']['USER']
password = settings.DATABASES['default']['PASSWORD']
database_name = settings.DATABASES['default']['NAME']
host = settings.DATABASES['default']['HOST']
port = settings.DATABASES['default']['PORT']
database_url = 'mysql://{user}:{password}@{host}:{port}/{database_name}'.format(
user=user,
password=password,
database_name=database_name,
host=host,
port=port,
)
if settings.DEBUG or user=='':
database_url = 'sqlite:///db.sqlite3'
+1 -17
View File
@@ -71,24 +71,8 @@ queuehigh = django_rq.get_queue('default')
from rowsandall_app.settings import SITE_URL from rowsandall_app.settings import SITE_URL
from rowers.types import otwtypes from rowers.types import otwtypes
user = settings.DATABASES['default']['USER'] from rowers.database import *
password = settings.DATABASES['default']['PASSWORD']
database_name = settings.DATABASES['default']['NAME']
host = settings.DATABASES['default']['HOST']
port = settings.DATABASES['default']['PORT']
database_url = 'mysql://{user}:{password}@{host}:{port}/{database_name}'.format(
user=user,
password=password,
database_name=database_name,
host=host,
port=port,
)
# Use SQLite local database when we're in debug mode
if settings.DEBUG or user == '':
# database_url = 'sqlite:///db.sqlite3'
database_url = 'sqlite:///' + database_name
# mapping the DB column names to the CSV file column names # mapping the DB column names to the CSV file column names
+1 -27
View File
@@ -133,34 +133,8 @@ def rdata(file,rower=rrower()):
return res return res
from utils import totaltime_sec_to_string
def totaltime_sec_to_string(totaltime):
hours = int(totaltime / 3600.)
if hours > 23:
message = 'Warning: The workout duration was longer than 23 hours. '
hours = 23
minutes = int((totaltime - 3600. * hours) / 60.)
if minutes > 59:
minutes = 59
if not message:
message = 'Warning: there is something wrong with the workout duration'
seconds = int(totaltime - 3600. * hours - 60. * minutes)
if seconds > 59:
seconds = 59
if not message:
message = 'Warning: there is something wrong with the workout duration'
tenths = int(10 * (totaltime - 3600. * hours - 60. * minutes - seconds))
if tenths > 9:
tenths = 9
if not message:
message = 'Warning: there is something wrong with the workout duration'
duration = "%s:%s:%s.%s" % (hours, minutes, seconds, tenths)
return duration
# Creates C2 stroke data # Creates C2 stroke data
def create_c2_stroke_data_db( def create_c2_stroke_data_db(
+2 -1
View File
@@ -328,7 +328,8 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
'peakforceangle','peakforce','spm','distance', 'peakforceangle','peakforce','spm','distance',
'workoutstate','driveenergy'] 'workoutstate','driveenergy']
rowdata = dataprep.getsmallrowdata_db(columns,ids=ids) rowdata = dataprep.getsmallrowdata_db(columns,ids=ids,
workstrokesonly=workstrokesonly)
rowdata.dropna(axis=1,how='all',inplace=True) rowdata.dropna(axis=1,how='all',inplace=True)
rowdata.dropna(axis=0,how='any',inplace=True) rowdata.dropna(axis=0,how='any',inplace=True)
@@ -22,6 +22,7 @@ import rowers.uploads as uploads
from rowers.mailprocessing import make_new_workout_from_email, send_confirm from rowers.mailprocessing import make_new_workout_from_email, send_confirm
import rowers.polarstuff as polarstuff import rowers.polarstuff as polarstuff
import rowers.c2stuff as c2stuff import rowers.c2stuff as c2stuff
import rowers.stravastuff as stravastuff
workoutmailbox = Mailbox.objects.get(name='workouts') workoutmailbox = Mailbox.objects.get(name='workouts')
failedmailbox = Mailbox.objects.get(name='Failed') failedmailbox = Mailbox.objects.get(name='Failed')
@@ -158,6 +159,11 @@ class Command(BaseCommand):
for r in rowers: for r in rowers:
c2stuff.get_c2_workouts(r) c2stuff.get_c2_workouts(r)
# Strava
rowers = Rower.objects.filter(strava_auto_import=True)
for r in rowers:
stravastuff.get_strava_workouts(r)
messages = Message.objects.filter(mailbox_id = workoutmailbox.id) messages = Message.objects.filter(mailbox_id = workoutmailbox.id)
message_ids = [m.id for m in messages] message_ids = [m.id for m in messages]
attachments = MessageAttachment.objects.filter( attachments = MessageAttachment.objects.filter(
+3 -16
View File
@@ -49,22 +49,7 @@ tweetapi = twitter.Api(consumer_key=TWEET_CONSUMER_KEY,
access_token_key=TWEET_ACCESS_TOKEN_KEY, access_token_key=TWEET_ACCESS_TOKEN_KEY,
access_token_secret=TWEET_ACCESS_TOKEN_SECRET) access_token_secret=TWEET_ACCESS_TOKEN_SECRET)
user = settings.DATABASES['default']['USER'] from rowers.database import *
password = settings.DATABASES['default']['PASSWORD']
database_name = settings.DATABASES['default']['NAME']
host = settings.DATABASES['default']['HOST']
port = settings.DATABASES['default']['PORT']
database_url = 'mysql://{user}:{password}@{host}:{port}/{database_name}'.format(
user=user,
password=password,
database_name=database_name,
host=host,
port=port,
)
if settings.DEBUG or user=='':
database_url = 'sqlite:///db.sqlite3'
timezones = ( timezones = (
(x,x) for x in pytz.common_timezones (x,x) for x in pytz.common_timezones
@@ -674,6 +659,7 @@ class Rower(models.Model):
verbose_name="Export Workouts to Strava as") verbose_name="Export Workouts to Strava as")
strava_auto_export = models.BooleanField(default=False) strava_auto_export = models.BooleanField(default=False)
strava_auto_import = models.BooleanField(default=False)
runkeepertoken = models.CharField(default='',max_length=200, runkeepertoken = models.CharField(default='',max_length=200,
blank=True,null=True) blank=True,null=True)
runkeeper_auto_export = models.BooleanField(default=False) runkeeper_auto_export = models.BooleanField(default=False)
@@ -2026,6 +2012,7 @@ class RowerImportExportForm(ModelForm):
'runkeeper_auto_export', 'runkeeper_auto_export',
'sporttracks_auto_export', 'sporttracks_auto_export',
'strava_auto_export', 'strava_auto_export',
'strava_auto_import',
'trainingpeaks_auto_export', 'trainingpeaks_auto_export',
] ]
+1 -43
View File
@@ -2,43 +2,8 @@ from matplotlib.ticker import MultipleLocator,FuncFormatter,NullFormatter
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy as np import numpy as np
from rows import format_pace_tick, format_pace, format_time, format_time_tick
# Make pace ticks for pace axis '2:00', '1:50', etc
def format_pace_tick(x,pos=None):
min=int(x/60)
sec=int(x-min*60.)
sec_str=str(sec).zfill(2)
template='%d:%s'
return template % (min,sec_str)
# Returns a pace string from a pace in seconds/500m, '1:45.4'
def format_pace(x,pos=None):
if isinf(x) or isnan(x):
x=0
min=int(x/60)
sec=(x-min*60.)
str1 = "{min:0>2}:{sec:0>4.1f}".format(
min = min,
sec = sec
)
return str1
# Returns a time string from a time in seconds
def format_time(x,pos=None):
min = int(x/60.)
sec = int(x-min*60)
str1 = "{min:0>2}:{sec:0>4.1f}".format(
min=min,
sec=sec,
)
return str1
# Formatting the distance tick marks # Formatting the distance tick marks
def format_dist_tick(x,pos=None): def format_dist_tick(x,pos=None):
@@ -46,13 +11,6 @@ def format_dist_tick(x,pos=None):
template='%6.3f' template='%6.3f'
return template % (km) return template % (km)
# Formatting the time tick marks (h:mm)
def format_time_tick(x,pos=None):
hour=int(x/3600)
min=int((x-hour*3600.)/60)
min_str=str(min).zfill(2)
template='%d:%s'
return template % (hour,min_str)
# Utility to select reasonable y axis range # Utility to select reasonable y axis range
# Basically the data range plus some padding, but with ultimate # Basically the data range plus some padding, but with ultimate
+1 -25
View File
@@ -50,32 +50,8 @@ from rowsandall_app.settings import (
#baseurl = 'https://polaraccesslink.com/v3-example' #baseurl = 'https://polaraccesslink.com/v3-example'
baseurl = 'https://polaraccesslink.com/v3' baseurl = 'https://polaraccesslink.com/v3'
# Custom exception handler, returns a 401 HTTP message
# with exception details in the json data
def custom_exception_handler(exc,message):
response = { from utils import NoTokenError, custom_exception_handler
"errors": [
{
"code": str(exc),
"detail": message,
}
]
}
res = HttpResponse(message)
res.status_code = 401
res.json = json.dumps(response)
return res
# Custom error class - to raise a NoTokenError
class PolarNoTokenError(Exception):
def __init__(self,value):
self.value=value
def __str__(self):
return repr(self.value)
# Exchange access code for long-lived access token # Exchange access code for long-lived access token
+2 -50
View File
@@ -36,63 +36,15 @@ from rowsandall_app.settings import (
RUNKEEPER_CLIENT_ID, RUNKEEPER_CLIENT_SECRET,RUNKEEPER_REDIRECT_URI, RUNKEEPER_CLIENT_ID, RUNKEEPER_CLIENT_SECRET,RUNKEEPER_REDIRECT_URI,
) )
# Custom error class - to raise a NoTokenError from utils import geo_distance,ewmovingaverage,NoTokenError, custom_exception_handler
class RunKeeperNoTokenError(Exception):
def __init__(self,value):
self.value=value
def __str__(self):
return repr(self.value)
# Exponentially weighted moving average
# Used for data smoothing of the jagged data obtained by Strava
# See bitbucket issue 72
def ewmovingaverage(interval,window_size):
# Experimental code using Exponential Weighted moving average
try:
intervaldf = pd.DataFrame({'v':interval})
idf_ewma1 = intervaldf.ewm(span=window_size)
idf_ewma2 = intervaldf[::-1].ewm(span=window_size)
i_ewma1 = idf_ewma1.mean().ix[:,'v']
i_ewma2 = idf_ewma2.mean().ix[:,'v']
interval2 = np.vstack((i_ewma1,i_ewma2[::-1]))
interval2 = np.mean( interval2, axis=0) # average
except ValueError:
interval2 = interval
return interval2
from utils import geo_distance
# Custom exception handler, returns a 401 HTTP message
# with exception details in the json data
def custom_exception_handler(exc,message):
response = {
"errors": [
{
"code": str(exc),
"detail": message,
}
]
}
res = HttpResponse(message)
res.status_code = 401
res.json = json.dumps(response)
return res
# Checks if user has SportTracks token, renews them if they are expired # Checks if user has SportTracks token, renews them if they are expired
def runkeeper_open(user): def runkeeper_open(user):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
if (r.runkeepertoken == '') or (r.runkeepertoken is None): if (r.runkeepertoken == '') or (r.runkeepertoken is None):
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
raise RunKeeperNoTokenError("User has no token") raise NoTokenError("User has no token")
else: else:
thetoken = r.runkeepertoken thetoken = r.runkeepertoken
+2 -27
View File
@@ -34,40 +34,15 @@ from rowers.models import Rower,Workout,checkworkoutuser
from rowsandall_app.settings import C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, SPORTTRACKS_CLIENT_SECRET, SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI from rowsandall_app.settings import C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, SPORTTRACKS_CLIENT_SECRET, SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI
# Custom error class - to raise a NoTokenError from utils import NoTokenError, custom_exception_handler
class SportTracksNoTokenError(Exception):
def __init__(self,value):
self.value=value
def __str__(self):
return repr(self.value)
# Custom exception handler, returns a 401 HTTP message
# with exception details in the json data
def custom_exception_handler(exc,message):
response = {
"errors": [
{
"code": str(exc),
"detail": message,
}
]
}
res = HttpResponse(message)
res.status_code = 401
res.json = json.dumps(response)
return res
# Checks if user has SportTracks token, renews them if they are expired # Checks if user has SportTracks token, renews them if they are expired
def sporttracks_open(user): def sporttracks_open(user):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
if (r.sporttrackstoken == '') or (r.sporttrackstoken is None): if (r.sporttrackstoken == '') or (r.sporttrackstoken is None):
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
raise SportTracksNoTokenError("User has no token") raise NoTokenError("User has no token")
else: else:
if (timezone.now()>r.sporttrackstokenexpirydate): if (timezone.now()>r.sporttrackstokenexpirydate):
thetoken = rower_sporttracks_token_refresh(user) thetoken = rower_sporttracks_token_refresh(user)
+162 -61
View File
@@ -16,6 +16,8 @@ from math import sin,cos,atan2,sqrt
import os,sys import os,sys
import gzip import gzip
from pytz import timezone as tz,utc
# Django # Django
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect, HttpResponse,JsonResponse from django.http import HttpResponseRedirect, HttpResponse,JsonResponse
@@ -24,6 +26,12 @@ from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
import django_rq
queue = django_rq.get_queue('default')
queuelow = django_rq.get_queue('low')
queuehigh = django_rq.get_queue('low')
# Project # Project
# from .models import Profile # from .models import Profile
from rowingdata import rowingdata from rowingdata import rowingdata
@@ -32,62 +40,29 @@ from rowers.models import Rower,Workout
from rowers.models import checkworkoutuser from rowers.models import checkworkoutuser
import dataprep import dataprep
from dataprep import columndict from dataprep import columndict
from utils import uniqify,isprorower,myqueue,NoTokenError, custom_exception_handler
from uuid import uuid4
import stravalib import stravalib
from stravalib.exc import ActivityUploadFailed,TimeoutExceeded from stravalib.exc import ActivityUploadFailed,TimeoutExceeded
import iso8601
from iso8601 import ParseError
import pytz
import arrow
from rowers.tasks import handle_strava_import_stroke_data
from rowsandall_app.settings import C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET from rowsandall_app.settings import C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET
# Exponentially weighted moving average
# Used for data smoothing of the jagged data obtained by Strava
# See bitbucket issue 72
def ewmovingaverage(interval,window_size):
# Experimental code using Exponential Weighted moving average
try: try:
intervaldf = pd.DataFrame({'v':interval}) from json.decoder import JSONDecodeError
idf_ewma1 = intervaldf.ewm(span=window_size) except ImportError:
idf_ewma2 = intervaldf[::-1].ewm(span=window_size) JSONDecodeError = ValueError
i_ewma1 = idf_ewma1.mean().ix[:,'v']
i_ewma2 = idf_ewma2.mean().ix[:,'v']
interval2 = np.vstack((i_ewma1,i_ewma2[::-1]))
interval2 = np.mean( interval2, axis=0) # average
except ValueError:
interval2 = interval
return interval2
from utils import geo_distance
# Custom exception handler, returns a 401 HTTP message from utils import geo_distance,ewmovingaverage
# with exception details in the json data
def custom_exception_handler(exc,message):
response = {
"errors": [
{
"code": str(exc),
"detail": message,
}
]
}
res = HttpResponse(message)
res.status_code = 401
res.json = json.dumps(response)
return res
# Custom error class - to raise a NoTokenError
class StravaNoTokenError(Exception):
def __init__(self,value):
self.value=value
def __str__(self):
return repr(self.value)
# Exchange access code for long-lived access token # Exchange access code for long-lived access token
@@ -106,7 +81,7 @@ def get_token(code):
try: try:
token_json = response.json() token_json = response.json()
thetoken = token_json['access_token'] thetoken = token_json['access_token']
except KeyError: except (KeyError,JSONDecodeError):
thetoken = 0 thetoken = 0
return [thetoken] return [thetoken]
@@ -115,7 +90,7 @@ def get_token(code):
def make_authorization_url(request): def make_authorization_url(request):
# Generate a random string for the state parameter # Generate a random string for the state parameter
# Save it for use later to prevent xsrf attacks # Save it for use later to prevent xsrf attacks
from uuid import uuid4
state = str(uuid4()) state = str(uuid4())
params = {"client_id": STRAVA_CLIENT_ID, params = {"client_id": STRAVA_CLIENT_ID,
@@ -144,6 +119,138 @@ def get_strava_workout_list(user):
return s return s
def add_stroke_data(user,stravaid,workoutid,startdatetime,csvfilename):
r = Rower.objects.get(user=user)
starttimeunix = arrow.get(startdatetime).timestamp
job = myqueue(queue,
handle_strava_import_stroke_data,
r.stravatoken,
stravaid,
workoutid,
starttimeunix,
csvfilename)
# gets all new Strava workouts for a rower
def get_strava_workouts(rower):
if not isprorower(rower):
return 0
res = get_strava_workout_list(rower.user)
if (res.status_code != 200):
return 0
else:
stravaids = [int(item['id']) for item in res.json()]
alldata = {}
for item in res.json():
alldata[item['id']] = item
knownstravaids = uniqify([
w.uploadedtostrava for w in Workout.objects.filter(user=rower)
])
newids = [stravaid for stravaid in stravaids if not stravaid in knownstravaids]
for stravaid in newids:
workoutid = create_async_workout(alldata,rower.user,stravaid)
return 1
def create_async_workout(alldata,user,stravaid):
data = alldata[stravaid]
r = Rower.objects.get(user=user)
distance = data['distance']
stravaid = data['id']
try:
workouttype = data['type']
except:
workouttype = 'rower'
if workouttype not in [x[0] for x in Workout.workouttypes]:
workouttype = 'other'
try:
comments = data['comments']
except:
comments = ' '
try:
thetimezone = tz(data['timezone'])
except:
thetimezone = 'UTC'
try:
rowdatetime = iso8601.parse_date(data['date_utc'])
except KeyError:
rowdatetime = iso8601.parse_date(data['start_date'])
except ParseError:
rowdatetime = iso8601.parse_date(data['date'])
try:
c2intervaltype = data['workout_type']
except KeyError:
c2intervaltype = ''
try:
title = data['name']
except KeyError:
title = ""
try:
t = data['comments'].split('\n', 1)[0]
title += t[:20]
except:
title = 'Imported'
workoutdate = rowdatetime.astimezone(
pytz.timezone(thetimezone)
).strftime('%Y-%m-%d')
starttime = rowdatetime.astimezone(
pytz.timezone(thetimezone)
).strftime('%H:%m:%S')
totaltime = data['elapsed_time']
duration = dataprep.totaltime_sec_to_string(totaltime)
weightcategory = 'hwt'
# Create CSV file name and save data to CSV file
csvfilename ='media/{code}_{importid}.csv'.format(
importid=stravaid,
code = uuid4().hex[:16]
)
w = Workout(
user=r,
workouttype = workouttype,
name = title,
date = workoutdate,
starttime = starttime,
startdatetime = rowdatetime,
timezone = thetimezone,
duration = duration,
distance=distance,
weightcategory = weightcategory,
uploadedtostrava = stravaid,
csvfilename = csvfilename,
notes = ''
)
w.save()
# Check if workout has stroke data, and get the stroke data
result = add_stroke_data(user,stravaid,w.id,rowdatetime,csvfilename)
return w.id
from utils import get_strava_stream
# Get a Strava workout summary data and stroke data by ID # Get a Strava workout summary data and stroke data by ID
def get_strava_workout(user,stravaid): def get_strava_workout(user,stravaid):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
@@ -165,18 +272,12 @@ def get_strava_workout(user,stravaid):
workoutsummary['timezone'] = "Etc/UTC" workoutsummary['timezone'] = "Etc/UTC"
startdatetime = workoutsummary['start_date'] startdatetime = workoutsummary['start_date']
url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/cadence?resolution="+fetchresolution+"&series_type="+series_type spmjson = get_strava_stream(r,'cadence',stravaid)
spmjson = requests.get(url,headers=headers) hrjson = get_strava_stream(r,'heartrate',stravaid)
url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/heartrate?resolution="+fetchresolution+"&series_type="+series_type timejson = get_strava_stream(r,'time',stravaid)
hrjson = requests.get(url,headers=headers) velojson = get_strava_stream(r,'velocity_smooth',stravaid)
url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/time?resolution="+fetchresolution+"&series_type="+series_type distancejson = get_strava_stream(r,'distance',stravaid)
timejson = requests.get(url,headers=headers) latlongjson = get_strava_stream(r,'latlng',stravaid)
url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/velocity_smooth?resolution="+fetchresolution+"&series_type="+series_type
velojson = requests.get(url,headers=headers)
url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/distance?resolution="+fetchresolution+"&series_type="+series_type
distancejson = requests.get(url,headers=headers)
url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/latlng?resolution="+fetchresolution+"&series_type="+series_type
latlongjson = requests.get(url,headers=headers)
try: try:
t = np.array(timejson.json()[0]['data']) t = np.array(timejson.json()[0]['data'])
@@ -335,7 +436,7 @@ def workout_strava_upload(user,w):
res = -1 res = -1
if (r.stravatoken == '') or (r.stravatoken is None): if (r.stravatoken == '') or (r.stravatoken is None):
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
raise StravaNoTokenError("Your hovercraft is full of eels") raise NoTokenError("Your hovercraft is full of eels")
else: else:
if (checkworkoutuser(user,w)): if (checkworkoutuser(user,w)):
try: try:
+135 -2
View File
@@ -8,6 +8,7 @@ import numpy as np
import re import re
from scipy import optimize from scipy import optimize
from scipy.signal import savgol_filter
import rowingdata import rowingdata
@@ -20,6 +21,7 @@ import datetime
import pytz import pytz
import iso8601 import iso8601
from matplotlib.backends.backend_agg import FigureCanvas from matplotlib.backends.backend_agg import FigureCanvas
#from matplotlib.backends.backend_cairo import FigureCanvasCairo as FigureCanvas #from matplotlib.backends.backend_cairo import FigureCanvasCairo as FigureCanvas
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
@@ -37,7 +39,7 @@ from django_rq import job
from django.utils import timezone from django.utils import timezone
from django.utils.html import strip_tags from django.utils.html import strip_tags
from utils import deserialize_list from utils import deserialize_list,ewmovingaverage
from rowers.dataprepnodjango import ( from rowers.dataprepnodjango import (
update_strokedata, new_workout_from_file, update_strokedata, new_workout_from_file,
@@ -45,7 +47,7 @@ from rowers.dataprepnodjango import (
update_agegroup_db,fitnessmetric_to_sql, update_agegroup_db,fitnessmetric_to_sql,
add_c2_stroke_data_db,totaltime_sec_to_string, add_c2_stroke_data_db,totaltime_sec_to_string,
create_c2_stroke_data_db,update_empower, create_c2_stroke_data_db,update_empower,
database_url_debug,database_url, database_url_debug,database_url,dataprep
) )
@@ -65,6 +67,8 @@ import requests
import longtask import longtask
import arrow import arrow
from utils import get_strava_stream
siteurl = SITE_URL siteurl = SITE_URL
# testing task # testing task
@@ -77,6 +81,135 @@ def add(x, y):
return x + y return x + y
@app.task
def handle_strava_import_stroke_data(stravatoken,
stravaid,workoutid,
starttimeunix,
csvfilename,debug=True,**kwargs):
# ready to fetch. Hurray
fetchresolution = 'high'
series_type = 'time'
authorizationstring = str('Bearer ' + stravatoken)
headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json',
'resolution': 'medium',}
url = "https://www.strava.com/api/v3/activities/"+str(stravaid)
workoutsummary = requests.get(url,headers=headers).json()
workoutsummary['timezone'] = "Etc/UTC"
startdatetime = workoutsummary['start_date']
spmjson = get_strava_stream(r,'cadence',stravaid)
hrjson = get_strava_stream(r,'heartrate',stravaid)
timejson = get_strava_stream(r,'time',stravaid)
velojson = get_strava_stream(r,'velocity_smooth',stravaid)
distancejson = get_strava_stream(r,'distance',stravaid)
latlongjson = get_strava_stream(r,'latlng',stravaid)
wattsjson = get_strava_stream(r,'watts',stravaid)
try:
t = np.array(timejson.json()[0]['data'])
nr_rows = len(t)
d = np.array(distancejson.json()[1]['data'])
if nr_rows == 0:
return 0
except IndexError:
d = 0*t
# return (0,"Error: No Distance information in the Strava data")
except KeyError:
return 0
try:
spm = np.array(spmjson.json()[1]['data'])
except:
spm = np.zeros(nr_rows)
try:
watts = np.array(wattsjson.json()[1]['data'])
except:
watts = np.zeros(nr_rows)
try:
hr = np.array(hrjson.json()[1]['data'])
except IndexError:
hr = np.zeros(nr_rows)
except KeyError:
hr = np.zeros(nr_rows)
try:
velo = np.array(velojson.json()[1]['data'])
except IndexError:
velo = np.zeros(nr_rows)
except KeyError:
velo = np.zeros(nr_rows)
f = np.diff(t).mean()
if f != 0:
windowsize = 2*(int(10./(f)))+1
else:
windowsize = 1
if windowsize > 3 and windowsize < len(velo):
velo2 = savgol_filter(velo,windowsize,3)
else:
velo2 = velo
coords = np.array(latlongjson.json()[0]['data'])
try:
lat = coords[:,0]
lon = coords[:,1]
except IndexError:
lat = np.zeros(len(t))
lon = np.zeros(len(t))
except KeyError:
lat = np.zeros(len(t))
lon = np.zeros(len(t))
strokelength = velo*60./(spm)
strokelength[np.isinf(strokelength)] = 0.0
pace = 500./(1.0*velo2)
pace[np.isinf(pace)] = 0.0
unixtime = starttimeunix+t
strokedistance = 60.*velo2/spm
nr_strokes = len(t)
df = pd.DataFrame({'TimeStamp (sec)':unixtime,
' ElapsedTime (sec)':t,
' Horizontal (meters)':d,
' Stroke500mPace (sec/500m)':pace,
' Cadence (stokes/min)':spm,
' HRCur (bpm)':hr,
' latitude':lat,
' longitude':lon,
' StrokeDistance (meters)':strokelength,
'cum_dist':d,
' DragFactor':np.zeros(nr_strokes),
' DriveLength (meters)':np.zeros(nr_strokes),
' StrokeDistance (meters)':strokedistance,
' DriveTime (ms)':np.zeros(nr_strokes),
' StrokeRecoveryTime (ms)':np.zeros(nr_strokes),
' AverageDriveForce (lbs)':np.zeros(nr_strokes),
' PeakDriveForce (lbs)':np.zeros(nr_strokes),
' lapIdx':np.zeros(nr_strokes),
' Power (watts)':watts,
})
df.sort_values(by='TimeStamp (sec)',ascending=True)
res = df.to_csv(csvfilename+'.gz',index_label='index',compression='gzip')
data = dataprep(df,id=workoutid,bands=False,debug=debug)
# startdatetime = datetime.datetime.strptime(startdatetime,"%Y-%m-%d-%H:%M:%S")
return 1
@app.task @app.task
def handle_c2_import_stroke_data(c2token, def handle_c2_import_stroke_data(c2token,
c2id,workoutid, c2id,workoutid,
+3 -3
View File
@@ -20,7 +20,7 @@ from time import strftime,strptime,mktime,time,daylight
import os import os
from rowers.tasks import handle_makeplot from rowers.tasks import handle_makeplot
from rowers.utils import serialize_list,deserialize_list from rowers.utils import serialize_list,deserialize_list
from rowers.c2stuff import C2NoTokenError from rowers.utils import NoTokenError
from shutil import copyfile from shutil import copyfile
from minimocktest import MockTestCase from minimocktest import MockTestCase
@@ -424,8 +424,8 @@ class C2Tests(TestCase):
def c2_notokentest(self): def c2_notokentest(self):
thetoken = c2_open(self.u) thetoken = c2_open(self.u)
# should raise C2NoTokenError # should raise NoTokenError
self.assertRaises(C2NoTokenError) self.assertRaises(NoTokenError)
class DataTest(TestCase): class DataTest(TestCase):
def setUp(self): def setUp(self):
+3 -49
View File
@@ -52,63 +52,17 @@ from async_messages import message_user,messages
# Custom error class - to raise a NoTokenError
class TPNoTokenError(Exception):
def __init__(self,value):
self.value=value
def __str__(self): from utils import geo_distance, NoTokenError,ewmovingaverage, custom_exception_handler
return repr(self.value)
# Exponentially weighted moving average
# Used for data smoothing of the jagged data obtained by Strava
# See bitbucket issue 72
def ewmovingaverage(interval,window_size):
# Experimental code using Exponential Weighted moving average
try:
intervaldf = pd.DataFrame({'v':interval})
idf_ewma1 = intervaldf.ewm(span=window_size)
idf_ewma2 = intervaldf[::-1].ewm(span=window_size)
i_ewma1 = idf_ewma1.mean().ix[:,'v']
i_ewma2 = idf_ewma2.mean().ix[:,'v']
interval2 = np.vstack((i_ewma1,i_ewma2[::-1]))
interval2 = np.mean( interval2, axis=0) # average
except ValueError:
interval2 = interval
return interval2
from utils import geo_distance
# Custom exception handler, returns a 401 HTTP message
# with exception details in the json data
def custom_exception_handler(exc,message):
response = {
"errors": [
{
"code": str(exc),
"detail": message,
}
]
}
res = HttpResponse(message)
res.status_code = 401
res.json = json.dumps(response)
return res
# Checks if user has UnderArmour token, renews them if they are expired # Checks if user has UnderArmour token, renews them if they are expired
def tp_open(user): def tp_open(user):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
if (r.tptoken == '') or (r.tptoken is None): if (r.tptoken == '') or (r.tptoken is None):
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
raise TPNoTokenError("User has no token") raise NoTokenError("User has no token")
else: else:
if (timezone.now()>r.tptokenexpirydate): if (timezone.now()>r.tptokenexpirydate):
res = do_refresh_token(r.tprefreshtoken) res = do_refresh_token(r.tprefreshtoken)
@@ -120,7 +74,7 @@ def tp_open(user):
r.save() r.save()
thetoken = r.tptoken thetoken = r.tptoken
else: else:
raise TPNoTokenError("Refresh token invalid") raise NoTokenError("Refresh token invalid")
else: else:
thetoken = r.tptoken thetoken = r.tptoken
+1 -1
View File
@@ -20,7 +20,7 @@ from time import strftime,strptime,mktime,time,daylight
import os import os
from rowers.tasks import handle_makeplot from rowers.tasks import handle_makeplot
from rowers.utils import serialize_list,deserialize_list from rowers.utils import serialize_list,deserialize_list
from rowers.c2stuff import C2NoTokenError
from shutil import copyfile from shutil import copyfile
from minimocktest import MockTestCase from minimocktest import MockTestCase
+3 -48
View File
@@ -39,63 +39,18 @@ from rowsandall_app.settings import (
UNDERARMOUR_REDIRECT_URI,UNDERARMOUR_CLIENT_KEY, UNDERARMOUR_REDIRECT_URI,UNDERARMOUR_CLIENT_KEY,
) )
# Custom error class - to raise a NoTokenError from utils import NoTokenError,ewmovingaverage
class UnderArmourNoTokenError(Exception):
def __init__(self,value):
self.value=value
def __str__(self): from utils import geo_distance, custom_exception_handler
return repr(self.value)
# Exponentially weighted moving average
# Used for data smoothing of the jagged data obtained by Strava
# See bitbucket issue 72
def ewmovingaverage(interval,window_size):
# Experimental code using Exponential Weighted moving average
try:
intervaldf = pd.DataFrame({'v':interval})
idf_ewma1 = intervaldf.ewm(span=window_size)
idf_ewma2 = intervaldf[::-1].ewm(span=window_size)
i_ewma1 = idf_ewma1.mean().ix[:,'v']
i_ewma2 = idf_ewma2.mean().ix[:,'v']
interval2 = np.vstack((i_ewma1,i_ewma2[::-1]))
interval2 = np.mean( interval2, axis=0) # average
except ValueError:
interval2 = interval
return interval2
from utils import geo_distance
# Custom exception handler, returns a 401 HTTP message
# with exception details in the json data
def custom_exception_handler(exc,message):
response = {
"errors": [
{
"code": str(exc),
"detail": message,
}
]
}
res = HttpResponse(message)
res.status_code = 401
res.json = json.dumps(response)
return res
# Checks if user has UnderArmour token, renews them if they are expired # Checks if user has UnderArmour token, renews them if they are expired
def underarmour_open(user): def underarmour_open(user):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
if (r.underarmourtoken == '') or (r.underarmourtoken is None): if (r.underarmourtoken == '') or (r.underarmourtoken is None):
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
raise UnderArmourNoTokenError("User has no token") raise NoTokenError("User has no token")
else: else:
if (timezone.now()>r.underarmourtokenexpirydate): if (timezone.now()>r.underarmourtokenexpirydate):
res = do_refresh_token( res = do_refresh_token(
+7 -7
View File
@@ -36,7 +36,7 @@ except:
from rowers.utils import ( from rowers.utils import (
geo_distance,serialize_list,deserialize_list,uniqify, geo_distance,serialize_list,deserialize_list,uniqify,
str2bool,range_to_color_hex,absolute,myqueue str2bool,range_to_color_hex,absolute,myqueue,NoTokenError
) )
def cleanbody(body): def cleanbody(body):
@@ -379,7 +379,7 @@ def do_sync(w,options):
if ('upload_to_C2' in options and options['upload_to_C2']) or (w.user.c2_auto_export and isprorower(w.user)): if ('upload_to_C2' in options and options['upload_to_C2']) or (w.user.c2_auto_export and isprorower(w.user)):
try: try:
message,id = c2stuff.workout_c2_upload(w.user.user,w) message,id = c2stuff.workout_c2_upload(w.user.user,w)
except c2stuff.C2NoTokenError: except NoTokenError:
id = 0 id = 0
message = "Something went wrong with the Concept2 sync" message = "Something went wrong with the Concept2 sync"
@@ -388,7 +388,7 @@ def do_sync(w,options):
message,id = stravastuff.workout_strava_upload( message,id = stravastuff.workout_strava_upload(
w.user.user,w w.user.user,w
) )
except stravastuff.StravaNoTokenError: except NoTokenError:
id = 0 id = 0
message = "Please connect to Strava first" message = "Please connect to Strava first"
@@ -398,7 +398,7 @@ def do_sync(w,options):
message,id = sporttracksstuff.workout_sporttracks_upload( message,id = sporttracksstuff.workout_sporttracks_upload(
w.user.user,w w.user.user,w
) )
except sporttracksstuff.SportTracksNoTokenError: except NoTokenError:
message = "Please connect to SportTracks first" message = "Please connect to SportTracks first"
id = 0 id = 0
@@ -408,7 +408,7 @@ def do_sync(w,options):
message,id = runkeeperstuff.workout_runkeeper_upload( message,id = runkeeperstuff.workout_runkeeper_upload(
w.user.user,w w.user.user,w
) )
except runkeeperstuff.RunKeeperNoTokenError: except NoTokenError:
message = "Please connect to Runkeeper first" message = "Please connect to Runkeeper first"
id = 0 id = 0
@@ -417,7 +417,7 @@ def do_sync(w,options):
message,id = underarmourstuff.workout_ua_upload( message,id = underarmourstuff.workout_ua_upload(
w.user.user,w w.user.user,w
) )
except underarmourstuff.UnderArmourNoTokenError: except NoTokenError:
message = "Please connect to MapMyFitness first" message = "Please connect to MapMyFitness first"
id = 0 id = 0
@@ -427,7 +427,7 @@ def do_sync(w,options):
message,id = tpstuff.workout_tp_upload( message,id = tpstuff.workout_tp_upload(
w.user.user,w w.user.user,w
) )
except tpstuff.TPNoTokenError: except NoTokenError:
message = "Please connect to TrainingPeaks first" message = "Please connect to TrainingPeaks first"
id = 0 id = 0
+65
View File
@@ -7,6 +7,7 @@ from django.conf import settings
import uuid import uuid
import datetime import datetime
import requests
lbstoN = 4.44822 lbstoN = 4.44822
@@ -378,3 +379,67 @@ def isprorower(r):
return result return result
# Exponentially weighted moving average
# Used for data smoothing of the jagged data obtained by Strava
# See bitbucket issue 72
def ewmovingaverage(interval,window_size):
# Experimental code using Exponential Weighted moving average
try:
intervaldf = pd.DataFrame({'v':interval})
idf_ewma1 = intervaldf.ewm(span=window_size)
idf_ewma2 = intervaldf[::-1].ewm(span=window_size)
i_ewma1 = idf_ewma1.mean().ix[:,'v']
i_ewma2 = idf_ewma2.mean().ix[:,'v']
interval2 = np.vstack((i_ewma1,i_ewma2[::-1]))
interval2 = np.mean( interval2, axis=0) # average
except ValueError:
interval2 = interval
return interval2
# Exceptions
# Custom error class - to raise a NoTokenError
class NoTokenError(Exception):
def __init__(self,value):
self.value=value
def __str__(self):
return repr(self.value)
# Custom exception handler, returns a 401 HTTP message
# with exception details in the json data
def custom_exception_handler(exc,message):
response = {
"errors": [
{
"code": str(exc),
"detail": message,
}
]
}
res = HttpResponse(message)
res.status_code = 401
res.json = json.dumps(response)
return res
def get_strava_stream(r,metric,stravaid,series_type='time',fetchresolution='high'):
authorizationstring = str('Bearer ' + r.stravatoken)
headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json',
'resolution': 'medium',}
url = "https://www.strava.com/api/v3/activities/{stravaid}/streams/{metric}?resolution={fetchresolutions}&series_type={series_type}".format(
stravaid=stravid,
fetchresolution=fetchresolution,
series_type=series_type,
metric=metric
)
return requests.get(url,headers=headers)
+28 -27
View File
@@ -90,18 +90,16 @@ import os,sys
import datetime import datetime
import iso8601 import iso8601
import c2stuff import c2stuff
from c2stuff import C2NoTokenError,c2_open from c2stuff import c2_open
from runkeeperstuff import RunKeeperNoTokenError,runkeeper_open from runkeeperstuff import runkeeper_open
from sporttracksstuff import SportTracksNoTokenError,sporttracks_open from sporttracksstuff import sporttracks_open
from tpstuff import TPNoTokenError,tp_open from tpstuff import tp_open
from iso8601 import ParseError from iso8601 import ParseError
import stravastuff import stravastuff
import polarstuff import polarstuff
from polarstuff import PolarNoTokenError
from stravastuff import StravaNoTokenError
import sporttracksstuff import sporttracksstuff
import underarmourstuff import underarmourstuff
from underarmourstuff import UnderArmourNoTokenError,underarmour_open from underarmourstuff import underarmour_open
import tpstuff import tpstuff
import runkeeperstuff import runkeeperstuff
import ownapistuff import ownapistuff
@@ -887,7 +885,7 @@ from utils import (
geo_distance,serialize_list,deserialize_list,uniqify, geo_distance,serialize_list,deserialize_list,uniqify,
str2bool,range_to_color_hex,absolute,myqueue,get_call, str2bool,range_to_color_hex,absolute,myqueue,get_call,
calculate_age,rankingdistances,rankingdurations, calculate_age,rankingdistances,rankingdurations,
is_ranking_piece,my_dict_from_instance,wavg is_ranking_piece,my_dict_from_instance,wavg,NoTokenError
) )
import datautils import datautils
@@ -1409,7 +1407,10 @@ def add_workout_from_runkeeperdata(user,importid,data):
unixtime = cum_time+starttimeunix unixtime = cum_time+starttimeunix
try:
unixtime[0] = starttimeunix unixtime[0] = starttimeunix
except IndexError:
return (0,'No data to import')
df['TimeStamp (sec)'] = unixtime df['TimeStamp (sec)'] = unixtime
@@ -2062,7 +2063,7 @@ def workout_tp_upload_view(request,id=0):
res = -1 res = -1
try: try:
thetoken = tp_open(r.user) thetoken = tp_open(r.user)
except TPNoTokenError: except NoTokenError:
return HttpResponseRedirect("/rowers/me/tpauthorize/") return HttpResponseRedirect("/rowers/me/tpauthorize/")
# ready to upload. Hurray # ready to upload. Hurray
@@ -2213,7 +2214,7 @@ def workout_c2_upload_view(request,id=0):
try: try:
message,c2id = c2stuff.workout_c2_upload(request.user,w) message,c2id = c2stuff.workout_c2_upload(request.user,w)
except C2NoTokenError: except NoTokenError:
return HttpResponseRedirect("/rowers/me/c2authorize/") return HttpResponseRedirect("/rowers/me/c2authorize/")
if message and c2id <=0: if message and c2id <=0:
@@ -2238,7 +2239,7 @@ def workout_runkeeper_upload_view(request,id=0):
try: try:
thetoken = runkeeper_open(r.user) thetoken = runkeeper_open(r.user)
except RunKeeperNoTokenError: except NoTokenError:
return HttpResponseRedirect("/rowers/me/runkeeperauthorize/") return HttpResponseRedirect("/rowers/me/runkeeperauthorize/")
# ready to upload. Hurray # ready to upload. Hurray
@@ -2300,7 +2301,7 @@ def workout_underarmour_upload_view(request,id=0):
try: try:
thetoken = underarmour_open(r.user) thetoken = underarmour_open(r.user)
except UnderArmourNoTokenError: except NoTokenError:
return HttpResponseRedirect("/rowers/me/underarmourauthorize/") return HttpResponseRedirect("/rowers/me/underarmourauthorize/")
# ready to upload. Hurray # ready to upload. Hurray
@@ -2364,7 +2365,7 @@ def workout_sporttracks_upload_view(request,id=0):
try: try:
thetoken = sporttracks_open(r.user) thetoken = sporttracks_open(r.user)
except SportTracksNoTokenError: except NoTokenError:
return HttpResponseRedirect("/rowers/me/sporttracksauthorize/") return HttpResponseRedirect("/rowers/me/sporttracksauthorize/")
@@ -8947,7 +8948,7 @@ def workout_export_view(request,id=0, message="", successmessage=""):
try: try:
thetoken = c2_open(request.user) thetoken = c2_open(request.user)
except C2NoTokenError: except NoTokenError:
thetoken = 0 thetoken = 0
if (checkworkoutuser(request.user,row)) and thetoken: if (checkworkoutuser(request.user,row)) and thetoken:
@@ -8957,7 +8958,7 @@ def workout_export_view(request,id=0, message="", successmessage=""):
try: try:
rktoken = runkeeper_open(request.user) rktoken = runkeeper_open(request.user)
except RunKeeperNoTokenError: except NoTokenError:
rktoken = 0 rktoken = 0
if (checkworkoutuser(request.user,row)) and rktoken: if (checkworkoutuser(request.user,row)) and rktoken:
@@ -9937,7 +9938,7 @@ def workout_sporttracksimport_view(request,message=""):
def c2listdebug_view(request,page=1,message=""): def c2listdebug_view(request,page=1,message=""):
try: try:
thetoken = c2_open(request.user) thetoken = c2_open(request.user)
except C2NoTokenError: except NoTokenError:
return HttpResponseRedirect("/rowers/me/c2authorize/") return HttpResponseRedirect("/rowers/me/c2authorize/")
r = getrower(request.user) r = getrower(request.user)
@@ -9980,7 +9981,7 @@ def c2listdebug_view(request,page=1,message=""):
def workout_getc2workout_all(request,page=1,message=""): def workout_getc2workout_all(request,page=1,message=""):
try: try:
thetoken = c2_open(request.user) thetoken = c2_open(request.user)
except C2NoTokenError: except NoTokenError:
return HttpResponseRedirect("/rowers/me/c2authorize/") return HttpResponseRedirect("/rowers/me/c2authorize/")
res = c2stuff.get_c2_workout_list(request.user,page=page) res = c2stuff.get_c2_workout_list(request.user,page=page)
@@ -10013,7 +10014,7 @@ def workout_getc2workout_all(request,page=1,message=""):
def workout_c2import_view(request,page=1,message=""): def workout_c2import_view(request,page=1,message=""):
try: try:
thetoken = c2_open(request.user) thetoken = c2_open(request.user)
except C2NoTokenError: except NoTokenError:
return HttpResponseRedirect("/rowers/me/c2authorize/") return HttpResponseRedirect("/rowers/me/c2authorize/")
res = c2stuff.get_c2_workout_list(request.user,page=page) res = c2stuff.get_c2_workout_list(request.user,page=page)
@@ -10239,7 +10240,7 @@ def workout_getstravaworkout_all(request):
def workout_getc2workout_view(request,c2id): def workout_getc2workout_view(request,c2id):
try: try:
thetoken = c2_open(request.user) thetoken = c2_open(request.user)
except C2NoTokenError: except NoTokenError:
return HttpResponseRedirect("/rowers/me/c2authorize/") return HttpResponseRedirect("/rowers/me/c2authorize/")
res = c2stuff.get_c2_workout(request.user,c2id) res = c2stuff.get_c2_workout(request.user,c2id)
@@ -10640,7 +10641,7 @@ def workout_upload_view(request,
if (upload_to_c2) or (w.user.c2_auto_export and isprorower(w.user)): if (upload_to_c2) or (w.user.c2_auto_export and isprorower(w.user)):
try: try:
message,id = c2stuff.workout_c2_upload(request.user,w) message,id = c2stuff.workout_c2_upload(request.user,w)
except C2NoTokenError: except NoTokenError:
id = 0 id = 0
message = "Something went wrong with the Concept2 sync" message = "Something went wrong with the Concept2 sync"
if id>1: if id>1:
@@ -10653,7 +10654,7 @@ def workout_upload_view(request,
message,id = stravastuff.workout_strava_upload( message,id = stravastuff.workout_strava_upload(
request.user,w request.user,w
) )
except StravaNoTokenError: except NoTokenError:
id = 0 id = 0
message = "Please connect to Strava first" message = "Please connect to Strava first"
if id>1: if id>1:
@@ -10666,7 +10667,7 @@ def workout_upload_view(request,
message,id = sporttracksstuff.workout_sporttracks_upload( message,id = sporttracksstuff.workout_sporttracks_upload(
request.user,w request.user,w
) )
except SportTracksNoTokenError: except NoTokenError:
message = "Please connect to SportTracks first" message = "Please connect to SportTracks first"
id = 0 id = 0
if id>1: if id>1:
@@ -10679,7 +10680,7 @@ def workout_upload_view(request,
message,id = runkeeperstuff.workout_runkeeper_upload( message,id = runkeeperstuff.workout_runkeeper_upload(
request.user,w request.user,w
) )
except RunKeeperNoTokenError: except NoTokenError:
message = "Please connect to Runkeeper first" message = "Please connect to Runkeeper first"
id = 0 id = 0
@@ -10694,7 +10695,7 @@ def workout_upload_view(request,
message,id = underarmourstuff.workout_ua_upload( message,id = underarmourstuff.workout_ua_upload(
request.user,w request.user,w
) )
except UnderArmourNoTokenError: except NoTokenError:
message = "Please connect to MapMyFitness first" message = "Please connect to MapMyFitness first"
id = 0 id = 0
@@ -10709,7 +10710,7 @@ def workout_upload_view(request,
message,id = tpstuff.workout_tp_upload( message,id = tpstuff.workout_tp_upload(
request.user,w request.user,w
) )
except TPNoTokenError: except NoTokenError:
message = "Please connect to TrainingPeaks first" message = "Please connect to TrainingPeaks first"
id = 0 id = 0
@@ -11138,7 +11139,7 @@ def workout_split_view(request,id=id):
r,row,splitsecond,splitmode r,row,splitsecond,splitmode
) )
except IndexError: except IndexError:
messages.error("Something went wrong in Split") messages.error(request,"Something went wrong in Split")
for message in mesgs: for message in mesgs:
messages.info(request,message) messages.info(request,message)
@@ -11301,7 +11302,7 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
try: try:
avpace = datetime.timedelta(seconds=int(500./normv)) avpace = datetime.timedelta(seconds=int(500./normv))
except: except (OverflowError, ZeroDivisionError):
avpace = datetime.timedelta(seconds=130) avpace = datetime.timedelta(seconds=130)
data = { data = {