Private
Public Access
1
0

Merge branch 'release/v8.49'

This commit is contained in:
Sander Roosendaal
2018-11-15 19:05:28 +01:00
19 changed files with 660 additions and 187 deletions

View File

@@ -44,7 +44,8 @@ class RowerInline(admin.StackedInline):
'polartoken','polartokenexpirydate', 'polartoken','polartokenexpirydate',
'polarrefreshtoken','polaruserid', 'polarrefreshtoken','polaruserid',
'polar_auto_import', 'polar_auto_import',
'stravatoken','stravaexportas','strava_auto_export', 'stravatoken','stravatokenexpirydate','stravarefreshtoken',
'stravaexportas','strava_auto_export',
'strava_auto_import', 'strava_auto_import',
'runkeepertoken','runkeeper_auto_export',)}), 'runkeepertoken','runkeeper_auto_export',)}),
('Team', ('Team',

View File

@@ -6,7 +6,7 @@
from rowers.imports import * from rowers.imports import *
import datetime import datetime
from requests import Request, Session from requests import Request, Session
import mytypes
from rowers.mytypes import otwtypes from rowers.mytypes import otwtypes
from iso8601 import ParseError from iso8601 import ParseError
@@ -428,7 +428,7 @@ def createc2workoutdata(w):
startdate = datetime.datetime.combine(w.date,datetime.time()) startdate = datetime.datetime.combine(w.date,datetime.time())
data = { data = {
"type": workouttype, "type": mytypes.c2mapping[workouttype],
"date": w.startdatetime.isoformat(), "date": w.startdatetime.isoformat(),
"timezone": w.timezone, "timezone": w.timezone,
"distance": int(w.distance), "distance": int(w.distance),
@@ -684,6 +684,12 @@ def process_callback(request):
# Uploading workout # Uploading workout
def workout_c2_upload(user,w): def workout_c2_upload(user,w):
message = 'trying C2 upload' message = 'trying C2 upload'
try:
if mytypes.c2mapping[w.workouttype] is None:
return "This workout type cannot be uploaded to Concept2",0
except KeyError:
return "This workout type cannot be uploaded to Concept2",0
thetoken = c2_open(user) thetoken = c2_open(user)
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
@@ -755,7 +761,7 @@ def add_workout_from_data(user,importid,data,strokedata,
source='c2',splitdata=None, source='c2',splitdata=None,
workoutsource='concept2'): workoutsource='concept2'):
try: try:
workouttype = data['type'] workouttype = mytypes.c2mappinginv[data['type']]
except KeyError: except KeyError:
workouttype = 'rower' workouttype = 'rower'

View File

@@ -741,6 +741,7 @@ def fetchcp(rower,theworkouts,table='cpdata'):
def create_row_df(r,distance,duration,startdatetime,workouttype='rower', def create_row_df(r,distance,duration,startdatetime,workouttype='rower',
avghr=None,avgpwr=None,avgspm=None, avghr=None,avgpwr=None,avgspm=None,
rankingpiece = False, rankingpiece = False,
duplicate=False,
title='Manual entry',notes='',weightcategory='hwt'): title='Manual entry',notes='',weightcategory='hwt'):
@@ -813,6 +814,7 @@ def create_row_df(r,distance,duration,startdatetime,workouttype='rower',
title=title, title=title,
notes=notes, notes=notes,
rankingpiece=rankingpiece, rankingpiece=rankingpiece,
duplicate=duplicate,
dosmooth=False, dosmooth=False,
workouttype=workouttype, workouttype=workouttype,
consistencychecks=False, consistencychecks=False,
@@ -829,6 +831,7 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
workoutsource='unknown', workoutsource='unknown',
notes='', totaldist=0, totaltime=0, notes='', totaldist=0, totaltime=0,
rankingpiece=False, rankingpiece=False,
duplicate=False,
summary='', summary='',
makeprivate=False, makeprivate=False,
oarlength=2.89, inboard=0.88, oarlength=2.89, inboard=0.88,
@@ -999,13 +1002,30 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
maxhr = np.nan_to_num(maxhr) maxhr = np.nan_to_num(maxhr)
averagehr = np.nan_to_num(averagehr) averagehr = np.nan_to_num(averagehr)
t = datetime.datetime.strptime(duration,"%H:%M:%S.%f")
delta = datetime.timedelta(hours=t.hour, minutes=t.minute, seconds=t.second)
workoutenddatetime = workoutstartdatetime+delta
# check for duplicate start times and duration # check for duplicate start times and duration
ws = Workout.objects.filter(startdatetime=workoutstartdatetime, ws = Workout.objects.filter(user=r,date=workoutdate,duplicate=False).exclude(
distance=totaldist, startdatetime__gt=workoutenddatetime
user=r) )
if (len(ws) != 0):
message = "Warning: This workout probably already exists in the database" ws2 = []
privacy = 'hidden'
for ww in ws:
t = ww.duration
delta = datetime.timedelta(hours=t.hour, minutes=t.minute, seconds=t.second)
enddatetime = ww.startdatetime+delta
if enddatetime > workoutstartdatetime:
ws2.append(ww)
if (len(ws2) != 0):
message = "Warning: This workout overlaps with an existing one and was marked as a duplicate"
duplicate = True
w = Workout(user=r, name=title, date=workoutdate, w = Workout(user=r, name=title, date=workoutdate,
@@ -1014,6 +1034,7 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
duration=duration, distance=totaldist, duration=duration, distance=totaldist,
weightcategory=r.weightcategory, weightcategory=r.weightcategory,
starttime=workoutstarttime, starttime=workoutstarttime,
duplicate=duplicate,
workoutsource=workoutsource, workoutsource=workoutsource,
rankingpiece=rankingpiece, rankingpiece=rankingpiece,
forceunit=forceunit, forceunit=forceunit,

View File

@@ -866,6 +866,8 @@ def update_agegroup_db(age,sex,weightcategory,wcdurations,wcpower,
df['sex'] = sex df['sex'] = sex
df['age'] = age df['age'] = age
df['weightcategory'] = weightcategory df['weightcategory'] = weightcategory
df.replace([np.inf,-np.inf],np.nan,inplace=True)
df.dropna(axis=0,inplace=True)
if debug: if debug:
engine = create_engine(database_url_debug, echo=False) engine = create_engine(database_url_debug, echo=False)

View File

@@ -94,7 +94,7 @@ def imports_open(user,oauth_data):
tokenname = oauth_data['tokenname'] tokenname = oauth_data['tokenname']
refreshtokenname = oauth_data['refreshtokenname'] refreshtokenname = oauth_data['refreshtokenname']
expirydatename = oauth_data['expirydatename'] expirydatename = oauth_data['expirydatename']
if tokenexpirydate and timezone.now()>tokenexpirydate: if tokenexpirydate and timezone.now()+timedelta(seconds=3599)>tokenexpirydate:
token = imports_token_refresh( token = imports_token_refresh(
user, user,
tokenname, tokenname,
@@ -102,6 +102,15 @@ def imports_open(user,oauth_data):
expirydatename, expirydatename,
oauth_data, oauth_data,
) )
elif tokenexpirydate is None and expirydatename is not None and 'strava' in expirydatename:
token = imports_token_refresh(
user,
tokenname,
refreshtokenname,
expirydatename,
oauth_data,
)
return token return token
@@ -156,7 +165,11 @@ def imports_do_refresh_token(refreshtoken,oauth_data,access_token=''):
try: try:
expires_in = token_json['expires_in'] expires_in = token_json['expires_in']
except KeyError: except KeyError:
expires_in = 0 try:
expires_at = arrow.get(token_json['expires_at']).timestamp
expires_in = expires_at - arrow.now().timestamp
except KeyError:
expires_in = 0
try: try:
refresh_token = token_json['refresh_token'] refresh_token = token_json['refresh_token']
except KeyError: except KeyError:
@@ -267,6 +280,11 @@ def imports_token_refresh(user,tokenname,refreshtokenname,expirydatename,oauth_d
refreshtoken = getattr(r,refreshtokenname) refreshtoken = getattr(r,refreshtokenname)
# for Strava transition
if not refreshtoken:
refreshtoken = getattr(r,tokenname)
res = imports_do_refresh_token(refreshtoken,oauth_data) res = imports_do_refresh_token(refreshtoken,oauth_data)
access_token = res[0] access_token = res[0]
expires_in = res[1] expires_in = res[1]

View File

@@ -237,8 +237,6 @@ def interactive_boxchart(datadf,fieldname,extratitle=''):
def interactive_activitychart(workouts,startdate,enddate,stack='type'): def interactive_activitychart(workouts,startdate,enddate,stack='type'):
if len(workouts) == 0:
return "",""
dates = [] dates = []
dates_sorting = [] dates_sorting = []
@@ -316,7 +314,10 @@ def interactive_activitychart(workouts,startdate,enddate,stack='type'):
label = CatAttr(columns=['date'], sort=False), label = CatAttr(columns=['date'], sort=False),
xlabel='Date', xlabel='Date',
ylabel='Time', ylabel='Time',
title='Activity', title='Activity {d1} to {d2}'.format(
d1 = startdate.strftime("%Y-%m-%d"),
d2 = enddate.strftime("%Y-%m-%d"),
),
stack=stack, stack=stack,
plot_width=350, plot_width=350,
plot_height=250, plot_height=250,

View File

@@ -653,6 +653,9 @@ class Rower(models.Model):
polar_auto_import = models.BooleanField(default=False) polar_auto_import = models.BooleanField(default=False)
stravatoken = models.CharField(default='',max_length=200,blank=True,null=True) stravatoken = models.CharField(default='',max_length=200,blank=True,null=True)
stravatokenexpirydate = models.DateTimeField(blank=True,null=True)
stravarefreshtoken = models.CharField(default='',max_length=1000,
blank=True,null=True)
stravaexportas = models.CharField(default="Rowing", stravaexportas = models.CharField(default="Rowing",
max_length=30, max_length=30,
choices=stravatypes, choices=stravatypes,
@@ -2131,6 +2134,7 @@ class Workout(models.Model):
privacy = models.CharField(default='visible',max_length=30, privacy = models.CharField(default='visible',max_length=30,
choices=privacychoices) choices=privacychoices)
rankingpiece = models.BooleanField(default=False,verbose_name='Ranking Piece') rankingpiece = models.BooleanField(default=False,verbose_name='Ranking Piece')
duplicate = models.BooleanField(default=False,verbose_name='Duplicate Workout')
def __unicode__(self): def __unicode__(self):
@@ -2179,6 +2183,41 @@ def auto_delete_file_on_delete(sender, instance, **kwargs):
if os.path.isfile(instance.csvfilename+'.gz'): if os.path.isfile(instance.csvfilename+'.gz'):
os.remove(instance.csvfilename+'.gz') os.remove(instance.csvfilename+'.gz')
@receiver(models.signals.post_delete,sender=Workout)
def update_duplicates_on_delete(sender, instance, **kwargs):
if instance.id:
duplicates = Workout.objects.filter(
user=instance.user,date=instance.date,
duplicate=True)
for d in duplicates:
t = d.duration
delta = datetime.timedelta(hours=t.hour, minutes=t.minute, seconds=t.second)
workoutenddatetime = d.startdatetime+delta
ws = Workout.objects.filter(
user=d.user,date=d.date,
).exclude(
pk__in=[instance.pk,d.pk]
).exclude(
startdatetime__gt=workoutenddatetime
)
ws2 = []
for ww in ws:
t = ww.duration
delta = datetime.timedelta(hours=t.hour, minutes=t.minute, seconds=t.second)
enddatetime = ww.startdatetime+delta
if enddatetime > d.startdatetime:
ws2.append(ww)
if len(ws2) == 0:
d.duplicate=False
d.save()
# Delete stroke data from the database when a workout is deleted # Delete stroke data from the database when a workout is deleted
@receiver(models.signals.post_delete,sender=Workout) @receiver(models.signals.post_delete,sender=Workout)
def auto_delete_strokedata_on_delete(sender, instance, **kwargs): def auto_delete_strokedata_on_delete(sender, instance, **kwargs):
@@ -2401,7 +2440,7 @@ class WorkoutForm(ModelForm):
# duration = forms.TimeInput(format='%H:%M:%S.%f') # duration = forms.TimeInput(format='%H:%M:%S.%f')
class Meta: class Meta:
model = Workout model = Workout
fields = ['name','date','starttime','timezone','duration','distance','workouttype','boattype','weightcategory','notes','rankingpiece'] fields = ['name','date','starttime','timezone','duration','distance','workouttype','boattype','weightcategory','notes','rankingpiece','duplicate']
widgets = { widgets = {
'date': AdminDateWidget(), 'date': AdminDateWidget(),
'notes': forms.Textarea, 'notes': forms.Textarea,

View File

@@ -10,9 +10,214 @@ workouttypes = (
('coastal','Coastal'), ('coastal','Coastal'),
('c-boat','Dutch C boat'), ('c-boat','Dutch C boat'),
('churchboat','Finnish Church boat'), ('churchboat','Finnish Church boat'),
('Ride','Ride'),
('Run','Run'),
('NordicSki','NordicSki'),
('Swim','Swim'),
('Hike','Hike'),
('Walk','Walk'),
('Canoeing','Canoeing'),
('Crossfit','Crossfit'),
('StandUpPaddling','StandUpPaddling'),
('IceSkate','IceSkate'),
('WeightTraining','WeightTraining'),
('InlineSkate','InlineSkate'),
('Kayaking','Kayaking'),
('Workout','Workout'),
('other','Other'), ('other','Other'),
) )
stravamapping = {
'water':'Rowing',
'rower':'Rowing',
'skierg':'NordicSki',
'bike':'Ride',
'dynamic':'Rowing',
'slides':'Rowing',
'paddle':'StandUpPaddling',
'snow':'NordicSki',
'coastal':'Rowing',
'c-boat':'Rowing',
'churchboat':'Rowing',
'Ride':'Ride',
'Run':'Run',
'NordicSki':'NordicSki',
'Swim':'Swim',
'Hike':'Hike',
'Walk':'Walk',
'Canoeing':'Canoeing',
'Crossfit':'Crossfit',
'StandUpPaddling':'StandUpPaddling',
'IceSkate':'IceSkate',
'WeightTraining':'WeightTraining',
'InlineSkate':'InlineSkate',
'Kayaking':'Kayaking',
'Workout':'Workout',
'other':'Workout',
}
stmapping = {
'water':'Rowing',
'rower':'Rowing',
'skierg':'Skiing:Nordic',
'bike':'Cycling',
'dynamic':'Rowing',
'slides':'Rowing',
'paddle':'Other:Paddling',
'snow':'Skiing:Nordic',
'coastal':'Rowing',
'c-boat':'Rowing',
'churchboat':'Rowing',
'Ride':'Cycling',
'Run':'Running',
'NordicSki':'Skiing:Nordic',
'Swim':'Swimming',
'Hike':'Hiking',
'RollerSki':'Other:RollerSki',
'Walk':'Other:Walk',
'Canoeing':'Other:Canoeing',
'Crossfit':'Other:Crossfit',
'StandUpPaddling':'Other:StandUpPaddling',
'IceSkate':'Skating',
'WeightTraining':'Other:WeightTraining',
'InlineSkate':'Skating:InlineSkate',
'Kayaking':'Other:Kayaking',
'Workout':'Other:Workout',
'other':'Other',
}
rkmapping = {
'water':'Rowing',
'rower':'Rowing',
'skierg':'Cross-Country Skiing',
'bike':'Cycling',
'dynamic':'Rowing',
'slides':'Rowing',
'paddle':'Other:Paddling',
'snow':'Cross-Country Skiing',
'coastal':'Rowing',
'c-boat':'Rowing',
'churchboat':'Rowing',
'Ride':'Cycling',
'Run':'Running',
'NordicSki':'Cross-Country Skiing',
'Swim':'Swimming',
'Hike':'Hiking',
'Walk':'Walking',
'Canoeing':'Other',
'Crossfit':'CrossFit',
'StandUpPaddling':'Other',
'IceSkate':'Skating',
'WeightTraining':'Other',
'InlineSkate':'Skating',
'Kayaking':'Other',
'Workout':'Other',
'other':'Other',
}
polarmapping = {
'water':'Rowing',
'rower':'Rowing',
'skierg':'Skiing',
'bike':'Cycling',
'dynamic':'Rowing',
'slides':'Rowing',
'paddle':'Other Outdoor',
'snow':'Skiing',
'coastal':'Rowing',
'c-boat':'Rowing',
'churchboat':'Rowing',
'Ride':'Cycling',
'Run':'Running',
'NordicSki':'Skiing',
'Swim':'Swimming',
'Hike':'Hiking',
'Walk':'Walking',
'Canoeing':'Canoeing',
'Crossfit':'Crossfit',
'StandUpPaddling':'Other Outdoor',
'IceSkate':'Skating',
'WeightTraining':'Strength training',
'InlineSkate':'Skating',
'Kayaking':'Kayaking',
'Workout':'Other Indoor',
'other':'Other Indoor',
}
tpmapping = {
'water':'rowing',
'rower':'rowing',
'skierg':'xc-ski',
'bike':'bike',
'dynamic':'rowing',
'slides':'rowing',
'paddle':'other',
'snow':'xc-ski',
'coastal':'rowing',
'c-boat':'rowing',
'churchboat':'rowing',
'Ride':'cycling',
'Run':'run',
'NordicSki':'xc-ski',
'Swim':'swim',
'Hike':'other',
'Walk':'walk',
'Canoeing':'other',
'Crossfit':'other',
'StandUpPaddling':'other',
'IceSkate':'other',
'WeightTraining':'strength',
'InlineSkate':'other',
'Kayaking':'other',
'Workout':'other',
'other':'other',
}
c2mapping = {
'water':'water',
'rower':'rower',
'skierg':'skierg',
'bike':'bike',
'dynamic':'dynamic',
'slides':'slides',
'paddle':'paddle',
'snow':'snow',
'coastal':'water',
'c-boat':'water',
'churchboat':'water',
'Ride':'bike',
'Run':None,
'NordicSki':'snow',
'Swim':None,
'Hike':None,
'Walk':None,
'Canoeing':'paddle',
'Crossfit':None,
'StandUpPaddling':None,
'IceSkate':None,
'WeightTraining':None,
'InlineSkate':None,
'Kayaking':None,
'Workout':None,
'other':None,
}
c2mappinginv = {value:key for key,value in c2mapping.iteritems() if value is not None}
stravamappinginv = {value:key for key,value in stravamapping.iteritems() if value is not None}
stmappinginv = {value:key for key,value in stmapping.iteritems() if value is not None}
rkmappinginv = {value:key for key,value in rkmapping.iteritems() if value is not None}
polarmappinginv = {value:key for key,value in polarmapping.iteritems() if value is not None}
otwtypes = ( otwtypes = (
'water', 'water',
'coastal', 'coastal',
@@ -20,6 +225,17 @@ otwtypes = (
'churchboat' 'churchboat'
) )
rowtypes = (
'water',
'rower',
'dynamic',
'slides',
'coastal',
'c-boat',
'churchboat'
)
checktypes = [i[0] for i in workouttypes] checktypes = [i[0] for i in workouttypes]
workoutsources = ( workoutsources = (

View File

@@ -52,7 +52,7 @@ baseurl = 'https://polaraccesslink.com/v3'
from utils import NoTokenError, custom_exception_handler from utils import NoTokenError, custom_exception_handler
import mytypes
# Exchange access code for long-lived access token # Exchange access code for long-lived access token
def get_token(code): def get_token(code):

View File

@@ -18,7 +18,7 @@ from stravalib.exc import ActivityUploadFailed,TimeoutExceeded
from iso8601 import ParseError from iso8601 import ParseError
from utils import myqueue from utils import myqueue
import mytypes
import gzip import gzip
from rowsandall_app.settings import ( from rowsandall_app.settings import (
@@ -40,11 +40,11 @@ oauth_data = {
'autorization_uri': "https://www.strava.com/oauth/authorize", 'autorization_uri': "https://www.strava.com/oauth/authorize",
'content_type': 'application/json', 'content_type': 'application/json',
'tokenname': 'stravatoken', 'tokenname': 'stravatoken',
'refreshtokenname': '', 'refreshtokenname': 'stravarefreshtoken',
'expirydatename': '', 'expirydatename': 'stravatokenexpirydate',
'bearer_auth': True, 'bearer_auth': True,
'base_url': "https://www.strava.com/oauth/token", 'base_url': "https://www.strava.com/oauth/token",
'grant_type': None, 'grant_type': 'refresh_token',
} }
@@ -52,6 +52,27 @@ oauth_data = {
def get_token(code): def get_token(code):
return imports_get_token(code, oauth_data) return imports_get_token(code, oauth_data)
def strava_open(user):
return imports_open(user, oauth_data)
def do_refresh_token(refreshtoken):
return imports_do_refresh_token(refreshtoken, oauth_data)
def rower_strava_token_refresh(user):
r = Rower.objects.get(user=user)
res = do_refresh_token(r.stravarefreshtoken)
access_token = res[0]
expires_in = res[1]
refresh_token = res[2]
expirydatetime = timezone.now()+timedelta(seconds=expires_in)
r.stravatoken = access_token
r.stravatokenexpirydate = expirydatetime
r.stravarefreshtoken = refresh_token
r.save()
return r.stravatoken
# Make authorization URL including random string # Make authorization URL including random string
def make_authorization_url(request): def make_authorization_url(request):
return imports_make_authorization_url(oauth_data) return imports_make_authorization_url(oauth_data)
@@ -62,6 +83,9 @@ def get_strava_workout_list(user,limit_n=0):
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"
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
elif (r.stravatokenexpirydate is None or timezone.now()+timedelta(seconds=3599)>r.stravatokenexpirydate):
s = "Token expired. Needs to refresh."
return custom_exception_handler(401,s)
else: else:
# ready to fetch. Hurray # ready to fetch. Hurray
authorizationstring = str('Bearer ' + r.stravatoken) authorizationstring = str('Bearer ' + r.stravatoken)
@@ -78,6 +102,7 @@ def get_strava_workout_list(user,limit_n=0):
s = requests.get(url,headers=headers,params=params) s = requests.get(url,headers=headers,params=params)
return s return s
@@ -87,9 +112,12 @@ def get_strava_workouts(rower):
if not isprorower(rower): if not isprorower(rower):
return 0 return 0
res = get_strava_workout_list(rower.user,limit_n=10) try:
thetoken = strava_open(rower.user)
except NoTokenError:
return 0
print res.status_code res = get_strava_workout_list(rower.user,limit_n=10)
if (res.status_code != 200): if (res.status_code != 200):
return 0 return 0
@@ -135,9 +163,9 @@ def create_async_workout(alldata,user,stravaid,debug=False):
distance = data['distance'] distance = data['distance']
stravaid = data['id'] stravaid = data['id']
try: try:
workouttype = data['type'] workouttype = mytypes.stravamappinginv[data['type']]
except: except:
workouttype = 'rower' workouttype = 'other'
if workouttype.lower() == 'rowing': if workouttype.lower() == 'rowing':
workouttype = 'rower' workouttype = 'rower'
@@ -227,6 +255,9 @@ def get_workout(user,stravaid):
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"
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
elif (r.stravatokenexpirydate is not None and timezone.now()>r.stravatokenexpirydate):
s = "Token expired. Needs to refresh."
return custom_exception_handler(401,s)
else: else:
# ready to fetch. Hurray # ready to fetch. Hurray
fetchresolution = 'high' fetchresolution = 'high'
@@ -413,14 +444,15 @@ def add_workout_from_data(user,importid,data,strokedata,
source='strava',splitdata=None, source='strava',splitdata=None,
workoutsource='strava'): workoutsource='strava'):
try: try:
workouttype = data['type'] workouttype = mytypes.stravamappinginv[data['type']]
except KeyError: except KeyError:
workouttype = 'rower' workouttype = 'other'
if workouttype.lower() == 'rowing': if workouttype.lower() == 'rowing':
workouttype = 'rower' workouttype = 'rower'
if 'summary_polyline' in data['map']:
workouttype = 'water' if 'summary_polyline' in data['map'] and workouttype=='rower':
workouttype = 'water'
if workouttype not in [x[0] for x in Workout.workouttypes]: if workouttype not in [x[0] for x in Workout.workouttypes]:
workouttype = 'other' workouttype = 'other'

View File

@@ -83,18 +83,15 @@
$( document ).ready(function() { $( document ).ready(function() {
$('#id_workouttype').on('change', function(){ $('#id_workouttype').on('change', function(){
if ( if (
$(this).val() == 'rower' $(this).val() == 'water'
|| $(this).val() == 'skierg' || $(this).val() == 'coastal'
|| $(this).val() == 'dynamic' || $(this).val() == 'c-boat'
|| $(this).val() == 'slides' || $(this).val() == 'churchboat'
|| $(this).val() == 'paddle'
|| $(this).val() == 'bike'
|| $(this).val() == 'snow'
) { ) {
$('#id_boattype').toggle(true);
} else {
$('#id_boattype').toggle(false); $('#id_boattype').toggle(false);
$('#id_boattype').val('1x'); $('#id_boattype').val('1x');
} else {
$('#id_boattype').toggle(true);
} }
}); });
$('#id_workouttype').change(); $('#id_workouttype').change();

View File

@@ -16,18 +16,15 @@
$( document ).ready(function() { $( document ).ready(function() {
$('#id_workouttype').on('change', function(){ $('#id_workouttype').on('change', function(){
if ( if (
$(this).val() == 'rower' $(this).val() == 'water'
|| $(this).val() == 'skierg' || $(this).val() == 'coastal'
|| $(this).val() == 'dynamic' || $(this).val() == 'c-boat'
|| $(this).val() == 'slides' || $(this).val() == 'churchboat'
|| $(this).val() == 'paddle'
|| $(this).val() == 'bike'
|| $(this).val() == 'snow'
) { ) {
$('#id_boattype').toggle(true);
} else {
$('#id_boattype').toggle(false); $('#id_boattype').toggle(false);
$('#id_boattype').val('1x'); $('#id_boattype').val('1x');
} else {
$('#id_boattype').toggle(true);
} }
}); });
$('#id_workouttype').change(); $('#id_workouttype').change();

View File

@@ -89,6 +89,9 @@
</td> </td>
<td> {{ workout.distance }}m</td> <td> {{ workout.distance }}m</td>
<td> {{ workout.duration |durationprint:"%H:%M:%S.%f" }} </td> <td> {{ workout.duration |durationprint:"%H:%M:%S.%f" }} </td>
<td>
<a href="/rowers/sessions/{{ psdict.id.1 }}/detach/{{ workout.id }}/">Detach</a>
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@@ -127,6 +130,10 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
<p>
<a href="/rowers/sessions/{{ psdict.id.1 }}/compare"
title="Compare the workouts of all athletes who did this session">Compare Workouts</a>
</p>
</li> </li>
{% if coursescript %} {% if coursescript %}
<li class="grid_2"> <li class="grid_2">

View File

@@ -112,7 +112,7 @@
<li class="grid_2 maxheight"> <li class="grid_2 maxheight">
{% if workouts %} {% if workouts %}
<form enctype="multipart/form-data" <form enctype="multipart/form-data"
action="/rowers/multi-compare" action="/rowers/multi-compare/"
method="post"> method="post">
<input type="checkbox" onClick="toggle(this)" /> Toggle All<br/> <input type="checkbox" onClick="toggle(this)" /> Toggle All<br/>

View File

@@ -76,25 +76,22 @@
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script> <script>
$( document ).ready(function() { $( document ).ready(function() {
$('#id_workouttype').on('change', function(){ $('#id_workouttype').on('change', function(){
if ( if (
$(this).val() == 'rower' $(this).val() == 'water'
|| $(this).val() == 'skierg' || $(this).val() == 'coastal'
|| $(this).val() == 'dynamic' || $(this).val() == 'c-boat'
|| $(this).val() == 'slides' || $(this).val() == 'churchboat'
|| $(this).val() == 'paddle' ) {
|| $(this).val() == 'bike' $('#id_boattype').toggle(true);
|| $(this).val() == 'snow' } else {
) { $('#id_boattype').toggle(false);
$('#id_boattype').toggle(false); $('#id_boattype').val('1x');
$('#id_boattype').val('1x'); }
} else { });
$('#id_boattype').toggle(true); $('#id_workouttype').change();
} });
});
$('#id_workouttype').change();
});
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -23,18 +23,15 @@
$( document ).ready(function() { $( document ).ready(function() {
$('#id_workouttype').on('change', function(){ $('#id_workouttype').on('change', function(){
if ( if (
$(this).val() == 'rower' $(this).val() == 'water'
|| $(this).val() == 'skierg' || $(this).val() == 'coastal'
|| $(this).val() == 'dynamic' || $(this).val() == 'c-boat'
|| $(this).val() == 'slides' || $(this).val() == 'churchboat'
|| $(this).val() == 'paddle'
|| $(this).val() == 'snow'
|| $(this).val() == 'bike'
) { ) {
$('#id_boattype').toggle(true);
} else {
$('#id_boattype').toggle(false); $('#id_boattype').toggle(false);
$('#id_boattype').val('1x'); $('#id_boattype').val('1x');
} else {
$('#id_boattype').toggle(true);
} }
}); });
$('#id_workouttype').change(); $('#id_workouttype').change();

View File

@@ -321,6 +321,15 @@ def mocked_requests(*args, **kwargs):
return MockResponse(json_data,200) return MockResponse(json_data,200)
elif stravasummarytester.match(args[0]): elif stravasummarytester.match(args[0]):
return MockResponse(stravasummaryjson,200) return MockResponse(stravasummaryjson,200)
elif 'token' in args[0]:
json_data = {
"token_type": "Bearer",
"access_token": "987654321234567898765432123456789",
"refresh_token": "1234567898765432112345678987654321",
"expires_at": 1531385304
}
return MockResponse(json_data,200)
if c2tester.match(args[0]): if c2tester.match(args[0]):
if c2uploadtester.match(args[0]): if c2uploadtester.match(args[0]):
@@ -403,7 +412,7 @@ class C2Objects(DjangoTestCase):
workoutstarttime = row.rowdatetime.strftime('%H:%M:%S') workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
self.w = Workout.objects.create( self.w = Workout.objects.create(
name='testworkout',workouttype='On-water', name='testworkout',workouttype='water',
user=self.r,date=self.nu.strftime('%Y-%m-%d'), user=self.r,date=self.nu.strftime('%Y-%m-%d'),
starttime=workoutstarttime, starttime=workoutstarttime,
startdatetime=row.rowdatetime, startdatetime=row.rowdatetime,
@@ -574,7 +583,7 @@ class STObjects(DjangoTestCase):
workoutstarttime = row.rowdatetime.strftime('%H:%M:%S') workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
self.w = Workout.objects.create( self.w = Workout.objects.create(
name='testworkout',workouttype='On-water', name='testworkout',workouttype='water',
user=self.r,date=self.nu.strftime('%Y-%m-%d'), user=self.r,date=self.nu.strftime('%Y-%m-%d'),
starttime=workoutstarttime, starttime=workoutstarttime,
startdatetime=row.rowdatetime, startdatetime=row.rowdatetime,
@@ -690,7 +699,7 @@ class RunKeeperObjects(DjangoTestCase):
workoutstarttime = row.rowdatetime.strftime('%H:%M:%S') workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
self.w = Workout.objects.create( self.w = Workout.objects.create(
name='testworkout',workouttype='On-water', name='testworkout',workouttype='water',
user=self.r,date=self.nu.strftime('%Y-%m-%d'), user=self.r,date=self.nu.strftime('%Y-%m-%d'),
starttime=workoutstarttime, starttime=workoutstarttime,
startdatetime=row.rowdatetime, startdatetime=row.rowdatetime,
@@ -782,7 +791,7 @@ class UAObjects(DjangoTestCase):
workoutstarttime = row.rowdatetime.strftime('%H:%M:%S') workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
self.w = Workout.objects.create( self.w = Workout.objects.create(
name='testworkout',workouttype='On-water', name='testworkout',workouttype='water',
user=self.r,date=self.nu.strftime('%Y-%m-%d'), user=self.r,date=self.nu.strftime('%Y-%m-%d'),
starttime=workoutstarttime, starttime=workoutstarttime,
startdatetime=row.rowdatetime, startdatetime=row.rowdatetime,
@@ -882,7 +891,7 @@ class TPObjects(DjangoTestCase):
workoutstarttime = row.rowdatetime.strftime('%H:%M:%S') workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
self.w = Workout.objects.create( self.w = Workout.objects.create(
name='testworkout',workouttype='On-water', name='testworkout',workouttype='water',
user=self.r,date=self.nu.strftime('%Y-%m-%d'), user=self.r,date=self.nu.strftime('%Y-%m-%d'),
starttime=workoutstarttime, starttime=workoutstarttime,
startdatetime=row.rowdatetime, startdatetime=row.rowdatetime,
@@ -1074,7 +1083,7 @@ class WorkoutTests(TestCase):
) )
nu = datetime.datetime.now() nu = datetime.datetime.now()
self.w = Workout.objects.create(name='testworkout', self.w = Workout.objects.create(name='testworkout',
workouttype='On-water', workouttype='water',
user=self.r,date=nu.strftime('%Y-%m-%d'), user=self.r,date=nu.strftime('%Y-%m-%d'),
starttime=nu.strftime('%H:%M:%S'), starttime=nu.strftime('%H:%M:%S'),
duration="0:55:00",distance=8000) duration="0:55:00",distance=8000)
@@ -1092,7 +1101,7 @@ class C2Tests(TestCase):
gdproptindate=timezone.now() gdproptindate=timezone.now()
) )
self.nu = datetime.datetime.now() self.nu = datetime.datetime.now()
self.w = Workout.objects.create(name='testworkout',workouttype='On-water', self.w = Workout.objects.create(name='testworkout',workouttype='water',
user=r,date=nu.strftime('%Y-%m-%d'), user=r,date=nu.strftime('%Y-%m-%d'),
starttime=nu.strftime('%H:%M:%S'), starttime=nu.strftime('%H:%M:%S'),
duration="0:55:00",distance=8000) duration="0:55:00",distance=8000)
@@ -1255,7 +1264,7 @@ class DataTest(TestCase):
workoutdate = row.rowdatetime.strftime('%Y-%m-%d') workoutdate = row.rowdatetime.strftime('%Y-%m-%d')
workoutstarttime = row.rowdatetime.strftime('%H:%M:%S') workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
w = Workout.objects.create(name='testworkout',workouttype='On-water', w = Workout.objects.create(name='testworkout',workouttype='water',
user=r,date=self.nu.strftime('%Y-%m-%d'), user=r,date=self.nu.strftime('%Y-%m-%d'),
starttime=workoutstarttime, starttime=workoutstarttime,
duration=duration,distance=totaldist, duration=duration,distance=totaldist,
@@ -1999,7 +2008,7 @@ class URLTests(TestCase):
self.nu = datetime.datetime.now() self.nu = datetime.datetime.now()
filename = 'rowers/testdata/testdata.csv' filename = 'rowers/testdata/testdata.csv'
self.wotw = Workout.objects.create(name='testworkout', self.wotw = Workout.objects.create(name='testworkout',
workouttype='On-water', workouttype='water',
user=r,date=self.nu.strftime('%Y-%m-%d'), user=r,date=self.nu.strftime('%Y-%m-%d'),
starttime=self.nu.strftime('%H:%M:%S'), starttime=self.nu.strftime('%H:%M:%S'),
duration="0:55:00",distance=8000, duration="0:55:00",distance=8000,
@@ -2241,7 +2250,7 @@ class subroutinetests(TestCase):
nu = datetime.datetime.now() nu = datetime.datetime.now()
filename = 'rowers/testdata/testdata.csv' filename = 'rowers/testdata/testdata.csv'
self.w = Workout.objects.create(name='testworkout', self.w = Workout.objects.create(name='testworkout',
workouttype='On-water', workouttype='water',
user=r,date=nu.strftime('%Y-%m-%d'), user=r,date=nu.strftime('%Y-%m-%d'),
starttime=nu.strftime('%H:%M:%S'), starttime=nu.strftime('%H:%M:%S'),
duration="0:55:00",distance=8000, duration="0:55:00",distance=8000,
@@ -2266,7 +2275,7 @@ class PlotTests(TestCase):
self.nu = datetime.datetime.now() self.nu = datetime.datetime.now()
filename = 'rowers/testdata/testdata.csv' filename = 'rowers/testdata/testdata.csv'
self.wotw = Workout.objects.create(name='testworkout', self.wotw = Workout.objects.create(name='testworkout',
workouttype='On-water', workouttype='water',
user=r,date=self.nu.strftime('%Y-%m-%d'), user=r,date=self.nu.strftime('%Y-%m-%d'),
starttime=self.nu.strftime('%H:%M:%S'), starttime=self.nu.strftime('%H:%M:%S'),
duration="0:55:00",distance=8000, duration="0:55:00",distance=8000,

View File

@@ -342,8 +342,9 @@ urlpatterns = [
url(r'^workout/(?P<id>\d+)/runkeeperuploadw/$',views.workout_runkeeper_upload_view), url(r'^workout/(?P<id>\d+)/runkeeperuploadw/$',views.workout_runkeeper_upload_view),
url(r'^workout/(?P<id>\d+)/underarmouruploadw/$',views.workout_underarmour_upload_view), url(r'^workout/(?P<id>\d+)/underarmouruploadw/$',views.workout_underarmour_upload_view),
url(r'^workout/(?P<id>\d+)/tpuploadw/$',views.workout_tp_upload_view), url(r'^workout/(?P<id>\d+)/tpuploadw/$',views.workout_tp_upload_view),
url(r'^multi-compare/workout/(?P<id>\d+)$',views.multi_compare_view), url(r'^multi-compare/workout/(?P<id>\d+)/user/(?P<userid>\d+)/$',views.multi_compare_view),
url(r'^multi-compare$',views.multi_compare_view), url(r'^multi-compare/workout/(?P<id>\d+)/$',views.multi_compare_view),
url(r'^multi-compare/$',views.multi_compare_view),
url(r'^user-boxplot/user/(?P<userid>\d+)$',views.boxplot_view), url(r'^user-boxplot/user/(?P<userid>\d+)$',views.boxplot_view),
url(r'^user-boxplot$',views.boxplot_view), url(r'^user-boxplot$',views.boxplot_view),
url(r'^user-boxplot-data$',views.boxplot_view_data), url(r'^user-boxplot-data$',views.boxplot_view_data),
@@ -486,12 +487,16 @@ urlpatterns = [
url(r'^sessions/multicreate/user/(?P<userid>\d+)/$', url(r'^sessions/multicreate/user/(?P<userid>\d+)/$',
views.plannedsession_multicreate_view), views.plannedsession_multicreate_view),
url(r'^sessions/(?P<id>\d+)/edit/$',views.plannedsession_edit_view), url(r'^sessions/(?P<id>\d+)/edit/$',views.plannedsession_edit_view),
url(r'^sessions/(?P<id>\d+)/compare/$',views.plannedsession_compare_view),
url(r'^sessions/(?P<id>\d+)/compare/user/(?P<userid>\d+)/$',views.plannedsession_compare_view),
url(r'^sessions/(?P<id>\d+)/edit/user/(?P<userid>\d+)/$',views.plannedsession_edit_view), url(r'^sessions/(?P<id>\d+)/edit/user/(?P<userid>\d+)/$',views.plannedsession_edit_view),
url(r'^sessions/(?P<id>\d+)/clone/user/(?P<userid>\d+)/$',views.plannedsession_clone_view), url(r'^sessions/(?P<id>\d+)/clone/user/(?P<userid>\d+)/$',views.plannedsession_clone_view),
url(r'^sessions/(?P<id>\d+)/clone/$',views.plannedsession_clone_view), url(r'^sessions/(?P<id>\d+)/clone/$',views.plannedsession_clone_view),
url(r'^sessions/(?P<id>\d+)$',views.plannedsession_view, url(r'^sessions/(?P<psid>\d+)/detach/(?P<id>\d+)/user/(?P<userid>\d+)/$',views.plannedsession_detach_view),
url(r'^sessions/(?P<psid>\d+)/detach/(?P<id>\d+)/$',views.plannedsession_detach_view),
url(r'^sessions/(?P<id>\d+)/$',views.plannedsession_view,
name='plannedsession_view'), name='plannedsession_view'),
url(r'^sessions/(?P<id>\d+)/user/(?P<userid>\d+)$',views.plannedsession_view, url(r'^sessions/(?P<id>\d+)/user/(?P<userid>\d+)/$',views.plannedsession_view,
name='plannedsession_view'), name='plannedsession_view'),
url(r'^sessions/(?P<pk>\d+)/deleteconfirm$',views.PlannedSessionDelete.as_view()), url(r'^sessions/(?P<pk>\d+)/deleteconfirm$',views.PlannedSessionDelete.as_view()),
url(r'^sessions/(?P<pk>\d+)/delete$',views.PlannedSessionDelete.as_view(), url(r'^sessions/(?P<pk>\d+)/delete$',views.PlannedSessionDelete.as_view(),

View File

@@ -116,6 +116,7 @@ from sporttracksstuff import sporttracks_open
from tpstuff import tp_open from tpstuff import tp_open
from iso8601 import ParseError from iso8601 import ParseError
import stravastuff import stravastuff
from stravastuff import strava_open
import polarstuff import polarstuff
import sporttracksstuff import sporttracksstuff
import underarmourstuff import underarmourstuff
@@ -1732,6 +1733,12 @@ def workout_strava_upload_view(request,id=0):
message = "" message = ""
r = getrower(request.user) r = getrower(request.user)
res = -1 res = -1
try:
thetoken = strava_open(request.user)
except NoTokenError:
return HttpResponseRedirect("/rowers/me/stravaauthorize")
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"
return HttpResponseRedirect("/rowers/me/stravaauthorize/") return HttpResponseRedirect("/rowers/me/stravaauthorize/")
@@ -1748,11 +1755,16 @@ def workout_strava_upload_view(request,id=0):
newnotes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com' newnotes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com'
except TypeError: except TypeError:
newnotes = 'from '+w.workoutsource+' via rowsandall.com' newnotes = 'from '+w.workoutsource+' via rowsandall.com'
activity_type = r.stravaexportas if w.workouttype in mytypes.rowtypes:
res,mes = stravastuff.handle_stravaexport(f,w.name, activity_type = r.stravaexportas
r.stravatoken, else:
description=newnotes, activity_type = mytypes.stravamapping[w.workouttype]
activity_type=activity_type)
res,mes = stravastuff.handle_stravaexport(
f,w.name,
r.stravatoken,
description=newnotes,
activity_type=activity_type)
if res==0: if res==0:
messages.error(request,mes) messages.error(request,mes)
w.uploadedtostrava = -1 w.uploadedtostrava = -1
@@ -2421,15 +2433,22 @@ def rower_process_stravacallback(request):
if res[0]: if res[0]:
access_token = res[0] access_token = res[0]
expires_in = res[1]
refresh_token = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
r = getrower(request.user) r = getrower(request.user)
r.stravatoken = access_token r.stravatoken = access_token
r.stravatokenexpirydate = expirydatetime
r.stravarefreshtoken = refresh_token
r.save() r.save()
successmessage = "Tokens stored. Good to go" successmessage = "Tokens stored. Good to go"
messages.info(request,successmessage) messages.info(request,successmessage)
return imports_view(request) url = reverse(workouts_view)
return HttpResponseRedirect(url)
else: else:
message = "Something went wrong with the Strava authorization" message = "Something went wrong with the Strava authorization"
messages.error(request,message) messages.error(request,message)
@@ -3373,6 +3392,11 @@ def addmanual_view(request):
except KeyError: except KeyError:
rankingpiece = False rankingpiece = False
try:
duplicate = form.cleaned_data['duplicate']
except KeyError:
duplicate = False
if private: if private:
privacy = 'private' privacy = 'private'
else: else:
@@ -3395,6 +3419,7 @@ def addmanual_view(request):
avghr=avghr, avghr=avghr,
rankingpiece=rankingpiece, rankingpiece=rankingpiece,
avgpwr=avgpwr, avgpwr=avgpwr,
duplicate=duplicate,
avgspm=avgspm, avgspm=avgspm,
title = name, title = name,
notes=notes, notes=notes,
@@ -5325,6 +5350,8 @@ def team_comparison_select(request,
r = getrequestrower(request,userid=userid) r = getrequestrower(request,userid=userid)
requestrower = getrower(request.user) requestrower = getrower(request.user)
request.session.pop('ps',None)
if 'waterboattype' in request.session: if 'waterboattype' in request.session:
waterboattype = request.session['waterboattype'] waterboattype = request.session['waterboattype']
else: else:
@@ -5521,10 +5548,53 @@ def team_comparison_select(request,
'teams':get_my_teams(request.user), 'teams':get_my_teams(request.user),
}) })
@login_required()
def plannedsession_compare_view(request,id=0,userid=0):
r = getrequestrower(request,userid=userid)
try:
ps = PlannedSession.objects.get(id=id)
except PlannedSession.DoesNotExist:
raise Http404("Planned session does not exist")
m = ps.manager
mm = m.rower
if ps.manager != request.user:
if r.rowerplan == 'coach':
teams = Team.objects.filter(manager=request.user)
members = Rower.objects.filter(team__in=teams).distinct()
teamusers = [m.user for m in members]
if ps.manager not in teamusers:
raise PermissionDenied("You do not have access to this session")
elif r not in ps.rower.all():
raise PermissionDenied("You do not have access to this session")
workouts = Workout.objects.filter(plannedsession=ps)
ids = [int(w.id) for w in workouts]
labeldict = {
int(w.id): w.__unicode__() for w in workouts
}
xparam = 'time'
yparam = 'hr'
plottype = 'line'
request.session['ids'] = ids
request.session['xparam'] = xparam
request.session['yparam'] = yparam
request.session['plottype'] = plottype
request.session['ps'] = ps.id
url = reverse(multi_compare_view,kwargs={'userid':userid,'id':ids[0]})
return HttpResponseRedirect(url)
# Team comparison # Team comparison
@login_required() @login_required()
def multi_compare_view(request,id=0): def multi_compare_view(request,id=0,userid=0):
promember=0 promember=0
if not request.user.is_anonymous(): if not request.user.is_anonymous():
r = getrower(request.user) r = getrower(request.user)
@@ -5549,45 +5619,9 @@ def multi_compare_view(request,id=0):
int(w.id): w.__unicode__() for w in workouts int(w.id): w.__unicode__() for w in workouts
} }
res = interactive_multiple_compare_chart(ids,xparam,yparam,
promember=promember,
plottype=plottype,
labeldict=labeldict)
script = res[0]
div = res[1]
errormessage = res[3]
if errormessage != '':
messages.error(request,errormessage)
r = getrower(request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':reverse(team_comparison_select,kwargs={'teamid':teamid}),
'name': 'Compare Select'
},
{
'url':reverse(multi_compare_view),
'name': 'Comparison Chart'
}
]
return render(request,'multicompare.html',
{'interactiveplot':script,
'the_div':div,
'breadcrumbs':breadcrumbs,
'rower':r,
'active':'nav-workouts',
'promember':promember,
'teamid':teamid,
'chartform':chartform,
'teams':get_my_teams(request.user),
})
else: else:
return HttpResponse("Form is not valid") return HttpResponse("Form is not valid")
if request.method == 'POST' and 'ids' in request.session: elif request.method == 'POST' and 'ids' in request.session:
chartform = ChartParamChoiceForm(request.POST) chartform = ChartParamChoiceForm(request.POST)
if chartform.is_valid(): if chartform.is_valid():
xparam = chartform.cleaned_data['xparam'] xparam = chartform.cleaned_data['xparam']
@@ -5601,42 +5635,24 @@ def multi_compare_view(request,id=0):
labeldict = { labeldict = {
int(w.id): w.__unicode__() for w in workouts int(w.id): w.__unicode__() for w in workouts
} }
elif 'ids' in request.session and 'plottype' in request.session:
res = interactive_multiple_compare_chart(ids,xparam,yparam, xparam = request.session['xparam']
promember=promember, yparam = request.session['yparam']
plottype=plottype, plottype = request.session['plottype']
labeldict=labeldict) teamid = 0
script = res[0] ids = request.session['ids']
div = res[1] workouts = [Workout.objects.get(id=id) for id in ids]
labeldict = {
r = getrower(request.user) int(w.id): w.__unicode__() for w in workouts
breadcrumbs = [ }
{ chartform = ChartParamChoiceForm(
'url':'/rowers/list-workouts', initial = {
'name':'Workouts' 'xparam':xparam,
}, 'yparam':yparam,
{ 'plottype':plottype,
'url':reverse(team_comparison_select,kwargs={'teamid':teamid}), 'teamid':teamid
'name': 'Compare Select'
},
{
'url':reverse(multi_compare_view),
'name': 'Comparison Chart'
} }
] )
return render(request,'multicompare.html',
{'interactiveplot':script,
'the_div':div,
'breadcrumbs':breadcrumbs,
'rower':r,
'active':'nav-workouts',
'promember':promember,
'teamid':teamid,
'chartform':chartform,
'teams':get_my_teams(request.user),
})
else: else:
url = reverse(team_comparison_select, url = reverse(team_comparison_select,
@@ -5645,6 +5661,76 @@ def multi_compare_view(request,id=0):
'teamid':0}) 'teamid':0})
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
res = interactive_multiple_compare_chart(ids,xparam,yparam,
promember=promember,
plottype=plottype,
labeldict=labeldict)
script = res[0]
div = res[1]
errormessage = res[3]
if errormessage != '':
messages.error(request,errormessage)
r = getrower(request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':reverse(team_comparison_select,kwargs={'teamid':teamid}),
'name': 'Compare Select'
},
{
'url':reverse(multi_compare_view),
'name': 'Comparison Chart'
}
]
if 'ps' in request.session:
ps = PlannedSession.objects.get(id=int(request.session['ps']))
breadcrumbs = [
{
'url': reverse(plannedsessions_view,
kwargs={'userid':userid}),
'name': 'Sessions'
},
{
'url':reverse(plannedsession_view,
kwargs={
'userid':userid,
'id':ps.id,
}
),
'name': ps.id
},
{
'url':reverse(plannedsession_compare_view,
kwargs={
'userid':userid,
'id':ps.id,
}
),
'name': 'Compare'
}
]
return render(request,'multicompare.html',
{'interactiveplot':script,
'the_div':div,
'breadcrumbs':breadcrumbs,
'rower':r,
'active':'nav-workouts',
'promember':promember,
'teamid':teamid,
'chartform':chartform,
'teams':get_my_teams(request.user),
})
# Multi Flex Chart with Grouping # Multi Flex Chart with Grouping
@user_passes_test(ispromember,login_url="/rowers/promembership", @user_passes_test(ispromember,login_url="/rowers/promembership",
message="This functionality requires a Pro plan or higher", message="This functionality requires a Pro plan or higher",
@@ -6740,6 +6826,7 @@ def workouts_view(request,message='',successmessage='',
enddate = startdate enddate = startdate
startdate = s startdate = s
startdatestring = startdate.strftime('%Y-%m-%d') startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d')
@@ -6755,6 +6842,10 @@ def workouts_view(request,message='',successmessage='',
except ValueError: except ValueError:
activity_enddate = enddate activity_enddate = enddate
g_startdate = activity_startdate
g_enddate = activity_enddate
if teamid: if teamid:
try: try:
theteam = Team.objects.get(id=teamid) theteam = Team.objects.get(id=teamid)
@@ -6771,6 +6862,7 @@ def workouts_view(request,message='',successmessage='',
team=theteam, team=theteam,
startdatetime__gte=activity_startdate, startdatetime__gte=activity_startdate,
startdatetime__lte=activity_enddate, startdatetime__lte=activity_enddate,
duplicate=False,
privacy='visible').order_by("-date", "-starttime") privacy='visible').order_by("-date", "-starttime")
elif theteam.viewing == 'coachonly': elif theteam.viewing == 'coachonly':
workouts = Workout.objects.filter( workouts = Workout.objects.filter(
@@ -6782,6 +6874,7 @@ def workouts_view(request,message='',successmessage='',
team=theteam,user=r, team=theteam,user=r,
startdatetime__gte=activity_startdate, startdatetime__gte=activity_startdate,
enddatetime__lte=activity_enddate, enddatetime__lte=activity_enddate,
duplicate=False,
privacy='visible').order_by("-startdatetime") privacy='visible').order_by("-startdatetime")
@@ -6797,6 +6890,7 @@ def workouts_view(request,message='',successmessage='',
user=r, user=r,
startdatetime__gte=activity_startdate, startdatetime__gte=activity_startdate,
startdatetime__lte=activity_enddate, startdatetime__lte=activity_enddate,
duplicate=False,
privacy='visible').order_by("-startdatetime") privacy='visible').order_by("-startdatetime")
else: else:
theteam = None theteam = None
@@ -6806,9 +6900,18 @@ def workouts_view(request,message='',successmessage='',
startdatetime__lte=enddate).order_by("-date", "-starttime") startdatetime__lte=enddate).order_by("-date", "-starttime")
g_workouts = Workout.objects.filter( g_workouts = Workout.objects.filter(
user=r, user=r,
duplicate=False,
startdatetime__gte=activity_startdate, startdatetime__gte=activity_startdate,
startdatetime__lte=activity_enddate).order_by("-startdatetime") startdatetime__lte=activity_enddate).order_by("-startdatetime")
if len(g_workouts) == 0:
g_workouts = Workout.objects.filter(
user=r,
startdatetime__gte=timezone.now()-timedelta(days=15)).order_by("-startdatetime")
g_enddate = timezone.now()
g_startdate = (timezone.now()-timedelta(days=15))
if rankingonly: if rankingonly:
workouts = workouts.exclude(rankingpiece=False) workouts = workouts.exclude(rankingpiece=False)
@@ -6846,9 +6949,10 @@ def workouts_view(request,message='',successmessage='',
else: else:
stack='type' stack='type'
script,div = interactive_activitychart(g_workouts, script,div = interactive_activitychart(g_workouts,
activity_startdate, g_startdate,
activity_enddate, g_enddate,
stack=stack) stack=stack)
messages.info(request,successmessage) messages.info(request,successmessage)
@@ -9845,6 +9949,11 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
except KeyError: except KeyError:
rankingpiece =- Workout.objects.get(id=id).rankingpiece rankingpiece =- Workout.objects.get(id=id).rankingpiece
try:
duplicate = form.cleaned_data['duplicate']
except KeyError:
duplicate = Workout.objects.get(id=id).duplicate
if private: if private:
privacy = 'private' privacy = 'private'
else: else:
@@ -9884,6 +9993,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
row.duration = duration row.duration = duration
row.distance = distance row.distance = distance
row.boattype = boattype row.boattype = boattype
row.duplicate = duplicate
row.privacy = privacy row.privacy = privacy
row.rankingpiece = rankingpiece row.rankingpiece = rankingpiece
row.timezone = thetimezone row.timezone = thetimezone
@@ -10268,6 +10378,11 @@ def workout_add_chart_view(request,id,plotnr=1):
# The page where you select which Strava workout to import # The page where you select which Strava workout to import
@login_required() @login_required()
def workout_stravaimport_view(request,message="",userid=0): def workout_stravaimport_view(request,message="",userid=0):
try:
thetoken = strava_open(request.user)
except NoTokenError:
return HttpResponseRedirect("/rowers/me/stravaauthorize/")
res = stravastuff.get_strava_workout_list(request.user) res = stravastuff.get_strava_workout_list(request.user)
r = getrequestrower(request,userid=userid) r = getrequestrower(request,userid=userid)
@@ -10277,6 +10392,7 @@ def workout_stravaimport_view(request,message="",userid=0):
r = getrower(request.user) r = getrower(request.user)
if (res.status_code != 200): if (res.status_code != 200):
if (res.status_code == 401): if (res.status_code == 401):
r = getrower(request.user) r = getrower(request.user)
@@ -10420,15 +10536,7 @@ def workout_runkeeperimport_view(request,message="",userid=0):
def workout_underarmourimport_view(request,message="",userid=0): def workout_underarmourimport_view(request,message="",userid=0):
res = underarmourstuff.get_underarmour_workout_list(request.user) res = underarmourstuff.get_underarmour_workout_list(request.user)
if (res.status_code != 200): if (res.status_code != 200):
if (res.status_code == 401): return HttpResponseRedirect("/rowers/me/underarmourauthorize/")
r = getrower(request.user)
if (r.underarmourtoken == '') or (r.underarmourtoken is None):
s = "Token doesn't exist. Need to authorize"
return HttpResponseRedirect("/rowers/me/underarmourauthorize/")
message = "Something went wrong in workout_underarmourimport_view"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
workouts = [] workouts = []
items = res.json()['_embedded']['workouts'] items = res.json()['_embedded']['workouts']
@@ -10778,7 +10886,7 @@ def workout_getimportview(request,externalid,source = 'c2'):
if strokedata.empty: if strokedata.empty:
distance = data['distance'] distance = data['distance']
c2id = data['id'] c2id = data['id']
workouttype = data['type'] workouttype = mytypes.c2mappinginv[data['type']]
verified = data['verified'] verified = data['verified']
startdatetime = iso8601.parse_date(data['date']) startdatetime = iso8601.parse_date(data['date'])
weightclass = data['weight_class'] weightclass = data['weight_class']
@@ -10827,7 +10935,7 @@ def workout_getimportview(request,externalid,source = 'c2'):
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
# strokdata not empty - continue # strokedata not empty - continue
id,message = importsources[source].add_workout_from_data( id,message = importsources[source].add_workout_from_data(
request.user, request.user,
externalid,data, externalid,data,
@@ -15302,6 +15410,26 @@ def plannedsession_edit_view(request,id=0,userid=0):
}) })
@login_required()
def plannedsession_detach_view(request,id=0,psid=0):
r = getrequestrower(request)
try:
ps = PlannedSession.objects.get(id=psid)
except PlannedSession.DoesNotExist:
raise Http404("Planned Session does not exist")
w = get_workout(id)
if (checkworkoutuser(request.user,w)==False):
return HttpResponseForbidden("Permission Error")
remove_workout_plannedsession(w,ps)
url = reverse(plannedsession_view,kwargs={'id':psid})
return HttpResponseRedirect(url)
@login_required() @login_required()
def plannedsession_view(request,id=0,userid=0): def plannedsession_view(request,id=0,userid=0):