diff --git a/rowers/c2stuff.py b/rowers/c2stuff.py index 5693e760..e5646ac9 100644 --- a/rowers/c2stuff.py +++ b/rowers/c2stuff.py @@ -266,7 +266,10 @@ def summaryfromsplitdata(splitdata,data,filename,sep='|'): idist = interval['distance'] itime = interval['time']/10. ipace = 500.*itime/idist - ispm = interval['stroke_rate'] + try: + ispm = interval['stroke_rate'] + except KeyError: + ispm = 0 try: irest_time = interval['rest_time']/10. except KeyError: diff --git a/rowers/dataprepnodjango.py b/rowers/dataprepnodjango.py index 7dad7bb2..8ac8692f 100644 --- a/rowers/dataprepnodjango.py +++ b/rowers/dataprepnodjango.py @@ -241,6 +241,7 @@ def add_c2_stroke_data_db(strokedata,workoutid,starttimeunix,csvfilename, hr = strokedata.ix[:,'hr'] except KeyError: hr = 0*spm + pace = strokedata.ix[:,'p']/10. pace = np.clip(pace,0,1e4) pace = pace.replace(0,300) @@ -1241,7 +1242,7 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, with engine.connect() as conn, conn.begin(): try: data.to_sql('strokedata',engine,if_exists='append',index=False) - except OperationalError: + except: data.drop(columns=['rhythm'],inplace=True) data.to_sql('strokedata',engine,if_exists='append',index=False) diff --git a/rowers/forms.py b/rowers/forms.py index 2bb383e7..f4877106 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -7,12 +7,14 @@ from django.contrib.auth.models import User from django.contrib.admin.widgets import AdminDateWidget from django.forms.extras.widgets import SelectDateWidget from django.utils import timezone,translation -from django.forms import ModelForm +from django.forms import ModelForm, Select import dataprep import types import datetime from django.forms import formset_factory from utils import landingpages +from metrics import axes + # login form class LoginForm(forms.Form): @@ -29,6 +31,7 @@ class EmailForm(forms.Form): message = forms.CharField() + # Upload the CrewNerd Summary CSV class CNsummaryForm(forms.Form): file = forms.FileField(required=True,validators=[must_be_csv]) @@ -919,3 +922,70 @@ class VirtualRaceSelectForm(forms.Form): self.fields['country'] = forms.ChoiceField( choices = get_countries(),initial='All' ) + +class FlexOptionsForm(forms.Form): + includereststrokes = forms.BooleanField(initial=True, required = False, + label='Include Rest Strokes') + plotchoices = ( + ('line','Line Plot'), + ('scatter','Scatter Plot'), + ) + plottype = forms.ChoiceField(choices=plotchoices,initial='scatter', + label='Chart Type') + + +class FlexAxesForm(forms.Form): + axchoices = ( + (ax[0],ax[1]) for ax in axes if ax[0] not in ['cumdist','None'] + ) + + + yaxchoices = ( + (ax[0], ax[1]) for ax in axes if ax[0] not in ['cumdist','distance','time'] + ) + + yaxchoices2 = ( + (ax[0], ax[1]) for ax in axes if ax[0] not in ['cumdist','distance','time'] + ) + + + xaxis = forms.ChoiceField( + choices=axchoices,label='X-Axis',required=True) + yaxis1 = forms.ChoiceField( + choices=yaxchoices,label='Left Axis',required=True) + yaxis2 = forms.ChoiceField( + choices=yaxchoices2,label='Right Axis',required=True) + + def __init__(self,request,*args,**kwargs): + super(FlexAxesForm, self).__init__(*args, **kwargs) + + rower = Rower.objects.get(user=request.user) + + axchoicespro = ( + ('',ax[1]) if ax[4] == 'pro' and ax[0] else (ax[0],ax[1]) for ax in axes + ) + + axchoicesbasicx = [] + axchoicesbasicy = [] + + for ax in axes: + if ax[4] != 'pro' and ax[0] != 'cumdist': + if ax[0] != 'None': + axchoicesbasicx.insert(0,(ax[0],ax[1])) + if ax[0] not in ['cumdist','distance','time']: + axchoicesbasicy.insert(0,(ax[0],ax[1])) + else: + if ax[0] != 'None': + axchoicesbasicx.insert(0,('None',ax[1]+' (PRO)')) + if ax[0] not in ['cumdist','distance','time']: + axchoicesbasicy.insert(0,('None',ax[1]+' (PRO)')) + + + if rower.rowerplan == 'basic': + self.fields['xaxis'].choices = axchoicesbasicx + self.fields['yaxis1'].choices = axchoicesbasicy + self.fields['yaxis2'].choices = axchoicesbasicy + + + + diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 1e9c7b5d..2cf6d157 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -181,10 +181,11 @@ def interactive_boxchart(datadf,fieldname,extratitle=''): tools=TOOLS, toolbar_location="above", toolbar_sticky=False, - x_mapper_type='datetime') + x_mapper_type='datetime',plot_width=920) yrange1 = Range1d(start=yaxminima[fieldname],end=yaxmaxima[fieldname]) plot.y_range = yrange1 + plot.sizing_mode = 'scale_width' plot.xaxis.axis_label = 'Date' plot.yaxis.axis_label = axlabels[fieldname] @@ -299,6 +300,7 @@ def interactive_activitychart(workouts,startdate,enddate,stack='type'): toolbar_location = None, ) + for legend in p.legend: new_items = [] for legend_item in legend.items: @@ -311,6 +313,7 @@ def interactive_activitychart(workouts,startdate,enddate,stack='type'): p.legend.location = "top_left" p.legend.background_fill_alpha = 0.7 + p.sizing_mode = 'scale_width' p.yaxis.axis_label = 'Minutes' @@ -411,6 +414,7 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False): # add watermark plot.extra_y_ranges = {"watermark": watermarkrange} plot.extra_x_ranges = {"watermark": watermarkrange} + plot.sizing_mode = 'scale_width' plot.image_url([watermarkurl],watermarkx,watermarky, watermarkw,watermarkh, @@ -625,6 +629,8 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False): ), plot]) + layout.sizing_mode = 'scale_width' + script, div = components(layout) js_resources = INLINE.render_js() css_resources = INLINE.render_css() @@ -748,9 +754,10 @@ def fitnessmetric_chart(fitnessmetrics,user,workoutmode='rower'): ) plot.xaxis.major_label_orientation = pi/4 + plot.sizing_mode = 'scale_width' plot.y_range = Range1d(0,1.5*max(power4min)) - plot.title.text = 'Power levels from workouts '+user.first_name + plot.title.text = 'Power levels ('+workoutmode+') from workouts '+user.first_name hover = plot.select(dict(type=HoverTool)) @@ -833,6 +840,7 @@ def interactive_histoall(theworkouts): plot.xaxis.axis_label = "Power (W)" plot.yaxis.axis_label = "% of strokes" plot.y_range = Range1d(0,1.05*max(hist_norm)) + hover = plot.select(dict(type=HoverTool)) @@ -850,6 +858,7 @@ def interactive_histoall(theworkouts): plot.add_layout(LinearAxis(y_range_name="fraction", axis_label="Cumulative % of strokes"),'right') + plot.sizing_mode = 'scale_width' script, div = components(plot) return [script,div] @@ -985,7 +994,7 @@ def course_map(course): ) div = """ -

 

+
""" return script,div @@ -1483,8 +1492,12 @@ def interactive_agegroupcpchart(age,normalized=False): x_axis_type = 'log' y_axis_type = 'linear' - plot = Figure(plot_width=900,x_axis_type=x_axis_type) + TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair' + plot = Figure(plot_width=900,x_axis_type=x_axis_type, + tools=TOOLS) + plot.sizing_mode = 'scale_width' + plot.line('duration','fitpowerfh',source=source, legend='Female HW',color='blue') plot.line('duration','fitpowerfl',source=source, @@ -1574,6 +1587,7 @@ def interactive_otwcpchart(powerdf,promember=0,rowername=""): # add watermark plot.extra_y_ranges = {"watermark": watermarkrange} + plot.sizing_mode = 'scale_width' plot.image_url([watermarkurl],1.8*max(thesecs),watermarky, watermarkw,watermarkh, @@ -1664,6 +1678,7 @@ def interactive_agegroup_plot(df,distance=2000,duration=None, TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair' plot = Figure(tools=TOOLS,plot_width=900) + plot.sizing_mode='scale_width' plot.circle('age','power',source=source,fill_color='red',size=15, legend='World Record') @@ -1861,6 +1876,7 @@ def interactive_cpchart(rower,thedistances,thesecs,theavpower, # add watermark plot.extra_y_ranges = {"watermark": watermarkrange} + plot.sizing_mode = 'scale_width' plot.image_url([watermarkurl],1.8*max(thesecs),watermarky, watermarkw,watermarkh, @@ -2043,6 +2059,7 @@ def interactive_windchart(id=0,promember=0): plot.xaxis.axis_label = "Distance (m)" plot.yaxis.axis_label = "Wind Speed (m/s)" plot.y_range = Range1d(-7,7) + plot.sizing_mode = 'scale_width' plot.extra_y_ranges = {"winddirection": Range1d(start=0,end=360)} @@ -2110,6 +2127,7 @@ def interactive_streamchart(id=0,promember=0): plot.xaxis.axis_label = "Distance (m)" plot.yaxis.axis_label = "River Current (m/s)" plot.y_range = Range1d(-2,2) + plot.sizing_mode = 'scale_width' script, div = components(plot) @@ -2179,6 +2197,7 @@ def interactive_chart(id=0,promember=0,intervaldata = {}): plot.line('time','pace',source=source,legend="Pace",name="pace") plot.title.text = row.name plot.title.text_font_size=value("1.0em") + plot.sizing_mode = 'scale_width' plot.xaxis.axis_label = "Time" plot.yaxis.axis_label = "Pace (/500m)" plot.xaxis[0].formatter = DatetimeTickFormatter( @@ -2348,7 +2367,7 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type, tools=TOOLS, toolbar_location="above", - toolbar_sticky=False) #,plot_width=500,plot_height=500) + toolbar_sticky=False,plot_width=920) # add watermark plot.extra_y_ranges = {"watermark": watermarkrange} @@ -2356,6 +2375,7 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', plot.title.text = title plot.title.text_font_size=value("1.0em") + plot.sizing_mode = 'scale_width' plot.image_url([watermarkurl],watermarkx,watermarky, watermarkw,watermarkh, @@ -2581,6 +2601,7 @@ def interactive_cum_flex_chart2(theworkouts,promember=0, # add watermark plot.extra_y_ranges = {"watermark": watermarkrange} plot.extra_x_ranges = {"watermark": watermarkrange} + plot.sizing_mode = 'scale_width' plot.image_url([watermarkurl],watermarkx,watermarky, watermarkw,watermarkh, @@ -2764,8 +2785,11 @@ def interactive_cum_flex_chart2(theworkouts,promember=0, title="Max Work per Stroke",callback=callback) callback.args["maxwork"] = slider_work_max - distmax = 100+100*int(datadf['distance'].max()/100.) - + try: + distmax = 100+100*int(datadf['distance'].max()/100.) + except KeyError: + distmax = 1000. + slider_dist_min = Slider(start=0,end=distmax,value=0,step=1, title="Min Distance",callback=callback) callback.args["mindist"] = slider_dist_min @@ -2785,6 +2809,8 @@ def interactive_cum_flex_chart2(theworkouts,promember=0, ), plot]) + layout.sizing_mode = 'scale_width' + script, div = components(layout) js_resources = INLINE.render_js() css_resources = INLINE.render_css() @@ -2984,8 +3010,6 @@ def interactive_flex_chart2(id=0,promember=0, - sizing_mode = 'fixed' # 'scale_width' also looks nice with this example - plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type, tools=TOOLS, toolbar_sticky=False @@ -2996,6 +3020,7 @@ def interactive_flex_chart2(id=0,promember=0, # add watermark plot.extra_y_ranges = {"watermark": watermarkrange} plot.extra_x_ranges = {"watermark": watermarkrange} + plot.sizing_mode = 'scale_width' plot.image_url([watermarkurl],watermarkx,watermarky, watermarkw,watermarkh, @@ -3322,7 +3347,9 @@ def interactive_flex_chart2(id=0,promember=0, slider_work_max, ], ), - plot]) + plot]) + + layout.sizing_mode = 'scale_width' script, div = components(layout) js_resources = INLINE.render_js() @@ -3489,6 +3516,8 @@ def thumbnail_flex_chart(rowdata,id=0,promember=0, +# plot.sizing_mode = 'scale_width' + plot.sizing_mode = 'fixed' plot.toolbar.logo = None plot.toolbar_location = None #plot.yaxis.visible = False @@ -3598,6 +3627,7 @@ def interactive_bar_chart(id=0,promember=0): # add watermark plot.extra_y_ranges = {"watermark": watermarkrange} plot.extra_x_ranges = {"watermark": watermarkrange} + plot.sizing_mode = 'scale_width' plot.image_url([watermarkurl],0.01,0.99, watermarkw,watermarkh, @@ -3765,6 +3795,7 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line', # add watermark plot.extra_y_ranges = {"watermark": watermarkrange} plot.extra_x_ranges = {"watermark": watermarkrange} + plot.sizing_mode = 'scale_width' plot.image_url([watermarkurl],0.05,0.9, watermarkw,watermarkh, @@ -4009,6 +4040,7 @@ def interactive_comparison_chart(id1=0,id2=0,xparam='distance',yparam='spm', # add watermark plot.extra_y_ranges = {"watermark": watermarkrange} plot.extra_x_ranges = {"watermark": watermarkrange} + plot.sizing_mode = 'scale_width' plot.image_url([watermarkurl],0.05,watermarky, watermarkw,watermarkh, @@ -4139,6 +4171,7 @@ def interactive_otw_advanced_pace_chart(id=0,promember=0): # add watermark plot.extra_y_ranges = {"watermark": watermarkrange} plot.extra_x_ranges = {"watermark": watermarkrange} + plot.sizing_mode = 'scale_width' plot.image_url([watermarkurl],watermarkx,watermarky, watermarkw,watermarkh, diff --git a/rowers/plannedsessions.py b/rowers/plannedsessions.py index da035b17..73f3a364 100644 --- a/rowers/plannedsessions.py +++ b/rowers/plannedsessions.py @@ -32,6 +32,34 @@ import courses from rowers.tasks import handle_check_race_course +def get_todays_micro(plan,thedate=date.today()): + thismicro = None + + thismacro = TrainingMacroCycle.objects.filter( + plan=plan, + startdate__lte = thedate, + enddate__gte = thedate + ) + + if thismacro: + thismeso = TrainingMesoCycle.objects.filter( + plan=thismacro[0], + startdate__lte = thedate, + enddate__gte = thedate + ) + + if thismeso: + thismicro = TrainingMicroCycle.objects.filter( + plan=thismeso[0], + startdate__lte = thedate, + enddate__gte = thedate + ) + + if thismicro: + thismicro = thismicro[0] + + return thismicro + # Low Level functions - to be called by higher level methods def add_workouts_plannedsession(ws,ps,r): result = 0 diff --git a/rowers/tasks.py b/rowers/tasks.py index 72f278e8..653c5513 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -1382,7 +1382,7 @@ def handle_makeplot(f1, f2, t, hrdata, plotnr, imagename, @app.task def handle_sendemail_invite(email, name, code, teamname, manager, debug=False,**kwargs): - fullemail = name + ' <' + email + '>' + fullemail = email subject = 'Invitation to join team ' + teamname siteurl = SITE_URL @@ -1414,7 +1414,7 @@ def handle_sendemailnewresponse(first_name, last_name, comment, workoutname, workoutid, commentid, debug=False,**kwargs): - fullemail = first_name + ' ' + last_name + ' <' + email + '>' + fullemail = email from_email = 'Rowsandall ' subject = 'New comment on workout ' + workoutname @@ -1451,7 +1451,7 @@ def handle_sendemailnewcomment(first_name, - fullemail = first_name + ' ' + last_name + ' <' + email + '>' + fullemail = email from_email = 'Rowsandall ' subject = 'New comment on workout ' + workoutname @@ -1480,7 +1480,7 @@ def handle_sendemailnewcomment(first_name, @app.task def handle_sendemail_request(email, name, code, teamname, requestor, id, debug=False,**kwargs): - fullemail = name + ' <' + email + '>' + fullemail = email subject = 'Request to join team ' + teamname from_email = 'Rowsandall ' @@ -1506,7 +1506,7 @@ def handle_sendemail_request(email, name, code, teamname, requestor, id, @app.task def handle_sendemail_request_accept(email, name, teamname, managername, debug=False,**kwargs): - fullemail = name + ' <' + email + '>' + fullemail = email subject = 'Welcome to ' + teamname from_email = 'Rowsandall ' @@ -1530,7 +1530,7 @@ def handle_sendemail_request_accept(email, name, teamname, managername, @app.task def handle_sendemail_request_reject(email, name, teamname, managername, debug=False,**kwargs): - fullemail = name + ' <' + email + '>' + fullemail = email subject = 'Your application to ' + teamname + ' was rejected' from_email = 'Rowsandall ' @@ -1553,7 +1553,7 @@ def handle_sendemail_request_reject(email, name, teamname, managername, @app.task def handle_sendemail_member_dropped(email, name, teamname, managername, debug=False,**kwargs): - fullemail = name + ' <' + email + '>' + fullemail = email subject = 'You were removed from ' + teamname from_email = 'Rowsandall ' @@ -1578,7 +1578,7 @@ def handle_sendemail_member_dropped(email, name, teamname, managername, def handle_sendemail_team_removed(email, name, teamname, managername, debug=False,**kwargs): - fullemail = name + ' <' + email + '>' + fullemail = email subject = 'You were removed from ' + teamname from_email = 'Rowsandall ' @@ -1602,7 +1602,7 @@ def handle_sendemail_team_removed(email, name, teamname, managername, @app.task def handle_sendemail_invite_reject(email, name, teamname, managername, debug=False,**kwargs): - fullemail = managername + ' <' + email + '>' + fullemail = email subject = 'Your invitation to ' + name + ' was rejected' from_email = 'Rowsandall ' @@ -1626,7 +1626,7 @@ def handle_sendemail_invite_reject(email, name, teamname, managername, @app.task def handle_sendemail_invite_accept(email, name, teamname, managername, debug=False,**kwargs): - fullemail = managername + ' <' + email + '>' + fullemail = email subject = 'Your invitation to ' + name + ' was accepted' from_email = 'Rowsandall ' diff --git a/rowers/tasks_standalone.py b/rowers/tasks_standalone.py index 939fead5..c0d7da11 100644 --- a/rowers/tasks_standalone.py +++ b/rowers/tasks_standalone.py @@ -15,7 +15,7 @@ from django.contrib.auth.models import User @app.task -def addcomment2(userid,id): +def addcomment2(userid,id,debug=False): time.sleep(5) # w = Workout.objects.get(id=id) diff --git a/rowers/templates/400.html b/rowers/templates/400.html deleted file mode 100644 index c5332ca6..00000000 --- a/rowers/templates/400.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends "basenofilters.html" %} -{% load staticfiles %} -{% load rowerfilters %} - -{% block title %}Rowsandall - Bad Request {% endblock %} - -{% block content %} - -
-

Bad Request

-

-HTTP Error 400 Bad Request. -

-
- -{% endblock %} diff --git a/rowers/templates/403.html b/rowers/templates/403.html deleted file mode 100644 index 5a17218e..00000000 --- a/rowers/templates/403.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "basenofilters.html" %} -{% load staticfiles %} -{% load rowerfilters %} - -{% block title %}Rowsandall - forbidden {% endblock %} - -{% block content %} - -
-

Forbidden

-

- Access forbidden. You probably tried to access functionality on a workout, - planned session - or chart that is not owned by you. -

-
- -{% endblock %} diff --git a/rowers/templates/404.html b/rowers/templates/404.html deleted file mode 100644 index 482eaeb3..00000000 --- a/rowers/templates/404.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends "basenofilters.html" %} -{% load staticfiles %} - -{% block title %}Rowsandall - not found {% endblock %} - -{% block content %} - -
-

Error 404 Page not found

-

-We could not find the page on our server. -

-
- -{% endblock %} diff --git a/rowers/templates/500.html b/rowers/templates/500.html deleted file mode 100644 index 252ea236..00000000 --- a/rowers/templates/500.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends "basenofilters.html" %} -{% load staticfiles %} - -{% block title %}Rowsandall - error {% endblock %} - -{% block content %} - -
-

Error 500 Internal Server Error

-

- The site reported an internal server error. The site developer has been - notified automatically with a full error report. You can help the developer - by reporting an issue on Bitbucket using the button below. -

- - -
- -{% endblock %} diff --git a/rowers/templates/502.html b/rowers/templates/502.html deleted file mode 100644 index edd8094e..00000000 --- a/rowers/templates/502.html +++ /dev/null @@ -1,578 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Rowsandall - - - - - - - - - - -
-
-   -
-
- -
-
-
- -
- -
- -
-
-

Free Data and Analysis. For Rowers. By Rowers.

-
-
- -
-
- -

login

- -
-
- -

 

- -
-
-
- - - -
-
-
- - -
-
- -

Register (free)

- -
-
- -

 

- -
-
- -

 

- -
-
- -

 

- -
-
- -

 

- -
-
- - -

 

- - -
-
- - -
-
- - - - - -
-
- - - - -
-

Error 502 Bad Gateway

-

- No valid server response received. This can have multiple reasons, - including time-outs or reaching the capacity limit. -

- -
- - -
-
- -
- - - -
- -
-
- -
-
- -
-
- -
-
- -
-
- -
- - - -
- -
- - - - - - - - - - - - -
- -
- « -
- - -
-
- -

Versions

-
-
- - loading -
- -
-
- - - - - -
-
- -

Settings from rowsandall_app.settings_dev

-
-
- - loading -
- -
-
- - - -
-
- -

Headers

-
-
- - loading -
- -
-
- - - -
-
- -

Request

-
-
- - loading -
- -
-
- - - -
-
- -

SQL queries from 0 connections

-
-
- - loading -
- -
-
- - - -
-
- -

Static files (1470 found, 0 used)

-
-
- - loading -
- -
-
- - - -
-
- -

Templates (3 rendered)

-
-
- - loading -
- -
-
- - - -
-
- -

Cache calls from 1 backend

-
-
- - loading -
- -
-
- - - -
-
- -

Signals

-
-
- - loading -
- -
-
- - - -
-
- -

Log messages

-
-
- - loading -
- -
-
- - - - -
-
- - diff --git a/rowers/templates/about_us.html b/rowers/templates/about_us.html index b2b2d03c..1d64d9b4 100644 --- a/rowers/templates/about_us.html +++ b/rowers/templates/about_us.html @@ -1,10 +1,9 @@ - {% extends "base.html" %} - {% block title %}Rowsandall - About us{% endblock title %} - {% block content %} +{% extends "newbase.html" %} +{% block title %}Rowsandall - About us{% endblock title %} +{% block main %} {% load rowerfilters %} -

Welcome to Rowsandall.com

Rowsandall.com is an online tool for indoor and On The Water (OTW) rowers. It accepts workout data from a number of devices and applications. It @@ -78,19 +77,13 @@ here. -

- - -
-

Credits

The project is based on python plotting code by Greg Smith (https://quantifiedrowing.wordpress.com/) and inspired by the RowPro Dan Burpee spreadsheet (http://www.sub7irc.com/RP_Split_Template.zip).

-
-
+

Advanced Analysis, Coaching and Planning (Premium Features)

@@ -119,12 +112,9 @@ and inspired by the RowPro Dan Burpee spreadsheet -
-
-
- {% if user.rower.rowerplan == 'basic' and user.rower.protrialexpires|date_dif == 1 %} -

Free Trial

+{% if user.rower.rowerplan == 'basic' and user.rower.protrialexpires|date_dif == 1 %} +

Free Trial

You qualify for a 14 day free trial. No credit card needed. Try out Pro membership for two weeks. Click the button below to @@ -188,5 +178,10 @@ and inspired by the RowPro Dan Burpee spreadsheet

If, for any reason, you are not happy with your Pro membership, please let me know through the contact form. I will contact you as soon as possible to discuss how we can make things better.

-
- {% endblock content %} + +{% endblock main %} + +{% block sidebar %} +{% include 'menu_help.html' %} +{% endblock %} + diff --git a/rowers/templates/agegroupchart.html b/rowers/templates/agegroupchart.html index f3ecf8b3..606c2f84 100644 --- a/rowers/templates/agegroupchart.html +++ b/rowers/templates/agegroupchart.html @@ -1,52 +1,44 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} -{% block title %}Rowsandall {% endblock %} +{% block title %}Rowsandall Age Group Records{% endblock %} -{% block content %} +{% block main %} - - + + - {{ interactiveplot |safe }} - - - +{{ interactiveplot |safe }} -
+

Interactive Plot

- -

Interactive Plot

- -

This chart shows the Indoor Rower World Records for your gender and - weight class. The red dots are the official records, and hovering - over them with your mouse shows you the name of the record holder. - The blue line is a fit to the data, which is used by rowsandall.com - to calculate your performance assessment. +

    +
  • +

    This chart shows the + + Indoor Rower World Records + for your gender and + weight class. The red dots are the official records, and hovering + over them with your mouse shows you the name of the record holder. + The blue line is a fit to the data, which is used by rowsandall.com + to calculate your performance assessment. +

    +
  • +
  • {{ the_div|safe }} +
  • + -
+ {% endblock %} + + +{% block sidebar %} +{% include 'menu_analytics.html' %} +{% endblock %} diff --git a/rowers/templates/agegroupcp.html b/rowers/templates/agegroupcp.html index 2bebbe61..5c466d40 100644 --- a/rowers/templates/agegroupcp.html +++ b/rowers/templates/agegroupcp.html @@ -1,47 +1,30 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} -{% block title %}Rowsandall {% endblock %} +{% block title %}Rowsandall Age Group CP{% endblock %} -{% block content %} +{% block main %} - - - - {{ interactiveplot |safe }} - - - + + -
+{{ interactiveplot |safe }} -

Interactive Plot

- +

Interactive Plot

+
    +
  • {{ the_div|safe }} - -
+ + {% endblock %} + +{% block sidebar %} +{% include 'menu_analytics.html' %} +{% endblock %} diff --git a/rowers/templates/analysis.html b/rowers/templates/analysis.html index f91d3a64..a4454ad7 100644 --- a/rowers/templates/analysis.html +++ b/rowers/templates/analysis.html @@ -1,171 +1,154 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Rowsandall - Analysis {% endblock %} -{% block content %} +{% block main %} -

Analysis

+

Analysis

Functionality to analyze multiple workouts.

-
-
-

Basic

-
-

- Ranking Pieces

-

Analyze your Concept2 ranking pieces over a date range and predict your pace on other pieces.

-
-
-

- Stroke Analysis -

-

- Plot all strokes in a date range and analyze several parameters (Power, Pace, SPM, Heart Rate). -

-
-
-

- Analysis Feature 3 -

-

- Reserved for future functionality. -

-
- -
- -
-

Pro

-
+
    +
  • +

    Ranking Pieces

    + +
    + Ranking Piece +
    +

    - {% if user|is_promember %} - Power Histogram - {% else %} - Power Histogram - {% endif %} + Analyze your Concept2 ranking pieces over a date range and predict your pace on other pieces.

    +
  • +
  • +

    Stroke Analysis

    + +
    + Stroke Analysis +
    +
    +

    + Plot all strokes in a date range and analyze several parameters (Power, Pace, SPM, Heart Rate). +

    +
  • +
  • +

    Power Histogram

    + {% if user|is_promember %} + + {% else %} + + {% endif %} +
    + Power Histogram +
    +

    Plot a power histogram of all your strokes over a date range.

    -
-
-

- {% if user|is_promember %} - Statistics - {% else %} - Statistics + +

  • +

    Statistics

    + {% if user|is_promember %} + + {% else %} + {% endif %} -

    +
    + Statistics +
    +

    - BETA: Statistics of stroke metrics over a date range -

    -
  • -
    -

    - {% if user|is_promember %} - Box Chart - {% else %} - Box Chart - {% endif %} + Statistics of stroke metrics over a date range

    + +
  • +

    Box Chart

    + {% if user|is_promember %} + + {% else %} + + {% endif %} +
    + Box Chart +
    +
    +

    BETA: Box Chart Statistics of stroke metrics over a date range

    -
  • -
    + +
  • +

    OTW Critical Power

    + {% if user|is_promember %} + + {% else %} + + {% endif %} +
    + OTW Critical Power +
    +
    +

    + Analyse power vs piece duration to make predictions. For On-The-Water rowing. +

    +
  • +
  • +

    OTE Critical Power

    + {% if user|is_promember %} + + {% else %} + + {% endif %} +
    + OTE Critical Power +
    +
    -
    -
    -
    -

    - - Ranking Pieces 2.0

    -

    Analyze your Concept2 ranking pieces over a date range and predict your pace on other pieces.

    -
    -
    +

    + Analyse power vs piece duration to make predictions, for erg pieces. +

    +
  • +
  • +

    Trend Flex

    + {% if user|is_promember %} + + {% else %} + + {% endif %} +
    + Trend Flex +
    +
    -
    -
    -

    - {% if user|is_promember %} - OTW Critical Power - {% else %} - OTW Critical Power - {% endif %} -

    -

    - Analyse power vs piece duration to make predictions. For On-The-Water rowing. -

    -
    -
    -

    - {% if user|is_promember %} - Multi Compare - {% else %} - Multi Compare - {% endif %} -

    -

    - Compare many workouts -

    -
    -
    -

    - {% if user|is_promember %} - Trend Flex - {% else %} - Trend Flex - {% endif %} -

    -

    - Select workouts and make X-Y charts of averages over various metrics -

    -
    -
    -
    -
    -

    - {% if user|is_promember %} - OTE Critical Power - {% else %} - OTE Critical Power - {% endif %} -

    -

    - Analyse power vs piece duration to make predictions, for erg pieces. -

    -
    -
    -

    - {% if user|is_planmember %} - Power Progress - {% else %} - Power Progress - {% endif %} -

    -

    - Monitoring power duration evidence from all your workouts. Feel free to explore. -

    -
    -
    -

    - {% if user|is_planmember %} - The Labs - {% else %} - The Labs - {% endif %} -

    -

    - Undisclosed new functionality. This is still experimental and - may not make sense. -

    -
    -
    -
  • +

    + Select workouts and make X-Y charts of averages over various metrics +

    + +
  • +

    Power Progress

    + {% if user|is_planmember %} + + {% else %} + + {% endif %} +
    + Power Progress +
    +
    +

    + Monitoring power duration evidence from all your workouts. Feel free to explore. +

    +
  • + +{% endblock %} + +{% block sidebar %} +{% include 'menu_analytics.html' %} {% endblock %} diff --git a/rowers/templates/async_tasks.html b/rowers/templates/async_tasks.html index 552f6b45..812221fa 100644 --- a/rowers/templates/async_tasks.html +++ b/rowers/templates/async_tasks.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -56,7 +56,7 @@ {% endblock %} -{% block content %} +{% block main %}

    Your Tasks Status

    @@ -117,4 +117,8 @@ +{% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} {% endblock %} diff --git a/rowers/templates/boxplot.html b/rowers/templates/boxplot.html index 346a780e..5d4b313f 100644 --- a/rowers/templates/boxplot.html +++ b/rowers/templates/boxplot.html @@ -1,10 +1,10 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Rowsandall Box Plot {% endblock %} -{% block content %} +{% block main %} - - -
    -

    Box Chart

    -
    -
    +

    Box Chart

    +
      +
    • +
      {{ the_div|safe }}
      -
    -
    -
    -
    + +
  • + {% csrf_token %} {{ chartform.as_table }}
    -
    -

    - -

    -
    +

    + +

  • -
    -
    + +
  • - You can use the form above to change the metric or filter the data. + You can use the form to change the metric or filter the data. Set Min SPM and Max SPM to select only strokes in a certain range of stroke rates. Set Work per Stroke to a minimum value to remove "paddle" strokes or turns.

    -
  • -
    -
    + + {% endblock %} @@ -92,3 +68,7 @@ {% endblock %} + +{% block sidebar %} +{% include 'menu_analytics.html' %} +{% endblock %} diff --git a/rowers/templates/brochure.html b/rowers/templates/brochure.html index 6beddbad..b1b1f166 100644 --- a/rowers/templates/brochure.html +++ b/rowers/templates/brochure.html @@ -1,5 +1,5 @@ - {% extends "base.html" %} +{% extends "newbase.html" %} {% block title %}Rowsandall Brochure{% endblock title %} {% block meta %} {% endblock meta %} - {% block content %} +{% block main %} -
    -

    Read our Brochure

    +

    Read our Brochure

    -
    - - object can't be rendered - -
    - +
    + + object can't be rendered +
    + +{% endblock %} - {% endblock content %} +{% block sidebar %} +{% include 'menu_help.html' %} +{% endblock %} diff --git a/rowers/templates/c2_list_import2.html b/rowers/templates/c2_list_import2.html index 033994e4..c88715aa 100644 --- a/rowers/templates/c2_list_import2.html +++ b/rowers/templates/c2_list_import2.html @@ -1,68 +1,74 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Workouts{% endblock %} -{% block content %} +{% block main %}

    Available on C2 Logbook

    -{% if workouts %} - -
    -

    This imports all workouts that have not been imported to rowsandall.com. +

      + {% if workouts %} +
    • + Import all NEW +

      This imports all workouts that have not been imported to rowsandall.com. The action may take a longer time to process, so please be patient. Click on Import in the list below to import an individual workout.

      -
    - -
    - {% if page > 1 %} - < + +
  • +

    + + {% if page > 1 %} + + + + {% endif %} + + + + +

    +
  • +
  • + + + + + + + + + + + + + + + {% for workout in workouts %} + + + + + + + + + + + + {% endfor %} + +
    Import Date/Time Duration Total Distance Type Source Comment New
    + Import{{ workout|lookup:'starttime' }}{{ workout|lookup:'duration' }}{{ workout|lookup:'distance' }}{{ workout|lookup:'rowtype' }}{{ workout|lookup:'source' }}{{ workout|lookup:'comment' }} + {{ workout|lookup:'new' }} +
    +
  • {% else %} -   +

    No workouts found

    {% endif %} -
    -
    - > -
    - -
    - - - - - - - - - - - - - - - {% for workout in workouts %} - - - - - - - - - - - - {% endfor %} - -
    Import Date/Time Duration Total Distance Type Source Comment New
    - Import{{ workout|lookup:'starttime' }}{{ workout|lookup:'duration' }}{{ workout|lookup:'distance' }}{{ workout|lookup:'rowtype' }}{{ workout|lookup:'source' }}{{ workout|lookup:'comment' }} - {{ workout|lookup:'new' }} -
    -
    -{% else %} -

    No workouts found

    -{% endif %} + +{% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} {% endblock %} diff --git a/rowers/templates/cn_form.html b/rowers/templates/cn_form.html index 354a1694..5a32d235 100644 --- a/rowers/templates/cn_form.html +++ b/rowers/templates/cn_form.html @@ -1,25 +1,25 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% block title %}CrewNerd Summary loading{% endblock %} -{% block content %} -
    -
    -

    Upload Workout Summary File (CrewNerd)

    - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. -

    - {% endif %} - - - {{ form.as_table }} -
    - {% csrf_token %} -
    - -
    -
    +{% block main %} + +

    Upload Workout Summary File (CrewNerd)

    + {% if form.errors %} +

    + Please correct the error{{ form.errors|pluralize }} below. +

    + {% endif %} + + + {{ form.as_table }} +
    + {% csrf_token %} +
    {% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/course_edit_view.html b/rowers/templates/course_edit_view.html index 50668820..436a07a7 100644 --- a/rowers/templates/course_edit_view.html +++ b/rowers/templates/course_edit_view.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block scripts %} @@ -7,49 +7,32 @@ {% block title %}{{ course.name }} {% endblock %} {% block og_title %}{{ course.name }} {% endblock %} -{% block content %} -
    -
    - {% if nosessions %} - Delete - {% else %} - - Update - {% endif %} -
    -
    - {% if course.manager == rower %} - View Course - {% else %} -   - {% endif %} -
    -
    - Courses -
    -
    -
    +{% block main %} +

    {{ course.name }}

    -

    {{ course.name }}

    - -
    +
      +
    • - - {{ form.as_table }} -
      - {% csrf_token %} -
      + + {{ form.as_table }} +
      + {% csrf_token %} -
      -
    -
    - {{ mapdiv|safe }} + +
  • +
    + {{ mapdiv|safe }} - {{ mapscript|safe }} -
    + {{ mapscript|safe }} +
  • + -
    + {% endblock %} + +{% block sidebar %} +{% include 'menu_racing.html' %} +{% endblock %} diff --git a/rowers/templates/course_form.html b/rowers/templates/course_form.html index de1d0db7..1c679e7d 100644 --- a/rowers/templates/course_form.html +++ b/rowers/templates/course_form.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -15,39 +15,45 @@ {% endblock %} -{% block content %} - -
    -
    -
    -

    Upload KML Course File

    - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. -

    - {% endif %} +{% block main %} +

    Upload KML Course File

    +
      +
    • +
      +

      Drag and drop files here

      +
      +
      + + {% if form.errors %} +

      + Please correct the error{{ form.errors|pluralize }} below. +

      + {% endif %} + - {{ form.as_table }} + {{ form.as_table }}
      {% csrf_token %} -
      - -
      -
      - +

      + +

      +
    • +
    + + - {% endblock %} - {% block scripts %} +{% block sidebar %} +{% include 'menu_racing.html' %} +{% endblock %} + + +{% block scripts %} - - - -
    - +{% block main %} +
    + + +
    +
    + + + +
    + + + +
    - +
      +
    • +

      Summary for {{ theuser.first_name }} {{ theuser.last_name }} + between {{ startdate|date }} and {{ enddate|date }}

      +
    • -
      -
      -   -
      -
      - {% if user.is_authenticated and user|is_manager %} - - -
      -
      + +
    • - {% csrf_token %} -
      - - {{ optionsform.as_table }} -
      -
      -
      - - -
      -
      -
    • -
      -

      Use this form to select a different date range:

      -

      - Select start and end date for a date range: -

      - -
      - - - {{ form.as_table }} -
      - {% csrf_token %} -
      -
      - -
      -
      -
      - Or use the last {{ deltaform }} days. -
      -
      - {% csrf_token %} - - -
      -
      -
      - - -
      - -

      Summary for {{ theuser.first_name }} {{ theuser.last_name }} - between {{ startdate|date }} and {{ enddate|date }}

      - - -
      - - - - - - -
      - - - - - - - - -
      - - {{ the_div|safe }} - -
      + + {{ optionsform.as_table }} +
      + + +
    • + + {{ form.as_table }} +
      +
    • +
    • + + {{ flexaxesform.as_table }} +
      +
    • +
    • + {% csrf_token %} + + +
    • +
    {% endblock %} @@ -253,16 +128,25 @@ $(function($) { console.log('loading script'); $.getJSON(window.location.protocol + '//'+window.location.host + '/rowers/flexalldata', function(json) { + console.log('got script'); var counter=0; var script = json.script; var div = json.div; + console.log('set vars'); $("#id_sitready").remove(); + console.log('sitready removed'); $("#id_chart").append(div); - console.log(div); + console.log('div appended'); $("#id_script").append(" {% endblock %} + +{% block sidebar %} +{% include 'menu_analytics.html' %} +{% endblock %} diff --git a/rowers/templates/cumstats.html b/rowers/templates/cumstats.html index dcc0982b..fbf39f11 100644 --- a/rowers/templates/cumstats.html +++ b/rowers/templates/cumstats.html @@ -1,17 +1,16 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} -{% block title %}Workout Statistics{% endblock %} - -{% block content %} +{% block title %}Rowsandall {% endblock %} +{% block main %} - - - -{{ plotscript |safe }} - - - - - -
    -
    - {% if theuser %} -

    {{ theuser.first_name }}'s Workout Statistics

    - {% else %} -

    {{ user.first_name }}'s Workout Statistics

    - {% endif %} -
    -
    - {% if user.is_authenticated and user|is_manager %} - +
    + +
    -
    -
    -

    Summary for {{ theuser.first_name }} {{ theuser.last_name }} - between {{ startdate|date }} and {{ enddate|date }}

    - -

    Direct link for other Pro users: - https://rowsandall.com/rowers/{{ id }}/cumstats/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}/p/{{ plotfield }} -

    - -
    - {% csrf_token %} -
    - - {{ optionsform.as_table }} -
    -
    -
    - - -
    -
    +
    + +
    -
    -

    Use this form to select a different date range:

    -

    - Select start and end date for a date range: -

    - -
    - - - {{ form.as_table }} -
    - {% csrf_token %} -
    -
    - -
    -
    -
    - Or use the last {{ deltaform }} days. -
    -
    - {% csrf_token %} - - -
    -
    -
    -
    -
    -{% if stats %} -{% for key, value in stats.items %} -

    {{ value.verbosename }}

    -
    -

    - Plot -

    -
    + + + +
    + +
    + +
      + +
    • +

      Summary for {{ theuser.first_name }} {{ theuser.last_name }} + between {{ startdate|date }} and {{ enddate|date }}

      +
    • + +
    • + {% if stats %} +

      Statistics

      - + + + + + + + + {% for key, value in stats.items %} - - - - - - - - - - - - - + + + + + + + + + {% endfor %}
      MetricValueMeanMinimum25%Median75%MaximumStandard Deviation
      Mean{{ value.mean|floatformat:-2 }}
      Minimum{{ value.min|floatformat:-2 }}
      25%{{ value.firstq|floatformat:-2 }}
      Median{{ value.median|floatformat:-2 }}
      75%{{ value.thirdq|floatformat:-2 }}
      Maximum{{ value.max|floatformat:-2 }}
      Standard Deviation{{ value.std|floatformat:-2 }}{{ value.verbosename }}{{ value.mean|floatformat:-2 }}{{ value.min|floatformat:-2 }}{{ value.firstq|floatformat:-2 }}{{ value.median|floatformat:-2 }}{{ value.thirdq|floatformat:-2 }}{{ value.max|floatformat:-2 }}{{ value.std|floatformat:-2 }}
      -{% endfor %} -{% endif %} -
    -
    - {% if cordict %} -
    -

    Correlation Matrix

    -

    This matrix indicates a positive (+) or negative (-) correlation between two parameters. The Spearman correlation coefficient has values between +1 and -1. Positive correlation between two metrics means that if one metric increases, the other value is also likely to increase. Negative is the opposite. The further from zero, the higher the likelyhood. + + {% endif %} + +

  • + {% if cordict %} +

    Correlation matrix

    +

    This matrix indicates a positive (+) or negative (-) correlation between two parameters. The Spearman correlation coefficient has values between +1 and -1. Positive correlation between two metrics means that if one metric increases, the other value is also likely to increase. Negative is the opposite. The further from zero, the higher the likelyhood.

    - - - - - {% for key,value in cordict.items %} - - {% endfor %} - - - - {% for key, thedict in cordict.items %} +
     
    {{ key }}
    + + + + {% for key,value in cordict.items %} + + {% endfor %} + + + + {% for key, thedict in cordict.items %} {% for key2,value in thedict.items %} @@ -218,24 +147,43 @@
    {{ value|floatformat:-1 }}
    {% elif value < -0.5 %}
    {{ value|floatformat:-1 }}
    - {% elif value < -0.1 %} -
    {{ value|floatformat:-1 }}
    - {% else %} -   - {% endif %} + {% elif value < -0.1 %} +
    {{ value|floatformat:-1 }}
    + {% else %} +   + {% endif %} {% endfor %} {% endfor %} - -
     
    {{ key }}
    {{ key }}
    -
    + + {% endif %} -
    - {{ plotdiv|safe }} -
    -
    -
  • + +
  • +
    + + {{ optionsform.as_table }} +
    + +
  • +
  • + + {{ form.as_table }} +
    +
  • +
  • + {% csrf_token %} + + +
  • + + +{% endblock %} + + +{% block sidebar %} +{% include 'menu_analytics.html' %} {% endblock %} diff --git a/rowers/templates/developers.html b/rowers/templates/developers.html index 8ee6a523..889080ae 100644 --- a/rowers/templates/developers.html +++ b/rowers/templates/developers.html @@ -1,126 +1,123 @@ -{% extends "base.html" %} -{% block title %}About us{% endblock title %} -{% block content %} +{% extends "newbase.html" %} +{% block title %}Rowsandall Developers Info{% endblock title %} +{% block main %} -
    -

    Resources for developers

    +

    Resources for developers

    -

    On this page, a work in progress, I will collect useful information - for developers of rowing data apps and hardware.

    - -

    I presume you have an app (smartphone app, dedicated hardware, web site) - where your users (customers) generate, collect or store their rowing - related workout data. You can now offer your users easy ways to get - their data on this site.

    - -

    There are three ways to allow your users to get data to Rowsandall.com.

    +
      +
    • -
      File based export from your app
      - -

      Enable export of TCX, FIT or CSV formatted files from your app. - The users - upload the file to Rowsandall.com.

      - -
        -
      • Advantages -
          -
        • User sees immediate results
        • -
        -
      • -
      • Disadvantages -
          -
        • It is a multi-step process: Download from your - app, store, upload.
        • -
        -
      • -
      +

      On this page, a work in progress, I will collect useful information + for developers of rowing data apps and hardware.

      +

      I presume you have an app (smartphone app, dedicated hardware, web site) + where your users (customers) generate, collect or store their rowing + related workout data. You can now offer your users easy ways to get + their data on this site.

      + +
    • -
      Email from your app
      - -

      Similar as above, generate TCX, FIT or CSV formatted files and - email them - to workouts@rowsandall.com directly from your app. The From: field - should be the email address of the registered user.

      - -
        -
      • Advantages -
          -
        • It's a simple process, which can be automated.
        • -
        -
      • -
      • Disadvantages -
          -
        • It may take up to five minutes for the workout to show up +
        • +

          There are three ways to allow your users to get data to Rowsandall.com.

          +

          File based export from your app

          + +

          Enable export of TCX, FIT or CSV formatted files from your app. + The users + upload the file to Rowsandall.com.

          + +
            +
          • Advantages +
              +
            • User sees immediate results
            • +
            +
          • +
          • Disadvantages +
              +
            • It is a multi-step process: Download from your + app, store, upload.
            • +
            +
          • +
          +

          Email from your app

          + +

          Similar as above, generate TCX, FIT or CSV formatted files and + email them + to workouts@rowsandall.com directly from your app. The From: field + should be the email address of the registered user.

          + +
            +
          • Advantages +
              +
            • It's a simple process, which can be automated.
            • +
            +
          • +
          • Disadvantages +
              +
            • It may take up to five minutes for the workout to show up on the site.
            • -
            -
          • +
          +
        • +
        +

        Using the REST API

        + +

        We are building a REST API which will allow you to post and + receive stroke + data from the site directly.

        + +

        The REST API is a work in progress. We are open to improvement + suggestions (provided they don't break existing apps). Please send + email to info@rowsandall.com + with questions and/or suggestions. We + will get back to you as soon as possible.

        + +
          +
        • Advantages +
            +
          • Once it is set up, this is a one-click operation.
          • +
          • You can read a user's workout data from the site and use + them in your app.
          • +
          • This is not limited to workout data. You could make a full mobile + version of our site.
          • +
          +
        • + +
        • Disadvantages +
            +
          • The API is not stable and not fully tested yet.
          • +
          • You need to register your app with us. We can revoke your + permissions if you misuse them.
          • +
          • The user user must grant permissions to your app.
          • +
          • You need to manage authorization tokens.
          • +
          +
        -
        Using the REST API
        - -

        We are building a REST API which will allow you to post and - receive stroke - data from the site directly.

        - -

        The REST API is a work in progress. We are open to improvement - suggestions (provided they don't break existing apps). Please send - email to info@rowsandall.com - with questions and/or suggestions. We - will get back to you as soon as possible.

        - -
          -
        • Advantages -
            -
          • Once it is set up, this is a one-click operation.
          • -
          • You can read a user's workout data from the site and use - them in your app.
          • -
          • This is not limited to workout data. You could make a full mobile - version of our site.
          • -
          -
        • - -
        • Disadvantages -
            -
          • The API is not stable and not fully tested yet.
          • -
          • You need to register your app with us. We can revoke your - permissions if you misuse them.
          • -
          • The user user must grant permissions to your app.
          • -
          • You need to manage authorization tokens.
          • -
          -
        • -
        - -
    - - - -
    -
    + +
  • Quick Links

    -
    Accepted file formats
    +

    Accepted file formats

    All files adhering to the standards TCX and FIT formats will be parsed.

    However, some rowing related parameters are not supported by TCX and FIT. Therefore, we are supporting the CSV format that is documented in the following link.

    - +

    Using this standard will guarantee that your user's data are accepted without complaints.

    -
    API related documentation
    +

    API related documentation

    -
    Registering an app
    +

    Registering an app

    We have disabled the self service app link for security reasons. We will replace it with a secure self service app link soon. If you need to register an app, please send email to info@rowsandall.com

    -
    Authentication
    +

    Authentication

    Standard Oauth2 authentication. Get authorization code by pointing your user to the authorization URL. @@ -128,19 +125,19 @@ expires, use the refresh token to refresh it.

    -

    The redirect URI for user authentication has to be https. +

    The redirect URI for user authentication has to be https. Developers of iOS or Android apps should contact me directly if this doesn't work for them. I can add exceptions.

    -
      +
      • Authorization URL: https://{{ request.get_host }}/rowers/o/authorize
      • Access Token request: https://{{ request.get_host }}/rowers/o/token/
      • Access Token refresh: https://{{ request.get_host }}/rowers/o/token/
      • Handy utility for testing: http://django-oauth-toolkit.herokuapp.com/consumer/
      -
      API documentation
      +

      API documentation

      Once you have a registered app, you have gone through the authorization and have successfully obtained an access token, you can use it to place @@ -149,7 +146,7 @@

      The workout summary data and the stroke data are obtained and sent separately.

      -
        +
        • API documentation (But refer to the below for stroke data.)
        • Try out the workout summary API
        • @@ -162,7 +159,7 @@ future to enable updating stroke data. Stroke data for workout {id} are posted to:

          -
            +
            • https://{{ request.get_host }}/rowers/api/workouts/{id}/strokedata
            @@ -180,14 +177,14 @@

            Mandatory data fields are:

            -
              +
              • time: Time (milliseconds since workout start)
              • distance: Distance (meters)
              • pace: Pace (milliseconds per 500m)
              • spm Stroke rate (strokes per minute)

              Optional data fiels are:

              -
                +
                • power: Power (Watt)
                • drivelength: Drive length (meters)
                • dragfactor: Drag factor
                • @@ -201,7 +198,7 @@
                • catch: Catch angle per Empower oarlock (degrees)
                • finish: Finish angle per Empower oarlock (degrees)
                • peakforceangle: Peak Force Angle per Empower oarlock (degrees)
                • -
                • slip: Wash as defined per Empower oarlock (degrees)
                • +
                • slip: Slip as defined per Empower oarlock (degrees)
                @@ -211,7 +208,12 @@ must have the same number of records. If an optional data field fails a test, its values are silently replaced by zeros.

                -
  • -
    + + + +{% endblock %} + +{% block sidebar %} +{% include 'menu_help.html' %} +{% endblock %} - {% endblock content %} diff --git a/rowers/templates/document_form.html b/rowers/templates/document_form.html index ab8a59f3..4bb4154a 100644 --- a/rowers/templates/document_form.html +++ b/rowers/templates/document_form.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -15,64 +15,65 @@ {% endblock %} -{% block content %} - -
    -
    -
    -

    Upload Workout File

    - {% if user.is_authenticated and user|is_manager %} -

    Looking for Team Manager - Upload?

    - {% endif %} - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. +{% block main %} +

    +
      +
    • + +
      + +

      Upload Workout File

      + {% if user.is_authenticated and user|is_manager %} +

      Looking for Team Manager + Upload?

      + {% endif %} + {% if form.errors %} +

      + Please correct the error{{ form.errors|pluralize }} below.

      - {% endif %} + {% endif %} - {{ form.as_table }} + {{ form.as_table }}
      {% csrf_token %} -
      -
      - - -
      - - - - -
    + + +
  • +

    Optional extra actions

    +

    + + {{ optionsform.as_table }} + +
    +

    +

    + You can select one static plot to be generated immediately for + this workout. You can select to export to major fitness + platforms automatically. + If you check "make private", this workout will not be visible to your followers and will not show up in your teams' workouts list. With the Landing Page option, you can select to which (workout related) page you will be + taken after a successfull upload. +

    + +

    + If you don't have a workout file but have written down the splits, + you can create a workout file yourself from this template +

    + + +

    Select Files with the File button or drag them on the marked area

    + +
  • + + + + + +
    {% endblock %} {% block scripts %} @@ -221,8 +222,8 @@ $('#id_workouttype').change(); console.log(value); }); - $("#id_drop-files").replaceWith( - '
    ' + $("#id_main").replaceWith( + '
    ' ); $.ajax({ data: data, @@ -310,3 +311,8 @@ $('#id_workouttype').change(); }; {% endblock %} + + + {% block sidebar %} + {% include 'menu_workouts.html' %} + {% endblock %} diff --git a/rowers/templates/email.html b/rowers/templates/email.html index 562f8615..ad105bc0 100644 --- a/rowers/templates/email.html +++ b/rowers/templates/email.html @@ -1,104 +1,117 @@ - {% extends "base.html" %} - {% block title %}Contact Us{% endblock title %} - {% block content %} -
    -

    Contact us through email

    +{% extends "newbase.html" %} +{% block title %}Contact Us{% endblock title %} +{% block main %} +

    Contact us through email

    +
      +
    • {% if form.errors %} -

      - Please correct the error{{ form.errors|pluralize }} below. -

      +

      + Please correct the error{{ form.errors|pluralize }} below. +

      {% endif %} - - -
      {% csrf_token %} - - + + + +
      - + + + {% csrf_token %} + + - - - -
      + - - - -
      - - - - - - -
      - - - -
      - - - -
      - - - - - -
      - Do you want to send me an email? - - -
      - - - -
      - -
      - - +
      + + +
      + + + + + + +
      + + + +
      + + + +
      + + + + + +
      + Do you want to send me an email? + + +
      + + + +
      + +
      + +
    • + +
    • +

      Bug reporting, feature requests

      + +

      + Bug reports and feature requests can be done through our BitBucket page. Please check on the following link if your bug or issue is a known one. Feel free to file any feature request. +

      +

      +
    • + +
    • +

      Facebook Group

      + +

      We run a facebook group where you can post questions and report problems, + especially if you think the wider user community benefits from the answers.

      + +
    • + +
    • +

      Twitter

      + +

      You can also check me on Twitter: +

      + When the site is down, this is the appropriate channel to look for apologies, updates, and offer help. +

      +
    • + +
    • +

      Rowsandall s.r.o.

      + +

      Rowsandall s.r.o.
      + Nové sady 988/2
      + 602 00 Brno
      + Czech Republic
      + IČ: 070 48 572
      + DIČ: CZ 070 48 572 (Nejsme plátce DPH)
      + Datová schránka: 7897syr
      + Email: info@rowsandall.com
      + The company is registered in the business register at the + Regional Court in Brno (Společnost je zapsána v obchodním rejstříku vedeném u Krajského soudu v Brně, oddíl C, vložka 105845)
      +

      + +
    • +
    +{% endblock %} + +{% block sidebar %} +{% include 'menu_help.html' %} +{% endblock %} -
    -

    Bug reporting, feature requests

    - -

    -Bug reports and feature requests can be done through our BitBucket page. Please check on the following link if your bug or issue is a known one. Feel free to file any feature request. -

    -

    - -

    Facebook Group

    - -

    We run a facebook group where you can post questions and report problems, - especially if you think the wider user community benefits from the answers.

    - - -

    Twitter

    - -

    You can also check me on Twitter: -

    -When the site is down, this is the appropriate channel to look for apologies, updates, and offer help. -

    - -

    Rowsandall s.r.o.

    - -

    Rowsandall s.r.o.
    - Nové sady 988/2
    - 602 00 Brno
    - Czech Republic
    - IČ: 070 48 572
    - DIČ: CZ 070 48 572 (Nejsme plátce DPH)
    - Datová schránka: 7897syr
    - Email: info@rowsandall.com
    - The company is registered in the business register at the - Regional Court in Brno (Společnost je zapsána v obchodním rejstříku vedeném u Krajského soudu v Brně, oddíl C, vložka 105845)
    -

    - -
    - {% endblock content %} diff --git a/rowers/templates/empower_fix.html b/rowers/templates/empower_fix.html index 8e059f80..5fd1101e 100644 --- a/rowers/templates/empower_fix.html +++ b/rowers/templates/empower_fix.html @@ -1,10 +1,10 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} -{% block title %}Workouts{% endblock %} +{% block title %}Empower FIX{% endblock %} -{% block content %} +{% block main %} -
    - {% include "teambuttons.html" with teamid=team.id team=team %} -
    -
    -

    Empower Workouts

    -

    This functionality is aimed at users who have uploaded workouts from - the Nielsen-Kellerman Empower Oarlock/SpeedCoach combination before the - power inflation bug was known (May 4, 2018).

    +

    Empower Workouts

    -

    Workouts recorded with a SpeedCoach running NK Firmware version 2.17 or - lower have Power and Work per Stroke values that are approximately - 9% (sculling) or 5% too high. The exact value of the error depends on your - inboard and oar length.

    - -

    Currently, we autocorrect workouts recorded with old Firmware upon - their upload, but workouts that were present on the site before - the bug was known still have incorrect values for Power and Work per Stroke. -

    - -

    - You can use this page to correct those workouts. -

    - - -
    -
    -
    - -
    -
    - - {{ dateform.as_table }} -
    - {% csrf_token %} -
    -
    - -
    -
    -
    - -
    - - -
    -
    - - - {% if workouts %} - - Toggle All
    - - - {{ form.as_table }} -
    - -{% else %} -

    No workouts found

    -{% endif %} -
    -
    -

    Select workouts on the left, - and press submit

    -
    +
      +
    • +

      Use the date form to reduce the selection

      - {% csrf_token %} - -

      -
    -
    -

    You can use the date form above to reduce the selection

    -
    -
    -
    +
    + + {{ dateform.as_table }} +
    + {% csrf_token %} + +
    +

    + +
  • +

    This functionality is aimed at users who have uploaded workouts from + the Nielsen-Kellerman Empower Oarlock/SpeedCoach combination before the + power inflation bug was known (May 4, 2018).

    + +

    Workouts recorded with a SpeedCoach running NK Firmware version 2.17 or + lower have Power and Work per Stroke values that are approximately + 9% (sculling) or 5% too high. The exact value of the error depends on your + inboard and oar length.

    + +

    Currently, we autocorrect workouts recorded with old Firmware upon + their upload, but workouts that were present on the site before + the bug was known still have incorrect values for Power and Work per Stroke. +

    +

    + You can use this page to correct those workouts. +

    +
  • + +
  • +

    +

    + {% if workouts %} + + Toggle All
    + + + {{ form.as_table }} +
    + {% csrf_token %} + + + +
    +

    + {% else %} +

    No workouts found

    + {% endif %} +
  • + {% if workouts %} +
  • +

    Select workouts + and press submit +

    +
  • + {% endif %} + {% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} +{% endblock %} diff --git a/rowers/templates/export_workouts.html b/rowers/templates/export_workouts.html index 61bec7b6..b45dc2f5 100644 --- a/rowers/templates/export_workouts.html +++ b/rowers/templates/export_workouts.html @@ -1,23 +1,24 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Rowsandall Workouts Summary Export{% endblock %} -{% block content %} -
    -
    -
    - - {{ form.as_table }} -
    - {% csrf_token %} -
    -
    - -
    -
    -
    +{% block main %} +

    Export all workouts

    +
      +
    • +

      +

      + + {{ form.as_table }} +
      + {% csrf_token %} + +
      +

      +
    • +
    • With this form, you can export a summary table for all workouts within a selected date range. The table will be sent to you as a CSV file which can be opened in excel. The table contains @@ -27,7 +28,11 @@ By setting the start date to your registration date or earlier and the end date to today, you will receive all workout data we are storing for you.

      -
    -
    + + {% endblock %} + +{% block sidebar %} +{% include 'menu_profile.html' %} +{% endblock %} diff --git a/rowers/templates/favoritecharts.html b/rowers/templates/favoritecharts.html index d8c52704..63fc02e5 100644 --- a/rowers/templates/favoritecharts.html +++ b/rowers/templates/favoritecharts.html @@ -1,41 +1,46 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% block title %}Change Favorite Charts{% endblock %} -{% block content %} +{% block main %} +

    Change Favorite Charts of {{ rower.user.first_name }} {{ rower.user.last_name }}

    +
    +
      {% csrf_token %} -
      -
      +
    • +

      -
    • {{ favorites_formset.management_form }} + {% for favorites_form in favorites_formset %} -
      -

      Chart {{ forloop.counter }}

      - - {{ favorites_form.as_table }} -
      -
      +
    • +
      +

      Chart {{ forloop.counter }}

      + + {{ favorites_form.as_table }} +
      +
      +
    • {% endfor %} -
      -

       

      -
      +
    - - {% endblock %} + +{% block sidebar %} +{% include 'menu_profile.html' %} +{% endblock %} diff --git a/rowers/templates/fitnessmetric.html b/rowers/templates/fitnessmetric.html index cba71c9b..fd8d5d03 100644 --- a/rowers/templates/fitnessmetric.html +++ b/rowers/templates/fitnessmetric.html @@ -1,10 +1,10 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Rowsandall Fitness Progress {% endblock %} -{% block content %} +{% block main %} - +{% if rower.user %} +

    {{ rower.user.first_name }} Power Estimates

    +{% else %} +

    {{ user.first_name }} Power Estimates

    +{% endif %} + -
    -
    -
    +
      +
    • + {{ the_div|safe }} +
    • +
    • + {{ form.as_table }}
      {% csrf_token %} -
      - -
      +
    • -
    -
    - {% if therower.user %} -

    {{ therower.user.first_name }} Power Estimates

    - {% else %} -

    {{ user.first_name }} Power Estimates

    - {% endif %} -
    -
    - {% if user.is_authenticated and user|is_manager %} - - {% endif %} -
    - - -
    -
    - - {{ the_div|safe }} - -
    + + + + +{% endblock %} + +{% block sidebar %} +{% include 'menu_analytics.html' %} {% endblock %} diff --git a/rowers/templates/flexchart3otw.html b/rowers/templates/flexchart3otw.html index 7c6180c7..6dc19c95 100644 --- a/rowers/templates/flexchart3otw.html +++ b/rowers/templates/flexchart3otw.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% load tz %} @@ -6,7 +6,7 @@ {% block title %} Flexible Plot {% endblock %} {% localtime on %} -{% block content %} +{% block main %} {{ js_res | safe }} {{ css_res| safe }} @@ -20,197 +20,45 @@ {{ the_script |safe }} - - - - - -

     

    - -
    - - -
    - - - +

    Flexible Chart

    - - -
    -
    -
    - {% csrf_token %} - {% if workstrokesonly %} - - - {% else %} - - - {% endif %} -
    - If your data source allows, this will show or hide strokes taken during rest intervals. -
    -
    - {% if plottype == 'scatter' %} - Line - {% else %} - Scatter - {% endif %} -
    -
    - -
    - -
    - - - {{ the_div|safe }} - -
    - -
    -
    - {% if maxfav >= 0 %} - Manage Favorites - {% else %} -   - {% endif %} -
    -
    - {% if favoritenr > 0 %} - < - {% else %} - < - {% endif %} -
    -
    +
  • + {% if favoritenr > 0 %} + + + + {% else %} + + + + {% endif %} {% csrf_token %} {% if workstrokesonly %} @@ -218,22 +66,28 @@ {% else %} {% endif %} - -
    -
  • -
    - {% if favoritenr < maxfav %} - > + + {% if favoritenr < maxfav %} + + + {% else %} - > + + + {% endif %} -
    - {% if favoritechartnotes %} -
    + + {% if favoritechartnotes %}

    Chart {{ favoritenr|add:1 }}:{{ favoritechartnotes }}

    -
    - {% endif %} -
    + {% endif %} + + {% endblock %} {% endlocaltime %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/flexthumbnails.html b/rowers/templates/flexthumbnails.html index 16de6af0..9e6c2179 100644 --- a/rowers/templates/flexthumbnails.html +++ b/rowers/templates/flexthumbnails.html @@ -1,9 +1,8 @@ -

    Flex Charts

    -
    - {{ charts | safe }} -
    +
      + {{ charts| safe }} +
    diff --git a/rowers/templates/forcecurve_single.html b/rowers/templates/forcecurve_single.html index c45300c4..e99669c3 100644 --- a/rowers/templates/forcecurve_single.html +++ b/rowers/templates/forcecurve_single.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% load tz %} @@ -6,7 +6,7 @@ {% block title %} Force Curve Plot {% endblock %} {% localtime on %} -{% block content %} +{% block main %} {{ js_res | safe }} {{ css_res| safe }} @@ -19,54 +19,34 @@ {{ the_script |safe }} +

    Empower Force Curve

    - - - - - -

     

    - - - -
    - - {{ the_div|safe }} - -
    + + {% endblock %} {% endlocaltime %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/fusion.html b/rowers/templates/fusion.html index 52de737e..89a11a4b 100644 --- a/rowers/templates/fusion.html +++ b/rowers/templates/fusion.html @@ -1,22 +1,34 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Workouts{% endblock %} -{% block content %} +{% block main %} -
    -

    Fusion Editor

    -
    -
    -
    +

    Fusion Editor

    +
      +
    • + +
      +

      + + {{ form.as_table }} +
      +

      +

      + {% csrf_token %} + +

      +
      +
    • +
    • - Adding sensor data from workout {{ workout2.id }} into workout {{ workout1.id }}. - This will create a new workout. After you submit the form, you will be - taken to the newly created workout. If you are happy with the result, you - can delete the two original workouts manually. + Adding sensor data from workout {{ workout2.id }} into workout {{ workout1.id }}. + This will create a new workout. After you submit the form, you will be + taken to the newly created workout. If you are happy with the result, you + can delete the two original workouts manually.

      Workout 1: {{ workout1.name }} @@ -24,21 +36,13 @@

      Workout 2: {{ workout2.name }}

      -

      On the right hand side, please select the columns from workout 2 that +

      Please select the columns from workout 2 that you want to replace the equivalent columns in workout 1.

      -
    -
    - -
    - - - {{ form.as_table }} -
    - {% csrf_token %} -
    -
    - -
    -
    + + {% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/fusion_list.html b/rowers/templates/fusion_list.html index b7f285d7..8da7c441 100644 --- a/rowers/templates/fusion_list.html +++ b/rowers/templates/fusion_list.html @@ -1,36 +1,34 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Workouts{% endblock %} -{% block content %} -
    -
    -

    Workout {{ id }}

    - - +{% block main %} +

    Workout {{ id }} Sensor Fusion

    +
      +
    • +
    + - - + + - - + + - + - + - + - + - + - -
    Rower:{{ first_name }} {{ last_name }}
    Name:{{ workout.name }}
    Date:{{ workout.date }}
    Time:{{ workout.starttime }}
    Distance:{{ workout.distance }}m
    Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
    Type:{{ workout.workouttype }}
    Weight Category:{{ workout.weightcategory }}
    -
    -
    + +

    @@ -40,82 +38,108 @@

    -
    - - Select start and end date for a date range: -
    -

    -

    - - - {{ dateform.as_table }} -
    - {% csrf_token %} -
    -
    +

    + Select start and end date for a date range: +

    +

    + + + + {{ dateform.as_table }} +
    + {% csrf_token %} +

    -

    -
    + +
  • +

    Fuse this workout with data from:

    + {% if workouts %} +

    + + {% if workouts.has_previous %} + {% if request.GET.q %} + + + + + + + {% else %} + + + + + + + {% endif %} + {% endif %} + + + Page {{ workouts.number }} of {{ workouts.paginator.num_pages }}. + + + {% if workouts.has_next %} + {% if request.GET.q %} + + + + + + + {% else %} + + + + + + + {% endif %} + {% endif %} + +

    - -
  • - -
    -

    Fuse this workout with data from:

    - {% if workouts %} - - - - - - - - - - - - - - - - {% for cworkout in workouts %} - - - - - - - - - - {% if id == cworkout.id %} - - {% else %} - - {% endif %} - - - {% endfor %} - -
    Date Time Name Type Distance Duration Avg HR Max HR Fusion
    {{ cworkout.date }} {{ cworkout.starttime }} {{ cworkout.name }} {{ cworkout.workouttype }} {{ cworkout.distance }}m {{ cworkout.duration |durationprint:"%H:%M:%S.%f" }} {{ cworkout.averagehr }} {{ cworkout.maxhr }}   Fusion
    - {% else %} -

    No workouts found

    - {% endif %} - -
    - - {% if workouts.has_previous %} - < + + + + + + + + + + + + + + + + {% for cworkout in workouts %} + + + + + + + + + + {% if id == cworkout.id %} + + {% else %} + + {% endif %} + + + {% endfor %} + +
    Date Time Name Type Distance Duration Avg HR Max HR Fusion
    {{ cworkout.date }} {{ cworkout.starttime }} {{ cworkout.name }} {{ cworkout.workouttype }} {{ cworkout.distance }}m {{ cworkout.duration |durationprint:"%H:%M:%S.%f" }} {{ cworkout.averagehr }} {{ cworkout.maxhr }}   Fusion
    + {% else %} +

    No workouts found

    {% endif %} - - - Page {{ workouts.number }} of {{ workouts.paginator.num_pages }}. - - - {% if workouts.has_next %} - > - {% endif %} -
    -
    -
    + + +{% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} {% endblock %} diff --git a/rowers/templates/gdpr_optin.html b/rowers/templates/gdpr_optin.html index f340bd93..bc2bb898 100644 --- a/rowers/templates/gdpr_optin.html +++ b/rowers/templates/gdpr_optin.html @@ -1,53 +1,52 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}GDPR Opt-In{% endblock %} -{% block content %} -
    -

    GDPR Opt-In

    -

    - - To comply with the European Union General Data Protection Regulation, - we need to record your consent to use personal data on this website. - Please take some time to review our data policies. If you agree and - opt in, click the green button at the bottom to be taken to the site. - If you do not agree, please use the red button to delete your - account. This will irreversibly delete all your data on rowsandall.com - and remove your account. - -

    -
    +{% block main %} +

    GDPR Opt-In

    +

    + + To comply with the European Union General Data Protection Regulation, + we need to record your consent to use personal data on this website. + Please take some time to review our data policies. If you agree and + opt in, click the green button at the bottom to be taken to the site. + If you do not agree, please use the red button to delete your + account. This will irreversibly delete all your data on rowsandall.com + and remove your account. + +

    +
    - {% include "privacypolicy.html" %} +{% include "privacypolicy.html" %} -
    +
    -

    - To start or continue using the site, please give your consent by clicking on the green Opt In button below. -

    +

    + To start or continue using the site, please give your consent by clicking on the green Opt In button below. +

    -

    -

    -

    - - +

    + Download your data +

    + +

    + Opt in and continue +

    + +

    {% csrf_token %} -
    - - -
    + +
    - -
    +

    {% endblock %} + +{% block sidebar %} +{% include 'menu_profile.html' %} +{% endblock %} + diff --git a/rowers/templates/graphimage_delete_confirm.html b/rowers/templates/graphimage_delete_confirm.html index 04989cea..8e8a6c02 100644 --- a/rowers/templates/graphimage_delete_confirm.html +++ b/rowers/templates/graphimage_delete_confirm.html @@ -1,43 +1,30 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Delete Graph Image {% endblock %} -{% block content %} -
    - - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. -

    - {% endif %} - -

    Confirm Graph Delete

    -

    This will permanently delete the graph

    - - -
    -

    - Cancel -

    - -
    -

    - Delete -

    -
    - -
    - -
    -

    - -/{{ graph.filename }} - -

    - -
    +{% block main %} +
      +
    • +
      + {% csrf_token %} +

      Are you sure you want to delete this chart?

      +

      + +

      +
      +
    • +
    • + + {{ object.filename }} + +
    • +
    {% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} +{% endblock %} diff --git a/rowers/templates/help.html b/rowers/templates/help.html index d3e3f858..a755360d 100644 --- a/rowers/templates/help.html +++ b/rowers/templates/help.html @@ -3,11 +3,168 @@ {% load rowerfilters %} {% block main %} -

    Main

    -

    Vestibulum consectetur sit amet nisi ut consectetur. Praesent efficitur, nibh vitae fringilla scelerisque, est neque faucibus quam, in iaculis purus libero eget mauris. Curabitur et luctus sapien, ac gravida orci. Aliquam erat volutpat. In hac habitasse platea dictumst. Aenean commodo, arcu a commodo efficitur, libero dolor mollis turpis, non posuere orci leo eget enim. Curabitur sit amet elementum orci, pulvinar dignissim urna. Morbi id ex eu ex congue laoreet. Aenean tincidunt dolor justo, semper pretium libero luctus nec. Ut vulputate metus accumsan leo imperdiet tincidunt. Phasellus nec rutrum dolor. Cras imperdiet sollicitudin arcu, id interdum nibh fermentum in. +

    Welcome to Rowsandall.com

    +
      +
    • +

      What is it?

      +

      + Rowsandall.com is an online tool for rowers to analyze data from On The Water + (OTW) and On The Erg (OTE) workouts. It accepts workout data from a + number of devices and applications. It analyzes the data to provide + valuable insights about your training, and enables you to share + data with many common online training tracking systems.

      -

      Vestibulum consectetur sit amet nisi ut consectetur. Praesent efficitur, nibh vitae fringilla scelerisque, est neque faucibus quam, in iaculis purus libero eget mauris. Curabitur et luctus sapien, ac gravida orci. Aliquam erat volutpat. In hac habitasse platea dictumst. Aenean commodo, arcu a commodo efficitur, libero dolor mollis turpis, non posuere orci leo eget enim. Curabitur sit amet elementum orci, pulvinar dignissim urna. Morbi id ex eu ex congue laoreet. Aenean tincidunt dolor justo, semper pretium libero luctus nec. Ut vulputate metus accumsan leo imperdiet tincidunt. Phasellus nec rutrum dolor. Cras imperdiet sollicitudin arcu, id interdum nibh fermentum in. +

      Indoor Rowing

      +

      + rowsandall.com is designed to work with all models of the Concept2 + Indoor rower. Using applications like ergstick, rowpro, ergdata or + painsled; a user can collect stroke by stroke data from the Concept2 + Performance Monitor (Models PM3, PM4 or PM5). Workout data can be exported + from these applications, usually in CSV format, and uploaded to + rowsandall.com. Users can upload workouts either through the online + interface, for by emailing workouts to the site to simplify the + process for some applications.

      +

      On The Water Rowing

      +

      + On the water rowers use either dedicated devices like Speedcoaches or + smartphone applications to collect data on their workouts. All of these + devices and applications provide a method to export workout data in CSV, + TCX or FIT format files. Workout data in these formats can be uploaded + to rowsandall.com. Users can upload workouts either through the online + interface, for by emailing workouts to the site to simplify the process + for some applications. +

      +

      Basic Analysis

      +

      + Many athletes use training approaches that use heart rate as a key metric. In general, HR training is managed by defining different training zones for different purposes. Rowsandall.com uses heart rate zone definitions that are consistent with the Concept2 Training Guide. +

      + +

      + After a user defines their training zones, any training files that are uploaded with HR data can be analyzed to provide time in HR zone pie charts. +

      + +

      + The tools also provide the ability to review a row, stroke by stroke in plots versus time or distance. Basic plots in include HR, Pace, Stroke rate, and power for the erg. +

      + + +

      + The tools also provide a text summary of the row. +

      + +

      Workout Export

      + +

      + rowsandall.com provides the ability to easily export workouts from the erg or boat to the Concept2 online logbook. Export to other sport tracking sites like Strava and SportTracks is also supported. Users can also export workout data by email. +

      + +

      Import Compatibility

      + +

      + Rowsandall.com tries to be compatible with the most important tools that rowers use to capture the data (both indoor and OTW). The list of supported tools + continues to be expanded. +

      +
    • +
    • +

      Getting your data on rowsandall.com

      + +

      + To start using the tool, you first need to get some workouts in it. + There are basically two ways. The first method works with a workout file + (CSV, FIT, TCX, etc). The details are described in this + blog post. + A straightforward way to upload your data is to use the + Upload Page. +

      + +

      + The second way to get data into the tool is by importing them + from other workout tracking portals like Strava, SportTracks, etc. + To do this, you use the Import menu on the left of the + Workouts List page. For this + to work, you need to have coupled the external fitness tracking + site with rowsandall.com. You can do this in your + User Profile. Don't worry, the site + will guide you through the process. +

      + +

      User settings

      + +

      Talking about the user profile, + we recommend that you look + at what parameters can be set there. To get the most out of + the site, we recommend you set the heart rate and power zones, + as well as check that your age and gender are correct. Exercise + data make much more sense when this context is taken into account. +

      + +

      Exploring a workout

      + +

      + In the Workouts List, + you can click on the name of a workout to open it. + Once you're on a workout page, the menu on the left will + give you all possibilities you have to edit, manipulate, chart, + or analyze the workout data. +

      + +

      + When you are about to do something irreversible (like deleting a workout) + the site will ask for a confirmation, so don't hesitate to explore. +

      + +

      Analysis

      + +

      + Some of our functionality is not related to a single workout, but instead + looks at comparisons, trends, statistics, and other. You can find all + that under the Analysis Tab. +

      + + +

      On-line Racing

      + +

      + On-line racing is a + fun way to race other Rowsandall.com users + rowing on the same stretch of water. +

      + +

      Training Plan

      + +

      + Under the Plan tab, you + will find your training plan and functionality to see how + you are progressing towards your goals. +

      + +

      Teams

      + +

      + The Teams tab brings you to + functionality related to interaction with your team, if you + are part of one. +

      + + +
    • +
    • +

      Need more help?

      + +

      + The links in the menu on the left bring you to our blog, where we + regularly publish how-to's and articles about new functionality, to + our Facebook group where you can discuss with other users, and to + a contact page where you can learn how to contact the developers + of this site. +

      +
    + +{% endblock %} + +{% block sideheader %} +

    Help

    {% endblock %} {% block sidebar %} diff --git a/rowers/templates/histo.html b/rowers/templates/histo.html index f952ec58..37b9c270 100644 --- a/rowers/templates/histo.html +++ b/rowers/templates/histo.html @@ -1,16 +1,16 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} -{% block title %}Rowsandall Histogram {% endblock %} +{% block title %}Rowsandall {% endblock %} -{% block content %} +{% block main %} - - -{{ interactiveplot |safe }} +
    + + +
    +
    + + - - +
    + + -
    -
    - {% if theuser %} -

    {{ theuser.first_name }}'s Stroke Analysis

    - {% else %} -

    {{ user.first_name }}'s Stroke Analysis

    - {% endif %} -
    -
    - {% if user.is_authenticated and user|is_manager %} - - {% endif %} -
    -
    -
    -
    -

    Warning: Large date ranges may take a long time to load. Huge date ranges may crash your browser.

    -
    - {% csrf_token %} -
    - - {{ optionsform.as_table }} -
    -
    -
    - - -
    -
    -
    -
    -

    Use this form to select a different date range:

    -

    - Select start and end date for a date range: -

    - -
    - - - {{ form.as_table }} -
    - {% csrf_token %} -
    -
    - -
    -
    -
    - Or use the last {{ deltaform }} days. -
    -
    - {% csrf_token %} - - -
    -
    -
    - -
    - +
    + +
    + +
      + +
    • Summary for {{ theuser.first_name }} {{ theuser.last_name }} between {{ startdate|date }} and {{ enddate|date }}

      -
    -
    + -
    - - {{ the_div|safe }} - -
    +
  • +
    + + {{ the_div|safe }} +
    +
  • +
  • +
    + + {{ optionsform.as_table }} +
    + +
  • +
  • + + {{ form.as_table }} +
    +
  • +
  • + {% csrf_token %} + + +
  • + {% endblock %} + +{% block scripts %} + + + + +{% endblock %} + +{% block sidebar %} +{% include 'menu_analytics.html' %} +{% endblock %} diff --git a/rowers/templates/histo_single.html b/rowers/templates/histo_single.html index 042acf0f..e1ee3677 100644 --- a/rowers/templates/histo_single.html +++ b/rowers/templates/histo_single.html @@ -1,66 +1,34 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}View Workout {% endblock %} -{% block content %} +{% block main %} - - + + - {{ interactiveplot |safe }} - - - +{{ interactiveplot |safe }} - - - - -
    -

    Indoor Rower Power Histogram

    -
    - - -
    +{% if user.is_authenticated and mayedit %} +

    Indoor Rower Power Histogram

    +
      +
    • {{ the_div|safe }} -
    + + +{% endif %} {% endblock %} + + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/image_form.html b/rowers/templates/image_form.html index e5454d4e..82e08395 100644 --- a/rowers/templates/image_form.html +++ b/rowers/templates/image_form.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -15,36 +15,33 @@ {% endblock %} -{% block content %} - -
    -
    -
    -

    Upload Image

    - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. +{% block main %} +

    Upload Image

    + +
      +
    • + +
      + + {% if form.errors %} +

      + Please correct the error{{ form.errors|pluralize }} below.

      - {% endif %} + {% endif %} - {{ form.as_table }} + {{ form.as_table }}
      {% csrf_token %} -
      - -
      -
      - - - - -
    • +

      + +

      +
    + + {% endblock %} {% block scripts %} @@ -169,7 +166,7 @@ }); $("#id_drop-files").replaceWith( - '
    ' + '
    ' ); $.ajax({ data: data, @@ -250,3 +247,7 @@ {% endblock %} + + {% block sidebar %} + {% include 'menu_workout.html' %} + {% endblock %} diff --git a/rowers/templates/instroke.html b/rowers/templates/instroke.html index 622394d5..531962e9 100644 --- a/rowers/templates/instroke.html +++ b/rowers/templates/instroke.html @@ -1,80 +1,60 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Advanced Features {% endblock %} -{% block content %} -
    - - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. -

    - {% endif %} - -

    In Stroke Metrics

    - {% if user.rower.rowerplan == 'basic' %} -

    This is a preview of the page with advanced functionality for Pro users. See the About page for more information and to sign up for Pro Membership +{% block main %} +

    In Stroke Metrics

    +
      +
    • + {% if user.rower.rowerplan == 'basic' %} + +

      + This is a preview of the page with advanced functionality for Pro users. + See the About page for more information + and to sign up for Pro Membership +

      {% endif %} -
      -

      - Edit Workout -

      -
      -
      -

      - Advanced Edit -

      -
      -
      -

      - Export -

      - -
      - -
      - - - - - - - - - - - - -
      Date:{{ workout.date }}
      Time:{{ workout.starttime }}
      Distance:{{ workout.distance }}m
      Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
      Public link to this workout - https://rowsandall.com/rowers/workout/{{ workout.id }} - -
      -
      +
    • +
    • + + + + + + + + + + + +
      Date:{{ workout.date }}
      Time:{{ workout.starttime }}
      Distance:{{ workout.distance }}m
      Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
      Public link to this workout + https://rowsandall.com/rowers/workout/{{ workout.id }} + + +
      +
    • -
      - {% if instrokemetrics %} - {% for metric in instrokemetrics %} - {% if forloop.first %} -
      - {% else %} -
      - {% endif %} +
    • + {% if instrokemetrics %} + {% for metric in instrokemetrics %} +

      {{ metric }} -

    • +

      {% endfor %} {% else %}

      Unfortunately, this workout doesn't have any in stroke metrics

      {% endif %} -
      -
      + +
    -
    -

     

    -
    {% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/legal.html b/rowers/templates/legal.html index c61932eb..83d8ca9b 100644 --- a/rowers/templates/legal.html +++ b/rowers/templates/legal.html @@ -1,22 +1,21 @@ - {% extends "base.html" %} - {% block title %}About us{% endblock title %} - {% block content %} +{% extends "newbase.html" %} +{% block title %}Legal{% endblock title %} +{% block main %} -
    -

    Terms and Conditions

    -

    Credit

    +

    Terms and Conditions

    +

    Credit

    This document was created using a Contractology template available at http://www.freenetlaw.com..

    -

    Introduction

    +

    Introduction

    These terms and conditions govern your use of this website; by using this website, you accept these terms and conditions in full. If you disagree with these terms and conditions or any part of these terms and conditions, you must not use this website.

    This website uses cookies. By using this website and agreeing to these terms and conditions, you consent to our rowsandall.com’s use of cookies in accordance with the terms of rowsandall.com’s privacy policy.

    -

    License to use website

    +

    License to use website

    Unless otherwise stated, rowsandall.com and/or its licensors own the intellectual property rights in the website and material on the website. Subject to the license below, all these intellectual property rights are reserved.

    @@ -33,7 +32,7 @@

    -

    Acceptable use

    +

    Acceptable use

    You must not use this website in any way that causes, or may cause, damage to the website or impairment of the availability or accessibility of the website; or in any way which is unlawful, illegal, fraudulent or harmful, or in connection with any unlawful, illegal, fraudulent or harmful purpose or activity.

    @@ -44,7 +43,7 @@

    You must not use this website to transmit or send unsolicited commercial communications.

    -

    Restricted access

    +

    Restricted access

    Access to certain areas of this website is restricted. rowsandall.com reserves the right to restrict access to areas of this website, or indeed this entire website, at rowsandall.com’s discretion.

    @@ -52,7 +51,7 @@

    rowsandall.com may disable your user ID and password in rowsandall.com’s sole discretion without notice or explanation.

    -

    User content

    +

    User content

    In these terms and conditions, your user content means material (including without limitation text, images, audio material, video material and audio-visual material) that you submit to this website, for whatever purpose.

    @@ -66,7 +65,7 @@

    Notwithstanding rowsandall.com’s rights under these terms and conditions in relation to user content, rowsandall.com does not undertake to monitor the submission of such content to, or the publication of such content on, this website.

    -

    No warranties

    +

    No warranties

    This website is provided as is without any representations or warranties, express or implied. rowsandall.com makes no representations or warranties in relation to this website or the information and materials provided on this website.

    @@ -79,7 +78,7 @@

    Nothing on this website constitutes, or is meant to constitute, advice of any kind. If you require advice in relation to any legal, financial or medica] matter you should consult an appropriate professional.

    -

    Limitations of liability

    +

    Limitations of liability

    rowsandall.com will not be liable to you (whether under the law of contact, the law of torts or otherwise) in relation to the contents of, or use of, or otherwise in connection with, this website: @@ -90,7 +89,7 @@

    These limitations of liability apply even if rowsandall.com has been expressly advised of the potential loss.

    -

    Exceptions

    +

    Exceptions

    Nothing in this website disclaimer will exclude or limit any warranty implied by law that it would be unlawful to exclude or limit; and nothing in this website disclaimer will exclude or limit rowsandall.com’s liability in respect of any: @@ -100,61 +99,59 @@

  • matter which it would be illegal or unlawful for rowsandall.com to exclude or limit, or to attempt or purport to exclude or limit, its liability.

    -

    Reasonableness

    +

    Reasonableness

    By using this website, you agree that the exclusions and limitations of liability set out in this website disclaimer are reasonable.

    If you do not think they are reasonable, you must not use this website.

    -

    Other parties

    +

    Other parties

    You agree that the limitations of warranties and liability set out in this website disclaimer will protect rowsandall.com’s officers, employees, agents, subsidiaries, successors, assigns and sub-contractors as well as rowsandall.com.

    -

    Unenforceable provisions

    +

    Unenforceable provisions

    If any provision of this website disclaimer is, or is found to be, unenforceable under applicable law, that will not affect the enforceability of the other provisions of this website disclaimer.

    -

    Indemnity

    +

    Indemnity

    You hereby indemnify rowsandall.com and undertake to keep rowsandall.com indemnified against any losses, damages, costs, liabilities and expenses (including without limitation legal expenses and any amounts paid by rowsandall.com to a third party in settlement of a claim or dispute on the advice of rowsandall.com’s legal advisers) incurred or suffered by rowsandall.com arising out of any breach by you of any provision of these terms and conditions, or arising out of any claim that you have breached any provision of these terms and conditions.

    -

    Breaches of these terms and conditions

    +

    Breaches of these terms and conditions

    Without prejudice to rowsandall.com’s other rights under these terms and conditions, if you breach these terms and conditions in any way, rowsandall.com may take such action as rowsandall.com deems appropriate to deal with the breach, including suspending your access to the website, prohibiting you from accessing the website, blocking computers using your IP address from accessing the website, contacting your internet service provider to request that they block your access to the website and/or bringing court proceedings against you.

    -

    Variation

    +

    Variation

    rowsandall.com may revise these terms and conditions from time-to-time. Revised terms and conditions will apply to the use of this website from the date of the publication of the revised terms and conditions on this website. Please check this page regularly to ensure you are familiar with the current version.

    -

    Assignment

    +

    Assignment

    rowsandall.com may transfer, sub-contract or otherwise deal with rowsandall.com’s rights and/or obligations under these terms and conditions without notifying you or obtaining your consent.

    You may not transfer, sub-contract or otherwise deal with your rights and/or obligations under these terms and conditions.

    -

    Severability

    +

    Severability

    If a provision of these terms and conditions is determined by any court or other competent authority to be unlawful and/or unenforceable, the other provisions will continue in effect. If any unlawful and/or unenforceable provision would be lawful or enforceable if part of it were deleted, that part will be deemed to be deleted, and the rest of the provision will continue in effect.

    -

    Entire agreement

    +

    Entire agreement

    These terms and conditions constitute the entire agreement between you and rowsandall.com in relation to your use of this website, and supersede all previous agreements in respect of your use of this website.

    -

    Law and jurisdiction

    +

    Law and jurisdiction

    These terms and conditions will be governed by and construed in accordance with Czech Law and any disputes relating to these terms and conditions will be subject to the exclusive jurisdiction of the courts of The Czech Republic.

    -

    rowsandall.com’s details

    +

    rowsandall.com’s details

    The rowsandall.com site is owned by Rowsandall s.r.o., Nové sady 988/2, Staré Brno, 602 00 Brno, Czech Republic (company identification number 070 48 572)

    You can contact rowsandall.com by using the email contact form.

    -
  • -

    Privacy Policy

    {% include "privacypolicy.html" %} @@ -162,5 +159,10 @@ -
    - {% endblock content %} + +{% endblock main %} + +{% block sidebar %} +{% include 'menu_help.html' %} +{% endblock %} + diff --git a/rowers/templates/list_courses.html b/rowers/templates/list_courses.html index 559a554a..5648405d 100644 --- a/rowers/templates/list_courses.html +++ b/rowers/templates/list_courses.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -8,7 +8,7 @@ {% endblock %} -{% block content %} +{% block main %} -
    - -
    - - {% if team %} +
      +
    • +

      - {% else %} - - {% endif %} - {{ dateform.as_table }}
      {% csrf_token %} -
    -
    - -
    - {% if user.is_authenticated and user|is_manager %} - - {% else %} -   - - {% endif %} - - -
    - -{% if team %} -
    - {% include "teambuttons.html" with teamid=team.id team=team %} -
    -{% endif %} - -
    - -
    + + +

    {% if team %} -

    {{ team.name }} Team Workouts

    - {% else %} -

    Workouts of {{ rower.user.first_name }} {{ rower.user.last_name }}

    +

    +

    + {% else %} + + {% endif %} + + + +
    +

    + +
  • + + + + {{ interactiveplot |safe }} + + {{ the_div |safe }} +
  • +
  • + {% if team %} +

    {{ team.name }} Team Workouts

    + {% else %} +

    + Workouts of {{ rower.user.first_name }} {{ rower.user.last_name }} +

    + {% endif %} +
  • +
  • +

    + + {% if workouts.has_previous %} + {% if request.GET.q %} + + + + + + + {% else %} + + + + + + + {% endif %} + {% endif %} + + + Page {{ workouts.number }} of {{ workouts.paginator.num_pages }}. + + + {% if workouts.has_next %} + {% if request.GET.q %} + + + + + + + {% else %} + + + + + + + {% endif %} + {% endif %} + +

    +
  • +
  • +

    + {% if rankingonly and not team %} + + Show All Workouts + + {% elif not team %} + + Show Only Ranking Pieces + {% endif %} - +

    +
  • +
  • + {% if workouts %} @@ -115,14 +167,13 @@ {% if not team %} - {% else %} {% endif %} - + {% for workout in workouts %} {% if workout.rankingpiece %} @@ -140,8 +191,8 @@ - {% if workout.user.user == user or user == team.manager %} - {% if workout.name != '' %} + {% if workout.user.user == user or user == team.manager %} + {% if workout.name != '' %} - {% if not team %} - - {% else %} + {% if team %} {% endif %} - + {% endfor %} @@ -189,140 +236,26 @@ {% else %}

    No workouts found

    {% endif %} - - - -
    - {% if team %} -
    -
    -

    -   -

    + + {% if announcements %} +
  • +

    What's New?

    +
  • + {% for a in announcements %} +
  • +
    +
    + {{ a.created }}: + {{ a.announcement|urlize }}
    - {% endif %} -
    - - - - {{ interactiveplot |safe }} - - - - {{ the_div |safe }} -
    -
    - {% if announcements %} -

    What's New?

    - {% for a in announcements %} -
    -
    - {{ a.created }}: - {{ a.announcement|urlize }} -
    -
    - {% endfor %} -

     

    - {% endif %} -
    -
    -

    About

    -

    This site is a beta site, pioneering rowing data - visualization and analysis. No warranties. The site's author is - Sander Roosendaal. A Masters rower. - - Read his blog -

    -

    © Rowsandall s.r.o.

    -
    - - -
    -
    -
  • -
    -
    - - -
    - {% if rankingonly and not team %} - - {% elif not team %} - + + {% endfor %} {% endif %} -
    - {% if user|is_promember %} - Glue Workouts - {% else %} - Glue - {% endif %} -
    - -

     

    - {% if team %} -
    - {% else %} - - {% endif %} -
    - -
    -
    - -
    - -
    -
    - - {% if workouts.has_previous %} - {% if request.GET.q %} - < - {% else %} - < - {% endif %} - {% endif %} - - - Page {{ workouts.number }} of {{ workouts.paginator.num_pages }}. - - - {% if workouts.has_next %} - {% if request.GET.q %} - > - {% else %} - > - {% endif %} - {% endif %} - + - {% endblock %} +{% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} +{% endblock %} diff --git a/rowers/templates/manualadd.html b/rowers/templates/manualadd.html index ef7ecc78..be698545 100644 --- a/rowers/templates/manualadd.html +++ b/rowers/templates/manualadd.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% load tz %} @@ -35,33 +35,33 @@ $('#id_workouttype').change(); {% endblock %} -{% block content %} -
    -

    Add Workout Manually

    -
    - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. +{% block main %} +

    Add Workout Manually

    +
      +
    • + {% if form.errors %} +

      + Please correct the error{{ form.errors|pluralize }} below. +

      + {% endif %} + +
      +
    Max HR     Owner
    {{ workout.date|date:"Y-m-d" }} {{ workout.starttime|date:"H:i" }} {{ workout.name }} @@ -164,23 +215,19 @@ {{ workout.duration |durationprint:"%H:%M:%S.%f" }} {{ workout.averagehr }} {{ workout.maxhr }} - Export - - - {{ workout.user.user.first_name }} - {{ workout.user.user.last_name }} - + + {{ workout.user.user.first_name }} + {{ workout.user.user.last_name }} + Flex - Delete + Delete
    + {{ form.as_table }} +
    + {% csrf_token %} +

    +

    - {% endif %} - - - - {{ form.as_table }} -
    - {% csrf_token %} -
    - -
    - -
  • + + + -
    -

     

    - -
    -
    +{% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} {% endblock %} diff --git a/rowers/templates/map_view.html b/rowers/templates/map_view.html index cde62d7f..e2f3c7b7 100644 --- a/rowers/templates/map_view.html +++ b/rowers/templates/map_view.html @@ -1,10 +1,10 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}{{ workout.name }} {% endblock %} -{% block content %} +{% block main %} - - +

    {{ workout.name }}

    + +
      + +
    • +
      + {{ mapdiv|safe }} -
      - - - - {% if user.is_authenticated and mayedit %} -
      -

      - Edit Workout -

      -
      -
      -

      - Workflow View -

      - -
      -
      -

      - Advanced Edit -

      - -
      - {% endif %} -
      -
      - {{ mapdiv|safe }} - - - {{ mapscript|safe }} -
      + {{ mapscript|safe }} +
    • +
    {% endblock %} + + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/menu_analytics.html b/rowers/templates/menu_analytics.html index cd6a3911..ba495712 100644 --- a/rowers/templates/menu_analytics.html +++ b/rowers/templates/menu_analytics.html @@ -1,3 +1,5 @@ +{% load staticfiles %} +{% load rowerfilters %}

    Analysis

    -
    + +
  • - You can use the form above to change the metric or filter the data. + You can use the form to change the metric or filter the data. Set Min SPM and Max SPM to select only strokes in a certain range of stroke rates. Set Work per Stroke to a minimum value to remove "paddle" strokes or turns.

    -
  • -
    -
    + + {% endblock %} @@ -93,3 +72,7 @@ {% endblock %} + +{% block sidebar %} +{% include 'menu_analytics.html' %} +{% endblock %} diff --git a/rowers/templates/oterankings.html b/rowers/templates/oterankings.html index 278ac219..04dbec69 100644 --- a/rowers/templates/oterankings.html +++ b/rowers/templates/oterankings.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -8,175 +8,109 @@ {% block title %}Workouts{% endblock %} -{% block content %} +{% block main %} - - + + - {{ interactiveplot |safe }} +{{ interactiveplot |safe }} - - +{% if theuser %} +

    {{ theuser.first_name }}'s Ranking Pieces

    +{% else %} +

    {{ user.first_name }}'s Ranking Pieces

    +{% endif %} +
      +
    • +

      Summary for {{ theuser.first_name }} {{ theuser.last_name }} + between {{ startdate|date }} and {{ enddate|date }}

      -
      -
      - {% if theuser %} -

      {{ theuser.first_name }}'s Ranking Pieces

      - {% else %} -

      {{ user.first_name }}'s Ranking Pieces

      - {% endif %} -
      -
      - {% if user.is_authenticated and user|is_manager %} - -
      +

      The table gives the efforts you marked as Ranking Piece. + The graph shows the best segments from those pieces, plotted as + average power (over the segment) vs the duration of the segment/ + In other words: How long you can hold that power. +

      -
      -

      Summary for {{ theuser.first_name }} {{ theuser.last_name }} - between {{ startdate|date }} and {{ enddate|date }}

      +

      Whenever you load or reload the page, a new calculation is started + as a background process. The page will reload automatically when the + calculation is ready. +

      -

      Direct link for other users: - {% if workouttype == 'water' %} - https://rowsandall.com/rowers/{{ id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }} - {% else %} - https://rowsandall.com/rowers/{{ id }}/ote-ranking/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }} - {% endif %} -

      +

      At the bottom of the page, you will find predictions derived from the model.

      +
    • +
    • +

      Use this form to select a different date range:

      +

      + Select start and end date for a date range: +

      -

      The table gives the efforts you marked as Ranking Piece. - The graph shows the best segments from those pieces, plotted as - average power (over the segment) vs the duration of the segment/ - In other words: How long you can hold that power. -

      + + {{ dateform.as_table }} +
      + {% csrf_token %} + +
      +
    • +
    • -

      Whenever you load or reload the page, a new calculation is started - as a background process. The page will reload automatically when - calculation is ready.

      -

      At the bottom of the page, you will find predictions derived from the model.

      -
    -
    -

    Use this form to select a different date range:

    -

    - Select start and end date for a date range: -

    - -
    - - - {{ dateform.as_table }} -
    - {% csrf_token %} -
    -
    - -
    -
    -
    - Or use the last {{ deltaform }} days. -
    -
    - {% csrf_token %} - - -
    -
    - - - -
    - -

    Critical Power Plot

    +

    Critical Power Plot

    {{ the_div|safe }} -
    + -
    +
  • -

    Ranking Piece Results

    +

    Ranking Piece Results

    - {% if rankingworkouts %} + {% if rankingworkouts %} - - +
    + - - - - - - - + + + + + + + - - + + {% for workout in rankingworkouts %} - - - - - - - - - - + + + + + + + + + + - {% endfor %} - -
    Distance Duration Avg Power Date Avg HR Max HR Edit Distance Duration Avg Power Date Avg HR Max HR Edit
    {{ workout.distance }} m {{ workout.duration |durationprint:"%H:%M:%S.%f" }} {{ avgpower|lookup:workout.id }} W {{ workout.date }} {{ workout.averagehr }} {{ workout.maxhr }} - {{ workout.name }}
    {{ workout.distance }} m {{ workout.duration |durationprint:"%H:%M:%S.%f" }} {{ avgpower|lookup:workout.id }} W {{ workout.date }} {{ workout.averagehr }} {{ workout.maxhr }} + {{ workout.name }}
    - {% else %} -

    No ranking workouts found

    - {% endif %} + {% endfor %} + + + {% else %} +

    No ranking workouts found

    + {% endif %} -
  • + -
    -

    Pace predictions for Ranking Pieces

    +
  • +

    Pace predictions for Ranking Pieces

    -

    Add non-ranking piece using the form. The piece will be added in the prediction tables below.

    +

    Add non-ranking piece using the form. The piece will be added in the prediction tables below.

    -
    - +
    @@ -208,25 +142,25 @@
    Duration
    -
    +
  • -
    +
  • {{ form.value }} {{ form.pieceunit }} {% csrf_token %} -
  • -
    minutes -
    -
    - - -
    + + + -
    + {% endblock %} + +{% block sidebar %} +{% include 'menu_analytics.html' %} +{% endblock %} diff --git a/rowers/templates/otwinteractive.html b/rowers/templates/otwinteractive.html index 66159c5b..33898af5 100644 --- a/rowers/templates/otwinteractive.html +++ b/rowers/templates/otwinteractive.html @@ -1,83 +1,50 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}View Workout {% endblock %} -{% block content %} +{% block main %} - - + + - {{ interactiveplot |safe }} - - - +{{ interactiveplot |safe }} -
    +

    Interactive Plot

    - -

    Interactive Plot

    - - {% if user.is_authenticated and mayedit %} -
    -

    - Edit Workout -

    -
    -
    -

    - Advanced Edit -

    - -
    - - -
    - OTW Power -
    - {% endif %} -
    -
    +
      +
    • {{ the_div|safe }} -
    + +
  • +

    +

    Notes

    +
      +
    • + Is your erg pace slower than you expected? This may be a sign of room for improvement regarding your technique. An alternative explanation is that your team mates are fatter than they told you! For example, put 80.0 kg if your four consists of 2 70kg guys and 2 90kg guys. +
    • +
    • + In order to speed up the calculation, we are running the calculation only for every 10th datapoint, using interpolation in between. Some very fine pace shifts may disappear. +
    • +
    • + While the wind and stream correction is fairly reliable, the OTW to OTE conversion sometimes throws errors. Those data points are omitted and replaced by interpolated values. We are sorry if this messed up some of your plots. +
    • +
    • + Read more details about the way we calculate things here. +
    • +
    +

    +
  • -
    -

    -

    Notes

    -
      -
    • Is your erg pace slower than you expected? This may be a sign of room for improvement regarding your technique. An alternative explanation is that your team mates are fatter than they told you! For example, put 80.0 kg if your four consists of 2 70kg guys and 2 90kg guys.
    • -
    • In order to speed up the calculation, we are running the calculation only for every 10th datapoint, using interpolation in between. Some very fine pace shifts may disappear.
    • -
    • While the wind and stream correction is fairly reliable, the OTW to OTE conversion sometimes throws errors. Those data points are omitted and replaced by interpolated values. We are sorry if this messed up some of your plots.
    • -
    • Read more details about the way we calculate things here.
    • -
    -

    + -
    +{% endblock %} -{% endblock %} \ No newline at end of file +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/otwrankings.html b/rowers/templates/otwrankings.html index 140c007b..dd56f3c4 100644 --- a/rowers/templates/otwrankings.html +++ b/rowers/templates/otwrankings.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -8,175 +8,111 @@ {% block title %}Workouts{% endblock %} -{% block content %} +{% block main %} - - + + - {{ interactiveplot |safe }} - - - +{{ interactiveplot |safe }} -
    -
    - {% if theuser %} -

    {{ theuser.first_name }}'s Ranking Pieces

    - {% else %} -

    {{ user.first_name }}'s Ranking Pieces

    - {% endif %} -
    -
    - {% if user.is_authenticated and user|is_manager %} - -
    +{% if theuser %} +

    {{ theuser.first_name }}'s Ranking Pieces

    +{% else %} +

    {{ user.first_name }}'s Ranking Pieces

    +{% endif %} -
    -

    Summary for {{ theuser.first_name }} {{ theuser.last_name }} - between {{ startdate|date }} and {{ enddate|date }}

    +
      -

      Direct link for other users: - {% if workouttype == 'water' %} - https://rowsandall.com/rowers/{{ id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }} - {% else %} - https://rowsandall.com/rowers/{{ id }}/ote-ranking/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }} - {% endif %} -

      +
    • +

      Summary for {{ theuser.first_name }} {{ theuser.last_name }} + between {{ startdate|date }} and {{ enddate|date }}

      -

      The table gives the efforts you marked as Ranking Piece. - The graph shows the best segments from those pieces, plotted as - average power (over the segment) vs the duration of the segment/ - In other words: How long you can hold that power. -

      +

      The table gives the efforts you marked as Ranking Piece. + The graph shows the best segments from those pieces, plotted as + average power (over the segment) vs the duration of the segment/ + In other words: How long you can hold that power. +

      -

      When you change the date range, the algorithm calculates new - parameters in a background process. You may have to reload the - page to get an updated prediction.

      -

      At the bottom of the page, you will find predictions derived from the model.

      -
    -
    -

    Use this form to select a different date range:

    -

    - Select start and end date for a date range: -

    +

    When you change the date range, the algorithm calculates new + parameters in a background process. You may have to reload the + page to get an updated prediction.

    +

    At the bottom of the page, you will find predictions derived from the model.

    + +
  • +

    Use this form to select a different date range:

    +

    + Select start and end date for a date range: +

    - - - - {{ dateform.as_table }} -
    - {% csrf_token %} -
  • -
    - -
    -
    -
    - Or use the last {{ deltaform }} days. -
    -
    - {% csrf_token %} - - -
    -
    + + {{ dateform.as_table }} +
    + {% csrf_token %} + + + +
  • -
    - -

    Critical Power Plot

    - +

    Critical Power Plot

    + {{ the_div|safe }} -
    +
  • -
    +
  • -

    Ranking Piece Results

    +

    Ranking Piece Results

    - {% if rankingworkouts %} - - - + {% if rankingworkouts %} + +
    + - - - - - - - + + + + + + + - - + + {% for workout in rankingworkouts %} - - - - - - - - - - + + + + + + + + + + - {% endfor %} - -
    Distance Duration Avg Power Date Avg HR Max HR Edit Distance Duration Avg Power Date Avg HR Max HR Edit
    {{ workout.distance }} m {{ workout.duration |durationprint:"%H:%M:%S.%f" }} {{ avgpower|lookup:workout.id }} W {{ workout.date }} {{ workout.averagehr }} {{ workout.maxhr }} - {{ workout.name }}
    {{ workout.distance }} m {{ workout.duration |durationprint:"%H:%M:%S.%f" }} {{ avgpower|lookup:workout.id }} W {{ workout.date }} {{ workout.averagehr }} {{ workout.maxhr }} + {{ workout.name }}
    - {% else %} -

    No ranking workouts found

    - {% endif %} + {% endfor %} + + + {% else %} +

    No ranking workouts found

    + {% endif %} + +
  • -
    +
  • +

    Pace predictions for Ranking Pieces

    -
    -

    Pace predictions for Ranking Pieces

    +

    Add non-ranking piece using the form. The piece will be added in the prediction tables below.

    + + -

    Add non-ranking piece using the form. The piece will be added in the prediction tables below.

    - - - -
    - +
    @@ -200,25 +136,24 @@
    Duration
    -
    - -
    +
  • + +
  • {{ form.value }} {{ form.pieceunit }} {% csrf_token %} -
  • -
    - minutes -
    -
    - - -
    + minutes + + + + -
    - +{% endblock %} + +{% block sidebar %} +{% include 'menu_analytics.html' %} {% endblock %} diff --git a/rowers/templates/otwsetpower.html b/rowers/templates/otwsetpower.html index 7a997bed..d9b56935 100644 --- a/rowers/templates/otwsetpower.html +++ b/rowers/templates/otwsetpower.html @@ -1,75 +1,74 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Advanced Features {% endblock %} -{% block content %} -
    +{% block main %} +

    Run OTW Power Calculations

    +
      +
    • +

      + For the advanced OTW power and wind correction calculation, + we need to know the boat type and the average weight per crew member. + Currently only 1x (single) is implemented. Setting the value to + 2x (double) will still run the calculations for a single. + We use FISA minimum boat weight and standard rigging for our calculations. +

      -

      Run OTW Power Calculations

      -
      -

      - Edit Workout -

      -
      -
      -

      - Workflow View -

      +

      The Quick calculation option potentially speeds up the calculation, + at the cost of a slight reduction in accuracy. It is recommended + to keep this option selected.

      -
      -
      -

      - Advanced Edit -

      - -
      -
      -

      - For the advanced OTW power and wind correction calculation, - we need to know the boat type and the average weight per crew member. - Currently only 1x (single) is implemented. Setting the value to - 2x (double) will still run the calculations for a single. - We use FISA minimum boat weight and standard rigging for our calculations. -

      - -

      The Quick calculation option potentially speeds up the calculation, - at the cost of a slight reduction in accuracy. It is recommended - to keep this option selected.

      +

      + The power calculations take wind and stream as inputs. +

      +

      + Set wind strength and direction +

      + +

      + + Set river stream strength + +

      + +
    • +
    • - {% if form.errors %} -

      - Please correct the error{{ form.errors|pluralize }} below. -

      - {% endif %} - - - {{ form.as_table }} -
      - {% csrf_token %} -
    -
    + {% if form.errors %} +

    + Please correct the error{{ form.errors|pluralize }} below. +

    + {% endif %} + + + {{ form.as_table }} +
    + {% csrf_token %} +

    - Start the calculations to get power values for your row. + Start the calculations to get power values for your row. +
    -
    - - -
    - -
    -
    - -
    -
    - The Rowsandall Physics Department at work. -
    -
    - + + +
  • +
    + +
    +
    + The Rowsandall Physics Department at work. +
    +
  • + {% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/panel_comments.html b/rowers/templates/panel_comments.html index 99de3bbc..3250e4fd 100644 --- a/rowers/templates/panel_comments.html +++ b/rowers/templates/panel_comments.html @@ -1,31 +1,29 @@ {% load rowerfilters %} {% load tz %} -
    - - -{% localtime on %} - -{% endlocaltime %} - - - - - - - - - - + + +
    Date/Time:{{ workout.startdatetime|localtime}}
    Distance:{{ workout.distance }}m
    Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
    Public link to this workout - https://rowsandall.com/rowers/workout/{{ workout.id }} -
    Comments +
    + + + diff --git a/rowers/templates/panel_map.html b/rowers/templates/panel_map.html index 9fd40d8a..77cfda5a 100644 --- a/rowers/templates/panel_map.html +++ b/rowers/templates/panel_map.html @@ -1,9 +1,13 @@ -
    - {{ mapdiv|safe }} - - - {{ mapscript|safe }} -
    +
      +
    • +
      + {{ mapdiv|safe }} + + + {{ mapscript|safe }} +
      +
    • +
    diff --git a/rowers/templates/panel_middlesocial.html b/rowers/templates/panel_middlesocial.html index b1c917d2..bf8e07a2 100644 --- a/rowers/templates/panel_middlesocial.html +++ b/rowers/templates/panel_middlesocial.html @@ -1,14 +1,13 @@ -
    -
    +

    Share -
    - +

    diff --git a/rowers/templates/panel_shortcomment.html b/rowers/templates/panel_shortcomment.html index 6774494b..800d8706 100644 --- a/rowers/templates/panel_shortcomment.html +++ b/rowers/templates/panel_shortcomment.html @@ -1,6 +1,7 @@ {% load rowerfilters %} {% load tz %} -
    +
      +
    • @@ -10,4 +11,5 @@
      Comments
      -
    + + diff --git a/rowers/templates/panel_statcharts.html b/rowers/templates/panel_statcharts.html index 39b6de59..ec79eea0 100644 --- a/rowers/templates/panel_statcharts.html +++ b/rowers/templates/panel_statcharts.html @@ -1,11 +1,14 @@ {% if statcharts %}

    Static Charts

    -{% for graph in statcharts %} -
    - - {{ graph.filename }} -
    -{% endfor %} -{% endif %} +
      + {% for graph in statcharts %} +
    • + + {{ graph.filename }} + +
    • + {% endfor %} + {% endif %} +
    diff --git a/rowers/templates/panel_stats.html b/rowers/templates/panel_stats.html index 248127f8..68543efc 100644 --- a/rowers/templates/panel_stats.html +++ b/rowers/templates/panel_stats.html @@ -1,5 +1,3 @@ -
    -

    - Workout Stats -

    -
    +

    + Workout Stats +

    diff --git a/rowers/templates/physics.html b/rowers/templates/physics.html index efc5757d..bdea3627 100644 --- a/rowers/templates/physics.html +++ b/rowers/templates/physics.html @@ -1,166 +1,164 @@ +{% extends "newbase.html" %} +{% block title %}About us{% endblock title %} +{% block main %} - {% extends "base.html" %} - {% block title %}About us{% endblock title %} - {% block content %} +

    How we calculate things

    -
    -

    How we calculate things

    -

    You are reading this because you want to understand how the wind/stream conversion and the conversion from OTW pace to OTE pace works.

    - -

    The conversions are done using a one-dimensional mechanical model that is -introduced here. -The model takes into account, among others, the following parameters: -

      +
        +
      • +

        You are reading this because you want to understand how the wind/stream conversion and the conversion from OTW pace to OTE pace works.

        + +

        The conversions are done using a one-dimensional mechanical model that is + introduced here. + The model takes into account, among others, the following parameters: +

        • Stroke rate
        • Stroke length
        • Rigging parameters
        • Rower and boat weight
        • -
        -For this site, we use "standard" rigging parameters, blade shapes, and FISA minimum boat weights. For the eight, I add the weight of a cox (at the FISA minimum weight). The stroke length is also set at a fixed value, but in the future I -will allow you to adjust to your own stroke length. -

        -

        -Knowing boat type (rigging), pace and stroke rate, and taking into account -the influence of wind and stream (if provided), I am able to find the -mechanical power that you provide to the rowing system by a reverse -calculation. That is, I vary the input force until I find the one that -corresponds to your actual pace at that stroke rate. -

        -

        -Knowing the power, I can calculate how fast you would have gone without -external wind and stream influences by running the calculation in a forward -way, using the power and force profile found. This is the wind/stream -corrected pace, which I think is useful to know and be able to compare -from training to training and between different rowing venues. -

        +
      + For this site, we use "standard" rigging parameters, blade shapes, and FISA minimum boat weights. For the eight, I add the weight of a cox (at the FISA minimum weight). The stroke length is also set at a fixed value, but in the future I + will allow you to adjust to your own stroke length. +

      +

      + Knowing boat type (rigging), pace and stroke rate, and taking into account + the influence of wind and stream (if provided), I am able to find the + mechanical power that you provide to the rowing system by a reverse + calculation. That is, I vary the input force until I find the one that + corresponds to your actual pace at that stroke rate. +

      +

      + Knowing the power, I can calculate how fast you would have gone without + external wind and stream influences by running the calculation in a forward + way, using the power and force profile found. This is the wind/stream + corrected pace, which I think is useful to know and be able to compare + from training to training and between different rowing venues. +

      + +

      + Using another algorithm to calculate total mechanical power on an erg, I can + calculate what the erg display would show you if you rowed on the erg with + the same average power, at the same stroke rate. The calculations are done + for a statical Concept2 erg with a fairly standard drag factor. I cannot + take into account the fact that you may use different technique or would + row at a different stroke rate on the erg. +

      -

      -Using another algorithm to calculate total mechanical power on an erg, I can -calculate what the erg display would show you if you rowed on the erg with -the same average power, at the same stroke rate. The calculations are done -for a statical Concept2 erg with a fairly standard drag factor. I cannot -take into account the fact that you may use different technique or would -row at a different stroke rate on the erg. -

      - -

      -It is important to understand that the Power display on the erg is not showing -you the complete picture. In my calculations, I use my proprietary algorithms -to calculate the additional power that goes into moving your body weight up -and down the slide on a static erg. To get the most accurate results, it is -important to be honest about your weight and set it independently for each -workout. -

      - -

      Not taken into account are the following factors: -

        +

        + It is important to understand that the Power display on the erg is not showing + you the complete picture. In my calculations, I use my proprietary algorithms + to calculate the additional power that goes into moving your body weight up + and down the slide on a static erg. To get the most accurate results, it is + important to be honest about your weight and set it independently for each + workout. +

        + +

        Not taken into account are the following factors: +

        • Water Temperature
        • Heavier/shorter/wider boats than the ones used by the elite
        • Bungees, weed, or other artefacts slowing down the boat
        • Boat stopping technique flaws
        • Effect of wave height or cross-wind
        • -
        -The water temperature has a small but measurable effect on the water density -(and thus on the drag). I am using the value at 20 degrees C, which is a -good average over the OTW season for a lake in a temperate climate. All -the other elements result in an equivalent erg pace that is probably slower -than what you can achieve on the erg. So look at it as an incentive to -improve your technique (big effect) and/or buy a faster boat (minor effect). -If your OTW to OTE pace conversion results in numbers close to what you -normally achieve on the erg, you are rowing like an elite rower (but possibly -at a lower power)! When I get around it, I will try to model the effect of cross-wind. -

        +
      + The water temperature has a small but measurable effect on the water density + (and thus on the drag). I am using the value at 20 degrees C, which is a + good average over the OTW season for a lake in a temperate climate. All + the other elements result in an equivalent erg pace that is probably slower + than what you can achieve on the erg. So look at it as an incentive to + improve your technique (big effect) and/or buy a faster boat (minor effect). + If your OTW to OTE pace conversion results in numbers close to what you + normally achieve on the erg, you are rowing like an elite rower (but possibly + at a lower power)! When I get around it, I will try to model the effect of cross-wind. +

      + +

      + I have checked the model both from a Physics perspective (I have a degree in + Physics, if you are interested) and compared with the data available. + An important data set has been published here by Dr Kleshnev. For sculling, + my algorithms are extremely close in reproducing that data set. For sweep + rowing, I am still fine tuning some parameters, but I am close for a pair and + a four. I had to make assumptions about Kleshnev's data, especially about the + stroke rate, but as I got realistic stroke rates (35 and higher) for world + record performance, I am quite confident. +

      + +

      + On top of that I am constantly comparing the model's results to my own sculling + and rowing, and I will be the first to admit flaws and correct them. So please + contact me if there are any inconsistencies, suspicions, questions or simply + if you want to chat about Rowing Physics. +

      -

      -I have checked the model both from a Physics perspective (I have a degree in -Physics, if you are interested) and compared with the data available. -An important data set has been published here by Dr Kleshnev. For sculling, -my algorithms are extremely close in reproducing that data set. For sweep -rowing, I am still fine tuning some parameters, but I am close for a pair and -a four. I had to make assumptions about Kleshnev's data, especially about the -stroke rate, but as I got realistic stroke rates (35 and higher) for world -record performance, I am quite confident. -

      + +
    • +

      Manual

      -

      -On top of that I am constantly comparing the model's results to my own sculling -and rowing, and I will be the first to admit flaws and correct them. So please -contact me if there are any inconsistencies, suspicions, questions or simply -if you want to chat about Rowing Physics. -

      - - -
    -
    -

    Manual

    - -

    Here's the best way - in my mind - to use the Rowing Physics -functionality. I am assuming you have successfully uploaded or imported -a rowing workout. You must have position data (lat/long) with your row. A -TCX from CrewNerd or RiM or a workout imported from SportTracks or Strava -(where you see a map of your workout on those sites) should have those -data. I am working on adding the FIT file format that is used by SpeedCoach -GPS. For now, export the data to Strava and then import them here. -

    - -

    Recipe for success: -

      +

      Here's the best way - in my mind - to use the Rowing Physics + functionality. I am assuming you have successfully uploaded or imported + a rowing workout. You must have position data (lat/long) with your row. A + TCX from CrewNerd or RiM or a workout imported from SportTracks or Strava + (where you see a map of your workout on those sites) should have those + data. I am working on adding the FIT file format that is used by SpeedCoach + GPS. For now, export the data to Strava and then import them here. +

      + +

      Recipe for success: +

      1. Click on the workout. This will bring you to the workout Edit view
      2. -
      3. Click on the "Advanced" button
      4. -
      5. Click on "Geeky Stuff"
      6. -
      7. Look for three buttons labelled "Edit Wind Data", "Edit Stream +
      8. Look for three menu items labelled "Edit Wind Data", "Edit Stream Data" and "OTW Power"
      9. If you have wind or stream data, click on the appropriate - button and enter your data.
      10. -
      11. If you have both wind and stream, click the shortcut button - on the respective page to take you to the other parameter
      12. + menu item and enter your data.
      13. Click on OTW Power
      14. Select the boat type and enter the average weight per crew member. Do not use the crew total weight.
      15. Click "Update & Run"
      16. Go do something else. You will receive an email when the calculations are finished. The calculation itself will take about 10 minutes for an -hour long row, but there may be other people's calculations in the queue, so + hour long row, but there may be other people's calculations in the queue, so it may take longer.
      17. Progress can be monitored by clicking on "here" in the message at the top of the page advising that the calculation has begun.
      18. -
      19. - When the calculation is complete, go back to the "Geeky Stuff" page - and click on "Corrected Pace Plot" to see the result. From here, you can re-run the calculation with different parameters.
      20. -
      -

      +
    +

    + +

    + Once you have run the calculation, the boat type, average crew weight, + Power and corrected pace data are stored permanently on the site. If you would + export the data to Strava or SportTracks now, those sites will have the + Power data. +

    + +

    Why does the calculation take so much time?

    -

    -Once you have run the calculation, the boat type, average crew weight, -Power and corrected pace data are stored permanently on the site. If you would -export the data to Strava or SportTracks now, those sites will have the -Power data. -

    +

    I am running the calculations from a first principles base, so for + each data point that I am calculating, I am finding the stroke average + force, then calculating corrected pace (wind/stream) and finding the + corresponding erg power. I am not taking any shortcuts. The advantage + of this approach is that I can give you numbers irrespective of your + weight, speed, stroke rate, sex, etc. The model can deal with circumstances + it has not encountered before. The downside is that it takes time.

    + +

    + A much faster approach would be to simply take pre-calculated data from + a table and interpolate. The advantage of this approach is is speed. The + disadvantage is that extrapolation outside the limits of the available + data is dangerous and will lead to erroneous results.

    + +

    Future versions of this site will use a hybrid approach + but only for pace/wind/stream/stroke rate/weight combinations that I consider + well validated. For that, I need to collect data, so keep the workouts coming! +

    + + + + + -

    Why does the calculation take so much time?

    +{% endblock %} -

    I am running the calculations from a first principles base, so for -each data point that I am calculating, I am finding the stroke average -force, then calculating corrected pace (wind/stream) and finding the -corresponding erg power. I am not taking any shortcuts. The advantage -of this approach is that I can give you numbers irrespective of your -weight, speed, stroke rate, sex, etc. The model can deal with circumstances -it has not encountered before. The downside is that it takes time.

    - -

    -A much faster approach would be to simply take pre-calculated data from -a table and interpolate. The advantage of this approach is is speed. The -disadvantage is that extrapolation outside the limits of the available -data is dangerous and will lead to erroneous results.

    - -

    Future versions of this site will use a hybrid approach -but only for pace/wind/stream/stroke rate/weight combinations that I consider -well validated. For that, I need to collect data, so keep the workouts coming! -

    - - - -
    - - {% endblock content %} +{% block sidebar %} +{% include 'menu_help.html' %} +{% endblock %} diff --git a/rowers/templates/plannedsession_multicreate.html b/rowers/templates/plannedsession_multicreate.html index 2bf4c0b4..3f8f8ffa 100644 --- a/rowers/templates/plannedsession_multicreate.html +++ b/rowers/templates/plannedsession_multicreate.html @@ -1,118 +1,65 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Plan entire microcycle{% endblock %} -{% block content %} -
    - {% include "planningbuttons.html" %} -
    +{% block main %} +

    Create Sessions for {{ rower.user.first_name }} {{ rower.user.last_name }}

    -
    -
    -

    Create Sessions for {{ rower.user.first_name }} {{ rower.user.last_name }}

    -
    - -{% if user.is_authenticated and user|is_manager %} - -{% endif %} -
    - -
    -

    - On this page, you can create and edit sessions for an entire time - period. You see a list of the current sessions planned for the - selected time period. Each row in the table is a session. You can - remove a session by clicking "remove" at the end of a row. - You can edit the date in the forms. If you need to add a new session, - click the "Add More" button to add a new session. Use the "Submit" - button to commit any changes you made. +

      +
    • +

      + On this page, you can create and edit sessions for an entire time + period. You see a list of the current sessions planned for the + selected time period. Each row in the table is a session. You can + remove a session by clicking "remove" at the end of a row. + You can edit the date in the forms. If you need to add a new session, + click the "Add More" button to add a new session. Use the "Submit" + button to commit any changes you made.

      -
      - {% csrf_token %} - {{ ps_formset.management_form }} - - - - - {% for field in ps_formset.0.visible_fields %} - - {% endfor %} - - - - {% for form in ps_formset %} - - +
       {{ field.label_tag }}
      {{ forloop.counter }} - {% if form.instance.pk %}{{ form.DELETE }}{% endif %} - {{ form.id }} - {% for field in form.hidden_fields %} - {{ field }} + + {% csrf_token %} + {{ ps_formset.management_form }} + + + + + {% for field in ps_formset.0.visible_fields %} + {% endfor %} - {% for field in form.visible_fields %} - + + + + {% for form in ps_formset %} + + + {% endfor %} + {% endfor %} - - {% endfor %} - -
       {{ field.label_tag }} - {{ field }} -
      {{ forloop.counter }} + {% if form.instance.pk %}{{ form.DELETE }}{% endif %} + {{ form.id }} + {% for field in form.hidden_fields %} + {{ field }} + {% endfor %} + {% for field in form.visible_fields %} + + {{ field }} +
      - Add More - - Clone multiple sessions +
      + + Add More + + or + + Clone multiple sessions + +
      +
    • +
    -
    - -
    - -
    -{% endblock %} - -{% block scripts %} @@ -200,3 +147,7 @@ {% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} diff --git a/rowers/templates/plannedsessioncreate.html b/rowers/templates/plannedsessioncreate.html index d803e32c..e66d61d0 100644 --- a/rowers/templates/plannedsessioncreate.html +++ b/rowers/templates/plannedsessioncreate.html @@ -1,71 +1,14 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}New Planned Session{% endblock %} -{% block content %} -
    - {% include "planningbuttons.html" %} -
    +{% block main %} +

    Create Sessions for {{ rower.user.first_name }} {{ rower.user.last_name }}

    -
    -
    -

    Create Sessions for {{ rower.user.first_name }} {{ rower.user.last_name }}

    -
    - -{% if user.is_authenticated and user|is_manager %} - - -{% endif %} -
    - Courses -
    -
    - -
    - - - - -
    + +
  • New Session

    {% if form.errors %} @@ -130,15 +70,12 @@ {{ form.as_table }} {% csrf_token %} -
    - -
    -
    -
  • -
    -
    + + +
    + + -
    {% endblock %} {% block scripts %} @@ -240,3 +177,7 @@ {% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} diff --git a/rowers/templates/plannedsessiondeleteconfirm.html b/rowers/templates/plannedsessiondeleteconfirm.html index 0a6f3d7f..a299e6b0 100644 --- a/rowers/templates/plannedsessiondeleteconfirm.html +++ b/rowers/templates/plannedsessiondeleteconfirm.html @@ -1,45 +1,43 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% block title %}Planned Session{% endblock %} -{% block content %} -
    - {% include "planningbuttons.html" %} - -
    -
    -

    Confirm Delete

    -

    This will permanently delete the planned session

    +{% block main %} +

    Confirm Delete

    +

    This will permanently delete the planned session

    - -
    +
    - -
    -

    - Delete +

    + {% csrf_token %} +

    Are you sure you want to delete {{ object }}?

    + +

    -
    + -
    - +
  • +

    Session {{ psdict.name.1 }}

    + + {% for attr in attrs %} + {% for key,value in psdict.items %} + {% if key == attr %} + + + + {% endif %} + {% endfor %} + {% endfor %} +
    {{ value.0 }}{{ value.1 }}
    +
  • + +{% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} {% endblock %} diff --git a/rowers/templates/plannedsessionedit.html b/rowers/templates/plannedsessionedit.html index deb11173..a3adbdfe 100644 --- a/rowers/templates/plannedsessionedit.html +++ b/rowers/templates/plannedsessionedit.html @@ -1,65 +1,21 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Update Planned Session{% endblock %} -{% block content %} -
    - {% include "planningbuttons.html" %} - -
    -
    -
    -

    Edit Session

    -
    - +{% block main %} +

    Edit Session

    {% if user.is_authenticated and user|is_manager %} - +

    {% endif %} -
    - Courses -
    -
    -
    - - -
    -

    {{ thesession.name }}

    + +
  • +

    {{ thesession.name }}

    {% if form.errors %}

    Please correct the error{{ form.errors|pluralize }} below.

    {% endif %} - +

    {{ form.as_table }}
    +

    {% csrf_token %} -
    - Delete +
    +
    -
    - Clone -
    -
    - -
    -
    +

    + Delete + Clone +

    + + -
    +
  • + -
    - - - -
    {% endblock %} +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} + + {% block scripts %} - -
    - {% include "planningbuttons.html" %} -
    -
    -

    Clone Multiple Sessions

    -
    - -{% if user.is_authenticated and user|is_manager %} - -
    -
    -
    - {% endif %} -
    - - {{ dateform.as_table }} -
    - {% csrf_token %} -
    -
    - -
    -
    -
    -
    -
    -
    - -
    -
    - -
    -
    -
    - -
    +

    Clone Multiple Sessions for {{ rower.user.first_name }} {{ rower.user.last_name }}

    -
    +
      +
    • - {% if plannedsessions %} - - Toggle All
      - - - {{ form.as_table }} -
      - -{% else %} -

      No sessions found

      -{% endif %} -
    -
    -

    Select one or more planned sessions on the left, - select the date when the new cycle starts below - and press submit

    - {% csrf_token %} - - {{ dateshiftform.as_table }} -
    -
    -

    - + {% if plannedsessions %} + + Toggle All
    + + + {{ form.as_table }} +
    + + {% else %} +

    No sessions found

    + {% endif %} + +
  • +

    Select one or more planned sessions on the left, + select the date when the new cycle starts below + and press submit

    + {% csrf_token %} + + {{ dateshiftform.as_table }} +
    +

    +

    -
  • -
    -

    You can use the date and search forms above to search through all +

    You can use the date and search forms above to search through all sessions.

    -
    -
    + +
    {% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} diff --git a/rowers/templates/plannedsessions_print.html b/rowers/templates/plannedsessions_print.html index 34b065fc..f09bb13e 100644 --- a/rowers/templates/plannedsessions_print.html +++ b/rowers/templates/plannedsessions_print.html @@ -1,100 +1,47 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Planned Sessions{% endblock %} -{% block content %} -
    - {% include "planningbuttons.html" %} -
    -
    -

    Plan for {{ rower.user.first_name }} {{ rower.user.last_name }}

    -

    - From {{ startdate }} to {{ enddate }} -

    -
    - -{% if user.is_authenticated and user|is_manager %} - -{% endif %} -
    - Courses -
    +{% block main %} +

    Plan for {{ rower.user.first_name }} {{ rower.user.last_name }}

    + {% for ps in plannedsessions %} -
    -

    Session {{ ps.name }}

    +

    Session {{ ps.name }}

    - + - + - + - + - + - + - + - +
    On or after{{ ps.startdate }}On or after{{ ps.startdate }}
    On or before{{ ps.enddate }}On or before{{ ps.enddate }}
    Session Type{{ ps.sessiontype }}Session Type{{ ps.sessiontype }}
    Session Mode{{ ps.sessionmode }}Session Mode{{ ps.sessionmode }}
    Criteria{{ ps.criterium }}Criteria{{ ps.criterium }}
    Value{{ ps.sessionvalue }}Value{{ ps.sessionvalue }}
    Unit{{ ps.sessionunit }}Unit{{ ps.sessionunit }}
    Comment{{ ps.comment|linebreaks }}Comment{{ ps.comment|linebreaks }}
    -
    {% endfor %} +{% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} {% endblock %} diff --git a/rowers/templates/plannedsessionscoach.html b/rowers/templates/plannedsessionscoach.html index 170de818..5f7310b5 100644 --- a/rowers/templates/plannedsessionscoach.html +++ b/rowers/templates/plannedsessionscoach.html @@ -1,197 +1,136 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Planned Sessions{% endblock %} -{% block content %} -
    - {% include "planningbuttons.html" %} -
    -
    - {% if theteam %} -

    Coach Overview. Team {{ theteam.name }}

    - {% else %} -

    Coach Overview

    - {% endif %} -
    - -{% if user.is_authenticated and user|is_manager %} - - +{% block main %} +{% if theteam %} +

    Coach Overview. Team {{ theteam.name }}

    +{% else %} +

    Coach Overview

    {% endif %} -
    - - - - - - - - {% for r in rowers %} - - - {% endfor %} - -
    On or afterOn or beforePreferred dateName
    - {{ r.user.first_name }} {{ r.user.last_name }} + + + + + + + + + {% for r in rowers %} + - {% endfor %} - - - - {% for thedict in statusdict %} - - - - - - {% for r in rowers %} - - {% endfor %} - - {% endfor %} - -
    On or afterOn or beforePreferred dateName
    + {{ r.user.first_name }} {{ r.user.last_name }}
    - {{ thedict|lookup:'startdate'|date:"Y-m-d" }} - - {{ thedict|lookup:'enddate'|date:"Y-m-d" }} - - {{ thedict|lookup:'preferreddate'|date:"Y-m-d" }} - - - {% if thedict|lookup:'name' %} - {{ thedict|lookup:'name' }} - {% else %} - Unnamed Session - {% endif %} - - - {% if thedict|lookup:'results'|lookup:r.id == 'completed' %} -   - {% elif thedict|lookup:'results'|lookup:r.id == 'partial' %} -   - {% elif thedict|lookup:'results'|lookup:r.id == 'not done' %} -   - {% elif thedict|lookup:'results'|lookup:r.id == 'not assigned' %} -   - {% else %} -   - {% endif %} -
    - - {% if unmatchedworkouts %} -

    Workouts that are not linked to any session

    - - - - - - - - - - - - - - - - {% for workout in unmatchedworkouts %} - - - - - {% if workout.user.user == user or user == team.manager %} - {% if workout.name != '' %} - - {% else %} - - {% endif %} - {% else %} - {% if workout.name != '' %} - - {% else %} - - {% endif %} - {% endif %} - - - - - - {% endfor %} - + + + + {% for thedict in statusdict %} + + + + + + {% for r in rowers %} + + {% endfor %} + + {% endfor %} + +
    Rower Date Time Name Type Distance Duration Avg HR Max HR
    {{ workout.user.user.first_name }} {{ workout.user.user.last_name }} {{ workout.date|date:"Y-m-d" }} {{ workout.starttime|date:"H:i" }} - - {{ workout.name }} - - - No Name - {{ workout.name }}No Name {{ workout.workouttype }} {{ workout.distance }}m {{ workout.duration |durationprint:"%H:%M:%S.%f" }} {{ workout.averagehr }} {{ workout.maxhr }}
    + {{ thedict|lookup:'startdate'|date:"Y-m-d" }} + + {{ thedict|lookup:'enddate'|date:"Y-m-d" }} + + {{ thedict|lookup:'preferreddate'|date:"Y-m-d" }} + + + {% if thedict|lookup:'name' %} + {{ thedict|lookup:'name' }} + {% else %} + Unnamed Session + {% endif %} + + + {% if thedict|lookup:'results'|lookup:r.id == 'completed' %} +   + {% elif thedict|lookup:'results'|lookup:r.id == 'partial' %} +   + {% elif thedict|lookup:'results'|lookup:r.id == 'not done' %} +   + {% elif thedict|lookup:'results'|lookup:r.id == 'not assigned' %} +   + {% else %} +   + {% endif %} +
    + +{% if unmatchedworkouts %} +

    Workouts that are not linked to any session

    + + + + + + + + + + + + + + + + {% for workout in unmatchedworkouts %} + + + + + {% if workout.user.user == user or user == team.manager %} + {% if workout.name != '' %} + + {% else %} + + {% endif %} + {% else %} + {% if workout.name != '' %} + + {% else %} + + {% endif %} + {% endif %} + + + + + + + {% endfor %} +
    Rower Date Time Name Type Distance Duration Avg HR Max HR
    {{ workout.user.user.first_name }} {{ workout.user.user.last_name }} {{ workout.date|date:"Y-m-d" }} {{ workout.starttime|date:"H:i" }} + + {{ workout.name }} + + + No Name + {{ workout.name }}No Name {{ workout.workouttype }} {{ workout.distance }}m {{ workout.duration |durationprint:"%H:%M:%S.%f" }} {{ workout.averagehr }} {{ workout.maxhr }}
    - - {% endif %} -
    +{% endif %} + {% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} diff --git a/rowers/templates/plannedsessionsmanage.html b/rowers/templates/plannedsessionsmanage.html index 6b8e4f66..35311a35 100644 --- a/rowers/templates/plannedsessionsmanage.html +++ b/rowers/templates/plannedsessionsmanage.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -16,119 +16,71 @@ {% endblock %} -{% block content %} -
    - {% include "planningbuttons.html" %} -
    -
    -
    -

    Manage Plan Execution for {{ rower.user.first_name }} {{ rower.user.last_name }}

    -
    +{% block main %} +

    Manage Plan Execution for {{ rower.user.first_name }} {{ rower.user.last_name }}

    - -{% if user.is_authenticated and user|is_manager %} - -{% endif %} -
    -
    -

    Select one session on the left, and one or more workouts on the right - to match the workouts to the session. For tests and training sessions, - the selected workouts must be done on the same date. For all sessions, - the workout dates must be between the start and end date for the - session. -

    -

    - If you select a workout that has already been matched to another session, - it will change to match this session. -

    -

    - Workouts marked with a red check mark () - are currently linked to one of your sessions. A workout can only be assigned to - one session at a time. -

    -
    -
    Select one session on the left, and one or more workouts on the right + to match the workouts to the session. For tests and training sessions, + the selected workouts must be done on the same date. For all sessions, + the workout dates must be between the start and end date for the + session. +

    +

    + If you select a workout that has already been matched to another session, + it will change to match this session. +

    +

    + Workouts marked with a red check mark () + are currently linked to one of your sessions. A workout can only be assigned to + one session at a time. +

    + + -
    -
    -

    Planned Sessions

    - - - {% for field in ps_form.hidden_fields %} - {{ field }} - {% endfor %} - {% for field in ps_form.visible_fields %} - - {% endfor %} - +
      +
    • +

      Planned Sessions

      +
    {{ field }}
    + + {% for field in ps_form.hidden_fields %} + {{ field }} + {% endfor %} + {% for field in ps_form.visible_fields %} + + {% endfor %} +
    {{ field }}
    -
    -
    -

    Workouts

    - - - {% for field in w_form.hidden_fields %} - {{ field }} - {% endfor %} - {% for field in w_form.visible_fields %} - - {% endfor %} - -
    - {{ field }} -
    -
    -
    -
    + +
  • +

    Workouts

    + + + {% for field in w_form.hidden_fields %} + {{ field }} + {% endfor %} + {% for field in w_form.visible_fields %} + + {% endfor %} + +
    + {{ field }} +
    +
  • +
  • {% csrf_token %} -
  • + +
    {% endblock %} +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} + + {% block scripts %} {% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} diff --git a/rowers/templates/plannedsessionteamedit.html b/rowers/templates/plannedsessionteamedit.html index 016a13c5..3e36dfe5 100644 --- a/rowers/templates/plannedsessionteamedit.html +++ b/rowers/templates/plannedsessionteamedit.html @@ -1,151 +1,106 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}New Planned Session{% endblock %} -{% block content %} -
    - {% include "planningbuttons.html" %} -
    +{% block main %} +

    Edit Team Session

    +
    + {% if form.errors %} +

    + Please correct the error{{ form.errors|pluralize }} below. +

    + {% endif %} + {% csrf_token %} -
    -
    -

    Edit Team Session

    -
    - -
    - -
    - - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. -

    - {% endif %} - {% csrf_token %} - -
    - Delete -
    -

    - {% endif %} -
    - - - -
    -

    Session {{ plannedsession.name }}

    +

    Session {{ plannedsession.name }}

    {{ form.as_table }}
    -
    - Delete -
    -
    - Clone -
    -
    +

    + Delete +

    +

    + Clone +

    +

    -

    - -
    +

    +
    -
    - -
    + + + {% endblock %} {% block scripts %} @@ -258,3 +213,7 @@ {% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} diff --git a/rowers/templates/plannedsessionview.html b/rowers/templates/plannedsessionview.html index 34df6972..fb967acd 100644 --- a/rowers/templates/plannedsessionview.html +++ b/rowers/templates/plannedsessionview.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -7,158 +7,145 @@ {% block title %}Planned Session{% endblock %} -{% block content %} +{% block main %} -
    - {% include "planningbuttons.html" %} - -
    -
    -
    - {% if user.is_authenticated and psdict.id.1|is_session_manager:user %} - +{% if user.is_authenticated and psdict.id.1|is_session_manager:user %} +

    + Edit Session - {% else %} -   - {% endif %} -

    -
    - {% if plannedsession.sessiontype == 'coursetest' %} - Courses - {% else %} -   - {% endif %} -
    -
    -
    -
    -

    Session {{ psdict.name.1 }}

    - - {% for attr in attrs %} - {% for key,value in psdict.items %} - {% if key == attr %} - - {% if key == 'comment' %} - - {% else %} - - {% endif %} - - {% endif %} - {% endfor %} - {% endfor %} -
    {{ value.0 }}{{ value.1|linebreaks }}{{ value.0 }}{{ value.1 }}
    -
    - -
    -
    -
    -

    {{ rower.user.first_name }} {{ rower.user.last_name }}

    -

    Status: {{ status }}

    -

    Percentage complete: {{ ratio }}

    -
    - -
    -
    - {% if coursescript %} -

    Course

    + + {% endfor %} + + + +
  • +

    {{ rower.user.first_name }} {{ rower.user.last_name }}

    +

    Status: {{ status }}

    +

    Percentage complete: {{ ratio }}

    +
  • +
  • +

    Stats

    + + + + + + + + + + + + + + {% for id, value in results.items %} + + + + + + + + + + {% endfor %} + +
    NameMinutesMetersrScoreTRIMPComplete DateStatus
    {{ value|lookup:'first_name' }} {{ value|lookup:'last_name' }}{{ value|lookup:'duration' }}{{ value|lookup:'distance' }}{{ value|lookup:'rscore' }}{{ value|lookup:'trimp' }}{{ value|lookup:'completedate' }}{{ value|lookup:'status' }}
    +
  • + {% if coursescript %} +
  • +

    Course

    {{ coursediv|safe }} - + {{ coursescript|safe }} - {% endif %} -
  • - -
    + + {% endif %} + + {% endblock %} +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} + {% block scripts %} diff --git a/rowers/templates/polar_list_import.html b/rowers/templates/polar_list_import.html index 3e61acf2..9e6a3817 100644 --- a/rowers/templates/polar_list_import.html +++ b/rowers/templates/polar_list_import.html @@ -1,10 +1,10 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Workouts{% endblock %} -{% block content %} +{% block main %}

    New Workouts Imported From Polar Flow

    Due to a limitation in Polar Flow's API, we can only access new workouts. We @@ -47,3 +47,7 @@

    No new workouts found

    {% endif %} {% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} +{% endblock %} diff --git a/rowers/templates/promembership.html b/rowers/templates/promembership.html index c3422175..201d3044 100644 --- a/rowers/templates/promembership.html +++ b/rowers/templates/promembership.html @@ -1,211 +1,245 @@ - {% extends "base.html" %} - {% block title %}Rowsandall Pro Membership{% endblock title %} - {% block content %} +{% extends "newbase.html" %} +{% block title %}Rowsandall Pro Membership{% endblock title %} +{% block main %} {% load rowerfilters %} -
    -

    Pro Membership

    +

    Paid Membership Plans

    -

    Donations are welcome to keep this web site going. To help cover the hosting - costs, I have created several paid plans offering advanced functionality. - Once I process your - donation, I will give you access to some special features on this - website.

    +
      +
    • +

      Donations are welcome to keep this web site going. To help cover the hosting + costs, I have created several paid plans offering advanced functionality. + Once I process your + donation, I will give you access to some special features on this + website.

      -

      The following table gives an overview of the different plans. As we are - constantly developing new functionality, the table might be slightly outdated. Don't - hesitate to contact us.

      +

      The following table gives an overview of the different plans. As we are + constantly developing new functionality, the table might be slightly outdated. Don't + hesitate to contact us.

      -

      The Pro membership is open for a free 14 day trial

      +

      The Pro membership is open for a free 14 day trial

      +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
       BASICPROSELF-COACHCOACH
      Basic rowing metrics (spm, time, distance, heart rate, power)
      Manual Import, Export, Synchronization and download of all your data
      Automatic Synchronization with other fitness sites 
      Heart rate and power zones
      Ranking Pieces, Stroke Analysis
      Advanced Analysis (Critical Power, Stats, Box Chart, Trend Flex) 
      Compare Workouts 
      Empower Stroke Profile 
      Sensor Fusion, Split Workout, In-stroke metrics 
      Create Training plans, tests and challenges for yourself. Track your performance + against plan.  
      Create Training plans, tests and challenges for your athletes. Track their performance + against plan.    
      Create and manage teams.   
      Manage your athlete's workouts   
      +

      +

      Coach and Self-Coach Membership

      + +

      The Coach plan functionality listed is available to the coach only. Individual athletes + can purchase upgrades to "Pro" and "Self-Coach" plans. +

      -

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
       BASICPROSELF-COACHCOACH
      Basic rowing metrics (spm, time, distance, heart rate, power)
      Manual Import, Export, Synchronization and download of all your data
      Automatic Synchronization with other fitness sites 
      Heart rate and power zones
      Ranking Pieces, Stroke Analysis
      Advanced Analysis (Critical Power, Stats, Box Chart, Trend Flex) 
      Compare Workouts 
      Empower Stroke Profile 
      Sensor Fusion, Split Workout, In-stroke metrics 
      Create Training plans, tests and challenges for yourself. Track your performance - against plan.  
      Create Training plans, tests and challenges for your athletes. Track their performance - against plan.    
      Create and manage teams. -    
      Manage your athlete's workouts -    
      -

      - -

      The Coach plan functionality listed is available to the coach only. Individual athletes - can purchase upgrades to Pro membership. -

      -

      Click on the PayPal button to pay for your Pro membership. Before you pay, please register for the free Basic membership and add your user name to the form. -Your payment will be valid for one year with automatic renewal which you can stop at any time. -You will be taken to the secure PayPal payment site. -

      -
    - -
    - {% if user.rower.rowerplan == 'basic' and user.rower.protrialexpires|date_dif == 1 %} -

    Free Trial

    -

    - You qualify for a 14 day free trial. No credit card needed. - Try out Pro or Self-Coach membership for two weeks. Click the button below to - sign up for the trial. After your trial period expires, you will be - automatically reset to the Basic plan, unless you upgrade to Pro. -

    - -
     
    - -{% endif %} - -

    Recurring Payment

    -

    You need a Paypal account for this

    -

    -

    - - - - - -
    Plans
    Your User Name
    - - - -
    -

    - -

    One Year Subscription

    -

    Only a credit card needed. Will not automatically renew

    - -

    -

    - - - - - -
    Plans
    Your User Name
    - - - -
    -

    - - -

    Payment Processing

    -

    After you do the payment, we will manually change your membership to - "Pro". Depending on our availability, this may take some time - (typically one working day). Don't hesitate to contact us - if you have any questions at this stage.

    - -

    If, for any reason, you are not happy with your Pro membership, please let me know through the contact form. I will contact you as soon as possible to discuss how we can make things better.

    - -
    +

    Rowsandall.com's Training Planning functionality + is part of the paid "Self-Coach" and "Coach" plans.

    + +

    On the "Self-Coach" plan, you can plan your own sessions.

    + +

    On the "Coach" plan, you can establish teams, see workouts done by + athletes on your team, and plan individual and group sessions for your + athletes. +

    + +

    If you would like to find a coach who helps you plan your training + through rowsandall.com, contact me throught the contact form.

    + + {% if user.rower.rowerplan == 'basic' and user.rower.protrialexpires|date_dif == 1 %} +

    Free Trial

    +

    + You qualify for a 14 day free trial. No credit card needed. + Try out Pro or Self-Coach membership for two weeks. Click the button below to + sign up for the trial. After your trial period expires, you will be + automatically reset to the Basic plan, unless you upgrade to Pro. +

    +

    Yes, I want to try Pro membership for 14 days for free. No strings attached.

    +

    Yes, I want to try Self-Coach membership for 14 days for free. No strings attached.

    + {% endif %} + +
  • +

    Click on the PayPal button to pay for your Pro membership. Before you pay, please register for the free Basic membership and add your user name to the form. + Your payment will be valid for one year. + You will be taken to the secure PayPal payment site. +

    +

    Recurring Payment

    +

    You need a Paypal account for this. This is plan will automatically renew each year.

    +

    +

    + + + + + + + + + + + + + + + +
    + Plans +
    + +
    + Your User Name +
    + +
    + + + +
    +

    +

    One Year Subscription

    +

    Only a credit card needed. Will not automatically renew

    + +

    +

    + + + + + +
    Plans
    Your User Name
    + + + +
    +

    +

    Payment Processing

    +

    After you do the payment, we will manually change your membership to + "Pro". Depending on our availability, this may take some time + (typically one working day). Don't hesitate to contact us + if you have any questions at this stage.

    + +

    If, for any reason, you are not happy with your Pro membership, please let me know through the contact form. I will contact you as soon as possible to discuss how we can make things better.

    + +
  • + - {% endblock content %} +{% endblock %} + +{% block sidebar %} +{% include 'menu_help.html' %} +{% endblock %} + diff --git a/rowers/templates/race_submit.html b/rowers/templates/race_submit.html index 4b4b397b..c4fd6468 100644 --- a/rowers/templates/race_submit.html +++ b/rowers/templates/race_submit.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -16,36 +16,33 @@ {% endblock %} -{% block content %} -
    -
    -

    Submit Your Result for {{ race.name }}

    -
    +{% block main %} +

    Submit Your Result for {{ race.name }}

    -
    - -
    -
    -

    Select one of the following workouts that you rowed within the race window

    - - - {% for field in w_form.hidden_fields %} - {{ field }} - {% endfor %} - {% for field in w_form.visible_fields %} - + {% endfor %} + +
    - {{ field.label }} +
      +
    • + +

      Select one of the following workouts that you rowed within the race window

      + + + {% for field in w_form.hidden_fields %} {{ field }} - - {% endfor %} - -
      - -
      - {% csrf_token %} - -
      + {% endfor %} + {% for field in w_form.visible_fields %} +
    + {{ field.label }} + {{ field }} +
    +

    + {% csrf_token %} + +

    + + @@ -53,3 +50,7 @@ {% block scripts %} {% endblock %} + +{% block sidebar %} +{% include 'menu_racing.html' %} +{% endblock %} diff --git a/rowers/templates/rankings.html b/rowers/templates/rankings.html index 00146e88..0a533016 100644 --- a/rowers/templates/rankings.html +++ b/rowers/templates/rankings.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -11,378 +11,320 @@ {% endblock %} -{% block content %} +{% block main %} - - + + - {{ interactiveplot |safe }} - - - +{{ interactiveplot |safe }} -
    -
    - {% if theuser %} -

    {{ theuser.first_name }}'s Ranking Pieces

    - {% else %} -

    {{ user.first_name }}'s Ranking Pieces

    - {% endif %} -
    -
    - {% if user.is_authenticated and user|is_manager %} - -
    +{% if theuser %} +

    {{ theuser.first_name }}'s Ranking Pieces

    +{% else %} +

    {{ user.first_name }}'s Ranking Pieces

    +{% endif %} -
    -

    Summary for {{ theuser.first_name }} {{ theuser.last_name }} - between {{ startdate|date }} and {{ enddate|date }}

    +
    -
    -

    Use this form to select a different date range:

    -

    - Select start and end date for a date range: -

    - -
    - - - {{ dateform.as_table }} -
    - {% csrf_token %} -
    -
    - -
    -
    -
    - Or use the last {{ deltaform }} days. -
    -
    - {% csrf_token %} - - -
    -
    - -
    - -

    Ranking Piece Results

    - - {% if rankingworkouts %} - - - - - - - - - - - - - - {% for workout in rankingworkouts %} - - - - - - - - - - - {% endfor %} - -
    Distance Duration Date Avg HR Max HR Edit
    {{ workout.distance }} {{ workout.duration |durationprint:"%H:%M:%S.%f" }} {{ workout.date }} {{ workout.averagehr }} {{ workout.maxhr }} - {{ workout.name }}
    - {% else %} -

    No ranking workouts found

    - {% endif %} - -

    Missing your best pieces? Upload stroke data of any Concept2 - ranking piece and they will be automatically added to this page.

    -

    Don't have stroke data for official Concept2 ranking pieces? - The PRO membership ranking piece functionality - allows you to include your best non ranking pieces and even use - parts of workouts for improved calculation accuracy. -

    - -

    Want to add race results but you don't have stroke data? - Click here.

    - -

    Scroll down for the chart and pace predictions for ranking pieces.

    - -
    - - -
    - -

    Critical Power Plot

    +

    Critical Power Plot

    {{ the_div|safe }} - -{% if age %} -

    The dashed lines are based on the - Concept2 - rankings for your age, gender - and weight category. World class means within 5% of - - World Record in terms - of power. - The percentile lines are estimates of where the percentiles - of the Concept2 rankings historically are for those of exactly - your age, gender and weight class. -

    -{% endif %} -
    - -
    -

    Pace predictions for Ranking Pieces

    - -

    Add non-ranking piece using the form. The piece will be added in the prediction tables below.

    -
    -
    - {{ form.value }} {{ form.pieceunit }} + + {% if age %} +

    The dashed lines are based on the + Concept2 + rankings for your age, gender + and weight category. World class means within 5% of + + World Record in terms + of power. + The percentile lines are estimates of where the percentiles + of the Concept2 rankings historically are for those of exactly + your age, gender and weight class. +

    + {% endif %} + +
  • +

    Ranking Piece Results

    + + {% if rankingworkouts %} + + + + + + + + + + + + + + {% for workout in rankingworkouts %} + + + + + + + + + - {% csrf_token %} - -
    - + {% endfor %} +
    +
    Distance Duration Date Avg HR Max HR Edit
    {{ workout.distance }} {{ workout.duration |durationprint:"%H:%M:%S.%f" }} {{ workout.date }} {{ workout.averagehr }} {{ workout.maxhr }} + + {{ workout.name }} + +
    + {% else %} +

    No ranking workouts found

    + {% endif %} + +

    Missing your best pieces? Upload stroke data of any Concept2 + ranking piece and they will be automatically added to this page.

    +

    Don't have stroke data for official Concept2 ranking pieces? + The PRO membership ranking piece functionality + allows you to include your best non ranking pieces and even use + parts of workouts for improved calculation accuracy. +

    + +

    Want to add race results but you don't have stroke data? + Click here.

    + +

    Scroll down for the chart and pace predictions for ranking pieces.

    + +
  • + + + +
  • +

    Pace predictions for Ranking Pieces

    + +

    Add non-ranking piece using the form. The piece will be added in the prediction tables below.

    + + {{ form.value }} {{ form.pieceunit }} + + {% csrf_token %} + +
  • -
    + / -
    -

    Paul's Law

    -{% if nrdata >= 1 %} - - - - - - - - - - - {% for pred in predictions %} - - {% for key, value in pred.items %} - {% if key == "distance" %} - - {% endif %} - {% if key == "pace" %} - - {% endif %} - {% if key == "power" %} - - {% endif %} - {% if key == "duration" %} - - {% endif %} - {% endfor %} - - {% endfor %} - -
    Duration Distance Power Pace
    {{ value }} m {{ value |paceprint }} {{ value }} W {{ value |deltatimeprint }}
    - -{% else %} -

    Insufficient data to make predictions

    - -{% endif %} -
    -
    -

    CP Model

    -{% if nrdata >= 4 %} - - - - - - - - - - - {% for pred in cpredictions %} - - {% for key, value in pred.items %} - {% if key == "distance" %} - - {% endif %} - {% if key == "pace" %} - - {% endif %} - {% if key == "power" %} - - {% endif %} - {% if key == "duration" %} - - {% endif %} - {% endfor %} - - {% endfor %} - -
    Duration Distance Power Pace
    {{ value }} m {{ value |paceprint }} {{ value }} W {{ value |deltatimeprint }}
    - -{% else %} -

    Insufficient data to make predictions

    - -{% endif %} -
    - -
    - {% if age and sex != 'not specified' %} -

    World Records

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - 100m - -
    - 500m - -
    - 1000m - -
    - 2000m - -
    - 5000m - -
    - 6000m - -
    - 10000m - -
    - Half Marathon - -
    - Full Marathon - -
    - 1 minute - -
    - 4 minutes - -
    - 30 minutes - -
    - 1 hour - -
    +
  • +

    Paul's Law

    + {% if nrdata >= 1 %} + + + + + + + + + + + {% for pred in predictions %} + + {% for key, value in pred.items %} + {% if key == "distance" %} + + {% endif %} + {% if key == "pace" %} + + {% endif %} + {% if key == "power" %} + + {% endif %} + {% if key == "duration" %} + + {% endif %} + {% endfor %} + + {% endfor %} + +
    Duration Distance Power Pace
    {{ value }} m {{ value |paceprint }} {{ value }} W {{ value |deltatimeprint }}
    + {% else %} - If you fill in your birth date and gender, you will see World Records for - your age group and gender at this place. You can edit your settings - here. -{% endif %} -
  • -
    +

    Insufficient data to make predictions

    + + {% endif %} + +
  • +

    CP Model

    + {% if nrdata >= 4 %} + + + + + + + + + + + {% for pred in cpredictions %} + + {% for key, value in pred.items %} + {% if key == "distance" %} + + {% endif %} + {% if key == "pace" %} + + {% endif %} + {% if key == "power" %} + + {% endif %} + {% if key == "duration" %} + + {% endif %} + {% endfor %} + + {% endfor %} + +
    Duration Distance Power Pace
    {{ value }} m {{ value |paceprint }} {{ value }} W {{ value |deltatimeprint }}
    + + {% else %} +

    Insufficient data to make predictions

    + + {% endif %} +
  • + {% if age and sex != 'not specified' %} +
  • +

    World Records

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + 100m + +
    + 500m + +
    + 1000m + +
    + 2000m + +
    + 5000m + +
    + 6000m + +
    + 10000m + +
    + Half Marathon + +
    + Full Marathon + +
    + 1 minute + +
    + 4 minutes + +
    + 30 minutes + +
    + 1 hour + +
    +
  • + {% endif %} +
  • +

    Use this form to select a different date range:

    +

    + Select start and end date for a date range: + +

    + + + {{ dateform.as_table }} +
    + {% csrf_token %} + +
    +
  • +
  • +

    Summary for {{ theuser.first_name }} {{ theuser.last_name }} + between {{ startdate|date }} and {{ enddate|date }}

    + +

    The table gives the best efforts achieved on the + official Concept2 ranking pieces in the selected date range. Also the percentile scores on the + chart are based on the Concept2 rankings.

    +
  • + {% endblock %} + +{% block sidebar %} +{% include 'menu_analytics.html' %} +{% endblock %} diff --git a/rowers/templates/redesign_analysis.html b/rowers/templates/redesign_analysis.html index 7fc572de..20dfc9c7 100644 --- a/rowers/templates/redesign_analysis.html +++ b/rowers/templates/redesign_analysis.html @@ -4,10 +4,30 @@ {% block main %}

    Main

    -

    Vestibulum consectetur sit amet nisi ut consectetur. Praesent efficitur, nibh vitae fringilla scelerisque, est neque faucibus quam, in iaculis purus libero eget mauris. Curabitur et luctus sapien, ac gravida orci. Aliquam erat volutpat. In hac habitasse platea dictumst. Aenean commodo, arcu a commodo efficitur, libero dolor mollis turpis, non posuere orci leo eget enim. Curabitur sit amet elementum orci, pulvinar dignissim urna. Morbi id ex eu ex congue laoreet. Aenean tincidunt dolor justo, semper pretium libero luctus nec. Ut vulputate metus accumsan leo imperdiet tincidunt. Phasellus nec rutrum dolor. Cras imperdiet sollicitudin arcu, id interdum nibh fermentum in. -

    -

    Vestibulum consectetur sit amet nisi ut consectetur. Praesent efficitur, nibh vitae fringilla scelerisque, est neque faucibus quam, in iaculis purus libero eget mauris. Curabitur et luctus sapien, ac gravida orci. Aliquam erat volutpat. In hac habitasse platea dictumst. Aenean commodo, arcu a commodo efficitur, libero dolor mollis turpis, non posuere orci leo eget enim. Curabitur sit amet elementum orci, pulvinar dignissim urna. Morbi id ex eu ex congue laoreet. Aenean tincidunt dolor justo, semper pretium libero luctus nec. Ut vulputate metus accumsan leo imperdiet tincidunt. Phasellus nec rutrum dolor. Cras imperdiet sollicitudin arcu, id interdum nibh fermentum in. -

    + +
      +
    • placeholder
    • +
    • placeholder
    • +
    • placeholder
    • +
    • placeholder
    • +
    • placeholder
    • +
    • placeholder
    • +
    • placeholder
    • +
    • placeholder
    • +
    • placeholder
    • +
    • placeholder
    • +
    • placeholder
    • +
    • +

      Vestibulum consectetur sit amet nisi ut consectetur. Praesent efficitur, nibh vitae fringilla scelerisque, est neque faucibus quam, in iaculis purus libero eget mauris. Curabitur et luctus sapien, ac gravida orci. Aliquam erat volutpat. In hac habitasse platea dictumst. Aenean commodo, arcu a commodo efficitur, libero dolor mollis turpis, non posuere orci leo eget enim. Curabitur sit amet elementum orci, pulvinar dignissim urna. Morbi id ex eu ex congue laoreet. Aenean tincidunt dolor justo, semper pretium libero luctus nec. Ut vulputate metus accumsan leo imperdiet tincidunt. Phasellus nec rutrum dolor. Cras imperdiet sollicitudin arcu, id interdum nibh fermentum in. +

        +
      • One
      • +
      • Two
      • +
      +

      +

      Vestibulum consectetur sit amet nisi ut consectetur. Praesent efficitur, nibh vitae fringilla scelerisque, est neque faucibus quam, in iaculis purus libero eget mauris. Curabitur et luctus sapien, ac gravida orci. Aliquam erat volutpat. In hac habitasse platea dictumst. Aenean commodo, arcu a commodo efficitur, libero dolor mollis turpis, non posuere orci leo eget enim. Curabitur sit amet elementum orci, pulvinar dignissim urna. Morbi id ex eu ex congue laoreet. Aenean tincidunt dolor justo, semper pretium libero luctus nec. Ut vulputate metus accumsan leo imperdiet tincidunt. Phasellus nec rutrum dolor. Cras imperdiet sollicitudin arcu, id interdum nibh fermentum in. +

      +
    • +
    {% endblock %} {% block sidebar %} diff --git a/rowers/templates/redesign_plan.html b/rowers/templates/redesign_plan.html index 4ac81a70..72057cd1 100644 --- a/rowers/templates/redesign_plan.html +++ b/rowers/templates/redesign_plan.html @@ -10,6 +10,7 @@

    {% endblock %} + {% block sidebar %} {% include 'menu_plan.html' %} {% endblock %} diff --git a/rowers/templates/redesign_racing.html b/rowers/templates/redesign_racing.html index a5f72774..dacdee63 100644 --- a/rowers/templates/redesign_racing.html +++ b/rowers/templates/redesign_racing.html @@ -10,6 +10,7 @@

    {% endblock %} + {% block sidebar %} {% include 'menu_racing.html' %} {% endblock %} diff --git a/rowers/templates/redesign_workout.html b/rowers/templates/redesign_workout.html index 96b24cb4..669cd281 100644 --- a/rowers/templates/redesign_workout.html +++ b/rowers/templates/redesign_workout.html @@ -10,6 +10,7 @@

    {% endblock %} + {% block sidebar %} {% include 'menu_workout.html' %} {% endblock %} diff --git a/rowers/templates/redesign_workouts.html b/rowers/templates/redesign_workouts.html index 79779d35..3205bcc2 100644 --- a/rowers/templates/redesign_workouts.html +++ b/rowers/templates/redesign_workouts.html @@ -10,6 +10,7 @@

    {% endblock %} + {% block sidebar %} {% include 'menu_workouts.html' %} {% endblock %} diff --git a/rowers/templates/registerthankyou.html b/rowers/templates/registerthankyou.html index 73ae6509..1c0914b1 100644 --- a/rowers/templates/registerthankyou.html +++ b/rowers/templates/registerthankyou.html @@ -1,27 +1,18 @@ - {% extends "base.html" %} - {% block title %}Contact Us - Thank You{% endblock title %} - {% block content %} -

    Thank you.

    -

    Thank you for registering. You can now login using the credential you provided. You can also view some of the videos below to get you started.

    +{% extends "newbase.html" %} +{% block title %}Contact Us - Thank You{% endblock title %} +{% block main %} +

    Thank you for registering

    +

    Thank you for registering. You can now login using the credentials you provided.

    Return home

    -

    Basic Navigation

    - - -

    Upload Page

    - - -

    Integration with Strava, SportTracks or Concept2 logbook

    - -

    - -

    - -

    - -

    - {% endblock content %} +{% endblock main %} + + +{% block sidebar %} +{% include 'menu_help.html' %} +{% endblock %} + diff --git a/rowers/templates/registration_form.html b/rowers/templates/registration_form.html index 0e337e78..bd75d942 100644 --- a/rowers/templates/registration_form.html +++ b/rowers/templates/registration_form.html @@ -1,45 +1,50 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Contact Us{% endblock title %} {% block meta %} {% endblock %} - {% block content %} -
    - - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. -

    - {% endif %} - -
    - {% csrf_token %} - - {{ form.as_table }} +{% block main %} +

    New User Registration

    +
      +
    • +
      + + {% if form.errors %} +

      + Please correct the error{{ form.errors|pluralize }} below. +

      + {% endif %} + + + {% csrf_token %} +
    + {{ form.as_table }}
    - - -
    - -
    -
    -

    To use rowsandall, you need to register and agree with the Terms of Service.

    -

    Registration is free.

    - -

    Some of our advanced services only work if you give us your - (approximate) birth date, sex and weight category, with 72.5 kg the - bounday between heavies and lighties for men, and 59 kg for women. -

    - -

    Also, we are restricting access to the site to 16 years and older - because of EU data protection regulations.

    - -
    - {% endblock content %} + +
    + +
  • + +

    To use rowsandall, you need to register and agree with the Terms of Service.

    +

    Registration is free.

    + +

    Some of our advanced services only work if you give us your + (approximate) birth date, sex and weight category, with 72.5 kg the + bounday between heavies and lighties for men, and 59 kg for women. +

    + +

    Also, we are restricting access to the site to 16 years and older + because of EU data protection regulations.

    +
  • + + +{% endblock main %} + +{% block sidebar %} +{% include 'menu_help.html' %} +{% endblock %} diff --git a/rowers/templates/rower_exportsettings.html b/rowers/templates/rower_exportsettings.html index fbaa80e2..980e8d73 100644 --- a/rowers/templates/rower_exportsettings.html +++ b/rowers/templates/rower_exportsettings.html @@ -1,31 +1,28 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% block title %}Change Rower Export Settings{% endblock %} -{% block content %} -
    -
    -

    -

    Export Settings

    - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. -

    - {% endif %} +{% block main %} +

    Import and Export Settings for {{ rower.user.first_name }} {{ rower.user.last_name }}

    +{% if form.errors %} +

    + Please correct the error{{ form.errors|pluralize }} below. +

    +{% endif %} -
    - - {{ form.as_table }} -
    - {% csrf_token %} -
    - - -

    -
    -
    -
    +
    + + {{ form.as_table }} +
    + {% csrf_token %} + +
    + +{% endblock %} + +{% block sidebar %} +{% include 'menu_profile.html' %} {% endblock %} diff --git a/rowers/templates/rower_form.html b/rowers/templates/rower_form.html index 8333ccef..c5b82b61 100644 --- a/rowers/templates/rower_form.html +++ b/rowers/templates/rower_form.html @@ -1,117 +1,25 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Change Rower {% endblock %} -{% block content %} -
    -
    -

    User Settings for {{ rower.user.first_name }} {{ rower.user.last_name }}

    -

    Need help? Click to read the tutorial

    -
    - -
    +{% block main %} +

    User Settings for {{ rower.user.first_name }} {{ rower.user.last_name }}

    + +

    Need help? Click to read the tutorial

    + +
      +
    • +

      Account Information

      -

      Heart Rate Zones

      -

      Set your heart rate zones with this form.

      - {% if form.errors %} -

      - Please correct the error{{ form.errors|pluralize }} below. + {% if rower.user == user %} + Password Change + {% else %} +   + {% endif %}

      - {% endif %} - -
      - - {{ form.as_table }} -
      - {% csrf_token %} -
      - - -

      -
      -
    -
    -

    -

    Power Zones

    -

    The power zones are defined relative to power as measured by the - indoor rower.

    -
    - {% if powerzonesform.errors %} -

    - Please correct the error{{ powerzonesform.errors|pluralize }} below. - {{ powerzonesform.non_field_errors }} - -

    - {% endif %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    IDZone NameLower Boundary (Watt)
    1{{ powerzonesform.ut3name }}
    2{{ powerzonesform.ut2name }}{{ powerzonesform.pw_ut2 }}
    3{{ powerzonesform.ut1name }}{{ powerzonesform.pw_ut1 }}
    4{{ powerzonesform.atname }}{{ powerzonesform.pw_at }}
    5{{ powerzonesform.trname }}{{ powerzonesform.pw_tr }}
    6{{ powerzonesform.anname }}{{ powerzonesform.pw_an }}
    - {% csrf_token %} -
    - -
    -
    -

    -
    -
    -
    -
    -

    -

    Account Information

    -
    -
    -

    - {% if rower.user == user %} - Password Change - {% else %} -   - {% endif %} -

    -
    -
    -

    - {% if userform.errors %} + {% if userform.errors %}

    Please correct the error{{ form.errors|pluralize }} below.

    @@ -133,137 +41,59 @@ {% csrf_token %} -
    - {% if rower.rowerplan == 'basic' and rower.user == user %} - Upgrade - {% else %} -   - {% endif %} -
    -
    - + {% if rower.rowerplan == 'basic' and rower.user == user %} + Upgrade + {% else %} +   + {% endif %} + -
    - - -

    -
    -
    + + {% if rower.user == user %} +
  • +

    GDPR - Data Protection

    -

    Functional Threshold Power and OTW Slack

    -

    Use this form to quickly change your zones based on the power of a - recent - full out 60 minutes effort on the ergometer. - It will update all zones defined above.

    -

    The OTW Power Slack is the percentage drop of your On-the-water - rowing power - vs the erg power. Typical values are around 15%. This will lower - the power zones for your OTW workouts.

    -
    - - {{ powerform.as_table }} -
    - {% csrf_token %} -
    - - -
    -
  • -
    -{% if rower.user == user %} -
    -
    -

    -

    Teams

    - + Download your data

    -
    - -

    -

    Favorite Charts

    - + Deactivate Account

    -
    -
    -
    -

    -

    Export Settings

    - + Delete Account

    -
    -
    -

    -

    Configure Workflow Layout

    - -

    -
    - - -
    - -
    -
    -

    -

    GDPR - Data Protection

    - - -
    -

    - Delete Account -

    -
    -

    -
    -
    + +
  • {% if grants %} -

    -

    Applications

    - - - - - - - - - - {% for grant in grants %} - - - - + {% endfor %} + +
    ApplicationScopeRevoke
    {{ grant.application }}{{ grant.scope }} - Revoke +

    Applications

    + + + + + + + + + + {% for grant in grants %} + + + + - - {% endfor %} - -
    ApplicationScopeRevoke
    {{ grant.application }}{{ grant.scope }} + Revoke
    -

    - {% else %} -

     

    +
    {% endif %} -
  • -
    + + {% endif %} +{% endblock %} + +{% block sidebar %} +{% include 'menu_profile.html' %} {% endblock %} diff --git a/rowers/templates/rower_preferences.html b/rowers/templates/rower_preferences.html new file mode 100644 index 00000000..74c8da4c --- /dev/null +++ b/rowers/templates/rower_preferences.html @@ -0,0 +1,108 @@ +{% extends "newbase.html" %} +{% load staticfiles %} +{% load rowerfilters %} + +{% block title %}Change Rower Preferences{% endblock %} + +{% block main %} +

    User Preferences for {{ rower.user.first_name }} {{ rower.user.last_name }}

    + +

    Need help? Click to read the tutorial

    + +
      +
    • +

      +

      Heart Rate Zones

      +

      Set your heart rate zones with this form.

      + {% if form.errors %} +

      + Please correct the error{{ form.errors|pluralize }} below. +

      + {% endif %} + +
      + + {{ form.as_table }} +
      + {% csrf_token %} + +
      +
    • +
    • +

      Power Zones

      +

      The power zones are defined relative to power as measured by the + indoor rower.

      +
      + {% if powerzonesform.errors %} +

      + Please correct the error{{ powerzonesform.errors|pluralize }} below. + {{ powerzonesform.non_field_errors }} + +

      + {% endif %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      IDZone NameLower Boundary (Watt)
      1{{ powerzonesform.ut3name }}
      2{{ powerzonesform.ut2name }}{{ powerzonesform.pw_ut2 }}
      3{{ powerzonesform.ut1name }}{{ powerzonesform.pw_ut1 }}
      4{{ powerzonesform.atname }}{{ powerzonesform.pw_at }}
      5{{ powerzonesform.trname }}{{ powerzonesform.pw_tr }}
      6{{ powerzonesform.anname }}{{ powerzonesform.pw_an }}
      + {% csrf_token %} +
      + +
      +
      +
    • +
    • +

      Functional Threshold Power and OTW Slack

      +

      Use this form to quickly change your zones based on the power of a + recent + full out 60 minutes effort on the ergometer. + It will update all zones defined above.

      +

      The OTW Power Slack is the percentage drop of your On-the-water + rowing power + vs the erg power. Typical values are around 15%. This will lower + the power zones for your OTW workouts.

      +
      + + {{ powerform.as_table }} +
      + {% csrf_token %} + +
      +
    • +
    + + + +{% endblock %} + +{% block sidebar %} +{% include 'menu_profile.html' %} +{% endblock %} diff --git a/rowers/templates/runkeeper_list_import.html b/rowers/templates/runkeeper_list_import.html index 5cda8c0d..8af34266 100644 --- a/rowers/templates/runkeeper_list_import.html +++ b/rowers/templates/runkeeper_list_import.html @@ -1,37 +1,41 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Workouts{% endblock %} -{% block content %} +{% block main %}

    Available on Runkeeper

    - {% if workouts %} - - - - - - - - - - - - {% for workout in workouts %} - - - - - - - - - {% endfor %} - -
    Import Date/Time Duration Total Distance Type
    -Import{{ workout|lookup:'starttime' }}{{ workout|lookup:'duration' }} {{ workout|lookup:'distance' }} m{{ workout|lookup:'type' }}
    - {% else %} -

    No workouts found

    - {% endif %} +{% if workouts %} + + + + + + + + + + + + {% for workout in workouts %} + + + + + + + + + {% endfor %} + +
    Import Date/Time Duration Total Distance Type
    + Import{{ workout|lookup:'starttime' }}{{ workout|lookup:'duration' }} {{ workout|lookup:'distance' }} m{{ workout|lookup:'type' }}
    +{% else %} +

    No workouts found

    +{% endif %} +{% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} {% endblock %} diff --git a/rowers/templates/show_graph.html b/rowers/templates/show_graph.html index 00cdab68..f4ac2a77 100644 --- a/rowers/templates/show_graph.html +++ b/rowers/templates/show_graph.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -14,60 +14,64 @@ {% endblock %} -{% block content %} -

    {{ workout.name }}

    -
    -
    - -
    -
    +{% block main %} +

    {{ workout.name }}

    +
    - -
    - -

     

    -
    -
    -{% if user.is_authenticated and user == rower.user %} -

    - Edit Workout -

    -{% else %} -

    - See Workout -

    -{% endif %} -
    -
    - {% if user.is_authenticated and user == rower.user %} -

    - Workflow View -

    - {% else %} -

     

    - {% endif %} -
    -
    -{% if user.is_authenticated and user == rower.user %} -

    - Delete Chart -

    -{% else %} -

     

    -{% endif %} -
    -
    -
    -

    - -/{{ graph.filename }} - -

    -
    + data-text="@rowsandall #rowingdata">Tweet + + +

    + +
  • + {% if user.is_authenticated and user == rower.user %} +

    + Edit Workout +

    + {% else %} +

    + See Workout +

    + {% endif %} + {% if user.is_authenticated and user == rower.user %} +

    + Workflow View +

    + {% else %} +

     

    + {% endif %} +
  • +
  • + {% if user.is_authenticated and user == rower.user %} +

    + Delete Chart +

    + {% else %} +

     

    + {% endif %} +
  • + {% endblock %} +{% block sidebar %} +{% include 'menu_workouts.html' %} +{% endblock %} diff --git a/rowers/templates/splitworkout.html b/rowers/templates/splitworkout.html index a264f626..86756bc8 100644 --- a/rowers/templates/splitworkout.html +++ b/rowers/templates/splitworkout.html @@ -1,131 +1,77 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% load tz %} {% block title %}Change Workout {% endblock %} -{% block content %} -
    - - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. -

    - {% endif %} - -

    Edit Workout Interval Data

    -
    -
    -

    - Edit -

    -
    -
    -

    - Export - -

    -
    -
    -

    - Advanced -

    - Advanced Functionality (More interactive Charts) - -
    -
    - +{% block main %} +

    Split Workout

    {% localtime on %} - -

    -

    - - {{ form.as_table }} -
    - {% csrf_token %} -
    - -
    -
    -

    -

    Workout Split

    -

    Split your workout in half at the given time using the form above. - Use the chart on - the right to find the point where you want to split.

    -

    Use any of the following formats: -

      -
    • H:MM:SS.d, e.g. 1:45:00.0 for one hour and 45 minutes
    • -
    • H:MM:SS, e.g. 1:45:00 for one hour and 45 minutes
    • -
    • MM:SS.d, e.g. 30:00.0 for thirty minutes
    • -
    • MM, e.g. 30 for thirty minutes
    • -
    -

    -

    Warning: If you deselect, "Keep Original", the original workout - cannot be restored afterwards.

    - -
    - -
    -
    +
      +
    • +

      +

      + + {{ form.as_table }} +
      + {% csrf_token %} + +
      +

      +
    • +
    • - - - - {{ thescript |safe }} - - - - - -
      + + + + {{ thescript |safe }} + {{ thediv |safe }} -
      - - - - - - - - - - - -
      Date/Time:{{ workout.startdatetime }}
      Distance:{{ workout.distance }}m
      Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
      Public link to this workout - https://rowsandall.com/rowers/workout/{{ workout.id }} - -
      Public link to interactive chart - https://rowsandall.com/rowers/workout/{{ workout.id }}/interactiveplot - -
      - {% endlocaltime %} + + Date/Time:{{ workout.startdatetime }} + + Distance:{{ workout.distance }}m + + Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }} + + Public link to this workout + + https://rowsandall.com/rowers/workout/{{ workout.id }} + + + + {% endlocaltime %} +
    • +
    • +

      Workout Split

      +

      Split your workout in half at the given time using the form above. + Use the chart on + the right to find the point where you want to split.

      +

      Use any of the following formats: +

        +
      • H:MM:SS.d, e.g. 1:45:00.0 for one hour and 45 minutes
      • +
      • H:MM:SS, e.g. 1:45:00 for one hour and 45 minutes
      • +
      • MM:SS.d, e.g. 30:00.0 for thirty minutes
      • +
      • MM, e.g. 30 for thirty minutes
      • +
      +

      +

      + Warning: If you deselect, "Keep Original", the original workout + cannot be restored afterwards. +

      +
    • +
    -
    -
    - +{% endblock %} + + {% block sidebar %} +{% include 'menu_workout.html' %} {% endblock %} diff --git a/rowers/templates/sporttracks_list_import.html b/rowers/templates/sporttracks_list_import.html index b547efc8..8400d5ff 100644 --- a/rowers/templates/sporttracks_list_import.html +++ b/rowers/templates/sporttracks_list_import.html @@ -1,52 +1,59 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Workouts{% endblock %} -{% block content %} +{% block main %}

    Available on SportTracks

    -{% if workouts %} - -
    -

    This imports all workouts that have not been imported to rowsandall.com. - The action may take a longer time to process, so please be patient. Click on Import in the list below to import an individual workout. -

    -
    -
    - - - - - - - - - - - - - - {% for workout in workouts %} - - - - - - - - - +{% if workouts %} +
      +
    • + Import all NEW +
    • +
    • +

      This imports all workouts that have not been imported to rowsandall.com. + The action may take a longer time to process, so please be patient. Click on Import in the list below to import an individual workout. +

      +
    • + +
    • +
    Import Name Date/Time Duration Total Distance Type New
    - Import{{ workout|lookup:'name' }}{{ workout|lookup:'starttime' }}{{ workout|lookup:'duration' }} {{ workout|lookup:'distance' }} m{{ workout|lookup:'type' }}{{ workout|lookup:'new' }}
    + + + + + + + + + + + + + {% for workout in workouts %} + + + + + + + + + - {% endfor %} - -
    Import Name Date/Time Duration Total Distance Type New
    + Import{{ workout|lookup:'name' }}{{ workout|lookup:'starttime' }}{{ workout|lookup:'duration' }} {{ workout|lookup:'distance' }} m{{ workout|lookup:'type' }}{{ workout|lookup:'new' }}
    -
    + {% endfor %} + + + + {% else %}

    No workouts found

    {% endif %} {% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} +{% endblock %} diff --git a/rowers/templates/strava_list_import.html b/rowers/templates/strava_list_import.html index b384cd5c..a3c1c97b 100644 --- a/rowers/templates/strava_list_import.html +++ b/rowers/templates/strava_list_import.html @@ -1,52 +1,60 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Workouts{% endblock %} -{% block content %} +{% block main %}

    Available on Strava

    {% if workouts %} - -
    -

    This imports all workouts that have not been imported to rowsandall.com. - The action may take a longer time to process, so please be patient. Click on Import in the list below to import an individual workout. -

    -
    +
      +
    • + Import all NEW +
    • +
    • +

      This imports all workouts that have not been imported to rowsandall.com. + The action may take a longer time to process, so please be patient. Click on Import in the list below to import an individual workout. +

      +
    • -
      - - - - - - - - - - - - - - {% for workout in workouts %} - - - - - - - - - - - {% endfor %} - -
      Import Name Date Duration Distance Type New
      - Import{{ workout|lookup:'name' }}{{ workout|lookup:'starttime' }}{{ workout|lookup:'duration' }} {{ workout|lookup:'distance' }} m{{ workout|lookup:'type' }}{{ workout|lookup:'new' }}
      -
      +
    • + + + + + + + + + + + + + + {% for workout in workouts %} + + + + + + + + + + + {% endfor %} + +
      Import Name Date Duration Distance Type New
      + Import{{ workout|lookup:'name' }}{{ workout|lookup:'starttime' }}{{ workout|lookup:'duration' }} {{ workout|lookup:'distance' }} m{{ workout|lookup:'type' }}{{ workout|lookup:'new' }}
      +
    • +
    {% else %} -

    No workouts found

    -{% endif %} +

    + No workouts found +

    + {% endif %} + {% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} {% endblock %} diff --git a/rowers/templates/streamedit.html b/rowers/templates/streamedit.html index b12def3c..8a0b00c3 100644 --- a/rowers/templates/streamedit.html +++ b/rowers/templates/streamedit.html @@ -1,48 +1,20 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Advanced Features {% endblock %} -{% block content %} -
    - -
    -

    Stream Editor

    -
    -
    -
    -

    - Edit Workout -

    -
    -
    -

    - Advanced Edit -

    - -
    -
    -

    OTW Power

    - Run calculations to get power values for your row. - -
    -
    -
    - -
    -

    Wind Edit

    -
    -
    -
    +{% block main %} +

    Stream Editor

    +
      +
    • Edit river Stream between turning points in your row. Use positive (+) values to denote rowing with the stream, negative (-) values to denote rowing against the stream.

      - +
    • +
    • {% if form.errors %}

      @@ -54,47 +26,26 @@ {{ form.as_table }} {% csrf_token %} -

    -
    - -
    - - - -
    - -
    - - - - - {{ interactiveplot |safe }} - - + - + + {{ interactiveplot |safe }} + + {{ the_div |safe }} + -
    - {{ the_div |safe }} -
    + {% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/strokedata_form.html b/rowers/templates/strokedata_form.html index df0d5388..21e39da9 100644 --- a/rowers/templates/strokedata_form.html +++ b/rowers/templates/strokedata_form.html @@ -1,23 +1,23 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} -{% block content %} - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. -

    - {% endif %} -
    -

    Stroke Data for workout {{ id }}

    +{% block main %} +{% if form.errors %} +

    + Please correct the error{{ form.errors|pluralize }} below. +

    +{% endif %} +

    Stroke Data for workout

    -
    - - {{ form.as_table }} -
    - {% csrf_token %} -
    - - -
    -
    +
    + + {{ form.as_table }} +
    + {% csrf_token %} + +
    {% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/summary_edit.html b/rowers/templates/summary_edit.html index a5b44e92..d4de5063 100644 --- a/rowers/templates/summary_edit.html +++ b/rowers/templates/summary_edit.html @@ -1,129 +1,56 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% load tz %} {% block title %}Change Workout {% endblock %} -{% block content %} -
    - - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. -

    - {% endif %} - -

    Edit Workout Interval Data

    -
    -
    -

    - Edit -

    -
    -
    -

    - Workflow View - -

    -
    -
    -

    - Advanced -

    - Advanced Functionality (More interactive Charts) - -
    -
    - - - {% localtime on %} - - - - - - - - - - - - -
    Date/Time:{{ workout.startdatetime }}
    Distance:{{ workout.distance }}m
    Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
    Public link to this workout - https://rowsandall.com/rowers/workout/{{ workout.id }} - -
    Public link to interactive chart - https://rowsandall.com/rowers/workout/{{ workout.id }}/interactiveplot - -
    - {% endlocaltime %} -

    Interval Translator

    -

    This is a quick way to enter the intervals using a special mini-language.

    -

    You enter something like 8x500m/3min, 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.

    - -

    Special characters are x (times), + and / (denotes a rest interval), as well as ( and ). Units are min (minutes), sec (seconds), m (meters) and km (km).

    - -

    A typical interval is described as "10min/5min", with the work part before the "/" and the rest part after it. A zero rest can be omitted, so a single 1000m piece could be described either as "1km" or "1000m". The basic units can be combined with "+" and "Nx". You can use parentheses as in the example below.

    - -

    Here are a few examples.

    - - - - - - - - - - - - - - - - - - - - - -
    8x500m/2min8 times 500m with 2 minutes rest
    10kmSingle distance of 10km
    6min/3min + 5min/3min + 3min/3min + 3min Four intervals of 6, 5, 3 and 3 minutes length, 3 minutes rest
    8x500m/3:30min 8 times 500m with 3 minutes 30 seconds rest
    4x((500m+500m)/5min)4 times 1km, but each km is reported as two 500m intervals without rest. Note the nested parentheses.
    2x500m/500mA 2k rowed as 500m "on", 500m "off"
    - -
    +{% block main %} +

    Edit Workout Interval Data

    +
      +
    • - {{ form.as_table }} + + + + + + + + +
      Name{{ workout.name }}
      Distance:{{ workout.distance }}m
      Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
      Public link to this workout + https://rowsandall.com/rowers/workout/{{ workout.id }} + +
      - {% csrf_token %} -
      +

      Interval Shorthand

      +

      + See the how-to at the bottom of this page for details on how to use this form. +

      + + + {{ form.as_table }} +
      + {% csrf_token %} -
      -
    • - -

      Intervals by Power/Pace

      - -

      With this form, you can specify a power or pace level. Everything faster/harder than the - specified pace/power will become a work interval. Everything slower will become a rest - interval. -

      - -
      - - {{ powerupdateform.as_table }} -
      - - {% csrf_token %} -
      - -
      -
      + +

      Intervals by Power/Pace

      - -
    - -
    -
    +

    With this form, you can specify a power or pace level. Everything faster/harder than the + specified pace/power will become a work interval. Everything slower will become a rest + interval. +

    +
    + + {{ powerupdateform.as_table }} +
    + + {% csrf_token %} + +
    + +
  • - - - -
    - {{ the_div |safe }} -
    -
  • - - -
    + {{ the_div |safe }} + +
  • Updated Summary

    {% csrf_token %} @@ -170,61 +72,58 @@ {% for field in detailform %} {{ field.as_hidden }} {% endfor %} -
    - -
    - - -
    -

    -

    -          {{ intervalstats }}
    -        
    +

    -
    -
  • - -
    -

    Detailed Summary Edit

    -

    This is still experimental and there are known bugs. Use at your own risk. Nothing is stored permanently until you hit Save on the summary above. You can use the restore original button to restore the original values.

    -
    -
    #
    -
    - Time -
    -
    - Distance -
    -
    - Type -
    -
    -
    - {% for i in nrintervals|times %} -
    -
    {{ i }}
    -
    - {% get_field_id i "intervalt_" detailform %} -
    -
    - {% get_field_id i "intervald_" detailform %} -
    -
    - {% get_field_id i "type_" detailform %} -
    -
    - {% endfor %} - {% csrf_token %} -
    - - -
    + + Reset to last saved +   + Restore Original data +
    -
    -
    +

    +

    +        {{ intervalstats }}
    +      
    +

    + +
  • +

    Interval Shorthand How-To

    +

    This is a quick way to enter the intervals using a special mini-language.

    +

    You enter something like 8x500m/3min, 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.

    + +

    Special characters are x (times), + and / (denotes a rest interval), as well as ( and ). Units are min (minutes), sec (seconds), m (meters) and km (km).

    + +

    A typical interval is described as "10min/5min", with the work part before the "/" and the rest part after it. A zero rest can be omitted, so a single 1000m piece could be described either as "1km" or "1000m". The basic units can be combined with "+" and "Nx". You can use parentheses as in the example below.

    + +

    Here are a few examples.

    + + + + + + + + + + + + + + + + + + + + + +
    8x500m/2min8 times 500m with 2 minutes rest
    10kmSingle distance of 10km
    6min/3min + 5min/3min + 3min/3min + 3min Four intervals of 6, 5, 3 and 3 minutes length, 3 minutes rest
    8x500m/3:30min 8 times 500m with 3 minutes 30 seconds rest
    4x((500m+500m)/5min)4 times 1km, but each km is reported as two 500m intervals without rest. Note the nested parentheses.
    2x500m/500mA 2k rowed as 500m "on", 500m "off"
    +
  • + +{% endblock %} + + +{% block sidebar %} +{% include 'menu_workout.html' %} {% endblock %} diff --git a/rowers/templates/team.html b/rowers/templates/team.html index 2d992033..35d82bc5 100644 --- a/rowers/templates/team.html +++ b/rowers/templates/team.html @@ -1,81 +1,56 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% block title %}Team {% endblock %} -{% block content %} -
    - {% include "teambuttons.html" with teamid=team.id %} -
    -
    -
    -

    {{ team.name }}

    +{% block main %} +

    {{ team.name }}

    -
    +
      +
    • {{ team.notes }}

      Manager: {{ team.manager.first_name }} {{ team.manager.last_name }}

      -
    -
    -
    - {% if ismember %} - - {% if team.manager == user %} -
    - Edit Team -
    - {% else %} -
    -

      -

    - {% endif %} - {% elif hasrequested %} -

    You have requested access to this team

    - {% else %} -

    You can request access to this team. By requesting access, you - agree to the Privacy Policy regarding - team functionality. You agree to share your workout data (except - workouts marked as "private") to all team members and the team manager. - You also grant the team manager access to your heart rate and power - zone settings, as well as your functional threshold information. You - are granting the team manager permission to edit your workouts.

    -
    + +
  • + + {% if hasrequested %} +

    You have requested access to this team

    + {% elif team not in myteams and team not in memberteams %} +

    You can request access to this team. By requesting access, you + agree to the Privacy Policy regarding + team functionality. You agree to share your workout data (except + workouts marked as "private") to all team members and the team manager. + You also grant the team manager access to your heart rate and power + zone settings, as well as your functional threshold information. You + are granting the team manager permission to edit your workouts.

    Join A request will be sent to the team manager - -
  • - {% endif %} -
    - -
    -
    -

    -

    Members

    - - - - - - - - - {% for member in members %} - - - {% if team.manager == user %} - - {% else %} - - {% endif %} - - {% endfor %} + {% endif %} + +
  • +

    Members

    +
  • Name 
    {{ member.user.first_name }} {{ member.user.last_name }}Drop 
    + + + + + + + + {% for member in members %} + + + {% if team.manager == user %} + + {% else %} + + {% endif %} + + {% endfor %} -
    Name 
    {{ member.user.first_name }} {{ member.user.last_name }}Drop 
    -

    + + +
  • - -
  • -
    {% if team.manager == user %}

    Use the form to add a new user. You can either select a user from the list of your existing club members who are not on this team yet, or you can type the user's email address, which also works for users who have not registered to the site yet.

    {% if inviteform.errors %} @@ -88,19 +63,19 @@ {{ inviteform.as_table }} {% csrf_token %} -
    - -
    + {% else %}

     

    {% endif %} -
    -
    -
    - + + {% endblock %} + +{% block sidebar %} +{% include 'menu_teams.html' %} +{% endblock %} diff --git a/rowers/templates/team_compare_select.html b/rowers/templates/team_compare_select.html index 7fb527e5..abdde333 100644 --- a/rowers/templates/team_compare_select.html +++ b/rowers/templates/team_compare_select.html @@ -1,10 +1,10 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Workouts{% endblock %} -{% block content %} +{% block main %} +

    {{ team.name }} Compare Workouts

    -
    - {% include "teambuttons.html" with teamid=team.id team=team %} -
    -
    -

    {{ team.name }} Team Workouts

    -
    -
    -
    - {% if team %} -
    - {% else %} - +
      +
    • +

      Select two or more workouts on the left, set your plot settings, + and press submit

      +

      + You can use the date and search forms to search through all + workouts from this team. +

      +

      + TIP: Agree with your team members to put tags (e.g. '8x500m') + in the notes section of + your workouts. That makes it easy to search. +

      +
    • +
    • + {% if workouts %} + + + Toggle All
      + + + {{ form.as_table }} +
      + + {% else %} +

      No workouts found

      + {% endif %} +
    • +
    • +

      + {% csrf_token %} + + {{ chartform.as_table }} +
      +

      +

      + +

      +
    • + +
    • + {% if team %} +
      + {% else %} + {% endif %} -
      {{ dateform.as_table }}
      - {% csrf_token %} -
      -
      - -
      -
      - {% if team %} -
      - {% else %} - - {% endif %} -
      {{ modalityform.as_table }}
      {% csrf_token %} -
      -
      - -
      +

      + +

      -
    -
    - {% if team %} -
    + +
  • + {% if team %} + {% else %} - -{% endif %} -
    + + {% endif %} -
    -
    -
    -
  • -
    - -
    - - -
    -
    - - - {% if workouts %} - - Toggle All
    - - - {{ form.as_table }} -
    - -{% else %} -

    No workouts found

    -{% endif %} -
    -
    -

    Select two or more workouts on the left, set your plot settings below, - and press submit

    - {% csrf_token %} - - {{ chartform.as_table }} -
    -
    -

    - -

    -
    -
    -

    You can use the date and search forms above to search through all - workouts from this team.

    -

    TIP: Agree with your team members to put tags (e.g. '8x500m') in the notes section of - your workouts. That makes it easy to search.

    -
    -
    -
    - + + + {% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} +{% endblock %} diff --git a/rowers/templates/team_document_form.html b/rowers/templates/team_document_form.html index ddf81fba..4db8e2fb 100644 --- a/rowers/templates/team_document_form.html +++ b/rowers/templates/team_document_form.html @@ -1,82 +1,100 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% block title %}File loading{% endblock %} {% block meta %} - - - + + + {% endblock %} -{% block content %} +{% block main %} +
      +
    • +

      Upload Workout File

      -
      -

      Upload Workout File

      - {% if form.errors %} -

      - Please correct the error{{ form.errors|pluralize }} below. -

      - {% endif %} + {% if form.errors %} +

      + Please correct the error{{ form.errors|pluralize }} below. +

      + {% endif %} - - {{ rowerform.as_table }} - {{ form.as_table }} -
      - {% csrf_token %} -
      + + {{ rowerform.as_table }} + {{ form.as_table }} +
      + {% csrf_token %} +
      -
      -
      - +
    • +
    • +

      Optional extra actions

      +

      + + {{ optionsform.as_table }} +
      +

      +

      + You can select one static plot to be generated immediately for this workout. You can select to upload to Concept2 automatically. If you check "make private", this workout will not be visible to your followers and will not show up in your teams' workouts list. +

      + +
    • +
    {% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} +{% endblock %} + +{% block scripts %} + + + +{% endblock %} diff --git a/rowers/templates/teamcreate.html b/rowers/templates/teamcreate.html index ebc4ccae..7014ef5c 100644 --- a/rowers/templates/teamcreate.html +++ b/rowers/templates/teamcreate.html @@ -1,38 +1,41 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% block title %}New Team{% endblock %} -{% block content %} -
    -

    Create a new Team

    -
    -
    -
    - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. -

    - {% endif %} +{% block main %} +

    Create a new Team

    - - {{ form.as_table }} -
    - {% csrf_token %} -
    - -
    -
    - +
      +
    • + + {% if form.errors %} +

      + Please correct the error{{ form.errors|pluralize }} below. +

      + {% endif %} + + + {{ form.as_table }} +
      + {% csrf_token %} + +
    • +
    • +
        +
      • Team Type: A private team is invisible on the Teams Management page, except for its members. The only way to add members is for the manager to send an invitation. An open team is visible for all rowsandall.com users. In addition to the invitation mechanism, any user can request to be added to this team. The team manager will always have to approve membership.
      • +
      • Sharing Behavior: When set to "All Members", all members of a team will see each other's workouts. This is the recommended setting. + If the sharing behavior is set to "Coach Only", team members only see their own workouts. The coach sees all team members' workouts.
      • +
      • These settings can be changed at any point in time through the Team Edit page
      • +
      +
    • +
    {% endblock %} + +{% block sidebar %} +{% include 'menu_teams.html' %} +{% endblock %} diff --git a/rowers/templates/teamdeleteconfirm.html b/rowers/templates/teamdeleteconfirm.html index 56088353..20120bc3 100644 --- a/rowers/templates/teamdeleteconfirm.html +++ b/rowers/templates/teamdeleteconfirm.html @@ -1,43 +1,38 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} -{% block title %}Leave Team {% endblock %} +{% block title %}Delete Team {% endblock %} -{% block content %} -
    +{% block main %} +

    Confirm Deleting the team {{ team.name }}

    +
      +
    • {% if form.errors %} -

      - Please correct the error{{ form.errors|pluralize }} below. -

      +

      + Please correct the error{{ form.errors|pluralize }} below. +

      {% endif %} - -

      Confirm Deleting the team {{ team.name }}

      +

      This will remove the team. Your team members and their workouts will not be deleted.

      + + +

      + Cancel +

      - -
      -

      - Cancel -

      - -
      -

      - Delete -

      -
      - -
    - -
    -

    -  -

    - -
    +

    + Delete +

    + + {% endblock %} + +{% block sidebar %} +{% include 'menu_teams.html' %} +{% endblock %} diff --git a/rowers/templates/teamedit.html b/rowers/templates/teamedit.html index b8ea6366..0f5b065a 100644 --- a/rowers/templates/teamedit.html +++ b/rowers/templates/teamedit.html @@ -1,37 +1,42 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} -{% block title %}New Team{% endblock %} +{% block title %}Edit Team{% endblock %} -{% block content %} -
    -

    Edit Team {{ team.name }}

    -
    -
    -
    - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. -

    - {% endif %} +{% block main %} - - {{ form.as_table }} -
    - {% csrf_token %} -
    - -
    -
    - +

    Edit Team {{ team.name }}

    + +
      +
    • + + {% if form.errors %} +

      + Please correct the error{{ form.errors|pluralize }} below. +

      + {% endif %} + + + {{ form.as_table }} +
      + {% csrf_token %} + + +
    • +
    • +
        +
      • Team Type: A private team is invisible on the Teams Management page, except for its members. The only way to add members is for the manager to send an invitation. An open team is visible for all rowsandall.com users. In addition to the invitation mechanism, any user can request to be added to this team. The team manager will always have to approve membership.
      • +
      • Sharing Behavior: When set to "All Members", all members of a team will see each other's workouts. This is the recommended setting. If te sharing bhavior is set to "Coach Only", team members only see their own workouts. The coach sees all team members' workouts.
      • +
      • These settings can be changed at any point in time through the Team Edit page +
      • +
      +
    • +
    - +{% endblock %} + +{% block sidebar %} +{% include 'menu_teams.html' %} {% endblock %} diff --git a/rowers/templates/teamleaveconfirm.html b/rowers/templates/teamleaveconfirm.html index 4210a404..cae64c93 100644 --- a/rowers/templates/teamleaveconfirm.html +++ b/rowers/templates/teamleaveconfirm.html @@ -1,45 +1,41 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Leave Team {% endblock %} -{% block content %} -
    +{% block main %} +

    Confirm Leaving the team {{ team.name }}

    +
      +
    • {% if form.errors %} -

      - Please correct the error{{ form.errors|pluralize }} below. -

      +

      + Please correct the error{{ form.errors|pluralize }} below. +

      {% endif %} - -

      Confirm Leaving the team {{ team.name }}

      +

      This will remove you and all your workouts from the team. If this is a closed team, you can only return when the team manager reinvites you. If this is an open team, you can return by applying for team membership.

      + + +

      + Cancel +

      +

      + Leave +

      +
    • +
    - -
    -

    - Cancel -

    -
    -

    - Leave -

    -
    - -
    - -
    -

    -  -

    - -
    +{% endblock %} + +{% block sidebar %} +{% include 'menu_teams.html' %} {% endblock %} diff --git a/rowers/templates/teams.html b/rowers/templates/teams.html index af2bc40e..e3094b70 100644 --- a/rowers/templates/teams.html +++ b/rowers/templates/teams.html @@ -1,46 +1,39 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% block title %}Teams {% endblock %} -{% block content %} -
    -
    -

    -

    My Teams

    - {% if teams %} - - - - - - - - +{% block main %} +
      + {% if teams %} +
    • +

      My Teams

      +
    Name 
    + + + + + + + {% for team in teams %} {% endfor %} -
    Name 
    - {{ team.name }} + {{ team.name }} -
    - Leave -
    + Leave
    - {% else %} -

    You are not a member of any team.

    - {% endif %} -

    - + + + {% endif %} -
    -
    - {% if otherteams %} + {% if otherteams %} +
  • Other Teams

    - +
    @@ -60,21 +53,16 @@ {% endfor %}
    Name
    - - {% else %} -

     

    - {% endif %} -
  • -
    + + {% endif %} -
    -
    - {% if user.rower.rowerplan == 'coach' %} + {% if user.rower.rowerplan == 'coach' %} +
  • Teams I manage

    Number of members: {{ clubsize }}

    Maximum club size: {{ max_clubsize }}

    {% if myteams %} - +
    @@ -88,33 +76,27 @@ {{ team.name }} {% endfor %}
    Name -
    - Delete -
    + Delete
    {% endif %} -
    - New Team -
    - {% else %} -

     

    - {% endif %} -
  • -
    - {% if invites or requests or myrequests or myinvites %} + New Team + + {% endif %} + {% if invites or requests or myrequests or myinvites %} +
  • Invitations and Requests

    This section lists open invites to join a team. By accepting a team invite, you are agreeing with the sharing of personal data between team members and coaches according to our privacy policy.

    - +

    As a team manager, by accepting a team invite, you are agreeing - with privacy policy regarding teams and + with our privacy policy regarding teams and personal data owned by team members.

    @@ -177,8 +159,7 @@ {% endfor %}
    -

    Manual code redeem

    -
    +

    Manual code redeem

    {% if form.errors %}

    @@ -187,20 +168,17 @@ {% endif %} {% csrf_token %} - {{ form.as_table }} + {{ form.as_table }}
    -

    - -
    -
    -

    - {% else %} -

     

    {% endif %} -
  • -
    + + {% endblock %} + +{% block sidebar %} +{% include 'menu_teams.html' %} +{% endblock %} diff --git a/rowers/templates/teamstats.html b/rowers/templates/teamstats.html index 83de0907..5dfa6a1b 100644 --- a/rowers/templates/teamstats.html +++ b/rowers/templates/teamstats.html @@ -1,35 +1,32 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% block title %}Team {% endblock %} -{% block content %} -
    - {% include "teambuttons.html" with teamid=team.id team=team %} -
    -
    -

    {{ team.name }} Stats

    -

    - Links to the cumulative statistics pages for your team's members -

    - - - {% for u in theusers %} - - - - - - - - - - - {% endfor %} - -
    {{ u.first_name }} {{ u.last_name }}Ranking PiecesStroke AnalysisPower HistogramStatsBox ChartOTW Ranking PiecesTrend Flex
    -
    - - +{% block main %} +

    {{ team.name }} Stats

    +

    + Links to the cumulative statistics pages for your team's members +

    + + + {% for u in theusers %} + + + + + + + + + + + {% endfor %} + +
    {{ u.first_name }} {{ u.last_name }}Ranking PiecesStroke AnalysisPower HistogramStatsBox ChartOTW Ranking PiecesTrend Flex
    {% endblock %} + +{% block sidebar %} +{% include 'menu_teams.html' %} +{% endblock %} diff --git a/rowers/templates/thankyou.html b/rowers/templates/thankyou.html index 25823e25..5d2e57e6 100644 --- a/rowers/templates/thankyou.html +++ b/rowers/templates/thankyou.html @@ -1,8 +1,12 @@ - {% extends "base.html" %} + {% extends "newbase.html" %} {% block title %}Contact Us - Thank You{% endblock title %} - {% block content %} + {% block main %}

    Thank you.

    Your email was sent and I will get back to you as soon as I can.

    Return home

    - {% endblock content %} \ No newline at end of file + {% endblock %} + + {% block sidebar %} +{% include 'menu_help.html' %} +{% endblock %} diff --git a/rowers/templates/trainingplan.html b/rowers/templates/trainingplan.html index cc17460f..1c52c06a 100644 --- a/rowers/templates/trainingplan.html +++ b/rowers/templates/trainingplan.html @@ -1,197 +1,72 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Rowsandall Training Plans{% endblock %} -{% block scripts %} -{% endblock %} +{% block main %} +

    Training Plan - {{ plan.name }}

    +

    This plan starts on {{ plan.startdate }} and ends on {{ plan.enddate }}. + {% if plan.target %} + The training plan target is: {{ plan.target.name }} on {{ plan.target.date }}. + {% endif %} +

    +

    Edit the plan

    -{% block content %} -
    -
    -

    Training Plan - {{ plan.name }}

    -

    This plan starts on {{ plan.startdate }} and ends on {{ plan.enddate }}. The training plan target is: {{ plan.target.name }} on {{ plan.target.date }}.

    -

    Edit the plan

    -
    -
    -

    Macro Cycles

    -
    -
    -

    Meso Cycles

    -
    -
    -

    Micro Cycles

    -
    -
    +

    Plan Macro, Meso and Micro Cycles

    - -
    - {% now "Y-m-d" as todays_date %} - - {% for key,macrocycle in cycles.items %} - -
    - - {% if macrocycle.0.type == 'filler' %} -
    - {% else %} -
    - {% endif %} - -
    - - - - - {% if macrocycle.0.type == 'userdefined' %} - - - - - - - - - - - - - - - - - - - - - - {% endif %} - {% if todays_date <= macrocycle.0.enddate|date:"Y-m-d" %} - - - - - - - {% else %} - - - - - - - {% endif %} -
    - {{ macrocycle.0.name }} ({{ macrocycle.0.startdate }} - {{ macrocycle.0.enddate }}) -
    dist (m)t (min)rScoreTRIMP
    plan{{ macrocycle.0.plandistance }}{{ macrocycle.0.plantime }}{{ macrocycle.0.planrscore }}{{ macrocycle.0.plantrimp }}
    actual{{ macrocycle.0.actualdistance }}{{ macrocycle.0.actualtime }}{{ macrocycle.0.actualrscore }}{{ macrocycle.0.actualtrimp }}
     
    - edit - / - delete - / - sessions -
     
    - sessions -
    -
    - -
    - - -
    - - {% for key, mesocycle in macrocycle.1.items %} - -
    - - {% if mesocycle.0.type == 'filler' %} -
    - {% else %} -
    - {% endif %} - -
    - - - - - {% if mesocycle.0.type == 'userdefined' %} - - - - - - - - - - - - - - - - - - - - - - {% endif %} - {% if todays_date <= mesocycle.0.enddate|date:"Y-m-d" %} - {% if mesocycle.0.plan.type == 'userdefined' %} - - - - - - - {% endif %} - {% else %} - {% if mesocycle.0.plan.type == 'userdefined' %} - - - - - - - {% endif %} - {% endif %} -
    - {{ mesocycle.0.name }} ({{ mesocycle.0.startdate }} - {{ mesocycle.0.enddate }}) -
    dist (m)t (min)rScoreTRIMP
    plan{{ mesocycle.0.plandistance }}{{ mesocycle.0.plantime }}{{ mesocycle.0.planrscore }}{{ mesocycle.0.plantrimp }}
    actual{{ mesocycle.0.actualdistance }}{{ mesocycle.0.actualtime }}{{ mesocycle.0.actualrscore }}{{ mesocycle.0.actualtrimp }}
     
    - edit - / - delete - / - sessions -
     
    - sessions -
    -
    - -
    - - -
    - - {% for microcycle in mesocycle.1 %} - - {% if microcycle.type == 'filler' %} -
    - {% else %} -
    - {% endif %} - +
      + + {% for key, macrocycle in cycles.items %} +
    • + + +
        +
      • + {% if macrocycle.0.type == 'filler' %} +
        - {% if microcycle.type == 'userdefined' %} + {% if todays_date <= macrocycle.0.enddate|date:"Y-m-d" %} + +   + + + + + {% else %} + +   + + + sessions + + {% endif %} +
        - {{ microcycle.name }} ({{ microcycle.startdate }} - {{ microcycle.enddate }}) + {{ macrocycle.0.name }} ({{ macrocycle.0.startdate }} - {{ macrocycle.0.enddate }})
        + edit + / + delete + / + sessions +
        +
        +
        + {% else %} +
        +
        + + + + + {% if macrocycle.0.type == 'userdefined' %} @@ -201,90 +76,340 @@ - - - - + + + + - - - - + + + + {% endif %} - {% if todays_date <= microcycle.enddate|date:"Y-m-d" %} - {% if microcycle.plan.type == 'userdefined' %} + {% if todays_date <= macrocycle.0.enddate|date:"Y-m-d" %} + + {% else %} + + + + + {% endif %} - {% else %} - {% if microcycle.plan.type == 'userdefined' %} - - - - - - - {% endif %} - {% endif %}
        + {{ macrocycle.0.name }} ({{ macrocycle.0.startdate }} - {{ macrocycle.0.enddate }}) +
        dist (m)
        plan{{ microcycle.plandistance }}{{ microcycle.plantime }}{{ microcycle.planrscore }}{{ microcycle.plantrimp }}{{ macrocycle.0.plandistance }}{{ macrocycle.0.plantime }}{{ macrocycle.0.planrscore }}{{ macrocycle.0.plantrimp }}
        actual{{ microcycle.actualdistance }}{{ microcycle.actualtime }}{{ microcycle.actualrscore }}{{ microcycle.actualtrimp }}{{ macrocycle.0.actualdistance }}{{ macrocycle.0.actualtime }}{{ macrocycle.0.actualrscore }}{{ macrocycle.0.actualtrimp }}
         
        - edit + edit / - delete + delete / - sessions + sessions +
         
        + sessions
         
        - sessions -
        -
        - - {% endfor %} - -
    - -
    - - {% endfor %} - -
    - -
    - + {% endif %} + +
  • + + +
      + + {% for key, mesocycle in macrocycle.1.items %} +
    • + {% if mesocycle.0.type == 'filler' %} +
      +
      + + + + + {% if todays_date <= mesocycle.0.enddate|date:"Y-m-d" %} + {% if mesocycle.0.plan.type == 'userdefined' %} + +   + + + + + {% endif %} + {% else %} + +   + + + sessions + + {% endif %} +
      + Meso {{ mesocycle.0.name }} ({{ mesocycle.0.startdate }} - {{ mesocycle.0.enddate }}) +
      + edit + / + delete + / + sessions +
      +
      +
      + {% else %} +
      +
      + + + + + {% if mesocycle.0.type == 'userdefined' %} + + + + + + + + + + + + + + + + + + + + + + {% endif %} + {% if todays_date <= mesocycle.0.enddate|date:"Y-m-d" %} + + + + + + + {% else %} + + + + + + + {% endif %} +
      + Meso {{ mesocycle.0.name }} ({{ mesocycle.0.startdate }} - {{ mesocycle.0.enddate }}) +
      dist (m)t (min)rScoreTRIMP
      plan{{ mesocycle.0.plandistance }}{{ mesocycle.0.plantime }}{{ mesocycle.0.planrscore }}{{ mesocycle.0.plantrimp }}
      actual{{ mesocycle.0.actualdistance }}{{ mesocycle.0.actualtime }}{{ mesocycle.0.actualrscore }}{{ mesocycle.0.actualtrimp }}
       
      + edit + / + delete + / + sessions +
       
      + sessions +
      +
      +
      + {% endif %} +
    • +
    • + + +
        + + {% for microcycle in mesocycle.1 %} +
      • + {% if microcycle.type == 'filler' %} +
        +
        + + + + + {% if todays_date <= microcycle.enddate|date:"Y-m-d" %} + {% if microcycle.plan.type == 'userdefined' %} + + + + + + + {% endif %} + {% else %} + {% if microcycle.plan.type == 'userdefined' %} + + + + + + + {% endif %} + {% endif %} +
        + Micro {{ microcycle.name }} ({{ microcycle.startdate }} - {{ microcycle.enddate }}) +
         
        + edit + / + delete + / + sessions +
         
        + sessions +
        +
        +
        + {% else %} +
        +
        + + + + + + + + + + + + + + + + + + + + + + + + + + {% if todays_date <= microcycle.enddate|date:"Y-m-d" %} + + + + + + + {% else %} + + + + + + + {% endif %} +
        + Micro {{ microcycle.name }} ({{ microcycle.startdate }} - {{ microcycle.enddate }}) +
        dist (m)t (min)rScoreTRIMP
        plan{{ microcycle.plandistance }}{{ microcycle.plantime }}{{ microcycle.planrscore }}{{ microcycle.plantrimp }}
        actual{{ microcycle.actualdistance }}{{ microcycle.actualtime }}{{ microcycle.actualrscore }}{{ microcycle.actualtrimp }}
         
        + edit + / + delete + / + sessions +
         
        + sessions +
        +
        +
        + {% endif %} +
      • + {% endfor %} + +
      +
    • + {% endfor %} + +
    +
  • + + {% endfor %} - -
    - -
    -

    Click on the plan cycles to edit their names, start and end dates. - The gray "filler" - cycles are generated, adjusted and deleted automatically to - ensure the entire plan - duration is covered with non-overlapping cycles. - Once you edit a filler cycle, it become a user-defined - cycle, which cannot be deleted - by the system.

    -

    Filler cycles which have a filler cycle as a parent cannot be edited - or deleted. You have to edit the parent cycle first. The reason is - that children of filler cycles are not safe. They are deleted when - their parent is deleted by the system.

    -

    Click on "Sessions" in the cycle of your interest to see details - of the individual training sessions planned for this period.

    -

    A good way to organize the plan is to think of micro - cycles as training weeks. Macro cycles - are typically used to address specific phases of preparation - and to indicate the racing - season and may span several months. - Meso cycles can be used to group sequences of three to five - light, medium and - hard weeks. It is recommended to work from left to right, - starting with the macro cycles.

    -
    + + + +

    Click on the cycle to fold out its contents.

    + +

    Click on the plan cycles to edit their names, start and end dates. + The gray "filler" + cycles are generated, adjusted and deleted automatically to + ensure the entire plan + duration is covered with non-overlapping cycles. + Once you edit a filler cycle, it become a user-defined + cycle, which cannot be deleted + by the system.

    +

    Filler cycles which have a filler cycle as a parent cannot be edited + or deleted. You have to edit the parent cycle first. The reason is + that children of filler cycles are not safe. They are deleted when + their parent is deleted by the system.

    +

    Click on "Sessions" in the cycle of your interest to see details + of the individual training sessions planned for this period.

    +

    A good way to organize the plan is to think of micro + cycles as training weeks. Macro cycles + are typically used to address specific phases of preparation + and to indicate the racing + season and may span several months. + Meso cycles can be used to group sequences of three to five + light, medium and + hard weeks. It is recommended to work from left to right, + starting with the macro cycles.

    +{% endblock %} + +{% block scripts %} +{% if thismicro %} + + +{% elif thismeso %} + + +{% endif %} +{% if thismacro %} + + +{% endif %} +{% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} {% endblock %} diff --git a/rowers/templates/trainingplan_create.html b/rowers/templates/trainingplan_create.html index 350e2119..cbf7ffb6 100644 --- a/rowers/templates/trainingplan_create.html +++ b/rowers/templates/trainingplan_create.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -8,32 +8,11 @@ {% endblock %} -{% block content %} - - -
    - {% if user.is_authenticated and user|is_manager %} - - {% endif %} -
    - - -
    -
    -

    Training Targets

    +{% block main %} +

    Manage Training Targets and Plan for {{ rower.user.first_name }} {{ rower.user.last_name }}

    +
      +
    • +

      Training Targets

      {% if targets %} @@ -59,31 +38,22 @@ {% else %} No training targets found {% endif %} - -
      -
      -

      Add a target

      + +
    • +

      Add a target

      -
      -
    • - {{ targetform.as_table }} -
      - {% csrf_token %} -
      + + + {{ targetform.as_table }} +
      + {% csrf_token %} -
      - + -
    -
    -
    - - -
    - -
    -

    Plans

    + +
  • +

    Plans

    {% if plans %} @@ -111,28 +81,24 @@

    No plans found

    {% endif %} - - - - -
    -
    -

    Add a plan

    - -
    -
    - {{ form.as_table }} -
    - {% csrf_token %} -
    +
  • +
  • +

    Add a plan

    + + + + {{ form.as_table }} +
    + {% csrf_token %} -
  • - -
    -
    + + -
    {% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} diff --git a/rowers/templates/trainingplan_delete.html b/rowers/templates/trainingplan_delete.html index ffb9362c..d39d971f 100644 --- a/rowers/templates/trainingplan_delete.html +++ b/rowers/templates/trainingplan_delete.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -8,23 +8,17 @@ {% endblock %} -{% block content %} - +{% block main %} -
    - -
    - {% csrf_token %} -

    Are you sure you want to delete {{ object }}?

    -
    - -
    -
    +
    + {% csrf_token %} +

    Are you sure you want to delete {{ object }}?

    + +
    -
    {% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} diff --git a/rowers/templates/trainingplan_edit.html b/rowers/templates/trainingplan_edit.html index 1dc67326..3f24d8d7 100644 --- a/rowers/templates/trainingplan_edit.html +++ b/rowers/templates/trainingplan_edit.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -8,25 +8,20 @@ {% endblock %} -{% block content %} - +{% block main %} -
    +
    + {% csrf_token %} + + {{ form.as_table }} +
    + +
    -
    - {% csrf_token %} - - {{ form.as_table }} -
    -
    - -
    -
    - -
    - {% endblock %} +{% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} + diff --git a/rowers/templates/user_boxplot_select.html b/rowers/templates/user_boxplot_select.html index 64960688..46008816 100644 --- a/rowers/templates/user_boxplot_select.html +++ b/rowers/templates/user_boxplot_select.html @@ -1,10 +1,10 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Workouts{% endblock %} -{% block content %} +{% block main %} - {% if team %} -
    - {% include "teambuttons.html" with teamid=team.id team=team %} -
    +{% if theuser %} +

    Select {{ theuser.first_name }}'s Workouts for BoxPlot

    +{% else %} +

    Select {{ user.first_name }}'s Workouts for BoxPlot

    {% endif %} -
    -
    - {% if theuser %} -

    {{ theuser.first_name }}'s Workouts

    - {% else %} -

    {{ user.first_name }}'s Workouts

    - {% endif %} -
    -
    - {% if user.is_authenticated and user|is_manager %} - -
    -
    -
    - {% if theuser %} -
    - {% else %} - - {% endif %} -
    - - - {{ dateform.as_table }} -
    - {% csrf_token %} -
    -
    - -
    -
    - {% if theuser %} -
    - {% else %} - - {% endif %} -
    - - - {{ optionsform.as_table }} -
    - {% csrf_token %} -
    -
    - -
    -
    -
    - -
    -
    -
    - -
    -
    - -
    -
    -
    - -
    -
    -
    +
      +
    • +

      You can use the date and search forms to search through all + workouts from this team.

      +

      TIP: Agree with your team members to put tags (e.g. '8x500m') in the notes section of + your workouts. That makes it easy to search.

      +
    • +
    • + {% if workouts %} @@ -149,31 +88,54 @@ {{ form.as_table }}
      -{% else %} -

      No workouts found

      -{% endif %} -
    -
    -

    Warning: You are on an experimental part of the site. Use at your own risk.

    -

    Select two or more workouts on the left, set your plot settings below, - and press submit"

    - {% csrf_token %} - - {{ chartform.as_table }} -
    -
    + {% else %} +

    No workouts found

    + {% endif %} + +
  • +

    Select two or more workouts, set your plot settings below, + and press submit +

    + {% csrf_token %} + + {{ chartform.as_table }} +

    -

    -
  • -
    -

    You can use the date and search forms above to search through all - workouts from this team.

    -

    TIP: Agree with your team members to put tags (e.g. '8x500m') in the notes section of - your workouts. That makes it easy to search.

    -
    -
    -
    +

    + + +
  • + {% if theuser %} +
    + {% else %} + + {% endif %} + + {{ dateform.as_table }} +
    + + {{ optionsform.as_table }} +
    + {% csrf_token %} + +
    +
  • +
  • +
    + + +
    +
  • + + {% endblock %} + +{% block sidebar %} +{% include 'menu_analytics.html' %} +{% endblock %} diff --git a/rowers/templates/user_multiflex_select.html b/rowers/templates/user_multiflex_select.html index 3e6e52d9..2e0ede5c 100644 --- a/rowers/templates/user_multiflex_select.html +++ b/rowers/templates/user_multiflex_select.html @@ -1,10 +1,10 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Workouts{% endblock %} -{% block content %} +{% block main %} - - {% if team %} -
    - {% include "teambuttons.html" with teamid=team.id team=team %} -
    +{% if theuser %} +

    Select {{ theuser.first_name }}'s Workouts for the Trend Flex Chart

    +{% else %} +

    Select {{ user.first_name }}'s Workouts for the Trend Flex Chart

    {% endif %} -
    -
    - {% if theuser %} -

    {{ theuser.first_name }}'s Workouts

    - {% else %} -

    {{ user.first_name }}'s Workouts

    - {% endif %} -
    -
    - {% if user.is_authenticated and user|is_manager %} - -
    -
    -
    - {% if theuser %} -
    - {% else %} - - {% endif %} -
    - - - {{ dateform.as_table }} -
    - {% csrf_token %} -
    -
    - -
    -
    - {% if theuser %} -
    - {% else %} - - {% endif %} -
    - - - {{ modalityform.as_table }} -
    - {% csrf_token %} -
    -
    - -
    -
    -
    - - -
    -
    -
    - -
    -
    - -
    -
    -
    - -
    - - -
    -
    - +
      +
    • +

      You can use the date and search forms to search through all + workouts from this team.

      +

      TIP: Agree with your team members to put tags (e.g. '8x500m') in the notes section of + your workouts. That makes it easy to search.

      +
    • +
    • + {% if workouts %} Toggle All
      {{ form.as_table }}
      -{% else %} -

      No workouts found

      -{% endif %} -
    -
    -

    Warning: You are on an experimental part of the site. Use at your own risk.

    -

    Select one or more workouts on the left, set your plot settings below, - and press submit"

    + {% else %} +

    No workouts found

    + {% endif %} + +
  • {% csrf_token %} {{ chartform.as_table }}
    -
    -

    - -

    -
    -
    -

    You can use the date and search forms above to search through all - workouts from this team.

    -

    TIP: Agree with your team members to put tags (e.g. '8x500m') in the notes section of - your workouts. That makes it easy to search.

    -
    -
  • -
    +

    + +

    + + +
  • + {% if theuser %} +
    + {% else %} + + {% endif %} + + + {{ dateform.as_table }} +
    + + {{ modalityform.as_table }} +
    + {% csrf_token %} + +
    +
  • +
  • +
    + + +
    +
  • + + {% endblock %} + +{% block sidebar %} +{% include 'menu_analytics.html' %} +{% endblock %} diff --git a/rowers/templates/userprofile_deactivate.html b/rowers/templates/userprofile_deactivate.html index 7f4b2c15..de600442 100644 --- a/rowers/templates/userprofile_deactivate.html +++ b/rowers/templates/userprofile_deactivate.html @@ -1,11 +1,10 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Deactivate your account{% endblock %} -{% block content %} -
    +{% block main %}

    Deactivate your account


    Account deactivation is reversible. After you logout, you will not be @@ -17,6 +16,10 @@ -

    + +{% endblock %} + +{% block sidebar %} +{% include 'menu_profile.html' %} {% endblock %} diff --git a/rowers/templates/userprofile_delete.html b/rowers/templates/userprofile_delete.html index 04331c3f..d5099477 100644 --- a/rowers/templates/userprofile_delete.html +++ b/rowers/templates/userprofile_delete.html @@ -1,12 +1,11 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Delete your account{% endblock %} -{% block content %} -
    -

    Delete your account

    +{% block main %} +

    Delete your account


    Warning: This will remove your account and all your data. You will not be able to recover from this action. We cannot restore @@ -17,6 +16,9 @@ -

    {% endblock %} + +{% block sidebar %} +{% include 'menu_profile.html' %} +{% endblock %} diff --git a/rowers/templates/virtualevent.html b/rowers/templates/virtualevent.html index c1e22e52..a731cf32 100644 --- a/rowers/templates/virtualevent.html +++ b/rowers/templates/virtualevent.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -8,21 +8,29 @@ {% include "monitorjobs.html" %} {% endblock %} -{% block content %} - -
    - -

    {{ race.name }}

    +{% block main %} -
    +

    {{ race.name }}

    -
    -
    +
      +
    • +

      +

      Course

      +

      +
      + {{ coursediv|safe }} + + {{ coursescript|safe }} +
      +
    • +
    • -

      Race Information

      - +

      Race Information

      +

      +

      +

      @@ -72,29 +80,50 @@

      {% for button in buttons %} {% if button == 'registerbutton' %} - Register +

      + Register +

      {% endif %} {% if button == 'submitbutton' %} - Submit Result + Submit Result {% endif %} {% if button == 'resubmitbutton' %} - Submit New Result +

      + Submit New Result +

      {% endif %} {% if button == 'withdrawbutton' %} - Withdraw +

      + Withdraw +

      {% endif %} {% if button == 'adddisciplinebutton' %} - Register New Boat +

      + + Register New Boat + +

      {% endif %} {% if button == 'editbutton' %} - Edit Race +

      + Edit Race + +

      {% endif %} {% endfor %} -

      {% endif %} + +
    • -

      Results

      +

      +

      Results

      +

      {% if results or dns %}

    • Course{{ race.course }}
      @@ -153,10 +182,30 @@ {% endif %}

      + + {% if form %} +
    • + +

      +

      Filter Results

      +

      +

      + +
      +

    • + {{ form.as_table }} +
      + + {% csrf_token %} + +

      +
    • + {% endif %} +
    • {% if records %}

      Registered Competitors

      - +
      @@ -188,8 +237,12 @@
      Name
      {% endif %}
      +
    • +
    • -

      Rules

      +

      +

      Rules

      +

      Virtual races are intended as an informal way to add a competitive element to training and as a quick way to set @@ -233,30 +286,11 @@ safety while participating in a virtual race.

      -
    -
    -

    -

    Course

    - {{ coursediv|safe }} - - {{ coursescript|safe }} - -

    - - {% if form %} -

    -

    Filter Results

    - - - - {{ form.as_table }} -
    - - {% csrf_token %} - -

    - {% endif %} -
    -
    + + {% endblock %} + +{% block sidebar %} +{% include 'menu_racing.html' %} +{% endblock %} diff --git a/rowers/templates/virtualeventcreate.html b/rowers/templates/virtualeventcreate.html index cc25ce89..6898709f 100644 --- a/rowers/templates/virtualeventcreate.html +++ b/rowers/templates/virtualeventcreate.html @@ -1,37 +1,42 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}New Virtual Race{% endblock %} -{% block content %} +{% block main %} -
    - -

    New Virtual Race

    +

    New Virtual Race

    +
      +
    • With this form, you can create a new virtual race. After you submit the form, the race is created and will be visible to all users. From - that moment, only the site admin can delete the race (admin@rowsandall.com). You can still edit the race until the start of the race window.

      + that moment, only the site admin can delete the race + (admin@rowsandall.com). You can still edit the race until + the start of the race window. +

      +
    • -
      +
    • {% if form.errors %}

      Please correct the error{{ form.errors|pluralize }} below.

      {% endif %} - - - {{ form.as_table }} -
      - {% csrf_token %} -
      +

      + + {{ form.as_table }} +
      +

      +

      + {% csrf_token %} -

      - -
    • -
      +

      + + +
      • All times are local times in the race course time zone
      • @@ -42,11 +47,15 @@
      • Participants can submit results until the evaluation closure time.

      -
    • -
    + + {% endblock %} {% block scripts %} {% endblock %} + +{% block sidebar %} +{% include 'menu_racing.html' %} +{% endblock %} diff --git a/rowers/templates/virtualeventedit.html b/rowers/templates/virtualeventedit.html index b12906a4..796cb490 100644 --- a/rowers/templates/virtualeventedit.html +++ b/rowers/templates/virtualeventedit.html @@ -1,34 +1,33 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Edit Virtual Race{% endblock %} -{% block content %} +{% block main %} -
    - -

    Edit Race {{ race.name }}

    +

    Edit Race {{ race.name }}

    - -
    +
      +
    • {% if form.errors %}

      Please correct the error{{ form.errors|pluralize }} below.

      {% endif %} - - - {{ form.as_table }} -
      - {% csrf_token %} -
      +

      + + {{ form.as_table }} +
      +

      +

      + {% csrf_token %} -

      +

      -
    -
    + +
    • All times are local times in the race course time zone
    • @@ -39,11 +38,16 @@
    • Participants can submit results until the evaluation closure time.

    -
  • -
    + + {% endblock %} {% block scripts %} {% endblock %} + + +{% block sidebar %} +{% include 'menu_racing.html' %} +{% endblock %} diff --git a/rowers/templates/virtualeventregister.html b/rowers/templates/virtualeventregister.html index e7501437..5ed6c79a 100644 --- a/rowers/templates/virtualeventregister.html +++ b/rowers/templates/virtualeventregister.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -16,13 +16,10 @@ {% endblock %} -{% block content %} -
    -
    -

    Register for {{ race.name }}

    -
    +{% block main %} +

    Register for {{ race.name }}

    + -
    @@ -44,10 +41,8 @@
    -
    {% csrf_token %} -
    @@ -55,3 +50,7 @@ {% block scripts %} {% endblock %} + +{% block sidebar %} +{% include 'menu_racing.html' %} +{% endblock %} diff --git a/rowers/templates/virtualevents.html b/rowers/templates/virtualevents.html index 5f7c92f7..4524a435 100644 --- a/rowers/templates/virtualevents.html +++ b/rowers/templates/virtualevents.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -24,8 +24,6 @@ data: data, success: function(data) { - console.log("got something back"); - console.log(data) $('#racelist').html(data) } @@ -57,40 +55,35 @@ {% endblock %} -{% block content %} +{% block main %} -
    -
    +
    -
    -

    - Courses +

    + {{ form.as_table }} + {% csrf_token %}

    -
    -
    - -
    - -
    - {{ form.as_table }} - {% csrf_token %} -
    -
    - -
    - -
    - -
    +

    + + +

    +
  • Click on the event name or on the Details button to see the event details (and manage your participation and results). Click on the course name to see the course details.

    - {% include 'racelist.html' %} -
  • + - - {% endblock %} +
  • + + {% include 'racelist.html' %} +
  • + + + +{% endblock %} + +{% block sidebar %} +{% include 'menu_racing.html' %} +{% endblock %} diff --git a/rowers/templates/windedit.html b/rowers/templates/windedit.html index e0eb56bb..933f5705 100644 --- a/rowers/templates/windedit.html +++ b/rowers/templates/windedit.html @@ -1,41 +1,13 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Advanced Features {% endblock %} -{% block content %} -
    -
    -

    Wind Editor

    -
    -
    -
    -

    - Edit Workout -

    -
    -
    -

    - Advanced Edit -

    - -
    -
    -

    OTW Power

    - Run calculations to get power values for your row. - -
    -
    -
    - - -
    -
    +{% block main %} +

    Wind Editor

    +
      +
    • Update wind between distance 1 and distance 2. Submit wind strength and direction at start and end of segment. Blank the form for values @@ -47,84 +19,61 @@ to find historical weather data from an on-line weather station near the location of your row.

      - -
      -
      - {% if form.errors %} -

      - Please correct the error{{ form.errors|pluralize }} below. -

      - {% endif %} - - - {{ form.as_table }} -
      - {% csrf_token %} - -
      -
      -

      Closest Airport Weather: {{ airport }} - ({{ airportdistance | floatformat:-1 }} km) - Airport Data

      -

      - Dark Sky Data -

      - Download wind speed and bearing from The Dark Sky +

    • +
    • + + {% if form.errors %} +

      + Please correct the error{{ form.errors|pluralize }} below.

      -
    -
    + {% endif %} + + + {{ form.as_table }} +
    + {% csrf_token %}

    Manual update of the wind data from the form.

    -
    + +
  • +

    Closest Airport Weather: {{ airport }} + ({{ airportdistance | floatformat:-1 }} km) + Airport Data

    +

    + Dark Sky Data +

    + Download wind speed and bearing from The Dark Sky +

    +
  • +
  • -
  • -
    -
    + +
  • - - + + - {{ interactiveplot |safe }} + {{ interactiveplot |safe }} - - - - -
    -
    - {{ the_div |safe }} -
    -
    - -
    -
    + {{ the_div |safe }} +
  • +
  • +
    {{ gmapdiv |safe }} {{ gmap |safe }}
    -
  • + + {% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/workflow.html b/rowers/templates/workflow.html index 7beed9d4..1a71dab0 100644 --- a/rowers/templates/workflow.html +++ b/rowers/templates/workflow.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% load tz %} @@ -37,69 +37,44 @@ var counter=0; $("#id_sitready").remove(); $("#id_thumbs").append( - "

    Click on the thumbnails to view the full chart.

    "); + "
  • Click on the thumbnails to view the full chart.

  • "); $.each(json, function(index, element) { console.log('adding thumbnail'); var counter2 = counter+1; - if (shownotes) { $("#id_thumbs").append( - "
    "+ + "
  • "+ ""+counter2+""+ - "
    "+ ""+element.div+""+ - ""+element.notes+""+ - "
  • "); - } else { - $("#id_thumbs").append( - "
    "+ - ""+counter2+""+ - "
    "+ - ""+element.div+""+ - "
    "); + ""); - } $("#id_thumbscripts").append(element.script); counter += 1; + }); }); - }); + + }); {% endblock %} -{% block content %} -
    -
    -

    {{ workout.name }}

    - {% if workout.user.user != user %} -

    {{ workout.user.user.first_name }} {{ workout.user.user.last_name }} - {% endif %} -

    -
    - {% block left_panel %} - - {% for templateName in leftTemplates %} - {% include templateName %} - {% endfor %} - {% endblock %} -
    -
    - {% block middle_panel %} - {% for templateName in middleTemplates %} -
    - {% include templateName %} -
    - {% endfor %} - {% endblock %} -
    -
    - {% block right_panel %} -

     

    - {% endblock %} -
    +{% block main %} +

    {{ workout.name }}

    +{% if workout.user.user != user %} +

    {{ workout.user.user.first_name }} {{ workout.user.user.last_name }}

    +{% endif %} + +{% block middle_panel %} +{% for templateName in middleTemplates %} +{% include templateName %} +{% endfor %} +{% endblock %} +{% block right_panel %} +{% endblock %} + +{% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} {% endblock %} diff --git a/rowers/templates/workflowconfig2.html b/rowers/templates/workflowconfig2.html index abdfe052..5d8e2bf3 100644 --- a/rowers/templates/workflowconfig2.html +++ b/rowers/templates/workflowconfig2.html @@ -1,66 +1,52 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} -{% block title %}Change Rower Export Settings{% endblock %} +{% block title %}Change Rower Workflow Page Settings{% endblock %} -{% block content %} +{% block main %} +

    Change Workflow Page Layout for {{ rower.user.first_name }} {{ rower.user.last_name }}

    -
    +
      {% if workoutid %} - + {% endif %} -
      -

      Workflow Page Configuration

      +
    • On this page, you can configure the content of your "Workflow" page for each workout. If you want to remove an element, change it to "None". You can add one new element at a time.

      -
      -

      Left Panel

      -
      - {{ leftpanel_formset.management_form }} - - {{ leftpanel_formset.as_table }} -
      - {% csrf_token %} - +
    • +
    • + + {{ middlepanel_formset.management_form }} + + {{ middlepanel_formset.as_table }} +
      + {% csrf_token %} + -
    • - -
      -

      Middle Panel

      -
      - {{ middlepanel_formset.management_form }} - - {{ middlepanel_formset.as_table }} -
      - {% csrf_token %} - -
      -
      - -
      -
      - {% if rower.defaultlandingpage == 'workout_edit_view' %} -

      Default landing page is Edit View. Set default landing page to

      - Workflow View - {% else %} -

      Default landing page is Workflow View. Set default landing page to

      - Edit View - {% endif %} -
      -
      - -
    -
    - - + +
  • + {% if rower.defaultlandingpage == 'workout_edit_view' %} +

    Default landing page is Edit View. Set default landing page to

    + Workflow View + {% else %} +

    Default landing page is Workflow View. Set default landing page to

    + Edit View + {% endif %} +
  • + {% endblock %} + + +{% block sidebar %} +{% include 'menu_profile.html' %} +{% endblock %} diff --git a/rowers/templates/workout_comments.html b/rowers/templates/workout_comments.html index 053c7c0d..0a988423 100644 --- a/rowers/templates/workout_comments.html +++ b/rowers/templates/workout_comments.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% load tz %} @@ -7,19 +7,13 @@ {% include "monitorjobs.html" %} {% endblock %} -{% block title %}Change Workout {% endblock %} +{% block title %}Comment Workout {% endblock %} -{% block content %} -
    -
    - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. -

    - {% endif %} - -

    Comments {{ workout.name }}

    +{% block main %} +

    Comments {{ workout.name }}

    +
    - {% for c in comments %} -
    -
    - {{ c.created }} - {{ c.user.first_name }} {{ c.user.last_name }} -
    -
    -
    -
    - {{ c.comment }} -
    + +
  • + {% for c in comments %} + {{ c.created }} + {{ c.user.first_name }} {{ c.user.last_name }} +
    +
    + {{ c.comment }}
    -
  • {% endfor %} - -
    {{ form.as_table }}
    {% csrf_token %} -
    - -
    +
    -
    - -
    - -
    -

    Images linked to this workout

    - - {% if graphs1 %} - - {% for graph in graphs1 %} - {% if forloop.counter == 1 %} -
    + + {% for graph in graphs %} +
  • - {{ graph.filename }} -
  • - {% elif forloop.counter == 3 %} -
    - - {{ graph.filename }} -
    - - {% else %} -
    - - {{ graph.filename }} -
    - {% endif %} + {{ graph.filename }} + + {% endfor %} - - {% for graph in graphs2 %} - {% if forloop.counter == 1 %} -
    - - {{ graph.filename }} -
    - {% elif forloop.counter == 3 %} -
    - - {{ graph.filename }} -
    - - {% else %} -
    - - {{ graph.filename }} -
    - {% endif %} - {% endfor %} - - - +
  • +

    Workout Summary

    + +

    +

    +        {{ workout.summary }}
    +      
    +

    + +
  • - {% else %} -

    No graphs found

    - {% endif %} -
    - - - -
    -

    Workout Summary

    +
  • + + -

    -

    -      {{ workout.summary }}
    -    
    -

    + {{ gmscript |safe }} -
  • - - - -
    - - - - {{ gmscript |safe }} - - -
    - {{ gmdiv|safe }} -
    -
    +
    + {{ gmdiv|safe }} +
    + + {% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/workout_delete_confirm.html b/rowers/templates/workout_delete_confirm.html index b64a5976..b54141d2 100644 --- a/rowers/templates/workout_delete_confirm.html +++ b/rowers/templates/workout_delete_confirm.html @@ -1,51 +1,41 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Change Workout {% endblock %} -{% block content %} -
    - - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. -

    - {% endif %} - -

    Confirm Workout Delete

    - This will delete the following workout and all linked graph images: - - - +{% block main %} +

    Delete {{ workout }}?

    +
      +
    • +

      + This will delete the following workout and all related data (charts, comments): +

      +
    + + + - + - + - + - -
    Name:{{ workout.name }}
    Date:{{ workout.date }}
    Time:{{ workout.starttime }}
    Distance:{{ workout.distance }}m
    Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
    - -
    -

    - Cancel -

    - -
    -

    - Delete -

    -
    - -
    - -
    -

    -   - -

    + + + +
  • +
    + {% csrf_token %} + +
    +
  • + -{% endblock %} \ No newline at end of file +{% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/workout_form.html b/rowers/templates/workout_form.html index 9cdb027c..026721ab 100644 --- a/rowers/templates/workout_form.html +++ b/rowers/templates/workout_form.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% load tz %} @@ -42,261 +42,102 @@ $('#id_workouttype').change(); {% endblock %} -{% block content %} -
    +{% block main %} - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. -

    - {% endif %} - -

    Edit Workout {{ workout.name }}

    -
    -
    -

    - Delete -

    -
    -
    -

    - Export - -

    -
    -
    -

    - Advanced -

    - Advanced Functionality (More interactive Charts) - -
    -
    -
    -
    -

    - Workflow View -

    -
    -
    -

    - Map View -

    -
    -
    -

    - Statistics -

    -
    -
    -
    - - - {% localtime on %} - {% if workout.user.user != user %} +

    Edit Workout {{ workout.name }}

    +
    + + {% localtime on %} + {% if workout.user.user != user %} - - {% endif %} - -{% endlocaltime %} - - - - - - - - - - - - - - -
    Rower:{{ rower.user.first_name }} {{ rower.user.last_name }}
    Date/Time:{{ workout.startdatetime|localtime}}
    Distance:{{ workout.distance }}m
    Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
    Public link to this workout - https://rowsandall.com/rowers/workout/{{ workout.id }} -
    Comments - Comment ({{ aantalcomments }}) -
    Public link to interactive chart - https://rowsandall.com/rowers/workout/{{ workout.id }}/interactiveplot - -
    -
    -
    + + {% endif %} + Date/Time:{{ workout.startdatetime|localtime}} + {% endlocaltime %} + + Distance:{{ workout.distance }}m + + Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }} + + Public link to this workout + + https://rowsandall.com/rowers/workout/{{ workout.id }} + + + + +
  • + {% if form.errors %} +

    + Please correct the error{{ form.errors|pluralize }} below. +

    + {% endif %} + +
    - - {{ form.as_table }} -
    - {% csrf_token %} -
    + + {{ form.as_table }} +
    + {% csrf_token %} -
    -
    -
  • -
    -
    -

    Images linked to this workout

    -
    -
    -

    - Add Time Plot -

    -
    - - -
    -
    -

    - Attach Image -

    -
    -
    -

    - Power Pie Chart -

    -
    -
    -

    Generating images takes roughly 1 second per minute - of your workout's duration. Please reload after a minute or so.

    -
    -
    - {% if graphs1 %} + + +
  • - {% for graph in graphs1 %} - {% if forloop.counter == 1 %} -
    - - {{ graph.filename }} -
    - {% elif forloop.counter == 3 %} -
    - - {{ graph.filename }} -
    +

    Workout Summary

    - {% else %} -
    - - {{ graph.filename }} -
    - {% endif %} - {% endfor %} - - - {% for graph in graphs2 %} - {% if forloop.counter == 1 %} -
    - - {{ graph.filename }} -
    - {% elif forloop.counter == 3 %} -
    - - {{ graph.filename }} -
    - - {% else %} -
    - - {{ graph.filename }} -
    - {% endif %} - {% endfor %} - - - - - {% else %} -

    No graphs found

    - {% endif %} -
  • - - - -
    -
    -

    Workout Summary

    - -

    -

    -          {{ workout.summary }}
    -        
    -

    -
    -

    - Update Summary -

    -
    -
    -
    -
    - -
    -
    -

     

    - -
    -
    -
    - - - -
    +

    +

    +        {{ workout.summary }}
    +      
    +

    + + {% if mapdiv %} +
  • - - +
    {{ mapdiv|safe }} - +
    {{ mapscript|safe }} -
  • -
    + + {% endif %} + {% for graph in graphs %} +
  • + + {{ graph.filename }} + +
  • + {% endfor %} + {% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/workout_join_select.html b/rowers/templates/workout_join_select.html index ece4ea83..50a0d5bb 100644 --- a/rowers/templates/workout_join_select.html +++ b/rowers/templates/workout_join_select.html @@ -1,10 +1,10 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Workouts{% endblock %} -{% block content %} +{% block main %} -
    - {% include "teambuttons.html" with teamid=team.id team=team %} -
    -
    -

    {{ team.name }} Team Workouts

    -
    -
    -
    - {% if team %} -
    - {% else %} - - {% endif %} -
    - - {{ dateform.as_table }} -
    - {% csrf_token %} -
    -
    - -
    -
    - {% if team %} -
    - {% else %} - - {% endif %} -
    - - {{ modalityform.as_table }} -
    - {% csrf_token %} -
    -
    - -
    -
    -
    -
    - {% if team %} -
    - {% else %} - -{% endif %} -
    - -
    -
    - -
    -
    -
    +

    {{ team.name }} Join Workouts

    -
    - - -
    -
    - - - {% if workouts %} +
      +
    • + + {% if workouts %} - Toggle All
      + Toggle All
      - - {{ form.as_table }} -
      + + {{ form.as_table }} +
      -{% else %} -

      No workouts found

      -{% endif %} -
    -
    -

    Warning: You are on an experimental part of the site. Use at your own risk.

    + {% else %} +

    No workouts found

    + {% endif %} + +
  • Select two or more workouts on the left, and press submit

    {{ joinparamform.as_table }}
    -

    - {% csrf_token %} + {% csrf_token %} +

    + +
  • +
  • + {% if team %} +
    + {% else %} + + {% endif %} + + {{ dateform.as_table }} +
    +

    + {% csrf_token %} +

    -
  • -
    -

    You can use the date and search forms above to search through all - workouts from this team.

    -
    -
    - + + +
  • + {% if team %} +
    + {% else %} + + {% endif %} + + {{ modalityform.as_table }} +
    +

    + {% csrf_token %} + +

    +
    +
  • +
  • + {% if team %} +
    + {% else %} + + {% endif %} + + +
    +
  • + {% endblock %} + + +{% block sidebar %} +{% include 'menu_workouts.html' %} +{% endblock %} diff --git a/rowers/templates/workout_view.html b/rowers/templates/workout_view.html index ea0fc977..852444d6 100644 --- a/rowers/templates/workout_view.html +++ b/rowers/templates/workout_view.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block scripts %} @@ -16,183 +16,119 @@ {% for graph in graphs1 %} {% block og_image %} {% if graphs1 %} -{% for graph in graphs1 %} - - - - - {% endfor %} - {% else %} - - - {% endif %} +{% for graph in graphs %} + + + + +{% endfor %} +{% else %} + + +{% endif %} {% endblock %} {% block image_src %} -{% for graph in graphs1 %} - +{% for graph in graphs %} + {% endfor %} {% endblock %} {% endfor %} -{% block content %} -
    +{% block main %} - -

    {{ workout.name }}

    -
    - -
    -
    - -
    - - - +

    {{ workout.name }}

    +
    + - - + + - - + + - + - + - + - + - + - - {% if user.is_authenticated %} - - + + {% if user.is_authenticated %} + + - - {% endif %} -
    Rower:{{ first_name }} {{ last_name }}
    Name:{{ workout.name }}
    Date:{{ workout.date }}
    Time:{{ workout.starttime }}
    Distance:{{ workout.distance }}m
    Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
    Type:{{ workout.workouttype }}
    Weight Category:{{ workout.weightcategory }}
    Comments
    Comments Comment ({{ aantalcomments }})
    - -

    Workout Summary

    - -

    -

    -{{ workout.summary }}
    -
    -

    + + {% endif %} + + +
  • +

    Workout Summary

    +

    +

    +        {{ workout.summary }}
    +      
    +

    +
  • + {% if mapdiv %} +
  • +
    {{ mapdiv|safe }} {{ mapscript|safe }} - -
    -
    - -

    Images linked to this workout

    - {% if graphs1 %} - - {% for graph in graphs1 %} - {% if forloop.counter == 1 %} -
    - - {{ graph.filename }} -
    - {% elif forloop.counter == 3 %} -
    - - {{ graph.filename }} -
    - - {% else %} -
    - - {{ graph.filename }} -
    - {% endif %} - {% endfor %} - - - {% for graph in graphs2 %} - {% if forloop.counter == 1 %} -
    - - {{ graph.filename }} -
    - {% elif forloop.counter == 3 %} -
    - - {{ graph.filename }} -
    - - {% else %} -
    - - {{ graph.filename }} -
    - {% endif %} - {% endfor %} - - - - - {% else %} -

    No graphs found

    - {% endif %} -
    - - - - - {{ interactiveplot |safe }} - - + - + + {{ interactiveplot |safe }} - -
    {{ the_div|safe }} -
    +
  • + {% for graph in graphs %} +
  • + + {{ graph.filename }} + +
  • + {% endfor %} + {% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/workoutstats.html b/rowers/templates/workoutstats.html index ac867eff..e89d3a79 100644 --- a/rowers/templates/workoutstats.html +++ b/rowers/templates/workoutstats.html @@ -1,52 +1,22 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Workout Statistics{% endblock %} -{% block content %} -
    -

    Workout Statistics for {{ workout.name }}

    -

    - This page lists a bunch of statistics for - your workout. The mean is of a metric is the mean with equal weight for - each stroke. The time weighted mean takes into account the stroke - duration. -

    -
    +{% block main %} +

    Workout Statistics for {{ workout.name }}

    +
      +
    • - Edit Workout + This page lists a bunch of statistics for + your workout. The mean is of a metric is the mean with equal weight for + each stroke. The time weighted mean takes into account the stroke + duration.

      -
    -
    -

    - Workflow View -

    - -
    -
    -

    - Advanced -

    -
    -
    -
    - {% csrf_token %} - {% if workstrokesonly == True %} - - - {% else %} - - - {% endif %} -
    - If your data source allows, this will show or hide strokes taken during rest intervals. -
    -
    - -
    + {% if otherstats %} -
    +
  • Workout Metrics

    @@ -66,94 +36,134 @@ {% endfor %}
    -
  • -
    + +
  • rPower: Equivalent steady state power for the duration of the workout.

    Heart Rate Drift: Comparing heart rate normalized for average power for the first and second half of the workout

    TRIMP: TRaining IMPact. A way to combine duration and heart rate into a single number.

    rScore: Score based on rPower and workout duration to estimate training effect

    rScore (HR): Score based on heart rate, designed to give values comparable to rScore. Used instead of rScore for workouts without power data.

    -
  • - + {% endif %} -
    +
  • +
    +
    + {% csrf_token %} + {% if workstrokesonly == True %} + + + {% else %} + + + {% endif %} +
    + If your data source allows, this will show or hide strokes taken during rest intervals. +
    +
  • -
    - {% if stats %} -

    Statistics

    - - - - - - - - - - - - - - - - {% for key, value in stats.items %} - - - - - - - - - - - - {% endfor %} - -
    MetricMeanTime Weighted MeanMinimum25%Median75%MaximumStandard Deviation
    {{ value.verbosename }}{{ value.mean|floatformat:-2 }} {{ value.wmean|floatformat:-2 }}{{ value.min|floatformat:-2 }}{{ value.firstq|floatformat:-2 }}{{ value.median|floatformat:-2 }}{{ value.thirdq|floatformat:-2 }}{{ value.max|floatformat:-2 }}{{ value.std|floatformat:-2 }}
    - {% endif %} -
    - -
    - {% if cordict %} -

    Correlation matrix

    -

    This matrix indicates a positive (+) or negative (-) correlation between two parameters. The Spearman correlation coefficient has values between +1 and -1. Positive correlation between two metrics means that if one metric increases, the other value is also likely to increase. Negative is the opposite. The further from zero, the higher the likelyhood. -

    - - - - - {% for key,value in cordict.items %} - - {% endfor %} - - - - {% for key, thedict in cordict.items %} - - - {% for key2,value in thedict.items %} - - {% endfor %} - - {% endfor %} - -
     
    {{ key }}
    {{ key }} - {% if value > 0.5 %} -
    {{ value|floatformat:-1 }}
    - {% elif value > 0.1 %} -
    {{ value|floatformat:-1 }}
    - {% elif value < -0.5 %} -
    {{ value|floatformat:-1 }}
    - {% elif value < -0.1 %} -
    {{ value|floatformat:-1 }}
    - {% else %} -   - {% endif %} -
    + {% if stats %} +
  • +

    Statistics

    + + + + + + + + + + + + + + + + {% for key, value in stats.items %} + + {% if value.std > 2 %} + + + + + + + + + + {% elif value.std > 1 %} + + + + + + + + + + {% else %} + + + + + + + + + + {% endif %} + + {% endfor %} + +
    MetricMeanTime Weighted MeanMinimum25%Median75%MaximumStandard Deviation
    {{ value.verbosename }}{{ value.mean|floatformat:0 }} {{ value.wmean|floatformat:0 }}{{ value.min|floatformat:0 }}{{ value.firstq|floatformat:0 }}{{ value.median|floatformat:0 }}{{ value.thirdq|floatformat:0 }}{{ value.max|floatformat:0 }}{{ value.std|floatformat:0 }}{{ value.verbosename }}{{ value.mean|floatformat:-1 }} {{ value.wmean|floatformat:-1 }}{{ value.min|floatformat:-1 }}{{ value.firstq|floatformat:-1 }}{{ value.median|floatformat:-1 }}{{ value.thirdq|floatformat:-1 }}{{ value.max|floatformat:-1 }}{{ value.std|floatformat:-1 }}{{ value.verbosename }}{{ value.mean|floatformat:-2 }} {{ value.wmean|floatformat:-2 }}{{ value.min|floatformat:-2 }}{{ value.firstq|floatformat:-2 }}{{ value.median|floatformat:-2 }}{{ value.thirdq|floatformat:-2 }}{{ value.max|floatformat:-2 }}{{ value.std|floatformat:-2 }}
    +
  • {% endif %} -
    + + {% if cordict %} +
  • +

    Correlation matrix

    +

    This matrix indicates a positive (+) or negative (-) correlation between two parameters. The Spearman correlation coefficient has values between +1 and -1. Positive correlation between two metrics means that if one metric increases, the other value is also likely to increase. Negative is the opposite. The further from zero, the higher the likelyhood. +

    + + + + + {% for key,value in cordict.items %} + + {% endfor %} + + + + {% for key, thedict in cordict.items %} + + + {% for key2,value in thedict.items %} + + {% endfor %} + + {% endfor %} + +
     
    {{ key }}
    {{ key }} + {% if value > 0.5 %} +
    {{ value|floatformat:-1 }}
    + {% elif value > 0.1 %} +
    {{ value|floatformat:-1 }}
    + {% elif value < -0.5 %} +
    {{ value|floatformat:-1 }}
    + {% elif value < -0.1 %} +
    {{ value|floatformat:-1 }}
    + {% else %} +   + {% endif %} +
    +
  • + {% endif %} + {% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py index dede3b70..66fde5a2 100644 --- a/rowers/templatetags/rowerfilters.py +++ b/rowers/templatetags/rowerfilters.py @@ -5,13 +5,21 @@ from django.utils import timezone import dateutil.parser import json import datetime +import re register = template.Library() from rowers.utils import calculate_age -from rowers.models import course_length +from rowers.models import course_length,WorkoutComment from rowers.plannedsessions import ( race_can_register, race_can_submit,race_rower_status ) +from rowers import c2stuff, runkeeperstuff +from rowers.c2stuff import c2_open +from rowers.runkeeperstuff import runkeeper_open + +from rowers.types import otwtypes +from rowers.utils import NoTokenError + def strfdelta(tdelta): minutes,seconds = divmod(tdelta.seconds,60) tenths = int(tdelta.microseconds/1e5) @@ -47,6 +55,24 @@ def secondstotimestring(tdelta): return res +@register.filter +def aantalcomments(workout): + try: + comments = WorkoutComment.objects.filter(workout=workout) + except: + return 0 + + aantalcomments = len(comments) + + return aantalcomments + +@register.filter +def water(workout): + try: + return workout.workouttype in otwtypes + except AttributeError: + return False + @register.filter def ddays(ddelta): return ddelta.days+1 @@ -76,6 +102,28 @@ def deltatimeprint(d): else: return strfdeltah(d) +@register.filter +def c2userid(user): + try: + thetoken = c2_open(user) + except NoTokenError: + return 0 + + c2userid = c2stuff.get_userid(thetoken) + + return c2userid + +@register.filter +def rkuserid(user): + try: + thetoken = runkeeper_open(user) + except NoTokenError: + return 0 + + rkuserid = runkeeperstuff.get_userid(thetoken) + + return rkuserid + @register.filter def courselength(course): return course_length(course) @@ -119,6 +167,18 @@ def is_session_manager(id,user): return ps.manager == user +from rowers.models import checkworkoutuser + +@register.filter +def may_edit(workout,request): + mayedit = 0 + if request.user == workout.user.user: + mayedit = True + if checkworkoutuser(request.user,workout): + mayedit = True + + return mayedit + @register.filter(name='times') def times(number): return range(number) @@ -128,7 +188,7 @@ def get_field_id(id,s,form): field_name = s+str(id) return form.__getitem__(field_name) -from rowers.models import Rower,Team +from rowers.models import Rower,Team,TrainingPlan,TrainingTarget from rowers.views import ispromember @register.filter @@ -282,3 +342,34 @@ def is_past_due(self): @property def is_not_past_due(self): return datetime.date.today() <= self.date + +@register.filter +def userurl(path,member): + pattern = re.compile('user\/\d+') + userstring = 'user/%s' % member.id + if pattern.search(path) is not None: + replaced = pattern.sub(userstring,path) + else: + replaced = path+userstring + + return replaced + +@register.filter +def timeurl(path,timestring): + pattern = re.compile('\?when=w.*') + timeurl = '?when=%s' % timestring + replaced = '' + + if pattern.search(path) is not None: + replaced = pattern.sub(timeurl,path) + + if not replaced: + replaced = path+timeurl + + return replaced + +@register.filter +def trainingplans(rower): + plans = TrainingPlan.objects.filter(rower=rower).order_by("-startdate") + + return plans diff --git a/rowers/tests.py b/rowers/tests.py index 43838b70..3b179640 100644 --- a/rowers/tests.py +++ b/rowers/tests.py @@ -429,10 +429,10 @@ class C2Objects(DjangoTestCase): response = self.c.get('/rowers/workout/1/c2uploadw/') self.assertRedirects(response, - expected_url = '/rowers/workout/1/export', + expected_url = '/rowers/workout/1/edit', status_code=302,target_status_code=200) - self.assertEqual(response.url, '/rowers/workout/1/export') + self.assertEqual(response.url, '/rowers/workout/1/edit') self.assertEqual(response.status_code, 302) @patch('rowers.c2stuff.requests.post', side_effect=mocked_requests) @@ -599,10 +599,10 @@ class STObjects(DjangoTestCase): response = self.c.get('/rowers/workout/1/sporttracksuploadw/') self.assertRedirects(response, - expected_url = '/rowers/workout/1/export', + expected_url = '/rowers/workout/1/edit', status_code=302,target_status_code=200) - self.assertEqual(response.url, '/rowers/workout/1/export') + self.assertEqual(response.url, '/rowers/workout/1/edit') self.assertEqual(response.status_code, 302) @patch('rowers.sporttracksstuff.requests.get', side_effect=mocked_requests) @@ -706,10 +706,10 @@ class RunKeeperObjects(DjangoTestCase): response = self.c.get('/rowers/workout/1/runkeeperuploadw/') self.assertRedirects(response, - expected_url = '/rowers/workout/1/export', + expected_url = '/rowers/workout/1/edit', status_code=302,target_status_code=200) - self.assertEqual(response.url, '/rowers/workout/1/export') + self.assertEqual(response.url, '/rowers/workout/1/edit') self.assertEqual(response.status_code, 302) @patch('rowers.runkeeperstuff.requests.get', side_effect=mocked_requests) @@ -807,10 +807,10 @@ class UAObjects(DjangoTestCase): response = self.c.get('/rowers/workout/1/underarmouruploadw/') self.assertRedirects(response, - expected_url = '/rowers/workout/1/export', + expected_url = '/rowers/workout/1/edit', status_code=302,target_status_code=200) - self.assertEqual(response.url, '/rowers/workout/1/export') + self.assertEqual(response.url, '/rowers/workout/1/edit') self.assertEqual(response.status_code, 302) @patch('rowers.underarmourstuff.requests.get', side_effect=mocked_requests) @@ -907,10 +907,10 @@ class TPObjects(DjangoTestCase): response = self.c.get('/rowers/workout/1/tpuploadw/') self.assertRedirects(response, - expected_url = '/rowers/workout/1/export', + expected_url = '/rowers/workout/1/edit', status_code=302,target_status_code=200) - self.assertEqual(response.url, '/rowers/workout/1/export') + self.assertEqual(response.url, '/rowers/workout/1/edit') self.assertEqual(response.status_code, 302) @@ -1334,11 +1334,9 @@ class ViewTest(TestCase): response = self.c.get('/rowers/workout/1/', form_data, follow=True) self.assertEqual(response.status_code, 200) - response = self.c.get('/rowers/workout/1/export', form_data, follow=True) + response = self.c.get('/rowers/workout/1/edit', form_data, follow=True) self.assertEqual(response.status_code, 200) - response = self.c.get('/rowers/workout/1/interactiveplot', form_data, follow=True) - self.assertEqual(response.status_code, 200) response = self.c.get('/rowers/workout/1/histo', form_data, follow=True) self.assertEqual(response.status_code, 200) @@ -1388,9 +1386,15 @@ class ViewTest(TestCase): 'boattype':'1x', 'notes':'aap noot mies', 'make_plot':False, - 'upload_to_c2':False, + 'upload_to_C2':False, + 'upload_to_Strava':False, + 'upload_to_SportTracks':False, + 'upload_to_RunKeeper':False, + 'upload_to_MapMyFitness':False, 'plottype':'timeplot', 'file': f, + 'makeprivate':False, + 'landingpage':'workout_edit_view', } form = DocumentsForm(form_data,file_data) @@ -1407,9 +1411,6 @@ class ViewTest(TestCase): response = self.c.get('/rowers/workout/1/', form_data, follow=True) self.assertEqual(response.status_code, 200) - response = self.c.get('/rowers/workout/1/interactiveplot', form_data, follow=True) - self.assertEqual(response.status_code, 200) - w = Workout.objects.get(id=1) f_to_be_deleted = w.csvfilename diff --git a/rowers/urls.py b/rowers/urls.py index 24fe4aeb..ef065810 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -117,7 +117,7 @@ urlpatterns = [ url(r'^404/$', TemplateView.as_view(template_name='404.html'),name='404'), url(r'^400/$', TemplateView.as_view(template_name='400.html'),name='400'), url(r'^403/$', TemplateView.as_view(template_name='403.html'),name='403'), - url(r'^imports/$', views.imports_view), +# url(r'^imports/$', views.imports_view), url(r'^exportallworkouts/?$',views.workouts_summaries_email_view), url(r'^update_empower$',views.rower_update_empower_view), url(r'^agegroupcp/(?P\d+)$',views.agegroupcpview), @@ -138,8 +138,8 @@ urlpatterns = [ url(r'^list-workouts/team/(?P\d+)/$',views.workouts_view), url(r'^(?P\d+)/list-workouts/$',views.workouts_view), url(r'^(?P\d+)/list-workouts/(?P\w+.*)/(?P\w+.*)$',views.workouts_view), - url(r'^u/(?P\d+)/list-workouts/$',views.workouts_view), - url(r'^u/(?P\d+)/list-workouts/(?P\w+.*)/(?P\w+.*)$',views.workouts_view), + url(r'^list-workouts/user/(?P\d+)/$',views.workouts_view), + url(r'^list-workouts/(?P\w+.*)/(?P\w+.*)/user/(?P\d+)/$',views.workouts_view), url(r'^list-workouts/(?P\w+.*)/(?P\w+.*)$',views.workouts_view), url(r'^virtualevents$',views.virtualevents_view), url(r'^virtualevent/create$',views.virtualevent_create_view), @@ -164,9 +164,7 @@ urlpatterns = [ url(r'^workouts-join-select/team/(?P\d+)/$',views.workouts_join_select), url(r'^workouts-join-select/(?P\w+.*)/(?P\w+.*)$',views.workouts_join_select), url(r'^workouts-join-select/$',views.workouts_join_select), - url(r'^user-boxplot-select/user/(?P\d+)/(?P\w+.*)/(?P\w+.*)$',views.user_boxplot_select), url(r'^user-boxplot-select/user/(?P\d+)/$',views.user_boxplot_select), - url(r'^user-boxplot-select/(?P\w+.*)/(?P\w+.*)$',views.user_boxplot_select), url(r'^user-boxplot-select/$',views.user_boxplot_select), url(r'^user-multiflex-select/user/(?P\d+)/(?P\w+.*)/(?P\w+.*)$',views.user_multiflex_select), url(r'^user-multiflex-select/user/(?P\d+)/$',views.user_multiflex_select), @@ -182,58 +180,41 @@ urlpatterns = [ url(r'^record-progress$',views.post_progress), url(r'^list-graphs/$',views.graphs_view), url(r'^fitness-progress/$',views.fitnessmetric_view), - url(r'^fitness-progress/rower/(?P\d+)$',views.fitnessmetric_view), - url(r'^fitness-progress/rower/(?P\d+)/(?P\w+.*)$',views.fitnessmetric_view), - url(r'^(?P\d+)/ote-bests/(?P\w+.*)/(?P\w+.*)$',views.rankings_view), - url(r'^(?P\d+)/ote-bests/(?P\d+)$',views.rankings_view), + url(r'^fitness-progress/user/(?P\d+)$',views.fitnessmetric_view), + url(r'^fitness-progress/user/(?P\d+)/(?P\w+.*)$',views.fitnessmetric_view), + url(r'^ote-bests/user/(?P\d+)/(?P\w+.*)/(?P\w+.*)$',views.rankings_view), + url(r'^ote-bests/user/(?P\d+)$',views.rankings_view), url(r'^ote-bests/(?P\w+.*)/(?P\w+.*)$',views.rankings_view), - url(r'^ote-bests/(?P\d+)$',views.rankings_view), url(r'^ote-bests/$',views.rankings_view), url(r'^(?P\d+)/ote-bests/$',views.rankings_view), url(r'^(?P\d+)/ote-bests2/(?P\w+.*)/(?P\w+.*)$',views.rankings_view2), - url(r'^(?P\d+)/ote-bests2/(?P\d+)$',views.rankings_view2), + url(r'^ote-bests2/user/(?P\d+)$',views.rankings_view2), url(r'^ote-bests2/(?P\w+.*)/(?P\w+.*)$',views.rankings_view2), - url(r'^ote-bests2/(?P\d+)$',views.rankings_view2), url(r'^ote-bests2/$',views.rankings_view2), - url(r'^(?P\d+)/ote-bests2/$',views.rankings_view2), - url(r'^(?P\d+)/otw-bests/(?P\w+.*)/(?P\w+.*)$',views.otwrankings_view), - url(r'^(?P\d+)/otw-bests/(?P\d+)$',views.otwrankings_view), + url(r'^otw-bests/user/(?P\d+)/(?P\w+.*)/(?P\w+.*)$',views.otwrankings_view), url(r'^otw-bests/(?P\w+.*)/(?P\w+.*)$',views.otwrankings_view), - url(r'^otw-bests/(?P\d+)$',views.otwrankings_view), url(r'^otw-bests/$',views.otwrankings_view), - url(r'^(?P\d+)/ote-ranking/(?P\w+.*)/(?P\w+.*)$',views.oterankings_view), - url(r'^(?P\d+)/ote-ranking/(?P\d+)$',views.oterankings_view), + url(r'^ote-ranking/user/(?P\d+)/(?P\w+.*)/(?P\w+.*)$',views.oterankings_view), url(r'^ote-ranking/(?P\w+.*)/(?P\w+.*)$',views.oterankings_view), - url(r'^ote-ranking/(?P\d+)$',views.oterankings_view), url(r'^ote-ranking/$',views.oterankings_view), - url(r'^(?P\d+)/ote-ranking/$',views.oterankings_view), - url(r'^(?P\d+)/flexall/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)$',views.cum_flex), - url(r'^flexall/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)$',views.cum_flex), - url(r'^flexall/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)$',views.cum_flex), + url(r'^ote-ranking/user/(?P\d+)/$',views.oterankings_view), + url(r'^flexall/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)/user/(?P\d+)$',views.cum_flex), + url(r'^flexall/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)/$',views.cum_flex), + url(r'^flexall/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)/$',views.cum_flex), + url(r'^flexall/user/(?P\d+)/$',views.cum_flex), url(r'^flexall/$',views.cum_flex), - url(r'^flexalldata/$',views.cum_flex_data), - - url(r'^histo/u/(?P\d+)$',views.histo), - url(r'^flexall/u/(?P\d+)$',views.cum_flex), - url(r'^(?P\d+)/histo/(?P\w+.*)/(?P\w+.*)$',views.histo), - url(r'^(?P\d+)/histo/(?P\d+)$',views.histo), - url(r'^histo/(?P\w+.*)/(?P\w+.*)$',views.histo), - url(r'^histo/(?P\d+)$',views.histo), + url(r'^histo/user/(?P\d+)$',views.histo), + url(r'^histodata$',views.histo_data), + url(r'^histo/user/(?P\d+)/(?P\w+.*)/(?P\w+.*)$',views.histo), url(r'^histo/$',views.histo), - url(r'^cumstats/u/(?P\d+)$',views.cumstats), - url(r'^cumstats/(?P\w+.*)/(?P\w+.*)/p/(?P\w+.*)$',views.cumstats), - url(r'^(?P\d+)/cumstats/(?P\w+.*)/(?P\w+.*)/p/(?P\w+.*)$',views.cumstats), - url(r'^(?P\d+)/cumstats/(?P\w+.*)/(?P\w+.*)$',views.cumstats), - url(r'^(?P\d+)/cumstats/(?P\d+)$',views.cumstats), + url(r'^cumstats/user/(?P\d+)$',views.cumstats), + url(r'^cumstats/(?P\w+.*)/(?P\w+.*)$',views.cumstats), + url(r'^cumstats/user/(?P\d+)/(?P\w+.*)/(?P\w+.*)$',views.cumstats), url(r'^cumstats/(?P\w+.*)/(?P\w+.*)$',views.cumstats), - url(r'^cumstats/(?P\d+)$',views.cumstats), url(r'^cumstats/$',views.cumstats), - url(r'^histo-all/$',views.histo_all), - url(r'^(?P\d+)/histo-all/$',views.histo_all), url(r'^graph/(?P\d+)/$',views.graph_show_view), - url(r'^graph/(?P\d+)/deleteconfirm$',views.graph_delete_confirm_view), - url(r'^graph/(?P\d+)/delete$',views.graph_delete_view), + url(r'^graph/(?P\d+)/delete$',views.GraphDelete.as_view(),name='graph_delete'), url(r'^workout/(?P\d+)/get-thumbnails$',views.get_thumbnails), url(r'^workout/(?P\d+)/toggle-ranking$',views.workout_toggle_ranking), url(r'^workout/(?P\d+)/get-testscript$',views.get_testscript), @@ -243,7 +224,7 @@ urlpatterns = [ url(r'^workout/(?P\d+)/task$',views.workout_test_task_view), url(r'^workout/(?P\d+)/forcecurve$',views.workout_forcecurve_view), url(r'^workout/(?P\d+)/unsubscribe$',views.workout_unsubscribe_view), - url(r'^workout/(?P\d+)/export$',views.workout_export_view), +# url(r'^workout/(?P\d+)/export$',views.workout_export_view), url(r'^workout/(?P\d+)/comment$',views.workout_comment_view), url(r'^workout/(?P\d+)/emailtcx$',views.workout_tcxemail_view), url(r'^workout/(?P\d+)/emailgpx$',views.workout_gpxemail_view), @@ -251,17 +232,17 @@ urlpatterns = [ url(r'^workout/(?P\d+)/csvtoadmin$',views.workout_csvtoadmin_view), url(r'^ergcpdatatoadmin/(?P\d+)/(?P\d+-\d+-\d+)/(?P\w+.*)$',views.otecp_toadmin_view), url(r'^otwcpdatatoadmin/(?P\d+)/(?P\d+-\d+-\d+)/(?P\w+.*)$',views.otwcp_toadmin_view), - url(r'^workout/compare/(?P\d+)/$',views.workout_comparison_list), - url(r'^workout/compare2/(?P\d+)/(?P\d+)/(?P\w+.*)/(?P\w+.*)/$',views.workout_comparison_view), +# url(r'^workout/compare/(?P\d+)/$',views.workout_comparison_list), +# url(r'^workout/compare2/(?P\d+)/(?P\d+)/(?P\w+.*)/(?P\w+.*)/$',views.workout_comparison_view), url(r'^workout/compare/(?P\d+)/(?P\d+-\d+-\d+)/(?P\w+.*)$',views.workout_comparison_list), url(r'^workout/(?P\d+)/edit$',views.workout_edit_view, name='workout_edit_view'), url(r'^workout/(?P\d+)/map$',views.workout_map_view), - url(r'^workout/(?P\d+)/setprivate$',views.workout_setprivate_view), +# url(r'^workout/(?P\d+)/setprivate$',views.workout_setprivate_view), url(r'^workout/(?P\d+)/updatecp$',views.workout_update_cp_view), - url(r'^workout/(?P\d+)/makepublic$',views.workout_makepublic_view), - url(r'^workout/(?P\d+)/geeky$',views.workout_geeky_view), - url(r'^workout/(?P\d+)/advanced$',views.workout_advanced_view), +# url(r'^workout/(?P\d+)/makepublic$',views.workout_makepublic_view), +# url(r'^workout/(?P\d+)/geeky$',views.workout_geeky_view), +# url(r'^workout/(?P\d+)/advanced$',views.workout_advanced_view), url(r'^workout/(?P\d+)/instroke/(?P\w+.*)$',views.instroke_chart), url(r'^workout/(?P\d+)/instroke$',views.instroke_view), url(r'^workout/(?P\d+)/stats$',views.workout_stats_view), @@ -272,46 +253,21 @@ urlpatterns = [ url(r'^workout/(?P\d+)/darkskywind$',views.workout_downloadwind_view), url(r'^workout/(?P\d+)/metar/(?P\w+)$',views.workout_downloadmetar_view), url(r'^workout/(?P\d+)/stream$',views.workout_stream_view), - url(r'^workout/(?P\d+)/crewnerdsummary$',views.workout_crewnerd_summary_view), +# url(r'^workout/(?P\d+)/crewnerdsummary$',views.workout_crewnerd_summary_view), url(r'^workout/(?P\d+)/editintervals$',views.workout_summary_edit_view), url(r'^workout/(?P\d+)/restore$',views.workout_summary_restore_view), url(r'^workout/(?P\d+)/split$',views.workout_split_view), - url(r'^workout/(?P\d+)/interactiveplot$',views.workout_biginteractive_view), +# url(r'^workout/(?P\d+)/interactiveplot$',views.workout_biginteractive_view), url(r'^workout/(?P\d+)/view$',views.workout_view), - url(r'^workout/(?P\d+)$',views.workout_view), + url(r'^workout/(?P\d+)/$',views.workout_view), url(r'^workout/fusion/(?P\d+)/(?P\d+)$',views.workout_fusion_view), url(r'^workout/fusion/(?P\d+)/$',views.workout_fusion_list), url(r'^workout/fusion/(?P\d+)/(?P\d+-\d+-\d+)/(?P\w+.*)$',views.workout_fusion_list), - url(r'^redesign$',TemplateView.as_view( - template_name='redesign.html'),name='redesign' - ), - url(r'^new_workouts$',TemplateView.as_view( - template_name='redesign_workouts.html'),name='redesign' - ), - url(r'^new_workout$',TemplateView.as_view( - template_name='redesign_workout.html'),name='redesign' - ), - url(r'^new_plan$',TemplateView.as_view( - template_name='redesign_plan.html'),name='redesign' - ), - url(r'^new_profile$',TemplateView.as_view( - template_name='redesign_profile.html'),name='redesign' - ), - url(r'^new_racing$',TemplateView.as_view( - template_name='redesign_racing.html'),name='redesign' - ), - url(r'^new_teams$',TemplateView.as_view( - template_name='redesign_teams.html'),name='redesign' - ), url(r'^help$',TemplateView.as_view( template_name='help.html'),name='help' ), - url(r'^new_analysis$',TemplateView.as_view( - template_name='redesign_analysis.html'),name='redesign' - ), url(r'^physics$',TemplateView.as_view(template_name='physics.html'),name='physics'), url(r'^partners$',TemplateView.as_view(template_name='partners.html'),name='partners'), - url(r'^workout/(?P\d+)/$',views.workout_view), # keeping the old URLs for retrofit url(r'^workout/(?P\d+)/addtimeplot$', views.workout_add_chart_view, @@ -338,24 +294,30 @@ urlpatterns = [ url(r'^workout/(?P\d+)/addstatic/(?P\d+)$', views.workout_add_chart_view), url(r'^workout/(?P\d+)/addstatic$',views.workout_add_chart_view), - url(r'^workout/(?P\d+)/delete$',views.workout_delete_view), + url(r'^workout/(?P\d+)/delete$',views.WorkoutDelete.as_view(),name='workout_delete'), url(r'^workout/(?P\d+)/smoothenpace$',views.workout_smoothenpace_view), url(r'^workout/(?P\d+)/undosmoothenpace$',views.workout_undo_smoothenpace_view), url(r'^workout/c2import/$',views.workout_c2import_view), url(r'^workout/c2list/$',views.workout_c2import_view), url(r'^workout/c2list/(?P\d+)$',views.workout_c2import_view), + url(r'^workout/c2list/user/(?P\d+)$',views.workout_c2import_view), + url(r'^workout/c2list/(?P\d+)/user/(?P\d+)$',views.workout_c2import_view), url(r'^workout/stravaimport/$',views.workout_stravaimport_view), + url(r'^workout/stravaimport/user/(?P\d+)$',views.workout_stravaimport_view), url(r'^workout/c2import/all/$',views.workout_getc2workout_all), url(r'^workout/c2import/all/(?P\d+)$',views.workout_getc2workout_all), url(r'^workout/(?P\w+.*)import/(?P\d+)/$',views.workout_getimportview), url(r'^workout/stravaimport/all/$',views.workout_getstravaworkout_all), url(r'^workout/stravaimport/next/$',views.workout_getstravaworkout_next), url(r'^workout/sporttracksimport/$',views.workout_sporttracksimport_view), + url(r'^workout/sporttracksimport/user/(?P\d+)$',views.workout_sporttracksimport_view), url(r'^workout/sporttracksimport/all/$',views.workout_getsporttracksworkout_all), url(r'^workout/polarimport/$',views.workout_polarimport_view), + url(r'^workout/polarimport/user/(?P\d+)/',views.workout_polarimport_view), url(r'^workout/runkeeperimport/$',views.workout_runkeeperimport_view), + url(r'^workout/runkeeperimport/user/(?P\d+)$',views.workout_runkeeperimport_view), url(r'^workout/underarmourimport/$',views.workout_underarmourimport_view), - url(r'^workout/(?P\d+)/deleteconfirm$',views.workout_delete_confirm_view), +# url(r'^workout/(?P\d+)/deleteconfirm$',views.workout_delete_confirm_view), url(r'^workout/(?P\d+)/c2uploadw/$',views.workout_c2_upload_view), url(r'^workout/(?P\d+)/stravauploadw/$',views.workout_strava_upload_view), url(r'^workout/(?P\d+)/recalcsummary/$',views.workout_recalcsummary_view), @@ -364,13 +326,11 @@ urlpatterns = [ url(r'^workout/(?P\d+)/underarmouruploadw/$',views.workout_underarmour_upload_view), url(r'^workout/(?P\d+)/tpuploadw/$',views.workout_tp_upload_view), url(r'^multi-compare$',views.multi_compare_view), - url(r'^user-boxplot/(?P\d+)$',views.boxplot_view), - url(r'^user-boxplot/$',views.boxplot_view), + url(r'^user-boxplot/user/(?P\d+)$',views.boxplot_view), url(r'^user-boxplot$',views.boxplot_view), url(r'^user-boxplot-data$',views.boxplot_view_data), - url(r'^user-multiflex/(?P\d+)$',views.multiflex_view), + url(r'^user-multiflex/user/(?P\d+)$',views.multiflex_view), url(r'^user-multiflex/$',views.multiflex_view), - url(r'^user-multiflex$',views.multiflex_view), url(r'^user-multiflex-data$',views.multiflex_data), url(r'^me/deactivate$',views.deactivate_user), url(r'^me/delete$',views.remove_user), @@ -379,6 +339,7 @@ urlpatterns = [ url(r'^me/teams/$',views.rower_teams_view), url(r'^me/calcdps/$',views.rower_calcdps_view), url(r'^me/exportsettings/$',views.rower_exportsettings_view), + url(r'^me/exportsettings/user/(?P\d+)$',views.rower_exportsettings_view), url(r'^team/(?P\d+)/$',views.team_view), url(r'^team/(?P\d+)/memberstats$',views.team_members_stats_view), url(r'^team/(?P\d+)/edit$',views.team_edit_view), @@ -399,7 +360,9 @@ urlpatterns = [ url(r'^me/request/(\w+.*)/$',views.manager_requests_view), url(r'^me/request/$',views.manager_requests_view), url(r'^me/edit/$',views.rower_edit_view), - url(r'^rower/edit/(?P\d+)$',views.rower_edit_view), + url(r'^me/edit/user/(?P\d+)$',views.rower_edit_view), + url(r'^me/preferences/$',views.rower_prefs_view), + url(r'^me/preferences/user/(?P\d+)$',views.rower_prefs_view), url(r'^me/edit/(.+.*)/$',views.rower_edit_view), url(r'^me/c2authorize/$',views.rower_c2_authorize), url(r'^me/polarauthorize/$',views.rower_polar_authorize), @@ -414,8 +377,10 @@ urlpatterns = [ url(r'^me/tprefresh/$',views.rower_tp_token_refresh), url(r'^me/c2refresh/$',views.rower_c2_token_refresh), url(r'^me/favoritecharts/$',views.rower_favoritecharts_view), - url(r'^me/workflowconfig$',views.workout_workflow_config_view), - url(r'^me/workflowconfig2$',views.workout_workflow_config2_view), + url(r'^me/favoritecharts/user/(?P\d+)$',views.rower_favoritecharts_view), +# url(r'^me/workflowconfig$',views.workout_workflow_config_view), + url(r'^me/workflowconfig2/$',views.workout_workflow_config2_view), + url(r'^me/workflowconfig2/user/(?P\d+)$',views.workout_workflow_config2_view), url(r'^me/workflowdefault$',views.workflow_default_view), url(r'^email/send/$', views.sendmail), url(r'^email/thankyou/$', TemplateView.as_view(template_name='thankyou.html'), name='thankyou'), @@ -424,15 +389,17 @@ urlpatterns = [ url(r'^brochure$',TemplateView.as_view(template_name='brochure.html'), name='brochure'), url(r'^developers', TemplateView.as_view(template_name='developers.html'),name='about'), - url(r'^compatibility', TemplateView.as_view(template_name='compatibility.html'),name='about'), - url(r'^videos', TemplateView.as_view(template_name='videos.html'),name='videos'), - url(r'^analysis', TemplateView.as_view(template_name='analysis.html'),name='analysis'), - url(r'^laboratory', TemplateView.as_view(template_name='laboratory.html'),name='laboratory'), +# url(r'^compatibility', TemplateView.as_view(template_name='compatibility.html'),name='about'), +# url(r'^videos', TemplateView.as_view(template_name='videos.html'),name='videos'), + url(r'^analysis/user/(?P\d+)', views.analysis_view,name='analysis'), + url(r'^laboratory/user/(?P\d+)', views.laboratory_view,name='laboratory'), + url(r'^analysis', views.analysis_view,name='analysis'), + url(r'^laboratory', views.laboratory_view,name='laboratory'), url(r'^promembership', TemplateView.as_view(template_name='promembership.html'),name='promembership'), url(r'^starttrial$',views.start_trial_view), url(r'^startplantrial$',views.start_plantrial_view), - url(r'^planmembership', TemplateView.as_view(template_name='planmembership.html'),name='planmembership'), - url(r'^paypaltest', TemplateView.as_view(template_name='paypaltest.html'),name='paypaltest'), +# url(r'^planmembership', TemplateView.as_view(template_name='planmembership.html'),name='planmembership'), + # url(r'^paypaltest', TemplateView.as_view(template_name='paypaltest.html'),name='paypaltest'), url(r'^legal', TemplateView.as_view(template_name='legal.html'),name='legal'), url(r'^register$',views.rower_register_view), url(r'^register/thankyou/$', TemplateView.as_view(template_name='registerthankyou.html'), name='registerthankyou'), @@ -442,114 +409,80 @@ urlpatterns = [ url(r'^workout/(?P\d+)/flexchart/(?P\w+.*)/(?P[\w\ ]+.*)/(?P[\w\ ]+.*)/(?P\w+.*)$',views.workout_flexchart3_view), url(r'^workout/(?P\d+)/flexchart/(?P\w+.*)/(?P[\w\ ]+.*)/(?P[\w\ ]+.*)$',views.workout_flexchart3_view), url(r'^workout/(?P\d+)/flexchart$',views.workout_flexchart3_view), - url(r'^workout/compare/(?P\d+)/(?P\d+)/(?P\w+.*)/(?P[\w\ ]+.*)/(?P[\w\ ]+.*)$',views.workout_comparison_view2), - url(r'^workout/compare/(?P\d+)/(?P\d+)/(?P\w+.*)/(?P[\w\ ]+.*)/$',views.workout_comparison_view2), +# url(r'^workout/compare/(?P\d+)/(?P\d+)/(?P\w+.*)/(?P[\w\ ]+.*)/(?P[\w\ ]+.*)$',views.workout_comparison_view2), +# url(r'^workout/compare/(?P\d+)/(?P\d+)/(?P\w+.*)/(?P[\w\ ]+.*)/$',views.workout_comparison_view2), url(r'^test\_callback',views.rower_process_testcallback), - url(r'^createplan$',views.rower_create_trainingplan), - url(r'^createplan/(?P\d+)$',views.rower_create_trainingplan), + url(r'^createplan/$',views.rower_create_trainingplan), + url(r'^createplan/user/(?P\d+)/$',views.rower_create_trainingplan), url(r'^deleteplan/(?P\d+)$',views.TrainingPlanDelete.as_view()), - url(r'^deletemicrocycle/(?P\d+)$',views.MicroCycleDelete.as_view()), - url(r'^deletemesocycle/(?P\d+)$',views.MesoCycleDelete.as_view()), - url(r'^deletemacrocycle/(?P\d+)$',views.MacroCycleDelete.as_view()), + url(r'^deletemicrocycle/(?P\d+)/$',views.MicroCycleDelete.as_view()), + url(r'^deletemesocycle/(?P\d+)/$',views.MesoCycleDelete.as_view()), + url(r'^deletemacrocycle/(?P\d+)/$',views.MacroCycleDelete.as_view()), # url(r'^deleteplan/(?P\d+)$',views.rower_delete_trainingplan), - url(r'^plan/(?P\d+)$',views.rower_trainingplan_view), - url(r'^macrocycle/(?P\d+)$',views.TrainingMacroCycleUpdate.as_view()), - url(r'^mesocycle/(?P\d+)$',views.TrainingMesoCycleUpdate.as_view()), - url(r'^microcycle/(?P\d+)$',views.TrainingMicroCycleUpdate.as_view()), - url(r'^deletetarget/(?P\d+)$',views.rower_delete_trainingtarget), - url(r'^editplan/(?P\d+)$',views.TrainingPlanUpdate.as_view()), - url(r'^edittarget/(?P\d+)$',views.TrainingTargetUpdate.as_view()), + url(r'^plan/(?P\d+)/$',views.rower_trainingplan_view), + url(r'^plan/(?P\d+)/user/(?P\d+)/$',views.rower_trainingplan_view), + url(r'^plan/(?P\d+)/micro/(?P\d+)/$',views.rower_trainingplan_view), + url(r'^plan/(?P\d+)/micro/(?P\d+)/user/(?P\d+)/$',views.rower_trainingplan_view), + url(r'^plan/(?P\d+)/meso/(?P\d+)/$',views.rower_trainingplan_view), + url(r'^plan/(?P\d+)/meso/(?P\d+)/user/(?P\d+)/$',views.rower_trainingplan_view), + url(r'^plan/(?P\d+)/macro/(?P\d+)/$',views.rower_trainingplan_view), + url(r'^plan/(?P\d+)/macro/(?P\d+)/user/(?P\d+)/$',views.rower_trainingplan_view), + url(r'^macrocycle/(?P\d+)/$',views.TrainingMacroCycleUpdate.as_view(), + name='macrocycle_update_view'), + url(r'^mesocycle/(?P\d+)/$',views.TrainingMesoCycleUpdate.as_view(), + name='mesocycle_update_view'), + url(r'^microcycle/(?P\d+)/$',views.TrainingMicroCycleUpdate.as_view(), + name='microcycle_update_view'), + url(r'^deletetarget/(?P\d+)/$',views.rower_delete_trainingtarget), + url(r'^editplan/(?P\d+)$',views.TrainingPlanUpdate.as_view(), + name='trainingplan_update_view'), + url(r'^edittarget/(?P\d+)/$',views.TrainingTargetUpdate.as_view(), + name='trainingtarget_update_view'), url(r'^workout/(?P\d+)/test\_strokedata$',views.strokedataform), - - url(r'^sessions/teamcreate$',views.plannedsession_teamcreate_view), - url(r'^sessions/teamcreate/(?P[\w\ ]+.*)$', + url(r'^sessions/teamcreate/user/(?P\d+)/$',views.plannedsession_teamcreate_view), + url(r'^sessions/teamcreate/team/(?P\d+)/user/(?P\d+)/$', views.plannedsession_teamcreate_view), - url(r'^sessions/teamcreate/(?P[\w\ ]+.*)/team/(?P\d+)$', + url(r'^sessions/teamcreate/$',views.plannedsession_teamcreate_view), + url(r'^sessions/teamcreate/team/$', views.plannedsession_teamcreate_view), - url(r'^sessions/teamcreate/team/(?P\d+)$', - views.plannedsession_teamcreate_view), - - url(r'^sessions/teamedit/(?P\d+)$',views.plannedsession_teamedit_view), - url(r'^sessions/teamedit/(?P\d+)/(?P[\w\ ]+.*)$',views.plannedsession_teamedit_view), - - url(r'^sessions/create$',views.plannedsession_create_view), - url(r'^sessions/create/rower/(?P\d+)$', + url(r'^sessions/teamedit/(?P\d+)/$',views.plannedsession_teamedit_view), + url(r'^sessions/teamedit/(?P\d+)/user/(?P\d+)/$',views.plannedsession_teamedit_view), + url(r'^sessions/create/$',views.plannedsession_create_view), + url(r'^sessions/create/user/(?P\d+)$', views.plannedsession_create_view), - url( - r'^sessions/create/(?P[\w\ ]+.*)/rower/(?P\d+)$', - views.plannedsession_create_view), - url(r'^sessions/create/(?P[\w\ ]+.*)$', - views.plannedsession_create_view), - - url(r'^sessions/multiclone$',views.plannedsession_multiclone_view), - url(r'^sessions/multiclone/(?P[\w\ ]+.*)/rower/(?P\d+)/extra/(?P\d+)$', + url(r'^sessions/multiclone/$',views.plannedsession_multiclone_view), + url(r'^sessions/multiclone/user/(?P\d+)/$', views.plannedsession_multiclone_view), - url(r'^sessions/multiclone/rower/(?P\d+)$', - views.plannedsession_multiclone_view), - url( - r'^sessions/multiclone/(?P[\w\ ]+.*)/rower/(?P\d+)$', - views.plannedsession_multiclone_view), - url(r'^sessions/multiclone/(?P[\w\ ]+.*)$', - views.plannedsession_multiclone_view), - - url(r'^sessions/multicreate$',views.plannedsession_multicreate_view), - url(r'^sessions/multicreate/(?P[\w\ ]+.*)/rower/(?P\d+)/extra/(?P\d+)$', + url(r'^sessions/multicreate/$',views.plannedsession_multicreate_view), + url(r'^sessions/multicreate/user/(?P\d+)/extra/(?P\d+)/$', views.plannedsession_multicreate_view), - url(r'^sessions/multicreate/rower/(?P\d+)$', + url(r'^sessions/multicreate/user/(?P\d+)/$', views.plannedsession_multicreate_view), - url( - r'^sessions/multicreate/(?P[\w\ ]+.*)/rower/(?P\d+)$', - views.plannedsession_multicreate_view), - url(r'^sessions/multicreate/(?P[\w\ ]+.*)$', - views.plannedsession_multicreate_view), - - url(r'^sessions/(?P\d+)/edit/(?P[\w\ ]+.*)/rower/(?P\d+)$',views.plannedsession_edit_view), - url(r'^sessions/(?P\d+)/edit/(?P[\w\ ]+.*)$',views.plannedsession_edit_view), - url(r'^sessions/(?P\d+)/edit$',views.plannedsession_edit_view), - - url(r'^sessions/(?P\d+)/clone$',views.plannedsession_clone_view), - url(r'^sessions/(?P\d+)/clone/(?P[\w\ ]+.*)/rower/(?P\d+)$',views.plannedsession_clone_view), - url(r'^sessions/(?P\d+)/clone/(?P[\w\ ]+.*)$',views.plannedsession_clone_view), - + url(r'^sessions/(?P\d+)/edit/$',views.plannedsession_edit_view), + url(r'^sessions/(?P\d+)/edit/user/(?P\d+)/$',views.plannedsession_edit_view), + url(r'^sessions/(?P\d+)/clone/user/(?P\d+)/$',views.plannedsession_clone_view), + url(r'^sessions/(?P\d+)/clone/$',views.plannedsession_clone_view), url(r'^sessions/(?P\d+)$',views.plannedsession_view, name='plannedsession_view'), - url(r'^sessions/(?P\d+)/(?P[\w\ ]+.*)/rower/(?P\d+)$',views.plannedsession_view, + url(r'^sessions/(?P\d+)/user/(?P\d+)$',views.plannedsession_view, name='plannedsession_view'), - url(r'^sessions/(?P\d+)/rower/(?P\d+)$',views.plannedsession_view, - name='plannedsession_view'), - url(r'^sessions/(?P\d+)/deleteconfirm$',views.plannedsession_deleteconfirm_view), - url(r'^sessions/(?P\d+)/delete$',views.plannedsession_delete_view), - url(r'^sessions/manage/session/(?P\d+)$', + url(r'^sessions/(?P\d+)/deleteconfirm$',views.PlannedSessionDelete.as_view()), + url(r'^sessions/(?P\d+)/delete$',views.PlannedSessionDelete.as_view(), + name='plannedsession_delete_view'), + url(r'^sessions/manage/session/(?P\d+)/$', views.plannedsessions_manage_view), - url(r'^sessions/manage/rower/(?P\d+)/session/(?P\d+)$', + url(r'^sessions/manage/session/(?P\d+)/user/(?P\d+)/$', views.plannedsessions_manage_view), - url(r'^sessions/manage/(?P[\w\ ]+.*)/rower/(?P\d+)/session/(?P\d+)$', - views.plannedsessions_manage_view), - url(r'^sessions/manage/(?P[\w\ ]+.*)/session/(?P\d+)$', - views.plannedsessions_manage_view), - - url(r'^sessions/manage/?$', views.plannedsessions_manage_view), - url(r'^sessions/manage/rower/(?P\d+)$', + url(r'^sessions/manage/user/(?P\d+)/$', views.plannedsessions_manage_view), - url(r'^sessions/manage/(?P[\w\ ]+.*)/rower/(?P\d+)$', - views.plannedsessions_manage_view), - url(r'^sessions/manage/(?P[\w\ ]+.*)$', - views.plannedsessions_manage_view), - url(r'^sessions/coach$',views.plannedsessions_coach_view), - url(r'^sessions/coach/(?P[\w\ ]+.*)/team/(?P\d+)$', - views.plannedsessions_coach_view), - url(r'^sessions/coach/(?P[\w\ ]+.*)$', - views.plannedsessions_coach_view), + url(r'^sessions/coach/$',views.plannedsessions_coach_view), + url(r'^sessions/coach/user/\d+/$',views.plannedsessions_coach_view), url(r'^sessions/print/?$',views.plannedsessions_print_view), - url(r'^sessions/print/rower/(?P\d+)$',views.plannedsessions_print_view), - url(r'^sessions/print/(?P[\w\ ]+.*)/rower/(?P\d+)$',views.plannedsessions_print_view), - url(r'^sessions/print/(?P[\w\ ]+.*)$',views.plannedsessions_print_view), - url(r'^sessions/?$',views.plannedsessions_view), - url(r'^sessions/rower/(?P\d+)$',views.plannedsessions_view), - url(r'^sessions/(?P[\w\ ]+.*)/rower/(?P\d+)$',views.plannedsessions_view), - url(r'^sessions/(?P[\w\ ]+.*)$',views.plannedsessions_view), + url(r'^sessions/print/user/(?P\d+)$',views.plannedsessions_print_view), + url(r'^sessions/$',views.plannedsessions_view), + url(r'^sessions/user/(?P\d+)$',views.plannedsessions_view), url(r'^courses/(?P\d+)/edit$',views.course_edit_view, name='course_edit_view'), url(r'^courses/(?P\d+)/delete$',views.course_delete_view), @@ -557,6 +490,9 @@ urlpatterns = [ url(r'^courses/(?P\d+)/replace$',views.course_replace_view), url(r'^courses/(?P\d+)$',views.course_view), url(r'^courses/(?P\d+)/map$',views.course_map_view), + # URLS to be created + url(r'^help$',TemplateView.as_view(template_name='help.html'), name='help'), + ] if settings.DEBUG: diff --git a/rowers/views.py b/rowers/views.py index 4a06cb58..0a682435 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -43,7 +43,8 @@ from rowers.forms import ( LandingPageForm,PlannedSessionSelectForm,WorkoutSessionSelectForm, PlannedSessionTeamForm,PlannedSessionTeamMemberForm, VirtualRaceSelectForm,WorkoutRaceSelectForm,CourseSelectForm, - RaceResultFilterForm,PowerIntervalUpdateForm + RaceResultFilterForm,PowerIntervalUpdateForm,FlexAxesForm, + FlexOptionsForm ) from django.core.urlresolvers import reverse, reverse_lazy @@ -208,6 +209,8 @@ class JSONResponse(HttpResponse): def getrequestrower(request,rowerid=0,userid=0,notpermanent=False): + userid = int(userid) + rowerid = int(rowerid) if notpermanent == False: if rowerid == 0 and 'rowerid' in request.session: @@ -236,7 +239,7 @@ def getrequestrower(request,rowerid=0,userid=0,notpermanent=False): request.session['rowerid'] = r.id return r - + def getrower(user): try: @@ -1021,6 +1024,28 @@ def rower_register_view(request): "registration_form.html", {'form':form,}) +# Shows analysis page +def analysis_view(request): + r = getrequestrower(request) + return render(request, + "analysis.html", + { + 'active':'nav-analysis', + 'rower':r, + } + ) + +# Shows laboratory page +def laboratory_view(request): + r = getrequestrower(request) + return render(request, + "laboratory.html", + { + 'active':'nav-analysis', + 'rower':r, + } + ) + # Shows email form and sends it if submitted def sendmail(request): if request.method == 'POST': @@ -1277,22 +1302,24 @@ def workout_tcxemail_view(request,id=0): if r.emailbounced: message = "Please check your email address first. Email to this address bounced." messages.error(request, message) - return HttpResponseRedirect( - reverse(workout_export_view, - kwargs = { - 'id':str(w.id), - }) - ) + + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':int(id) + }) + + return HttpResponseRedirect(url) + tcxfile,tcxmessg = stravastuff.createstravaworkoutdata(w,dozip=False) if tcxfile == 0: message = "Something went wrong (TCX export) "+tcxmessg messages.error(request,message) - url = reverse(workout_export_view, - kwargs = { - 'id':str(w.id), - }) + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':int(id) + }) return HttpResponseRedirect(url) if tcxfile: res = myqueue(queuehigh,handle_sendemailtcx, @@ -1307,19 +1334,19 @@ def workout_tcxemail_view(request,id=0): messages.info(request,successmessage) - url = reverse(workout_export_view, - kwargs = { - 'id':str(w.id), - }) + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':int(id) + }) response = HttpResponseRedirect(url) else: message = "You are not allowed to export this workout" messages.error(request,message) - url = reverse(workout_export_view, - kwargs = { - 'id':str(w.id), - }) + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':int(id) + }) response = HttpResponseRedirect(url) return response @@ -1375,12 +1402,12 @@ def workout_gpxemail_view(request,id=0): if r.emailbounced: message = "Please check your email address first. Email to this address bounced." messages.error(request, message) - return HttpResponseRedirect( - reverse(workout_export_view, - kwargs = { - 'id':str(w.id), - }) - ) + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':int(id) + }) + + return HttpResponseRedirect(url) w = get_workout(id) @@ -1399,17 +1426,17 @@ def workout_gpxemail_view(request,id=0): successmessage = "The GPX file was sent to you per email" messages.info(request,successmessage) - url = reverse(workout_export_view, - kwargs = { - 'id':str(w.id), - }) + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':int(id) + }) response = HttpResponseRedirect(url) else: message = "You are not allowed to export this workout" messages.error(request,message) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1425,7 +1452,7 @@ def workouts_summaries_email_view(request): message = "Please check your email address first. Email to this address bounced." messages.error(request, message) return HttpResponseRedirect( - reverse(workout_export_view, + reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1468,7 +1495,7 @@ def workout_csvemail_view(request,id=0): message = "Please check your email address first. Email to this address bounced." messages.error(request, message) return HttpResponseRedirect( - reverse(workout_export_view, + reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1486,7 +1513,7 @@ def workout_csvemail_view(request,id=0): successmessage = "The CSV file was sent to you per email" messages.info(request,successmessage) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1495,7 +1522,7 @@ def workout_csvemail_view(request,id=0): else: message = "You are not allowed to export this workout" messages.error(request,message) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1577,7 +1604,7 @@ def workout_tp_upload_view(request,id=0): message = "You are not allowed to export this workout to TP" messages.error(request,message) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1621,7 +1648,7 @@ def workout_strava_upload_view(request,id=0): os.remove(tcxfile) except WindowsError: pass - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1635,7 +1662,8 @@ def workout_strava_upload_view(request,id=0): os.remove(tcxfile) except WindowsError: pass - url = "/rowers/workout/"+str(w.id)+"/edit" + url = reverse(workout_edit_view,kwargs={'id':w.id}) + messages.info(request,mes) except: @@ -1651,14 +1679,14 @@ def workout_strava_upload_view(request,id=0): messages.error(request,message) w.uploadedtostrava = -1 w.save() - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) response = HttpResponseRedirect(url) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), } @@ -1670,7 +1698,7 @@ def workout_strava_upload_view(request,id=0): w.uploadedtostrava = -1 w.save() os.remove(tcxfile) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1697,10 +1725,11 @@ def workout_c2_upload_view(request,id=0): messages.info(request,message) - url = reverse(workout_export_view, - kwargs = { - 'id':str(w.id), - }) + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':int(id) + }) + response = HttpResponseRedirect(url) @@ -1725,7 +1754,7 @@ def workout_runkeeper_upload_view(request,id=0): if not data: message = "Data error" messages.error(request,message) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1750,7 +1779,8 @@ def workout_runkeeper_upload_view(request,id=0): runkeeperid = runkeeperstuff.getidfromresponse(response) w.uploadedtorunkeeper = runkeeperid w.save() - url = "/rowers/workout/"+str(w.id)+"/export" + url = reverse(workout_edit_view, kwargs={'id':w.id}) + return HttpResponseRedirect(url) else: s = response @@ -1761,7 +1791,7 @@ def workout_runkeeper_upload_view(request,id=0): message = "You are not authorized to upload this workout" messages.error(request,message) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1781,13 +1811,13 @@ def workout_underarmour_upload_view(request,id=0): return HttpResponseRedirect("/rowers/me/underarmourauthorize/") # ready to upload. Hurray - + if (checkworkoutuser(request.user,w)): data = underarmourstuff.createunderarmourworkoutdata(w) if not data: message = "Data error" messages.error(request,message) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1814,7 +1844,8 @@ def workout_underarmour_upload_view(request,id=0): underarmourid = underarmourstuff.getidfromresponse(response) w.uploadedtounderarmour = underarmourid w.save() - url = "/rowers/workout/"+str(w.id)+"/export" + url = reverse(workout_edit_view,kwargs={'id':w.id}) + return HttpResponseRedirect(url) else: s = response @@ -1823,8 +1854,8 @@ def workout_underarmour_upload_view(request,id=0): else: message = "You are not authorized to upload this workout" messages.error(request,message) - - url = reverse(workout_export_view, + + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1851,7 +1882,7 @@ def workout_sporttracks_upload_view(request,id=0): if not data: message = "Data error" messages.error(request,message) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1879,7 +1910,8 @@ def workout_sporttracks_upload_view(request,id=0): w.save() message = "Upload to SportTracks was successful" messages.info(request,message) - url = "/rowers/workout/"+str(w.id)+"/export" + + url = reverse(workout_edit_view,kwargs={'id':w.id}) return HttpResponseRedirect(url) else: s = response @@ -1889,7 +1921,7 @@ def workout_sporttracks_upload_view(request,id=0): message = "You are not authorized to upload this workout" messages.error(request,message) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -2039,8 +2071,11 @@ def rower_c2_token_refresh(request): else: message = "Something went wrong (refreshing tokens). Please reauthorize:" messages.error(request,message) - - return imports_view(request) + + url = reverse(workouts_view) + + return HttpResponseRedirect(url) + # Underarmour token refresh. URL for manual refresh. Not visible to users @login_required() @@ -2064,7 +2099,11 @@ def rower_underarmour_token_refresh(request): successmessage = "Tokens refreshed. Good to go" messages.info(request,successmessage) - return imports_view(request) + + url = reverse(workouts_view) + + return HttpResponseRedirect(url) + # TrainingPeaks token refresh. URL for manual refresh. Not visible to users @@ -2088,7 +2127,12 @@ def rower_tp_token_refresh(request): successmessage = "Tokens refreshed. Good to go" messages.info(request,successmessage) - return imports_view(request) + + url = reverse(workouts_view) + + return HttpResponseRedirect(url) + + # SportTracks token refresh. URL for manual refresh. Not visible to users @@ -2112,7 +2156,11 @@ def rower_sporttracks_token_refresh(request): successmessage = "Tokens refreshed. Good to go" messages.info(request,successmessage) - return imports_view(request) + + url = reverse(workouts_view) + + return HttpResponseRedirect(url) + # Concept2 Callback @@ -2124,14 +2172,21 @@ def rower_process_callback(request): except MultiValueDictKeyError: message = "The resource owner or authorization server denied the request" messages.error(request,message) - return imports_view(request) + + url = reverse(workouts_view) + + return HttpResponseRedirect(url) access_token = res[0] if access_token == 0: message = res[1] message += ' Contact info@rowsandall.com if this behavior persists.' messages.error(request,message) - return imports_view(request) + + url = reverse(workouts_view) + + return HttpResponseRedirect(url) + expires_in = res[1] refresh_token = res[2] @@ -2146,7 +2201,11 @@ def rower_process_callback(request): successmessage = "Tokens stored. Good to go" messages.info(request,successmessage) - return imports_view(request) + + url = reverse(workouts_view) + + return HttpResponseRedirect(url) + # The imports page @login_required() @@ -2180,7 +2239,10 @@ def imports_view(request): def test_reverse_view(request): successmessage = "Tokens stored. Good to go" messages.info(request,successmessage) - return imports_view(request) + + url = reverse(workouts_view) + + return HttpResponseRedirect(url) # dummy @login_required() @@ -2199,7 +2261,11 @@ def rower_process_polarcallback(request): message = "access error" messages.error(request,message) - return imports_view(request) + url = reverse(workouts_view) + + return HttpResponseRedirect(url) + + access_token, expires_in, user_id = polarstuff.get_token(code) @@ -2214,7 +2280,11 @@ def rower_process_polarcallback(request): successmessage = "Tokens stored. Good to go" messages.info(request,successmessage) - return imports_view(request) + url = reverse(workouts_view) + + return HttpResponseRedirect(url) + + # Process Strava Callback @@ -2229,7 +2299,10 @@ def rower_process_stravacallback(request): message = "access error" messages.error(request,message) - return imports_view(request) + url = reverse(workouts_view) + + return HttpResponseRedirect(url) + res = stravastuff.get_token(code) @@ -2247,7 +2320,10 @@ def rower_process_stravacallback(request): else: message = "Something went wrong with the Strava authorization" messages.error(request,message) - return imports_view(request) + url = reverse(workouts_view) + + return HttpResponseRedirect(url) + # Process Runkeeper callback @login_required() @@ -2258,7 +2334,11 @@ def rower_process_runkeepercallback(request): if access_token == 0: messages.error(request,"Something went wrong importing the token") - return imports_view(request) + url = reverse(workouts_view) + + return HttpResponseRedirect(url) + + r = getrower(request.user) r.runkeepertoken = access_token @@ -2267,7 +2347,11 @@ def rower_process_runkeepercallback(request): successmessage = "Tokens stored. Good to go" messages.info(request,successmessage) - return imports_view(request) + url = reverse(workouts_view) + + return HttpResponseRedirect(url) + + # Process SportTracks callback @login_required() @@ -2291,7 +2375,11 @@ def rower_process_sporttrackscallback(request): successmessage = "Tokens stored. Good to go" messages.info(request,successmessage) - return imports_view(request) + url = reverse(workouts_view) + + return HttpResponseRedirect(url) + + # Process Underarmour callback @login_required() @@ -2314,7 +2402,11 @@ def rower_process_underarmourcallback(request): successmessage = "Tokens stored. Good to go" messages.info(request,successmessage) - return imports_view(request) + url = reverse(workouts_view) + + return HttpResponseRedirect(url) + + # Process TrainingPeaks callback @login_required() @@ -2336,7 +2428,11 @@ def rower_process_tpcallback(request): successmessage = "Tokens stored. Good to go" messages.info(request,successmessage) - return imports_view(request) + url = reverse(workouts_view) + + return HttpResponseRedirect(url) + + # Process Own API callback - for API testing purposes @login_required() @@ -2358,95 +2454,12 @@ def rower_process_testcallback(request): return HttpResponse(text) -# View around the Histogram of all strokes. Not implemented in UI -@login_required() -def histo_all(request,theuser=0, - startdate=timezone.now()-datetime.timedelta(days=10), - enddate=timezone.now(), - deltadays=-1, - startdatestring="", - enddatestring="", - options={ - 'includereststrokes':False, - 'workouttypes':[i[0] for i in types.workouttypes], - 'waterboattype':types.waterboattype - }): - - if 'options' in request.session: - options = request.session['options'] - workouttypes = options['workouttypes'] - includereststrokes = options['includereststrokes'] - waterboattype = options['waterboattype'] - workstrokesonly = not includereststrokes - - -# checktypes = ['water','rower','dynamic','slides','skierg', -# 'paddle','snow','coastal','other'] - - - if deltadays>0: - startdate = enddate-datetime.timedelta(days=int(deltadays)) - - if startdatestring != "": - startdate = iso8601.parse_date(startdatestring) - - if enddatestring != "": - enddate = iso8601.parse_date(enddatestring) - - - startdate = datetime.datetime.combine(startdate,datetime.time()) - enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) - - if enddate < startdate: - s = enddate - enddate = startdate - startdate = s - - promember=0 - if theuser == 0: - theuser = request.user.id - - if not request.user.is_anonymous(): - r = getrower(request.user) - result = request.user.is_authenticated() and ispromember(request.user) - if result: - promember=1 - - if not promember: - return HttpResponseRedirect("/rowers/about/") - - # get all indoor rows of past 12 months - ayearago = timezone.now()-datetime.timedelta(days=365) +def keyvalue_get_default(key,options,def_options): try: - r2 = getrower(theuser) - allergworkouts = Workout.objects.filter(user=r2, - workouttype__in=['rower','dynamic','slides'], - startdatetime__gte=ayearago) - except Rower.DoesNotExist: - allergworkouts = [] - r2=0 - - try: - u = User.objects.get(id=theuser) - except User.DoesNotExist: - u = '' - - if allergworkouts: - res = interactive_histoall(allergworkouts) - script = res[0] - div = res[1] - else: - script = '' - div = '

    No erg pieces uploaded yet.

    ' - - return render(request, 'histoall.html', - {'interactiveplot':script, - 'the_div':div, - 'id':theuser, - 'theuser':u, - 'teams':get_my_teams(request.user), - }) + return options[key] + except KeyError: + return def_options[key] # The Flex plot for a large selection of workouts @login_required() @@ -2455,7 +2468,7 @@ def cum_flex_data( options={ 'includereststrokes':False, 'rankingonly':False, - 'workouttypes':[i[0] for i in types.workouttypes], + 'modality':'all', 'waterboattype':types.waterboattype, 'theuser':0, 'xparam':'spm', @@ -2468,38 +2481,26 @@ def cum_flex_data( def_options = options + if 'options' in request.session: options = request.session['options'] - try: - workouttypes = options['workouttypes'] - rankingonly = options['rankingonly'] - includereststrokes = options['includereststrokes'] - waterboattype = options['waterboattype'] - workstrokesonly = not includereststrokes - theuser = options['theuser'] - xparam = options['xparam'] - yparam1 = options['yparam1'] - yparam2 = options['yparam2'] - startdatestring = options['startdatestring'] - enddatestring = options['enddatestring'] - deltadays = options['deltadays'] - except KeyError: - workouttypes = def_options['workouttypes'] - rankingonly = def_options['rankingonly'] - includereststrokes = def_options['includereststrokes'] - waterboattype = def_options['waterboattype'] - workstrokesonly = not includereststrokes - theuser = def_options['theuser'] - xparam = def_options['xparam'] - yparam1 = def_options['yparam1'] - yparam2 = def_options['yparam2'] - startdatestring = def_options['startdatestring'] - enddatestring = def_options['enddatestring'] - deltadays = def_options['deltadays'] - request.session['options'] = def_options - + modality = keyvalue_get_default('modality',options,def_options) + rankingonly = keyvalue_get_default('rankingonly',options,def_options) + includereststrokes = keyvalue_get_default('includereststrokes',options,def_options) + waterboattype = keyvalue_get_default('waterboattype',options,def_options) + workstrokesonly = not includereststrokes + theuser = keyvalue_get_default('theuser',options,def_options) + xparam = keyvalue_get_default('xparam',options,def_options) + yparam1 = keyvalue_get_default('yparam1',options,def_options) + yparam2 = keyvalue_get_default('yparam2',options,def_options) + startdatestring = keyvalue_get_default('startdatestring',options,def_options) + enddatestring = keyvalue_get_default('enddatestring',options,def_options) + if modality == 'all': + modalities = [m[0] for m in types.workouttypes] + else: + modalities = [modality] try: startdate = iso8601.parse_date(startdatestring) @@ -2511,9 +2512,6 @@ def cum_flex_data( except ParseError: enddate = timezone.now() - if deltadays>0: - startdate = enddate-datetime.timedelta(days=int(deltadays)) - if enddate < startdate: s = enddate @@ -2538,7 +2536,7 @@ def cum_flex_data( rankingpiece = [True,False] allworkouts = Workout.objects.filter(user=r2, - workouttype__in=workouttypes, + workouttype__in=modalities, boattype__in=waterboattype, startdatetime__gte=startdate, startdatetime__lte=enddate, @@ -2564,7 +2562,100 @@ def cum_flex_data( "script":script, "div":div, } + + return JSONResponse(data) + +# The Flex plot for a large selection of workouts +@login_required() +def histo_data( + request, + options={ + 'includereststrokes':False, + 'rankingonly':False, + 'modality':'all', + 'waterboattype':types.waterboattype, + 'theuser':0, + 'enddatestring':'', + 'startdatestring':'', + 'deltadays':-1, + }): + + def_options = options + + + if 'options' in request.session: + options = request.session['options'] + + modality = keyvalue_get_default('modality',options,def_options) + rankingonly = keyvalue_get_default('rankingonly',options,def_options) + includereststrokes = keyvalue_get_default('includereststrokes',options,def_options) + waterboattype = keyvalue_get_default('waterboattype',options,def_options) + workstrokesonly = not includereststrokes + theuser = keyvalue_get_default('theuser',options,def_options) + startdatestring = keyvalue_get_default('startdatestring',options,def_options) + enddatestring = keyvalue_get_default('enddatestring',options,def_options) + + if modality == 'all': + modalities = [m[0] for m in types.workouttypes] + else: + modalities = [modality] + + try: + startdate = iso8601.parse_date(startdatestring) + except ParseError: + startdate = timezone.now()-datetime.timedelta(days=7) + + try: + enddate = iso8601.parse_date(enddatestring) + except ParseError: + enddate = timezone.now() + + + if enddate < startdate: + s = enddate + enddate = startdate + startdate = s + + promember=0 + if theuser == 0: + theuser = request.user.id + + if not request.user.is_anonymous(): + r = getrower(request.user) + result = request.user.is_authenticated() and ispromember(request.user) + if result: + promember=1 + + r2 = getrower(theuser) + + if rankingonly: + rankingpiece = [True,] + else: + rankingpiece = [True,False] + allworkouts = Workout.objects.filter(user=r2, + workouttype__in=modalities, + boattype__in=waterboattype, + startdatetime__gte=startdate, + startdatetime__lte=enddate, + rankingpiece__in=rankingpiece) + + if allworkouts: + res = interactive_histoall(allworkouts) + script = res[0] + div = res[1] + else: + script = '' + div = '

    No pieces uploaded for this date range.

    ' + + scripta = script.split('\n')[2:-1] + script = ''.join(scripta) + + data = { + "script":script, + "div":div, + } + return JSONResponse(data) @@ -2587,18 +2678,31 @@ def cum_flex(request,theuser=0, 'rankingonly':False, }): - if 'options' in request.session: - options = request.session['options'] - try: - workouttypes = options['workouttypes'] - except KeyError: - workouttypes = [i[0] for i in types.workouttypes] + r = getrequestrower(request,userid=theuser) + theuser = r.user + + if 'waterboattype' in request.session: + waterboattype = request.session['waterboattype'] + else: + waterboattype = types.waterboattype + + + if 'rankingonly' in request.session: + rankingonly = request.session['rankingonly'] + else: + rankingonly = False + + if 'modalities' in request.session: + modalities = request.session['modalities'] + if len(modalities) > 1: + modality = 'all' + else: + modality = modalities[0] + else: + modalities = [m[0] for m in types.workouttypes] + modality = 'all' - try: - includereststrokes = options['includereststrokes'] - except KeyError: - includereststrokes = False try: rankingonly = options['rankingonly'] @@ -2606,15 +2710,15 @@ def cum_flex(request,theuser=0, rankingonly = False try: - waterboattype = options['waterboattype'] + includereststrokes = options['includereststrokes'] except KeyError: - waterboattype = types.waterboattype - + includereststrokes = False + + workstrokesonly = not includereststrokes - - if deltadays>0: - startdate = enddate-datetime.timedelta(days=int(deltadays)) + waterboattype = types.waterboattype + if startdatestring != "": startdate = iso8601.parse_date(startdatestring) @@ -2627,32 +2731,14 @@ def cum_flex(request,theuser=0, enddate = startdate startdate = s - promember=0 - if theuser == 0: - if 'rowerid' in request.session: - try: - r = Rower.objects.get(id=request.session['rowerid']) - theuser = r.user.id - except Rower.DoesNotExist: - theuser = request.user.id - else: - theuser = request.user.id - - - if not request.user.is_anonymous(): - r = getrower(request.user) - result = request.user.is_authenticated() and ispromember(request.user) - if result: - promember=1 - # get all indoor rows of in date range # process form - if request.method == 'POST' and "daterange" in request.POST: + if request.method == 'POST': form = DateRangeForm(request.POST) - deltaform = DeltaDaysForm(request.POST) - optionsform = StatsOptionsForm() + modalityform = TrendFlexModalForm(request.POST) + flexaxesform = FlexAxesForm(request,request.POST) if form.is_valid(): startdate = form.cleaned_data['startdate'] enddate = form.cleaned_data['enddate'] @@ -2660,131 +2746,112 @@ def cum_flex(request,theuser=0, s = enddate enddate = startdate startdate = s - elif request.method == 'POST' and "datedelta" in request.POST: - deltaform = DeltaDaysForm(request.POST) - optionsform = StatsOptionsForm() - if deltaform.is_valid(): - deltadays = deltaform.cleaned_data['deltadays'] - if deltadays != 0 and isinstance(deltadays, Number): - enddate = timezone.now() - startdate = enddate-datetime.timedelta(days=deltadays) - if startdate > enddate: - s = enddate - enddate = startdate - startdate = s - form = DateRangeForm(initial={ - 'startdate': startdate, - 'enddate': enddate, - }) + if modalityform.is_valid(): + modality = modalityform.cleaned_data['modality'] + waterboattype = modalityform.cleaned_data['waterboattype'] + rankingonly = modalityform.cleaned_data['rankingonly'] + if modality == 'all': + modalities = [m[0] for m in types.workouttypes] else: - form = DateRangeForm() - optionsform = StatsOptionsForm() - elif request.method == 'POST' and 'options' in request.POST: - optionsform = StatsOptionsForm(request.POST) - if optionsform.is_valid(): - includereststrokes = optionsform.cleaned_data['includereststrokes'] - rankingonly = optionsform.cleaned_data['rankingonly'] - workstrokesonly = not includereststrokes - waterboattype = optionsform.cleaned_data['waterboattype'] - workouttypes = [] - for type in types.checktypes: - if optionsform.cleaned_data[type]: - workouttypes.append(type) - - - options = { - 'includereststrokes':includereststrokes, - 'workouttypes':workouttypes, - 'waterboattype':waterboattype, - 'rankingonly':rankingonly, - } - request.session['options'] = options + modalities = [modality] + + if modality != 'water': + waterboattype = [b[0] for b in types.boattypes] + + + request.session['modalities'] = modalities + request.session['waterboattype'] = waterboattype + request.session['rankingonly'] = rankingonly form = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, }) - deltaform = DeltaDaysForm() - else: - form = DateRangeForm(initial={ - 'startdate': startdate, - 'enddate': enddate, - }) - deltaform = DeltaDaysForm() + if flexaxesform.is_valid(): + xparam = flexaxesform.cleaned_data['xaxis'] + yparam1 = flexaxesform.cleaned_data['yaxis1'] + yparam2 = flexaxesform.cleaned_data['yaxis2'] else: form = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, }) - deltaform = DeltaDaysForm() - optionsform = StatsOptionsForm() + includereststrokes = False + + workstrokesonly = not includereststrokes + modalityform = TrendFlexModalForm( + initial={ + 'modality':modality, + 'waterboattype':waterboattype, + 'rankingonly':rankingonly, + } + ) + initial = { + 'xaxis':xparam, + 'yaxis1':yparam1, + 'yaxis2':yparam2 + } + flexaxesform = FlexAxesForm(request,initial=initial) + + negtypes = [] + for b in types.boattypes: + if b[0] not in waterboattype: + negtypes.append(b[0]) + + script = '' div = get_call() js_resources = '' css_resources = '' - axchoicesbasic = {ax[0]:ax[1] for ax in axes if ax[4]=='basic'} - axchoicespro = {ax[0]:ax[1] for ax in axes if ax[4]=='pro'} - noylist = ["time","distance"] - axchoicesbasic.pop("cumdist") - - # set options form correctly - initial = {} - initial['includereststrokes'] = includereststrokes - initial['rankingonly'] = rankingonly - initial['waterboattype'] = waterboattype - - for wtype in types.checktypes: - if wtype in workouttypes: - initial[wtype] = True - else: - initial[wtype] = False - - - try: - u = User.objects.get(id=theuser) - except User.DoesNotExist: - u = '' - - optionsform = StatsOptionsForm(initial=initial) + - - options['includereststrokes'] = includereststrokes - options['rankingonly'] = includereststrokes - options['workouttypes'] =workouttypes - options['waterboattype'] =waterboattype - options['theuser'] =theuser - options['rankingonly'] = rankingonly - options['xparam'] =xparam - options['yparam1'] =yparam1 - options['yparam2'] =yparam2 - options['startdatestring'] = startdatestring - options['enddatestring'] =enddatestring - options['deltadays'] =deltadays - + + options = { + 'xparam': xparam, + 'yparam1': yparam1, + 'yparam2': yparam2, + 'modality': modality, + 'theuser': theuser.id, + 'waterboattype':waterboattype, + 'startdatestring':startdatestring, + 'enddatestring':enddatestring, + 'rankingonly':rankingonly, + 'includereststrokes':includereststrokes, + } request.session['options'] = options + promember=0 + mayedit=0 + if not request.user.is_anonymous(): + result = request.user.is_authenticated() and ispromember(request.user) + if result: + promember = 1 + + + request.session['options'] = options + + return render(request, 'cum_flex.html', {'interactiveplot':script, 'the_div':div, 'js_res': js_resources, 'css_res':css_resources, 'id':theuser, - 'theuser':u, + 'rower':r, + 'active':'nav-analysis', + 'theuser':theuser, 'startdate':startdate, 'enddate':enddate, 'form':form, - 'optionsform':optionsform, - 'deltaform':deltaform, + 'optionsform':modalityform, 'xparam':xparam, 'yparam1':yparam1, 'yparam2':yparam2, 'promember':promember, 'teams':get_my_teams(request.user), - 'axchoicesbasic':axchoicesbasic, - 'axchoicespro':axchoicespro, - 'noylist':noylist, + 'flexaxesform':flexaxesform, }) @@ -2793,6 +2860,7 @@ def fitnessmetric_view(request,id=0,mode='rower', startdate=timezone.now()-timezone.timedelta(days=365), enddate=timezone.now()): + therower = getrequestrower(request,userid=id) theuser = therower.user @@ -2818,7 +2886,8 @@ def fitnessmetric_view(request,id=0,mode='rower', return render(request,'fitnessmetric.html', { - 'therower':therower, + 'rower':therower, + 'active':'nav-analysis', 'chartscript':script, 'the_div':thediv, 'mode':mode, @@ -2854,10 +2923,32 @@ def workout_forcecurve_view(request,id=0,workstrokesonly=False): script,div,js_resources,css_resources = interactive_forcecurve([row], workstrokesonly=workstrokesonly) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(row.id) + }, + { + 'url':reverse(workout_forcecurve_view,kwargs={'id':id}), + 'name': 'Empower Force Curve' + } + + ] + + r = getrower(request.user) + return render(request, 'forcecurve_single.html', { 'the_script':script, + 'rower':r, + 'workout':row, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', 'the_div':div, 'js_res': js_resources, 'css_res':css_resources, @@ -2883,7 +2974,7 @@ def workout_test_task_view(request,id=0): # Show Stroke power histogram for a workout @login_required() def workout_histo_view(request,id=0): - row = get_workout(id) + w = get_workout(id) promember=0 mayedit=0 @@ -2892,19 +2983,40 @@ def workout_histo_view(request,id=0): result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 - if request.user == row.user.user: + if request.user == w.user.user: mayedit=1 if not promember: return HttpResponseRedirect("/rowers/about/") - res = interactive_histoall([row]) + res = interactive_histoall([w]) script = res[0] div = res[1] + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(w.id) + }, + { + 'url':reverse(workout_histo_view,kwargs={'id':id}), + 'name': 'Histogram' + } + + ] + + return render(request, 'histo_single.html', {'interactiveplot':script, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', + 'workout':w, + 'rower':r, 'the_div':div, 'id':int(id), 'mayedit':mayedit, @@ -2914,7 +3026,7 @@ def workout_histo_view(request,id=0): # Histogram for a date/time range -@login_required() +@user_passes_test(ispromember,login_url="/",redirect_field_name=None) def histo(request,theuser=0, startdate=timezone.now()-datetime.timedelta(days=365), enddate=timezone.now(), @@ -2924,29 +3036,50 @@ def histo(request,theuser=0, options={ 'includereststrokes':False, 'workouttypes':[i[0] for i in types.workouttypes], - 'waterboattype':types.waterboattype + 'waterboattype':types.waterboattype, + 'rankingonly': False, }): - if 'options' in request.session: - options = request.session['options'] + r = getrequestrower(request,userid=theuser) + theuser = r.user + + if 'waterboattype' in request.session: + waterboattype = request.session['waterboattype'] + else: + waterboattype = types.waterboattype + + + if 'rankingonly' in request.session: + rankingonly = request.session['rankingonly'] + else: + rankingonly = False + + if 'modalities' in request.session: + modalities = request.session['modalities'] + if len(modalities) > 1: + modality = 'all' + else: + modality = modalities[0] + else: + modalities = [m[0] for m in types.workouttypes] + modality = 'all' + try: - workouttypes = options['workouttypes'] - includereststrokes = options['includereststrokes'] - waterboattype = options['waterboattype'] + rankingonly = options['rankingonly'] except KeyError: - workouttypes = [i[0] for i in types.workouttypes] - waterboattype = types.waterboattype - includereststrokes = False + rankingonly = False + try: + includereststrokes = options['includereststrokes'] + except KeyError: + includereststrokes = False + + workstrokesonly = not includereststrokes + waterboattype = types.waterboattype -# checktypes = ['water','rower','dynamic','slides','skierg', -# 'paddle','snow','coastal','other'] - - if deltadays>0: - startdate = enddate-datetime.timedelta(days=int(deltadays)) if startdatestring != "": startdate = iso8601.parse_date(startdatestring) @@ -2959,27 +3092,13 @@ def histo(request,theuser=0, enddate = startdate startdate = s - promember=0 - r = getrequestrower(request,userid=theuser) - theuser = r.user.id - - - if not request.user.is_anonymous(): - r = getrower(request.user) - result = request.user.is_authenticated() and ispromember(request.user) - if result: - promember=1 - - if not promember: - return HttpResponseRedirect("/rowers/promembership/") # get all indoor rows of in date range # process form - if request.method == 'POST' and "daterange" in request.POST: + if request.method == 'POST': form = DateRangeForm(request.POST) - deltaform = DeltaDaysForm(request.POST) - optionsform = StatsOptionsForm() + modalityform = TrendFlexModalForm(request.POST) if form.is_valid(): startdate = form.cleaned_data['startdate'] enddate = form.cleaned_data['enddate'] @@ -2987,118 +3106,90 @@ def histo(request,theuser=0, s = enddate enddate = startdate startdate = s - elif request.method == 'POST' and "datedelta" in request.POST: - deltaform = DeltaDaysForm(request.POST) - optionsform = StatsOptionsForm() - if deltaform.is_valid(): - deltadays = deltaform.cleaned_data['deltadays'] - if deltadays != 0 and deltadays != None: - enddate = timezone.now() - startdate = enddate-datetime.timedelta(days=deltadays) - if startdate > enddate: - s = enddate - enddate = startdate - startdate = s - form = DateRangeForm(initial={ - 'startdate': startdate, - 'enddate': enddate, - }) + if modalityform.is_valid(): + modality = modalityform.cleaned_data['modality'] + waterboattype = modalityform.cleaned_data['waterboattype'] + rankingonly = modalityform.cleaned_data['rankingonly'] + if modality == 'all': + modalities = [m[0] for m in types.workouttypes] else: - form = DateRangeForm() - optionsform = StatsOptionsForm() - elif request.method == 'POST' and 'options' in request.POST: - optionsform = StatsOptionsForm(request.POST) - if optionsform.is_valid(): - includereststrokes = optionsform.cleaned_data['includereststrokes'] - workstrokesonly = not includereststrokes - waterboattype = optionsform.cleaned_data['waterboattype'] - workouttypes = [] - for type in types.checktypes: - if optionsform.cleaned_data[type]: - workouttypes.append(type) - - - options = { - 'includereststrokes':includereststrokes, - 'workouttypes':workouttypes, - 'waterboattype':waterboattype, - } - request.session['options'] = options - form = DateRangeForm(initial={ - 'startdate': startdate, - 'enddate': enddate, - }) - deltaform = DeltaDaysForm() - else: - form = DateRangeForm(initial={ - 'startdate': startdate, - 'enddate': enddate, - }) - deltaform = DeltaDaysForm() + modalities = [modality] + if modality != 'water': + waterboattype = [b[0] for b in types.boattypes] + + + request.session['modalities'] = modalities + request.session['waterboattype'] = waterboattype + request.session['rankingonly'] = rankingonly + form = DateRangeForm(initial={ + 'startdate': startdate, + 'enddate': enddate, + }) else: form = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, }) - deltaform = DeltaDaysForm() - optionsform = StatsOptionsForm() - try: - r2 = getrower(theuser) - allergworkouts = Workout.objects.filter(user=r2, - workouttype__in=workouttypes, - boattype__in=waterboattype, - startdatetime__gte=startdate, - startdatetime__lte=enddate) - - except Rower.DoesNotExist: - allergworkouts = [] - r2=0 + includereststrokes = False - try: - u = User.objects.get(id=theuser) - except User.DoesNotExist: - u = '' + workstrokesonly = not includereststrokes + modalityform = TrendFlexModalForm( + initial={ + 'modality':modality, + 'waterboattype':waterboattype, + 'rankingonly':rankingonly, + } + ) - if allergworkouts: - res = interactive_histoall(allergworkouts) - script = res[0] - div = res[1] - else: - script = '' - typesstring = ', '.join('{}'.format(item) for i,item in enumerate(workouttypes)) - if 'water' in workouttypes: - boatsstring = 'water ('+', '.join('{}'.format(item) for i,item in enumerate(waterboattype))+')' - typesstring = typesstring.replace('water',boatsstring) - div = '

    No pieces found for '+typesstring+' for this date range.

    ' - - # set options form correctly - initial = {} - initial['includereststrokes'] = includereststrokes - - initial['waterboattype'] = waterboattype - - for wtype in types.checktypes: - if wtype in workouttypes: - initial[wtype] = True - else: - initial[wtype] = False + negtypes = [] + for b in types.boattypes: + if b[0] not in waterboattype: + negtypes.append(b[0]) - optionsform = StatsOptionsForm(initial=initial) + + script = '' + div = get_call() + js_resources = '' + css_resources = '' + + + + options = { + 'modality': modality, + 'theuser': theuser.id, + 'waterboattype':waterboattype, + 'startdatestring':startdatestring, + 'enddatestring':enddatestring, + 'rankingonly':rankingonly, + 'includereststrokes':includereststrokes, + } + + request.session['options'] = options + + promember=0 + mayedit=0 + if not request.user.is_anonymous(): + result = request.user.is_authenticated() and ispromember(request.user) + if result: + promember = 1 + + request.session['options'] = options return render(request, 'histo.html', {'interactiveplot':script, 'the_div':div, 'id':theuser, - 'theuser':u, + 'active':'nav-analysis', + 'theuser':theuser, + 'rower':r, 'startdate':startdate, 'enddate':enddate, 'form':form, - 'optionsform':optionsform, - 'deltaform':deltaform, + 'optionsform':modalityform, 'teams':get_my_teams(request.user), }) @@ -3188,6 +3279,7 @@ def addmanual_view(request): return render(request,'manualadd.html', {'form':form, + 'active':'nav-workouts', }) @login_required() @@ -3531,6 +3623,8 @@ def rankings_view(request,theuser=0, 'cpredictions':cpredictions, 'nrdata':len(thedistances), 'form':form, + 'rower':r, + 'active':'nav-analysis', 'dateform':dateform, 'deltaform':deltaform, 'worldclasspower':worldclasspower, @@ -3974,6 +4068,8 @@ def rankings_view2(request,theuser=0, 'deltaform':deltaform, 'id': theuser, 'theuser':uu, + 'rower':r, + 'active':'nav-analysis', 'age':age, 'sex':r.sex, 'recalc':recalc, @@ -4280,6 +4376,8 @@ def otwrankings_view(request,theuser=0, 'interactiveplot':script, 'the_div':div, 'cpredictions':cpredictions, + 'rower':r, + 'active':'nav-analysis', 'avgpower':avgpower, 'form':form, 'dateform':dateform, @@ -4789,6 +4887,8 @@ def oterankings_view(request,theuser=0, {'rankingworkouts':theworkouts, 'interactiveplot':script, 'the_div':div, + 'rower':r, + 'active':'nav-analysis', 'cpredictions':cpredictions, 'avgpower':avgpower, 'form':form, @@ -4955,13 +5055,6 @@ def workouts_join_select(request, except Rower.DoesNotExist: raise Http404("Rower doesn't exist") -# if 'startdate' in request.session: -# startdate = iso8601.parse_date(request.session['startdate']) - - -# if 'enddate' in request.session: -# enddate = iso8601.parse_date(request.session['enddate']) - if 'waterboattype' in request.session: waterboattype = request.session['waterboattype'] @@ -5091,6 +5184,7 @@ def workouts_join_select(request, 'dateform':dateform, 'startdate':startdate, 'enddate':enddate, + 'active':'nav-workouts', 'team':theteam, 'form':form, 'joinparamform':joinparamform, @@ -5114,13 +5208,6 @@ def team_comparison_select(request, except Rower.DoesNotExist: raise Http404("Rower doesn't exist") -# if 'startdate' in request.session: -# startdate = iso8601.parse_date(request.session['startdate']) - - -# if 'enddate' in request.session: -# enddate = iso8601.parse_date(request.session['enddate']) - if 'waterboattype' in request.session: waterboattype = request.session['waterboattype'] @@ -5142,21 +5229,16 @@ def team_comparison_select(request, modalities = [m[0] for m in types.workouttypes] modality = 'all' - if request.method == 'POST' and 'daterange' in request.POST: + if request.method == 'POST': dateform = DateRangeForm(request.POST) if dateform.is_valid(): startdate = dateform.cleaned_data['startdate'] enddate = dateform.cleaned_data['enddate'] startdatestring = startdate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d') - else: - dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) + request.session['startdate'] = startdatestring + request.session['enddate'] = enddatestring - - if request.method == 'POST' and 'modality' in request.POST: modalityform = TrendFlexModalForm(request.POST) if modalityform.is_valid(): modality = modalityform.cleaned_data['modality'] @@ -5177,6 +5259,19 @@ def team_comparison_select(request, request.session['modalities'] = modalities request.session['waterboattype'] = waterboattype + else: + dateform = DateRangeForm(initial={ + 'startdate':startdate, + 'enddate':enddate, + }) + modalityform = TrendFlexModalForm(initial={ + 'modality':modality, + 'waterboattype':waterboattype, + 'rankingonly':rankingonly, + }) + + + negtypes = [] for b in types.boattypes: @@ -5247,16 +5342,23 @@ def team_comparison_select(request, theid = 0 chartform = ChartParamChoiceForm(initial={'teamid':0}) - modalityform = TrendFlexModalForm(initial={ - 'modality':modality, - 'waterboattype':waterboattype, - 'rankingonly':rankingonly, - }) - messages.info(request,successmessage) messages.error(request,message) + r = getrower(request.user) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(team_comparison_select,kwargs={'teamid':teamid}), + 'name': 'Compare Select' + }, + + ] + return render(request, 'team_compare_select.html', {'workouts': workouts, 'dateform':dateform, @@ -5264,6 +5366,9 @@ def team_comparison_select(request, 'enddate':enddate, 'team':theteam, 'form':form, + 'rower':r, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', 'chartform':chartform, 'modalityform':modalityform, 'teams':get_my_teams(request.user), @@ -5308,9 +5413,27 @@ def multi_compare_view(request): 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, @@ -5340,9 +5463,29 @@ def multi_compare_view(request): script = res[0] div = res[1] + 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, @@ -5387,17 +5530,14 @@ def user_multiflex_select(request, except: ploterrorbars = False - - -# if 'startdate' in request.session: -# startdate = iso8601.parse_date(request.session['startdate']) + if 'startdate' in request.session: + startdate = iso8601.parse_date(request.session['startdate']) -# if 'enddate' in request.session: -# enddate = iso8601.parse_date(request.session['enddate']) + if 'enddate' in request.session: + enddate = iso8601.parse_date(request.session['enddate']) - if 'waterboattype' in request.session: waterboattype = request.session['waterboattype'] else: waterboattype = types.waterboattype @@ -5418,7 +5558,7 @@ def user_multiflex_select(request, modalities = [m[0] for m in types.workouttypes] modality = 'all' - if request.method == 'POST' and 'daterange' in request.POST: + if request.method == 'POST': dateform = DateRangeForm(request.POST) if dateform.is_valid(): startdate = dateform.cleaned_data['startdate'] @@ -5427,13 +5567,6 @@ def user_multiflex_select(request, enddatestring = enddate.strftime('%Y-%m-%d') request.session['startdate'] = startdatestring request.session['enddate'] = enddatestring - else: - dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) - - if request.method == 'POST' and 'modality' in request.POST: modalityform = TrendFlexModalForm(request.POST) if modalityform.is_valid(): modality = modalityform.cleaned_data['modality'] @@ -5451,6 +5584,12 @@ def user_multiflex_select(request, request.session['modalities'] = modalities request.session['waterboattype'] = waterboattype request.session['rankingonly'] = rankingonly + else: + dateform = DateRangeForm(initial={ + 'startdate':startdate, + 'enddate':enddate, + }) + startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) @@ -5527,12 +5666,29 @@ def user_multiflex_select(request, request.session['modalities'] = modalities + breadcrumbs = [ + { + 'url':'/rowers/analysis', + 'name':'Analysis' + }, + { + 'url':reverse(user_multiflex_select,kwargs={'userid':userid}), + 'name': 'Compare Select' + }, + { + 'url':reverse(multi_compare_view), + 'name': 'Comparison Chart' + } + ] + return render(request, 'user_multiflex_select.html', {'workouts': workouts, 'dateform':dateform, + 'breadcrumbs':breadcrumbs, 'startdate':startdate, 'enddate':enddate, 'theuser':user, + 'rower':r, 'form':form, 'chartform':chartform, 'modalityform':modalityform, @@ -5795,6 +5951,13 @@ def multiflex_view(request,userid=0, except KeyError: palette = 'monochrome_blue' + if 'startdate' in request.session: + startdate = iso8601.parse_date(request.session['startdate']) + + + if 'enddate' in request.session: + enddate = iso8601.parse_date(request.session['enddate']) + workstrokesonly = not includereststrokes if userid==0: @@ -5895,11 +6058,32 @@ def multiflex_view(request,userid=0, request.session['options'] = options + + r = getrequestrower(request,userid=userid) + + breadcrumbs = [ + { + 'url':'/rowers/analysis', + 'name':'Analysis' + }, + { + 'url':reverse(user_multiflex_select,kwargs={'userid':userid}), + 'name': 'Trend Flex Select' + }, + { + 'url':reverse(multiflex_view), + 'name': 'Trend Flex Chart' + } + ] return render(request,'multiflex.html', {'interactiveplot':'', + 'active':'nav-analysis', + 'rower':r, + 'breadcrumbs':breadcrumbs, 'the_div':div, + 'active':'nav-analysis', 'chartform':chartform, 'userid':userid, 'teams':get_my_teams(request.user), @@ -5925,6 +6109,7 @@ def user_boxplot_select(request, r = getrequestrower(request,userid=userid) user = r.user + userid = user.id if 'options' in request.session: options = request.session['options'] @@ -5945,17 +6130,17 @@ def user_boxplot_select(request, except KeyError: includereststrokes = False + if 'startdate' in request.session: + startdate = iso8601.parse_date(request.session['startdate']) + + + if 'enddate' in request.session: + enddate = iso8601.parse_date(request.session['enddate']) workstrokesonly = not includereststrokes waterboattype = types.waterboattype -# if 'startdate' in request.session: -# startdate = iso8601.parse_date(request.session['startdate']) - - -# if 'enddate' in request.session: -# enddate = iso8601.parse_date(request.session['enddate']) if startdatestring != "": startdate = iso8601.parse_date(startdatestring) @@ -5968,14 +6153,8 @@ def user_boxplot_select(request, enddate = startdate startdate = s -# if 'startdate' in request.session: -# startdate = iso8601.parse_date(request.session['startdate']) - -# if 'enddate' in request.session: -# enddate = iso8601.parse_date(request.session['enddate']) - - if request.method == 'POST' and 'daterange' in request.POST: + if request.method == 'POST': dateform = DateRangeForm(request.POST) if dateform.is_valid(): startdate = dateform.cleaned_data['startdate'] @@ -5984,30 +6163,48 @@ def user_boxplot_select(request, enddatestring = enddate.strftime('%Y-%m-%d') request.session['startdate'] = startdatestring request.session['enddate'] = enddatestring + optionsform = TrendFlexModalForm(request.POST) + if optionsform.is_valid(): + modality = optionsform.cleaned_data['modality'] + waterboattype = optionsform.cleaned_data['waterboattype'] + if modality == 'all': + modalities = [m[0] for m in types.workouttypes] + else: + modalities = [modality] + if modality != 'water': + waterboattype = [b[0] for b in types.boattypes] + + + if 'rankingonly' in optionsform.cleaned_data: + rankingonly = optionsform.cleaned_data['rankingonly'] + else: + rankingonly = False + + request.session['modalities'] = modalities + request.session['waterboattype'] = waterboattype else: dateform = DateRangeForm(initial={ 'startdate':startdate, 'enddate':enddate, }) - if request.method == 'POST' and 'optionsform' in request.POST: - optionsform = StatsOptionsForm(request.POST) - if optionsform.is_valid(): - workouttypes = [] - waterboattype = optionsform.cleaned_data['waterboattype'] - rankingonly = optionsform.cleaned_data['rankingonly'] - for type in types.checktypes: - if optionsform.cleaned_data[type]: - workouttypes.append(type) - - options = { - 'workouttypes':workouttypes, - 'waterboattype':waterboattype, - 'rankingonly':rankingonly, - } + if 'modalities' in request.session: + modalities = request.session['modalities'] + if len(modalities) > 1: + modality = 'all' + else: + modality = modalities[0] + else: + modalities = [m[0] for m in types.workouttypes] + modality = 'all' - - request.session['options'] = options + + + + negtypes = [] + for b in types.boattypes: + if b[0] not in waterboattype: + negtypes.append(b[0]) startdate = datetime.datetime.combine(startdate,datetime.time()) @@ -6029,42 +6226,19 @@ def user_boxplot_select(request, if b[0] not in waterboattype: negtypes.append(b[0]) - if rankingonly: - rankingpiece = [True] - else: - rankingpiece = [True,False] - - waterinclude = False - for ttype in types.otwtypes: - if ttype in workouttypes: - waterinclude = True - - if waterinclude: - workoutsw = Workout.objects.filter(user=r, - startdatetime__gte=startdate, - startdatetime__lte=enddate, - workouttype__in=types.otwtypes, - rankingpiece__in=rankingpiece, - ).exclude(boattype__in=negtypes) - workouttypes = [w for w in workouttypes if w not in types.otwtypes] - - workoutse = Workout.objects.filter(user=r, + workouts = Workout.objects.filter(user=r, startdatetime__gte=startdate, startdatetime__lte=enddate, - workouttype__in=workouttypes, - rankingpiece__in=rankingpiece) + workouttype__in=modalities, + ).order_by( + "-date", "-starttime" + ).exclude(boattype__in=negtypes) + # workouttypes = [w for w in workouttypes if w not in types.otwtypes] - if waterinclude: - workouts = workoutse | workoutsw - workouts = workouts.distinct().order_by("-date", "-starttime") - else: - workouts = workoutse.order_by("-date","-starttime") + if rankingonly: + workouts = [w for w in workouts if w.rankingpiece] - if waterinclude: - for ttype in types.otwtypes: - workouttypes.append(ttype) - query = request.GET.get('q') if query: query_list = query.split() @@ -6079,19 +6253,11 @@ def user_boxplot_select(request, form.fields["workouts"].queryset = workouts chartform = BoxPlotChoiceForm() - # set options form correctly - initial = {} - initial['waterboattype'] = waterboattype - initial['rankingonly'] = rankingonly - - for wtype in types.checktypes: - if wtype in workouttypes: - initial[wtype] = True - else: - initial[wtype] = False - - - optionsform = StatsOptionsForm(initial=initial) + optionsform = TrendFlexModalForm(initial={ + 'modality':modality, + 'waterboattype':waterboattype, + 'rankingonly':rankingonly, + }) messages.info(request,successmessage) messages.error(request,message) @@ -6101,13 +6267,27 @@ def user_boxplot_select(request, request.session['startdate'] = startdatestring request.session['enddate'] = enddatestring + + breadcrumbs = [ + { + 'url':'/rowers/analysis', + 'name':'Analysis' + }, + { + 'url':reverse(user_boxplot_select,kwargs={'userid':userid}), + 'name': 'BoxPlot Select' + }, + ] return render(request, 'user_boxplot_select.html', {'workouts': workouts, 'dateform':dateform, 'startdate':startdate, 'enddate':enddate, + 'rower':r, + 'breadcrumbs':breadcrumbs, 'theuser':user, 'form':form, + 'active':'nav-analysis', 'chartform':chartform, 'optionsform':optionsform, 'teams':get_my_teams(request.user), @@ -6276,7 +6456,7 @@ def boxplot_view(request,userid=0, else: return HttpResponse("invalid form") else: - url = reverse(user_boxplot_select) + url = reverse(user_boxplot_select,kwargs={'userid':userid}) return HttpResponseRedirect(url) div = get_call() @@ -6292,9 +6472,28 @@ def boxplot_view(request,userid=0, request.session['options'] = options + r = getrequestrower(request,userid=userid) + breadcrumbs = [ + { + 'url':'/rowers/Analysis', + 'name':'Analysis' + }, + { + 'url':reverse(user_boxplot_select,kwargs={'userid':userid}), + 'name': 'BoxPlot Select' + }, + { + 'url':reverse(boxplot_view,kwargs={'userid':userid}), + 'name': 'BoxPlot Select' + }, + ] + return render(request,'boxplot.html', {'interactiveplot':'', 'the_div':div, + 'rower':r, + 'breadcrumbs':breadcrumbs, + 'active':'nav-analysis', 'chartform':chartform, 'userid':userid, 'teams':get_my_teams(request.user), @@ -6323,6 +6522,7 @@ def courses_view(request): return render(request,'list_courses.html', {'courses':courses, + 'active':'nav-racing', 'rower':r, }) @@ -6466,10 +6666,19 @@ def workouts_view(request,message='',successmessage='', messages.info(request,successmessage) messages.error(request,message) - + + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + ] + return render(request, 'list_workouts.html', {'workouts': workouts, + 'active': 'nav-workouts', 'rower':r, + 'breadcrumbs':breadcrumbs, 'dateform':dateform, 'startdate':startdate, 'enddate':enddate, @@ -6491,74 +6700,76 @@ def workout_comparison_list(request,id=0,message='',successmessage='', try: r = getrower(request.user) - u = User.objects.get(id=r.user.id) - if request.method == 'POST': - dateform = DateRangeForm(request.POST) - if dateform.is_valid(): - startdate = dateform.cleaned_data['startdate'] - enddate = dateform.cleaned_data['enddate'] - else: - dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) - - if startdatestring: - startdate = iso8601.parse_date(startdatestring) - if enddatestring: - enddate = iso8601.parse_date(enddatestring) - - startdate = datetime.datetime.combine(startdate,datetime.time()) - enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) - #enddate = enddate+datetime.timedelta(days=1) - - if enddate < startdate: - s = enddate - enddate = startdate - startdate = s - - workouts = Workout.objects.filter(user=r, - startdatetime__gte=startdate, - startdatetime__lte=enddate).order_by("-date", "-starttime").exclude(id=id) - - query = request.GET.get('q') - if query: - query_list = query.split() - workouts = workouts.filter( - reduce(operator.and_, - (Q(name__icontains=q) for q in query_list)) | - reduce(operator.and_, - (Q(notes__icontains=q) for q in query_list)) - ) - - paginator = Paginator(workouts,15) # show 25 workouts per page - page = request.GET.get('page') - - try: - workouts = paginator.page(page) - except PageNotAnInteger: - workouts = paginator.page(1) - except EmptyPage: - workouts = paginator.page(paginator.num_pages) - - row = get_workout(id) - - messages.error(request,message) - messages.info(request,successmessage) - - return render(request, 'comparison_list.html', - {'id':int(id), - 'workout':row, - 'workouts': workouts, - 'last_name':u.last_name, - 'first_name':u.first_name, - 'dateform':dateform, - 'startdate':startdate, - 'enddate':enddate, - 'teams':get_my_teams(request.user), - }) except Rower.DoesNotExist: raise Http404("User has no rower instance") + u = User.objects.get(id=r.user.id) + + if request.method == 'POST': + dateform = DateRangeForm(request.POST) + if dateform.is_valid(): + startdate = dateform.cleaned_data['startdate'] + enddate = dateform.cleaned_data['enddate'] + else: + dateform = DateRangeForm(initial={ + 'startdate':startdate, + 'enddate':enddate, + }) + + if startdatestring: + startdate = iso8601.parse_date(startdatestring) + if enddatestring: + enddate = iso8601.parse_date(enddatestring) + + startdate = datetime.datetime.combine(startdate,datetime.time()) + enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) + #enddate = enddate+datetime.timedelta(days=1) + + if enddate < startdate: + s = enddate + enddate = startdate + startdate = s + + workouts = Workout.objects.filter(user=r, + startdatetime__gte=startdate, + startdatetime__lte=enddate).order_by("-date", "-starttime").exclude(id=id) + + query = request.GET.get('q') + if query: + query_list = query.split() + workouts = workouts.filter( + reduce(operator.and_, + (Q(name__icontains=q) for q in query_list)) | + reduce(operator.and_, + (Q(notes__icontains=q) for q in query_list)) + ) + + paginator = Paginator(workouts,15) # show 25 workouts per page + page = request.GET.get('page') + + try: + workouts = paginator.page(page) + except PageNotAnInteger: + workouts = paginator.page(1) + except EmptyPage: + workouts = paginator.page(paginator.num_pages) + + row = get_workout(id) + + messages.error(request,message) + messages.info(request,successmessage) + + return render(request, 'comparison_list.html', + {'id':int(id), + 'workout':row, + 'workouts': workouts, + 'last_name':u.last_name, + 'first_name':u.first_name, + 'dateform':dateform, + 'startdate':startdate, + 'enddate':enddate, + 'teams':get_my_teams(request.user), + }) + # List of workouts to compare a selected workout to @user_passes_test(ispromember,login_url="/",redirect_field_name=None) @@ -6569,167 +6780,188 @@ def workout_fusion_list(request,id=0,message='',successmessage='', try: r = getrower(request.user) - u = User.objects.get(id=r.user.id) - if request.method == 'POST': - dateform = DateRangeForm(request.POST) - if dateform.is_valid(): - startdate = dateform.cleaned_data['startdate'] - enddate = dateform.cleaned_data['enddate'] - else: - dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) - - if startdatestring: - startdate = iso8601.parse_date(startdatestring) - if enddatestring: - enddate = iso8601.parse_date(enddatestring) - - startdate = datetime.datetime.combine(startdate,datetime.time()) - enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) - #enddate = enddate+datetime.timedelta(days=1) - - if enddate < startdate: - s = enddate - enddate = startdate - startdate = s - - workouts = Workout.objects.filter(user=r, - startdatetime__gte=startdate, - startdatetime__lte=enddate).order_by("-date", "-starttime").exclude(id=id) - - query = request.GET.get('q') - if query: - query_list = query.split() - workouts = workouts.filter( - reduce(operator.and_, - (Q(name__icontains=q) for q in query_list)) | - reduce(operator.and_, - (Q(notes__icontains=q) for q in query_list)) - ) - - paginator = Paginator(workouts,15) # show 25 workouts per page - page = request.GET.get('page') - - try: - workouts = paginator.page(page) - except PageNotAnInteger: - workouts = paginator.page(1) - except EmptyPage: - workouts = paginator.page(paginator.num_pages) - row = get_workout(id) - - messages.info(request,successmessage) - messages.error(request,message) - - return render(request, 'fusion_list.html', - {'id':int(id), - 'workout':row, - 'workouts': workouts, - 'last_name':u.last_name, - 'first_name':u.first_name, - 'dateform':dateform, - 'startdate':startdate, - 'enddate':enddate, - 'teams':get_my_teams(request.user), - }) except Rower.DoesNotExist: raise Http404("User has no rower instance") + + u = User.objects.get(id=r.user.id) + if request.method == 'POST': + dateform = DateRangeForm(request.POST) + if dateform.is_valid(): + startdate = dateform.cleaned_data['startdate'] + enddate = dateform.cleaned_data['enddate'] + else: + dateform = DateRangeForm(initial={ + 'startdate':startdate, + 'enddate':enddate, + }) + + if startdatestring: + startdate = iso8601.parse_date(startdatestring) + if enddatestring: + enddate = iso8601.parse_date(enddatestring) + + startdate = datetime.datetime.combine(startdate,datetime.time()) + enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) + #enddate = enddate+datetime.timedelta(days=1) + + if enddate < startdate: + s = enddate + enddate = startdate + startdate = s + + workouts = Workout.objects.filter(user=r, + startdatetime__gte=startdate, + startdatetime__lte=enddate).order_by("-date", "-starttime").exclude(id=id) + + query = request.GET.get('q') + if query: + query_list = query.split() + workouts = workouts.filter( + reduce(operator.and_, + (Q(name__icontains=q) for q in query_list)) | + reduce(operator.and_, + (Q(notes__icontains=q) for q in query_list)) + ) + + paginator = Paginator(workouts,15) # show 25 workouts per page + page = request.GET.get('page') + + try: + workouts = paginator.page(page) + except PageNotAnInteger: + workouts = paginator.page(1) + except EmptyPage: + workouts = paginator.page(paginator.num_pages) + row = get_workout(id) + + messages.info(request,successmessage) + messages.error(request,message) + + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,row.id), + 'name': str(row.id) + }, + { + 'url':reverse(workout_fusion_list,kwargs={'id':id}), + 'name': 'Sensor Fusion' + } + + ] + + return render(request, 'fusion_list.html', + {'id':int(id), + 'workout':row, + 'rower':r, + 'active':'nav-workouts', + 'breadcrumbs':breadcrumbs, + 'workouts': workouts, + 'last_name':u.last_name, + 'first_name':u.first_name, + 'dateform':dateform, + 'startdate':startdate, + 'enddate':enddate, + 'teams':get_my_teams(request.user), + }) -# Basic 'EDIT' view of workout +# Basic view of workout def workout_view(request,id=0): request.session['referer'] = absolute(request)['PATH'] - try: - # check if valid ID exists (workout exists) - row = Workout.objects.get(id=id) - comments = WorkoutComment.objects.filter(workout=row) - - aantalcomments = len(comments) - - - if row.privacy == 'private': - raise PermissionDenied("Access denied") + if not request.user.is_anonymous(): + rower = getrower(request.user) + else: + rower = None - g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime") - for i in g: - try: - width,height = Image.open(i.filename).size - i.width = width - i.height = height - i.save() - except: - pass + try: + row = Workout.objects.get(id=id) + except Workout.DoesNotExist: + raise Http404("Workout doesn't exist") + + comments = WorkoutComment.objects.filter(workout=row) + + aantalcomments = len(comments) + + + if row.privacy == 'private' and not checkworkoutuser(request.user,row): + raise PermissionDenied("Access denied") + + g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime") + for i in g: + try: + width,height = Image.open(i.filename).size + i.width = width + i.height = height + i.save() + except: + pass - r = Rower.objects.get(id=row.user.id) - u = User.objects.get(id=r.user.id) - # create interactive plot - res = interactive_chart(id) - script = res[0] - div = res[1] + # create interactive plot + res = interactive_chart(id) + script = res[0] + div = res[1] - # create map - f1 = row.csvfilename - u = row.user.user - r = getrower(u) - rowdata = rdata(f1) - hascoordinates = 1 - if rowdata != 0: - try: - latitude = rowdata.df[' latitude'] - if not latitude.std(): - hascoordinates = 0 - except KeyError,AttributeError: + # create map + f1 = row.csvfilename + rowdata = rdata(f1) + hascoordinates = 1 + if rowdata != 0: + try: + latitude = rowdata.df[' latitude'] + if not latitude.std(): hascoordinates = 0 - - else: + except KeyError,AttributeError: hascoordinates = 0 + else: + hascoordinates = 0 - if hascoordinates: - mapscript,mapdiv = leaflet_chart(rowdata.df[' latitude'], - rowdata.df[' longitude'], - row.name) + + if hascoordinates: + mapscript,mapdiv = leaflet_chart(rowdata.df[' latitude'], + rowdata.df[' longitude'], + row.name) - else: - mapscript = "" - mapdiv = "" + else: + mapscript = "" + mapdiv = "" - - # render page - if (len(g)<=3): - return render(request, 'workout_view.html', - {'workout':row, - 'graphs1':g[0:3], - 'last_name':u.last_name, - 'first_name':u.first_name, - 'interactiveplot':script, - 'aantalcomments':aantalcomments, - 'mapscript':mapscript, - 'mapdiv':mapdiv, - 'teams':get_my_teams(request.user), - 'the_div':div}) - else: - return render(request, 'workout_view.html', - {'workout':row, - 'graphs1':g[0:3], - 'graphs2':g[3:6], - 'last_name':u.last_name, - 'first_name':u.first_name, - 'teams':get_my_teams(request.user), - 'aantalcomments':aantalcomments, - 'mapscript':mapscript, - 'mapdiv':mapdiv, - 'interactiveplot':script, - 'the_div':div}) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(workout_view,kwargs={'id':id}), + 'name': id, + } + + ] - - except Workout.DoesNotExist: - raise Http404("Workout doesn't exist") + u = row.user.user + + return render(request, 'workout_view.html', + {'workout':row, + 'rower':rower, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', + 'graphs':g, + 'last_name':u.last_name, + 'first_name':u.first_name, + 'interactiveplot':script, + 'aantalcomments':aantalcomments, + 'mapscript':mapscript, + 'mapdiv':mapdiv, + 'teams':get_my_teams(request.user), + 'the_div':div}) + # Resets stroke data to raw data (pace) @user_passes_test(ispromember,login_url="/",redirect_field_name=None) @@ -6737,6 +6969,7 @@ def workout_undo_smoothenpace_view( request,id=0,message="",successmessage="" ): row = get_workout(id) + r = getrower(request.user) if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" @@ -6757,7 +6990,12 @@ def workout_undo_smoothenpace_view( row.write_csv(filename,gzip=True) dataprep.update_strokedata(id,row.df) - url = "/rowers/workout/"+str(id)+"/advanced" + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':id, + } + ) + return HttpResponseRedirect(url) @@ -6767,6 +7005,8 @@ def workout_undo_smoothenpace_view( def workout_smoothenpace_view(request,id=0,message="",successmessage=""): row = get_workout(id) + r = getrower(request.user) + if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) @@ -6796,7 +7036,11 @@ def workout_smoothenpace_view(request,id=0,message="",successmessage=""): row.write_csv(filename,gzip=True) dataprep.update_strokedata(id,row.df) - url = "/rowers/workout/"+str(id)+"/advanced" + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':id, + } + ) return HttpResponseRedirect(url) @@ -6804,6 +7048,22 @@ def workout_smoothenpace_view(request,id=0,message="",successmessage=""): @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""): row = get_workout(id) + r = getrower(request.user) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(row.id) + }, + { + 'url':reverse(workout_crewnerd_summary_view,kwargs={'id':id}), + 'name': 'CrewNerd Summary' + } + + ] if request.method == 'POST': form = CNsummaryForm(request.POST,request.FILES) @@ -6840,6 +7100,10 @@ def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""): return render(request, "cn_form.html", {'form':form, + 'active':'nav-workouts', + 'rower':r, + 'workout':row, + 'breadcrumbs':breadcrumbs, 'teams':get_my_teams(request.user), 'id':row.id}) else: @@ -6848,6 +7112,10 @@ def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""): return render(request, "cn_form.html", {'form':form, + 'active':'nav-workouts', + 'rower':r, + 'workout':row, + 'breadcrumbs':breadcrumbs, 'teams':get_my_teams(request.user), 'id':row.id}) @@ -6988,6 +7256,22 @@ def workout_downloadmetar_view(request,id=0, @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_wind_view(request,id=0,message="",successmessage=""): row = get_workout(id) + r = getrower(request.user) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(row.id) + }, + { + 'url':reverse(workout_wind_view,kwargs={'id':id}), + 'name': 'Wind' + } + + ] if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" @@ -7091,6 +7375,9 @@ def workout_wind_view(request,id=0,message="",successmessage=""): return render(request, 'windedit.html', {'workout':row, + 'rower':r, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', 'teams':get_my_teams(request.user), 'interactiveplot':script, 'form':form, @@ -7105,6 +7392,7 @@ def workout_wind_view(request,id=0,message="",successmessage=""): @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_stream_view(request,id=0,message="",successmessage=""): row = get_workout(id) + r = getrower(request.user) if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" @@ -7157,11 +7445,30 @@ def workout_stream_view(request,id=0,message="",successmessage=""): script = res[0] div = res[1] + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(row.id) + }, + { + 'url':reverse(workout_stream_view,kwargs={'id':id}), + 'name': 'Stream' + } + + ] + messages.info(request,successmessage) messages.error(request,message) return render(request, 'streamedit.html', {'workout':row, + 'rower':r, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', 'teams':get_my_teams(request.user), 'interactiveplot':script, 'form':form, @@ -7170,9 +7477,16 @@ def workout_stream_view(request,id=0,message="",successmessage=""): # Form to set average crew weight and boat type, then run power calcs @user_passes_test(ispromember, login_url="/",redirect_field_name=None) def workout_otwsetpower_view(request,id=0,message="",successmessage=""): - row = get_workout(id) + w = get_workout(id) + r = getrower(request.user) - if (checkworkoutuser(request.user,row)==False): + mayedit = 0 + if request.user == w.user.user: + mayedit=1 + if checkworkoutuser(request.user,w): + mayedit=1 + + if (checkworkoutuser(request.user,w)==False): message = "You are not allowed to edit this workout" messages.error(request,message) url = reverse(workouts_view) @@ -7188,13 +7502,13 @@ def workout_otwsetpower_view(request,id=0,message="",successmessage=""): quick_calc = form.cleaned_data['quick_calc'] boattype = form.cleaned_data['boattype'] weightvalue = form.cleaned_data['weightvalue'] - row.boattype = boattype - row.weightvalue = weightvalue - row.save() + w.boattype = boattype + w.weightvalue = weightvalue + w.save() # load row data & create power/wind/bearing columns if not set - f1 = row.csvfilename + f1 = w.csvfilename rowdata = rdata(f1) if rowdata == 0: return HttpResponse("Error: CSV Data File Not Found") @@ -7261,26 +7575,69 @@ def workout_otwsetpower_view(request,id=0,message="",successmessage=""): response = HttpResponseRedirect(url) else: - form = AdvancedWorkoutForm(instance=row) + form = AdvancedWorkoutForm(instance=w) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(w.id) + }, + { + 'url':reverse(workout_otwsetpower_view,kwargs={'id':id}), + 'name': 'OTW Power' + } + + ] + + messages.error(request,message) messages.info(request,successmessage) return render(request, 'otwsetpower.html', - {'workout':row, + {'workout':w, + 'rower':w, + 'mayedit':mayedit, + 'active':'nav-workouts', + 'breadcrumbs':breadcrumbs, 'teams':get_my_teams(request.user), 'form':form, }) @login_required() def instroke_view(request,id=0): - row = get_workout(id) + w = get_workout(id) + r = getrower(request.user) + mayedit = 0 + if request.user == w.user.user: + mayedit=1 + if checkworkoutuser(request.user,w): + mayedit=1 + + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(w.id) + }, + { + 'url':reverse(instroke_view,kwargs={'id':id}), + 'name': 'In-Stroke Metrics' + } + + ] # form = WorkoutForm(instance=row) - g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime") + g = GraphImage.objects.filter(workout=w).order_by("-creationdatetime") # check if user is owner of this workout - if (checkworkoutuser(request.user,row)==False): + if (checkworkoutuser(request.user,w)==False): message = "You are not allowed to edit this workout" messages.error(request,message) url = reverse(workouts_view) @@ -7288,7 +7645,7 @@ def instroke_view(request,id=0): return HttpResponseRedirect(url) from metrics import nometrics - rowdata = rrdata(csvfile=row.csvfilename) + rowdata = rrdata(csvfile=w.csvfilename) try: instrokemetrics = rowdata.get_instroke_columns() instrokemetrics = [m for m in instrokemetrics if not m in nometrics] @@ -7298,7 +7655,11 @@ def instroke_view(request,id=0): return render(request, 'instroke.html', - {'workout':row, + {'workout':w, + 'rower':r, + 'active':'nav-workouts', + 'breadcrumbs':breadcrumbs, + 'mayedit':mayedit, 'teams':get_my_teams(request.user), 'instrokemetrics':instrokemetrics, }) @@ -7423,14 +7784,13 @@ def instroke_chart(request,id=0,metric=''): return HttpResponseRedirect(url) # Cumulative stats page -@login_required() +@user_passes_test(ispromember,login_url="/",redirect_field_name=None) def cumstats(request,theuser=0, startdate=timezone.now()-datetime.timedelta(days=30), enddate=timezone.now(), deltadays=-1, startdatestring="", enddatestring="", - plotfield='spm', options={ 'includereststrokes':False, 'workouttypes':['rower','dynamic','slides'], @@ -7438,19 +7798,29 @@ def cumstats(request,theuser=0, 'rankingonly':False, }): - if 'options' in request.session: - options = request.session['options'] + r = getrequestrower(request,userid=theuser) + theuser = r.user + + if 'waterboattype' in request.session: + waterboattype = request.session['waterboattype'] + else: + waterboattype = types.waterboattype - try: - workouttypes = options['workouttypes'] - except KeyError: - workouttypes = ['rower','dynamic','slides'] + if 'rankingonly' in request.session: + rankingonly = request.session['rankingonly'] + else: + rankingonly = False - try: - includereststrokes = options['includereststrokes'] - except KeyError: - includereststrokes = False + if 'modalities' in request.session: + modalities = request.session['modalities'] + if len(modalities) > 1: + modality = 'all' + else: + modality = modalities[0] + else: + modalities = [m[0] for m in types.workouttypes] + modality = 'all' try: @@ -7458,12 +7828,16 @@ def cumstats(request,theuser=0, except KeyError: rankingonly = False + try: + includereststrokes = options['includereststrokes'] + except KeyError: + includereststrokes = False + + workstrokesonly = not includereststrokes waterboattype = types.waterboattype - - if deltadays>0: - startdate = enddate-datetime.timedelta(days=int(deltadays)) + if startdatestring != "": startdate = iso8601.parse_date(startdatestring) @@ -7476,33 +7850,13 @@ def cumstats(request,theuser=0, enddate = startdate startdate = s - promember=0 - if theuser == 0: - if 'rowerid' in request.session: - try: - r = Rower.objects.get(id=request.session['rowerid']) - theuser = r.user.id - except Rower.DoesNotExist: - theuser = request.user.id - else: - theuser = request.user.id - - - if not request.user.is_anonymous(): - r = getrower(request.user) - result = request.user.is_authenticated() and ispromember(request.user) - if result: - promember=1 - - if not promember: - return HttpResponseRedirect("/rowers/promembership/") # get all indoor rows of in date range + # process form - if request.method == 'POST' and "daterange" in request.POST: + if request.method == 'POST': form = DateRangeForm(request.POST) - deltaform = DeltaDaysForm(request.POST) - optionsform = StatsOptionsForm() + modalityform = TrendFlexModalForm(request.POST) if form.is_valid(): startdate = form.cleaned_data['startdate'] enddate = form.cleaned_data['enddate'] @@ -7510,111 +7864,121 @@ def cumstats(request,theuser=0, s = enddate enddate = startdate startdate = s - elif request.method == 'POST' and "datedelta" in request.POST: - deltaform = DeltaDaysForm(request.POST) - if deltaform.is_valid(): - deltadays = deltaform.cleaned_data['deltadays'] - if deltadays != 0 and isinstance(deltadays, Number): - enddate = timezone.now() - startdate = enddate-datetime.timedelta(days=deltadays) - if startdate > enddate: - s = enddate - enddate = startdate - startdate = s - form = DateRangeForm(initial={ - 'startdate': startdate, - 'enddate': enddate, - }) - optionsform = StatsOptionsForm() + startdatestring = startdate.strftime('%Y-%m-%d') + enddatestring = enddate.strftime('%Y-%m-%d') + if modalityform.is_valid(): + modality = modalityform.cleaned_data['modality'] + waterboattype = modalityform.cleaned_data['waterboattype'] + rankingonly = modalityform.cleaned_data['rankingonly'] + if modality == 'all': + modalities = [m[0] for m in types.workouttypes] else: - form = DateRangeForm() - optionsform = StatsOptionsForm() - elif request.method == 'POST' and 'options' in request.POST: - optionsform = StatsOptionsForm(request.POST) - if optionsform.is_valid(): - includereststrokes = optionsform.cleaned_data['includereststrokes'] - workstrokesonly = not includereststrokes - workouttypes = [] - rankingonly = optionsform.cleaned_data['rankingonly'] - waterboattype = optionsform.cleaned_data['waterboattype'] - for type in types.checktypes: - if optionsform.cleaned_data[type]: - workouttypes.append(type) + modalities = [modality] - options = { - 'includereststrokes':includereststrokes, - 'workouttypes':workouttypes, - 'waterboattype':waterboattype, - 'rankingonly':rankingonly, - } - request.session['options'] = options - form = DateRangeForm() - deltaform = DeltaDaysForm() - else: - form = DateRangeForm() - deltaform = DeltaDaysForm() + if modality != 'water': + waterboattype = [b[0] for b in types.boattypes] + + + request.session['modalities'] = modalities + request.session['waterboattype'] = waterboattype + request.session['rankingonly'] = rankingonly + form = DateRangeForm(initial={ + 'startdate': startdate, + 'enddate': enddate, + }) else: form = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, }) - deltaform = DeltaDaysForm() - optionsform = StatsOptionsForm() + includereststrokes = False + + workstrokesonly = not includereststrokes + modalityform = TrendFlexModalForm( + initial={ + 'modality':modality, + 'waterboattype':waterboattype, + 'rankingonly':rankingonly, + } + ) + + negtypes = [] + for b in types.boattypes: + if b[0] not in waterboattype: + negtypes.append(b[0]) + + + + script = '' + div = get_call() + js_resources = '' + css_resources = '' + + options = { + 'modality': modality, + 'theuser': theuser.id, + 'waterboattype':waterboattype, + 'startdatestring':startdatestring, + 'enddatestring':enddatestring, + 'rankingonly':rankingonly, + 'includereststrokes':includereststrokes, + } + + + request.session['options'] = options + + + if modality == 'all': + modalities = [m[0] for m in types.workouttypes] + else: + modalities = [modality] + + try: + startdate = iso8601.parse_date(startdatestring) + except ParseError: + startdate = timezone.now()-datetime.timedelta(days=7) try: - r2 = getrower(theuser) - if rankingonly: - rankingpiece = [True] - else: - rankingpiece = [True,False] + enddate = iso8601.parse_date(enddatestring) + except ParseError: + enddate = timezone.now() + + + if enddate < startdate: + s = enddate + enddate = startdate + startdate = s + + promember=0 + if theuser == 0: + theuser = request.user.id + + if not request.user.is_anonymous(): + r = getrower(request.user) + result = request.user.is_authenticated() and ispromember(request.user) + if result: + promember=1 + + r2 = getrower(theuser) + + if rankingonly: + rankingpiece = [True,] + else: + rankingpiece = [True,False] - waterinclude = False + allworkouts = Workout.objects.filter( + user=r2, + workouttype__in=modalities, + boattype__in=waterboattype, + startdatetime__gte=startdate, + startdatetime__lte=enddate, + rankingpiece__in=rankingpiece + ).order_by("-date", "-starttime") - for ttype in types.otwtypes: - if ttype in workouttypes: - waterinclude = True - - if waterinclude: - workoutsw = Workout.objects.filter(user=r2, - startdatetime__gte=startdate, - startdatetime__lte=enddate, - workouttype='water', - boattype__in='waterboattype', - rankingpiece__in=rankingpiece, - ) - workouttypes = [w for w in workouttypes if w != 'water'] - - workoutse = Workout.objects.filter(user=r2, - startdatetime__gte=startdate, - startdatetime__lte=enddate, - workouttype__in=workouttypes, - rankingpiece__in=rankingpiece) - - if waterinclude: - allergworkouts = workoutse | workoutsw - allergworkouts = allergworkouts.distinct().order_by("-date", "-starttime") - else: - allergworkouts = workoutse.order_by("-date","-starttime") - - if waterinclude: - for ttype in types.otwtypes: - workouttypes.append(ttype) - - - - except Rower.DoesNotExist: - allergworkouts = [] - r2=0 - - try: - u = User.objects.get(id=theuser) - except User.DoesNotExist: - u = '' - - ids = [int(workout.id) for workout in allergworkouts] + ids = [int(workout.id) for workout in allworkouts] datemapping = { - w.id:w.date for w in allergworkouts + w.id:w.date for w in allworkouts } @@ -7630,10 +7994,7 @@ def cumstats(request,theuser=0, if datadf.empty: stats = {} - plotfield = 'spm' cordict = {} - div = "No Data found" - script = '' response = render(request, 'cumstats.html', @@ -7641,17 +8002,15 @@ def cumstats(request,theuser=0, 'stats':stats, 'teams':get_my_teams(request.user), 'options':options, + 'active':'nav-analysis', + 'rower':r, 'id':theuser, - 'theuser':u, + 'theuser':theuser, 'startdate':startdate, 'enddate':enddate, 'form':form, - 'deltaform':deltaform, - 'optionsform':optionsform, + 'optionsform':modalityform, 'cordict':cordict, - 'plotscript':script, - 'plotdiv':div, - 'plotfield':plotfield, }) request.session['options'] = options @@ -7696,7 +8055,6 @@ def cumstats(request,theuser=0, datadf['workoutid'].replace(datemapping,inplace=True) datadf.rename(columns={"workoutid":"date"},inplace=True) datadf = datadf.sort_values(['date']) - script,div = interactive_boxchart(datadf,plotfield) # set options form correctly initial = {} @@ -7704,32 +8062,22 @@ def cumstats(request,theuser=0, initial['waterboattype'] = waterboattype initial['rankingonly'] = rankingonly - for wtype in types.checktypes: - if wtype in workouttypes: - initial[wtype] = True - else: - initial[wtype] = False - - - optionsform = StatsOptionsForm(initial=initial) response = render(request, 'cumstats.html', { 'stats':stats, 'teams':get_my_teams(request.user), + 'active':'nav-analysis', + 'rower':r, 'options':options, 'id':theuser, - 'theuser':u, + 'theuser':theuser, 'startdate':startdate, 'enddate':enddate, 'form':form, - 'deltaform':deltaform, - 'optionsform':optionsform, + 'optionsform':modalityform, 'cordict':cordict, - 'plotscript':script, - 'plotdiv':div, - 'plotfield':plotfield, }) request.session['options'] = options @@ -7743,6 +8091,28 @@ def workout_stats_view(request,id=0,message="",successmessage=""): r = getrower(request.user) w = get_workout(id) + mayedit = 0 + if request.user == w.user.user: + mayedit=1 + if checkworkoutuser(request.user,w): + mayedit=1 + + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(w.id) + }, + { + 'url':reverse(workout_stats_view,kwargs={'id':id}), + 'name': 'Stats' + } + + ] + workstrokesonly = True if request.method == 'POST' and 'workstrokesonly' in request.POST: workstrokesonly = str2bool(request.POST['workstrokesonly']) @@ -7875,7 +8245,11 @@ def workout_stats_view(request,id=0,message="",successmessage=""): { 'stats':stats, 'teams':get_my_teams(request.user), - 'workout':row, + 'workout':w, + 'rower':r, + 'mayedit':mayedit, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', 'workstrokesonly':workstrokesonly, 'cordict':cordict, 'otherstats':otherstats, @@ -8048,6 +8422,15 @@ def workflow_default_view(request): return HttpResponseRedirect(url) +def get_workout_default_page(request,id): + if request.user.is_anonymous(): + return reverse(workout_view,kwargs={'id':str(id)}) + else: + r = Rower.objects.get(user=request.user) + if r.defaultlandingpage == 'workout_edit_view': + return reverse(workout_edit_view,kwargs={'id':str(id)}) + else: + return reverse(workout_workflow_view,kwargs={'id':str(id)}) # Workflow Configuration @login_required() @@ -8091,7 +8474,7 @@ def workout_workflow_config_view(request): }) @login_required() -def workout_workflow_config2_view(request): +def workout_workflow_config2_view(request,userid=0): request.session['referer'] = absolute(request)['PATH'] request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE try: @@ -8100,7 +8483,7 @@ def workout_workflow_config2_view(request): workoutid = 0 - r = getrower(request.user) + r = getrequestrower(request,userid=userid,notpermanent=True) MiddlePanelFormSet = formset_factory(WorkFlowMiddlePanelElement,extra=1) LeftPanelFormSet = formset_factory(WorkFlowLeftPanelElement,extra=1) @@ -8161,7 +8544,7 @@ def workout_workflow_config2_view(request): return render(request,tmplt, { - 'rower':getrower(request.user), + 'rower':r, 'leftpanel_formset':leftpanel_formset, 'middlepanel_formset':middlepanel_formset, 'workoutid': workoutid, @@ -8233,11 +8616,29 @@ def workout_workflow_view(request,id): pass + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(row.id) + }, + { + 'url':reverse(workout_workflow_view,kwargs={'id':id}), + 'name': 'View' + } + + ] + return render(request, 'workflow.html', { 'middleTemplates':middleTemplates, 'leftTemplates':leftTemplates, + 'active':'nav-workouts', + 'breadcrumbs':breadcrumbs, 'charts':charts, 'workout':row, 'mapscript':mapscript, @@ -8377,12 +8778,23 @@ def workout_flexchart3_view(request,*args,**kwargs): except KeyError: messages.error(request,'We cannot save the ad hoc metrics in a favorite chart') - if request.method == 'POST' and 'workstrokesonly' in request.POST: - workstrokesonly = request.POST['workstrokesonly'] - if workstrokesonly == 'True': - workstrokesonly = True - else: - workstrokesonly = False + if request.method == 'POST' and 'xaxis' in request.POST: + flexoptionsform = FlexOptionsForm(request.POST) + if flexoptionsform.is_valid(): + cd = flexoptionsform.cleaned_data + includereststrokes = cd['includereststrokes'] + plottype = cd['plottype'] + + workstrokesonly = not includereststrokes + + flexaxesform = FlexAxesForm(request,request.POST) + + + if flexaxesform.is_valid(): + cd = flexaxesform.cleaned_data + xparam = cd['xaxis'] + yparam1 = cd['yaxis1'] + yparam2 = cd['yaxis2'] if not promember: for name,d in rowingmetrics: @@ -8475,10 +8887,49 @@ def workout_flexchart3_view(request,*args,**kwargs): except KeyError: pass + initial = { + 'xaxis':xparam, + 'yaxis1':yparam1, + 'yaxis2':yparam2 + } + flexaxesform = FlexAxesForm(request,initial=initial) + + initial = { + 'includereststrokes': not workstrokesonly, + 'plottype':plottype + } + + flexoptionsform = FlexOptionsForm(initial=initial) + + row = Workout.objects.get(id=id) + + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(row.id) + }, + { + 'url':reverse(workout_flexchart3_view,kwargs=kwargs), + 'name': 'Flex Chart' + } + + ] + + return render(request, 'flexchart3otw.html', {'the_script':script, 'the_div':div, + 'breadcrumbs':breadcrumbs, + 'rower':r, + 'active':'nav-workouts', + 'workout':row, + 'chartform':flexaxesform, + 'optionsform':flexoptionsform, 'js_res': js_resources, 'css_res':css_resources, 'teams':get_my_teams(request.user), @@ -8543,14 +8994,31 @@ def workout_biginteractive_view(request,id=0,message="",successmessage=""): # The interactive plot with wind corrected pace for OTW outings def workout_otwpowerplot_view(request,id=0,message="",successmessage=""): - row = get_workout(id) + w = get_workout(id) + r = getrower(request.user) + + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(w.id) + }, + { + 'url':reverse(workout_otwpowerplot_view,kwargs={'id':id}), + 'name': 'Interactive OTW Power Plot' + } + + ] # check if user is owner of this workout # create interactive plot - f1 = row.csvfilename - u = row.user.user + f1 = w.csvfilename + u = w.user.user # r = getrower(u) promember=0 @@ -8560,7 +9028,7 @@ def workout_otwpowerplot_view(request,id=0,message="",successmessage=""): result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 - if request.user == row.user.user: + if request.user == w.user.user: mayedit=1 # create interactive plot @@ -8573,7 +9041,10 @@ def workout_otwpowerplot_view(request,id=0,message="",successmessage=""): return render(request, 'otwinteractive.html', - {'workout':row, + {'workout':w, + 'rower':r, + 'active':'nav-workouts', + 'breadcrumbs':breadcrumbs, 'teams':get_my_teams(request.user), 'interactiveplot':script, 'the_div':div, @@ -8671,7 +9142,7 @@ def workout_unsubscribe_view(request,id=0): @login_required() def workout_comment_view(request,id=0): w = get_workout(id) - + if w.privacy == 'private' and w.user.user != request.user: return HttpResponseForbidden("Permission error") @@ -8751,46 +9222,79 @@ def workout_comment_view(request,id=0): except: pass + rower = getrower(request.user) - if (len(g)<=3): - return render(request, - 'workout_comments.html', - {'workout':w, - 'teams':get_my_teams(request.user), - 'graphs1':g[0:3], - 'comments':comments, - 'form':form, - }) - else: - return render(request, - 'workout_comments.html', - {'workout':w, - 'teams':get_my_teams(request.user), - 'graphs1':g[0:3], - 'graphs1':g[3:6], - 'comments':comments, - 'form':form, - }) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(w.id) + }, + { + 'url':reverse(workout_comment_view,kwargs={'id':id}), + 'name': 'Comments' + } + + ] + + + return render(request, + 'workout_comments.html', + {'workout':w, + 'rower':rower, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', + 'teams':get_my_teams(request.user), + 'graphs':g, + 'comments':comments, + 'form':form, + }) # for ajax calls def course_map_view(request,id=0): - if id != 0: - try: - course = GeoCourse.objects.get(id=id) - except GeoCourse.DoesNotExist: - return Http404("Course doesn't exist") + try: + course = GeoCourse.objects.get(id=id) + except GeoCourse.DoesNotExist: + return Http404("Course doesn't exist") - script,div = course_map(course) + script,div = course_map(course) + + breadcrumbs = [ + { + 'url': reverse(virtualevents_view), + 'name': 'Racing' + }, + { + 'url': reverse(courses_view), + 'name': 'Courses' + }, + { + 'url': reverse(course_view,kwargs={'id':course.id}), + 'name': course.name + }, + { + 'url': reverse(course_map_view,kwargs={'id':course.id}), + 'name': 'Map' + } + ] + + r = getrower(request.user) + + + return render(request, + 'coursemap.html', + { + 'mapdiv':div, + 'course':course, + 'mapscript':script, + 'active':'nav-racing', + 'rower':r, + 'breadcrumbs':breadcrumbs, + }) - return render(request, - 'coursemap.html', - { - 'mapdiv':div, - 'mapscript':script - }) - - else: - return "" @login_required() def course_replace_view(request,id=0): @@ -8825,9 +9329,31 @@ def course_replace_view(request,id=0): script,div = course_map(course) + breadcrumbs = [ + { + 'url': reverse(virtualevents_view), + 'name': 'Racing' + }, + { + 'url': reverse(courses_view), + 'name': 'Courses' + }, + { + 'url': reverse(course_view,kwargs={'id':course.id}), + 'name': course.name + }, + { + 'url': reverse(course_replace_view,kwargs={'id':course.id}), + 'name': 'Replace Markers' + } + ] + return render(request, 'course_replace.html', {'course':course, + 'active':'nav-racing', + 'breadcrumbs':breadcrumbs, + 'rower':r, 'mapdiv':div, 'mapscript':script, 'form':form}) @@ -8888,10 +9414,31 @@ def course_edit_view(request,id=0): course.save() form = GeoCourseEditForm(instance=course) + + breadcrumbs = [ + { + 'url': reverse(virtualevents_view), + 'name': 'Racing' + }, + { + 'url': reverse(courses_view), + 'name': 'Courses' + }, + { + 'url': reverse(course_view,kwargs={'id':course.id}), + 'name': course.name + }, + { + 'url': reverse(course_edit_view,kwargs={'id':course.id}), + 'name': 'Edit' + } + ] return render(request, 'course_edit_view.html', { 'course':course, + 'active':'nav-racing', + 'breadcrumbs':breadcrumbs, 'mapscript':script, 'mapdiv':div, 'nosessions':nosessions, @@ -8911,8 +9458,25 @@ def course_view(request,id=0): script,div = course_map(course) + breadcrumbs = [ + { + 'url': reverse(virtualevents_view), + 'name': 'Racing' + }, + { + 'url': reverse(courses_view), + 'name': 'Courses' + }, + { + 'url': reverse(course_view,kwargs={'id':course.id}), + 'name': course.name + }, + ] + return render(request, 'course_view.html', { + 'active':'nav-racing', + 'breadcrumbs':breadcrumbs, 'course':course, 'mapscript':script, 'mapdiv':div, @@ -8931,6 +9495,9 @@ def workout_edit_view(request,id=0,message="",successmessage=""): row = get_workout(id) + if (checkworkoutuser(request.user,row)==False): + raise PermissionDenied("Access denied") + form = WorkoutForm(instance=row) if request.method == 'POST': @@ -8990,50 +9557,43 @@ def workout_edit_view(request,id=0,message="",successmessage=""): thetimezone = 'UTC' - # check if user is owner of this workout - if checkworkoutuser(request.user,row): - row.name = name - row.date = date - row.starttime = starttime - row.startdatetime = startdatetime - row.workouttype = workouttype - row.weightcategory = weightcategory - row.notes = notes - row.duration = duration - row.distance = distance - row.boattype = boattype - row.privacy = privacy - row.rankingpiece = rankingpiece - row.timezone = thetimezone - try: - row.save() - except IntegrityError: - pass - # change data in csv file - r = rdata(row.csvfilename) - if r == 0: - return HttpResponse("Error: CSV Data File Not Found") - r.rowdatetime = startdatetime - r.write_csv(row.csvfilename,gzip=True) - dataprep.update_strokedata(id,r.df) - successmessage = "Changes saved" + row.name = name + row.date = date + row.starttime = starttime + row.startdatetime = startdatetime + row.workouttype = workouttype + row.weightcategory = weightcategory + row.notes = notes + row.duration = duration + row.distance = distance + row.boattype = boattype + row.privacy = privacy + row.rankingpiece = rankingpiece + row.timezone = thetimezone + try: + row.save() + except IntegrityError: + pass + # change data in csv file + + r = rdata(row.csvfilename) + if r == 0: + return HttpResponse("Error: CSV Data File Not Found") + r.rowdatetime = startdatetime + r.write_csv(row.csvfilename,gzip=True) + dataprep.update_strokedata(id,r.df) + successmessage = "Changes saved" - if rankingpiece: - dataprep.runcpupdate(row.user,type=row.workouttype) + if rankingpiece: + dataprep.runcpupdate(row.user,type=row.workouttype) - messages.info(request,successmessage) - url = reverse(workout_edit_view, - kwargs = { - 'id':str(row.id), - }) - response = HttpResponseRedirect(url) - else: - message = "You are not allowed to change this workout" - messages.error(request,message) - url = reverse(workouts_view) - - response = HttpResponseRedirect(url) + messages.info(request,successmessage) + url = reverse(workout_edit_view, + kwargs = { + 'id':str(row.id), + }) + response = HttpResponseRedirect(url) #else: # form not POSTed form = WorkoutForm(instance=row) @@ -9051,15 +9611,6 @@ def workout_edit_view(request,id=0,message="",successmessage=""): pass - # check if user is owner of this workout - - comments = WorkoutComment.objects.filter(workout=row) - - aantalcomments = len(comments) - - if (checkworkoutuser(request.user,row)==False): - raise PermissionDenied("Access denied") - # create interactive plot f1 = row.csvfilename u = row.user.user @@ -9093,45 +9644,68 @@ def workout_edit_view(request,id=0,message="",successmessage=""): except KeyError: pass + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(row.id) + }, + { + 'url':reverse(workout_edit_view,kwargs={'id':id}), + 'name': 'Edit' + } + + ] + + r = getrower(request.user) + # render page - if (len(g)<=3): - return render(request, 'workout_form.html', - {'form':form, - 'workout':row, - 'teams':get_my_teams(request.user), - 'graphs1':g[0:3], - 'mapscript':mapscript, - 'aantalcomments':aantalcomments, - 'mapdiv':mapdiv, - 'rower':r, - }) - - else: - return render(request, 'workout_form.html', - {'form':form, - 'teams':get_my_teams(request.user), - 'workout':row, - 'graphs1':g[0:3], - 'graphs2':g[3:6], - 'mapscript':mapscript, - 'aantalcomments':aantalcomments, - 'mapdiv':mapdiv, - 'rower':r, - }) + return render(request, 'workout_form.html', + {'form':form, + 'workout':row, + 'teams':get_my_teams(request.user), + 'graphs':g, + 'breadcrumbs':breadcrumbs, + 'rower':r, + 'active':'nav-workouts', + 'mapscript':mapscript, + 'mapdiv':mapdiv, + 'rower':r, + }) + - return HttpResponseRedirect(url) @login_required() def workout_map_view(request,id=0): request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE request.session['referer'] = absolute(request)['PATH'] - row = get_workout(id) + w = get_workout(id) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(w.id) + }, + { + 'url':reverse(workout_map_view,kwargs={'id':id}), + 'name': 'Map' + } + + ] + + # create interactive plot - f1 = row.csvfilename - u = row.user.user + f1 = w.csvfilename + u = w.user.user r = getrower(u) rowdata = rdata(f1) hascoordinates = 1 @@ -9150,7 +9724,7 @@ def workout_map_view(request,id=0): if hascoordinates: mapscript,mapdiv = leaflet_chart2(rowdata.df[' latitude'], rowdata.df[' longitude'], - row.name) + w.name) else: mapscript = "" mapdiv = "" @@ -9161,12 +9735,15 @@ def workout_map_view(request,id=0): result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 - if request.user == row.user.user: + if request.user == w.user.user: mayedit=1 return render(request, 'map_view.html', {'mapscript':mapscript, - 'workout':row, + 'workout':w, + 'rower':r, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', 'mapdiv':mapdiv, 'mayedit':mayedit, }) @@ -9183,6 +9760,22 @@ def workout_uploadimage_view(request,id): w = get_workout(id) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(w.id) + }, + { + 'url':reverse(workout_uploadimage_view,kwargs={'id':id}), + 'name': 'Upload Image' + } + + ] + if not checkworkoutuser(request.user,w): raise PermissionDenied("You are not allowed to edit this workout") @@ -9250,6 +9843,9 @@ def workout_uploadimage_view(request,id): form = ImageForm() return render(request,'image_form.html', {'form':form, + 'rower':r, + 'active':'nav-workouts', + 'breadcrumbs':breadcrumbs, 'teams':get_my_teams(request.user), 'workout': w, }) @@ -9310,6 +9906,7 @@ def course_upload_view(request): form = CourseForm() return render(request,'course_form.html', {'form':form, + 'active':'nav-racing', }) else: return {'result':0} @@ -9321,6 +9918,7 @@ def course_upload_view(request): def workout_add_chart_view(request,id,plotnr=1): w = get_workout(id) + r = getrower(request.user) plotnr = int(plotnr) @@ -9345,18 +9943,24 @@ def workout_add_chart_view(request,id,plotnr=1): except KeyError: request.session['async_tasks'] = [(jobid,'make_plot')] - try: - url = request.session['referer'] - except KeyError: - url = "/rowers/workout/"+str(w.id)+"/edit" + + url = reverse(r.defaultlandingpage,kwargs={'id':str(w.id)}) + return HttpResponseRedirect(url) # The page where you select which Strava workout to import @login_required() -def workout_stravaimport_view(request,message=""): +def workout_stravaimport_view(request,message="",userid=0): res = stravastuff.get_strava_workout_list(request.user) + r = getrequestrower(request,userid=userid) + + if r.user != request.user: + messages.info(request,"You cannot import other people's workouts from Concept2") + + r = getrower(request.user) + if (res.status_code != 200): if (res.status_code == 401): r = getrower(request.user) @@ -9416,9 +10020,25 @@ def workout_stravaimport_view(request,message=""): res = dict(zip(keys,values)) workouts.append(res) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(workout_stravaimport_view), + 'name':'Strava' + }, + ] + + + r = getrower(request.user) return render(request,'strava_list_import.html', {'workouts':workouts, + 'rower':r, + 'active':'nav-workouts', + 'breadcrumbs':breadcrumbs, 'teams':get_my_teams(request.user), }) @@ -9426,7 +10046,7 @@ def workout_stravaimport_view(request,message=""): # The page where you select which RunKeeper workout to import @login_required() -def workout_runkeeperimport_view(request,message=""): +def workout_runkeeperimport_view(request,message="",userid=0): res = runkeeperstuff.get_runkeeper_workout_list(request.user) if (res.status_code != 200): if (res.status_code == 401): @@ -9442,29 +10062,46 @@ def workout_runkeeperimport_view(request,message=""): else: url = reverse(workouts_view) return HttpResponseRedirect(url) - else: - workouts = [] - for item in res.json()['items']: - d = int(float(item['total_distance'])) - i = getidfromuri(item['uri']) - ttot = str(datetime.timedelta(seconds=int(float(item['duration'])))) - s = item['start_time'] - r = item['type'] - keys = ['id','distance','duration','starttime','type'] - values = [i,d,ttot,s,r] - res = dict(zip(keys,values)) - workouts.append(res) - return render(request,'runkeeper_list_import.html', - {'workouts':workouts, - 'teams':get_my_teams(request.user), - }) + workouts = [] + for item in res.json()['items']: + d = int(float(item['total_distance'])) + i = getidfromuri(item['uri']) + ttot = str(datetime.timedelta(seconds=int(float(item['duration'])))) + s = item['start_time'] + r = item['type'] + keys = ['id','distance','duration','starttime','type'] + values = [i,d,ttot,s,r] + + res = dict(zip(keys,values)) + workouts.append(res) + + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(workout_runkeeperimport_view), + 'name':'Runkeeper' + } + ] + + r = getrower(request.user) + + return render(request,'runkeeper_list_import.html', + {'workouts':workouts, + 'rower':r, + 'active':'nav-workouts', + 'breadcrumbs':breadcrumbs, + 'teams':get_my_teams(request.user), + }) return HttpResponse(res) # The page where you select which RunKeeper workout to import @login_required() -def workout_underarmourimport_view(request,message=""): +def workout_underarmourimport_view(request,message="",userid=0): res = underarmourstuff.get_underarmour_workout_list(request.user) if (res.status_code != 200): if (res.status_code == 401): @@ -9476,38 +10113,53 @@ def workout_underarmourimport_view(request,message=""): messages.error(request,message) url = reverse(workouts_view) return HttpResponseRedirect(url) - else: - workouts = [] - items = res.json()['_embedded']['workouts'] - for item in items: - s = item['start_datetime'] - i,r = underarmourstuff.get_idfromuri(request.user,item['_links']) - n = item['name'] - try: - d = item['aggregates']['distance_total'] - except KeyError: - d = 0 - try: - ttot = item['aggregates']['active_time_total'] - except KeyError: - ttot = 0 - - keys = ['id','distance','duration','starttime','type'] - values = [i,d,ttot,s,r] - thedict = dict(zip(keys,values)) - - workouts.append(thedict) - return render(request,'underarmour_list_import.html', - {'workouts':workouts, - 'teams':get_my_teams(request.user), - }) + workouts = [] + items = res.json()['_embedded']['workouts'] + for item in items: + s = item['start_datetime'] + i,r = underarmourstuff.get_idfromuri(request.user,item['_links']) + n = item['name'] + try: + d = item['aggregates']['distance_total'] + except KeyError: + d = 0 + try: + ttot = item['aggregates']['active_time_total'] + except KeyError: + ttot = 0 + + keys = ['id','distance','duration','starttime','type'] + values = [i,d,ttot,s,r] + thedict = dict(zip(keys,values)) + + workouts.append(thedict) + + rower = getrower(request.user) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(workout_c2import_view), + 'name':'Concept2' + }, + ] + + return render(request,'underarmour_list_import.html', + {'workouts':workouts, + 'breadcrumbs':breadcrumbs, + 'rower':rower, + 'active':'nav-workouts', + 'teams':get_my_teams(request.user), + }) return HttpResponse(res) # the page where you select which Polar workout to Import @login_required() -def workout_polarimport_view(request): +def workout_polarimport_view(request,userid=0): exercises = polarstuff.get_polar_workouts(request.user) workouts = [] @@ -9515,7 +10167,7 @@ def workout_polarimport_view(request): a = exercises.status_code if a == 401: messages.error(request,'Not authorized. You need to connect to Polar first') - url = reverse(imports_view) + url = reverse(workouts_view) return HttpResponseRedirect(url) except: pass @@ -9537,10 +10189,25 @@ def workout_polarimport_view(request): res = dict(zip(keys,values)) workouts.append(res) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(workout_polarimport_view), + 'name':'Polar' + }, + ] + + r = getrower(request.user) return render(request, 'polar_list_import.html', { 'workouts':workouts, + 'active':'nav-workouts', + 'rower':r, + 'breadcrumbs':breadcrumbs, 'teams':get_my_teams(request.user), }) @@ -9549,7 +10216,9 @@ def workout_polarimport_view(request): # The page where you select which SportTracks workout to import @login_required() -def workout_sporttracksimport_view(request,message=""): +def workout_sporttracksimport_view(request,message="",userid=0): + + res = sporttracksstuff.get_sporttracks_workout_list(request.user) if (res.status_code != 200): if (res.status_code == 401): @@ -9566,31 +10235,48 @@ def workout_sporttracksimport_view(request,message=""): else: url = reverse(workouts_view) return HttpResponseRedirect(url) - else: - workouts = [] - r = getrower(request.user) - stids = [int(getidfromuri(item['uri'])) for item in res.json()['items']] - knownstids = uniqify([ - w.uploadedtosporttracks for w in Workout.objects.filter(user=r) - ]) - newids = [stid for stid in stids if not stid in knownstids] - for item in res.json()['items']: - d = int(float(item['total_distance'])) - i = int(getidfromuri(item['uri'])) - if i in knownstids: - nnn = '' - else: - nnn = 'NEW' - n = item['name'] - ttot = str(datetime.timedelta(seconds=int(float(item['duration'])))) - s = item['start_time'] - r = item['type'] - keys = ['id','distance','duration','starttime','type','name','new'] - values = [i,d,ttot,s,r,n,nnn] - res = dict(zip(keys,values)) - workouts.append(res) - return render(request,'sporttracks_list_import.html', + + workouts = [] + r = getrower(request.user) + stids = [int(getidfromuri(item['uri'])) for item in res.json()['items']] + knownstids = uniqify([ + w.uploadedtosporttracks for w in Workout.objects.filter(user=r) + ]) + newids = [stid for stid in stids if not stid in knownstids] + for item in res.json()['items']: + d = int(float(item['total_distance'])) + i = int(getidfromuri(item['uri'])) + if i in knownstids: + nnn = '' + else: + nnn = 'NEW' + n = item['name'] + ttot = str(datetime.timedelta(seconds=int(float(item['duration'])))) + s = item['start_time'] + r = item['type'] + keys = ['id','distance','duration','starttime','type','name','new'] + values = [i,d,ttot,s,r,n,nnn] + res = dict(zip(keys,values)) + workouts.append(res) + + r = getrower(request.user) + + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(workout_sporttracksimport_view), + 'name':'SportTracks' + }, + ] + + return render(request,'sporttracks_list_import.html', {'workouts':workouts, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', + 'rower':r, 'teams':get_my_teams(request.user), }) @@ -9674,7 +10360,15 @@ def workout_getc2workout_all(request,page=1,message=""): # List of workouts available on Concept2 logbook - for import @login_required() -def workout_c2import_view(request,page=1,message=""): +def workout_c2import_view(request,page=1,userid=0,message=""): + + r = getrequestrower(request,userid=userid) + + if r.user != request.user: + messages.info(request,"You cannot import other people's workouts from Concept2") + + r = getrower(request.user) + try: thetoken = c2_open(request.user) except NoTokenError: @@ -9687,38 +10381,57 @@ def workout_c2import_view(request,page=1,message=""): messages.error(request,message) url = reverse(workouts_view) return HttpResponseRedirect(url) - else: - workouts = [] - r = getrower(request.user) - c2ids = [item['id'] for item in res.json()['data']] - knownc2ids = uniqify([ - w.uploadedtoc2 for w in Workout.objects.filter(user=r) - ]) - newids = [c2id for c2id in c2ids if not c2id in knownc2ids] - for item in res.json()['data']: - d = item['distance'] - i = item['id'] - ttot = item['time_formatted'] - s = item['date'] - r = item['type'] - s2 = item['source'] - c = item['comments'] - if i in knownc2ids: - nnn = '' - else: - nnn = 'NEW' - keys = ['id','distance','duration','starttime','rowtype','source','comment','new'] - values = [i,d,ttot,s,r,s2,c,nnn] - res = dict(zip(keys,values)) - workouts.append(res) - - return render(request, - 'c2_list_import2.html', - {'workouts':workouts, - 'teams':get_my_teams(request.user), - 'page':page, - }) + workouts = [] + c2ids = [item['id'] for item in res.json()['data']] + knownc2ids = uniqify([ + w.uploadedtoc2 for w in Workout.objects.filter(user=r) + ]) + newids = [c2id for c2id in c2ids if not c2id in knownc2ids] + for item in res.json()['data']: + d = item['distance'] + i = item['id'] + ttot = item['time_formatted'] + s = item['date'] + r = item['type'] + s2 = item['source'] + c = item['comments'] + if i in knownc2ids: + nnn = '' + else: + nnn = 'NEW' + keys = ['id','distance','duration','starttime','rowtype','source','comment','new'] + values = [i,d,ttot,s,r,s2,c,nnn] + res = dict(zip(keys,values)) + workouts.append(res) + + + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(workout_c2import_view), + 'name':'Concept2' + }, + { + 'url':reverse(workout_c2import_view,kwargs={'page':page}), + 'name':'Page '+str(page) + } + ] + + r = getrower(request.user) + + return render(request, + 'c2_list_import2.html', + {'workouts':workouts, + 'rower':r, + 'active':'nav-workouts', + 'breadcrumbs':breadcrumbs, + 'teams':get_my_teams(request.user), + 'page':page, + }) importsources = { 'c2':c2stuff, @@ -9736,7 +10449,10 @@ def workout_getimportview(request,externalid,source = 'c2'): res = importsources[source].get_workout(request.user,externalid) if not res[0]: messages.error(request,res[1]) - return imports_view(request) + url = reverse(workouts_view) + + return HttpResponseRedirect(url) + strokedata = res[1] data = res[0] @@ -9989,6 +10705,17 @@ def workout_upload_view(request, r = getrower(request.user) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url': reverse(workout_upload_view), + 'name': 'Upload' + } + ] + if 'uploadoptions' in request.session: uploadoptions = request.session['uploadoptions'] try: @@ -10345,7 +11072,9 @@ def workout_upload_view(request, form = DocumentsForm(initial=docformoptions) optionsform = UploadOptionsForm(initial=uploadoptions) return render(request, 'document_form.html', - {'form':form, + {'form':form, + 'active':'nav-workouts', + 'breadcrumbs':breadcrumbs, 'teams':get_my_teams(request.user), 'optionsform': optionsform, }) @@ -10367,6 +11096,17 @@ def team_workout_upload_view(request,message="", else: request.session['uploadoptions'] = uploadoptions + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url': reverse(team_workout_upload_view), + 'name': 'Team Upload' + } + ] + myteams = Team.objects.filter(manager=request.user) @@ -10386,6 +11126,7 @@ def team_workout_upload_view(request,message="", res = handle_uploaded_file(f) t = form.cleaned_data['title'] offline = form.cleaned_data['offline'] + boattype = form.cleaned_data['boattype'] if rowerform.is_valid(): u = rowerform.cleaned_data['user'] if u: @@ -10490,6 +11231,8 @@ def team_workout_upload_view(request,message="", 'team_document_form.html', {'form':form, 'teams':get_my_teams(request.user), + 'active': 'nav-workouts', + 'breadcrumbs':breadcrumbs, 'optionsform': optionsform, 'rowerform': rowerform, }) @@ -10505,6 +11248,9 @@ def team_workout_upload_view(request,message="", {'form':form, 'teams':get_my_teams(request.user), 'optionsform': optionsform, + 'active': 'nav-workouts', + 'breadcrumbs':breadcrumbs, + 'rower':r, 'rowerform':rowerform, }) @@ -10618,16 +11364,24 @@ def graphs_view(request): ) g = GraphImage.objects.filter(workout__in=workouts).order_by("-creationdatetime") - if (len(g)<=5): - return render(request, 'list_graphs.html', - {'graphs1': g[0:4], - 'teams':get_my_teams(request.user), - }) - else: - return render(request, 'list_graphs.html', - {'graphs1': g[0:5], - 'teams':get_my_teams(request.user), - 'graphs2': g[5:10]}) + + + paginator = Paginator(g,8) + page = request.GET.get('page') + + try: + g = paginator.page(page) + except PageNotAnInteger: + g = paginator.page(1) + except EmptyPage: + g = paginator.page(paginator.num_pages) + + return render(request, 'list_graphs.html', + {'graphs': g, + 'active':'nav-workouts', + 'teams':get_my_teams(request.user), + }) + except Rower.DoesNotExist: raise Http404("User has no rower instance") @@ -10646,10 +11400,29 @@ def graph_show_view(request,id): w = Workout.objects.get(id=g.workout.id) r = Rower.objects.get(id=w.user.id) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,w.id), + 'name': str(w.id) + }, + { + 'url':reverse(graph_show_view,kwargs={'id':id}), + 'name': 'Chart' + } + + ] + + return render(request,'show_graph.html', {'graph':g, 'teams':get_my_teams(request.user), 'workout':w, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', 'rower':r,}) except GraphImage.DoesNotExist: @@ -10719,6 +11492,21 @@ def workout_split_view(request,id=id): r = row.user + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,row.id), + 'name': str(row.id) + }, + { + 'url':reverse(graph_show_view,kwargs={'id':id}), + 'name': 'Chart' + } + + ] if request.method == 'POST': form = WorkoutSplitForm(request.POST) if form.is_valid(): @@ -10779,6 +11567,9 @@ def workout_split_view(request,id=id): return render(request, 'splitworkout.html', {'form':form, + 'rower':r, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', 'workout':row, 'thediv':script, 'thescript':div, @@ -10788,6 +11579,9 @@ def workout_split_view(request,id=id): # Fuse two workouts @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_fusion_view(request,id1=0,id2=1): + + r = getrower(request.user) + try: w1 = Workout.objects.get(id=id1) w2 = Workout.objects.get(id=id2) @@ -10828,20 +11622,36 @@ def workout_fusion_view(request,id1=0,id2=1): }) return HttpResponseRedirect(url) - else: - return render(request, 'fusion.html', - {'form':form, - 'teams':get_my_teams(request.user), - 'workout1':w1, - 'workout2':w2, - }) - - - form = FusionMetricChoiceForm(instance=w2) + else: + form = FusionMetricChoiceForm(instance=w2) + + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,w1.id), + 'name': str(w1.id) + }, + { + 'url':reverse(workout_fusion_list,kwargs={'id':id1}), + 'name': 'Sensor Fusion' + }, + { + 'url':reverse(workout_fusion_view,kwargs={'id1':id1,'id2':id2}), + 'name': 'Sensor Fusion' + } + + ] return render(request, 'fusion.html', {'form':form, 'teams':get_my_teams(request.user), + 'workout':w1, + 'rower':r, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', 'workout1':w1, 'workout2':w2, }) @@ -10852,6 +11662,22 @@ def workout_fusion_view(request,id1=0,id2=1): def workout_summary_edit_view(request,id,message="",successmessage="" ): row = get_workout_permitted(request.user,id) + r = getrower(request.user) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,row.id), + 'name': str(row.id) + }, + { + 'url':reverse(workout_summary_edit_view,kwargs={'id':id}), + 'name': 'Edit Intervals' + } + + ] s = "" # still here - this is a workout we may edit @@ -11229,6 +12055,9 @@ def workout_summary_edit_view(request,id,message="",successmessage="" 'detailform':detailform, 'powerupdateform':powerupdateform, 'workout':row, + 'rower':r, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', 'teams':get_my_teams(request.user), 'intervalstats':intervalstats, 'nrintervals':nrintervals, @@ -11241,10 +12070,10 @@ def workout_summary_edit_view(request,id,message="",successmessage="" # Page where user can manage his favorite charts @login_required() -def rower_favoritecharts_view(request): +def rower_favoritecharts_view(request,userid=0): message = '' successmessage = '' - r = getrower(request.user) + r = getrequestrower(request,userid=userid,notpermanent=True) favorites = FavoriteChart.objects.filter(user=r).order_by('id') aantal = len(favorites) favorites_data = [{'yparam1':f.yparam1, @@ -11299,6 +12128,7 @@ def rower_favoritecharts_view(request): context = { 'favorites_formset':favorites_formset, 'teams':get_my_teams(request.user), + 'rower':r, } @@ -11307,8 +12137,8 @@ def rower_favoritecharts_view(request): # page where user sets his export settings @login_required() -def rower_exportsettings_view(request): - r = getrower(request.user) +def rower_exportsettings_view(request,userid=0): + r = getrequestrower(request,userid=userid) if request.method == 'POST': form = RowerExportForm(request.POST) if form.is_valid(): @@ -11321,20 +12151,44 @@ def rower_exportsettings_view(request): messages.info(request,'Settings saved') else: form = RowerExportForm(instance=r) + + breadcrumbs = [ + { + 'url':'/rowers/me', + 'name': 'Profile' + }, + { + 'url': reverse(rower_exportsettings_view), + 'name': 'Export Settings' + } + ] + return render(request, 'rower_exportsettings.html', {'form':form, - 'rower':r, + 'rower':r, + 'breadcrumbs': breadcrumbs, }) # Page where user can set his details # Add email address to form so user can change his email address @login_required() -def rower_edit_view(request,rowerid=0,message=""): - r = getrequestrower(request,rowerid=rowerid,notpermanent=True) +def rower_edit_view(request,rowerid=0,userid=0,message=""): + r = getrequestrower(request,rowerid=rowerid,userid=userid,notpermanent=True) rowerid = r.id - + + breadcrumbs = [ + { + 'url':'/rowers/me/edit', + 'name': 'Profile' + }, + { + 'url': reverse(rower_edit_view), + 'name': 'Account Settings' + } + ] + if request.method == 'POST' and "ut2" in request.POST: form = RowerForm(request.POST) if form.is_valid(): @@ -11369,6 +12223,7 @@ def rower_edit_view(request,rowerid=0,message=""): 'teams':get_my_teams(request.user), 'powerform':powerform, 'rower':r, + 'breadcrumbs':breadcrumbs, 'accountform':accountform, 'userform':userform, }) @@ -11387,6 +12242,7 @@ def rower_edit_view(request,rowerid=0,message=""): return render(request, 'rower_form.html', {'form':form, 'teams':get_my_teams(request.user), + 'breadcrumbs':breadcrumbs, 'powerzonesform':powerzonesform, 'userform':userform, 'accountform':accountform, @@ -11425,7 +12281,7 @@ def rower_edit_view(request,rowerid=0,message=""): messages.info(request,message) url = reverse(rower_edit_view, kwargs = { - 'rowerid':r.id, + 'userid':r.user.id, }) response = HttpResponseRedirect(url) except Rower.DoesNotExist: @@ -11445,6 +12301,7 @@ def rower_edit_view(request,rowerid=0,message=""): 'teams':get_my_teams(request.user), 'powerform':powerform, 'rower':r, + 'breadcrumbs':breadcrumbs, 'userform':userform, 'accountform':accountform, }) @@ -11487,6 +12344,7 @@ def rower_edit_view(request,rowerid=0,message=""): 'teams':get_my_teams(request.user), 'powerzonesform':powerzonesform, 'powerform':powerform, + 'breadcrumbs':breadcrumbs, 'userform':userform, 'accountform':accountform, 'rower':r, @@ -11509,6 +12367,7 @@ def rower_edit_view(request,rowerid=0,message=""): 'teams':get_my_teams(request.user), 'powerform':powerform, 'powerzonesform':powerzonesform, + 'breadcrumbs':breadcrumbs, 'accountform':accountform, 'userform':userform, 'rower':r, @@ -11567,6 +12426,7 @@ def rower_edit_view(request,rowerid=0,message=""): {'form':form, 'teams':get_my_teams(request.user), 'powerzonesform':powerzonesform, + 'breadcrumbs':breadcrumbs, 'powerform':powerform, 'accountform':accountform, 'userform':userform, @@ -11580,6 +12440,7 @@ def rower_edit_view(request,rowerid=0,message=""): {'form':form, 'teams':get_my_teams(request.user), 'powerzonesform':powerzonesform, + 'breadcrumbs':breadcrumbs, 'powerform':powerform, 'accountform':accountform, 'userform':userform, @@ -11600,6 +12461,7 @@ def rower_edit_view(request,rowerid=0,message=""): 'form':form, 'teams':get_my_teams(request.user), 'powerform':powerform, + 'breadcrumbs':breadcrumbs, 'powerzonesform':powerzonesform, 'userform':userform, 'accountform':accountform, @@ -11609,6 +12471,307 @@ def rower_edit_view(request,rowerid=0,message=""): except Rower.DoesNotExist: raise Http404("This user doesn't exist") +# Page where user can set his details +# Add email address to form so user can change his email address +@login_required() +def rower_prefs_view(request,userid=0,message=""): + r = getrequestrower(request,rowerid=rowerid,userid=userid,notpermanent=True) + + rowerid = r.id + + breadcrumbs = [ + { + 'url':'/rowers/me/edit', + 'name': 'Profile' + }, + { + 'url': reverse(rower_prefs_view), + 'name': 'Zones' + } + ] + + if request.method == 'POST' and "ut2" in request.POST: + form = RowerForm(request.POST) + if form.is_valid(): + # something + cd = form.cleaned_data + hrmax = cd['max'] + ut2 = cd['ut2'] + ut1 = cd['ut1'] + at = cd['at'] + tr = cd['tr'] + an = cd['an'] + rest = cd['rest'] + try: + r.max = max(min(hrmax,250),10) + r.ut2 = max(min(ut2,250),10) + r.ut1 = max(min(ut1,250),10) + r.at = max(min(at,250),10) + r.tr = max(min(tr,250),10) + r.an = max(min(an,250),10) + r.rest = max(min(rest,250),10) + r.save() + successmessage = "Your Heart Rate data were changed" + messages.info(request,successmessage) + form = RowerForm(instance=r) + powerform = RowerPowerForm(instance=r) + powerzonesform = RowerPowerZonesForm(instance=r) + accountform = AccountRowerForm(instance=r) + userform = UserForm(instance=r.user) + return render(request, 'rower_preferences.html', + {'form':form, + 'powerzonesform':powerzonesform, + 'teams':get_my_teams(request.user), + 'breadcrumbs':breadcrumbs, + 'powerform':powerform, + 'rower':r, + 'accountform':accountform, + 'userform':userform, + }) + except Rower.DoesNotExist: + message = "Funny. This user doesn't exist." + messages.error(request,message) + url = reverse(workouts_view) + response = HttpResponseRedirect(url) + else: + message = HttpResponse("invalid form") + #form = RowerForm(instance=r) + powerform = RowerPowerForm(instance=r) + powerzonesform = RowerPowerZonesForm(instance=r) + userform = UserForm(instance=r.user) + accountform = AccountRowerForm(instance=r) + return render(request, 'rower_preferences.html', + {'form':form, + 'teams':get_my_teams(request.user), + 'powerzonesform':powerzonesform, + 'breadcrumbs':breadcrumbs, + 'userform':userform, + 'accountform':accountform, + 'powerform':powerform, + 'rower':r, + }) + + + + return response + elif request.method == 'POST' and "ftp" in request.POST: + powerform = RowerPowerForm(request.POST) + if powerform.is_valid(): + cd = powerform.cleaned_data + hrftp = cd['hrftp'] + if hrftp == 0: + hrftp = int((r.an+r.tr)/2.) + ftp = cd['ftp'] + otwslack = cd['otwslack'] + try: + powerfrac = 100*np.array([r.pw_ut2, + r.pw_ut1, + r.pw_at, + r.pw_tr,r.pw_an])/r.ftp + r.ftp = max(min(ftp,650),50) + r.otwslack = max(min(otwslack,50),0) + ut2,ut1,at,tr,an = (r.ftp*powerfrac/100.).astype(int) + r.pw_ut2 = ut2 + r.pw_ut1 = ut1 + r.pw_at = at + r.pw_tr = tr + r.pw_an = an + r.hrftp = hrftp + r.save() + message = "FTP and/or OTW slack values changed." + messages.info(request,message) + url = reverse(rower_prefs_view, + kwargs = { + 'userid':r.user.id, + }) + response = HttpResponseRedirect(url) + except Rower.DoesNotExist: + message = "Funny. This user doesn't exist." + messages.error(request,message) + url = reverse(rower_edit_view) + response = HttpResponseRedirect(url) + else: + message = HttpResponse("invalid form") + form = RowerForm(instance=r) + #powerform = RowerPowerForm(instance=r) + powerzonesform = RowerPowerZonesForm(instance=r) + userform = UserForm(instance=r.user) + accountform = AccountRowerForm(instance=r) + return render(request, 'rower_preferences.html', + {'form':form, + 'teams':get_my_teams(request.user), + 'breadcrumbs':breadcrumbs, + 'powerform':powerform, + 'rower':r, + 'userform':userform, + 'accountform':accountform, + }) + + + return response + elif request.method == 'POST' and "ut3name" in request.POST: + powerzonesform = RowerPowerZonesForm(request.POST) + if powerzonesform.is_valid(): + cd = powerzonesform.cleaned_data + pw_ut2 = cd['pw_ut2'] + pw_ut1 = cd['pw_ut1'] + pw_at = cd['pw_at'] + pw_tr = cd['pw_tr'] + pw_an = cd['pw_an'] + ut3name = cd['ut3name'] + ut2name = cd['ut2name'] + ut1name = cd['ut1name'] + atname = cd['atname'] + trname = cd['trname'] + anname = cd['anname'] + powerzones = [ut3name,ut2name,ut1name,atname,trname,anname] + try: + r.pw_ut2 = pw_ut2 + r.pw_ut1 = pw_ut1 + r.pw_at = pw_at + r.pw_tr = pw_tr + r.pw_an = pw_an + r.powerzones = powerzones + r.save() + successmessage = "Your Power Zone data were changed" + messages.info(request,successmessage) + form = RowerForm(instance=r) + accountform = AccountRowerForm(instance=r) + userform = UserForm(instance=r.user) + powerform = RowerPowerForm(instance=r) + powerzonesform = RowerPowerZonesForm(instance=r) + return render(request, 'rower_preferences.html', + {'form':form, + 'teams':get_my_teams(request.user), + 'powerzonesform':powerzonesform, + 'powerform':powerform, + 'userform':userform, + 'breadcrumbs':breadcrumbs, + 'accountform':accountform, + 'rower':r, + }) + except Rower.DoesNotExist: + message = "Funny. This user doesn't exist." + messages.error(request,message) + url = reverse(workouts_view) + response = HttpResponseRedirect(url) + return response + else: + form = RowerForm(instance=r) + powerform = RowerPowerForm(instance=r) + accountform = AccountRowerForm(instance=r) + userform = UserForm(instance=r.user) + #powerzonesform = RowerPowerZonesForm(instance=r) + message = HttpResponse("invalid form") + return render(request, 'rower_preferences.html', + {'form':form, + 'teams':get_my_teams(request.user), + 'powerform':powerform, + 'breadcrumbs':breadcrumbs, + 'powerzonesform':powerzonesform, + 'accountform':accountform, + 'userform':userform, + 'rower':r, + }) + elif request.method == 'POST' and "weightcategory" in request.POST: + accountform = AccountRowerForm(request.POST) + userform = UserForm(request.POST,instance=r.user) + if accountform.is_valid() and userform.is_valid(): + # process + cd = accountform.cleaned_data + ucd = userform.cleaned_data + first_name = ucd['first_name'] + last_name = ucd['last_name'] + email = ucd['email'] + sex = cd['sex'] + defaultlandingpage = cd['defaultlandingpage'] + weightcategory = cd['weightcategory'] + birthdate = cd['birthdate'] + showfavoritechartnotes = cd['showfavoritechartnotes'] + getemailnotifications = cd['getemailnotifications'] + getimportantemails = cd['getimportantemails'] + defaulttimezone=cd['defaulttimezone'] + u = r.user + if u.email != email and len(email): + resetbounce = True + else: + resetbounce = False + if len(first_name): + u.first_name = first_name + u.last_name = last_name + if len(email): ## and check_email_freeforuse(u,email): + u.email = email + resetbounce = True + + + u.save() + r.defaulttimezone=defaulttimezone + r.weightcategory = weightcategory + r.getemailnotifications = getemailnotifications + r.getimportantemails = getimportantemails + r.defaultlandingpage = defaultlandingpage + r.showfavoritechartnotes = showfavoritechartnotes + r.sex = sex + r.birthdate = birthdate + if resetbounce and r.emailbounced: + r.emailbounced = False + r.save() + form = RowerForm(instance=r) + powerform = RowerPowerForm(instance=r) + powerzonesform = RowerPowerZonesForm(instance=r) + accountform = AccountRowerForm(instance=r) + userform = UserForm(instance=u) + successmessage = 'Account Information changed' + messages.info(request,successmessage) + return render(request, 'rower_preferences.html', + {'form':form, + 'teams':get_my_teams(request.user), + 'powerzonesform':powerzonesform, + 'breadcrumbs':breadcrumbs, + 'powerform':powerform, + 'accountform':accountform, + 'userform':userform, + 'rower':r, + }) + else: + form = RowerForm(instance=r) + powerform = RowerPowerForm(instance=r) + powerzonesform = RowerPowerZonesForm(instance=r) + return render(request, 'rower_preferences.html', + {'form':form, + 'teams':get_my_teams(request.user), + 'powerzonesform':powerzonesform, + 'powerform':powerform, + 'breadcrumbs':breadcrumbs, + 'accountform':accountform, + 'userform':userform, + 'rower':r, + }) + + + else: + try: + form = RowerForm(instance=r) + powerform = RowerPowerForm(instance=r) + powerzonesform = RowerPowerZonesForm(instance=r) + accountform = AccountRowerForm(instance=r) + userform = UserForm(instance=r.user) + grants = AccessToken.objects.filter(user=request.user) + return render(request, 'rower_preferences.html', + { + 'form':form, + 'teams':get_my_teams(request.user), + 'powerform':powerform, + 'powerzonesform':powerzonesform, + 'breadcrumbs':breadcrumbs, + 'userform':userform, + 'accountform':accountform, + 'grants':grants, + 'rower':r, + }) + except Rower.DoesNotExist: + raise Http404("This user doesn't exist") + # Revoke an app that you granted access through the API. # this views is called when you press a button on the User edit page # the button is only there when you have granted access to an app @@ -11626,13 +12789,8 @@ def rower_revokeapp_view(request,id=0): form = RowerForm(instance=r) powerform = RowerPowerForm(instance=r) grants = AccessToken.objects.filter(user=request.user) - return render(request, 'rower_form.html', - { - 'form':form, - 'teams':get_my_teams(request.user), - 'powerform':powerform, - 'grants':grants, - }) + url = reverse(rower_edit_view) + return HttpResponseRedirect(url) except AccessToken.DoesNotExist: raise Http404("Access token doesn't exist") @@ -11661,13 +12819,25 @@ def trydf(df,aantal,column): # Stroke data form to test API upload @login_required() def strokedataform(request,id=0): + + try: + id=int(id) + except ValueError: + id = 0 + + try: + w = Workout.objects.get(id=id) + except Workout.DoesNotExist: + raise Http404("Workout doesn't exist") + if request.method == 'GET': form = StrokeDataForm() return render(request, 'strokedata_form.html', { 'form':form, 'teams':get_my_teams(request.user), - 'id':int(id), + 'id':id, + 'workout':w, }) elif request.method == 'POST': form = StrokeDataForm() @@ -11676,7 +12846,8 @@ def strokedataform(request,id=0): { 'form':form, 'teams':get_my_teams(request.user), - 'id':int(id), + 'id':id, + 'workout':w, }) # Process the POSTed stroke data according to the API definition @@ -11830,11 +13001,14 @@ def strokedatajson(request,id): import teams @login_required() -def team_view(request,id=0): +def team_view(request,id=0,userid=0): ismember = 0 hasrequested = 0 - r = getrower(request.user) - myteams = Team.objects.filter(manager=request.user) + r = getrequestrower(request,userid=userid) + + myteams, memberteams, otherteams = get_teams(request) + teams.remove_expired_invites() + try: t = Team.objects.get(id=id) @@ -11874,13 +13048,27 @@ def team_view(request,id=0): if r in members: ismember = 1 + breadcrumbs = [ + { + 'url':reverse(rower_teams_view), + 'name': 'Teams' + }, + { + 'url':reverse(team_view,kwargs={'id':id}), + 'name': t.name + } + ] return render(request, 'team.html', { 'team':t, 'teams':get_my_teams(request.user), + 'myteams':myteams, + 'memberteams':memberteams, 'members':members, + 'breadcrumbs':breadcrumbs, + 'active':'nav-teams', 'inviteform':inviteform, 'ismember':ismember, 'hasrequested':hasrequested, @@ -11893,10 +13081,31 @@ def team_leaveconfirm_view(request,id=0): except Team.DoesNotExist: raise Http404("Team doesn't exist") + myteams, memberteams, otherteams = get_teams(request) + + breadcrumbs = [ + { + 'url':reverse(rower_teams_view), + 'name': 'Teams' + }, + { + 'url':reverse(team_view,kwargs={'id':id}), + 'name': t.name + }, + { + 'url':reverse(team_leaveconfirm_view,kwargs={'id':id}), + 'name': 'Leave' + } + ] return render(request,'teamleaveconfirm.html', { 'team':t, 'teams':get_my_teams(request.user), + 'myteams':myteams, + 'memberteams':memberteams, + 'otherteams':otherteams, + 'active':'nav-teams', + 'breadcrumbs':breadcrumbs, }) @login_required() @@ -12014,6 +13223,7 @@ def rower_update_empower_view( return render(request, 'empower_fix.html', {'workouts':workouts, + 'active': 'nav-workouts', 'dateform':dateform, 'form':form, 'rower':r @@ -12030,6 +13240,19 @@ def team_leave_view(request,id=0): from rowers.forms import TeamInviteCodeForm +def get_teams(request): + r = Rower.objects.get(user=request.user) + + myteams = Team.objects.filter( + manager=request.user).order_by('name') + memberteams = Team.objects.filter( + rower=r).exclude(manager=request.user).order_by('name') + otherteams = Team.objects.filter( + private='open').exclude( + rower=r).exclude(manager=request.user).order_by('name') + + return myteams, memberteams, otherteams + @login_required() def rower_teams_view(request,message='',successmessage=''): if request.method == 'POST': @@ -12046,9 +13269,11 @@ def rower_teams_view(request,message='',successmessage=''): r = getrower(request.user) ts = Team.objects.filter(rower=r) - myteams = Team.objects.filter(manager=request.user) - otherteams = Team.objects.filter(private='open').exclude(rower=r).exclude(manager=request.user).order_by('name') + + + myteams, memberteams, otherteams = get_teams(request) teams.remove_expired_invites() + invites = TeamInvite.objects.filter(user=request.user) requests = TeamRequest.objects.filter(user=request.user) @@ -12059,12 +13284,23 @@ def rower_teams_view(request,message='',successmessage=''): messages.info(request,successmessage) messages.error(request,message) + + breadcrumbs = [ + { + 'url':reverse(rower_teams_view), + 'name': 'Teams' + } + ] + return render(request, 'teams.html', { 'teams':ts, + 'active':'nav-teams', + 'breadcrumbs':breadcrumbs, 'clubsize':clubsize, 'max_clubsize':max_clubsize, 'myteams':myteams, + 'memberteams':memberteams, 'invites':invites, 'otherteams':otherteams, 'requests':requests, @@ -12237,10 +13473,32 @@ def team_edit_view(request,id=0): else: teamcreateform = TeamForm(instance=t) + myteams, memberteams, otherteams = get_teams(request) + + breadcrumbs = [ + { + 'url':reverse(rower_teams_view), + 'name': 'Teams' + }, + { + 'url':reverse(team_view,kwargs={'id':id}), + 'name': t.name + }, + { + 'url':reverse(team_edit_view,kwargs={'id':id}), + 'name': 'Edit' + } + ] + return render(request,'teamedit.html', { 'form':teamcreateform, 'teams':get_my_teams(request.user), + 'myteams':myteams, + 'memberteams':memberteams, + 'otherteams':otherteams, + 'active':'nav-teams', + 'breadcrumbs':breadcrumbs, 'team':t, }) @@ -12264,10 +13522,27 @@ def team_create_view(request): else: teamcreateform = TeamForm() + myteams, memberteams, otherteams = get_teams(request) + + breadcrumbs = [ + { + 'url':reverse(rower_teams_view), + 'name': 'Teams' + }, + { + 'url':reverse(team_create_view), + 'name': "New Team" + }, + ] return render(request,'teamcreate.html', { 'teams':get_my_teams(request.user), 'form':teamcreateform, + 'myteams':myteams, + 'memberteams':memberteams, + 'otherteams':otherteams, + 'active':'nav-teams', + 'breadcrumbs':breadcrumbs, }) @user_passes_test(iscoachmember,login_url="/",redirect_field_name=None) @@ -12280,10 +13555,30 @@ def team_deleteconfirm_view(request,id): if t.manager != request.user: raise PermissionDenied("You are not allowed to delete this team") + myteams, memberteams, otherteams = get_teams(request) + + breadcrumbs = [ + { + 'url':reverse(rower_teams_view), + 'name': 'Teams' + }, + { + 'url':reverse(team_view,kwargs={'id':id}), + 'name': t.name + }, + { + 'url':reverse(team_deleteconfirm_view,kwargs={'id':id}), + 'name': 'Leave' + } + ] return render(request,'teamdeleteconfirm.html', { 'teams':get_my_teams(request.user), - 'team':t + 'team':t, + 'myteams':myteams, + 'memberteams':memberteams, + 'otherteams':otherteams, + 'active':'nav-teams', }) @user_passes_test(iscoachmember,login_url="/",redirect_field_name=None) @@ -12316,11 +13611,31 @@ def team_members_stats_view(request,id): theusers = [member.user for member in members] + myteams, memberteams, otherteams = get_teams(request) + breadcrumbs = [ + { + 'url':reverse(rower_teams_view), + 'name': 'Teams' + }, + { + 'url':reverse(team_view,kwargs={'id':id}), + 'name': t.name + }, + { + 'url':reverse(team_members_stats_view,kwargs={'id':id}), + 'name': 'Members Stats' + } + ] response = render(request,'teamstats.html', { 'teams':get_my_teams(request.user), + 'myteams':myteams, + 'memberteams':memberteams, + 'otherteams':otherteams, + 'active':'nav-teams', + 'breadcrumbs':breadcrumbs, 'team':t, 'theusers':theusers, }) @@ -12334,6 +13649,7 @@ def agegroupcpview(request,age,normalize=0): response = render(request,'agegroupcp.html', { + 'active': 'nav-analysis', 'interactiveplot':script, 'the_div':div, } @@ -12372,23 +13688,29 @@ def agegrouprecordview(request,sex='male',weightcategory='hwt', return render(request, 'agegroupchart.html', { - 'interactiveplot':script, - 'the_div':div, + 'interactiveplot':script, + 'active':'nav-analysis', + 'the_div':div, }) # Cloning sessions @user_passes_test(hasplannedsessions,login_url="/rowers/planmembership/", redirect_field_name=None) def plannedsession_multiclone_view( - request,timeperiod='none', - rowerid=0, + request, + userid=0, startdate=timezone.now()-datetime.timedelta(days=30), enddate=timezone.now()): - r = getrequestrower(request,rowerid=rowerid) + r = getrequestrower(request,userid=userid) - if timeperiod != 'none': - startdate,enddate = get_dates_timeperiod(timeperiod) + when = request.GET.get('when') + if when: + timeperiod = when + else: + timeperiod = 'thisweek' + + startdate,enddate = get_dates_timeperiod(timeperiod) if request.method == 'POST' and 'daterange' in request.POST: @@ -12430,10 +13752,11 @@ def plannedsession_multiclone_view( url = reverse(plannedsession_multicreate_view, kwargs = { - 'rowerid':r.id, - 'timeperiod':timeperiod, + 'userid':r.user.id, }) - + if when: + url += '?when='+when + return HttpResponseRedirect(url) sps = PlannedSession.objects.filter( @@ -12477,6 +13800,7 @@ def plannedsession_multiclone_view( 'form':form, 'dateshiftform':dateshiftform, 'rower':r, + 'active':'nav-plan', 'timeperiod':timeperiod, } ) @@ -12484,9 +13808,18 @@ def plannedsession_multiclone_view( # Individual user creates training for himself @user_passes_test(hasplannedsessions,login_url="/rowers/planmembership/", redirect_field_name=None) -def plannedsession_create_view(request,timeperiod='thisweek',rowerid=0): +def plannedsession_create_view(request, + userid=0): - r = getrequestrower(request,rowerid=rowerid) + r = getrequestrower(request,userid=userid) + + + when = request.GET.get('when') + if when: + timeperiod = when + else: + timeperiod = 'thisweek' + startdate,enddate = get_dates_timeperiod(timeperiod) if request.method == 'POST': @@ -12534,7 +13867,7 @@ def plannedsession_create_view(request,timeperiod='thisweek',rowerid=0): url = reverse(plannedsession_create_view, kwargs = { - 'rowerid':r.id, + 'userid':r.user.id, 'timeperiod':timeperiod, }) return HttpResponseRedirect(url) @@ -12563,6 +13896,7 @@ def plannedsession_create_view(request,timeperiod='thisweek',rowerid=0): if fprefdate > enddate: fprefdate = enddate + forminitial = { 'startdate':fstartdate, 'enddate':fenddate, @@ -12600,6 +13934,7 @@ def plannedsession_create_view(request,timeperiod='thisweek',rowerid=0): 'teams':get_my_teams(request.user), 'plan':trainingplan, 'form':sessioncreateform, + 'active':'nav-plan', 'plannedsessions':sps, 'rower':r, 'timeperiod':timeperiod, @@ -12607,12 +13942,18 @@ def plannedsession_create_view(request,timeperiod='thisweek',rowerid=0): @user_passes_test(hasplannedsessions,login_url="/rowers/planmembership/", redirect_field_name=None) -def plannedsession_multicreate_view(request,timeperiod='thisweek', - teamid=0,rowerid=0,extrasessions=0): +def plannedsession_multicreate_view(request, + teamid=0,userid=0,extrasessions=0): extrasessions=int(extrasessions) - r = getrequestrower(request,rowerid=rowerid) + r = getrequestrower(request,userid=userid) + + when = request.GET.get('when') + if when: + timeperiod = when + else: + timeperiod = 'thisweek' startdate,enddate = get_dates_timeperiod(timeperiod) try: @@ -12664,6 +14005,7 @@ def plannedsession_multicreate_view(request,timeperiod='thisweek', initials = [initial for i in range(extrasessions)] + PlannedSessionFormSet = modelformset_factory( PlannedSession, form=PlannedSessionFormSmall, @@ -12687,10 +14029,14 @@ def plannedsession_multicreate_view(request,timeperiod='thisweek', url = reverse(plannedsession_multicreate_view, kwargs = { - 'rowerid':r.id, - 'timeperiod':timeperiod + 'userid':r.user.id, } ) + + + if when: + url += '?when='+when + return HttpResponseRedirect(url) ps_formset = PlannedSessionFormSet(queryset = qset, @@ -12699,6 +14045,7 @@ def plannedsession_multicreate_view(request,timeperiod='thisweek', context = { 'ps_formset':ps_formset, 'rower':r, + 'active':'nav-plan', 'plan':trainingplan, 'timeperiod':timeperiod, 'teams':get_my_teams(request.user), @@ -12711,9 +14058,16 @@ def plannedsession_multicreate_view(request,timeperiod='thisweek', @user_passes_test(iscoachmember,login_url="/rowers/planmembership/", redirect_field_name=None) def plannedsession_teamcreate_view(request,timeperiod='thisweek', - teamid=0): + teamid=0,userid=0): + therower = getrequestrower(request,userid=userid) + when = request.GET.get('when') + if when: + timeperiod = when + else: + timeperiod = 'thisweek' + teams = Team.objects.filter(manager=request.user) if len(teams)>0: teamchoices = [(team.id, team.name) for team in teams] @@ -12791,7 +14145,13 @@ def plannedsession_teamcreate_view(request,timeperiod='thisweek', }) return HttpResponseRedirect(url) else: - sessioncreateform = PlannedSessionForm() + initial = { + 'startdate':startdate, + 'enddate':enddate, + 'preferreddate':startdate, + } + + sessioncreateform = PlannedSessionForm(initial=initial) sessionteamselectform = PlannedSessionTeamForm( request.user ) @@ -12804,15 +14164,25 @@ def plannedsession_teamcreate_view(request,timeperiod='thisweek', 'teamform':sessionteamselectform, 'timeperiod':timeperiod, 'plannedsessions':sps, - 'rower':getrower(request.user), + 'rower':therower, + 'active':'nav-plan' }) # Manager edits sessions for entire team @user_passes_test(iscoachmember,login_url="/rowers/planmembership/", redirect_field_name=None) -def plannedsession_teamedit_view(request,timeperiod='thisweek', - sessionid=0): +def plannedsession_teamedit_view(request, + sessionid=0,userid=0): + r = getrequestrower(request,userid=userid) + + when = request.GET.get('when') + if when: + timeperiod = when + else: + timeperiod = 'thisweek' + + try: ps = PlannedSession.objects.get(id=sessionid) except PlannedSession.DoesNotExist: @@ -12902,8 +14272,11 @@ def plannedsession_teamedit_view(request,timeperiod='thisweek', url = reverse(plannedsession_teamedit_view, kwargs = { 'sessionid':sessionid, - 'timeperiod':timeperiod, }) + + if when: + url += '?when='+when + return HttpResponseRedirect(url) else: sessioncreateform = PlannedSessionForm(instance=ps) @@ -12922,6 +14295,8 @@ def plannedsession_teamedit_view(request,timeperiod='thisweek', { 'plannedsession':ps, 'plan':trainingplan, + 'rower':r, + 'active':'nav-plan', 'teams':get_my_teams(request.user), 'form':sessioncreateform, 'teamform':sessionteamselectform, @@ -12932,11 +14307,21 @@ def plannedsession_teamedit_view(request,timeperiod='thisweek', @user_passes_test(iscoachmember,login_url="/rowers/planmembership/", redirect_field_name=None) -def plannedsessions_coach_view(request,timeperiod='thisweek', - teamid=0): +def plannedsessions_coach_view(request, + teamid=0,userid=0): + + therower = getrower(request.user) + + when = request.GET.get('when') + if when: + timeperiod = when + else: + timeperiod = 'thisweek' + startdate,enddate = get_dates_timeperiod(timeperiod) + trainingplan = None if teamid != 0: @@ -12992,6 +14377,8 @@ def plannedsessions_coach_view(request,timeperiod='thisweek', 'statusdict':statusdict, 'timeperiod':timeperiod, 'rowers':rowers, + 'rower':therower, + 'active':'nav-plan', 'theteam':theteam, 'unmatchedworkouts':unmatchedworkouts, 'rower':getrower(request.user) @@ -12999,9 +14386,16 @@ def plannedsessions_coach_view(request,timeperiod='thisweek', ) @login_required() -def plannedsessions_view(request,timeperiod='thisweek',rowerid=0): +def plannedsessions_view(request, + userid=0): - r = getrequestrower(request,rowerid=rowerid) + r = getrequestrower(request,userid=userid) + + when = request.GET.get('when') + if when: + timeperiod = when + else: + timeperiod = 'thisweek' startdate,enddate = get_dates_timeperiod(timeperiod) @@ -13037,6 +14431,7 @@ def plannedsessions_view(request,timeperiod='thisweek',rowerid=0): 'teams':get_my_teams(request.user), 'plannedsessions':sps, 'plan':trainingplan, + 'active': 'nav-plan', 'rower':r, 'timeperiod':timeperiod, 'completeness':completeness, @@ -13046,10 +14441,16 @@ def plannedsessions_view(request,timeperiod='thisweek',rowerid=0): }) @login_required() -def plannedsessions_print_view(request,timeperiod='thisweek',rowerid=0): +def plannedsessions_print_view(request,userid=0): - r = getrequestrower(request,rowerid=rowerid) + r = getrequestrower(request,userid=userid) + when = request.GET.get('when') + if when: + timeperiod = when + else: + timeperiod = 'thisweek' + startdate,enddate = get_dates_timeperiod(timeperiod) try: @@ -13072,6 +14473,7 @@ def plannedsessions_print_view(request,timeperiod='thisweek',rowerid=0): 'plan':trainingplan, 'plannedsessions':sps, 'rower':r, + 'active':'nav-plan', 'startdate':startdate, 'enddate':enddate, 'timeperiod':timeperiod, @@ -13079,14 +14481,20 @@ def plannedsessions_print_view(request,timeperiod='thisweek',rowerid=0): @login_required() -def plannedsessions_manage_view(request,timeperiod='thisweek',rowerid=0, +def plannedsessions_manage_view(request,userid=0, initialsession=0): is_ajax = False if request.is_ajax(): is_ajax = True - r = getrequestrower(request,rowerid=rowerid) + when = request.GET.get('when') + if when: + timeperiod = when + else: + timeperiod = 'thisweek' + + r = getrequestrower(request,userid=userid) startdate,enddate = get_dates_timeperiod(timeperiod) @@ -13117,7 +14525,7 @@ def plannedsessions_manage_view(request,timeperiod='thisweek',rowerid=0, "date","startdatetime","id" ) - + initialworkouts = [w.id for w in Workout.objects.filter(user=r,plannedsession=ps0)] linkedworkouts = [] @@ -13194,6 +14602,23 @@ def plannedsessions_manage_view(request,timeperiod='thisweek',rowerid=0, return JSONResponse(ajax_response) + + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url':reverse(plannedsessions_manage_view, + kwargs={ + 'userid':userid, + 'initialsession':initialsession, + } + ), + 'name': 'Link Sessions to Workouts' + }, + ] return render(request,'plannedsessionsmanage.html', { @@ -13201,6 +14626,8 @@ def plannedsessions_manage_view(request,timeperiod='thisweek',rowerid=0, 'plan':trainingplan, 'plannedsessions':sps, 'workouts':ws, + 'active':'nav-plan', + 'breadcrumbs':breadcrumbs, 'timeperiod':timeperiod, 'rower':r, 'ps_form':ps_form, @@ -13212,10 +14639,15 @@ def plannedsessions_manage_view(request,timeperiod='thisweek',rowerid=0, # need clarity on cloning behavior time shift @user_passes_test(hasplannedsessions,login_url="/rowers/planmembership/", redirect_field_name=None) -def plannedsession_clone_view(request,id=0,rowerid=0, - timeperiod='thisweek'): +def plannedsession_clone_view(request,id=0,userid=0): - r = getrequestrower(request,rowerid=rowerid) + r = getrequestrower(request,userid=userid) + + when = request.GET.get('when') + if when: + timeperiod = when + else: + timeperiod = 'thisweek' startdate,enddate = get_dates_timeperiod(timeperiod) @@ -13257,10 +14689,12 @@ def plannedsession_clone_view(request,id=0,rowerid=0, url = reverse(plannedsession_edit_view, kwargs = { 'id':ps.id, - 'timeperiod':timeperiod, - 'rowerid':r.id, + 'userid':r.user.id, } - ) + ) + + if when: + url += '?when='+when return HttpResponseRedirect(url) @@ -13268,9 +14702,15 @@ def plannedsession_clone_view(request,id=0,rowerid=0, # Edit an existing planned session @user_passes_test(hasplannedsessions,login_url="/rowers/planmembership/", redirect_field_name=None) -def plannedsession_edit_view(request,id=0,timeperiod='thisweek',rowerid=0): +def plannedsession_edit_view(request,id=0,userid=0): - r = getrequestrower(request,rowerid=rowerid) + r = getrequestrower(request,userid=userid) + + when = request.GET.get('when') + if when: + timeperiod = when + else: + timeperiod = 'thisweek' startdate,enddate = get_dates_timeperiod(timeperiod) @@ -13290,11 +14730,10 @@ def plannedsession_edit_view(request,id=0,timeperiod='thisweek',rowerid=0): if ps.manager != request.user: raise PermissionDenied("You are not allowed to edit this planned session") - + if ps.team.all() or len(ps.rower.all())>1: url = reverse(plannedsession_teamedit_view, kwargs={ - 'timeperiod':timeperiod, 'sessionid':id, }) return HttpResponseRedirect(url) @@ -13320,20 +14759,57 @@ def plannedsession_edit_view(request,id=0,timeperiod='thisweek',rowerid=0): url = reverse(plannedsession_edit_view, kwargs={ 'id':int(ps.id), - 'timeperiod':timeperiod, - 'rowerid':r.id, + 'userid':r.user.id, }) + + if when: + url += '?when='+when + return HttpResponseRedirect(url) else: sessioncreateform = PlannedSessionForm(instance=ps) sps = get_sessions(r,startdate=startdate,enddate=enddate) - + + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url': reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Sessions' + }, + { + 'url':reverse(plannedsession_view, + kwargs={ + 'userid':userid, + 'id':id, + } + ), + 'name': ps.id + }, + { + 'url':reverse(plannedsession_edit_view, + kwargs={ + 'userid':userid, + 'id':id, + } + ), + 'name': 'Edit' + } + ] + + return render(request,'plannedsessionedit.html', { 'teams':get_my_teams(request.user), 'plan':trainingplan, + 'breadcrumbs':breadcrumbs, 'form':sessioncreateform, + 'active':'nav-plan', 'plannedsessions':sps, 'thesession':ps, 'rower':r, @@ -13343,16 +14819,16 @@ def plannedsession_edit_view(request,id=0,timeperiod='thisweek',rowerid=0): @login_required() -def plannedsession_view(request,id=0,rowerid=0, - timeperiod='thisweek'): +def plannedsession_view(request,id=0,userid=0): - m = getrower(request.user) + r = getrequestrower(request,userid=userid) - - if not rowerid: - r = m + when = request.GET.get('when') + if when: + timeperiod = when else: - r = Rower.objects.get(id=rowerid) + timeperiod = 'thisweek' + try: ps = PlannedSession.objects.get(id=id) @@ -13366,6 +14842,8 @@ def plannedsession_view(request,id=0,rowerid=0, coursescript = '' coursediv = '' + m = ps.manager + if ps.manager != request.user: if r.rowerplan == 'coach': teams = Team.objects.filter(manager=request.user) @@ -13465,6 +14943,30 @@ def plannedsession_view(request,id=0,rowerid=0, enddate__gte = enddate)[0] except IndexError: trainingplan = None + + + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url': reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Sessions' + }, + { + 'url':reverse(plannedsession_view, + kwargs={ + 'userid':userid, + 'id':id, + } + ), + 'name': ps.id + } + ] + return render(request,'plannedsessionview.html', { @@ -13476,6 +14978,8 @@ def plannedsession_view(request,id=0,rowerid=0, 'sessionvalue','sessionunit','comment', ], 'workouts': ws, + 'active':'nav-plan', + 'breadcrumbs':breadcrumbs, 'manager':m, 'rower':r, 'ratio':ratio, @@ -13490,58 +14994,78 @@ def plannedsession_view(request,id=0,rowerid=0, } ) -@login_required() -def plannedsession_delete_view(request,id=0): - r = getrower(request.user) +class PlannedSessionDelete(DeleteView): + model = PlannedSession + template_name = 'plannedsessiondeleteconfirm.html' - try: - ps = PlannedSession.objects.get(id=id) - except PlannedSession.DoesNotExist: - raise Http404("Planned Session does not exist") + # extra parameters + def get_context_data(self, **kwargs): + context = super(PlannedSessionDelete,self).get_context_data(**kwargs) + if 'userid' in kwargs: + userid = kwargs['userid'] + else: + userid = 0 - if ps.manager != request.user: - raise PermissionDenied("You are not allowed to delete this planned session") + context['active']= 'nav-plan' + context['rower'] = getrequestrower(self.request,userid=userid) + context['ps'] = self.object - ws = Workout.objects.filter(plannedsession=ps) - for w in ws: - w.plannedsession=None - w.save() - - ps.delete() + psdict = my_dict_from_instance(self.object,PlannedSession) - url = reverse(plannedsessions_view) + context['psdict'] = psdict - return HttpResponseRedirect(url) - -@login_required() -def plannedsession_deleteconfirm_view(request,id=0): + context['attrs'] = ['name','startdate','enddate','sessiontype'] - r = getrower(request.user) + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url': reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Sessions' + }, + { + 'url':reverse(plannedsession_view, + kwargs={ + 'userid':userid, + 'id':self.object.pk, + } + ), + 'name': self.object.pk + }, + { + 'url':reverse('plannedsession_delete_view', + kwargs={'pk':self.object.pk}), + 'name': 'Delete' + } + ] - try: - ps = PlannedSession.objects.get(id=id) - except PlannedSession.DoesNotExist: - raise Http404("Planned Session does not exist") + context['breadcrumbs'] = breadcrumbs + return context - if ps.manager != request.user: - raise PermissionDenied("You are not allowed to delete this planned session") + def get_success_url(self): + ws = Workout.objects.filter(plannedsession=self.object) + for w in ws: + w.plannedsession = None + w.save() + + url = reverse(plannedsessions_view) + + return url + + def get_object(self, *args, **kwargs): + obj = super(PlannedSessionDelete, self).get_object(*args, **kwargs) + m = Rower.objects.get(user=obj.manager) + if not checkaccessuser(self.request.user,m): + raise PermissionDenied('You are not allowed to delete this planned session') + + return obj - - psdict = my_dict_from_instance(ps,PlannedSession) - - return render(request,'plannedsessiondeleteconfirm.html', - { - 'ps':ps, - 'psdict': psdict, - 'attrs':[ - 'name','startdate','enddate','sessiontype', - ], - 'rower':r, - } - - ) def virtualevents_view(request): is_ajax = False @@ -13626,6 +15150,7 @@ def virtualevents_view(request): return render(request,'virtualevents.html', { 'races':races, 'form':form, + 'active':'nav-racing', 'rower':r, } ) @@ -13757,6 +15282,7 @@ def virtualevent_view(request,id=0): 'dns':dns, 'records':records, 'form':form, + 'active':'nav-racing', }) @login_required() @@ -13901,8 +15427,8 @@ def virtualevent_addboat_view(request,id=0): { 'form':form, 'race':race, - 'rowerid':r.id, - + 'userid':r.user.id, + 'active': 'nav-racing', }) @login_required() @@ -13994,7 +15520,7 @@ def virtualevent_register_view(request,id=0): { 'form':form, 'race':race, - 'rowerid':r.id, + 'userid':r.user.id, }) @@ -14116,6 +15642,7 @@ def virtualevent_create_view(request): { 'form':racecreateform, 'rower':r, + 'active':'nav-racing', }) @@ -14305,6 +15832,7 @@ def virtualevent_submit_result_view(request,id=0): { 'race':race, 'workouts':ws, + 'active':'nav-racing', 'rower':r, 'w_form':w_form, }) @@ -14355,12 +15883,27 @@ def rower_create_trainingplan(request,userid=0): plans = TrainingPlan.objects.filter(rower=therower).order_by("-startdate") form = TrainingPlanForm(targets=targets) - + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url':reverse(rower_create_trainingplan, + kwargs={'userid':userid}), + 'name': 'Manage Plans and Targets' + } + ] + + return render(request,'trainingplan_create.html', { 'form':form, 'rower':therower, + 'breadcrumbs':breadcrumbs, 'plans':plans, + 'active':'nav-plan', 'targets':targets, 'targetform':targetform, }) @@ -14374,7 +15917,7 @@ def rower_delete_trainingtarget(request,id=0): if checkaccessuser(request.user,target.rower): target.delete() - messages.info(request,"We have deleted the training plan") + messages.info(request,"We have deleted the training target") else: raise PermissionDenied("Access denied") @@ -14416,12 +15959,59 @@ class MicroCycleDelete(DeleteView): model = TrainingMicroCycle template_name = 'trainingplan_delete.html' + # extra parameters + def get_context_data(self, **kwargs): + context = super(MicroCycleDelete, self).get_context_data(**kwargs) + + if 'userid' in kwargs: + userid = kwargs['userid'] + else: + userid=0 + + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url':reverse(rower_trainingplan_view, + kwargs={'userid':userid, + 'id':self.object.plan.plan.plan.id}), + 'name': self.object.plan.plan.plan.name + }, + { + 'url':reverse('macrocycle_update_view', + kwargs={'pk':self.object.plan.plan.pk}), + 'name': self.object.plan.plan.name + }, + { + 'url':reverse('mesocycle_update_view', + kwargs={'pk':self.object.plan.pk}), + 'name': self.object.plan.name + }, + { + 'url':reverse('microcycle_update_view', + kwargs={'pk':self.object.pk}), + 'name': self.object.name + } + + ] + + context['active'] = 'nav-plan' + context['breadcrumbs'] = breadcrumbs + context['rower'] = getrequestrower(self.request,userid=userid) + + return context + def get_success_url(self): plan = self.object.plan.plan.plan createmacrofillers(plan) + thismesoid = self.object.plan.pk return reverse(rower_trainingplan_view, kwargs = { - 'id':plan.id + 'id':plan.id, + 'thismesoid':thismesoid }) def get_object(self, *args, **kwargs): @@ -14435,12 +16025,54 @@ class MesoCycleDelete(DeleteView): model = TrainingMesoCycle template_name = 'trainingplan_delete.html' + # extra parameters + def get_context_data(self, **kwargs): + context = super(MesoCycleDelete, self).get_context_data(**kwargs) + + if 'userid' in kwargs: + userid = kwargs['userid'] + else: + userid=0 + + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url':reverse(rower_trainingplan_view, + kwargs={'userid':userid, + 'id':self.object.plan.plan.id}), + 'name': self.object.plan.plan.name + }, + { + 'url':reverse('macrocycle_update_view', + kwargs={'pk':self.object.plan.pk}), + 'name': self.object.plan.name + }, + { + 'url':reverse('mesocycle_update_view', + kwargs={'pk':self.object.pk}), + 'name': self.object.name + } + + ] + + context['active'] = 'nav-plan' + context['breadcrumbs'] = breadcrumbs + context['rower'] = getrequestrower(self.request,userid=userid) + + return context + def get_success_url(self): plan = self.object.plan.plan + thismacroid = self.object.plan.pk createmacrofillers(plan) return reverse(rower_trainingplan_view, kwargs = { - 'id':plan.id + 'id':plan.id, + 'thismacroid':thismacroid, }) def get_object(self, *args, **kwargs): @@ -14450,11 +16082,143 @@ class MesoCycleDelete(DeleteView): return obj +class GraphDelete(DeleteView): + login_required = True + model = GraphImage + template_name = 'graphimage_delete_confirm.html' + + # extra parameters + def get_context_data(self, **kwargs): + context = super(GraphDelete, self).get_context_data(**kwargs) + + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(self.request,self.object.workout.id), + 'name': str(self.object.workout.id) + }, + { + 'url':reverse(graph_show_view,kwargs={'id':self.object.pk}), + 'name': 'Chart' + }, + { 'url':reverse('graph_delete',kwargs={'pk':str(self.object.pk)}), + 'name': 'Delete' + } + + ] + + context['active'] = 'nav-workouts' + context['rower'] = getrower(self.request.user) + context['breadcrumbs'] = breadcrumbs + + return context + + + def get_success_url(self): + w = self.object.workout + return reverse(workout_edit_view,kwargs={'id':str(w.id)}) + + def get_object(self, *args, **kwargs): + obj = super(GraphDelete, self).get_object(*args, **kwargs) + if not checkaccessuser(self.request.user,obj.workout.user): + raise PermissionDenied('You are not allowed to delete this chart') + + return obj + +class WorkoutDelete(DeleteView): + login_required = True + model = Workout + template_name = 'workout_delete_confirm.html' + + # extra parameters + def get_context_data(self, **kwargs): + context = super(WorkoutDelete, self).get_context_data(**kwargs) + + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(self.request,self.object.id), + 'name': str(self.object.id) + }, + { 'url':reverse('workout_delete',kwargs={'pk':str(self.object.pk)}), + 'name': 'Delete' + } + + ] + + mayedit=0 + if not self.request.user.is_anonymous(): + r = getrower(self.request.user) + result = self.request.user.is_authenticated() and ispromember(self.request.user) + if result: + promember=1 + if self.request.user == self.object.user.user: + mayedit=1 + + context['active'] = 'nav-workouts' + context['rower'] = getrower(self.request.user) + context['breadcrumbs'] = breadcrumbs + context['workout'] = self.object + context['mayedit'] = mayedit + context['promember'] = promember + + return context + + + def get_success_url(self): + return reverse(workouts_view) + + def get_object(self, *args, **kwargs): + obj = super(WorkoutDelete, self).get_object(*args, **kwargs) + if not checkaccessuser(self.request.user,obj.user): + raise PermissionDenied('You are not allowed to delete this workout') + + return obj class MacroCycleDelete(DeleteView): model = TrainingMacroCycle template_name = 'trainingplan_delete.html' + # extra parameters + def get_context_data(self, **kwargs): + context = super(MacroCycleDelete, self).get_context_data(**kwargs) + + if 'userid' in kwargs: + userid = kwargs['userid'] + else: + userid=0 + + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url':reverse(rower_trainingplan_view, + kwargs={'userid':userid, + 'id':self.object.plan.id}), + 'name': self.object.plan.name + }, + { + 'url':reverse('macrocycle_update_view', + kwargs={'pk':self.object.pk}), + 'name': self.object.name + } + ] + + context['active'] = 'nav-plan' + context['breadcrumbs'] = breadcrumbs + context['rower'] = getrequestrower(self.request,userid=userid) + + return context + def get_success_url(self): plan = self.object.plan createmacrofillers(plan) @@ -14474,11 +16238,25 @@ class MacroCycleDelete(DeleteView): @user_passes_test(hasplannedsessions,login_url="/", redirect_field_name=None) -def rower_trainingplan_view(request,id=0): +def rower_trainingplan_view(request, + id=0, + userid=0, + thismicroid=0, + thismacroid=0, + thismesoid=0): + + when = request.GET.get('when') + if when: + startdate,enddate = get_dates_timeperiod(when) + else: + startdate = datetime.date.today() + try: plan = TrainingPlan.objects.get(id=id) except TrainingPlan.DoesNotExist: raise Http404("Training Plan Does Not Exist") + + r = getrequestrower(request,userid=userid) if not checkaccessuser(request.user,plan.rower): raise PermissionDenied("Access denied") @@ -14605,11 +16383,50 @@ def rower_trainingplan_view(request,id=0): cycles[count] = (m,mesos) count += 1 + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url':reverse(rower_trainingplan_view, + kwargs={'userid':userid, + 'id':id}), + 'name': plan.name + } + ] + + if thismicroid: + thismicro = TrainingMicroCycle.objects.get(id=int(thismicroid)) + else: + if not thismacroid and not thismesoid: + thismicro = get_todays_micro(plan,thedate=startdate) + else: + thismicro = None + + if thismacroid: + thismacro = TrainingMacroCycle.objects.get(id=int(thismacroid)) + else: + thismacro = None + + if thismesoid: + thismeso = TrainingMesoCycle.objects.get(id=int(thismesoid)) + else: + thismeso = None + + return render(request,'trainingplan.html', { 'plan':plan, + 'active':'nav-plan', + 'breadcrumbs':breadcrumbs, + 'rower':r, 'cycles':cycles, + 'thismicro':thismicro, + 'thismacro':thismacro, + 'thismeso':thismeso, } ) @@ -14618,13 +16435,47 @@ class TrainingMacroCycleUpdate(UpdateView): template_name = 'trainingplan_edit.html' form_class = TrainingMacroCycleForm + # extra parameters + def get_context_data(self, **kwargs): + context = super(TrainingMacroCycleUpdate, self).get_context_data(**kwargs) + + if 'userid' in kwargs: + userid = kwargs['userid'] + else: + userid=0 + + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url':reverse(rower_trainingplan_view, + kwargs={'userid':userid, + 'id':self.object.plan.id}), + 'name': self.object.plan.name + }, + { + 'url':reverse('macrocycle_update_view', + kwargs={'pk':self.object.pk}), + 'name': self.object.name + } + ] + + context['active'] = 'nav-plan' + context['breadcrumbs'] = breadcrumbs + context['rower'] = getrequestrower(self.request,userid=userid) + + return context def get_success_url(self): plan = self.object.plan createmacrofillers(plan) return reverse(rower_trainingplan_view, kwargs = { - 'id':plan.id + 'id':plan.id, + 'thismacro':self.object.id, } ) @@ -14649,13 +16500,53 @@ class TrainingMesoCycleUpdate(UpdateView): template_name = 'trainingplan_edit.html' form_class = TrainingMesoCycleForm + # extra parameters + def get_context_data(self, **kwargs): + context = super(TrainingMesoCycleUpdate, self).get_context_data(**kwargs) + + if 'userid' in kwargs: + userid = kwargs['userid'] + else: + userid=0 + + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url':reverse(rower_trainingplan_view, + kwargs={'userid':userid, + 'id':self.object.plan.plan.id}), + 'name': self.object.plan.plan.name + }, + { + 'url':reverse('macrocycle_update_view', + kwargs={'pk':self.object.plan.pk}), + 'name': self.object.plan.name + }, + { + 'url':reverse('mesocycle_update_view', + kwargs={'pk':self.object.pk}), + 'name': self.object.name + } + + ] + + context['active'] = 'nav-plan' + context['breadcrumbs'] = breadcrumbs + context['rower'] = getrequestrower(self.request,userid=userid) + + return context def get_success_url(self): plan = self.object.plan createmesofillers(plan) return reverse(rower_trainingplan_view, kwargs = { - 'id':plan.plan.id + 'id':plan.plan.id, + 'thismesoid':self.object.id, } ) @@ -14683,13 +16574,58 @@ class TrainingMicroCycleUpdate(UpdateView): template_name = 'trainingplan_edit.html' form_class = TrainingMicroCycleForm + # extra parameters + def get_context_data(self, **kwargs): + context = super(TrainingMicroCycleUpdate, self).get_context_data(**kwargs) + + if 'userid' in kwargs: + userid = kwargs['userid'] + else: + userid=0 + + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url':reverse(rower_trainingplan_view, + kwargs={'userid':userid, + 'id':self.object.plan.plan.plan.id}), + 'name': self.object.plan.plan.plan.name + }, + { + 'url':reverse('macrocycle_update_view', + kwargs={'pk':self.object.plan.plan.pk}), + 'name': self.object.plan.plan.name + }, + { + 'url':reverse('mesocycle_update_view', + kwargs={'pk':self.object.plan.pk}), + 'name': self.object.plan.name + }, + { + 'url':reverse('microcycle_update_view', + kwargs={'pk':self.object.pk}), + 'name': self.object.name + } + + ] + + context['active'] = 'nav-plan' + context['breadcrumbs'] = breadcrumbs + context['rower'] = getrequestrower(self.request,userid=userid) + + return context def get_success_url(self): plan = self.object.plan createmicrofillers(plan) return reverse(rower_trainingplan_view, kwargs = { - 'id':plan.plan.plan.id + 'id':plan.plan.plan.id, + 'thismicroid':self.object.pk } ) def form_valid(self, form): diff --git a/rowsandall_workouts_2017-09-28_2018-09-28.csv b/rowsandall_workouts_2017-09-28_2018-09-28.csv new file mode 100644 index 00000000..b767fb45 --- /dev/null +++ b/rowsandall_workouts_2017-09-28_2018-09-28.csv @@ -0,0 +1,434 @@ +,Stroke Data CSV,Stroke Data TCX,TRIMP Training Load,TSS Training Load,date,distance (m),duration ,name,notes,timezone,type,weight (kg),weight category +0,https://rowsandall.com/rowers/workout/2542/emailcsv,https://rowsandall.com/rowers/workout/2542/emailtcx,0,0,1970-01-01 00:00:00+00:00,9578,00:47:55.400000,Test Auto Sync Pro User,,Europe/Prague,rower,80.0,lwt +1,https://rowsandall.com/rowers/workout/2500/emailcsv,https://rowsandall.com/rowers/workout/2500/emailtcx,0,0,2014-02-26 20:32:33+00:00,65,00:00:02,Sample Workout JSON,,UTC,water,80.0,lwt +2,https://rowsandall.com/rowers/workout/3033/emailcsv,https://rowsandall.com/rowers/workout/3033/emailtcx,95,264,2015-06-02 06:29:02+00:00,14002,00:59:37.700000,Test P,,Europe/Prague,water,80.0,hwt +3,https://rowsandall.com/rowers/workout/2501/emailcsv,https://rowsandall.com/rowers/workout/2501/emailtcx,0,0,2016-04-02 08:15:08+00:00,10269,01:05:24,Completed a workout (generic) 6.35 mi on 04/02/16,,Europe/Prague,water,80.0,lwt +4,https://rowsandall.com/rowers/workout/2408/emailcsv,https://rowsandall.com/rowers/workout/2408/emailtcx,0,0,2016-07-30 08:40:36+00:00,1001,00:03:41,Comparison,,Europe/Berlin,rower,80.0,lwt +5,https://rowsandall.com/rowers/workout/3032/emailcsv,https://rowsandall.com/rowers/workout/3032/emailtcx,0,0,2016-07-31 12:35:01+00:00,1111,00:04:32.700000,,,Europe/Prague,water,80.0,hwt +6,https://rowsandall.com/rowers/workout/2572/emailcsv,https://rowsandall.com/rowers/workout/2572/emailtcx,0,0,2016-11-28 08:37:02+00:00,499,00:01:58,BC,,Europe/Prague,rower,80.0,lwt +7,https://rowsandall.com/rowers/workout/2379/emailcsv,https://rowsandall.com/rowers/workout/2379/emailtcx,0,0,2017-10-27 16:14:42+00:00,742,00:02:25,Test Weba,,Europe/Prague,water,80.0,lwt +8,https://rowsandall.com/rowers/workout/2380/emailcsv,https://rowsandall.com/rowers/workout/2380/emailtcx,0,0,2017-10-27 16:14:42+00:00,742,00:02:25,Test Weba,,Europe/Belgrade,water,80.0,lwt +9,https://rowsandall.com/rowers/workout/2521/emailcsv,https://rowsandall.com/rowers/workout/2521/emailtcx,0,0,2017-11-09 09:15:32+00:00,49842,02:19:37,Import from Polar Flow,imported through email,Europe/Helsinki,water,80.0,lwt +10,https://rowsandall.com/rowers/workout/2524/emailcsv,https://rowsandall.com/rowers/workout/2524/emailtcx,0,0,2017-11-09 09:15:32+00:00,49842,02:19:37,Import from Polar Flow,imported through email,Europe/Helsinki,water,80.0,lwt +11,https://rowsandall.com/rowers/workout/2527/emailcsv,https://rowsandall.com/rowers/workout/2527/emailtcx,0,0,2017-11-09 09:15:32+00:00,49842,02:19:37,Import from Polar Flow,imported through email,Europe/Helsinki,water,80.0,lwt +12,https://rowsandall.com/rowers/workout/2531/emailcsv,https://rowsandall.com/rowers/workout/2531/emailtcx,0,0,2017-11-09 09:15:32+00:00,49842,02:19:37,Import from Polar Flow,imported through email,Europe/Helsinki,water,80.0,lwt +13,https://rowsandall.com/rowers/workout/2523/emailcsv,https://rowsandall.com/rowers/workout/2523/emailtcx,0,0,2017-11-09 15:15:32+00:00,0,02:28:28,Import from Polar Flow,imported through email,Europe/Helsinki,water,80.0,lwt +14,https://rowsandall.com/rowers/workout/2526/emailcsv,https://rowsandall.com/rowers/workout/2526/emailtcx,0,0,2017-11-09 15:15:32+00:00,0,02:28:28,Import from Polar Flow,imported through email,Europe/Helsinki,water,80.0,lwt +15,https://rowsandall.com/rowers/workout/2529/emailcsv,https://rowsandall.com/rowers/workout/2529/emailtcx,0,0,2017-11-09 15:15:32+00:00,0,02:28:28,Import from Polar Flow,imported through email,Europe/Helsinki,water,80.0,lwt +16,https://rowsandall.com/rowers/workout/2533/emailcsv,https://rowsandall.com/rowers/workout/2533/emailtcx,0,0,2017-11-09 15:15:32+00:00,0,02:28:28,Import from Polar Flow,imported through email,Europe/Helsinki,water,80.0,lwt +17,https://rowsandall.com/rowers/workout/2520/emailcsv,https://rowsandall.com/rowers/workout/2520/emailtcx,0,0,2017-11-09 18:15:32+00:00,10016,01:06:03,Polar Import,,Europe/Helsinki,rower,80.0,lwt +18,https://rowsandall.com/rowers/workout/2522/emailcsv,https://rowsandall.com/rowers/workout/2522/emailtcx,0,0,2017-11-09 18:15:32+00:00,10016,01:06:03,Import from Polar Flow,imported through email,Europe/Helsinki,water,80.0,lwt +19,https://rowsandall.com/rowers/workout/2525/emailcsv,https://rowsandall.com/rowers/workout/2525/emailtcx,0,0,2017-11-09 18:15:32+00:00,10016,01:06:03,Import from Polar Flow,imported through email,Europe/Helsinki,water,80.0,lwt +20,https://rowsandall.com/rowers/workout/2528/emailcsv,https://rowsandall.com/rowers/workout/2528/emailtcx,0,0,2017-11-09 18:15:32+00:00,10016,01:06:03,Import from Polar Flow,imported through email,Europe/Helsinki,water,80.0,lwt +21,https://rowsandall.com/rowers/workout/2532/emailcsv,https://rowsandall.com/rowers/workout/2532/emailtcx,0,0,2017-11-09 18:15:32+00:00,10016,01:06:03,Import from Polar Flow,imported through email,Europe/Helsinki,water,80.0,lwt +22,https://rowsandall.com/rowers/workout/2494/emailcsv,https://rowsandall.com/rowers/workout/2494/emailtcx,0,0,2017-11-12 10:25:00+00:00,10526,00:44:01.700000,Race,,Europe/Rome,water,80.0,lwt +23,https://rowsandall.com/rowers/workout/2496/emailcsv,https://rowsandall.com/rowers/workout/2496/emailtcx,0,0,2017-11-12 10:25:00+00:00,10526,00:44:01.700000,Race,,Europe/Rome,water,80.0,lwt +24,https://rowsandall.com/rowers/workout/2497/emailcsv,https://rowsandall.com/rowers/workout/2497/emailtcx,0,0,2017-11-12 10:25:00+00:00,10526,00:44:01.700000,SpdCoach 2,,Europe/Rome,rower,80.0,lwt +25,https://rowsandall.com/rowers/workout/2498/emailcsv,https://rowsandall.com/rowers/workout/2498/emailtcx,0,0,2017-11-12 10:25:00+00:00,10526,00:44:01.700000,boat speed test,,Europe/Rome,water,80.0,lwt +26,https://rowsandall.com/rowers/workout/2499/emailcsv,https://rowsandall.com/rowers/workout/2499/emailtcx,0,0,2017-11-12 10:25:00+00:00,10526,00:44:01.700000,Boat Speed test 2,,Europe/Rome,water,80.0,lwt +27,https://rowsandall.com/rowers/workout/2724/emailcsv,https://rowsandall.com/rowers/workout/2724/emailtcx,0,0,2018-01-01 09:27:47+00:00,5000,00:18:37,C2 Import Workout from 2018-01-01 09:27:47+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +28,https://rowsandall.com/rowers/workout/2723/emailcsv,https://rowsandall.com/rowers/workout/2723/emailtcx,0,0,2018-01-01 09:48:09+00:00,2963,00:13:29,C2 Import Workout from 2018-01-01 09:48:09+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +29,https://rowsandall.com/rowers/workout/2722/emailcsv,https://rowsandall.com/rowers/workout/2722/emailtcx,0,0,2018-01-02 18:22:08+00:00,14183,00:59:49.900000,C2 Import Workout from 2018-01-02 18:22:08+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +30,https://rowsandall.com/rowers/workout/2721/emailcsv,https://rowsandall.com/rowers/workout/2721/emailtcx,0,0,2018-01-03 16:24:47+00:00,4821,00:20:04.500000,C2 Import Workout from 2018-01-03 16:24:47+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +31,https://rowsandall.com/rowers/workout/2720/emailcsv,https://rowsandall.com/rowers/workout/2720/emailtcx,0,0,2018-01-03 16:46:19+00:00,2000,00:07:07.500000,C2 Import Workout from 2018-01-03 16:46:19+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +32,https://rowsandall.com/rowers/workout/2719/emailcsv,https://rowsandall.com/rowers/workout/2719/emailtcx,0,0,2018-01-03 16:57:04+00:00,2997,00:13:35.100000,C2 Import Workout from 2018-01-03 16:57:04+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +33,https://rowsandall.com/rowers/workout/2718/emailcsv,https://rowsandall.com/rowers/workout/2718/emailtcx,0,0,2018-01-06 13:59:30+00:00,4722,00:20:06.200000,C2 Import Workout from 2018-01-06 13:59:30+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +34,https://rowsandall.com/rowers/workout/2717/emailcsv,https://rowsandall.com/rowers/workout/2717/emailtcx,0,0,2018-01-06 14:20:43+00:00,1171,00:04:00,C2 Import Workout from 2018-01-06 14:20:43+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +35,https://rowsandall.com/rowers/workout/2716/emailcsv,https://rowsandall.com/rowers/workout/2716/emailtcx,0,0,2018-01-06 14:25:53+00:00,8006,00:36:13.600000,C2 Import Workout from 2018-01-06 14:25:53+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +36,https://rowsandall.com/rowers/workout/2715/emailcsv,https://rowsandall.com/rowers/workout/2715/emailtcx,0,0,2018-01-07 14:03:51+00:00,2520,00:10:42.700000,C2 Import Workout from 2018-01-07 14:03:51+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +37,https://rowsandall.com/rowers/workout/2714/emailcsv,https://rowsandall.com/rowers/workout/2714/emailtcx,0,0,2018-01-07 14:18:45+00:00,9996,00:42:35.100000,C2 Import Workout from 2018-01-07 14:18:45+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +38,https://rowsandall.com/rowers/workout/2713/emailcsv,https://rowsandall.com/rowers/workout/2713/emailtcx,0,0,2018-01-09 18:22:19+00:00,5267,00:22:34.200000,C2 Import Workout from 2018-01-09 18:22:19+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +39,https://rowsandall.com/rowers/workout/2712/emailcsv,https://rowsandall.com/rowers/workout/2712/emailtcx,0,0,2018-01-09 18:46:20+00:00,1000,00:03:21.400000,C2 Import Workout from 2018-01-09 18:46:20+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +40,https://rowsandall.com/rowers/workout/2711/emailcsv,https://rowsandall.com/rowers/workout/2711/emailtcx,0,0,2018-01-09 18:51:03+00:00,2300,00:10:04.500000,C2 Import Workout from 2018-01-09 18:51:03+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +41,https://rowsandall.com/rowers/workout/2710/emailcsv,https://rowsandall.com/rowers/workout/2710/emailtcx,0,0,2018-01-12 16:09:10+00:00,2008,00:08:39.100000,C2 Import Workout from 2018-01-12 16:09:10+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +42,https://rowsandall.com/rowers/workout/2709/emailcsv,https://rowsandall.com/rowers/workout/2709/emailtcx,0,0,2018-01-12 16:19:00+00:00,500,00:01:33.400000,C2 Import Workout from 2018-01-12 16:19:00+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +43,https://rowsandall.com/rowers/workout/2708/emailcsv,https://rowsandall.com/rowers/workout/2708/emailtcx,0,0,2018-01-12 16:21:06+00:00,2002,00:09:25.600000,C2 Import Workout from 2018-01-12 16:21:06+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +44,https://rowsandall.com/rowers/workout/2707/emailcsv,https://rowsandall.com/rowers/workout/2707/emailtcx,0,0,2018-01-12 16:31:08+00:00,100,00:00:17.600000,C2 Import Workout from 2018-01-12 16:31:08+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +45,https://rowsandall.com/rowers/workout/2706/emailcsv,https://rowsandall.com/rowers/workout/2706/emailtcx,0,0,2018-01-12 16:31:58+00:00,2011,00:08:56.600000,C2 Import Workout from 2018-01-12 16:31:58+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +46,https://rowsandall.com/rowers/workout/2705/emailcsv,https://rowsandall.com/rowers/workout/2705/emailtcx,0,0,2018-01-13 12:40:00+00:00,2000,00:06:55.800000,C2 Import Workout from 2018-01-13 12:40:00+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +47,https://rowsandall.com/rowers/workout/2704/emailcsv,https://rowsandall.com/rowers/workout/2704/emailtcx,0,0,2018-01-16 18:40:49+00:00,2007,00:08:30.800000,C2 Import Workout from 2018-01-16 18:40:49+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +48,https://rowsandall.com/rowers/workout/2703/emailcsv,https://rowsandall.com/rowers/workout/2703/emailtcx,0,0,2018-01-16 18:50:09+00:00,333,00:01:00,C2 Import Workout from 2018-01-16 18:50:09+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +49,https://rowsandall.com/rowers/workout/2702/emailcsv,https://rowsandall.com/rowers/workout/2702/emailtcx,0,0,2018-01-16 18:51:41+00:00,6820,00:31:22.200000,C2 Import Workout from 2018-01-16 18:51:41+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +50,https://rowsandall.com/rowers/workout/2701/emailcsv,https://rowsandall.com/rowers/workout/2701/emailtcx,0,0,2018-01-16 19:24:23+00:00,2008,00:09:01.100000,C2 Import Workout from 2018-01-16 19:24:23+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +51,https://rowsandall.com/rowers/workout/2700/emailcsv,https://rowsandall.com/rowers/workout/2700/emailtcx,0,0,2018-01-18 19:01:45+00:00,2007,00:08:27.600000,C2 Import Workout from 2018-01-18 19:01:45+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +52,https://rowsandall.com/rowers/workout/2699/emailcsv,https://rowsandall.com/rowers/workout/2699/emailtcx,0,0,2018-01-18 19:10:50+00:00,6918,00:29:14.200000,C2 Import Workout from 2018-01-18 19:10:50+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +53,https://rowsandall.com/rowers/workout/2698/emailcsv,https://rowsandall.com/rowers/workout/2698/emailtcx,0,0,2018-01-18 19:40:40+00:00,1217,00:06:11.100000,C2 Import Workout from 2018-01-18 19:40:40+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +54,https://rowsandall.com/rowers/workout/2697/emailcsv,https://rowsandall.com/rowers/workout/2697/emailtcx,0,0,2018-01-18 19:47:32+00:00,6697,00:29:16.700000,C2 Import Workout from 2018-01-18 19:47:32+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +55,https://rowsandall.com/rowers/workout/2696/emailcsv,https://rowsandall.com/rowers/workout/2696/emailtcx,0,0,2018-01-18 20:17:58+00:00,1997,00:09:10.700000,C2 Import Workout from 2018-01-18 20:17:58+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +56,https://rowsandall.com/rowers/workout/2692/emailcsv,https://rowsandall.com/rowers/workout/2692/emailtcx,0,0,2018-01-19 00:00:00+00:00,200,00:02:00.200000,C2 Import Workout from 2018-01-19 00:00:00+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +57,https://rowsandall.com/rowers/workout/2693/emailcsv,https://rowsandall.com/rowers/workout/2693/emailtcx,0,0,2018-01-19 00:00:00+00:00,200,00:01:55,C2 Import Workout from 2018-01-19 00:00:00+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +58,https://rowsandall.com/rowers/workout/2694/emailcsv,https://rowsandall.com/rowers/workout/2694/emailtcx,0,0,2018-01-19 00:00:00+00:00,200,00:02:00,C2 Import Workout from 2018-01-19 00:00:00+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +59,https://rowsandall.com/rowers/workout/2695/emailcsv,https://rowsandall.com/rowers/workout/2695/emailtcx,0,0,2018-01-19 00:00:00+00:00,200,00:01:10,C2 Import Workout from 2018-01-19 00:00:00+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +60,https://rowsandall.com/rowers/workout/2691/emailcsv,https://rowsandall.com/rowers/workout/2691/emailtcx,0,0,2018-01-20 13:22:31+00:00,19381,01:20:39.100000,C2 Import Workout from 2018-01-20 13:22:31+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +61,https://rowsandall.com/rowers/workout/2690/emailcsv,https://rowsandall.com/rowers/workout/2690/emailtcx,0,0,2018-01-24 18:55:23+00:00,210,00:01:43.400000,C2 Import Workout from 2018-01-24 18:55:23+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +62,https://rowsandall.com/rowers/workout/2689/emailcsv,https://rowsandall.com/rowers/workout/2689/emailtcx,0,0,2018-01-24 18:59:06+00:00,2110,00:08:42.700000,C2 Import Workout from 2018-01-24 18:59:06+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +63,https://rowsandall.com/rowers/workout/2688/emailcsv,https://rowsandall.com/rowers/workout/2688/emailtcx,0,0,2018-01-24 19:08:04+00:00,6081,00:25:10,C2 Import Workout from 2018-01-24 19:08:04+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +64,https://rowsandall.com/rowers/workout/2687/emailcsv,https://rowsandall.com/rowers/workout/2687/emailtcx,0,0,2018-01-24 19:24:18+00:00,14426,01:00:12.600000,C2 Import Workout from 2018-01-24 19:24:18+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +65,https://rowsandall.com/rowers/workout/2686/emailcsv,https://rowsandall.com/rowers/workout/2686/emailtcx,0,0,2018-01-27 13:23:26+00:00,14981,01:01:39.600000,C2 Import Workout from 2018-01-27 13:23:26+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +66,https://rowsandall.com/rowers/workout/2685/emailcsv,https://rowsandall.com/rowers/workout/2685/emailtcx,0,0,2018-01-27 14:25:07+00:00,18338,01:15:22.800000,C2 Import Workout from 2018-01-27 14:25:07+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +67,https://rowsandall.com/rowers/workout/2684/emailcsv,https://rowsandall.com/rowers/workout/2684/emailtcx,0,0,2018-02-03 14:20:18+00:00,12181,00:51:46,C2 Import Workout from 2018-02-03 14:20:18+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +68,https://rowsandall.com/rowers/workout/2683/emailcsv,https://rowsandall.com/rowers/workout/2683/emailtcx,0,0,2018-02-06 17:54:01+00:00,7210,00:31:09,C2 Import Workout from 2018-02-06 17:54:01+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +69,https://rowsandall.com/rowers/workout/2682/emailcsv,https://rowsandall.com/rowers/workout/2682/emailtcx,0,0,2018-02-10 09:10:14+00:00,3040,00:12:47.200000,C2 Import Workout from 2018-02-10 09:10:14+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +70,https://rowsandall.com/rowers/workout/2681/emailcsv,https://rowsandall.com/rowers/workout/2681/emailtcx,0,0,2018-02-10 09:23:52+00:00,9305,00:40:50.900000,C2 Import Workout from 2018-02-10 09:23:52+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +71,https://rowsandall.com/rowers/workout/2680/emailcsv,https://rowsandall.com/rowers/workout/2680/emailtcx,0,0,2018-02-10 10:06:00+00:00,2009,00:09:04.200000,C2 Import Workout from 2018-02-10 10:06:00+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +72,https://rowsandall.com/rowers/workout/2679/emailcsv,https://rowsandall.com/rowers/workout/2679/emailtcx,0,0,2018-02-11 16:57:32+00:00,14430,01:01:23,C2 Import Workout from 2018-02-11 16:57:32+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +73,https://rowsandall.com/rowers/workout/2335/emailcsv,https://rowsandall.com/rowers/workout/2335/emailtcx,0,0,2018-02-12 05:02:06+00:00,3872,00:27:12.200000,SpdCoach 1,,America/Los_Angeles,water,80.0,lwt +74,https://rowsandall.com/rowers/workout/2338/emailcsv,https://rowsandall.com/rowers/workout/2338/emailtcx,0,0,2018-02-12 05:02:06+00:00,9336,09:58:35,Joined Workout,,America/Los_Angeles,water,80.0,lwt +75,https://rowsandall.com/rowers/workout/2339/emailcsv,https://rowsandall.com/rowers/workout/2339/emailtcx,0,0,2018-02-12 05:02:06+00:00,7522,09:41:48.200000,Joined Workout,,America/Los_Angeles,water,80.0,lwt +76,https://rowsandall.com/rowers/workout/2341/emailcsv,https://rowsandall.com/rowers/workout/2341/emailtcx,0,0,2018-02-12 05:02:06+00:00,9336,09:58:35,Joined Workout,,America/Los_Angeles,water,80.0,lwt +77,https://rowsandall.com/rowers/workout/2342/emailcsv,https://rowsandall.com/rowers/workout/2342/emailtcx,0,0,2018-02-12 05:02:06+00:00,9336,09:58:35,Joined Workout,,America/Los_Angeles,water,80.0,lwt +78,https://rowsandall.com/rowers/workout/2343/emailcsv,https://rowsandall.com/rowers/workout/2343/emailtcx,0,0,2018-02-12 05:02:06+00:00,9336,09:58:35,Joined Workout,,America/Los_Angeles,water,80.0,lwt +79,https://rowsandall.com/rowers/workout/2410/emailcsv,https://rowsandall.com/rowers/workout/2410/emailtcx,0,0,2018-02-12 05:02:06+00:00,3872,00:27:12.200000,Test,,America/Los_Angeles,water,80.0,lwt +80,https://rowsandall.com/rowers/workout/2414/emailcsv,https://rowsandall.com/rowers/workout/2414/emailtcx,0,0,2018-02-12 05:02:06+00:00,9336,09:58:35,Joined Workout,,America/Los_Angeles,water,80.0,lwt +81,https://rowsandall.com/rowers/workout/2416/emailcsv,https://rowsandall.com/rowers/workout/2416/emailtcx,0,0,2018-02-12 05:02:06+00:00,3872,00:27:12.200000,Test aaaa,,America/Los_Angeles,water,80.0,lwt +82,https://rowsandall.com/rowers/workout/2417/emailcsv,https://rowsandall.com/rowers/workout/2417/emailtcx,0,0,2018-02-12 05:02:06+00:00,9336,09:58:35,Joined Workout,,America/Los_Angeles,water,80.0,lwt +83,https://rowsandall.com/rowers/workout/2418/emailcsv,https://rowsandall.com/rowers/workout/2418/emailtcx,0,0,2018-02-12 05:02:06+00:00,3872,00:27:12.200000,Test i,,America/Los_Angeles,water,80.0,lwt +84,https://rowsandall.com/rowers/workout/2419/emailcsv,https://rowsandall.com/rowers/workout/2419/emailtcx,0,0,2018-02-12 05:02:06+00:00,11394,09:42:05.500000,Joined Workout,,America/Los_Angeles,water,80.0,lwt +85,https://rowsandall.com/rowers/workout/2420/emailcsv,https://rowsandall.com/rowers/workout/2420/emailtcx,0,0,2018-02-12 13:55:06+00:00,3872,00:27:12.200000,Test ii,,America/Los_Angeles,water,80.0,lwt +86,https://rowsandall.com/rowers/workout/2421/emailcsv,https://rowsandall.com/rowers/workout/2421/emailtcx,0,0,2018-02-12 13:55:06+00:00,350236,00:49:05.500000,Joined Workout,,America/Los_Angeles,water,80.0,lwt +87,https://rowsandall.com/rowers/workout/2344/emailcsv,https://rowsandall.com/rowers/workout/2344/emailtcx,0,0,2018-02-12 14:02:06+00:00,3872,00:27:12.200000,SpdCoach Sessions,imported through email,America/Los_Angeles,water,80.0,lwt +88,https://rowsandall.com/rowers/workout/2347/emailcsv,https://rowsandall.com/rowers/workout/2347/emailtcx,0,0,2018-02-12 14:02:06+00:00,9336,00:58:35,Joined Workout,imported through email,America/Los_Angeles,water,80.0,lwt +89,https://rowsandall.com/rowers/workout/2411/emailcsv,https://rowsandall.com/rowers/workout/2411/emailtcx,0,0,2018-02-12 14:02:06+00:00,3872,00:27:12.200000,Test 2,,America/Los_Angeles,water,80.0,lwt +90,https://rowsandall.com/rowers/workout/2415/emailcsv,https://rowsandall.com/rowers/workout/2415/emailtcx,0,0,2018-02-12 14:02:06+00:00,9336,00:58:35,Joined Workout,,America/Los_Angeles,water,80.0,lwt +91,https://rowsandall.com/rowers/workout/2430/emailcsv,https://rowsandall.com/rowers/workout/2430/emailtcx,0,0,2018-02-12 14:02:06+00:00,3872,00:27:12.200000,Water,,America/Los_Angeles,water,80.0,lwt +92,https://rowsandall.com/rowers/workout/2445/emailcsv,https://rowsandall.com/rowers/workout/2445/emailtcx,0,0,2018-02-12 14:02:06+00:00,3872,00:27:12.200000,Test P,imported through email,America/Los_Angeles,water,80.0,lwt +93,https://rowsandall.com/rowers/workout/2449/emailcsv,https://rowsandall.com/rowers/workout/2449/emailtcx,0,0,2018-02-12 14:02:06+00:00,3872,00:27:12.200000,Water 2x,imported through email,America/Los_Angeles,water,80.0,lwt +94,https://rowsandall.com/rowers/workout/2412/emailcsv,https://rowsandall.com/rowers/workout/2412/emailtcx,0,0,2018-02-12 14:22:01+00:00,3650,00:14:53.200000,Test 3,,America/Los_Angeles,water,80.0,lwt +95,https://rowsandall.com/rowers/workout/2336/emailcsv,https://rowsandall.com/rowers/workout/2336/emailtcx,0,0,2018-02-12 14:29:01+00:00,3650,00:14:53.200000,SpdCoach 2," +Summary for your location at 2018-02-12T14:55:00Z: Temperature 9.0C/48.2F. Wind: 2.5 m/s (5.0 kt). Wind Bearing: 280.0 degrees",America/Los_Angeles,water,80.0,lwt +96,https://rowsandall.com/rowers/workout/2340/emailcsv,https://rowsandall.com/rowers/workout/2340/emailtcx,0,0,2018-02-12 14:29:01+00:00,5464,00:31:40,Joined Workout,,America/Los_Angeles,water,80.0,lwt +97,https://rowsandall.com/rowers/workout/2345/emailcsv,https://rowsandall.com/rowers/workout/2345/emailtcx,0,0,2018-02-12 14:29:01+00:00,3650,00:14:53.200000,SpdCoach Sessions (2),imported through email,America/Los_Angeles,water,80.0,lwt +98,https://rowsandall.com/rowers/workout/2433/emailcsv,https://rowsandall.com/rowers/workout/2433/emailtcx,0,0,2018-02-12 14:29:01+00:00,3650,00:14:53.200000,Test,,America/Los_Angeles,rower,80.0,lwt +99,https://rowsandall.com/rowers/workout/2442/emailcsv,https://rowsandall.com/rowers/workout/2442/emailtcx,0,0,2018-02-12 14:29:01+00:00,3650,00:14:53.200000,Water,imported through email,America/Los_Angeles,water,80.0,lwt +100,https://rowsandall.com/rowers/workout/2444/emailcsv,https://rowsandall.com/rowers/workout/2444/emailtcx,0,0,2018-02-12 14:29:01+00:00,3650,00:14:53.200000,SpdCoach 2,imported through email,America/Los_Angeles,water,80.0,lwt +101,https://rowsandall.com/rowers/workout/2337/emailcsv,https://rowsandall.com/rowers/workout/2337/emailtcx,0,0,2018-02-12 14:49:02+00:00,1814,00:11:39,SpdCoach 3,,America/Los_Angeles,water,80.0,lwt +102,https://rowsandall.com/rowers/workout/2346/emailcsv,https://rowsandall.com/rowers/workout/2346/emailtcx,0,0,2018-02-12 14:49:02+00:00,1814,00:11:39,SpdCoach Sessions (3),imported through email,America/Los_Angeles,water,80.0,lwt +103,https://rowsandall.com/rowers/workout/2413/emailcsv,https://rowsandall.com/rowers/workout/2413/emailtcx,0,0,2018-02-12 14:49:02+00:00,1814,00:11:39,Test 4,,America/Los_Angeles,water,80.0,lwt +104,https://rowsandall.com/rowers/workout/2443/emailcsv,https://rowsandall.com/rowers/workout/2443/emailtcx,0,0,2018-02-12 14:49:02+00:00,1814,00:11:39,water weer,imported through email,America/Los_Angeles,water,80.0,lwt +105,https://rowsandall.com/rowers/workout/2678/emailcsv,https://rowsandall.com/rowers/workout/2678/emailtcx,0,0,2018-02-12 18:39:42+00:00,2014,00:08:37.700000,C2 Import Workout from 2018-02-12 18:39:42+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +106,https://rowsandall.com/rowers/workout/2677/emailcsv,https://rowsandall.com/rowers/workout/2677/emailtcx,0,0,2018-02-12 18:49:03+00:00,10562,00:46:12.400000,C2 Import Workout from 2018-02-12 18:49:03+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +107,https://rowsandall.com/rowers/workout/2676/emailcsv,https://rowsandall.com/rowers/workout/2676/emailtcx,0,0,2018-02-12 19:36:43+00:00,1998,00:09:39.800000,C2 Import Workout from 2018-02-12 19:36:43+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +108,https://rowsandall.com/rowers/workout/2675/emailcsv,https://rowsandall.com/rowers/workout/2675/emailtcx,0,0,2018-02-15 18:16:50+00:00,12440,00:51:41.500000,C2 Import Workout from 2018-02-15 18:16:50+00:00,imported from Concept2 log,UTC,rower,80.0,lwt +109,https://rowsandall.com/rowers/workout/2674/emailcsv,https://rowsandall.com/rowers/workout/2674/emailtcx,0,0,2018-02-15 19:08:28+00:00,14477,01:00:21.800000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +110,https://rowsandall.com/rowers/workout/2673/emailcsv,https://rowsandall.com/rowers/workout/2673/emailtcx,0,0,2018-02-16 14:24:03+00:00,3012,00:12:58.800000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +111,https://rowsandall.com/rowers/workout/2672/emailcsv,https://rowsandall.com/rowers/workout/2672/emailtcx,0,0,2018-02-16 14:37:40+00:00,5277,00:23:00,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +112,https://rowsandall.com/rowers/workout/2671/emailcsv,https://rowsandall.com/rowers/workout/2671/emailtcx,0,0,2018-02-16 15:01:29+00:00,1036,00:05:03.600000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +113,https://rowsandall.com/rowers/workout/2670/emailcsv,https://rowsandall.com/rowers/workout/2670/emailtcx,0,0,2018-02-16 15:07:03+00:00,5113,00:23:22,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +114,https://rowsandall.com/rowers/workout/2669/emailcsv,https://rowsandall.com/rowers/workout/2669/emailtcx,0,0,2018-02-16 15:34:40+00:00,1259,00:05:48.600000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +115,https://rowsandall.com/rowers/workout/2668/emailcsv,https://rowsandall.com/rowers/workout/2668/emailtcx,0,0,2018-02-18 13:30:59+00:00,13541,00:56:07.300000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +116,https://rowsandall.com/rowers/workout/2667/emailcsv,https://rowsandall.com/rowers/workout/2667/emailtcx,0,0,2018-02-20 18:08:13+00:00,2008,00:08:36.300000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +117,https://rowsandall.com/rowers/workout/2666/emailcsv,https://rowsandall.com/rowers/workout/2666/emailtcx,0,0,2018-02-20 18:17:17+00:00,10914,00:48:06.100000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +118,https://rowsandall.com/rowers/workout/2665/emailcsv,https://rowsandall.com/rowers/workout/2665/emailtcx,0,0,2018-02-20 19:06:36+00:00,1513,00:07:37.300000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +119,https://rowsandall.com/rowers/workout/2381/emailcsv,https://rowsandall.com/rowers/workout/2381/emailtcx,0,0,2018-02-21 06:26:01+00:00,6119,00:27:20.200000,3x2km Racice,,Europe/Prague,water,80.0,lwt +120,https://rowsandall.com/rowers/workout/2384/emailcsv,https://rowsandall.com/rowers/workout/2384/emailtcx,0,0,2018-02-22 09:32:01+00:00,8000,00:35:08.500000,8k trial,,Europe/Prague,water,80.0,lwt +121,https://rowsandall.com/rowers/workout/2664/emailcsv,https://rowsandall.com/rowers/workout/2664/emailtcx,0,0,2018-02-22 18:40:03+00:00,11481,00:49:17.400000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +122,https://rowsandall.com/rowers/workout/2663/emailcsv,https://rowsandall.com/rowers/workout/2663/emailtcx,0,0,2018-02-23 15:12:10+00:00,2008,00:08:44.800000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +123,https://rowsandall.com/rowers/workout/2383/emailcsv,https://rowsandall.com/rowers/workout/2383/emailtcx,0,0,2018-02-23 15:17:03+00:00,5918,00:25:10.700000,Horin,,Europe/Prague,water,80.0,lwt +124,https://rowsandall.com/rowers/workout/2662/emailcsv,https://rowsandall.com/rowers/workout/2662/emailtcx,0,0,2018-02-23 15:21:44+00:00,8804,00:39:58,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +125,https://rowsandall.com/rowers/workout/2661/emailcsv,https://rowsandall.com/rowers/workout/2661/emailtcx,0,0,2018-02-23 16:02:23+00:00,2008,00:09:05,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +126,https://rowsandall.com/rowers/workout/2385/emailcsv,https://rowsandall.com/rowers/workout/2385/emailtcx,0,0,2018-02-24 11:58:09+00:00,17045,01:35:59,Washington,,America/New_York,water,80.0,lwt +127,https://rowsandall.com/rowers/workout/2660/emailcsv,https://rowsandall.com/rowers/workout/2660/emailtcx,0,0,2018-02-24 15:07:07+00:00,14544,01:01:04.500000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +128,https://rowsandall.com/rowers/workout/2386/emailcsv,https://rowsandall.com/rowers/workout/2386/emailtcx,0,0,2018-02-25 12:24:59+00:00,6182,00:26:24.400000,9x500m/70sec,,Europe/Prague,rower,80.0,lwt +129,https://rowsandall.com/rowers/workout/2659/emailcsv,https://rowsandall.com/rowers/workout/2659/emailtcx,0,0,2018-02-25 14:15:13+00:00,16623,01:22:28,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +130,https://rowsandall.com/rowers/workout/2400/emailcsv,https://rowsandall.com/rowers/workout/2400/emailtcx,0,0,2018-02-25 22:45:28+00:00,4175,00:25:07.700000,Test CP,,Europe/Prague,rower,80.0,lwt +131,https://rowsandall.com/rowers/workout/2387/emailcsv,https://rowsandall.com/rowers/workout/2387/emailtcx,0,0,2018-02-27 13:19:44+00:00,2206,00:10:00,StatsError,,Europe/Prague,rower,80.0,lwt +132,https://rowsandall.com/rowers/workout/2658/emailcsv,https://rowsandall.com/rowers/workout/2658/emailtcx,0,0,2018-02-27 16:32:56+00:00,13549,00:56:44.900000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +133,https://rowsandall.com/rowers/workout/2657/emailcsv,https://rowsandall.com/rowers/workout/2657/emailtcx,0,0,2018-03-03 13:50:12+00:00,2012,00:08:42.700000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +134,https://rowsandall.com/rowers/workout/2656/emailcsv,https://rowsandall.com/rowers/workout/2656/emailtcx,0,0,2018-03-03 14:00:26+00:00,6270,00:26:41.700000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +135,https://rowsandall.com/rowers/workout/2655/emailcsv,https://rowsandall.com/rowers/workout/2655/emailtcx,0,0,2018-03-03 14:27:41+00:00,999,00:05:44.100000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +136,https://rowsandall.com/rowers/workout/2395/emailcsv,https://rowsandall.com/rowers/workout/2395/emailtcx,0,0,2018-03-03 14:33:51+00:00,6098,00:26:35.400000,Flex chart not working,,Europe/Prague,rower,80.0,lwt +137,https://rowsandall.com/rowers/workout/2654/emailcsv,https://rowsandall.com/rowers/workout/2654/emailtcx,0,0,2018-03-03 14:33:51+00:00,6098,00:26:35.400000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +138,https://rowsandall.com/rowers/workout/2653/emailcsv,https://rowsandall.com/rowers/workout/2653/emailtcx,0,0,2018-03-03 15:01:34+00:00,1559,00:07:16.600000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +139,https://rowsandall.com/rowers/workout/2392/emailcsv,https://rowsandall.com/rowers/workout/2392/emailtcx,0,0,2018-03-05 13:51:23+00:00,13802,01:06:00,Mike's average workout,,Europe/Prague,rower,80.0,lwt +140,https://rowsandall.com/rowers/workout/2396/emailcsv,https://rowsandall.com/rowers/workout/2396/emailtcx,0,0,2018-03-05 13:51:23+00:00,4058,00:17:00,Mike's average workout (1),,Europe/Prague,rower,80.0,lwt +141,https://rowsandall.com/rowers/workout/2397/emailcsv,https://rowsandall.com/rowers/workout/2397/emailtcx,0,0,2018-03-05 14:08:23+00:00,9738,00:49:00,Mike's average workout (2),,Europe/Prague,rower,80.0,lwt +142,https://rowsandall.com/rowers/workout/2389/emailcsv,https://rowsandall.com/rowers/workout/2389/emailtcx,0,0,2018-03-05 17:48:31+00:00,7022,00:29:44.400000,test,wat een kutsessie,Europe/Prague,rower,80.0,lwt +143,https://rowsandall.com/rowers/workout/2652/emailcsv,https://rowsandall.com/rowers/workout/2652/emailtcx,0,0,2018-03-05 17:48:31+00:00,7022,00:29:45.900000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +144,https://rowsandall.com/rowers/workout/2390/emailcsv,https://rowsandall.com/rowers/workout/2390/emailtcx,0,0,2018-03-05 17:50:46+00:00,5019,00:31:27.500000,,,Europe/Prague,rower,80.0,lwt +145,https://rowsandall.com/rowers/workout/2651/emailcsv,https://rowsandall.com/rowers/workout/2651/emailtcx,0,0,2018-03-08 18:34:05+00:00,2010,00:08:38.700000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +146,https://rowsandall.com/rowers/workout/2650/emailcsv,https://rowsandall.com/rowers/workout/2650/emailtcx,0,0,2018-03-08 18:43:29+00:00,10444,00:46:52.300000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +147,https://rowsandall.com/rowers/workout/2649/emailcsv,https://rowsandall.com/rowers/workout/2649/emailtcx,0,0,2018-03-08 19:31:31+00:00,269,00:01:42,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +148,https://rowsandall.com/rowers/workout/2398/emailcsv,https://rowsandall.com/rowers/workout/2398/emailtcx,0,0,2018-03-09 11:52:48+00:00,4160,00:13:00,test template,"Dit zijn de notes +Sommige zinnen zijn heel heel heel heel heel heel heel heel lang Sommige zinnen zijn heel heel heel heel heel heel heel heel lang Sommige zinnen zijn heel heel heel heel heel heel heel heel lang Sommige zinnen zijn heel heel heel heel heel heel heel heel lang +LAngwoordzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz",Europe/Prague,rower,80.0,lwt +149,https://rowsandall.com/rowers/workout/2399/emailcsv,https://rowsandall.com/rowers/workout/2399/emailtcx,0,0,2018-03-09 11:59:13+00:00,4160,00:13:00,Test P,,Europe/Prague,rower,80.0,lwt +150,https://rowsandall.com/rowers/workout/2401/emailcsv,https://rowsandall.com/rowers/workout/2401/emailtcx,0,0,2018-03-13 07:32:40+00:00,6479,00:30:27.300000,high power values?,,Europe/Prague,rower,80.0,lwt +151,https://rowsandall.com/rowers/workout/2648/emailcsv,https://rowsandall.com/rowers/workout/2648/emailtcx,0,0,2018-03-13 18:18:31+00:00,2009,00:08:30,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +152,https://rowsandall.com/rowers/workout/2404/emailcsv,https://rowsandall.com/rowers/workout/2404/emailtcx,0,0,2018-03-13 18:28:00.900000+00:00,9996,00:40:00,C2 Rower Timed. 10.0,"C2 Rower Timed. 10.0km, 40.00 min. (PainSled)",Europe/Prague,rower,80.0,lwt +153,https://rowsandall.com/rowers/workout/2405/emailcsv,https://rowsandall.com/rowers/workout/2405/emailtcx,0,0,2018-03-13 18:28:00.900000+00:00,10850,00:45:32.400000,C2 Rower Timed. 10.0,"C2 Rower Timed. 10.0km, 40.00 min. (PainSled)",Europe/Prague,rower,80.0,lwt +154,https://rowsandall.com/rowers/workout/2647/emailcsv,https://rowsandall.com/rowers/workout/2647/emailtcx,0,0,2018-03-13 18:28:01+00:00,10864,00:46:12.800000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +155,https://rowsandall.com/rowers/workout/2646/emailcsv,https://rowsandall.com/rowers/workout/2646/emailtcx,0,0,2018-03-13 19:15:15+00:00,1015,00:05:05.600000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +156,https://rowsandall.com/rowers/workout/2645/emailcsv,https://rowsandall.com/rowers/workout/2645/emailtcx,0,0,2018-03-13 19:18:21+00:00,2009,00:08:29.600000,Imported workout,imported from Concept2 log,UTC,rower,80.0,hwt +157,https://rowsandall.com/rowers/workout/2644/emailcsv,https://rowsandall.com/rowers/workout/2644/emailtcx,0,0,2018-03-13 19:28:00+00:00,9996,00:40:00,Imported workout,imported from Concept2 log,UTC,rower,80.0,hwt +158,https://rowsandall.com/rowers/workout/2402/emailcsv,https://rowsandall.com/rowers/workout/2402/emailtcx,0,0,2018-03-13 21:15:46+00:00,1311,00:13:04.100000,Stroke Count,,Europe/Prague,rower,80.0,lwt +159,https://rowsandall.com/rowers/workout/2403/emailcsv,https://rowsandall.com/rowers/workout/2403/emailtcx,0,0,2018-03-14 19:33:20+00:00,10850,00:45:32.400000,4x10min,,Europe/Prague,rower,80.0,lwt +160,https://rowsandall.com/rowers/workout/2406/emailcsv,https://rowsandall.com/rowers/workout/2406/emailtcx,0,0,2018-03-15 06:22:52+00:00,336,00:01:00,Test 1 minute,,Europe/Prague,rower,80.0,lwt +161,https://rowsandall.com/rowers/workout/2422/emailcsv,https://rowsandall.com/rowers/workout/2422/emailtcx,0,0,2018-03-15 06:22:52+00:00,8136,23:59:59.900000,Joined Workout,,Europe/Prague,rower,80.0,lwt +162,https://rowsandall.com/rowers/workout/2407/emailcsv,https://rowsandall.com/rowers/workout/2407/emailtcx,0,0,2018-03-15 06:23:09+00:00,7800,00:30:00,Thirty Dirty,,Europe/Prague,rower,80.0,lwt +163,https://rowsandall.com/rowers/workout/2434/emailcsv,https://rowsandall.com/rowers/workout/2434/emailtcx,0,0,2018-03-15 06:25:09+00:00,7800,23:59:59.900000,30 min,,Europe/Prague,rower,80.0,lwt +164,https://rowsandall.com/rowers/workout/2643/emailcsv,https://rowsandall.com/rowers/workout/2643/emailtcx,0,0,2018-03-15 18:48:48+00:00,2013,00:08:36.700000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +165,https://rowsandall.com/rowers/workout/2425/emailcsv,https://rowsandall.com/rowers/workout/2425/emailtcx,0,0,2018-03-15 18:58:08+00:00,10169,00:44:31.200000,Joined Workout,,Europe/Prague,rower,80.0,lwt +166,https://rowsandall.com/rowers/workout/2438/emailcsv,https://rowsandall.com/rowers/workout/2438/emailtcx,0,0,2018-03-15 18:58:08+00:00,8157,00:34:21.600000,Test,imported through email,Europe/Prague,rower,80.0,lwt +167,https://rowsandall.com/rowers/workout/2642/emailcsv,https://rowsandall.com/rowers/workout/2642/emailtcx,0,0,2018-03-15 18:58:08+00:00,8157,00:34:21.500000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +168,https://rowsandall.com/rowers/workout/2423/emailcsv,https://rowsandall.com/rowers/workout/2423/emailtcx,0,0,2018-03-15 18:58:08.620390+00:00,8157,00:34:21.600000,6x4min,,Europe/Prague,rower,80.0,lwt +169,https://rowsandall.com/rowers/workout/2431/emailcsv,https://rowsandall.com/rowers/workout/2431/emailtcx,0,0,2018-03-15 18:58:08.620390+00:00,8157,00:34:21.600000,Indoor,,Europe/Prague,rower,80.0,lwt +170,https://rowsandall.com/rowers/workout/2432/emailcsv,https://rowsandall.com/rowers/workout/2432/emailtcx,0,0,2018-03-15 18:58:08.620390+00:00,8157,00:34:21.600000,test,,Europe/Prague,rower,80.0,lwt +171,https://rowsandall.com/rowers/workout/2435/emailcsv,https://rowsandall.com/rowers/workout/2435/emailtcx,0,0,2018-03-15 18:58:08.620390+00:00,8157,00:34:21.600000,Test,,Europe/Prague,rower,80.0,lwt +172,https://rowsandall.com/rowers/workout/2436/emailcsv,https://rowsandall.com/rowers/workout/2436/emailtcx,0,0,2018-03-15 18:58:08.620390+00:00,8157,00:34:21.600000,r,,Europe/Prague,rower,80.0,lwt +173,https://rowsandall.com/rowers/workout/2437/emailcsv,https://rowsandall.com/rowers/workout/2437/emailtcx,0,0,2018-03-15 18:58:08.620390+00:00,8157,00:34:21.600000,a,,Europe/Prague,rower,80.0,lwt +174,https://rowsandall.com/rowers/workout/2440/emailcsv,https://rowsandall.com/rowers/workout/2440/emailtcx,0,0,2018-03-15 19:33:15+00:00,2012,00:09:24.300000,another one,imported through email,Europe/Prague,rower,80.0,lwt +175,https://rowsandall.com/rowers/workout/2641/emailcsv,https://rowsandall.com/rowers/workout/2641/emailtcx,0,0,2018-03-15 19:33:15+00:00,2012,00:09:24.300000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +176,https://rowsandall.com/rowers/workout/2424/emailcsv,https://rowsandall.com/rowers/workout/2424/emailtcx,0,0,2018-03-15 19:33:15.009331+00:00,2012,00:09:24.400000,6x4min,,Europe/Prague,rower,80.0,lwt +177,https://rowsandall.com/rowers/workout/2429/emailcsv,https://rowsandall.com/rowers/workout/2429/emailtcx,0,0,2018-03-16 12:04:52+00:00,9595,00:54:45,Joined Workout,,America/New_York,rower,80.0,lwt +178,https://rowsandall.com/rowers/workout/2426/emailcsv,https://rowsandall.com/rowers/workout/2426/emailtcx,0,0,2018-03-16 12:09:23+00:00,9269,00:50:14,Test P,,America/New_York,rower,80.0,lwt +179,https://rowsandall.com/rowers/workout/2428/emailcsv,https://rowsandall.com/rowers/workout/2428/emailtcx,0,0,2018-03-16 12:09:23+00:00,9269,00:50:14,,,America/New_York,rower,80.0,lwt +180,https://rowsandall.com/rowers/workout/2427/emailcsv,https://rowsandall.com/rowers/workout/2427/emailtcx,0,0,2018-03-16 13:00:52+00:00,326,00:04:03,Water Workout,,America/New_York,water,80.0,lwt +181,https://rowsandall.com/rowers/workout/2640/emailcsv,https://rowsandall.com/rowers/workout/2640/emailtcx,0,0,2018-03-17 14:14:56+00:00,15231,01:03:19.100000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +182,https://rowsandall.com/rowers/workout/2639/emailcsv,https://rowsandall.com/rowers/workout/2639/emailtcx,0,0,2018-03-18 14:03:27+00:00,3876,00:16:16.200000,Imported workout,imported from Concept2 log,UTC,slides,80.0,lwt +183,https://rowsandall.com/rowers/workout/2638/emailcsv,https://rowsandall.com/rowers/workout/2638/emailtcx,0,0,2018-03-18 14:20:52+00:00,6000,00:22:17.500000,Imported workout,imported from Concept2 log,UTC,slides,80.0,lwt +184,https://rowsandall.com/rowers/workout/2637/emailcsv,https://rowsandall.com/rowers/workout/2637/emailtcx,0,0,2018-03-18 14:44:46+00:00,4163,00:19:13.200000,Imported workout,imported from Concept2 log,UTC,slides,80.0,lwt +185,https://rowsandall.com/rowers/workout/2636/emailcsv,https://rowsandall.com/rowers/workout/2636/emailtcx,0,0,2018-03-22 18:38:59+00:00,2007,00:08:43.800000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +186,https://rowsandall.com/rowers/workout/2635/emailcsv,https://rowsandall.com/rowers/workout/2635/emailtcx,0,0,2018-03-22 18:48:29+00:00,6770,00:29:38.200000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +187,https://rowsandall.com/rowers/workout/2634/emailcsv,https://rowsandall.com/rowers/workout/2634/emailtcx,0,0,2018-03-22 19:18:50+00:00,2010,00:08:46.700000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +188,https://rowsandall.com/rowers/workout/2633/emailcsv,https://rowsandall.com/rowers/workout/2633/emailtcx,0,0,2018-03-24 09:20:07+00:00,15446,01:22:56.300000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +189,https://rowsandall.com/rowers/workout/2451/emailcsv,https://rowsandall.com/rowers/workout/2451/emailtcx,0,0,2018-03-25 09:59:12+00:00,29000,01:50:13,test,,Europe/Zurich,rower,80.0,lwt +190,https://rowsandall.com/rowers/workout/2452/emailcsv,https://rowsandall.com/rowers/workout/2452/emailtcx,0,0,2018-03-25 09:59:12+00:00,29000,01:50:13,Background processing test,imported through email,Europe/Zurich,water,80.0,lwt +191,https://rowsandall.com/rowers/workout/2632/emailcsv,https://rowsandall.com/rowers/workout/2632/emailtcx,0,0,2018-03-25 14:29:09+00:00,6664,00:46:07,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +192,https://rowsandall.com/rowers/workout/2453/emailcsv,https://rowsandall.com/rowers/workout/2453/emailtcx,0,0,2018-03-29 13:45:00+00:00,2000,07:25:00,test api,string,UTC,water,80.0,lwt +193,https://rowsandall.com/rowers/workout/2454/emailcsv,https://rowsandall.com/rowers/workout/2454/emailtcx,0,0,2018-04-02 11:02:55+00:00,3848,00:22:14.200000,RP3 curve,,Europe/Prague,rower,80.0,lwt +194,https://rowsandall.com/rowers/workout/2455/emailcsv,https://rowsandall.com/rowers/workout/2455/emailtcx,0,0,2018-04-02 11:04:18+00:00,3848,00:22:14.200000,,,Europe/Prague,rower,80.0,lwt +195,https://rowsandall.com/rowers/workout/2631/emailcsv,https://rowsandall.com/rowers/workout/2631/emailtcx,0,0,2018-04-03 14:34:01+00:00,2822,00:14:43.700000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +196,https://rowsandall.com/rowers/workout/2630/emailcsv,https://rowsandall.com/rowers/workout/2630/emailtcx,0,0,2018-04-03 14:50:01+00:00,7505,00:39:46.500000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +197,https://rowsandall.com/rowers/workout/2456/emailcsv,https://rowsandall.com/rowers/workout/2456/emailtcx,0,0,2018-04-03 15:50:52+00:00,7200,00:37:44,In Stroke,,Europe/Prague,water,80.0,lwt +198,https://rowsandall.com/rowers/workout/2457/emailcsv,https://rowsandall.com/rowers/workout/2457/emailtcx,0,0,2018-04-03 15:50:52+00:00,7200,00:37:44,In Stroke 2,,Europe/Prague,water,80.0,lwt +199,https://rowsandall.com/rowers/workout/2629/emailcsv,https://rowsandall.com/rowers/workout/2629/emailtcx,0,0,2018-04-04 18:30:27+00:00,27,00:00:03.200000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +200,https://rowsandall.com/rowers/workout/2628/emailcsv,https://rowsandall.com/rowers/workout/2628/emailtcx,0,0,2018-04-04 19:00:43+00:00,8309,00:48:07.400000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +201,https://rowsandall.com/rowers/workout/2458/emailcsv,https://rowsandall.com/rowers/workout/2458/emailtcx,0,0,2018-04-07 07:41:02+00:00,10568,01:06:08.800000,2x3km fail,,Europe/Prague,water,80.0,lwt +202,https://rowsandall.com/rowers/workout/2460/emailcsv,https://rowsandall.com/rowers/workout/2460/emailtcx,0,0,2018-04-07 07:41:02+00:00,10568,01:06:08.800000,Update,,Europe/Prague,water,80.0,lwt +203,https://rowsandall.com/rowers/workout/2627/emailcsv,https://rowsandall.com/rowers/workout/2627/emailtcx,0,0,2018-04-07 07:41:02+00:00,2243,00:12:20.500000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +204,https://rowsandall.com/rowers/workout/2626/emailcsv,https://rowsandall.com/rowers/workout/2626/emailtcx,0,0,2018-04-07 07:55:02+00:00,3167,00:16:59.800000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +205,https://rowsandall.com/rowers/workout/2625/emailcsv,https://rowsandall.com/rowers/workout/2625/emailtcx,0,0,2018-04-07 08:14:02+00:00,2977,00:14:58,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +206,https://rowsandall.com/rowers/workout/2623/emailcsv,https://rowsandall.com/rowers/workout/2623/emailtcx,0,0,2018-04-07 08:33:03+00:00,2180,00:14:07.700000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +207,https://rowsandall.com/rowers/workout/2622/emailcsv,https://rowsandall.com/rowers/workout/2622/emailtcx,0,0,2018-04-10 17:38:22+00:00,11703,00:48:59.500000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +208,https://rowsandall.com/rowers/workout/2621/emailcsv,https://rowsandall.com/rowers/workout/2621/emailtcx,0,0,2018-04-10 18:27:53+00:00,17284,01:11:55.500000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +209,https://rowsandall.com/rowers/workout/2620/emailcsv,https://rowsandall.com/rowers/workout/2620/emailtcx,0,0,2018-04-11 18:37:27+00:00,10122,01:15:44.100000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +210,https://rowsandall.com/rowers/workout/2619/emailcsv,https://rowsandall.com/rowers/workout/2619/emailtcx,0,0,2018-04-12 16:17:02+00:00,10408,00:53:45.100000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +211,https://rowsandall.com/rowers/workout/2618/emailcsv,https://rowsandall.com/rowers/workout/2618/emailtcx,0,0,2018-04-15 08:34:05+00:00,11129,01:03:38,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +212,https://rowsandall.com/rowers/workout/2617/emailcsv,https://rowsandall.com/rowers/workout/2617/emailtcx,0,0,2018-04-18 13:43:04+00:00,1942,00:10:55.300000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +213,https://rowsandall.com/rowers/workout/2616/emailcsv,https://rowsandall.com/rowers/workout/2616/emailtcx,0,0,2018-04-18 13:57:01+00:00,7979,00:36:10.600000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +214,https://rowsandall.com/rowers/workout/2615/emailcsv,https://rowsandall.com/rowers/workout/2615/emailtcx,0,0,2018-04-18 14:49:06+00:00,1512,00:08:27.600000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +215,https://rowsandall.com/rowers/workout/2614/emailcsv,https://rowsandall.com/rowers/workout/2614/emailtcx,0,0,2018-04-20 15:51:41+00:00,9748,01:06:50.100000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +216,https://rowsandall.com/rowers/workout/2613/emailcsv,https://rowsandall.com/rowers/workout/2613/emailtcx,0,0,2018-04-21 15:38:04+00:00,5254,00:27:02.700000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +217,https://rowsandall.com/rowers/workout/2612/emailcsv,https://rowsandall.com/rowers/workout/2612/emailtcx,0,0,2018-04-22 07:15:01+00:00,12282,01:08:28.600000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +218,https://rowsandall.com/rowers/workout/2471/emailcsv,https://rowsandall.com/rowers/workout/2471/emailtcx,0,0,2018-04-23 11:00:00+00:00,1017,00:03:55.200000,7x150min/1min,,America/New_York,water,80.0,lwt +219,https://rowsandall.com/rowers/workout/2472/emailcsv,https://rowsandall.com/rowers/workout/2472/emailtcx,0,0,2018-04-23 11:00:00+00:00,1017,00:03:55.200000,7x15min test 2,,America/New_York,water,80.0,lwt +220,https://rowsandall.com/rowers/workout/2473/emailcsv,https://rowsandall.com/rowers/workout/2473/emailtcx,0,0,2018-04-23 11:00:00+00:00,1017,00:03:55.200000,7x150min/1min,,America/New_York,water,80.0,lwt +221,https://rowsandall.com/rowers/workout/2474/emailcsv,https://rowsandall.com/rowers/workout/2474/emailtcx,0,0,2018-04-23 11:00:00+00:00,1017,00:03:55,test firmware,,America/New_York,water,80.0,lwt +222,https://rowsandall.com/rowers/workout/2478/emailcsv,https://rowsandall.com/rowers/workout/2478/emailtcx,0,0,2018-04-23 11:00:00+00:00,1017,00:03:55.200000,Firmware 2,,America/New_York,water,80.0,lwt +223,https://rowsandall.com/rowers/workout/2611/emailcsv,https://rowsandall.com/rowers/workout/2611/emailtcx,0,0,2018-04-23 17:46:25+00:00,13558,00:58:11.900000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +224,https://rowsandall.com/rowers/workout/2465/emailcsv,https://rowsandall.com/rowers/workout/2465/emailtcx,0,0,2018-04-23 20:42:00+00:00,8000,00:36:01.500000,Racice,,Europe/Prague,water,80.0,lwt +225,https://rowsandall.com/rowers/workout/2610/emailcsv,https://rowsandall.com/rowers/workout/2610/emailtcx,0,0,2018-04-24 12:12:03+00:00,2959,00:16:42.700000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +226,https://rowsandall.com/rowers/workout/2609/emailcsv,https://rowsandall.com/rowers/workout/2609/emailtcx,0,0,2018-04-24 12:32:00+00:00,7516,00:38:49.500000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +227,https://rowsandall.com/rowers/workout/2466/emailcsv,https://rowsandall.com/rowers/workout/2466/emailtcx,0,0,2018-04-24 13:35:30+00:00,9420,00:48:00,,,Europe/Prague,water,80.0,lwt +228,https://rowsandall.com/rowers/workout/2608/emailcsv,https://rowsandall.com/rowers/workout/2608/emailtcx,0,0,2018-04-25 17:50:26+00:00,10171,01:12:33.600000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +229,https://rowsandall.com/rowers/workout/2470/emailcsv,https://rowsandall.com/rowers/workout/2470/emailtcx,0,0,2018-04-25 22:59:01+00:00,4159,00:26:43.200000,SpeedCoach 7x150m,,America/New_York,water,80.0,lwt +230,https://rowsandall.com/rowers/workout/2607/emailcsv,https://rowsandall.com/rowers/workout/2607/emailtcx,0,0,2018-04-26 16:18:01+00:00,2016,00:12:30.500000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +231,https://rowsandall.com/rowers/workout/2606/emailcsv,https://rowsandall.com/rowers/workout/2606/emailtcx,0,0,2018-04-26 16:33:00+00:00,4605,00:22:35.700000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +232,https://rowsandall.com/rowers/workout/2605/emailcsv,https://rowsandall.com/rowers/workout/2605/emailtcx,0,0,2018-04-26 16:56:03+00:00,1301,00:07:07.100000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +233,https://rowsandall.com/rowers/workout/2604/emailcsv,https://rowsandall.com/rowers/workout/2604/emailtcx,0,0,2018-04-28 08:02:01+00:00,11924,01:18:20.500000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +234,https://rowsandall.com/rowers/workout/2469/emailcsv,https://rowsandall.com/rowers/workout/2469/emailtcx,0,0,2018-04-29 07:22:01+00:00,8040,00:50:06,Test P,,America/New_York,water,80.0,lwt +235,https://rowsandall.com/rowers/workout/2603/emailcsv,https://rowsandall.com/rowers/workout/2603/emailtcx,0,0,2018-04-29 07:22:01+00:00,8040,00:50:05.500000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +236,https://rowsandall.com/rowers/workout/2559/emailcsv,https://rowsandall.com/rowers/workout/2559/emailtcx,0,0,2018-05-03 16:02:08+00:00,10909,01:00:35,Intervals Tests (6),imported through email,Europe/Amsterdam,water,80.0,lwt +237,https://rowsandall.com/rowers/workout/2602/emailcsv,https://rowsandall.com/rowers/workout/2602/emailtcx,0,0,2018-05-04 05:47:04+00:00,3472,00:18:53.700000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +238,https://rowsandall.com/rowers/workout/2479/emailcsv,https://rowsandall.com/rowers/workout/2479/emailtcx,0,0,2018-05-04 06:07:04+00:00,5997,00:25:53.900000,Zip test,imported through email,Europe/Prague,water,80.0,lwt +239,https://rowsandall.com/rowers/workout/2481/emailcsv,https://rowsandall.com/rowers/workout/2481/emailtcx,0,0,2018-05-04 06:07:04+00:00,5997,00:25:53.900000,Zip test 2,imported through email,Europe/Prague,water,80.0,lwt +240,https://rowsandall.com/rowers/workout/2483/emailcsv,https://rowsandall.com/rowers/workout/2483/emailtcx,0,0,2018-05-04 06:07:04+00:00,5997,00:25:53.900000,Test Zip 3,imported through email,Europe/Prague,water,80.0,lwt +241,https://rowsandall.com/rowers/workout/2601/emailcsv,https://rowsandall.com/rowers/workout/2601/emailtcx,0,0,2018-05-04 06:07:04+00:00,5997,00:25:53.900000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +242,https://rowsandall.com/rowers/workout/2860/emailcsv,https://rowsandall.com/rowers/workout/2860/emailtcx,0,0,2018-05-04 06:07:04+00:00,5997,00:25:53.900000,Zip test,"imported through email + from speedcoach2v2.15 via rowsandall.com",Europe/Prague,water,80.0,hwt +243,https://rowsandall.com/rowers/workout/2600/emailcsv,https://rowsandall.com/rowers/workout/2600/emailtcx,0,0,2018-05-04 06:35:04+00:00,1009,00:06:44.500000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +244,https://rowsandall.com/rowers/workout/2599/emailcsv,https://rowsandall.com/rowers/workout/2599/emailtcx,0,0,2018-05-05 07:06:02+00:00,11180,01:01:01.600000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +245,https://rowsandall.com/rowers/workout/2598/emailcsv,https://rowsandall.com/rowers/workout/2598/emailtcx,0,0,2018-05-05 08:11:04+00:00,1152,00:06:56.100000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +246,https://rowsandall.com/rowers/workout/2597/emailcsv,https://rowsandall.com/rowers/workout/2597/emailtcx,0,0,2018-05-07 15:41:06+00:00,11019,01:00:40.500000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +247,https://rowsandall.com/rowers/workout/2490/emailcsv,https://rowsandall.com/rowers/workout/2490/emailtcx,0,0,2018-05-10 09:11:24+00:00,15263,01:39:34,Naarden goed,,Europe/Amsterdam,water,80.0,lwt +248,https://rowsandall.com/rowers/workout/2491/emailcsv,https://rowsandall.com/rowers/workout/2491/emailtcx,0,0,2018-05-10 09:11:24+00:00,7378,00:44:59,Naarden goed (1),,Europe/Amsterdam,water,80.0,lwt +249,https://rowsandall.com/rowers/workout/2492/emailcsv,https://rowsandall.com/rowers/workout/2492/emailtcx,0,0,2018-05-10 09:56:24+00:00,7876,00:54:33,Naarden goed (2),,Europe/Amsterdam,water,80.0,lwt +250,https://rowsandall.com/rowers/workout/2596/emailcsv,https://rowsandall.com/rowers/workout/2596/emailtcx,0,0,2018-05-10 16:15:02+00:00,11983,01:01:26,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +251,https://rowsandall.com/rowers/workout/2485/emailcsv,https://rowsandall.com/rowers/workout/2485/emailtcx,0,0,2018-05-10 16:55:01+00:00,8000,00:36:01.500000,6k,,Europe/Prague,water,80.0,lwt +252,https://rowsandall.com/rowers/workout/2486/emailcsv,https://rowsandall.com/rowers/workout/2486/emailtcx,0,0,2018-05-10 16:55:01+00:00,8000,00:36:01.500000,Racice 2,,Europe/Prague,water,80.0,lwt +253,https://rowsandall.com/rowers/workout/2488/emailcsv,https://rowsandall.com/rowers/workout/2488/emailtcx,0,0,2018-05-11 07:02:52+00:00,15364,01:22:34,Naarden 1,,Europe/Amsterdam,water,80.0,lwt +254,https://rowsandall.com/rowers/workout/2504/emailcsv,https://rowsandall.com/rowers/workout/2504/emailtcx,0,0,2018-05-12 10:01:00+00:00,16814,01:42:53,DC,"Summary for your location at 2018-05-12T12:52:00Z: Temperature 21.1C/69.9F. Wind: 3.0 m/s (6.0 kt). Wind Bearing: 30.0 degrees +Summary for your location at 2018-05-12T10:52:00Z: Temperature 19.4C/66.9F. Wind: 3.0 m/s (6.0 kt). Wind Bearing: 40.0 degrees +Summary for your location at 2018-05-12T11:52:00Z: Temperature 20.6C/69.0F. Wind: 3.6 m/s (7.0 kt). Wind Bearing: 20.0 degrees +Summary for your location at 2018-05-12T10:52:26+00:00: Clear. Temperature 64.52F/18.0C. Wind: 0.96 m/s. Wind Bearing: 29 degrees",America/New_York,water,80.0,lwt +255,https://rowsandall.com/rowers/workout/2931/emailcsv,https://rowsandall.com/rowers/workout/2931/emailtcx,0,0,2018-05-12 14:22:12+00:00,6510,00:46:56,Test P,,Europe/Bratislava,water,80.0,hwt +256,https://rowsandall.com/rowers/workout/2513/emailcsv,https://rowsandall.com/rowers/workout/2513/emailtcx,0,0,2018-05-13 07:57:02+00:00,4605,00:35:11.700000,Quad,,Europe/Bratislava,water,80.0,lwt +257,https://rowsandall.com/rowers/workout/2595/emailcsv,https://rowsandall.com/rowers/workout/2595/emailtcx,0,0,2018-05-13 07:57:02+00:00,4605,00:35:11.600000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +258,https://rowsandall.com/rowers/workout/2502/emailcsv,https://rowsandall.com/rowers/workout/2502/emailtcx,0,0,2018-05-13 09:22:49+00:00,973,00:03:53.900000,Completed a workout (generic) 0.6 mi on 05/13/18,,Europe/Bratislava,water,80.0,lwt +259,https://rowsandall.com/rowers/workout/2503/emailcsv,https://rowsandall.com/rowers/workout/2503/emailtcx,0,0,2018-05-13 09:22:49+00:00,973,00:03:53.900000,Completed a workout (generic) 0.6 mi on 05/13/18,,Europe/Bratislava,water,80.0,lwt +260,https://rowsandall.com/rowers/workout/2594/emailcsv,https://rowsandall.com/rowers/workout/2594/emailtcx,0,0,2018-05-13 16:50:32+00:00,2723,00:27:17.200000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +261,https://rowsandall.com/rowers/workout/2593/emailcsv,https://rowsandall.com/rowers/workout/2593/emailtcx,0,0,2018-05-13 16:51:01+00:00,5929,00:43:44.500000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +262,https://rowsandall.com/rowers/workout/2855/emailcsv,https://rowsandall.com/rowers/workout/2855/emailtcx,0,0,2018-05-14 13:53:04+00:00,6091,00:25:00.200000,Hradiste," + from csv via rowsandall.com",Europe/Prague,water,80.0,lwt +263,https://rowsandall.com/rowers/workout/2963/emailcsv,https://rowsandall.com/rowers/workout/2963/emailtcx,0,0,2018-05-14 13:53:04+00:00,6091,00:25:00.200000,Hradiste," + from csv via rowsandall.com",Europe/Prague,water,80.0,hwt +264,https://rowsandall.com/rowers/workout/2493/emailcsv,https://rowsandall.com/rowers/workout/2493/emailtcx,0,0,2018-05-15 10:35:00+00:00,10526,00:44:01.700000,Turin,,Europe/Rome,water,80.0,lwt +265,https://rowsandall.com/rowers/workout/2489/emailcsv,https://rowsandall.com/rowers/workout/2489/emailtcx,0,0,2018-05-17 17:40:00+00:00,15364,01:22:34,Naarden 2,,Europe/Amsterdam,water,80.0,lwt +266,https://rowsandall.com/rowers/workout/2464/emailcsv,https://rowsandall.com/rowers/workout/2464/emailtcx,0,0,2018-05-18 13:02:00+00:00,6091,00:25:00.200000,Hradiste,"Failed to download METAR data +Failed to download METAR data +Failed to download METAR data +Failed to download METAR data +Failed to download METAR data",Europe/Prague,water,80.0,lwt +267,https://rowsandall.com/rowers/workout/2505/emailcsv,https://rowsandall.com/rowers/workout/2505/emailtcx,0,0,2018-05-19 11:55:34+00:00,14274,01:12:33.600000,Roos,,America/New_York,water,80.0,lwt +268,https://rowsandall.com/rowers/workout/2506/emailcsv,https://rowsandall.com/rowers/workout/2506/emailtcx,0,0,2018-05-19 11:55:34+00:00,7384,00:33:59.800000,Roos (1),,America/New_York,water,80.0,lwt +269,https://rowsandall.com/rowers/workout/2507/emailcsv,https://rowsandall.com/rowers/workout/2507/emailtcx,0,0,2018-05-19 12:29:34+00:00,6885,00:38:32.200000,Roos (2),,America/New_York,water,80.0,lwt +270,https://rowsandall.com/rowers/workout/2511/emailcsv,https://rowsandall.com/rowers/workout/2511/emailtcx,0,0,2018-05-27 10:17:01+00:00,10663,00:59:29.700000,Test P,,Europe/Prague,water,80.0,lwt +271,https://rowsandall.com/rowers/workout/2518/emailcsv,https://rowsandall.com/rowers/workout/2518/emailtcx,0,0,2018-05-27 10:17:01+00:00,10663,00:59:29.700000,dubbel,,Europe/Prague,water,80.0,lwt +272,https://rowsandall.com/rowers/workout/2592/emailcsv,https://rowsandall.com/rowers/workout/2592/emailtcx,0,0,2018-05-27 10:17:01+00:00,10663,00:59:29.600000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +273,https://rowsandall.com/rowers/workout/2591/emailcsv,https://rowsandall.com/rowers/workout/2591/emailtcx,0,0,2018-05-28 15:11:01+00:00,9642,00:56:16.100000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +274,https://rowsandall.com/rowers/workout/2514/emailcsv,https://rowsandall.com/rowers/workout/2514/emailtcx,0,0,2018-05-30 18:05:25+00:00,11237,01:04:45.500000,Eight,imported through email,Europe/Prague,water,80.0,lwt +275,https://rowsandall.com/rowers/workout/2516/emailcsv,https://rowsandall.com/rowers/workout/2516/emailtcx,0,0,2018-05-30 18:05:25+00:00,11237,01:04:45.500000,Twee Zonder,imported through email,Europe/Prague,water,80.0,lwt +276,https://rowsandall.com/rowers/workout/2590/emailcsv,https://rowsandall.com/rowers/workout/2590/emailtcx,0,0,2018-05-30 18:05:25+00:00,11237,01:04:45.500000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +277,https://rowsandall.com/rowers/workout/2512/emailcsv,https://rowsandall.com/rowers/workout/2512/emailtcx,0,0,2018-05-31 16:30:07+00:00,8837,00:47:42.500000,Test,,Europe/Prague,water,80.0,lwt +278,https://rowsandall.com/rowers/workout/2589/emailcsv,https://rowsandall.com/rowers/workout/2589/emailtcx,0,0,2018-05-31 16:30:07+00:00,8837,00:47:42.500000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +279,https://rowsandall.com/rowers/workout/2508/emailcsv,https://rowsandall.com/rowers/workout/2508/emailtcx,0,0,2018-06-01 07:57:02+00:00,4605,00:35:11.700000,Piestany,,Europe/Bratislava,water,80.0,lwt +280,https://rowsandall.com/rowers/workout/2510/emailcsv,https://rowsandall.com/rowers/workout/2510/emailtcx,0,0,2018-06-01 08:02:02+00:00,3975,00:30:08.700000,Piestany (2),,Europe/Bratislava,water,80.0,lwt +281,https://rowsandall.com/rowers/workout/2509/emailcsv,https://rowsandall.com/rowers/workout/2509/emailtcx,0,0,2018-06-01 11:30:07+00:00,8837,00:47:42.500000,Test,,Europe/Prague,water,80.0,lwt +282,https://rowsandall.com/rowers/workout/2569/emailcsv,https://rowsandall.com/rowers/workout/2569/emailtcx,0,0,2018-06-01 15:00:00+00:00,11237,01:04:45.500000,Acht Intervals,,Europe/Prague,water,80.0,lwt +283,https://rowsandall.com/rowers/workout/2588/emailcsv,https://rowsandall.com/rowers/workout/2588/emailtcx,0,0,2018-06-02 17:00:07+00:00,12511,01:07:58,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +284,https://rowsandall.com/rowers/workout/2587/emailcsv,https://rowsandall.com/rowers/workout/2587/emailtcx,0,0,2018-06-03 07:57:02+00:00,10830,01:04:35.900000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +285,https://rowsandall.com/rowers/workout/2586/emailcsv,https://rowsandall.com/rowers/workout/2586/emailtcx,0,0,2018-06-03 15:50:25+00:00,6125,00:48:38.600000,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +286,https://rowsandall.com/rowers/workout/2535/emailcsv,https://rowsandall.com/rowers/workout/2535/emailtcx,0,0,2018-06-05 13:07:02+00:00,0,00:05:01,Test - Delete,,Europe/Prague,rower,80.0,lwt +287,https://rowsandall.com/rowers/workout/2536/emailcsv,https://rowsandall.com/rowers/workout/2536/emailtcx,0,0,2018-06-05 13:07:02+00:00,0,00:05:01,Test Sync - Delete," + from tcx via rowsandall.com",Europe/Prague,rower,80.0,lwt +288,https://rowsandall.com/rowers/workout/2537/emailcsv,https://rowsandall.com/rowers/workout/2537/emailtcx,0,0,2018-06-05 13:07:02+00:00,0,00:05:01,Test P," + from tcx via rowsandall.com",Europe/Prague,rower,80.0,lwt +289,https://rowsandall.com/rowers/workout/2538/emailcsv,https://rowsandall.com/rowers/workout/2538/emailtcx,0,0,2018-06-05 13:07:02+00:00,0,00:05:01,Test P,imported through email,Europe/Prague,rower,80.0,lwt +290,https://rowsandall.com/rowers/workout/2539/emailcsv,https://rowsandall.com/rowers/workout/2539/emailtcx,0,0,2018-06-05 13:07:02+00:00,0,00:05:01,Test P,"imported through email + from tcx via rowsandall.com",Europe/Prague,rower,80.0,lwt +291,https://rowsandall.com/rowers/workout/2540/emailcsv,https://rowsandall.com/rowers/workout/2540/emailtcx,0,0,2018-06-05 13:07:02+00:00,0,00:05:01,Workout from Background Queue,imported through email,Europe/Prague,rower,80.0,lwt +292,https://rowsandall.com/rowers/workout/2624/emailcsv,https://rowsandall.com/rowers/workout/2624/emailtcx,0,0,2018-06-05 13:51:01.900000+00:00,12744,01:08:22.100000,imported through ema,imported through email,Europe/Prague,water,80.0,lwt +293,https://rowsandall.com/rowers/workout/2534/emailcsv,https://rowsandall.com/rowers/workout/2534/emailtcx,0,0,2018-06-05 14:05:02+00:00,0,00:01:33,Import from Polar Flow,imported through email,Europe/Prague,rower,80.0,lwt +294,https://rowsandall.com/rowers/workout/2519/emailcsv,https://rowsandall.com/rowers/workout/2519/emailtcx,0,0,2018-06-05 14:09:57+00:00,0,00:03:38,TCX from Polar API,,Europe/Prague,water,80.0,lwt +295,https://rowsandall.com/rowers/workout/2546/emailcsv,https://rowsandall.com/rowers/workout/2546/emailtcx,0,0,2018-06-05 15:51:01+00:00,12744,01:08:22.200000,8x glad,,Europe/Prague,water,80.0,lwt +296,https://rowsandall.com/rowers/workout/2547/emailcsv,https://rowsandall.com/rowers/workout/2547/emailtcx,0,0,2018-06-05 15:51:01+00:00,12746,01:08:22.200000,1x glad,,Europe/Prague,c-boat,80.0,lwt +297,https://rowsandall.com/rowers/workout/2548/emailcsv,https://rowsandall.com/rowers/workout/2548/emailtcx,0,68,2018-06-05 15:51:01+00:00,12744,01:08:22.200000,1x glad,,Europe/Prague,water,80.0,lwt +298,https://rowsandall.com/rowers/workout/2549/emailcsv,https://rowsandall.com/rowers/workout/2549/emailtcx,0,0,2018-06-05 15:51:01+00:00,12746,01:08:22.200000,test,,Europe/Prague,water,80.0,lwt +299,https://rowsandall.com/rowers/workout/2550/emailcsv,https://rowsandall.com/rowers/workout/2550/emailtcx,0,0,2018-06-05 15:51:01+00:00,12746,01:08:22.200000,test tcx,,Europe/Prague,water,80.0,lwt +300,https://rowsandall.com/rowers/workout/2552/emailcsv,https://rowsandall.com/rowers/workout/2552/emailtcx,0,0,2018-06-05 15:51:01+00:00,6522,00:33:00,8x glad (1),,Europe/Prague,water,80.0,lwt +301,https://rowsandall.com/rowers/workout/2585/emailcsv,https://rowsandall.com/rowers/workout/2585/emailtcx,0,0,2018-06-05 15:51:01+00:00,12744,01:08:22.100000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +302,https://rowsandall.com/rowers/workout/2553/emailcsv,https://rowsandall.com/rowers/workout/2553/emailtcx,0,0,2018-06-05 16:24:01+00:00,6213,00:35:19.500000,8x glad (2),,Europe/Prague,water,80.0,lwt +303,https://rowsandall.com/rowers/workout/2584/emailcsv,https://rowsandall.com/rowers/workout/2584/emailtcx,0,0,2018-06-06 17:40:24+00:00,5273,00:42:59,Imported workout,imported from Concept2 log,UTC,rower,80.0,lwt +304,https://rowsandall.com/rowers/workout/2545/emailcsv,https://rowsandall.com/rowers/workout/2545/emailtcx,0,0,2018-06-08 07:25:33+00:00,1994,00:01:59.400000,Test,,Europe/Prague,rower,80.0,hwt +305,https://rowsandall.com/rowers/workout/2560/emailcsv,https://rowsandall.com/rowers/workout/2560/emailtcx,0,0,2018-06-11 15:21:01+00:00,10369,00:56:21.400000,Intervals Tests (7),imported through email,Europe/Prague,water,80.0,lwt +306,https://rowsandall.com/rowers/workout/2568/emailcsv,https://rowsandall.com/rowers/workout/2568/emailtcx,0,11,2018-06-11 15:21:01+00:00,10369,00:56:21.400000,3/2/1," + 5x5min/5min + 5x5min/5min",Europe/Prague,water,80.0,lwt +307,https://rowsandall.com/rowers/workout/2583/emailcsv,https://rowsandall.com/rowers/workout/2583/emailtcx,0,0,2018-06-11 15:21:01+00:00,10369,00:56:21.400000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +308,https://rowsandall.com/rowers/workout/2554/emailcsv,https://rowsandall.com/rowers/workout/2554/emailtcx,0,0,2018-06-13 14:18:19+00:00,16543,01:30:01.300000,Intervals Tests,,America/New_York,rower,80.0,lwt +309,https://rowsandall.com/rowers/workout/2555/emailcsv,https://rowsandall.com/rowers/workout/2555/emailtcx,0,0,2018-06-13 14:18:35+00:00,12205,01:03:29.500000,Intervals Tests (2),,America/New_York,rower,80.0,lwt +310,https://rowsandall.com/rowers/workout/2556/emailcsv,https://rowsandall.com/rowers/workout/2556/emailtcx,0,0,2018-06-13 14:18:48+00:00,9016,00:48:28.100000,Intervals Tests (3)," + 9x1km",America/New_York,rower,80.0,lwt +311,https://rowsandall.com/rowers/workout/2557/emailcsv,https://rowsandall.com/rowers/workout/2557/emailtcx,0,0,2018-06-13 14:19:01+00:00,4789,00:22:05,Intervals Tests (4),,America/New_York,rower,80.0,lwt +312,https://rowsandall.com/rowers/workout/2558/emailcsv,https://rowsandall.com/rowers/workout/2558/emailtcx,0,0,2018-06-13 14:19:19+00:00,0,00:22:54,Intervals Tests (5),,Europe/Prague,rower,80.0,lwt +313,https://rowsandall.com/rowers/workout/2582/emailcsv,https://rowsandall.com/rowers/workout/2582/emailtcx,0,0,2018-06-13 16:11:02+00:00,4191,00:23:36.400000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +314,https://rowsandall.com/rowers/workout/2581/emailcsv,https://rowsandall.com/rowers/workout/2581/emailtcx,0,0,2018-06-15 08:05:04+00:00,7301,00:39:44.100000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +315,https://rowsandall.com/rowers/workout/2580/emailcsv,https://rowsandall.com/rowers/workout/2580/emailtcx,0,0,2018-06-16 08:04:00+00:00,5182,00:40:48.100000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +316,https://rowsandall.com/rowers/workout/2579/emailcsv,https://rowsandall.com/rowers/workout/2579/emailtcx,0,0,2018-06-16 13:17:06+00:00,8196,00:52:10.500000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +317,https://rowsandall.com/rowers/workout/2950/emailcsv,https://rowsandall.com/rowers/workout/2950/emailtcx,0,0,2018-06-17 10:23:24+00:00,6004,01:00:54,Lunch Run,,Europe/Vienna,water,80.0,hwt +318,https://rowsandall.com/rowers/workout/2954/emailcsv,https://rowsandall.com/rowers/workout/2954/emailtcx,0,0,2018-06-17 10:23:24+00:00,6004,01:00:54,Lunch Run,,Europe/Vienna,water,80.0,hwt +319,https://rowsandall.com/rowers/workout/3035/emailcsv,https://rowsandall.com/rowers/workout/3035/emailtcx,0,17,2018-06-17 10:23:24+00:00,6004,01:00:54,Lunch Run, ,Europe/Vienna,other,80.0,hwt +320,https://rowsandall.com/rowers/workout/2578/emailcsv,https://rowsandall.com/rowers/workout/2578/emailtcx,0,0,2018-06-18 16:05:05+00:00,10313,00:55:54.900000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +321,https://rowsandall.com/rowers/workout/2948/emailcsv,https://rowsandall.com/rowers/workout/2948/emailtcx,0,0,2018-06-18 16:05:05+00:00,10313,00:55:54,Steady,,Europe/Prague,water,80.0,hwt +322,https://rowsandall.com/rowers/workout/2952/emailcsv,https://rowsandall.com/rowers/workout/2952/emailtcx,0,0,2018-06-18 16:05:05+00:00,10313,00:55:54,Steady,,Europe/Prague,water,80.0,hwt +323,https://rowsandall.com/rowers/workout/2573/emailcsv,https://rowsandall.com/rowers/workout/2573/emailtcx,0,0,2018-06-19 05:57:02+00:00,10437,00:56:06.900000,Pink,,Europe/Prague,water,80.0,lwt +324,https://rowsandall.com/rowers/workout/2577/emailcsv,https://rowsandall.com/rowers/workout/2577/emailtcx,0,0,2018-06-19 05:57:02+00:00,10437,00:56:06.900000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +325,https://rowsandall.com/rowers/workout/2946/emailcsv,https://rowsandall.com/rowers/workout/2946/emailtcx,0,0,2018-06-19 05:57:04+00:00,10437,00:56:04,3x6min,,Europe/Prague,water,80.0,hwt +326,https://rowsandall.com/rowers/workout/2570/emailcsv,https://rowsandall.com/rowers/workout/2570/emailtcx,0,0,2018-06-19 15:11:14.220000+00:00,7538,00:56:14.600000,Test,,Europe/Vienna,water,80.0,lwt +327,https://rowsandall.com/rowers/workout/2571/emailcsv,https://rowsandall.com/rowers/workout/2571/emailtcx,0,0,2018-06-20 18:24:08+00:00,7538,00:56:14.500000,Ritmo,,Europe/Vienna,water,80.0,lwt +328,https://rowsandall.com/rowers/workout/2858/emailcsv,https://rowsandall.com/rowers/workout/2858/emailtcx,0,0,2018-06-22 04:33:02.500000+00:00,3344,00:16:55.700000,imported through ema,imported through email,Europe/Prague,water,80.0,hwt +329,https://rowsandall.com/rowers/workout/2576/emailcsv,https://rowsandall.com/rowers/workout/2576/emailtcx,0,0,2018-06-22 05:41:04+00:00,9298,00:50:44,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +330,https://rowsandall.com/rowers/workout/2944/emailcsv,https://rowsandall.com/rowers/workout/2944/emailtcx,0,0,2018-06-22 05:41:04+00:00,9298,00:50:44,Steady,,Europe/Prague,water,80.0,hwt +331,https://rowsandall.com/rowers/workout/2575/emailcsv,https://rowsandall.com/rowers/workout/2575/emailtcx,0,0,2018-06-22 06:33:01+00:00,3344,00:16:55.700000,Imported workout,imported from Concept2 log,UTC,water,80.0,lwt +332,https://rowsandall.com/rowers/workout/2942/emailcsv,https://rowsandall.com/rowers/workout/2942/emailtcx,27,11,2018-06-22 06:33:02+00:00,3344,00:16:54,Steady (2),,Europe/Prague,water,80.0,hwt +333,https://rowsandall.com/rowers/workout/2940/emailcsv,https://rowsandall.com/rowers/workout/2940/emailtcx,0,0,2018-06-22 10:45:04+00:00,7540,00:56:14,test delete,,Europe/Vienna,water,80.0,hwt +334,https://rowsandall.com/rowers/workout/2725/emailcsv,https://rowsandall.com/rowers/workout/2725/emailtcx,0,0,2018-06-22 12:17:56+00:00,15642,01:15:00,Mike," + 3x20min/5min",Europe/Prague,water,80.0,lwt +335,https://rowsandall.com/rowers/workout/2726/emailcsv,https://rowsandall.com/rowers/workout/2726/emailtcx,0,0,2018-06-22 12:17:56+00:00,15642,01:15:00,Mike test,,Europe/Prague,water,80.0,lwt +336,https://rowsandall.com/rowers/workout/2848/emailcsv,https://rowsandall.com/rowers/workout/2848/emailtcx,0,0,2018-06-24 05:34:07+00:00,10194,00:57:48.900000,Imported,,Europe/Prague,water,80.0,lwt +337,https://rowsandall.com/rowers/workout/2849/emailcsv,https://rowsandall.com/rowers/workout/2849/emailtcx,0,0,2018-06-24 05:34:07.200000+00:00,10194,00:57:48.900000,Imported,,Europe/Prague,water,80.0,lwt +338,https://rowsandall.com/rowers/workout/2957/emailcsv,https://rowsandall.com/rowers/workout/2957/emailtcx,0,0,2018-06-24 05:34:07.200000+00:00,10194,00:57:48.900000,Imported,,Europe/Prague,water,80.0,hwt +339,https://rowsandall.com/rowers/workout/2938/emailcsv,https://rowsandall.com/rowers/workout/2938/emailtcx,0,0,2018-06-24 07:34:07+00:00,10194,00:57:43,Double,,Europe/Prague,water,80.0,hwt +340,https://rowsandall.com/rowers/workout/3059/emailcsv,https://rowsandall.com/rowers/workout/3059/emailtcx,0,0,2018-06-26 15:06:01+00:00,10393,01:00:55.900000,C2 Import Workout from 2018-06-26 15:06:01+00:00,imported from Concept2 log,UTC,water,80.0,lwt +341,https://rowsandall.com/rowers/workout/2936/emailcsv,https://rowsandall.com/rowers/workout/2936/emailtcx,42,30,2018-06-26 15:06:04+00:00,5651,00:31:24,Sprintervals,,Europe/Prague,water,80.0,hwt +342,https://rowsandall.com/rowers/workout/2934/emailcsv,https://rowsandall.com/rowers/workout/2934/emailtcx,0,0,2018-06-26 15:38:08+00:00,1056,00:05:12,Sprintervals (2),,Europe/Brussels,water,80.0,hwt +343,https://rowsandall.com/rowers/workout/2932/emailcsv,https://rowsandall.com/rowers/workout/2932/emailtcx,0,0,2018-06-26 15:44:06+00:00,3684,00:22:50,Sprintervals (3),,Europe/Prague,water,80.0,hwt +344,https://rowsandall.com/rowers/workout/2959/emailcsv,https://rowsandall.com/rowers/workout/2959/emailtcx,160,38,2018-06-30 05:31:02.200000+00:00,13878,01:21:16.600000,," + from speedcoach2v2.15 via rowsandall.com",Europe/Prague,water,80.0,hwt +345,https://rowsandall.com/rowers/workout/2958/emailcsv,https://rowsandall.com/rowers/workout/2958/emailtcx,0,0,2018-06-30 07:31:01+00:00,13878,01:21:16.600000,C2 Import Workout from 2018-06-30 07:31:01+00:00,imported from Concept2 log,UTC,water,80.0,lwt +346,https://rowsandall.com/rowers/workout/3030/emailcsv,https://rowsandall.com/rowers/workout/3030/emailtcx,80,6031,2018-07-01 11:57:30+00:00,12879,00:57:00,Ride to pub, ,Europe/Prague,other,80.0,hwt +347,https://rowsandall.com/rowers/workout/3031/emailcsv,https://rowsandall.com/rowers/workout/3031/emailtcx,0,0,2018-07-02 16:21:01+00:00,12532,01:09:16.500000,Test P,,Europe/Prague,water,80.0,hwt +348,https://rowsandall.com/rowers/workout/3058/emailcsv,https://rowsandall.com/rowers/workout/3058/emailtcx,0,0,2018-07-02 16:21:01+00:00,2858,00:15:40.600000,C2 Import Workout from 2018-07-02 16:21:01+00:00,imported from Concept2 log,UTC,water,80.0,lwt +349,https://rowsandall.com/rowers/workout/3063/emailcsv,https://rowsandall.com/rowers/workout/3063/emailtcx,120,79,2018-07-02 16:21:01+00:00,12532,01:09:16.500000,Test Strava Export,,Europe/Prague,water,80.0,hwt +350,https://rowsandall.com/rowers/workout/3064/emailcsv,https://rowsandall.com/rowers/workout/3064/emailtcx,120,79,2018-07-02 16:21:01+00:00,12532,01:09:16.500000,Test Strava Export,,Europe/Prague,water,80.0,hwt +351,https://rowsandall.com/rowers/workout/3065/emailcsv,https://rowsandall.com/rowers/workout/3065/emailtcx,0,0,2018-07-02 16:21:01+00:00,12532,01:09:16.500000,Test Strava Export,,Europe/Prague,water,80.0,hwt +352,https://rowsandall.com/rowers/workout/3066/emailcsv,https://rowsandall.com/rowers/workout/3066/emailtcx,120,79,2018-07-02 16:21:01+00:00,12532,01:09:16.500000,Test Strava Export,,Europe/Prague,water,80.0,hwt +353,https://rowsandall.com/rowers/workout/3068/emailcsv,https://rowsandall.com/rowers/workout/3068/emailtcx,0,0,2018-07-02 16:21:01+00:00,12532,01:09:16.500000,Should be exported To Strava,,Europe/Prague,water,80.0,hwt +354,https://rowsandall.com/rowers/workout/3070/emailcsv,https://rowsandall.com/rowers/workout/3070/emailtcx,120,79,2018-07-02 16:21:01+00:00,12532,01:09:16.500000,Should Not Be Exported To Strava,,Europe/Prague,water,80.0,hwt +355,https://rowsandall.com/rowers/workout/3057/emailcsv,https://rowsandall.com/rowers/workout/3057/emailtcx,0,0,2018-07-02 16:40:02+00:00,2001,00:08:29,C2 Import Workout from 2018-07-02 16:40:02+00:00,imported from Concept2 log,UTC,water,80.0,lwt +356,https://rowsandall.com/rowers/workout/3056/emailcsv,https://rowsandall.com/rowers/workout/3056/emailtcx,0,0,2018-07-02 16:50:02+00:00,2136,00:09:16.200000,C2 Import Workout from 2018-07-02 16:50:02+00:00,imported from Concept2 log,UTC,water,80.0,lwt +357,https://rowsandall.com/rowers/workout/3055/emailcsv,https://rowsandall.com/rowers/workout/3055/emailtcx,0,0,2018-07-02 17:01:02+00:00,2058,00:08:38.800000,C2 Import Workout from 2018-07-02 17:01:02+00:00,imported from Concept2 log,UTC,water,80.0,lwt +358,https://rowsandall.com/rowers/workout/3008/emailcsv,https://rowsandall.com/rowers/workout/3008/emailtcx,22,0,2018-07-02 17:01:05+00:00,2058,00:08:35,2ks with focus (4),,Europe/Prague,water,80.0,hwt +359,https://rowsandall.com/rowers/workout/3028/emailcsv,https://rowsandall.com/rowers/workout/3028/emailtcx,22,0,2018-07-02 17:01:05+00:00,2058,00:08:35,2ks with focus (4),,Europe/Prague,water,80.0,hwt +360,https://rowsandall.com/rowers/workout/3054/emailcsv,https://rowsandall.com/rowers/workout/3054/emailtcx,0,0,2018-07-02 17:11:02+00:00,2149,00:09:47.300000,C2 Import Workout from 2018-07-02 17:11:02+00:00,imported from Concept2 log,UTC,water,80.0,lwt +361,https://rowsandall.com/rowers/workout/3006/emailcsv,https://rowsandall.com/rowers/workout/3006/emailtcx,27,0,2018-07-02 17:11:05+00:00,2149,00:09:44,2ks with focus (5),,Europe/Prague,water,80.0,hwt +362,https://rowsandall.com/rowers/workout/3026/emailcsv,https://rowsandall.com/rowers/workout/3026/emailtcx,27,0,2018-07-02 17:11:05+00:00,2149,00:09:44,2ks with focus (5),,Europe/Prague,water,80.0,hwt +363,https://rowsandall.com/rowers/workout/3053/emailcsv,https://rowsandall.com/rowers/workout/3053/emailtcx,0,0,2018-07-02 17:24:02+00:00,1328,00:06:15.500000,C2 Import Workout from 2018-07-02 17:24:02+00:00,imported from Concept2 log,UTC,water,80.0,lwt +364,https://rowsandall.com/rowers/workout/3004/emailcsv,https://rowsandall.com/rowers/workout/3004/emailtcx,10,0,2018-07-02 17:24:05+00:00,1328,00:06:12,2ks with focus (6),,Europe/Prague,water,80.0,hwt +365,https://rowsandall.com/rowers/workout/3024/emailcsv,https://rowsandall.com/rowers/workout/3024/emailtcx,10,0,2018-07-02 17:24:05+00:00,1328,00:06:12,2ks with focus (6),,Europe/Prague,water,80.0,hwt +366,https://rowsandall.com/rowers/workout/2964/emailcsv,https://rowsandall.com/rowers/workout/2964/emailtcx,0,0,2018-07-04 15:06:42+00:00,7350,00:31:07,Test,,Europe/Prague,rower,80.0,hwt +367,https://rowsandall.com/rowers/workout/2965/emailcsv,https://rowsandall.com/rowers/workout/2965/emailtcx,0,103,2018-07-04 16:19:01+00:00,10471,01:00:27.200000,Test TRIMP,,Europe/Prague,water,80.0,hwt +368,https://rowsandall.com/rowers/workout/2966/emailcsv,https://rowsandall.com/rowers/workout/2966/emailtcx,0,98,2018-07-04 16:19:01+00:00,10471,01:00:27.200000,Test Rscore,,Europe/Prague,water,80.0,hwt +369,https://rowsandall.com/rowers/workout/3052/emailcsv,https://rowsandall.com/rowers/workout/3052/emailtcx,0,0,2018-07-04 16:19:01+00:00,10471,01:00:27.100000,C2 Import Workout from 2018-07-04 16:19:01+00:00,imported from Concept2 log,UTC,water,80.0,lwt +370,https://rowsandall.com/rowers/workout/2967/emailcsv,https://rowsandall.com/rowers/workout/2967/emailtcx,0,96,2018-07-04 16:19:02+00:00,10471,01:00:26,Startjes," + from csv via rowsandall.com",Europe/Prague,water,80.0,hwt +371,https://rowsandall.com/rowers/workout/3002/emailcsv,https://rowsandall.com/rowers/workout/3002/emailtcx,0,98,2018-07-04 16:19:02+00:00,10471,01:00:26,Startjes,,Europe/Prague,water,80.0,hwt +372,https://rowsandall.com/rowers/workout/3022/emailcsv,https://rowsandall.com/rowers/workout/3022/emailtcx,0,98,2018-07-04 16:19:02+00:00,10471,01:00:26,Startjes,,Europe/Prague,water,80.0,hwt +373,https://rowsandall.com/rowers/workout/3051/emailcsv,https://rowsandall.com/rowers/workout/3051/emailtcx,0,0,2018-07-07 07:43:02+00:00,2874,00:16:39.700000,C2 Import Workout from 2018-07-07 07:43:02+00:00,imported from Concept2 log,UTC,water,80.0,lwt +374,https://rowsandall.com/rowers/workout/3000/emailcsv,https://rowsandall.com/rowers/workout/3000/emailtcx,17,0,2018-07-07 07:43:03+00:00,2874,00:16:38,2x race prep,,Europe/Prague,water,80.0,hwt +375,https://rowsandall.com/rowers/workout/3020/emailcsv,https://rowsandall.com/rowers/workout/3020/emailtcx,17,0,2018-07-07 07:43:03+00:00,2874,00:16:38,2x race prep,,Europe/Prague,water,80.0,hwt +376,https://rowsandall.com/rowers/workout/3050/emailcsv,https://rowsandall.com/rowers/workout/3050/emailtcx,0,0,2018-07-07 08:01:01+00:00,743,00:02:45.500000,C2 Import Workout from 2018-07-07 08:01:01+00:00,imported from Concept2 log,UTC,water,80.0,lwt +377,https://rowsandall.com/rowers/workout/2998/emailcsv,https://rowsandall.com/rowers/workout/2998/emailtcx,2,0,2018-07-07 08:01:02+00:00,743,00:02:44,2x race prep (2),,Europe/Prague,bike,80.0,hwt +378,https://rowsandall.com/rowers/workout/3018/emailcsv,https://rowsandall.com/rowers/workout/3018/emailtcx,2,0,2018-07-07 08:01:02+00:00,743,00:02:44,2x race prep (2),,Europe/Prague,water,80.0,hwt +379,https://rowsandall.com/rowers/workout/3049/emailcsv,https://rowsandall.com/rowers/workout/3049/emailtcx,0,0,2018-07-07 08:05:03+00:00,2827,00:15:21.800000,C2 Import Workout from 2018-07-07 08:05:03+00:00,imported from Concept2 log,UTC,water,80.0,lwt +380,https://rowsandall.com/rowers/workout/2996/emailcsv,https://rowsandall.com/rowers/workout/2996/emailtcx,21,0,2018-07-07 08:05:06+00:00,2827,00:15:18,2x race prep (3),,Europe/Prague,water,80.0,hwt +381,https://rowsandall.com/rowers/workout/3016/emailcsv,https://rowsandall.com/rowers/workout/3016/emailtcx,21,0,2018-07-07 08:05:06+00:00,2827,00:15:18,2x race prep (3),,Europe/Prague,water,80.0,hwt +382,https://rowsandall.com/rowers/workout/3048/emailcsv,https://rowsandall.com/rowers/workout/3048/emailtcx,0,0,2018-07-07 08:22:01+00:00,749,00:03:12.500000,C2 Import Workout from 2018-07-07 08:22:01+00:00,imported from Concept2 log,UTC,water,80.0,lwt +383,https://rowsandall.com/rowers/workout/2994/emailcsv,https://rowsandall.com/rowers/workout/2994/emailtcx,4,0,2018-07-07 08:22:02+00:00,749,00:03:11,2x race prep (4),,Europe/Prague,water,80.0,hwt +384,https://rowsandall.com/rowers/workout/3014/emailcsv,https://rowsandall.com/rowers/workout/3014/emailtcx,4,0,2018-07-07 08:22:02+00:00,749,00:03:11,2x race prep (4),,Europe/Prague,water,80.0,hwt +385,https://rowsandall.com/rowers/workout/3047/emailcsv,https://rowsandall.com/rowers/workout/3047/emailtcx,0,0,2018-07-07 08:27:03+00:00,2058,00:11:55.800000,C2 Import Workout from 2018-07-07 08:27:03+00:00,imported from Concept2 log,UTC,water,80.0,lwt +386,https://rowsandall.com/rowers/workout/2992/emailcsv,https://rowsandall.com/rowers/workout/2992/emailtcx,13,14,2018-07-07 08:27:06+00:00,2058,00:11:52,2x race prep (5),,Europe/Prague,water,75.0,hwt +387,https://rowsandall.com/rowers/workout/3012/emailcsv,https://rowsandall.com/rowers/workout/3012/emailtcx,13,15,2018-07-07 08:27:06+00:00,2058,00:11:52,2x race prep (5),,Europe/Prague,water,80.0,hwt +388,https://rowsandall.com/rowers/workout/2969/emailcsv,https://rowsandall.com/rowers/workout/2969/emailtcx,100,116,2018-07-08 13:17:02+00:00,10372,01:00:18.100000,HRTSS,,Europe/Prague,water,80.0,hwt +389,https://rowsandall.com/rowers/workout/3046/emailcsv,https://rowsandall.com/rowers/workout/3046/emailtcx,0,0,2018-07-08 13:17:02+00:00,10372,01:00:18,C2 Import Workout from 2018-07-08 13:17:02+00:00,imported from Concept2 log,UTC,water,80.0,lwt +390,https://rowsandall.com/rowers/workout/3034/emailcsv,https://rowsandall.com/rowers/workout/3034/emailtcx,0,0,2018-07-08 13:17:08+00:00,10372,01:00:12,Tempovky,,Europe/Prague,water,80.0,hwt +391,https://rowsandall.com/rowers/workout/3045/emailcsv,https://rowsandall.com/rowers/workout/3045/emailtcx,0,0,2018-07-13 13:08:00+00:00,2481,00:13:41,C2 Import Workout from 2018-07-13 13:08:00+00:00,imported from Concept2 log,UTC,water,80.0,lwt +392,https://rowsandall.com/rowers/workout/3044/emailcsv,https://rowsandall.com/rowers/workout/3044/emailtcx,0,0,2018-07-13 13:23:01+00:00,4085,00:22:09.200000,C2 Import Workout from 2018-07-13 13:23:01+00:00,imported from Concept2 log,UTC,water,80.0,lwt +393,https://rowsandall.com/rowers/workout/3043/emailcsv,https://rowsandall.com/rowers/workout/3043/emailtcx,0,0,2018-07-14 08:59:02+00:00,7051,00:50:00.500000,C2 Import Workout from 2018-07-14 08:59:02+00:00,imported from Concept2 log,UTC,water,80.0,lwt +394,https://rowsandall.com/rowers/workout/3042/emailcsv,https://rowsandall.com/rowers/workout/3042/emailtcx,0,0,2018-07-14 13:33:00+00:00,4471,00:51:42.100000,C2 Import Workout from 2018-07-14 13:33:00+00:00,imported from Concept2 log,UTC,water,80.0,lwt +395,https://rowsandall.com/rowers/workout/3041/emailcsv,https://rowsandall.com/rowers/workout/3041/emailtcx,0,0,2018-07-14 15:48:01+00:00,4486,00:56:58.100000,C2 Import Workout from 2018-07-14 15:48:01+00:00,imported from Concept2 log,UTC,water,80.0,lwt +396,https://rowsandall.com/rowers/workout/3040/emailcsv,https://rowsandall.com/rowers/workout/3040/emailtcx,0,0,2018-07-15 06:10:01+00:00,4233,00:29:17,C2 Import Workout from 2018-07-15 06:10:01+00:00,imported from Concept2 log,UTC,water,80.0,lwt +397,https://rowsandall.com/rowers/workout/3039/emailcsv,https://rowsandall.com/rowers/workout/3039/emailtcx,0,0,2018-07-15 09:01:01+00:00,4978,00:41:45,C2 Import Workout from 2018-07-15 09:01:01+00:00,imported from Concept2 log,UTC,water,80.0,lwt +398,https://rowsandall.com/rowers/workout/3038/emailcsv,https://rowsandall.com/rowers/workout/3038/emailtcx,0,0,2018-07-18 05:00:01+00:00,11159,00:59:58,C2 Import Workout from 2018-07-18 05:00:01+00:00,imported from Concept2 log,UTC,water,80.0,lwt +399,https://rowsandall.com/rowers/workout/3037/emailcsv,https://rowsandall.com/rowers/workout/3037/emailtcx,0,0,2018-07-20 09:29:01+00:00,8436,00:45:44.500000,C2 Import Workout from 2018-07-20 09:29:01+00:00,imported from Concept2 log,UTC,water,80.0,lwt +400,https://rowsandall.com/rowers/workout/3036/emailcsv,https://rowsandall.com/rowers/workout/3036/emailtcx,0,0,2018-07-21 05:13:03+00:00,9639,00:54:19.500000,C2 Import Workout from 2018-07-21 05:13:03+00:00,imported from Concept2 log,UTC,water,80.0,lwt +401,https://rowsandall.com/rowers/workout/3061/emailcsv,https://rowsandall.com/rowers/workout/3061/emailtcx,77,43,2018-08-17 05:16:03+00:00,8315,00:45:11.400000,Brnenske 2k,,Europe/Prague,water,80.0,hwt +402,https://rowsandall.com/rowers/workout/3075/emailcsv,https://rowsandall.com/rowers/workout/3075/emailtcx,83,28,2018-08-18 07:54:04+00:00,9590,00:55:08,Technique 2x, ,Europe/Prague,water,80.0,hwt +403,https://rowsandall.com/rowers/workout/3074/emailcsv,https://rowsandall.com/rowers/workout/3074/emailtcx,94,17,2018-08-26 22:00:01.500000+00:00,9903,00:54:36,Imported,,Europe/Prague,bike,80.0,hwt +404,https://rowsandall.com/rowers/workout/3076/emailcsv,https://rowsandall.com/rowers/workout/3076/emailtcx,94,1104,2018-08-26 22:00:01.500000+00:00,9903,00:54:36,Imported,,Europe/Prague,bike,80.0,hwt +405,https://rowsandall.com/rowers/workout/3072/emailcsv,https://rowsandall.com/rowers/workout/3072/emailtcx,0,0,2018-08-27 14:36:47+00:00,5855,00:14:58.700000,Bike Erg,,Europe/Prague,bikeerg,80.0,hwt +406,https://rowsandall.com/rowers/workout/3073/emailcsv,https://rowsandall.com/rowers/workout/3073/emailtcx,15,4,2018-08-27 14:36:47+00:00,5855,00:14:58.700000,Bike Erg,,Europe/Prague,bike,80.0,hwt +407,https://rowsandall.com/rowers/workout/3077/emailcsv,https://rowsandall.com/rowers/workout/3077/emailtcx,0,51,2018-09-11 05:31:02+00:00,7490,00:35:07.500000,7.5k,,Europe/Prague,water,80.0,hwt +408,https://rowsandall.com/rowers/workout/3080/emailcsv,https://rowsandall.com/rowers/workout/3080/emailtcx,0,0,2018-09-12 04:17:04+00:00,5921,00:34:50,Sofia part II, ,Europe/Sofia,other,80.0,hwt +409,https://rowsandall.com/rowers/workout/3079/emailcsv,https://rowsandall.com/rowers/workout/3079/emailtcx,87,60,2018-09-13 12:15:58+00:00,23476,00:59:59.700000,Mike 2,,Europe/Prague,rower,80.0,hwt +410,https://rowsandall.com/rowers/workout/3078/emailcsv,https://rowsandall.com/rowers/workout/3078/emailtcx,94,59,2018-09-14 13:02:04+00:00,24267,00:59:57.200000,Mike,,Europe/Prague,rower,80.0,hwt diff --git a/static/css/rowsandall2.css b/static/css/rowsandall2.css index 1d72ba77..0a324392 100644 --- a/static/css/rowsandall2.css +++ b/static/css/rowsandall2.css @@ -85,12 +85,14 @@ cox { */ th { - font-weight: bold; + font-weight: bold; + align: left; } .listtable tbody tr:nth-of-type(even) { background-color: #DDD; } .listtable thead th { font-weight: bold; + align: left; } @@ -231,7 +233,7 @@ th.rotate > div > span { } .site-announcement { - font: 1.1em/1.5em sans-serif; + font: 1.0em/1.2em sans-serif; text-decoration: none; display: block; padding: .2em .5em .2em .5em; @@ -243,6 +245,15 @@ th.rotate > div > span { border: solid 1px #333; } + + +.contentli { + margin-left: 30px; + display: list-item; + list-style-type: circle; +} + + .dot { border-radius: 50%; display: block; @@ -254,131 +265,53 @@ th.rotate > div > span { .dot:hover { text-decoration: none; -} +} + +.rounder { + border-radius: 10px; + display: block; + overflow-x: hidden; + border: solid 1px #333; + padding: 5px; + margin: 2px; +} + +.vignet { + border-radius: 50%; + display: block; + overflow: hidden; + padding: 5px; + margin: 5px; + -webkit-box-shadow: inset 0px 0px 85px rgba(0,0,0,0.4); + -moz-box-shadow: inset 0px 0px 85px rgba(0,0,0,0.4); + box-shadow: inset 0px 0px 85px rgba(0,0,0,0.4); + + line-height: 0; /* ensure no space between bottom */ + +} + +.vignet img { + position: relative; + transform: scale(1.5); + z-index: -1; +} .button { font: 1.1em/1.5em sans-serif; text-decoration: none; display: block; - /* width: 100%; */ - color: white; - padding: 0.2em 0.0em 0.2em 0.0em; + margin: 0; + /* color: white; */ + padding: 0; zoom: 1; -/* border-radius: .5em; */ -/* -moz-border-radius: .5em; */ -/* -webkit-border-radius: .5em; */ -/* -box-shadow: 0 1px 3px rgba(0,0,0,0.5); */ -/* -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.5); */ -/* -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.5); /* -/* text-shadow: 0 -1px 1px rgba(0,0,0,0.25); */ text-align: center; } -.input { - font: 1.1em/1.5em sans-serif; - text-decoration: none; - display: block; - /* width: 100%; */ - color: white; - padding: 0.2em 0.0em 0.2em 0.0em; - zoom: 1; - border-radius: .5em; - -moz-border-radius: .5em; - -webkit-border-radius: .5em; -/* -box-shadow: 0 1px 3px rgba(0,0,0,0.5); */ -/* -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.5); */ -/* -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.5); */ -/* text-shadow: 0 -1px 1px rgba(0,0,0,0.25); */ - text-align: center; -} - -.blueicon { - text-decoration: none; - display: block; - /* width: 100%; */ - color: #1c75bc; - padding: 0.2em 0.0em 0.2em 0.0em; - zoom: 1; - - - -a.button { - color: white; - } - -.button:hover { - background: #d8e6ff; /* old browsers */ - text-decoration: none; -} - .button:active { - position: relative; - top: 1px; + position: relative; + top: 1px; } -.bigrounded { - -webkit-border-radius: 2em; - -moz-border-radius: 2em; - border-radius: 2em; -} -.medium { - font-size: 1.2em; -} -.small { - font-size: 1.0em; -} - -/* black */ -.black { - color: #d7d7d7; - border: solid 1px #333; - background: #333; -} -.black:hover { - background: #000; -} -.black:active { - color: #666; -} - -/* gray */ -.gray { - color: #e9e9e9; - border: solid 1px #555; - background: #6e6e6e; -} -.gray:hover { - background: #616161; -} -.gray:active { - color: #afafaf; -} - -/* white */ -.white { - color: #606060; - border: solid 1px #b7b7b7; - background: #fff; -} -.white:hover { - background: #ededed; -} -.white:active { - color: #999; -} - -/* orange */ -.orange { - color: #fef4e9; - border: solid 1px #da7c0c; - background: #f78d1d; -} -.orange:hover { - background: #f47c20; -} -.orange:active { - color: #fcd3a5; -} /* red */ .red { @@ -393,9 +326,17 @@ a.button { color: #de898c; } -/* blue */ -.bluetext { - color: #27aae2; +/* green */ +.green { + color: #e8f0de; + border: solid 1px #538312; + background: #64991e; +} +.green:hover { + background: #538018; +} +.green:active { + color: #a9c08c; } .blue { @@ -411,210 +352,31 @@ a.button { color: #ffffff; } -.rbluetext { - color: #27aae1; +.leafletmap { + padding:0; + margin:0; + display: grid; + grid-gap: 0; } -.rdarkbluetext { - color: #1c75bc; +/* orange */ +.orange { + color: #fef4e9; + border: solid 1px #da7c0c; + background: #f78d1d; } - -.rblue { - color: #fae7e9; - border: solid 1px #27aae1; - background: #27aae1; +.orange:hover { + background: #f47c20; } - -.rblue:active { - color: #ffffff; -} - -.rblue:hover { - background: #1c75bc; - border: solid 1px #27aae1; -} - - -/* rosy */ -.rosy { - color: #fae7e9; - border: solid 1px #b73948; - background: #da5867; -} -.rosy:hover { - background: #ba4b58; -} -.rosy:active { - color: #dca4ab; -} - -/* green */ -.green { - color: #e8f0de; - border: solid 1px #538312; - background: #64991e; -} -.green:hover { - background: #538018; -} -.green:active { - color: #a9c08c; -} - -/* palegreen */ -.palegreen { - background: palegreen; - box-shadow:inset 0px 0px 0px 6px #fff; - -moz-box-shadow:inset 0px 0px 0px 6px #fff; - box-shadow:inset 0px 0px 0px 6px #fff; -} - -/* paleblue */ -.paleblue { -# padding: 8px; - background: aliceblue; - box-shadow:inset 0px 0px 0px 6px #fff; - -moz-box-shadow:inset 0px 0px 0px 6px #fff; - box-shadow:inset 0px 0px 0px 6px #fff; -} - -/* lightsalmon */ -.lightsalmon { -# padding: 4px; - background: lightsalmon; - box-shadow:inset 0px 0px 0px 6px #fff; - -moz-box-shadow:inset 0px 0px 0px 6px #fff; - box-shadow:inset 0px 0px 0px 6px #fff; -} - -/* filler */ -.filler { - background: darkgray; - box-shadow:inset 0px 0px 0px 6px #fff; - -moz-box-shadow:inset 0px 0px 0px 6px #fff; - box-shadow:inset 0px 0px 0px 6px #fff; -} - -.padded { - padding: 10px; -} - -/* pink */ -.pink { - color: #feeef5; - border: solid 1px #d2729e; - background: #f895c2; -} -.pink:hover { - background: #d57ea5; -} -.pink:active { - color: #f3c3d9; -} - - -.greenbar { - border: 1px solid #666; - color: #000; - - - overflow: hidden; - background-color: #8f8; -/* padding: 10px 0; */ - text-align: center; -} - - -#footer { - text-align:center; - } - -.container_12, -.container_16, -.container_24 { - background-color: #fff; - background-repeat: repeat-y; -/* margin-bottom: 20px; */ -} - -.container_12 { -/* background-image: url(../img/12_col.gif); */ -} - - -/* Style The Dropdown Button */ -.dropbtn { - color: white; -} - -/* The container
    - needed to position the dropdown content */ -.dropdown { - position: relative; - display: inline-block; -} - -/* Dropdown Content (Hidden by Default) */ -.dropdown-content { - display: none; - position: absolute; - min-width: 160px; -/* box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); */ - z-index: 999; -} - -/* Links inside the dropdown */ -.dropdown-content a { - text-decoration: none; - display: block; - z-index: 999; -} - -/* Change color of dropdown links on hover */ -/* .dropdown-content a:hover {background-color: #f1f1f1} */ - -/* Show the dropdown menu on hover */ -.dropdown:hover .dropdown-content { - display: block; -} - -/* Change the background color of the dropdown button when the dropdown content is shown */ -.dropdown:hover .dropbtn { - background-color: #3e8e41; -} - -.flexplot { - position: relative; - z-index: 10; -} - -a.wh:link { - color: #e9e9e9; -} - -a.wh:visited { - color: #e9e9e9; -} - -a.wh:hover { - color: #e9e9e9; -} - - -.bk-canvas-map { - overflow: hidden; - } - - -/* container */ -.container { - padding: 5% 5%; +.orange:active { + color: #fcd3a5; } /* CSS talk bubble */ .talk-bubble { margin: 40px; - display: inline-block; - position: relative; + display: inline-block; + position: relative; width: 200px; height: auto; background-color: lightyellow; @@ -865,6 +627,285 @@ a.wh:hover { } + +/* palegreen */ +.palegreen { + background: palegreen; + box-shadow:inset 0px 0px 0px 6px #fff; + -moz-box-shadow:inset 0px 0px 0px 6px #fff; + box-shadow:inset 0px 0px 0px 6px #fff; +} + +/* paleblue */ +.paleblue { +# padding: 8px; + background: aliceblue; + box-shadow:inset 0px 0px 0px 6px #fff; + -moz-box-shadow:inset 0px 0px 0px 6px #fff; + box-shadow:inset 0px 0px 0px 6px #fff; +} + +/* lightsalmon */ +.lightsalmon { +# padding: 4px; + background: lightsalmon; + box-shadow:inset 0px 0px 0px 6px #fff; + -moz-box-shadow:inset 0px 0px 0px 6px #fff; + box-shadow:inset 0px 0px 0px 6px #fff; +} + +/* filler */ +.filler { + background: darkgray; + box-shadow:inset 0px 0px 0px 6px #fff; + -moz-box-shadow:inset 0px 0px 0px 6px #fff; + box-shadow:inset 0px 0px 0px 6px #fff; +} + +.padded { + padding: 10px; +} + + +.input { + font: 1.1em/1.5em sans-serif; + text-decoration: none; + display: block; + /* width: 100%; */ + color: white; + padding: 0.2em 0.0em 0.2em 0.0em; + zoom: 1; + border-radius: .5em; + -moz-border-radius: .5em; + -webkit-border-radius: .5em; +/* -box-shadow: 0 1px 3px rgba(0,0,0,0.5); */ +/* -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.5); */ +/* -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.5); */ +/* text-shadow: 0 -1px 1px rgba(0,0,0,0.25); */ + text-align: center; +} + +.blueicon { + text-decoration: none; + display: block; + /* width: 100%; */ + color: #1c75bc; + padding: 0.2em 0.0em 0.2em 0.0em; + zoom: 1; +} + + + + +.bigrounded { + -webkit-border-radius: 2em; + -moz-border-radius: 2em; + border-radius: 2em; +} +.medium { + font-size: 1.2em; +} +.small { + font-size: 1.0em; +} + +/* black */ +.black { + color: #d7d7d7; + border: solid 1px #333; + background: #333; +} +.black:hover { + background: #000; +} +.black:active { + color: #666; +} + +/* gray */ +.gray { + color: #e9e9e9; + border: solid 1px #555; + background: #6e6e6e; +} +.gray:hover { + background: #616161; +} +.gray:active { + color: #afafaf; +} + +/* white */ +.white { + color: #606060; + border: solid 1px #b7b7b7; + background: #fff; +} +.white:hover { + background: #ededed; +} +.white:active { + color: #999; +} + + + +/* blue */ +.bluetext { + color: #27aae2; +} + + +.rbluetext { + color: #27aae1; +} + +.rdarkbluetext { + color: #1c75bc; +} + +.rblue { + color: #fae7e9; + border: solid 1px #27aae1; + background: #27aae1; +} + +.rblue:active { + color: #ffffff; +} + +.rblue:hover { + background: #1c75bc; + border: solid 1px #27aae1; +} + + +/* rosy */ +.rosy { + color: #fae7e9; + border: solid 1px #b73948; + background: #da5867; +} +.rosy:hover { + background: #ba4b58; +} +.rosy:active { + color: #dca4ab; +} + + +/* pink */ +.pink { + color: #feeef5; + border: solid 1px #d2729e; + background: #f895c2; +} +.pink:hover { + background: #d57ea5; +} +.pink:active { + color: #f3c3d9; +} + + +.greenbar { + border: 1px solid #666; + color: #000; + + + overflow: hidden; + background-color: #8f8; +/* padding: 10px 0; */ + text-align: center; +} + + +#footer { + text-align:center; + } + +.container_12, +.container_16, +.container_24 { + background-color: #fff; + background-repeat: repeat-y; +/* margin-bottom: 20px; */ +} + +.container_12 { +/* background-image: url(../img/12_col.gif); */ +} + + +/* Style The Dropdown Button */ +.dropbtn { + color: white; +} + +/* The container
    - needed to position the dropdown content */ +.dropdown { + position: relative; + display: inline-block; +} + +/* Dropdown Content (Hidden by Default) */ +.dropdown-content { + display: none; + position: absolute; + min-width: 160px; +/* box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); */ + z-index: 999; +} + +/* Links inside the dropdown */ +.dropdown-content a { + text-decoration: none; + display: block; + z-index: 999; +} + +/* Change color of dropdown links on hover */ +/* .dropdown-content a:hover {background-color: #f1f1f1} */ + +/* Show the dropdown menu on hover */ +.dropdown:hover .dropdown-content { + display: block; +} + +/* Change the background color of the dropdown button when the dropdown content is shown */ +.dropdown:hover .dropbtn { + background-color: #3e8e41; +} + +.flexplot { + position: relative; + z-index: 10; +} + +a.wh:link { + color: #e9e9e9; +} + +a.wh:visited { + color: #e9e9e9; +} + +a.wh:hover { + color: #e9e9e9; +} + + +.bk-canvas-map { + overflow: hidden; + } + + +/* container */ +.container { + padding: 5% 5%; +} + + .wrapwords{ -ms-word-break: break-all; word-break: break-all; @@ -890,25 +931,16 @@ a.wh:hover { } -.cd-accordion-menu input[type=checkbox] { - /* hide native checkbox */ - position: absolute; - opacity: 0; -} -.cd-accordion-menu label, .cd-accordion-menu a { - position: relative; - display: block; - padding: 18px 18px 18px 64px; - background: #4d5158; - box-shadow: inset 0 -1px #555960; - color: #ffffff; - font-size: 1.6rem; +.mapdiv { + padding: 0 !important; } +.mapdiv img { + float: left !important; + padding: 0 !important; +} -.cd-accordion-menu input[type=checkbox]:checked + label + ul, -.cd-accordion-menu input[type=checkbox]:checked + label:nth-of-type(n) + ul { - /* use label:nth-of-type(n) to fix a bug on safari (<= 8.0.8) with multiple adjacent-sibling selectors*/ - /* show children when item is checked */ - display: block; -} +.paypalpix { + width: auto !important; + max-width: 1px; +} diff --git a/static/css/style3.css b/static/css/style3.css deleted file mode 100644 index 2c78de87..00000000 --- a/static/css/style3.css +++ /dev/null @@ -1,125 +0,0 @@ -* {box-sizing: border-box;} - - .wrapper { - max-width: 1400px; - margin: 0 auto; - font: 1.2em Helvetica, arial, sans-serif; - } - - .wrapper > * { - border: 2px solid #f08c00; - padding: 5px; - } - - nav ul { - list-style: none; - margin: 0; - padding: 0; - display: flex; - justify-content: space-between; - } - - header ul { - list-style: none; - margin: 0; - padding: 0; - } - - aside ul { - list-style: none; - margin: 0; - padding: 0; - } - - user ul { - list-style: none; - margin: 0; - padding: 0; - display: flex; - justify-content: flex-end; - } - -.main-head { - grid-area: header; -} -.main-user { - grid-area: user; -} - -.content { - grid-area: content; -} -.main-nav { - grid-area: nav; -} -.side { - grid-area: sidebar; -} -.ad { - grid-area: ad; -} -.main-footer { - grid-area: footer; -} -.wrapper { - display: grid; - grid-gap: 2px; - grid-template-areas: - "header" - "user" - "nav" - "content" - "sidebar" - "ad" - "footer"; -} - -@media (max-width: 449px) { - nav a { - font-size: 0px; - } - - nav a i { - font-size: 20px; - } -} - -@media (min-width: 450px) { - .wrapper { - grid-template-columns: 1fr 3fr; - grid-template-areas: - "header header" - "user user" - "nav nav" - "sidebar content" - "ad footer"; - } - nav ul { - display: flex; - justify-content: space-between; - } - header ul { - display: flex; - justify-content: space-between; - } - - -} -@media (min-width: 768px) { - .wrapper { - grid-template-columns: 1fr 5fr 1fr; - grid-template-areas: - "header header header user" - "nav nav nav nav" - "sidebar content content content" - "sidebar content content content" - "ad footer footer footer" - } - nav ul { - flex-direction: row; - } - header ul { - flex-direction: row; - } - -} diff --git a/static/css/styles2.css b/static/css/styles2.css index 00c2282b..7714b404 100644 --- a/static/css/styles2.css +++ b/static/css/styles2.css @@ -10,7 +10,11 @@ .wrapper > * { /* border: 2px solid #f08c00; */ border 0; - padding: 5px; + padding: 5px; + } + + .wrapper nav { + padding: 0; } a { @@ -25,6 +29,8 @@ justify-content: space-between; } + + footer ul { list-style: none; display: flex; @@ -195,7 +201,6 @@ aside .cd-accordion-menu label { cursor: pointer; - background: #35383d; } @@ -217,6 +222,84 @@ display: block; } + + main .cd-accordion-menu { + width: 100%; + max-width: 600px; + } + + main .cd-accordion-menu ul { + /* by default hide all sub menus */ + display: none; + } + + main .cd-accordion-menu li { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } + + main .cd-accordion-menu input[type=checkbox] { + /* hide native checkbox */ + position: absolute; + opacity: 0; + } + + main .cd-accordion-menu label { + position: relative; + display: block; + font-size: 1.0em; + } + + + + main .cd-accordion-menu ul, + main .cd-accordion-menu li { + list-style: none; + } + + main .cd-accordion-menu label::before + { + /* icons */ + font: normal normal normal 1.0em/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + content: '\f0da'; + display: inline-block; + width: 16px; + height: 16px; + + -webkit-transform: translateY(0%); + -moz-transform: translateY(0%); + -ms-transform: translateY(0%); + -o-transform: translateY(0%); + transform: translateY(0%); + } + + main .cd-accordion-menu label { + cursor: pointer; + } + + + main .cd-accordion-menu input[type=checkbox]:checked + label::before { + /* rotate arrow */ + -webkit-transform: translateY(20%) rotate(90deg); + -moz-transform: translateY(20%) rotate(90deg); + -ms-transform: translateY(20%) rotate(90deg); + -o-transform: translateY(20%) rotate(90deg); + transform: translateY(20%) rotate(90deg); + + } + + + main .cd-accordion-menu input[type=checkbox]:checked + label + ul, + main .cd-accordion-menu input[type=checkbox]:checked + label:nth-of-type(n) + ul { + /* use label:nth-of-type(n) to fix a bug on safari (<= 8.0.8) with multiple adjacent-sibling selectors*/ + /* show children when item is checked */ + display: block; + } + user ul { @@ -232,23 +315,47 @@ } nav li { - margin: 0.2em; + margin: 0; + padding: 0.2em; } nav a { - color: white; - transition: all .2s ease-in-out; + margin: 1em; } - + + nav a, nav a i { color: white; } + nav li:hover, + nav li:hover a, + nav li:hover a i { + color: #1c75bc; + background: white; + } + nav li.selected { + color: #1c75bc; + background: white; + } + + .nav-active, + .nav-active a, + .nav-active a i { + color: #1c75bc; + background: white; + } + + + .main-head { grid-area: header; background: #ededed; } + + + .main-user { grid-area: user; background: #ededed; @@ -257,17 +364,74 @@ .content { grid-area: content; padding: 1.2em 1.2em 1.2em 1.2em; + font-size: 1.3rem; } + +.content h1 { + font-size 1.3em; + font-weight: normal; +} + +.content h2 { + font-size 1.2em; + font-weight: normal; +} + +.content h3 { + font-size 1.1em; + font-weight: bold; +} + +.content em { + font-style: italic; +} + .main-nav { grid-area: nav; background: #1c75bc; } + +.side-nav { + grid-area: side-nav; + background: #1c75bc; +} + .side { grid-area: sidebar; background: #35383d; + /* border-top: 1px solid #dddddd; */ padding: 0; } +.sideheader { + grid-area: side-header; + color: #dddddd; + background: #35383d; + padding: 0; +} + +.side h2 { + color: #dddddd; + font-weight: bold; + text-align: left; + font-size: 1.0em; + padding: 5px; + margin: 0; + margin-left: 30px; + padding-bottom: 0; +} + +.sideheader h1 { + font-weight: bold; + text-align: left; + font-size: 1.4em; + color: #dddddd; + padding: 5px; + margin: 0; + margin-left: 30px; + padding-bottom: 0; +} + .ad { grid-area: ad; background: #35383d; @@ -283,17 +447,90 @@ .wrapper { display: grid; /* grid-gap: 2px; */ - grid-template-areas: + grid-template-areas: "header" - "user" - "nav" + "user" + "nav" "content" "sidebar" "ad" "footer"; } -@media (max-width: 449px) { +.main-content { + list-style: none; + margin: 0; +} + + +.main-content li { + margin: 0; + overflow-x: hidden; +} + + + +.main-content li.grid_2 { + grid-column-end: span 1; +} + +.main-content li.grid_3 { + grid-column-end: span 1; +} + +.main-content li.grid_3 { + grid-column-end: span 4; +} + + +.maxheight { + max-height: 300px; + overflow: scroll; +} + +.main-content li.grid_4 { + grid-column-end: span 1; +} + +.main-content li img { + display: block; + width: 100%; + height: auto; + padding: 5px; +} + + + +@media (min-height: 600px) { + .maxheight { + max-height: 450px; + overflow: scroll; + } +} + +@media (min-height: 600px) { + .maxheight { + max-height: 450px; + overflow: scroll; + } +} + +@media (min-height: 800px) { + .maxheight { + max-height: 600px; + overflow: scroll; + } +} + +@media (min-height: 1000px) { + .maxheight { + max-height: 800px; + overflow: scroll; + } +} + + +@media (max-width: 600px) { nav a { font-size: 0px; } @@ -303,17 +540,19 @@ } } -@media (min-width: 450px) { +@media (min-width: 450px) { .wrapper { grid-template-columns: 1fr 3fr; grid-template-areas: "header header" "user user" - "nav nav" + "side-nav nav" + "sidebar content" "sidebar content" "ad footer"; } + nav ul { display: flex; justify-content: space-between; @@ -325,21 +564,38 @@ justify-content: space-between; } + .main-content { + display: grid; + grid-template-columns: repeat(2,1fr); + grid-gap: 10px; + } + + .main-content li.grid_2 { + grid-column-end: span 2; + } + + .main-content li.grid_3 { + grid-column-end: span 2; + } + + .main-content li.grid_4 { + grid-column-end: span 2; + } } - -@media (min-width: 768px) { +@media (min-width: 768px) { .wrapper { grid-template-columns: 1fr 4fr 1fr; grid-template-areas: "header header user" - "nav nav nav" - "sidebar content content" - "sidebar content content" - "sidebar footer footer" - "ad footer footer" + "side-nav nav nav" + "sidebar content content" + "sidebar content content" + "sidebar content content" + "sidebar footer footer" + "ad footer footer" } nav ul { @@ -350,10 +606,30 @@ flex-direction: row; } + .main-content { + display: grid; + grid-template-columns: repeat(4,1fr); + grid-gap: 10px; + } + .main-content li.grid_2 { + grid-column-end: span 2; + } + + .main-content li.grid_3 { + grid-column-end: span 3; + } + + .main-content li.grid_4 { + grid-column-end: span 4; + } } +@media print { + header, user, nav, aside, footer { + display: none; + } aside .cd-accordion-menu.animated label::before { /* this class is used if you're using jquery to animate the accordion */ @@ -362,4 +638,4 @@ aside .cd-accordion-menu.animated label::before { transition: transform 0.3s; } - + diff --git a/static/img/boxplot.png b/static/img/boxplot.png new file mode 100644 index 00000000..b2ec02d4 Binary files /dev/null and b/static/img/boxplot.png differ diff --git a/static/img/comparison.png b/static/img/comparison.png new file mode 100644 index 00000000..348b76f3 Binary files /dev/null and b/static/img/comparison.png differ diff --git a/static/img/histogram.png b/static/img/histogram.png new file mode 100644 index 00000000..8154d5d5 Binary files /dev/null and b/static/img/histogram.png differ diff --git a/static/img/otecp.png b/static/img/otecp.png new file mode 100644 index 00000000..0b511187 Binary files /dev/null and b/static/img/otecp.png differ diff --git a/static/img/otwcp.png b/static/img/otwcp.png new file mode 100644 index 00000000..0bf1038f Binary files /dev/null and b/static/img/otwcp.png differ diff --git a/static/img/powerprogress.png b/static/img/powerprogress.png new file mode 100644 index 00000000..818950f2 Binary files /dev/null and b/static/img/powerprogress.png differ diff --git a/static/img/rankingpiece.png b/static/img/rankingpiece.png new file mode 100644 index 00000000..7daf55cb Binary files /dev/null and b/static/img/rankingpiece.png differ diff --git a/static/img/statistics.PNG b/static/img/statistics.PNG new file mode 100644 index 00000000..1bf5b6e7 Binary files /dev/null and b/static/img/statistics.PNG differ diff --git a/static/img/statistics.xcf b/static/img/statistics.xcf new file mode 100644 index 00000000..394dc511 Binary files /dev/null and b/static/img/statistics.xcf differ diff --git a/static/img/strokeanalysis.png b/static/img/strokeanalysis.png new file mode 100644 index 00000000..c0b02f7d Binary files /dev/null and b/static/img/strokeanalysis.png differ diff --git a/static/img/trendflex.png b/static/img/trendflex.png new file mode 100644 index 00000000..be40047f Binary files /dev/null and b/static/img/trendflex.png differ diff --git a/templates/400.html b/templates/400.html index 025dfc60..33bfb894 100644 --- a/templates/400.html +++ b/templates/400.html @@ -1,16 +1,19 @@ -{% extends "basenofilters.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} -{% block title %}Change Workout {% endblock %} +{% block title %}Error {% endblock %} -{% block content %} +{% block main %} -

    Bad Request

    +

    HTTP Error 400 Bad Request.

    -
    {% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} +{% endblock %} diff --git a/templates/403.html b/templates/403.html index 3a3d7df7..298ff76e 100644 --- a/templates/403.html +++ b/templates/403.html @@ -1,17 +1,20 @@ -{% extends "basenofilters.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} -{% block title %}Change Workout {% endblock %} +{% block title %}Error {% endblock %} -{% block content %} +{% block main %} -

    Forbidden

    +

    - Access forbidden. You probably tried to access functionality on a workout - or chart that is not owned by you. + Access forbidden. You probably tried to access an object (workout, session, + chart) that is not owned by you.

    -
    {% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} +{% endblock %} diff --git a/templates/404.html b/templates/404.html index 33fd4812..e0b109df 100644 --- a/templates/404.html +++ b/templates/404.html @@ -1,15 +1,22 @@ -{% extends "basenofilters.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% block title %}Change Workout {% endblock %} -{% block content %} +{% block main %} -

    Error 404 Page not found

    -

    -We could not find the page on our server. -

    -
    + +
      +
    • +

      + We could not find the page on our server. +

      +
    • +
    {% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} +{% endblock %} diff --git a/templates/500.html b/templates/500.html index d60793eb..a7717929 100644 --- a/templates/500.html +++ b/templates/500.html @@ -1,21 +1,30 @@ -{% extends "basenofilters.html" %} +{% extends "newbase.html" %} {% load staticfiles %} -{% block title %}Change Workout {% endblock %} +{% block title %}Error {% endblock %} -{% block content %} +{% block main %} -

    Error 500 Internal Server Error

    -

    - The site reported an internal server error. The site developer has been - notified automatically with a full error report. You can help the developer - by reporting an issue on Bitbucket using the button below. -

    - -
    +
      +
    • +

      + The site reported an internal server error. The site developer has been + notified automatically with a full error report. You can help the developer + by reporting an issue on Bitbucket using the button below. +

      +
    • +
    • + + Report an issue + +
    • +
    {% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} +{% endblock %} diff --git a/templates/502.html b/templates/502.html new file mode 100644 index 00000000..84b3cabb --- /dev/null +++ b/templates/502.html @@ -0,0 +1,30 @@ +{% extends "newbase.html" %} +{% load staticfiles %} + +{% block title %}Error{% endblock %} + +{% block main %} + +

    Error 502 Internal Server Error

    + +
      +
    • +

      + The site reported an internal server error. The site developer has been + notified automatically with a full error report. You can help the developer + by reporting an issue on Bitbucket using the button below. +

      +
    • +
    • + + Report an issue + +
    • +
    + +{% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} +{% endblock %} diff --git a/templates/newbase.html b/templates/newbase.html index 04a0c6c8..0967858d 100644 --- a/templates/newbase.html +++ b/templates/newbase.html @@ -113,7 +113,13 @@ } }); - + + {% analytical_head_bottom %} @@ -125,7 +131,7 @@
  • Rowsandall logo + alt="Rowsandall logo" width="200px">
  • @@ -143,7 +149,7 @@
  • {% if user.is_authenticated %}
  • - + {% if user.rower.rowerplan == 'pro' %} {% elif user.rower.rowerplan == 'coach' %} @@ -151,7 +157,7 @@ {% elif user.rower.rowerplan == 'plan' %} {% else %} - + {% endif %} @@ -176,30 +182,34 @@ + +   + +
    +
      + {% if user.rower.protrialexpires and user.rower.protrialexpires|is_future_date %} + {% if user.rower.plantrialexpires and user.rower.rowerplan != 'plan' %} +
    • +

      + {{ user.rower.protrialexpires|date_dif|ddays }} days left of your Self-Coach trial - Would you like to upgrade now? +

      +
    • + {% else %} + {% if user.rower.rowerplan == 'basic' %} +
    • +

      + {{ user.rower.protrialexpires|date_dif|ddays }} days left of your Pro trial - Would you like to upgrade now? +

      +
    • + {% endif %} + {% endif %} + {% endif %} + {% if user.rower.emailbounced %} +
    • +

      + Your email bounced. Please update your email address in the user settings +

      +
    • + {% endif %} + {% if messages %} + {% for message in messages %} +
    • + {% if message.level == DEFAULT_MESSAGE_LEVELS.ERROR %} +

      + {% else %} +

      + {% endif %} + {{ message|safe }} +

      +
    • + {% endfor %} + {% endif %} + {% if breadcrumbs %} +
    • +

      + + You are here: + {% for crumb in breadcrumbs %} + {{ crumb.name }} + {% if not forloop.last %} +  /  + {% endif %} + {% endfor %} + +

      +
    • + {% endif %} +
    + {% block main %} - {% endblock %} + {% endblock %}
    - +
  • @@ -261,19 +329,19 @@

    About

    @@ -281,7 +349,7 @@

    Paid Plans

    @@ -289,10 +357,10 @@

    Legal