Private
Public Access
1
0

Merge branch 'release/teamcompare'

This commit is contained in:
Sander Roosendaal
2017-02-19 16:56:39 +01:00
12 changed files with 622 additions and 141 deletions

View File

@@ -449,6 +449,7 @@ def get_username(access_token):
try:
res = me_json['data']['username']
id = me_json['data']['id']
except KeyError:
res = None
@@ -482,7 +483,7 @@ def process_callback(request):
access_token = get_token(code)
username = get_username(access_token)
username,id = get_username(access_token)
return HttpResponse("got a user name: %s" % username)

View File

@@ -252,3 +252,23 @@ class StatsOptionsForm(forms.Form):
paddle = forms.BooleanField(initial=False,required=False)
snow = 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())

View File

@@ -8,6 +8,8 @@ from rowingdata import rowingdata as rrdata
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.models import CustomJS,Slider
from bokeh.charts import Histogram,HeatMap
@@ -52,7 +54,7 @@ import rowers.dataprep as dataprep
axlabels = {
'time': 'Time',
'distance': 'Distance (m)',
'cumdist': 'Distance (m)',
'cumdist': 'Cumulative Distance (m)',
'hr': 'Heart Rate (bpm)',
'spm': 'Stroke Rate (spm)',
'pace': 'Pace (/500m)',
@@ -1579,6 +1581,139 @@ def interactive_bar_chart(id=0,promember=0):
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',
promember=0,plottype='line'):

View File

@@ -398,17 +398,23 @@ class Workout(models.Model):
privacy = models.CharField(default='visible',max_length=30,
choices=privacychoices)
def __str__(self):
def __unicode__(self):
date = self.date
name = self.name
distance = str(self.distance)
ownerfirst = self.user.user.first_name
ownerlast = self.user.user.last_name
duration = self.duration
try:
stri = date.strftime('%Y-%m-%d')+'_'+name
except AttributeError:
stri = str(date)+'_'+name
stri = u'{d} {n} {dist}m {duration:%H:%M:%S} {ownerfirst} {ownerlast}'.format(
d = date.strftime('%Y-%m-%d'),
n = name,
dist = distance,
duration = duration,
ownerfirst = ownerfirst,
ownerlast = ownerlast,
)
return stri
@@ -890,7 +896,7 @@ class WorkoutComment(models.Model):
def __unicode__(self):
return u'Comment to: {w} by {u1} {u2}'.format(
w=self.workout.name,
w=self.workout,
u1 = self.user.first_name,
u2 = self.user.last_name,
)

View File

@@ -39,15 +39,18 @@ from rowsandall_app.settings import C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SEC
def ewmovingaverage(interval,window_size):
# Experimental code using Exponential Weighted moving average
intervaldf = pd.DataFrame({'v':interval})
idf_ewma1 = intervaldf.ewm(span=window_size)
idf_ewma2 = intervaldf[::-1].ewm(span=window_size)
try:
intervaldf = pd.DataFrame({'v':interval})
idf_ewma1 = intervaldf.ewm(span=window_size)
idf_ewma2 = intervaldf[::-1].ewm(span=window_size)
i_ewma1 = idf_ewma1.mean().ix[:,'v']
i_ewma2 = idf_ewma2.mean().ix[:,'v']
i_ewma1 = idf_ewma1.mean().ix[:,'v']
i_ewma2 = idf_ewma2.mean().ix[:,'v']
interval2 = np.vstack((i_ewma1,i_ewma2[::-1]))
interval2 = np.mean( interval2, axis=0) # average
interval2 = np.vstack((i_ewma1,i_ewma2[::-1]))
interval2 = np.mean( interval2, axis=0) # average
except ValueError:
interval2 = interval
return interval2
@@ -234,7 +237,7 @@ def handle_stravaexport(f2,workoutname,stravatoken,description=''):
act = client.upload_activity(f2,'tcx',name=workoutname)
try:
res = act.wait(poll_interval=5.0)
res = act.wait(poll_interval=5.0,timeout=30)
message = 'Workout successfully synchronized to Strava'
except:
res = 0
@@ -246,6 +249,7 @@ def handle_stravaexport(f2,workoutname,stravatoken,description=''):
act = client.update_activity(res.id,activity_type='Rowing',description=description)
else:
message = 'Strava upload timed out.'
return (0,message)
return (res.id,message)

View File

@@ -6,57 +6,57 @@
{% 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>
<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 }}
{{ 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>
<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 Plot</h1>
{% if user.is_authenticated and mayedit %}
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2 suffix_2 omega">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/advanced">Advanced Edit</a>
</p>
</div>
{% endif %}
<div id="theplot" class="grid_12 alpha flexplot">
<h1>Interactive Plot</h1>
{% if user.is_authenticated and mayedit %}
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2 suffix_2 omega">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/advanced">Advanced Edit</a>
</p>
</div>
{% endif %}
<div id="theplot" class="grid_12 alpha flexplot">
{{ the_div|safe }}
</div>
</div>
</div>
{% endblock %}
{% endblock %}

View File

@@ -6,88 +6,91 @@
{% block content %}
<div id="exportbuttons" class="grid_6 alpha">
<h3>Export Workout</h3>
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2 suffix_2 omega">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/advanced">Advanced Edit</a>
</p>
</div>
<div class="grid_6 alpha">
<p>
Click on the icon to upload this workout to your site of choice. A checkmark indicates that the workout has already been uploaded. If the button is grayed out, click it to authorize the connection to that site. Use TCX or CSV export to email a TCX or CSV file of your workout to yourself.
</p>
</div>
<h3>Export Workout</h3>
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2 suffix_2 omega">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/advanced">Advanced Edit</a>
</p>
</div>
<div class="grid_6 alpha">
<p>
Click on the icon to upload this workout to your site of choice. A checkmark indicates that the workout has already been uploaded. If the button is grayed out, click it to authorize the connection to that site. Use TCX or CSV export to email a TCX or CSV file of your workout to yourself.
</p>
{% if workout.uploadedtoc2 == 0 %}
{% if user.rower.c2token == None or user.rower.c2token == '' %}
<div class="grid_1 alpha">
<a href="/rowers/me/c2authorize">
<img src="/static/img/c2square_gray.png" alt="C2 icon" width="60" height="60"></a>
</div>
{% else %}
<div class="grid_1 alpha">
<a href="/rowers/workout/{{ workout.id }}/c2uploadw"><img src="/static/img/c2square.jpg" alt="Concept2 icon" width="60" height="60"></a>
</div>
{% endif %}
{% else %}
<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>
</div>
{% if workout.uploadedtoc2 == 0 %}
{% if user.rower.c2token == None or user.rower.c2token == '' %}
<div class="grid_1 alpha">
<a href="/rowers/me/c2authorize">
<img src="/static/img/c2square_gray.png" alt="C2 icon" width="60" height="60"></a>
</div>
{% else %}
<div class="grid_1 alpha">
<a href="/rowers/workout/{{ workout.id }}/c2uploadw"><img src="/static/img/c2square.jpg" alt="Concept2 icon" width="60" height="60"></a>
</div>
{% endif %}
{% else %}
<div class="grid_1 alpha">
<img src="/static/img/c2square_checked.png" alt="Concept2 icon" width="60" height="60"></a>
</div>
{% endif %}
{% if workout.uploadedtostrava == 0 %}
{% if user.rower.stravatoken == None or user.rower.stravatoken == '' %}
<div class="grid_1">
<a href="/rowers/me/stravaauthorize">
<img src="/static/img/stravasquare_gray.png" alt="Strava icon" width="60" height="60"></a>
</div>
{% else %}
<div class="grid_1">
<a href="/rowers/workout/{{ workout.id }}/stravauploadw"><img src="/static/img/stravasquare.png" alt="Strava icon" width="60" height="60"></a>
</div>
{% endif %}
{% if user.rower.stravatoken == None or user.rower.stravatoken == '' %}
<div class="grid_1">
<a href="/rowers/me/stravaauthorize">
<img src="/static/img/stravasquare_gray.png" alt="Strava icon" width="60" height="60"></a>
</div>
{% else %}
<div class="grid_1">
<img src="/static/img/stravasquare_checked.png" alt="Concept2 icon" width="60" height="60"></a>
</div>
<div class="grid_1">
<a href="/rowers/workout/{{ workout.id }}/stravauploadw"><img src="/static/img/stravasquare.png" alt="Strava icon" width="60" height="60"></a>
</div>
{% endif %}
{% else %}
<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>
</div>
{% endif %}
{% if workout.uploadedtosporttracks == 0 %}
{% if user.rower.sporttrackstoken == None or user.rower.sporttrackstoken == '' %}
<div class="grid_1">
<a href="/rowers/me/sporttracksauthorize">
<img src="/static/img/sporttrackssquare_gray.png" alt="SportTracks icon" width="60" height="60"></a>
</div>
{% else %}
<div class="grid_1">
<a href="/rowers/workout/{{ workout.id }}/sporttracksuploadw">
<img src="/static/img/sporttrackssquare.png" alt="SportTracks icon" width="60" height="60"></a>
</div>
{% endif %}
{% if user.rower.sporttrackstoken == None or user.rower.sporttrackstoken == '' %}
<div class="grid_1">
<a href="/rowers/me/sporttracksauthorize">
<img src="/static/img/sporttrackssquare_gray.png" alt="SportTracks icon" width="60" height="60"></a>
</div>
{% else %}
<div class="grid_1">
<img src="/static/img/sporttrackssquare_checked.png" alt="Concept2 icon" width="60" height="60"></a>
</div>
<div class="grid_1">
<a href="/rowers/workout/{{ workout.id }}/sporttracksuploadw">
<img src="/static/img/sporttrackssquare.png" alt="SportTracks icon" width="60" height="60"></a>
</div>
{% endif %}
<div class="grid_1">
<a href="/rowers/workout/{{ workout.id }}/emailtcx">
<img src="/static/img/export.png" alt="TCX Export" width="60" height="60"></a>
</div>
{% else %}
<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>
</div>
{% endif %}
<div class="grid_1">
<a href="/rowers/workout/{{ workout.id }}/emailtcx">
<img src="/static/img/export.png" alt="TCX Export" width="60" height="60"></a>
</div>
<div class="grid_1">
<a href="/rowers/workout/{{ workout.id }}/emailcsv">
<img src="/static/img/CSVsquare.png" alt="CSV Export" width="60" height="60"></a>
</div>
<div class="grid_1">
<a href="/rowers/workout/{{ workout.id }}/emailcsv">
<img src="/static/img/CSVsquare.png" alt="CSV Export" width="60" height="60"></a>
</div>
</div>
@@ -100,16 +103,16 @@ You only need to do this once. After that, the site will have access until you
revoke the authorization for the "rowingdata" app.</p>
<div class="grid_2 alpha">
<p><a href="/rowers/me/stravaauthorize/"><img src="/static/img/ConnectWithStrava.png" alt="connect with strava" width="120"></a></p>
<p><a href="/rowers/me/stravaauthorize/"><img src="/static/img/ConnectWithStrava.png" alt="connect with strava" width="120"></a></p>
</div>
<div class="grid_2">
<p><a href="/rowers/me/c2authorize/"><img src="/static/img/blueC2logo.png" alt="connect with Concept2" width="120"></a></p>
<p><a href="/rowers/me/c2authorize/"><img src="/static/img/blueC2logo.png" alt="connect with Concept2" width="120"></a></p>
</div>
<div class="grid_2 omega">
<p><a href="/rowers/me/sporttracksauthorize/"><img src="/static/img/sporttracks-button.png" alt="connect with SportTracks" width="120"></a></p>
<p><a href="/rowers/me/sporttracksauthorize/"><img src="/static/img/sporttracks-button.png" alt="connect with SportTracks" width="120"></a></p>
</div>
</div>
@@ -117,4 +120,4 @@ revoke the authorization for the "rowingdata" app.</p>
{% endblock %}
{% endblock %}

View File

@@ -112,6 +112,13 @@
</div>
<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">
{% if announcements %}
<h3>What's New?</h3>

View 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 %}

View 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 %}

View File

@@ -113,6 +113,13 @@ urlpatterns = [
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/$',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'^(?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),
@@ -204,6 +211,7 @@ urlpatterns = [
url(r'^workout/(\d+)/stravauploadw/$',views.workout_strava_upload_view),
url(r'^workout/(\d+)/recalcsummary/$',views.workout_recalcsummary_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/s/(?P<successmessage>\w+.*)$',views.rower_teams_view),
url(r'^me/teams/c/(?P<message>\w+.*)$',views.rower_teams_view),

View File

@@ -26,7 +26,7 @@ from rowers.forms import (
StatsOptionsForm,PredictedPieceForm,DateRangeForm,DeltaDaysForm,
EmailForm, RegistrationForm, RegistrationFormTermsOfService,
RegistrationFormUniqueEmail,CNsummaryForm,UpdateWindForm,
UpdateStreamForm
UpdateStreamForm,WorkoutMultipleCompareForm,ChartParamChoiceForm
)
from rowers.models import Workout, User, Rower, WorkoutForm,FavoriteChart
from rowers.models import (
@@ -810,7 +810,10 @@ def workout_strava_upload_view(request,id=0):
message = mes
w.uploadedtostrava = -1
w.save()
os.remove(tcxfile)
try:
os.remove(tcxfile)
except WindowsError:
pass
url = reverse(workout_export_view,
kwargs = {
'id':str(w.id),
@@ -1958,6 +1961,137 @@ def workout_setprivate_view(request,id,
})
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
@login_required()
@@ -3302,6 +3436,16 @@ def workout_export_view(request,id=0, message="", successmessage=""):
row = Workout.objects.get(id=id)
except Workout.DoesNotExist:
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)
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
@@ -3318,6 +3462,7 @@ def workout_export_view(request,id=0, message="", successmessage=""):
'export.html',
{'workout':row,
'message':message,
'successmessage':successmessage,
'c2userid':c2userid,
})