Merge branch 'release/teamcompare'
This commit is contained in:
@@ -449,6 +449,7 @@ def get_username(access_token):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
res = me_json['data']['username']
|
res = me_json['data']['username']
|
||||||
|
id = me_json['data']['id']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
res = None
|
res = None
|
||||||
|
|
||||||
@@ -482,7 +483,7 @@ def process_callback(request):
|
|||||||
|
|
||||||
access_token = get_token(code)
|
access_token = get_token(code)
|
||||||
|
|
||||||
username = get_username(access_token)
|
username,id = get_username(access_token)
|
||||||
|
|
||||||
return HttpResponse("got a user name: %s" % username)
|
return HttpResponse("got a user name: %s" % username)
|
||||||
|
|
||||||
|
|||||||
@@ -252,3 +252,23 @@ class StatsOptionsForm(forms.Form):
|
|||||||
paddle = forms.BooleanField(initial=False,required=False)
|
paddle = forms.BooleanField(initial=False,required=False)
|
||||||
snow = forms.BooleanField(initial=False,required=False)
|
snow = forms.BooleanField(initial=False,required=False)
|
||||||
other = forms.BooleanField(initial=False,required=False)
|
other = forms.BooleanField(initial=False,required=False)
|
||||||
|
|
||||||
|
class WorkoutMultipleCompareForm(forms.Form):
|
||||||
|
workouts = forms.ModelMultipleChoiceField(queryset=Workout.objects.all(),
|
||||||
|
widget=forms.CheckboxSelectMultiple())
|
||||||
|
|
||||||
|
from rowers.interactiveplots import axlabels
|
||||||
|
|
||||||
|
axlabels.pop('None')
|
||||||
|
axlabels = list(axlabels.items())
|
||||||
|
|
||||||
|
|
||||||
|
class ChartParamChoiceForm(forms.Form):
|
||||||
|
plotchoices = (
|
||||||
|
('line','Line Plot'),
|
||||||
|
('scatter','Scatter Plot'),
|
||||||
|
)
|
||||||
|
xparam = forms.ChoiceField(choices=axlabels,initial='distance')
|
||||||
|
yparam = forms.ChoiceField(choices=axlabels,initial='hr')
|
||||||
|
plottype = forms.ChoiceField(choices=plotchoices,initial='scatter')
|
||||||
|
teamid = forms.IntegerField(widget=forms.HiddenInput())
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ from rowingdata import rowingdata as rrdata
|
|||||||
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from bokeh.palettes import Dark2_8 as palette
|
||||||
|
import itertools
|
||||||
from bokeh.plotting import figure, ColumnDataSource, Figure,curdoc
|
from bokeh.plotting import figure, ColumnDataSource, Figure,curdoc
|
||||||
from bokeh.models import CustomJS,Slider
|
from bokeh.models import CustomJS,Slider
|
||||||
from bokeh.charts import Histogram,HeatMap
|
from bokeh.charts import Histogram,HeatMap
|
||||||
@@ -52,7 +54,7 @@ import rowers.dataprep as dataprep
|
|||||||
axlabels = {
|
axlabels = {
|
||||||
'time': 'Time',
|
'time': 'Time',
|
||||||
'distance': 'Distance (m)',
|
'distance': 'Distance (m)',
|
||||||
'cumdist': 'Distance (m)',
|
'cumdist': 'Cumulative Distance (m)',
|
||||||
'hr': 'Heart Rate (bpm)',
|
'hr': 'Heart Rate (bpm)',
|
||||||
'spm': 'Stroke Rate (spm)',
|
'spm': 'Stroke Rate (spm)',
|
||||||
'pace': 'Pace (/500m)',
|
'pace': 'Pace (/500m)',
|
||||||
@@ -1579,6 +1581,139 @@ def interactive_bar_chart(id=0,promember=0):
|
|||||||
|
|
||||||
return [script,div]
|
return [script,div]
|
||||||
|
|
||||||
|
def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line',
|
||||||
|
promember=0,
|
||||||
|
labeldict=None):
|
||||||
|
columns = [xparam,yparam,
|
||||||
|
'ftime','distance','fpace',
|
||||||
|
'power','hr','spm',
|
||||||
|
'time','pace','workoutstate',
|
||||||
|
'workoutid']
|
||||||
|
|
||||||
|
datadf = dataprep.getsmallrowdata_db(columns,ids=ids)
|
||||||
|
|
||||||
|
yparamname = axlabels[yparam]
|
||||||
|
|
||||||
|
#datadf = datadf[datadf[yparam] > 0]
|
||||||
|
|
||||||
|
#datadf = datadf[datadf[xparam] > 0]
|
||||||
|
|
||||||
|
# check if dataframe not empty
|
||||||
|
if datadf.empty:
|
||||||
|
return ['','<p>No non-zero data in selection</p>','','']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if xparam=='distance':
|
||||||
|
xaxmax = datadf['distance'].max()
|
||||||
|
xaxmin = datadf['distance'].min()
|
||||||
|
else:
|
||||||
|
xaxmax = yaxmaxima[xparam]
|
||||||
|
xaxmin = yaxminima[xparam]
|
||||||
|
|
||||||
|
|
||||||
|
x_axis_type = 'linear'
|
||||||
|
y_axis_type = 'linear'
|
||||||
|
|
||||||
|
# Add hover to this comma-separated string and see what changes
|
||||||
|
if (promember==1):
|
||||||
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,resize,crosshair'
|
||||||
|
else:
|
||||||
|
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,crosshair'
|
||||||
|
|
||||||
|
if yparam == 'pace':
|
||||||
|
y_axis_type = 'datetime'
|
||||||
|
yaxmax = 90.
|
||||||
|
yaxmin = 150.
|
||||||
|
|
||||||
|
if xparam == 'time':
|
||||||
|
x_axis_type = 'datetime'
|
||||||
|
|
||||||
|
if xparam != 'time':
|
||||||
|
xvals = xaxmin+np.arange(100)*(xaxmax-xaxmin)/100.
|
||||||
|
else:
|
||||||
|
xvals = np.arange(100)
|
||||||
|
|
||||||
|
plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type,
|
||||||
|
tools=TOOLS,
|
||||||
|
toolbar_location="above",
|
||||||
|
plot_width=920,
|
||||||
|
toolbar_sticky=False)
|
||||||
|
|
||||||
|
colors = itertools.cycle(palette)
|
||||||
|
|
||||||
|
cntr = 0
|
||||||
|
|
||||||
|
for id,color in itertools.izip(ids,colors):
|
||||||
|
group = datadf[datadf['workoutid']==int(id)].copy()
|
||||||
|
group.sort_values(by='time',ascending=True,inplace=True)
|
||||||
|
group['x'] = group[xparam]
|
||||||
|
group['y'] = group[yparam]
|
||||||
|
|
||||||
|
ymean = group['y'].mean()
|
||||||
|
ylabel = Label(x=100,y=70+20*cntr,
|
||||||
|
x_units='screen',y_units='screen',
|
||||||
|
text=yparam+": {ymean:6.2f}".format(ymean=ymean),
|
||||||
|
background_fill_alpha=.7,
|
||||||
|
text_color=color,
|
||||||
|
)
|
||||||
|
if yparam != 'time' and yparam != 'pace':
|
||||||
|
plot.add_layout(ylabel)
|
||||||
|
|
||||||
|
print cntr,id,len(group),ymean
|
||||||
|
|
||||||
|
source = ColumnDataSource(
|
||||||
|
group
|
||||||
|
)
|
||||||
|
|
||||||
|
if labeldict:
|
||||||
|
legend=labeldict[id]
|
||||||
|
else:
|
||||||
|
legend=str(id)
|
||||||
|
|
||||||
|
if plottype=='line':
|
||||||
|
plot.line('x','y',source=source,color=color,legend=legend)
|
||||||
|
else:
|
||||||
|
plot.scatter('x','y',source=source,color=color,legend=legend,
|
||||||
|
fill_alpha=0.4,line_color=None)
|
||||||
|
|
||||||
|
cntr += 1
|
||||||
|
|
||||||
|
plot.legend.location='bottom_right'
|
||||||
|
plot.xaxis.axis_label = axlabels[xparam]
|
||||||
|
plot.yaxis.axis_label = axlabels[yparam]
|
||||||
|
|
||||||
|
if (xparam != 'time') and (xparam != 'distance') and (xparam != 'cumdist'):
|
||||||
|
xrange1 = Range1d(start=yaxminima[xparam],end=yaxmaxima[xparam])
|
||||||
|
plot.x_range = xrange1
|
||||||
|
|
||||||
|
if xparam == 'time':
|
||||||
|
xrange1 = Range1d(start=xaxmin,end=xaxmax)
|
||||||
|
plot.x_range = xrange1
|
||||||
|
plot.xaxis[0].formatter = DatetimeTickFormatter(
|
||||||
|
hours = ["%H"],
|
||||||
|
minutes = ["%M"],
|
||||||
|
seconds = ["%S"],
|
||||||
|
days = ["0"],
|
||||||
|
months = [""],
|
||||||
|
years = [""]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if yparam == 'pace':
|
||||||
|
plot.yaxis[0].formatter = DatetimeTickFormatter(
|
||||||
|
seconds = ["%S"],
|
||||||
|
minutes = ["%M"]
|
||||||
|
)
|
||||||
|
|
||||||
|
script, div = components(plot)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return [script,div]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def interactive_comparison_chart(id1=0,id2=0,xparam='distance',yparam='spm',
|
def interactive_comparison_chart(id1=0,id2=0,xparam='distance',yparam='spm',
|
||||||
promember=0,plottype='line'):
|
promember=0,plottype='line'):
|
||||||
|
|
||||||
|
|||||||
@@ -398,17 +398,23 @@ class Workout(models.Model):
|
|||||||
privacy = models.CharField(default='visible',max_length=30,
|
privacy = models.CharField(default='visible',max_length=30,
|
||||||
choices=privacychoices)
|
choices=privacychoices)
|
||||||
|
|
||||||
def __str__(self):
|
def __unicode__(self):
|
||||||
|
|
||||||
date = self.date
|
date = self.date
|
||||||
name = self.name
|
name = self.name
|
||||||
|
distance = str(self.distance)
|
||||||
|
ownerfirst = self.user.user.first_name
|
||||||
|
ownerlast = self.user.user.last_name
|
||||||
|
duration = self.duration
|
||||||
|
|
||||||
try:
|
stri = u'{d} {n} {dist}m {duration:%H:%M:%S} {ownerfirst} {ownerlast}'.format(
|
||||||
stri = date.strftime('%Y-%m-%d')+'_'+name
|
d = date.strftime('%Y-%m-%d'),
|
||||||
except AttributeError:
|
n = name,
|
||||||
stri = str(date)+'_'+name
|
dist = distance,
|
||||||
|
duration = duration,
|
||||||
|
ownerfirst = ownerfirst,
|
||||||
|
ownerlast = ownerlast,
|
||||||
|
)
|
||||||
|
|
||||||
return stri
|
return stri
|
||||||
|
|
||||||
@@ -890,7 +896,7 @@ class WorkoutComment(models.Model):
|
|||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return u'Comment to: {w} by {u1} {u2}'.format(
|
return u'Comment to: {w} by {u1} {u2}'.format(
|
||||||
w=self.workout.name,
|
w=self.workout,
|
||||||
u1 = self.user.first_name,
|
u1 = self.user.first_name,
|
||||||
u2 = self.user.last_name,
|
u2 = self.user.last_name,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ from rowsandall_app.settings import C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SEC
|
|||||||
def ewmovingaverage(interval,window_size):
|
def ewmovingaverage(interval,window_size):
|
||||||
# Experimental code using Exponential Weighted moving average
|
# Experimental code using Exponential Weighted moving average
|
||||||
|
|
||||||
|
try:
|
||||||
intervaldf = pd.DataFrame({'v':interval})
|
intervaldf = pd.DataFrame({'v':interval})
|
||||||
idf_ewma1 = intervaldf.ewm(span=window_size)
|
idf_ewma1 = intervaldf.ewm(span=window_size)
|
||||||
idf_ewma2 = intervaldf[::-1].ewm(span=window_size)
|
idf_ewma2 = intervaldf[::-1].ewm(span=window_size)
|
||||||
@@ -48,6 +49,8 @@ def ewmovingaverage(interval,window_size):
|
|||||||
|
|
||||||
interval2 = np.vstack((i_ewma1,i_ewma2[::-1]))
|
interval2 = np.vstack((i_ewma1,i_ewma2[::-1]))
|
||||||
interval2 = np.mean( interval2, axis=0) # average
|
interval2 = np.mean( interval2, axis=0) # average
|
||||||
|
except ValueError:
|
||||||
|
interval2 = interval
|
||||||
|
|
||||||
return interval2
|
return interval2
|
||||||
|
|
||||||
@@ -234,7 +237,7 @@ def handle_stravaexport(f2,workoutname,stravatoken,description=''):
|
|||||||
|
|
||||||
act = client.upload_activity(f2,'tcx',name=workoutname)
|
act = client.upload_activity(f2,'tcx',name=workoutname)
|
||||||
try:
|
try:
|
||||||
res = act.wait(poll_interval=5.0)
|
res = act.wait(poll_interval=5.0,timeout=30)
|
||||||
message = 'Workout successfully synchronized to Strava'
|
message = 'Workout successfully synchronized to Strava'
|
||||||
except:
|
except:
|
||||||
res = 0
|
res = 0
|
||||||
@@ -246,6 +249,7 @@ def handle_stravaexport(f2,workoutname,stravatoken,description=''):
|
|||||||
act = client.update_activity(res.id,activity_type='Rowing',description=description)
|
act = client.update_activity(res.id,activity_type='Rowing',description=description)
|
||||||
else:
|
else:
|
||||||
message = 'Strava upload timed out.'
|
message = 'Strava upload timed out.'
|
||||||
|
return (0,message)
|
||||||
|
|
||||||
return (res.id,message)
|
return (res.id,message)
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ Click on the icon to upload this workout to your site of choice. A checkmark ind
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="grid_1 alpha">
|
<div class="grid_1 alpha">
|
||||||
|
<a href="http://log.concept2.com/profile/{{ c2userid }}/log/{{ workout.uploadedtoc2 }}">
|
||||||
<img src="/static/img/c2square_checked.png" alt="Concept2 icon" width="60" height="60"></a>
|
<img src="/static/img/c2square_checked.png" alt="Concept2 icon" width="60" height="60"></a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -59,6 +60,7 @@ Click on the icon to upload this workout to your site of choice. A checkmark ind
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="grid_1">
|
<div class="grid_1">
|
||||||
|
<a href="https://www.strava.com/activities/{{ workout.uploadedtostrava }}">
|
||||||
<img src="/static/img/stravasquare_checked.png" alt="Concept2 icon" width="60" height="60"></a>
|
<img src="/static/img/stravasquare_checked.png" alt="Concept2 icon" width="60" height="60"></a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -76,6 +78,7 @@ Click on the icon to upload this workout to your site of choice. A checkmark ind
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="grid_1">
|
<div class="grid_1">
|
||||||
|
<a href="https://sporttracks.mobi/activity/{{ workout.uploadedtosporttracks }}">
|
||||||
<img src="/static/img/sporttrackssquare_checked.png" alt="Concept2 icon" width="60" height="60"></a>
|
<img src="/static/img/sporttrackssquare_checked.png" alt="Concept2 icon" width="60" height="60"></a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -112,6 +112,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid_4 omega">
|
<div class="grid_4 omega">
|
||||||
|
{% if team %}
|
||||||
|
<div class="grid_4" id="teambuttons">
|
||||||
|
<div class="grid_3 alpha">
|
||||||
|
<a class="button gray small" href="/rowers/team-compare-select/team/{{ team.id }}/">Multi Workout Compare</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
<div class="grid_4" id="announcements">
|
<div class="grid_4" id="announcements">
|
||||||
{% if announcements %}
|
{% if announcements %}
|
||||||
<h3>What's New?</h3>
|
<h3>What's New?</h3>
|
||||||
|
|||||||
58
rowers/templates/multicompare.html
Normal file
58
rowers/templates/multicompare.html
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load rowerfilters %}
|
||||||
|
|
||||||
|
{% block title %}View Comparison {% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
|
||||||
|
<script async="true" type="text/javascript">
|
||||||
|
Bokeh.set_log_level("info");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{{ interactiveplot |safe }}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Set things up to resize the plot on a window resize. You can play with
|
||||||
|
// the arguments of resize_width_height() to change the plot's behavior.
|
||||||
|
var plot_resize_setup = function () {
|
||||||
|
var plotid = Object.keys(Bokeh.index)[0]; // assume we have just one plot
|
||||||
|
var plot = Bokeh.index[plotid];
|
||||||
|
var plotresizer = function() {
|
||||||
|
// arguments: use width, use height, maintain aspect ratio
|
||||||
|
plot.resize_width_height(true, false, false);
|
||||||
|
};
|
||||||
|
window.addEventListener('resize', plotresizer);
|
||||||
|
plotresizer();
|
||||||
|
};
|
||||||
|
window.addEventListener('load', plot_resize_setup);
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
|
||||||
|
html, body {height: 100%; margin:5px;}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="workouts" class="grid_12 alpha">
|
||||||
|
<h1>Interactive Comparison</h1>
|
||||||
|
<div class="grid_2 alpha">
|
||||||
|
<a class="button gray small" href="/rowers/list-workouts/team/{{ teamid }}/">Team Workouts</a>
|
||||||
|
</div>
|
||||||
|
<div class="grid_2">
|
||||||
|
<a class="button gray small" href="/rowers/team-compare-select/team/{{ teamid }}/">Multi Compare</a>
|
||||||
|
</div>
|
||||||
|
<div class="grid_2 suffix_6 omega">
|
||||||
|
<a class="button gray small" href="/rowers/team/{{ teamid }}/">Team Page</a>
|
||||||
|
</div>
|
||||||
|
<div class="grid_12 alpha">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id="theplot" class="grid_12 alpha flexplot">
|
||||||
|
{{ the_div|safe }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
94
rowers/templates/team_compare_select.html
Normal file
94
rowers/templates/team_compare_select.html
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load rowerfilters %}
|
||||||
|
|
||||||
|
{% block title %}Workouts{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
setTimeout("location.reload(true);",60000);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="grid_12">
|
||||||
|
|
||||||
|
<div class="grid_4 alpha">
|
||||||
|
|
||||||
|
{% if team %}
|
||||||
|
<form enctype="multipart/form-data" action="/rowers/team-compare-select/team/{{ team.id }}/" method="post">
|
||||||
|
{% else %}
|
||||||
|
<form enctype="multipart/form-data" action="/rowers/team-compare-select/" method="post">
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<table>
|
||||||
|
{{ dateform.as_table }}
|
||||||
|
</table>
|
||||||
|
{% csrf_token %}
|
||||||
|
</div>
|
||||||
|
<div class="grid_2">
|
||||||
|
<input name='daterange' class="button green" type="submit" value="Submit"> </form>
|
||||||
|
</div>
|
||||||
|
<div class="grid_5 prefix_1 omega">
|
||||||
|
{% if team %}
|
||||||
|
<form id="searchform" action="/rowers/team-compare-select/team/{{ team.id }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}"
|
||||||
|
method="get" accept-charset="utf-8">
|
||||||
|
{% else %}
|
||||||
|
<form id="searchform" action="/rowers/team-compare-select/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}"
|
||||||
|
method="get" accept-charset="utf-8">
|
||||||
|
{% endif %}
|
||||||
|
<div class="grid_3 prefix_1 alpha">
|
||||||
|
<input class="searchfield" id="searchbox" name="q" type="text" placeholder="Search">
|
||||||
|
</div>
|
||||||
|
<div class="grid_1 omega">
|
||||||
|
<button class="button blue small" type="submit">
|
||||||
|
Search
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid_12 alpha">
|
||||||
|
<h3>{{ team.name }} Team Workouts</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form enctype="multipart/form-data" action="/rowers/multi-compare" method="post">
|
||||||
|
<div id="workouts_table" class="grid_8 alpha">
|
||||||
|
|
||||||
|
|
||||||
|
{% if workouts %}
|
||||||
|
|
||||||
|
|
||||||
|
<table width="100%" class="listtable">
|
||||||
|
{{ form.as_table }}
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<p> No workouts found </p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div id="form_settings" class="grid_4 alpha">
|
||||||
|
<p><b>Warning: You are on an experimental part of the site. Use at your own risk.</b></p>
|
||||||
|
<p>Select two or more workouts on the left, set your plot settings below,
|
||||||
|
and press submit"</p>
|
||||||
|
{% csrf_token %}
|
||||||
|
<table>
|
||||||
|
{{ chartform.as_table }}
|
||||||
|
</table>
|
||||||
|
<div class="grid_1 prefix_2 suffix_1">
|
||||||
|
<p>
|
||||||
|
<input name='workoutselectform' class="button green" type="submit" value="Submit">
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="grid_4">
|
||||||
|
<p>You can use the date and search forms above to search through all
|
||||||
|
workouts from this team.</p>
|
||||||
|
<p>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.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -113,6 +113,13 @@ urlpatterns = [
|
|||||||
url(r'^list-workouts/team/(?P<teamid>\d+)/$',views.workouts_view),
|
url(r'^list-workouts/team/(?P<teamid>\d+)/$',views.workouts_view),
|
||||||
url(r'^list-workouts/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.workouts_view),
|
url(r'^list-workouts/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.workouts_view),
|
||||||
url(r'^list-workouts/$',views.workouts_view),
|
url(r'^list-workouts/$',views.workouts_view),
|
||||||
|
url(r'^team-compare-select/c/(?P<message>\w+.*)/$',views.team_comparison_select),
|
||||||
|
url(r'^team-compare-select/s/(?P<successmessage>\w+.*)/$',views.team_comparison_select),
|
||||||
|
url(r'^team-compare-select/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.team_comparison_select),
|
||||||
|
url(r'^team-compare-select/team/(?P<teamid>\d+)/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.team_comparison_select),
|
||||||
|
url(r'^team-compare-select/team/(?P<teamid>\d+)/$',views.team_comparison_select),
|
||||||
|
url(r'^team-compare-select/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.team_comparison_select),
|
||||||
|
url(r'^team-compare-select/$',views.team_comparison_select),
|
||||||
url(r'^list-graphs/$',views.graphs_view),
|
url(r'^list-graphs/$',views.graphs_view),
|
||||||
url(r'^(?P<theuser>\d+)/ote-bests/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.rankings_view),
|
url(r'^(?P<theuser>\d+)/ote-bests/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.rankings_view),
|
||||||
url(r'^(?P<theuser>\d+)/ote-bests/(?P<deltadays>\d+)$',views.rankings_view),
|
url(r'^(?P<theuser>\d+)/ote-bests/(?P<deltadays>\d+)$',views.rankings_view),
|
||||||
@@ -204,6 +211,7 @@ urlpatterns = [
|
|||||||
url(r'^workout/(\d+)/stravauploadw/$',views.workout_strava_upload_view),
|
url(r'^workout/(\d+)/stravauploadw/$',views.workout_strava_upload_view),
|
||||||
url(r'^workout/(\d+)/recalcsummary/$',views.workout_recalcsummary_view),
|
url(r'^workout/(\d+)/recalcsummary/$',views.workout_recalcsummary_view),
|
||||||
url(r'^workout/(\d+)/sporttracksuploadw/$',views.workout_sporttracks_upload_view),
|
url(r'^workout/(\d+)/sporttracksuploadw/$',views.workout_sporttracks_upload_view),
|
||||||
|
url(r'^multi-compare$',views.multi_compare_view),
|
||||||
url(r'^me/teams/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.rower_teams_view),
|
url(r'^me/teams/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.rower_teams_view),
|
||||||
url(r'^me/teams/s/(?P<successmessage>\w+.*)$',views.rower_teams_view),
|
url(r'^me/teams/s/(?P<successmessage>\w+.*)$',views.rower_teams_view),
|
||||||
url(r'^me/teams/c/(?P<message>\w+.*)$',views.rower_teams_view),
|
url(r'^me/teams/c/(?P<message>\w+.*)$',views.rower_teams_view),
|
||||||
|
|||||||
147
rowers/views.py
147
rowers/views.py
@@ -26,7 +26,7 @@ from rowers.forms import (
|
|||||||
StatsOptionsForm,PredictedPieceForm,DateRangeForm,DeltaDaysForm,
|
StatsOptionsForm,PredictedPieceForm,DateRangeForm,DeltaDaysForm,
|
||||||
EmailForm, RegistrationForm, RegistrationFormTermsOfService,
|
EmailForm, RegistrationForm, RegistrationFormTermsOfService,
|
||||||
RegistrationFormUniqueEmail,CNsummaryForm,UpdateWindForm,
|
RegistrationFormUniqueEmail,CNsummaryForm,UpdateWindForm,
|
||||||
UpdateStreamForm
|
UpdateStreamForm,WorkoutMultipleCompareForm,ChartParamChoiceForm
|
||||||
)
|
)
|
||||||
from rowers.models import Workout, User, Rower, WorkoutForm,FavoriteChart
|
from rowers.models import Workout, User, Rower, WorkoutForm,FavoriteChart
|
||||||
from rowers.models import (
|
from rowers.models import (
|
||||||
@@ -810,7 +810,10 @@ def workout_strava_upload_view(request,id=0):
|
|||||||
message = mes
|
message = mes
|
||||||
w.uploadedtostrava = -1
|
w.uploadedtostrava = -1
|
||||||
w.save()
|
w.save()
|
||||||
|
try:
|
||||||
os.remove(tcxfile)
|
os.remove(tcxfile)
|
||||||
|
except WindowsError:
|
||||||
|
pass
|
||||||
url = reverse(workout_export_view,
|
url = reverse(workout_export_view,
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'id':str(w.id),
|
'id':str(w.id),
|
||||||
@@ -1958,6 +1961,137 @@ def workout_setprivate_view(request,id,
|
|||||||
})
|
})
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
# Team comparison
|
||||||
|
@login_required()
|
||||||
|
def team_comparison_select(request,
|
||||||
|
startdatestring="",
|
||||||
|
enddatestring="",
|
||||||
|
message='',
|
||||||
|
successmessage='',
|
||||||
|
startdate=timezone.now()-datetime.timedelta(days=30),
|
||||||
|
enddate=timezone.now()+datetime.timedelta(days=1),
|
||||||
|
teamid=0):
|
||||||
|
|
||||||
|
try:
|
||||||
|
r = Rower.objects.get(user=request.user)
|
||||||
|
except Rower.DoesNotExist:
|
||||||
|
raise Http404("Rower doesn't exist")
|
||||||
|
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
|
||||||
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||||
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||||
|
enddate = enddate+datetime.timedelta(days=1)
|
||||||
|
|
||||||
|
if startdatestring:
|
||||||
|
startdate = iso8601.parse_date(startdatestring)
|
||||||
|
if enddatestring:
|
||||||
|
enddate = iso8601.parse_date(enddatestring)
|
||||||
|
|
||||||
|
if enddate < startdate:
|
||||||
|
s = enddate
|
||||||
|
enddate = startdate
|
||||||
|
startdate = s
|
||||||
|
|
||||||
|
try:
|
||||||
|
theteam = Team.objects.get(id=teamid)
|
||||||
|
except Team.DoesNotExist:
|
||||||
|
raise Http404("Team doesn't exist")
|
||||||
|
|
||||||
|
if theteam.viewing == 'allmembers' or theteam.manager == request.user:
|
||||||
|
workouts = Workout.objects.filter(team=theteam,
|
||||||
|
startdatetime__gte=startdate,
|
||||||
|
startdatetime__lte=enddate).order_by("-date", "-starttime")
|
||||||
|
elif theteam.viewing == 'coachonly':
|
||||||
|
workouts = Workout.objects.filter(team=theteam,user=r,
|
||||||
|
startdatetime__gte=startdate,
|
||||||
|
startdatetime__lte=enddate).order_by("-date","-starttime")
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
theteam = None
|
||||||
|
workouts = Workout.objects.filter(user=r,
|
||||||
|
startdatetime__gte=startdate,
|
||||||
|
startdatetime__lte=enddate).order_by("-date", "-starttime")
|
||||||
|
|
||||||
|
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))
|
||||||
|
)
|
||||||
|
|
||||||
|
form = WorkoutMultipleCompareForm()
|
||||||
|
form.fields["workouts"].queryset = workouts
|
||||||
|
|
||||||
|
chartform = ChartParamChoiceForm(initial={'teamid':theteam.id})
|
||||||
|
|
||||||
|
return render(request, 'team_compare_select.html',
|
||||||
|
{'workouts': workouts,
|
||||||
|
'message': message,
|
||||||
|
'successmessage':successmessage,
|
||||||
|
'dateform':dateform,
|
||||||
|
'startdate':startdate,
|
||||||
|
'enddate':enddate,
|
||||||
|
'team':theteam,
|
||||||
|
'form':form,
|
||||||
|
'chartform':chartform,
|
||||||
|
})
|
||||||
|
|
||||||
|
@login_required()
|
||||||
|
def multi_compare_view(request):
|
||||||
|
promember=0
|
||||||
|
if not request.user.is_anonymous():
|
||||||
|
r = Rower.objects.get(user=request.user)
|
||||||
|
result = request.user.is_authenticated() and ispromember(request.user)
|
||||||
|
if result:
|
||||||
|
promember=1
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = WorkoutMultipleCompareForm(request.POST)
|
||||||
|
chartform = ChartParamChoiceForm(request.POST)
|
||||||
|
if form.is_valid() and chartform.is_valid():
|
||||||
|
cd = form.cleaned_data
|
||||||
|
workouts = cd['workouts']
|
||||||
|
xparam = chartform.cleaned_data['xparam']
|
||||||
|
yparam = chartform.cleaned_data['yparam']
|
||||||
|
plottype = chartform.cleaned_data['plottype']
|
||||||
|
teamid = chartform.cleaned_data['teamid']
|
||||||
|
ids = [w.id for w in workouts]
|
||||||
|
labeldict = {
|
||||||
|
w.id: w.__unicode__() for w in workouts
|
||||||
|
}
|
||||||
|
|
||||||
|
res = interactive_multiple_compare_chart(ids,xparam,yparam,
|
||||||
|
promember=promember,
|
||||||
|
plottype=plottype,
|
||||||
|
labeldict=labeldict)
|
||||||
|
script = res[0]
|
||||||
|
div = res[1]
|
||||||
|
|
||||||
|
return render(request,'multicompare.html',
|
||||||
|
{'interactiveplot':script,
|
||||||
|
'the_div':div,
|
||||||
|
'promember':promember,
|
||||||
|
'teamid':teamid,
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return HttpResponse("Form is not valid")
|
||||||
|
else:
|
||||||
|
url = reverse(workouts_view)
|
||||||
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
# List Workouts
|
# List Workouts
|
||||||
@login_required()
|
@login_required()
|
||||||
@@ -3302,6 +3436,16 @@ def workout_export_view(request,id=0, message="", successmessage=""):
|
|||||||
row = Workout.objects.get(id=id)
|
row = Workout.objects.get(id=id)
|
||||||
except Workout.DoesNotExist:
|
except Workout.DoesNotExist:
|
||||||
raise Http404("Workout doesn't exist")
|
raise Http404("Workout doesn't exist")
|
||||||
|
|
||||||
|
try:
|
||||||
|
thetoken = c2_open(request.user)
|
||||||
|
except C2NoTokenError:
|
||||||
|
thetoken = 0
|
||||||
|
|
||||||
|
if (checkworkoutuser(request.user,row)) and thetoken:
|
||||||
|
c2userid = c2stuff.get_userid(thetoken)
|
||||||
|
else:
|
||||||
|
c2userid = 0
|
||||||
|
|
||||||
form = WorkoutForm(instance=row)
|
form = WorkoutForm(instance=row)
|
||||||
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
|
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
|
||||||
@@ -3318,6 +3462,7 @@ def workout_export_view(request,id=0, message="", successmessage=""):
|
|||||||
'export.html',
|
'export.html',
|
||||||
{'workout':row,
|
{'workout':row,
|
||||||
'message':message,
|
'message':message,
|
||||||
|
'successmessage':successmessage,
|
||||||
'c2userid':c2userid,
|
'c2userid':c2userid,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user