Private
Public Access
1
0

Merge branch 'release/v16.2.0'

This commit is contained in:
Sander Roosendaal
2021-05-12 09:29:09 +02:00
20 changed files with 305 additions and 283 deletions
+83
View File
@@ -0,0 +1,83 @@
from requests_oauthlib import OAuth1Session, OAuth1
import hmac
import hashlib
import requests
from urllib.parse import quote_plus as rawurlencode
import logging
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
payload = {
'workoutName': '30min at threshold',
'sport': 'GENERIC',
'description': 'Uploaded from Rowsandall.com',
'estimatedDurationInSecs': 1800,
'estimatedDistanceInMeters': 6936,
'workoutProvider': 'Rowsandall.com',
'workoutSourceId': 'Rowsandall.com',
'steps': [
{
'type': 'Step', 'stepOrder': 0,
'repeatType': 'Step',
'repeatValue': 1,
'intensity': 'Active',
'description': '0',
'durationType': 'TIME',
'durationValue': 1800,
'durationValueType': '',
'targetType': 'Power',
'targetValue': 1226,
'targetValueLow': 0, 'targetValueHigh': (0,)
}
]
}
payload = {}
oauth_consumer_key = b'ca29ba5e-6868-4468-987d-4ee60a1f04bf'
oauth_consumer_secret = b'SKRqjML9mOBV7BcPpN7LsbuDNDtvLOvRiyo'
oauth_token = b'79454eab-bf82-4329-9de2-82a6bd911498'
oauth_token_secret = b'DihdHJ2ThEdbsyoStpPTEmYh5F52L697HhD'
authheaders = OAuth1(client_key=oauth_consumer_key,
client_secret=oauth_consumer_secret,
resource_owner_key=oauth_token,
resource_owner_secret=oauth_token_secret,
signature_method='HMAC-SHA1',
#encoding='base64'
)
url = 'https://apis.garmin.com/training-api/workout/'
response = requests.post(url,payload,auth=authheaders)
# build base_string
base_string1 = b'POST&https%3A%2F%2Fapis.garmin.com%2Ftraining-api%2Fworkout%2F&description%3DUploaded%2520from%2520Rowsandall.com%26estimatedDistanceInMeters%3D6936%26estimatedDurationInSecs%3D1800%26oauth_consumer_key%3Dca29ba5e-6868-4468-987d-4ee60a1f04bf%26oauth_nonce%3D163208869057942765101620205416%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1620205416%26oauth_token%3D79454eab-bf82-4329-9de2-82a6bd911498%26oauth_version%3D1.0%26sport%3DGENERIC%26steps%3Ddescription%26steps%3DdurationType%26steps%3DdurationValue%26steps%3DdurationValueType%26steps%3Dintensity%26steps%3DrepeatType%26steps%3DrepeatValue%26steps%3DstepOrder%26steps%3DtargetType%26steps%3DtargetValue%26steps%3DtargetValueHigh%26steps%3DtargetValueLow%26steps%3Dtype%26workoutName%3D30min%2520at%2520threshold%26workoutProvider%3DRowsandall.com%26workoutSourceId%3DRowsandall.com'
base_stringa = b'POST&https%3A%2F%2Fapis.garmin.com%2Ftraining-api%2Fworkout%2F&oauth_consumer_key%3Dca29ba5e-6868-4468-987d-4ee60a1f04bf%26oauth_nonce%3D90559685229655402871620208938%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1620208938%26oauth_token%3D79454eab-bf82-4329-9de2-82a6bd911498%26oauth_version%3D1.0'
base_string2 = b'POST&https%3A%2F%2Fapis.garmin.com%2Ftraining-api%2Fworkout%2F&oauth_consumer_key%3Dca29ba5e-6868-4468-987d-4ee60a1f04bf%26oauth_nonce%3DIyk9Ambokd2%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1620138340%26oauth_token%3D673806b7-aa7b-4064-8290-2dd1b0236ae6%26oauth_version%3D1.0'
base_string3 = b'POST&https%3A%2F%2Fapis.garmin.com%2Ftraining-api%2Fworkout&oauth_consumer_key%3Dca29ba5e-6868-4468-987d-4ee60a1f04bf%26oauth_nonce%3D90559685229655402871620208938%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1620208938%26oauth_token%3D79454eab-bf82-4329-9de2-82a6bd911498%26oauth_version%3D1.0'
auth = b'&'.join([oauth_consumer_secret,oauth_token_secret])
signingKey = bytes(rawurlencode(oauth_consumer_secret) + "&" + rawurlencode(oauth_token_secret),'utf-8')
print(auth)
print(signingKey)
print(base_string3)
sig = hmac.new(signingKey, base_string3, hashlib.sha1)
print(response.status_code)
print(response.text)
print(sig.hexdigest().encode())
+21 -13
View File
@@ -238,8 +238,8 @@ def create_async_workout(alldata,user,c2id):
duration = dataprep.totaltime_sec_to_string(totaltime) duration = dataprep.totaltime_sec_to_string(totaltime)
try: try:
timezone_str = tz(data['timezone']) timezone_str = data['timezone']
except: except: # pragma: no cover
timezone_str = 'UTC' timezone_str = 'UTC'
workoutdate = startdatetime.astimezone( workoutdate = startdatetime.astimezone(
@@ -848,6 +848,9 @@ def get_workout(user,c2id,do_async=False):
data = s.json()['data'] data = s.json()['data']
splitdata = None splitdata = None
#with open('c2temp.json','w') as f:
# f.write(json.dumps(s.json()))
# print(s.json())
if 'workout' in data: if 'workout' in data:
if 'splits' in data['workout']: # pragma: no cover if 'splits' in data['workout']: # pragma: no cover
@@ -1076,20 +1079,21 @@ def add_workout_from_data(user,importid,data,strokedata,
except: # pragma: no cover except: # pragma: no cover
comments = ' ' comments = ' '
try:
thetimezone = tz(data['timezone']) thetimezone = pytz.timezone(data['timezone'])
except:
thetimezone = 'UTC'
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
try: try:
rowdatetime = iso8601.parse_date(data['date_utc']) rowdatetime = iso8601.parse_date(data['date_utc'])
thetimezone = 'UTC'
except KeyError: # pragma: no cover except KeyError: # pragma: no cover
rowdatetime = iso8601.parse_date(data['start_date']) rowdatetime = iso8601.parse_date(data['start_date'])
rowdatetime = rowdatetime.make_aware(thetimezone)
except ParseError: # pragma: no cover except ParseError: # pragma: no cover
rowdatetime = iso8601.parse_date(data['date']) rowdatetime = iso8601.parse_date(data['date'])
rowdatetime = rowdatetime.make_aware(thetimezone)
try: try:
@@ -1119,7 +1123,9 @@ def add_workout_from_data(user,importid,data,strokedata,
cum_time = res[0] cum_time = res[0]
lapidx = res[1] lapidx = res[1]
starttimeunix = starttimeunix - cum_time.max() totaltime = data['time']/10.
starttimeunix = starttimeunix - totaltime
unixtime = cum_time+starttimeunix unixtime = cum_time+starttimeunix
# unixtime[0] = starttimeunix # unixtime[0] = starttimeunix
@@ -1161,7 +1167,6 @@ def add_workout_from_data(user,importid,data,strokedata,
velo = 1000./pace velo = 1000./pace
pace = 500./velo pace = 500./velo
# save csv # save csv
# Create data frame with all necessary data to write to csv # Create data frame with all necessary data to write to csv
df = pd.DataFrame({'TimeStamp (sec)':unixtime, df = pd.DataFrame({'TimeStamp (sec)':unixtime,
@@ -1223,15 +1228,18 @@ def add_workout_from_data(user,importid,data,strokedata,
w = Workout.objects.get(id=id) w = Workout.objects.get(id=id)
local_tz = pytz.timezone(data['timezone'])
# local_tz = pytz.timezone(thetimezone)
w.startdatetime = w.startdatetime.astimezone(local_tz)
w.starttime = w.startdatetime.strftime('%H:%M:%S')
w.timezone = local_tz
w.duration = dataprep.totaltime_sec_to_string(totaltime) w.duration = dataprep.totaltime_sec_to_string(totaltime)
w.distance = totaldist w.distance = totaldist
w.startdatetime = rowdatetime
w.starttime = rowdatetime.time()
w.date = rowdatetime.date()
w.save() w.save()
return id,message return id,message
+15 -1
View File
@@ -1692,6 +1692,7 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
try: try:
latavg = row.df[' latitude'].mean() latavg = row.df[' latitude'].mean()
lonavg = row.df[' longitude'].mean() lonavg = row.df[' longitude'].mean()
@@ -1717,6 +1718,7 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
except KeyError: except KeyError:
timezone_str = r.defaulttimezone timezone_str = r.defaulttimezone
duration = totaltime_sec_to_string(totaltime) duration = totaltime_sec_to_string(totaltime)
workoutdate = workoutstartdatetime.astimezone( workoutdate = workoutstartdatetime.astimezone(
@@ -1785,6 +1787,8 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
except ValidationError: except ValidationError:
return (0,'Unable to create your workout') return (0,'Unable to create your workout')
if privacy == 'visible': if privacy == 'visible':
ts = Team.objects.filter(rower=r) ts = Team.objects.filter(rower=r)
for t in ts: for t in ts:
@@ -2013,6 +2017,16 @@ def new_workout_from_file(r, f2,
return -1, message, f2 return -1, message, f2
# Some people try to upload Concept2 logbook summaries # Some people try to upload Concept2 logbook summaries
if fileformat == 'imageformat': # pragma: no cover
os.remove(f2)
message = "You cannot upload image files here"
return (0, message, f2)
if fileformat == 'json': # pragma: no cover
os.remove(f2)
message = "JSON format not supported in direct upload"
return (0, message, f2)
if fileformat == 'c2log': if fileformat == 'c2log':
os.remove(f2) os.remove(f2)
message = "This summary does not contain stroke data. Use the files containing stroke by stroke data." message = "This summary does not contain stroke data. Use the files containing stroke by stroke data."
@@ -2435,7 +2449,7 @@ def getsmallrowdata_db(columns, ids=[], doclean=True,workstrokesonly=True,comput
else: else:
try: try:
df = pd.read_parquet(csvfilenames[0],columns=columns) df = pd.read_parquet(csvfilenames[0],columns=columns)
except (OSError,ArrowInvalid): except (OSError,ArrowInvalid,IndexError):
rowdata,row = getrowdata(id=ids[0]) rowdata,row = getrowdata(id=ids[0])
if rowdata and len(rowdata.df): # pragma: no cover if rowdata and len(rowdata.df): # pragma: no cover
data = dataprep(rowdata.df,id=ids[0],bands=True,otwpower=True,barchart=True) data = dataprep(rowdata.df,id=ids[0],bands=True,otwpower=True,barchart=True)
-26
View File
@@ -994,32 +994,6 @@ class PowerIntervalUpdateForm(forms.Form):
activeminutesmin = forms.IntegerField(required=False,initial=0,widget=forms.HiddenInput()) activeminutesmin = forms.IntegerField(required=False,initial=0,widget=forms.HiddenInput())
activeminutesmax = forms.IntegerField(required=False,initial=0,widget=forms.HiddenInput()) activeminutesmax = forms.IntegerField(required=False,initial=0,widget=forms.HiddenInput())
# Form used to update interval stats
class IntervalUpdateForm(forms.Form):
def __init__(self, *args, **kwargs):
typechoices = (
(1,'single time'),
(2,'single distance'),
(3,'rest (time based)'),
(3,'rest (distance based)'),
(4,'work (time based)'),
(5,'work (distance based)'),
)
aantal = int(kwargs.pop('aantal'))
super(IntervalUpdateForm, self).__init__(*args, **kwargs)
for i in range(aantal):
self.fields['intervalt_%s' % i] = forms.DurationField(label='Time '+str(i+1))
self.fields['intervald_%s' % i] = forms.IntegerField(label='Distance '+str(i+1))
self.fields['type_%s' % i] = forms.ChoiceField(choices=typechoices,
required=True,
initial=4,
label = 'Type '+str(i+1))
self.fields['intervalt_%s' % i].widget.attrs['style'] = 'width:76px; height: 16px;'
self.fields['intervald_%s' % i].widget.attrs['style'] = 'width:76px; height: 16px;'
self.fields['type_%s' % i].widget.attrs['style'] = 'width:156px; height: 22px;'
self.fields['intervald_%s' % i].widget = forms.TimeInput(format='%H:%M:%S.%f')
boattypes = mytypes.boattypes boattypes = mytypes.boattypes
workouttypes = mytypes.workouttypes workouttypes = mytypes.workouttypes
+4 -13
View File
@@ -2,6 +2,7 @@ from rowers.imports import *
import datetime import datetime
import requests import requests
from requests import Session, Request
from requests_oauthlib import OAuth1,OAuth1Session from requests_oauthlib import OAuth1,OAuth1Session
from requests_oauthlib.oauth1_session import TokenRequestDenied from requests_oauthlib.oauth1_session import TokenRequestDenied
from requests import Request, Session from requests import Request, Session
@@ -302,24 +303,14 @@ def ps_to_garmin(ps,r):
lijst.append(gstep) lijst.append(gstep)
payload['steps'] = lijst payload['steps'] = lijst
url = 'https://apis.garmin.com/training-api/workout/'
garmin = OAuth1Session(oauth_data['client_id'], garmin = OAuth1Session(oauth_data['client_id'],
client_secret=oauth_data['client_secret'], client_secret=oauth_data['client_secret'],
resource_owner_key=r.garmintoken, resource_owner_key=r.garmintoken,
resource_owner_secret=r.garminrefreshtoken, resource_owner_secret=r.garminrefreshtoken,
signature_method='HMAC-SHA1' signature_method='HMAC-SHA1',
) encoding='base64'
url = 'https://apis.garmin.com/training-api/workout/'
garminauth = OAuth1(
client_key=oauth_data['client_id'],
client_secret=oauth_data['client_secret'],
resource_owner_key=r.garmintoken,
resource_owner_secret=r.garminrefreshtoken,
signature_method='HMAC-SHA1'
) )
+3 -1
View File
@@ -571,7 +571,6 @@ def interactive_activitychart(workouts,startdate,enddate,stack='type',toolbar_lo
while d<=enddate: while d<=enddate:
dd = d.strftime('%d') dd = d.strftime('%d')
if totaldays<30: if totaldays<30:
dates.append(d.strftime('%m/%d')) dates.append(d.strftime('%m/%d'))
dates_sorting.append(d.strftime('%Y/%m/%d')) dates_sorting.append(d.strftime('%Y/%m/%d'))
@@ -582,6 +581,9 @@ def interactive_activitychart(workouts,startdate,enddate,stack='type',toolbar_lo
rscores.append(0) rscores.append(0)
trimps.append(0) trimps.append(0)
links.append('') links.append('')
try:
types.append(types[0])
except IndexError:
types.append('rower') types.append('rower')
try: try:
+1 -1
View File
@@ -3158,7 +3158,7 @@ class Workout(models.Model):
timezone = models.CharField(default='UTC', timezone = models.CharField(default='UTC',
choices=timezones, choices=timezones,
max_length=100) max_length=100)
distance = models.IntegerField(default=0,blank=True) distance = models.IntegerField(default=0)
duration = models.TimeField(blank=True) duration = models.TimeField(blank=True)
dragfactor = models.IntegerField(default=0,blank=True) dragfactor = models.IntegerField(default=0,blank=True)
+10 -10
View File
@@ -21,7 +21,7 @@ workouttypes_ordered = collections.OrderedDict({
'c-boat':'Dutch C boat', 'c-boat':'Dutch C boat',
'churchboat':'Finnish Church boat', 'churchboat':'Finnish Church boat',
'Ride':'Ride', 'Ride':'Ride',
'Bike':'Bike', 'bike':'Bike',
'Run':'Run', 'Run':'Run',
'NordicSki':'NordicSki', 'NordicSki':'NordicSki',
'Swim':'Swim', 'Swim':'Swim',
@@ -36,7 +36,7 @@ workouttypes_ordered = collections.OrderedDict({
'Kayaking':'Kayaking', 'Kayaking':'Kayaking',
'Workout':'Workout', 'Workout':'Workout',
'Yoga':'Yoga', 'Yoga':'Yoga',
'bike':'Bike', # 'bike':'Bike',
'other':'Other', 'other':'Other',
} }
) )
@@ -51,7 +51,7 @@ stravacollection = (
('water','Rowing'), ('water','Rowing'),
('rower','Rowing'), ('rower','Rowing'),
('skierg','NordicSki'), ('skierg','NordicSki'),
('Bike','Ride'), ('bike','Ride'),
('bikeerg','Ride'), ('bikeerg','Ride'),
('dynamic','Rowing'), ('dynamic','Rowing'),
('slides','Rowing'), ('slides','Rowing'),
@@ -84,7 +84,7 @@ garmincollection = (
('water','ROWING'), ('water','ROWING'),
('rower','INDOOR_ROWING'), ('rower','INDOOR_ROWING'),
('skierg','CROSS_COUNTRY_SKIING'), ('skierg','CROSS_COUNTRY_SKIING'),
('Bike','ROAD_BIKING'), ('bike','ROAD_BIKING'),
('bikeerg','INDOOR_CYCLING'), ('bikeerg','INDOOR_CYCLING'),
('dynamic','INDOOR_ROWING'), ('dynamic','INDOOR_ROWING'),
('slides','INDOOR_ROWING'), ('slides','INDOOR_ROWING'),
@@ -117,7 +117,7 @@ fitcollection = (
('water','rowing'), ('water','rowing'),
('rower','rowing'), ('rower','rowing'),
('skierg','cross_country_skiing'), ('skierg','cross_country_skiing'),
('Bike','cycling'), ('bike','cycling'),
('bikeerg','cycling'), ('bikeerg','cycling'),
('dynamic','rowing'), ('dynamic','rowing'),
('slides','rowing'), ('slides','rowing'),
@@ -153,7 +153,7 @@ stcollection = (
('water','Rowing'), ('water','Rowing'),
('rower','Rowing'), ('rower','Rowing'),
('skierg','Skiing:Nordic'), ('skierg','Skiing:Nordic'),
('Bike','Cycling'), ('bike','Cycling'),
('bikeerg','Cycling'), ('bikeerg','Cycling'),
('dynamic','Rowing'), ('dynamic','Rowing'),
('slides','Rowing'), ('slides','Rowing'),
@@ -187,7 +187,7 @@ rkcollection = (
('water','Rowing'), ('water','Rowing'),
('rower','Rowing'), ('rower','Rowing'),
('skierg','Cross-Country Skiing'), ('skierg','Cross-Country Skiing'),
('Bike','Cycling'), ('bike','Cycling'),
('bikeerg','Cycling'), ('bikeerg','Cycling'),
('dynamic','Rowing'), ('dynamic','Rowing'),
('slides','Rowing'), ('slides','Rowing'),
@@ -220,7 +220,7 @@ polarcollection = (
('water','Rowing'), ('water','Rowing'),
('rower','Rowing'), ('rower','Rowing'),
('skierg','Skiing'), ('skierg','Skiing'),
('Bike','Cycling'), ('bike','Cycling'),
('bikeerg','Cycling'), ('bikeerg','Cycling'),
('dynamic','Rowing'), ('dynamic','Rowing'),
('slides','Rowing'), ('slides','Rowing'),
@@ -253,7 +253,7 @@ tpcollection = (
('water','rowing'), ('water','rowing'),
('rower','rowing'), ('rower','rowing'),
('skierg','xc-ski'), ('skierg','xc-ski'),
('Bike','bike'), ('bike','bike'),
('Bikeerg','bike'), ('Bikeerg','bike'),
('dynamic','rowing'), ('dynamic','rowing'),
('slides','rowing'), ('slides','rowing'),
@@ -286,7 +286,7 @@ c2collection = (
('water','water'), ('water','water'),
('rower','rower'), ('rower','rower'),
('skierg','skierg'), ('skierg','skierg'),
('Bike','bike'), ('bike','bike'),
('bikeerg','bike'), ('bikeerg','bike'),
('dynamic','dynamic'), ('dynamic','dynamic'),
('slides','slides'), ('slides','slides'),
+2 -1
View File
@@ -68,7 +68,8 @@ def validate_file_extension(value):
ext = os.path.splitext(value.name)[1] ext = os.path.splitext(value.name)[1]
valid_extensions = ['.tcx','.csv','.TCX','.gpx','.GPX', valid_extensions = ['.tcx','.csv','.TCX','.gpx','.GPX',
'.CSV','.fit','.FIT','.zip','.ZIP', '.CSV','.fit','.FIT','.zip','.ZIP',
'.gz','.GZ','.xls'] '.gz','.GZ','.xls',
'.jpg','.jpeg','.tiff','.png','.gif','.bmp']
if not ext in valid_extensions: # pragma: no cover if not ext in valid_extensions: # pragma: no cover
raise ValidationError(u'File not supported!') raise ValidationError(u'File not supported!')
+1 -1
View File
@@ -330,7 +330,7 @@ def create_async_workout(alldata,user,stravaid,debug=False):
starttime = rowdatetime.astimezone( starttime = rowdatetime.astimezone(
pytz.timezone(thetimezone) pytz.timezone(thetimezone)
).strftime('%H:%m:%S') ).strftime('%H:%M:%S')
totaltime = data['elapsed_time'] totaltime = data['elapsed_time']
duration = dataprep.totaltime_sec_to_string(totaltime) duration = dataprep.totaltime_sec_to_string(totaltime)
+13 -14
View File
@@ -419,7 +419,7 @@ def handle_c2_import_stroke_data(c2token,
duration = datetime.datetime.strptime(duration,'%H:%M:%S.%f').time() duration = datetime.datetime.strptime(duration,'%H:%M:%S.%f').time()
try: try:
timezone_str = tz(workoutdata['timezone']) timezone_str = workoutdata['timezone']
except: except:
timezone_str = 'UTC' timezone_str = 'UTC'
@@ -2991,7 +2991,7 @@ def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone
c2id = data['id'] c2id = data['id']
workouttype = data['type'] workouttype = data['type']
verified = data['verified'] verified = data['verified']
startdatetime = iso8601.parse_date(data['date']) startdatetime = iso8601.parse_date(data['date_utc'])
weightclass = data['weight_class'] weightclass = data['weight_class']
try: try:
@@ -3013,21 +3013,18 @@ def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone
totaltime = data['time']/10. totaltime = data['time']/10.
duration = totaltime_sec_to_string(totaltime) duration = totaltime_sec_to_string(totaltime)
starttimeunix = arrow.get(startdatetime).timestamp()-totaltime
startdatetime = arrow.get(starttimeunix)
try: timezone = pytz.timezone(data['timezone'])
timezone_str = tz(data['timezone']) startdatetime = startdatetime.astimezone(timezone)
except:
timezone_str = defaulttimezone
startdatetime = startdatetime.replace(tzinfo=None)
tz = pytz.timezone(timezone_str)
startdatetime = tz.localize(startdatetime)
workoutdate = startdatetime.astimezone( workoutdate = startdatetime.astimezone(
pytz.timezone(timezone_str) timezone
).strftime('%Y-%m-%d') ).strftime('%Y-%m-%d')
starttime = startdatetime.astimezone( starttime = startdatetime.astimezone(
pytz.timezone(timezone_str) timezone
).strftime('%H:%M:%S') ).strftime('%H:%M:%S')
try: try:
@@ -3056,8 +3053,6 @@ def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone
cum_time = res[0] cum_time = res[0]
lapidx = res[1] lapidx = res[1]
starttimeunix = arrow.get(startdatetime).timestamp()
starttimeunix = starttimeunix-cum_time.max()
unixtime = cum_time+starttimeunix unixtime = cum_time+starttimeunix
# unixtime[0] = starttimeunix # unixtime[0] = starttimeunix
@@ -3134,6 +3129,7 @@ def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone
'workouttype':workouttype, 'workouttype':workouttype,
'boattype':'1x', 'boattype':'1x',
'c2id':c2id, 'c2id':c2id,
'timezone':str(timezone)
} }
session = requests.session() session = requests.session()
@@ -3165,8 +3161,11 @@ def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone
parkedids = [] parkedids = []
with open('c2blocked.json','r') as c2blocked: with open('c2blocked.json','r') as c2blocked:
try:
jsondata = json.load(c2blocked) jsondata = json.load(c2blocked)
parkedids = jsondata['ids'] parkedids = jsondata['ids']
except JSONDecodeError: # pragma: no cover
parkedids = []
newparkedids = [id for id in parkedids if id != newc2id] newparkedids = [id for id in parkedids if id != newc2id]
with open('c2blocked.json','wt') as c2blocked: with open('c2blocked.json','wt') as c2blocked:
@@ -3364,7 +3363,7 @@ def fetch_strava_workout(stravatoken,oauth_data,stravaid,csvfilename,userid,debu
comments = ' ' comments = ' '
try: try:
thetimezone = tz(workoutsummary['timezone']) thetimezone = workoutsummary['timezone']
except: except:
thetimezone = 'UTC' thetimezone = 'UTC'
-35
View File
@@ -111,9 +111,6 @@
{% for key,value in formvalues.items %} {% for key,value in formvalues.items %}
<input type="hidden" name="{{ key }}" value="{{ value|safe }}"> <input type="hidden" name="{{ key }}" value="{{ value|safe }}">
{% endfor %} {% endfor %}
{% for field in detailform %}
{{ field.as_hidden }}
{% endfor %}
<p> <p>
<input type="submit" value="Save"> <input type="submit" value="Save">
</p> </p>
@@ -130,38 +127,6 @@
</p> </p>
</li> </li>
<li class="grid_2"> <li class="grid_2">
<h1>Detailed Summary Edit</h1>
<p>This is still experimental and there are known bugs. Use at your own risk. Nothing is stored permanently until you hit Save in the Updated Summary section. You can use the restore original button to restore the original values.</p>
<form enctype="multipart/form-data" action="/rowers/workout/{{ workout.id|encode }}/editintervals/" method="post">
<table width=100%>
<thead>
<tr>
<th>#</th><th>Time</th><th>Distance</th><th>Type</th>
</tr>
</thead>
<tbody>
{% for i in nrintervals|times %}
<tr>
<td>{{ i }}&nbsp;</td>
<td>
{% get_field_id i "intervalt_" detailform %}
</td>
<td>
{% get_field_id i "intervald_" detailform %}
</td>
<td>
{% get_field_id i "type_" detailform %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% csrf_token %}
<input type="hidden" name="nrintervals" value={{ nrintervals }}>
<input class="button" type="submit" value="Update">
</form>
</li>
<li class="grid_4">
<h1 id="howto">Interval Shorthand How-To</h1> <h1 id="howto">Interval Shorthand How-To</h1>
<p>This is a quick way to enter the intervals using a special mini-language.</p> <p>This is a quick way to enter the intervals using a special mini-language.</p>
<p>You enter something like <em>8x500m/3min</em>, press "Update" and the site will interpret this for you and update the summary on the right. If you're happy with the result, press the green Save button to update the values. Nothing will be changed permanently until you hit Save.</p> <p>You enter something like <em>8x500m/3min</em>, press "Update" and the site will interpret this for you and update the summary on the right. If you're happy with the result, press the green Save button to update the values. Nothing will be changed permanently until you hit Save.</p>
+2 -2
View File
@@ -559,7 +559,7 @@ def iterrows(df): # pragma: no cover
return df.iterrows() return df.iterrows()
@register.filter(name='times') @register.filter(name='times')
def times(number): def times(number): # pragma: no cover
return range(number) return range(number)
@register.simple_tag @register.simple_tag
@@ -567,7 +567,7 @@ def get_df_iloc(data,i,j): # pragma: no cover
return data.iloc(i,j) return data.iloc(i,j)
@register.simple_tag @register.simple_tag
def get_field_id(id,s,form): def get_field_id(id,s,form): # pragma: no cover
field_name = s+str(id) field_name = s+str(id)
return form.__getitem__(field_name) return form.__getitem__(field_name)
+12
View File
@@ -713,6 +713,13 @@ def mocked_requests(*args, **kwargs):
with open('rowers/tests/testdata/c2jsonworkoutdata.txt','r') as infile: with open('rowers/tests/testdata/c2jsonworkoutdata.txt','r') as infile:
c2workoutdata = json.load(infile) c2workoutdata = json.load(infile)
with open('rowers/tests/testdata/c2_timezone.json','r') as infile:
c2timezoneworkoutdata = json.load(infile)
with open('rowers/tests/testdata/c2_timezone2.json','r') as infile:
c2timezoneworkoutdata2 = json.load(infile)
with open('rowers/tests/testdata/c2jsonstrokedata.txt','r') as infile: with open('rowers/tests/testdata/c2jsonstrokedata.txt','r') as infile:
c2strokedata = json.load(infile) c2strokedata = json.load(infile)
@@ -1181,7 +1188,12 @@ def mocked_requests(*args, **kwargs):
if c2strokestester.match(args[0]): if c2strokestester.match(args[0]):
return MockResponse(c2strokedata,200) return MockResponse(c2strokedata,200)
elif c2importtester.match(args[0]): elif c2importtester.match(args[0]):
if '12' in args[0]:
return MockResponse(c2workoutdata,200) return MockResponse(c2workoutdata,200)
elif '31' in args[0]:
return MockResponse(c2timezoneworkoutdata2,200)
else:
return MockResponse(c2timezoneworkoutdata,200)
elif c2workoutlisttester.match(args[0]): elif c2workoutlisttester.match(args[0]):
return MockResponse(c2workoutlist,200) return MockResponse(c2workoutlist,200)
elif 'access_token' in args[0]: elif 'access_token' in args[0]:
+1 -1
View File
@@ -427,7 +427,7 @@ workout bike
self.assertEqual(len(ws),1) self.assertEqual(len(ws),1)
w = ws[0] w = ws[0]
self.assertEqual(w.workouttype,'Bike') self.assertEqual(w.workouttype,'bike')
+35
View File
@@ -158,6 +158,7 @@ class C2Objects(DjangoTestCase):
self.r.c2token = '12' self.r.c2token = '12'
self.r.c2refreshtoken = 'ab' self.r.c2refreshtoken = 'ab'
self.r.tokenexpirydate = arrow.get(datetime.datetime.now()+datetime.timedelta(days=1)).datetime self.r.tokenexpirydate = arrow.get(datetime.datetime.now()+datetime.timedelta(days=1)).datetime
self.r.defaulttimezone = 'Europe/Prague'
self.r.save() self.r.save()
self.c.login(username='john',password='koeinsloot') self.c.login(username='john',password='koeinsloot')
@@ -261,6 +262,39 @@ class C2Objects(DjangoTestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
@patch('rowers.c2stuff.requests.get', side_effect=mocked_requests)
@patch('rowers.dataprep.create_engine')
def test_c2_import_tz(self, mock_get, mocked_sqlalchemy):
response = self.c.get('/rowers/workout/c2import/22/',follow=True)
self.assertRedirects(response,
expected_url='/rowers/workout/'+encoded2+'/edit/',
status_code=302,target_status_code=200)
self.assertEqual(response.status_code, 200)
w = Workout.objects.get(id=2)
self.assertEqual(w.timezone,'Europe/Prague')
@patch('rowers.c2stuff.requests.get', side_effect=mocked_requests)
@patch('rowers.dataprep.create_engine')
def test_c2_import_tz2(self, mock_get, mocked_sqlalchemy):
response = self.c.get('/rowers/workout/c2import/31/',follow=True)
self.assertRedirects(response,
expected_url='/rowers/workout/'+encoded2+'/edit/',
status_code=302,target_status_code=200)
self.assertEqual(response.status_code, 200)
w = Workout.objects.get(id=2)
self.assertEqual(w.timezone,'Europe/Amsterdam')
self.assertEqual(w.starttime.strftime("%H:%M:%S"),"20:04:56")
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
def test_strokedata(self, mocked_sqlalchemy): def test_strokedata(self, mocked_sqlalchemy):
@@ -309,6 +343,7 @@ class C2Objects(DjangoTestCase):
res = tasks.handle_c2_async_workout(alldata,self.u.id,self.r.c2token,33991243,0,self.r.defaulttimezone) res = tasks.handle_c2_async_workout(alldata,self.u.id,self.r.c2token,33991243,0,self.r.defaulttimezone)
self.assertEqual(res,1) self.assertEqual(res,1)
@override_settings(TESTING=True) @override_settings(TESTING=True)
class C2ObjectsTokenExpired(DjangoTestCase): class C2ObjectsTokenExpired(DjangoTestCase):
def setUp(self): def setUp(self):
+63 -16
View File
@@ -524,7 +524,6 @@ def get_strava_stream(r,metric,stravaid,series_type='time',fetchresolution='high
if metric=='power': # pragma: no cover if metric=='power': # pragma: no cover
with open('data.txt', 'w') as outfile: with open('data.txt', 'w') as outfile:
json.dump(s.json(), outfile) json.dump(s.json(), outfile)
print('saved to file')
for data in s.json(): for data in s.json():
y = None y = None
@@ -589,18 +588,15 @@ def step_to_time_dist(step,avgspeed = 3.2,ftp=200,ftspm=25,ftv=3.7):
seconds = 0 seconds = 0
distance = 0 distance = 0
rscore = 0 rscore = 0
durationtype = step['durationType'] durationtype = step.get('durationType',0)
value = step.get('durationValue',0)
if step['durationValue'] == 0: # pragma: no cover if value == 0: # pragma: no cover
return 0,0,0 return 0,0,0
try: targettype = step.get('targetType',0)
targettype = step['targetType']
except KeyError: # pragma: no cover
targettype = 0
if durationtype == 'Time': if durationtype == 'Time':
value = step['durationValue']
seconds = value/1000. seconds = value/1000.
distance = avgspeed*seconds distance = avgspeed*seconds
rscore = 60.*seconds/3600. rscore = 60.*seconds/3600.
@@ -616,6 +612,8 @@ def step_to_time_dist(step,avgspeed = 3.2,ftp=200,ftspm=25,ftv=3.7):
elif valuelow != 0 and valuehigh != 0: # pragma: no cover elif valuelow != 0 and valuehigh != 0: # pragma: no cover
distance = seconds*(valuelow+valuehigh)/2. distance = seconds*(valuelow+valuehigh)/2.
velomid = (valuelow+valuehigh)/2000. velomid = (valuelow+valuehigh)/2000.
else:
velomid = avgspeed
veloratio = (velomid/ftv)**(3.0) veloratio = (velomid/ftv)**(3.0)
rscoreperhour = 100.*veloratio rscoreperhour = 100.*veloratio
@@ -657,22 +655,21 @@ def step_to_time_dist(step,avgspeed = 3.2,ftp=200,ftspm=25,ftv=3.7):
rscore = 100*(avgpower/ftp)*seconds/3600. rscore = 100*(avgpower/ftp)*seconds/3600.
return seconds,distance,rscore return seconds,distance,rscore
elif durationtype == 'Distance': # pragma: no cover elif durationtype == 'Distance':
value = step['durationValue']
distance = value/100. distance = value/100.
seconds = distance/avgspeed seconds = distance/avgspeed
rscore = 60*seconds/3600. rscore = 60.*float(seconds)/3600.
if targettype == 'Speed': if targettype == 'Speed': # pragma: no cover
value = step.get('targetValue',0) value = step.get('targetValue',0)
valuelow = step.get('targetValueLow',0) valuelow = step.get('targetValueLow',0)
valuehigh = step.get('targetValueHigh',0) valuehigh = step.get('targetValueHigh',0)
velomid = 0 velomid = 0
if value != 0: # pragma: no cover if value != 0:
seconds = distance/value seconds = distance/value
velomid = value/1000. velomid = value/1000.
elif valuelow != 0 and valuehigh != 0: # pragma: no cover elif valuelow != 0 and valuehigh != 0:
velomid = (valuelow+valuehigh)/2000. velomid = (valuelow+valuehigh)/2000.
seconds = distance/velomid seconds = distance/velomid
@@ -680,7 +677,7 @@ def step_to_time_dist(step,avgspeed = 3.2,ftp=200,ftspm=25,ftv=3.7):
rscoreperhour = 100.*veloratio rscoreperhour = 100.*veloratio
rscore = rscoreperhour*seconds/3600. rscore = rscoreperhour*seconds/3600.
if targettype == 'Power': if targettype == 'Power': # pragma: no cover
value = step.get('targetValue',0) value = step.get('targetValue',0)
valuelow = step.get('targetValueLow',0) valuelow = step.get('targetValueLow',0)
valuehigh = step.get('targetValueHigh',0) valuehigh = step.get('targetValueHigh',0)
@@ -702,7 +699,7 @@ def step_to_time_dist(step,avgspeed = 3.2,ftp=200,ftspm=25,ftv=3.7):
rscore = 100.*(avgpower/ftp)*seconds/3600. rscore = 100.*(avgpower/ftp)*seconds/3600.
if targettype == 'Cadence': if targettype == 'Cadence': # pragma: no cover
value = step.get('targetValue',0) value = step.get('targetValue',0)
valuelow = step.get('targetValueLow',0) valuelow = step.get('targetValueLow',0)
valuehigh = step.get('targetValueHigh',0) valuehigh = step.get('targetValueHigh',0)
@@ -1167,3 +1164,53 @@ def request_is_ajax(request):
# is_ajax = True # is_ajax = True
return is_ajax return is_ajax
def intervals_to_string(vals, units, typ):
if vals is None or units is None or typ is None: # pragma: no cover
return ''
if len(vals) != len(units) or len(vals) != len(typ): # pragma: no cover
return ''
s = ''
previous = 'rest'
for i in range(len(vals)):
if typ[i] == 'rest' and previous == 'rest':
if units[i] == 'min': # pragma: no cover
val = int(vals[i])*60
unit = 'sec'
else:
val = int(vals[i])
if units[i] == 'meters': # pragma: no cover
unit = 'm'
if units[i] == 'seconds':
unit = 'sec'
s += '+0min/{val}{unit}'.format(val=val,unit=unit)
elif typ[i] == 'rest': # pragma: no cover
if units[i] == 'min':
val = int(vals[i])*60
unit = 'sec'
else:
val = int(vals[i])
if units[i] == 'meters':
unit = 'm'
if units[i] == 'seconds':
unit = 'sec'
s += '/{val}{unit}'.format(val=val,unit=unit)
previous = 'rest'
else: # pragma: no cover # work interval
if units[i] == 'min':
val = int(vals[i])*60
unit = 'sec'
else:
val = int(vals[i])
if units[i] == 'meters':
unit = 'm'
if units[i] == 'seconds':
unit = 'sec'
s += '+{val}{unit}'.format(val=val,unit=unit)
previous = 'work'
if s[0] == '+':
s = s[1:]
return s
+1
View File
@@ -2099,6 +2099,7 @@ def workout_getimportview(request,externalid,source = 'c2',do_async=False):
workoutdate = startdatetime.astimezone( workoutdate = startdatetime.astimezone(
pytz.timezone(timezone_str) pytz.timezone(timezone_str)
).strftime('%Y-%m-%d') ).strftime('%Y-%m-%d')
+2 -2
View File
@@ -91,7 +91,7 @@ from django.utils.datastructures import MultiValueDictKeyError
from django.utils import timezone,translation from django.utils import timezone,translation
from django.core.mail import send_mail, BadHeaderError from django.core.mail import send_mail, BadHeaderError
from rowers.forms import ( from rowers.forms import (
SummaryStringForm,IntervalUpdateForm,StrokeDataForm, SummaryStringForm,StrokeDataForm,
StatsOptionsForm,PredictedPieceForm,DateRangeForm,DeltaDaysForm, StatsOptionsForm,PredictedPieceForm,DateRangeForm,DeltaDaysForm,
FitnessMetricForm,PredictedPieceFormNoDistance, FitnessMetricForm,PredictedPieceFormNoDistance,
EmailForm, RegistrationForm, RegistrationFormTermsOfService, EmailForm, RegistrationForm, RegistrationFormTermsOfService,
@@ -1161,7 +1161,7 @@ def get_my_teams(user):
return teams return teams
# Used for the interval editor - translates seconds to a time object # Used for the interval editor - translates seconds to a time object
def get_time(second): def get_time(second): # pragma: no cover
if (second<=0) or (second>1e9): if (second<=0) or (second>1e9):
hours = 0 hours = 0
minutes=0 minutes=0
+31 -141
View File
@@ -13,6 +13,7 @@ import numpy
from rowers.mailprocessing import send_confirm from rowers.mailprocessing import send_confirm
import rowers.uploads as uploads import rowers.uploads as uploads
import rowers.utils as utils import rowers.utils as utils
from rowers.utils import intervals_to_string
from urllib.parse import urlparse, parse_qs from urllib.parse import urlparse, parse_qs
from json.decoder import JSONDecodeError from json.decoder import JSONDecodeError
@@ -2297,7 +2298,7 @@ def workout_view(request,id=0,raceresult=0,sessionresult=0,nocourseraceresult=0)
intervaldata['itime'] = itime intervaldata['itime'] = itime
intervaldata['itype'] = itype intervaldata['itype'] = itype
rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)',0.1,mode='larger', vals, units, typ = rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)',0.1,mode='larger',
debug=False,smoothwindow=15., debug=False,smoothwindow=15.,
activewindow = [startsecond,endsecond]) activewindow = [startsecond,endsecond])
summary = rowdata.allstats() summary = rowdata.allstats()
@@ -2316,7 +2317,7 @@ def workout_view(request,id=0,raceresult=0,sessionresult=0,nocourseraceresult=0)
intervaldata['itime'] = itime intervaldata['itime'] = itime
intervaldata['itype'] = itype intervaldata['itype'] = itype
rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)',0.1,mode='larger', vals, units, typ = rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)',0.1,mode='larger',
debug=False,smoothwindow=15., debug=False,smoothwindow=15.,
activewindow = [startsecond,endsecond]) activewindow = [startsecond,endsecond])
summary = rowdata.allstats() summary = rowdata.allstats()
@@ -2336,7 +2337,7 @@ def workout_view(request,id=0,raceresult=0,sessionresult=0,nocourseraceresult=0)
intervaldata['itime'] = itime intervaldata['itime'] = itime
intervaldata['itype'] = itype intervaldata['itype'] = itype
rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)',0.1,mode='larger', vals, units, typ = rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)',0.1,mode='larger',
debug=False,smoothwindow=15., debug=False,smoothwindow=15.,
activewindow = [startsecond,endsecond]) activewindow = [startsecond,endsecond])
summary = rowdata.allstats() summary = rowdata.allstats()
@@ -4812,7 +4813,7 @@ def workout_upload_api(request):
totalDistance = post_data.get('totalDistance',None) totalDistance = post_data.get('totalDistance',None)
elapsedTime = post_data.get('elapsedTime',None) elapsedTime = post_data.get('elapsedTime',None)
summary = post_data.get('summary',None) summary = post_data.get('summary',None)
timezone = post_data.get('timezone',None)
r = None r = None
if form.is_valid(): if form.is_valid():
@@ -4904,6 +4905,13 @@ def workout_upload_api(request):
return JSONResponse(status=200,data=message) return JSONResponse(status=200,data=message)
w = Workout.objects.get(id=id) w = Workout.objects.get(id=id)
if timezone is not None: # pragma: no cover
w.startdatetime = w.startdatetime.astimezone(pytz.timezone(timezone))
w.workoutdate = w.startdatetime.strftime('%Y-%m-%d')
w.starttime = w.starttime.strftime('%H:%M:%S')
w.timezone = timezone
w.save()
if make_plot: # pragma: no cover if make_plot: # pragma: no cover
@@ -6240,7 +6248,7 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
if powerorpace == 'power' and power is not None: if powerorpace == 'power' and power is not None:
try: try:
rowdata.updateinterval_metric( vals, units, typ = rowdata.updateinterval_metric(
' Power (watts)',power,mode='larger', ' Power (watts)',power,mode='larger',
debug=False,smoothwindow=15., debug=False,smoothwindow=15.,
activewindow=[activesecondsmin,activesecondsmax], activewindow=[activesecondsmin,activesecondsmax],
@@ -6250,7 +6258,7 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
elif powerorpace == 'pace': # pragma: no cover elif powerorpace == 'pace': # pragma: no cover
try: try:
velo = 500./pace_secs velo = 500./pace_secs
rowdata.updateinterval_metric( vals, units, typ = rowdata.updateinterval_metric(
' AverageBoatSpeed (m/s)',velo,mode='larger', ' AverageBoatSpeed (m/s)',velo,mode='larger',
debug=False,smoothwindow=15., debug=False,smoothwindow=15.,
activewindow=[activesecondsmin,activesecondsmax], activewindow=[activesecondsmin,activesecondsmax],
@@ -6259,7 +6267,7 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
messages.error(request,'Error updating pace') messages.error(request,'Error updating pace')
elif powerorpace == 'work': # pragma: no cover elif powerorpace == 'work': # pragma: no cover
try: try:
rowdata.updateinterval_metric( vals, units, typ = rowdata.updateinterval_metric(
'driveenergy',work,mode='larger', 'driveenergy',work,mode='larger',
debug=False,smoothwindow=15., debug=False,smoothwindow=15.,
activewindow=[activesecondsmin,activesecondsmax], activewindow=[activesecondsmin,activesecondsmax],
@@ -6268,13 +6276,17 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
messages.error(request,'Error updating Work per Stroke') messages.error(request,'Error updating Work per Stroke')
elif powerorpace == 'spm': # pragma: no cover elif powerorpace == 'spm': # pragma: no cover
try: try:
rowdata.updateinterval_metric( vals, units, typ = rowdata.updateinterval_metric(
' Cadence (stokes/min)',spm,mode='larger', ' Cadence (stokes/min)',spm,mode='larger',
debug=False,smoothwindow=2., debug=False,smoothwindow=2.,
activewindow=[activesecondsmin,activesecondsmax],) activewindow=[activesecondsmin,activesecondsmax],)
except: except:
messages.error(request,'Error updating SPM') messages.error(request,'Error updating SPM')
intervalString = ''
if vals is not None:
intervalString = intervals_to_string(vals, units, typ)
intervalstats = rowdata.allstats() intervalstats = rowdata.allstats()
itime,idist,itype = rowdata.intervalstats_values() itime,idist,itype = rowdata.intervalstats_values()
nrintervals = len(idist) nrintervals = len(idist)
@@ -6296,7 +6308,7 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
'activeminutesmin': activeminutesmin, 'activeminutesmin': activeminutesmin,
'activeminutesmax': activeminutesmax, 'activeminutesmax': activeminutesmax,
} }
form = SummaryStringForm() form = SummaryStringForm(initial={'intervalstring':intervalString})
powerupdateform = PowerIntervalUpdateForm(initial=data) powerupdateform = PowerIntervalUpdateForm(initial=data)
savebutton = 'savepowerpaceform' savebutton = 'savepowerpaceform'
formvalues = { formvalues = {
@@ -6368,7 +6380,7 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
pace_secs = 120. pace_secs = 120.
if powerorpace == 'power' and power is not None: if powerorpace == 'power' and power is not None:
rowdata.updateinterval_metric(' Power (watts)',power,mode='larger', vals, units, typ = rowdata.updateinterval_metric(' Power (watts)',power,mode='larger',
debug=False,smoothwindow=15, debug=False,smoothwindow=15,
activewindow=[activesecondsmin,activesecondsmax], activewindow=[activesecondsmin,activesecondsmax],
) )
@@ -6376,7 +6388,7 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
elif powerorpace == 'pace': # pragma: no cover elif powerorpace == 'pace': # pragma: no cover
try: try:
velo = 500./pace_secs velo = 500./pace_secs
rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)',velo,mode='larger', vals, units, typ = rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)',velo,mode='larger',
debug=False,smoothwindow=15, debug=False,smoothwindow=15,
activewindow=[activesecondsmin,activesecondsmax], activewindow=[activesecondsmin,activesecondsmax],
) )
@@ -6384,7 +6396,7 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
messages.error(request,'Error updating pace') messages.error(request,'Error updating pace')
elif powerorpace == 'work': # pragma: no cover elif powerorpace == 'work': # pragma: no cover
try: try:
rowdata.updateinterval_metric( vals, units, typ = rowdata.updateinterval_metric(
'driveenergy',work,mode='larger', 'driveenergy',work,mode='larger',
debug=False,smoothwindow=15., debug=False,smoothwindow=15.,
activewindow=[activesecondsmin,activesecondsmax],) activewindow=[activesecondsmin,activesecondsmax],)
@@ -6392,7 +6404,7 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
messages.error(request,'Error updating Work per Stroke') messages.error(request,'Error updating Work per Stroke')
elif powerorpace == 'spm': # pragma: no cover elif powerorpace == 'spm': # pragma: no cover
try: try:
rowdata.updateinterval_metric(' Cadence (stokes/min)',spm,mode='larger', vals, units, typ = rowdata.updateinterval_metric(' Cadence (stokes/min)',spm,mode='larger',
debug=False,smoothwindow=2., debug=False,smoothwindow=2.,
activewindow=[activesecondsmin,activesecondsmax], activewindow=[activesecondsmin,activesecondsmax],
) )
@@ -6400,6 +6412,11 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
messages.error(request,'Error updating SPM') messages.error(request,'Error updating SPM')
intervalString = ''
if vals is not None:
intervalString = intervals_to_string(vals, units, typ)
intervalstats = rowdata.allstats() intervalstats = rowdata.allstats()
itime,idist,itype = rowdata.intervalstats_values() itime,idist,itype = rowdata.intervalstats_values()
nrintervals = len(idist) nrintervals = len(idist)
@@ -6414,135 +6431,10 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
'activeminutesmax': activeminutesmax, 'activeminutesmax': activeminutesmax,
} }
powerupdateform = PowerIntervalUpdateForm(initial=cd) powerupdateform = PowerIntervalUpdateForm(initial=cd)
form = SummaryStringForm() form = SummaryStringForm(initial={'intervalstring':intervalString})
form = SummaryStringForm()
# we are saving the results obtained from the detailed form
elif request.method == 'POST' and "savedetailform" in request.POST: # pragma: no cover
savebutton = 'savedetailform'
form = SummaryStringForm()
nrintervals = int(request.POST['nrintervals'])
detailform = IntervalUpdateForm(request.POST,aantal=nrintervals)
itime = []
idist = []
itype = []
ivalues = []
iunits = []
itypes = []
iresults = []
for i in range(nrintervals):
try:
t = datetime.datetime.strptime(request.POST['intervalt_%s' % i],"%H:%M:%S.%f")
except ValueError:
t = datetime.datetime.strptime(request.POST['intervalt_%s' % i],"%H:%M:%S")
timesecs = 3600*t.hour+60*t.minute+t.second+t.microsecond/1.e6
itime += [timesecs]
idist += [int(request.POST['intervald_%s' % i])]
itype += [int(request.POST['type_%s' % i])]
if itype[i] == 3: # rest
itypes += ['rest']
ivalues += [timesecs]
iresults += [idist[i]]
iunits += ['seconds']
if itype[i] == 5 or itype[i] == 2: # distance based work
itypes += ['work']
ivalues += [idist[i]]
iresults += [timesecs]
iunits += ['meters']
if itype[i] == 4 or itype[i] == 1: # time based work
itypes += ['work']
ivalues += [timesecs]
iresults += [idist[i]]
iunits += ['seconds']
rowdata.updateintervaldata(ivalues,iunits,itypes,iresults=iresults)
intervalstats = rowdata.allstats()
row.summary = intervalstats
try:
row.notes += "\n"+s
except TypeError:
pass
row.save()
rowdata.write_csv(f1,gzip=True)
dataprep.update_strokedata(encoder.decode_hex(id),rowdata.df)
messages.info(request,"Updated interval data saved")
form = SummaryStringForm()
powerupdateform = PowerIntervalUpdateForm(initial={
'power': int(normp),
'pace': avpace,
'selector': 'power',
'work': int(normw),
'spm': int(normspm),
'activeminutesmin': 0,
'activeminutesmax': activeminutesmax,
})
# we are processing the details form
elif request.method == 'POST' and "nrintervals" in request.POST: # pragma: no cover
savebutton = 'savedetailform'
nrintervals = int(request.POST['nrintervals'])
detailform = IntervalUpdateForm(request.POST,aantal=nrintervals)
if detailform.is_valid():
cd = detailform.cleaned_data
itime = []
idist = []
itype = []
ivalues = []
iunits = []
itypes = []
iresults = []
for i in range(nrintervals):
t = cd['intervalt_%s' % i]
timesecs = t.total_seconds()
itime += [timesecs]
idist += [cd['intervald_%s' % i]]
itype += [cd['type_%s' % i]]
if itype[i] == '3': # rest
itypes += ['rest']
ivalues += [timesecs]
iresults += [idist[i]]
iunits += ['seconds']
if itype[i] == '5' or itype[i] == '2': # distance based work
itypes += ['work']
ivalues += [idist[i]]
iresults += [timesecs]
iunits += ['meters']
if itype[i] == '4' or itype[i] == '1': # time based work
itypes += ['work']
ivalues += [timesecs]
iresults += [idist[i]]
iunits += ['seconds']
rowdata.updateintervaldata(ivalues,iunits,
itypes,iresults=iresults)
intervalstats = rowdata.allstats()
form = SummaryStringForm()
powerupdateform = PowerIntervalUpdateForm()
initial = {}
for i in range(nrintervals):
try:
initial['intervald_%s' % i] = idist[i]
initial['intervalt_%s' % i] = get_time(itime[i])
initial['type_%s' % i] = itype[i]
except IndexError: # pragma: no cover
pass
detailform = IntervalUpdateForm(aantal=nrintervals,initial=initial)
# create interactive plot # create interactive plot
try: try:
@@ -6562,13 +6454,11 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
div = '' div = ''
# render page # render page
return render(request, 'summary_edit.html', return render(request, 'summary_edit.html',
{'form':form, {'form':form,
'activeminutesmax':activeminutesmax, 'activeminutesmax':activeminutesmax,
'activeminutesmin':activeminutesmin, 'activeminutesmin':activeminutesmin,
'maxminutes': maxminutes, 'maxminutes': maxminutes,
'detailform':detailform,
'powerupdateform':powerupdateform, 'powerupdateform':powerupdateform,
'workout':row, 'workout':row,
'rower':r, 'rower':r,