Private
Public Access
1
0

Merge branch 'develop' into feature/restapi

This commit is contained in:
Sander Roosendaal
2016-11-23 14:58:16 +01:00
21 changed files with 733 additions and 321 deletions

View File

@@ -8,6 +8,7 @@ from pandas import DataFrame,Series
import pandas as pd
import numpy as np
import itertools
from django.conf import settings
from sqlalchemy import create_engine
@@ -84,9 +85,33 @@ def rdata(file,rower=rrower()):
def getrowdata_db(id=0):
data = read_df_sql(id)
data['pace'] = data['pace']/1.0e6
data['ergpace'] = data['ergpace']/1.0e6
data['nowindpace'] = data['nowindpace']/1.0e6
data['time'] = data['time']/1.0e6
data['x_right'] = data['x_right']/1.0e6
if data.empty:
rowdata,row = getrowdata(id=id)
if rowdata:
data = dataprep(rowdata.df,id=id,bands=True,barchart=True,otwpower=True)
else:
data = pd.DataFrame() # returning empty dataframe
else:
row = Workout.objects.get(id=id)
return data,row
def getsmallrowdata_db(xparam,yparam1,yparam2,ids=[]):
if yparam2 == 'None':
yparam2 = yparam1
prepmultipledata(ids)
data = read_cols_df_sql(ids,xparam,yparam1,yparam2)
if xparam == 'time':
data['time'] = data['time']/1.0e6
if yparam1 == 'pace':
data['pace'] = data['pace']/1.0e6
if yparam2 == 'pace':
data['pace'] = data['pace']/1.0e6
return data
@@ -110,7 +135,34 @@ def getrowdata(id=0):
return rowdata,row
# temporary
def prepmultipledata(ids):
query = sa.text('SELECT DISTINCT workoutid FROM strokedata')
with engine.connect() as conn, conn.begin():
res = conn.execute(query)
res = list(itertools.chain.from_iterable(res.fetchall()))
res = list(set(ids)-set(res))
for id in res:
rowdata,row = getrowdata(id=id)
if rowdata:
data = dataprep(rowdata.df,id=id,bands=True,barchart=True,otwpower=True)
return res
def read_cols_df_sql(ids,col1,col2,col3):
columns = list(set((col1,col2,col3,'distance','spm')))
cls = ''
for column in columns:
cls += column+', '
cls = cls[:-2]
query = sa.text('SELECT {columns} FROM strokedata WHERE workoutid IN {ids}'.format(
columns = cls,
ids = tuple(ids),
))
df = pd.read_sql_query(query,engine)
return df
def read_df_sql(id):
df = pd.read_sql_query(sa.text('SELECT * FROM strokedata WHERE workoutid={id}'.format(
id=id)), engine)
@@ -118,6 +170,37 @@ def read_df_sql(id):
def smalldataprep(therows,xparam,yparam1,yparam2):
df = pd.DataFrame()
if yparam2 == 'None':
yparam2 = 'power'
df[xparam] = []
df[yparam1] = []
df[yparam2] = []
df['distance'] = []
df['spm'] = []
for workout in therows:
f1 = workout.csvfilename
try:
rowdata = dataprep(rrdata(f1).df)
rowdata = pd.DataFrame({xparam: rowdata[xparam],
yparam1: rowdata[yparam1],
yparam2: rowdata[yparam2],
'distance': rowdata['distance'],
'spm': rowdata['spm'],
}
)
df = pd.concat([df,rowdata],ignore_index=True)
except IOError:
pass
return df
def dataprep(rowdatadf,id=0,bands=False,barchart=False,otwpower=False):
rowdatadf.set_index([range(len(rowdatadf))],inplace=True)
t = rowdatadf.ix[:,'TimeStamp (sec)']
@@ -134,7 +217,10 @@ def dataprep(rowdatadf,id=0,bands=False,barchart=False,otwpower=False):
power = rowdatadf.ix[:,' Power (watts)']
averageforce = rowdatadf.ix[:,' AverageDriveForce (lbs)']
drivelength = rowdatadf.ix[:,' DriveLength (meters)']
try:
workoutstate = rowdatadf.ix[:,' WorkoutState']
except KeyError:
workoutstate = 0*hr
peakforce = rowdatadf.ix[:,' PeakDriveForce (lbs)']
@@ -152,7 +238,10 @@ def dataprep(rowdatadf,id=0,bands=False,barchart=False,otwpower=False):
drivelength = savgol_filter(drivelength,windowsize,3)
forceratio = savgol_filter(forceratio,windowsize,3)
try:
t2 = t.fillna(method='ffill').apply(lambda x: timedeltaconv(x))
except TypeError:
t2 = 0*t
p2 = p.fillna(method='ffill').apply(lambda x: timedeltaconv(x))
@@ -179,6 +268,7 @@ def dataprep(rowdatadf,id=0,bands=False,barchart=False,otwpower=False):
fpace = nicepaceformat(p2),
driveenergy=driveenergy,
power=power,
workoutstate=workoutstate,
averageforce=averageforce,
drivelength=drivelength,
peakforce=peakforce,

View File

@@ -5,9 +5,11 @@ from rowingdata import cumcpdata,histodata
from rowingdata import rowingdata as rrdata
from django.utils import timezone
from bokeh.plotting import figure, ColumnDataSource, Figure,curdoc
from bokeh.models import CustomJS,Slider
from bokeh.charts import Histogram
from bokeh.charts import Histogram,HeatMap
from bokeh.resources import CDN,INLINE
from bokeh.embed import components
from bokeh.layouts import layout,widgetbox
@@ -68,14 +70,13 @@ from rowers.dataprep import timedeltaconv
def interactive_histoall(theworkouts):
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
therows = []
for workout in theworkouts:
f1 = workout.csvfilename
rowdata = rdata(f1)
if rowdata != 0:
therows.append(rowdata)
ids = [w.id for w in theworkouts]
rowdata = dataprep.getsmallrowdata_db('power','power','power',ids=ids)
histopwr = rowdata['power'].values
if len(histopwr) == 0:
return "","CSV file not found","",""
histopwr = histodata(therows)
# throw out nans
histopwr = histopwr[~np.isinf(histopwr)]
histopwr = histopwr[histopwr > 25]
@@ -494,18 +495,15 @@ def interactive_chart(id=0,promember=0):
else:
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
rowdata,row = dataprep.getrowdata(id=id)
if rowdata == 0:
return "","CSV Data File Not Found"
datadf = dataprep.dataprep(rowdata.df)
datadf,row = dataprep.getrowdata_db(id=id)
if datadf.empty:
return "","CSV Data File Not Found"
source = ColumnDataSource(
datadf
)
plot = Figure(x_axis_type="datetime",y_axis_type="datetime",
plot_width=400,
plot_height=400,
@@ -552,10 +550,10 @@ def interactive_chart(id=0,promember=0):
hover.mode = 'mouse'
plot.extra_y_ranges = {"hr": Range1d(start=100,end=200)}
plot.extra_y_ranges = {"hrax": Range1d(start=100,end=200)}
plot.line('time','hr',source=source,color="red",
y_range_name="hr", legend="Heart Rate")
plot.add_layout(LinearAxis(y_range_name="hr",axis_label="HR"),'right')
y_range_name="hrax", legend="Heart Rate")
plot.add_layout(LinearAxis(y_range_name="hrax",axis_label="HR"),'right')
plot.legend.location = "bottom_right"
@@ -563,22 +561,14 @@ def interactive_chart(id=0,promember=0):
return [script,div]
def interactive_cum_flex_chart(theworkouts,promember=0,
def interactive_cum_flex_chart2(theworkouts,promember=0,
xparam='spm',
yparam1='power',
yparam2='hr',
):
therows = []
for workout in theworkouts:
f1 = workout.csvfilename
rowdata = rdata(f1)
if rowdata != 0:
therows.append(rowdata.df)
datadf = pd.concat(therows)
yparam2='spm'):
# datadf = dataprep.smalldataprep(theworkouts,xparam,yparam1,yparam2)
ids = [w.id for w in theworkouts]
datadf = dataprep.getsmallrowdata_db(xparam,yparam1,yparam2,ids=ids)
axlabels = {
'time': 'Time',
@@ -626,21 +616,11 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
}
datadf = dataprep.dataprep(datadf)
if yparam1 != 'pace' and yparam1 != 'time':
datadf = datadf[datadf[yparam1] > 0]
elif yparam1 == 'time':
datadf = datadf[datadf['timesecs'] > 0]
else:
datadf = datadf[datadf['pseconds']>0]
if xparam != 'time' and xparam != 'pace':
datadf = datadf[datadf[xparam] > 0]
elif xparam == 'time':
datadf = datadf[datadf['timesecs']>0]
else:
datadf = datadf[datadf['pseconds']>0]
if yparam2 != 'None':
datadf = datadf[datadf[yparam2] > 0]
@@ -651,8 +631,6 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
datadf['x1'] = datadf.ix[:,xparam]
tseconds = datadf.ix[:,'timesecs']
datadf['y1'] = datadf.ix[:,yparam1]
if yparam2 != 'None':
@@ -661,12 +639,7 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
datadf['y2'] = datadf['y1']
if xparam=='time':
xaxmax = tseconds.max()
xaxmin = tseconds.min()
xaxmax = 1.0e3*xaxmax
xaxmin = 1.0e3*xaxmin
elif xparam=='distance':
if xparam=='distance':
xaxmax = datadf['x1'].max()
xaxmin = datadf['x1'].min()
else:
@@ -674,27 +647,16 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
xaxmin = yaxminima[xparam]
# average values
if xparam != 'time':
x1mean = datadf['x1'].mean()
else:
x1mean = 0
y1mean = datadf['y1'].mean()
y2mean = datadf['y2'].mean()
if xparam != 'time':
xvals = pd.Series(xaxmin+np.arange(100)*(xaxmax-xaxmin)/100.)
else:
xvals = pd.Series(np.arange(100))
x_axis_type = 'linear'
y_axis_type = 'linear'
if xparam == 'time':
x_axis_type = 'datetime'
if yparam1 == 'pace':
y_axis_type = 'datetime'
y1mean = datadf.ix[:,'pseconds'].mean()
datadf['xname'] = xparam
datadf['yname1'] = yparam1
@@ -703,6 +665,7 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
else:
datadf['yname2'] = yparam1
source = ColumnDataSource(
datadf
)
@@ -713,9 +676,9 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
# Add hover to this comma-separated string and see what changes
if (promember==1):
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,resize,crosshair'
else:
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,crosshair'
plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type,
tools=TOOLS,
@@ -735,7 +698,7 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
text_color='green',
)
if (xparam != 'time') and (xparam != 'distance'):
plot.add_layout(x1means)
plot.add_layout(xlabel)
@@ -746,7 +709,7 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
background_fill_alpha=.7,
text_color='blue',
)
if yparam1 != 'time' and yparam1 != 'pace':
plot.add_layout(y1label)
y2label = y1label
@@ -761,29 +724,9 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
yrange1 = Range1d(start=yaxminima[yparam1],end=yaxmaxima[yparam1])
plot.y_range = yrange1
if (xparam != 'time') and (xparam != 'distance'):
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 yparam1 == 'pace':
plot.yaxis[0].formatter = DatetimeTickFormatter(
seconds = ["%S"],
minutes = ["%M"]
)
if yparam2 != 'None':
yrange2 = Range1d(start=yaxminima[yparam2],end=yaxmaxima[yparam2])
@@ -809,18 +752,6 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
if yparam2 != 'pace' and yparam2 != 'time':
plot.add_layout(y2label)
hover = plot.select(dict(type=HoverTool))
hover.tooltips = OrderedDict([
('Time','@ftime'),
('Pace','@fpace'),
('HR','@hr{int}'),
('SPM','@spm{1.1}'),
('Power','@power{int}'),
])
hover.mode = 'mouse'
callback = CustomJS(args = dict(source=source,source2=source2,
x1means=x1means,
@@ -835,11 +766,7 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
var y1 = data['y1']
var y2 = data['y2']
var spm1 = data['spm']
var time1 = data['time']
var pace1 = data['pace']
var hr1 = data['hr']
var distance1 = data['distance']
var power1 = data['power']
var xname = data['xname'][0]
var yname1 = data['yname1'][0]
var yname2 = data['yname2'][0]
@@ -856,11 +783,7 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
data2['y1'] = []
data2['y2'] = []
data2['spm'] = []
data2['time'] = []
data2['pace'] = []
data2['hr'] = []
data2['distance'] = []
data2['power'] = []
data2['x1mean'] = []
data2['y1mean'] = []
data2['y2mean'] = []
@@ -875,11 +798,7 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
data2['y1'].push(y1[i])
data2['y2'].push(y2[i])
data2['spm'].push(spm1[i])
data2['time'].push(time1[i])
data2['pace'].push(pace1[i])
data2['hr'].push(hr1[i])
data2['distance'].push(distance1[i])
data2['power'].push(power1[i])
xm += x1[i]
ym1 += y1[i]
@@ -944,6 +863,7 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
def interactive_flex_chart2(id=0,promember=0,
xparam='time',
yparam1='pace',
@@ -994,8 +914,8 @@ def interactive_flex_chart2(id=0,promember=0,
'drivespeed':4,
}
rowdata,row = dataprep.getrowdata(id=id)
if rowdata == 0:
rowdata,row = dataprep.getrowdata_db(id=id)
if rowdata.empty:
return "","CSV Data File Not Found"
workoutstateswork = [1,4,5,8,9,6,7]
@@ -1004,16 +924,10 @@ def interactive_flex_chart2(id=0,promember=0,
if workstrokesonly:
try:
rowdata.df = rowdata.df[~rowdata.df[' WorkoutState'].isin(workoutstatesrest)]
rowdata = rowdata[~rowdata['workoutstate'].isin(workoutstatesrest)]
except KeyError:
pass
rowdata = dataprep.dataprep(rowdata.df)
# get user
# u = User.objects.get(id=row.user.id)
rowdata['x1'] = rowdata.ix[:,xparam]
rowdata['y1'] = rowdata.ix[:,yparam1]
@@ -1336,15 +1250,10 @@ def interactive_flex_chart2(id=0,promember=0,
def interactive_bar_chart(id=0,promember=0):
# check if valid ID exists (workout exists)
rowdata,row = dataprep.getrowdata(id=id)
if rowdata == 0:
rowdata,row = dataprep.getrowdata_db(id=id)
if rowdata.empty:
return "","CSV Data File Not Found"
rowdata = dataprep.dataprep(rowdata.df,bands=True,barchart=True)
# Add hover to this comma-separated string and see what changes
if (promember==1):
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
@@ -1355,8 +1264,6 @@ def interactive_bar_chart(id=0,promember=0):
rowdata
)
plot = Figure(x_axis_type="datetime",y_axis_type="datetime",
toolbar_sticky=False,
plot_width=920,
@@ -1453,19 +1360,14 @@ def interactive_comparison_chart(id1=0,id2=0,xparam='distance',yparam='spm',
# check if valid ID exists (workout exists)
rowdata1,row1 = dataprep.getrowdata(id=id1)
rowdata2,row2 = dataprep.getrowdata(id=id2)
if rowdata1 == 0:
rowdata1,row1 = dataprep.getrowdata_db(id=id1)
rowdata2,row2 = dataprep.getrowdata_db(id=id2)
if rowdata1.empty:
return "","CSV Data File Not Found"
if rowdata2 == 0:
if rowdata2.empty:
return "","CSV Data File Not Found"
rowdata1 = dataprep.dataprep(rowdata1.df)
rowdata2 = dataprep.dataprep(rowdata2.df)
x1 = rowdata1.ix[:,xparam]
x2 = rowdata2.ix[:,xparam]
@@ -1601,12 +1503,10 @@ def interactive_comparison_chart(id1=0,id2=0,xparam='distance',yparam='spm',
def interactive_otw_advanced_pace_chart(id=0,promember=0):
# check if valid ID exists (workout exists)
rowdata,row = dataprep.getrowdata(id=id)
if rowdata == 0:
rowdata,row = dataprep.getrowdata_db(id=id)
if rowdata.empty:
return "","CSV Data File Not Found"
rowdata = dataprep.dataprep(rowdata.df,otwpower=True)
# Add hover to this comma-separated string and see what changes
if (promember==1):
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'

View File

@@ -9,8 +9,31 @@ from django.forms.widgets import SplitDateTimeWidget
from datetimewidget.widgets import DateTimeWidget
import os
# Create your models here.
from django.conf import settings
from sqlalchemy import create_engine
import sqlalchemy as sa
from sqlite3 import OperationalError
user = settings.DATABASES['default']['USER']
password = settings.DATABASES['default']['PASSWORD']
database_name = settings.DATABASES['default']['NAME']
host = settings.DATABASES['default']['HOST']
port = settings.DATABASES['default']['PORT']
database_url = 'mysql://{user}:{password}@{host}:{port}/{database_name}'.format(
user=user,
password=password,
database_name=database_name,
host=host,
port=port,
)
if settings.DEBUG:
database_url = 'sqlite:///db.sqlite3'
engine = create_engine(database_url, echo=False)
# Create your models here.
class Team(models.Model):
name = models.CharField(max_length=150)
notes = models.CharField(blank=True,max_length=200)
@@ -120,6 +143,17 @@ def auto_delete_file_on_delete(sender, instance, **kwargs):
if os.path.isfile(instance.csvfilename):
os.remove(instance.csvfilename)
@receiver(models.signals.post_delete,sender=Workout)
def auto_delete_strokedata_on_delete(sender, instance, **kwargs):
if instance.id:
query = sa.text('DELETE FROM strokedata WHERE workoutid={id};'.format(
id=instance.id,
))
with engine.connect() as conn, conn.begin():
try:
result = conn.execute(query)
except:
print "Database Locked"
class StrokeData(models.Model):
class Meta:
@@ -130,6 +164,7 @@ class StrokeData(models.Model):
timesecs = models.FloatField(null=True)
hr = models.IntegerField(null=True)
pace = models.TimeField(null=True)
workoutstate = models.IntegerField(null=True,default=1)
pseconds = models.FloatField(null=True)
spm = models.FloatField(null=True)
cumdist = models.FloatField(null=True)

View File

@@ -15,7 +15,9 @@
<h1>Advanced Workout Editor</h1>
{% if user.rower.rowerplan == 'basic' %}
<p>This is a preview of the page with advanced functionality for Pro users. See <a href="/rowers/promembership">the page about Pro membership</a> for more information and to sign up for Pro Membership</a>
<p>This is a preview of the page with advanced functionality for Pro users.
See
<a href="/rowers/promembership">the page about Pro membership</a> for more information and to sign up for Pro Membership</a>
{% endif %}
<div class="grid_2 alpha">
<p>
@@ -42,7 +44,7 @@
</tr>
<th>Public link to this workout</th>
<td>
<a href="/rowers/workout/{{ workout.id }}">http://rowsandall.com/rowers/workout/{{ workout.id }}</a>
<a href="/rowers/workout/{{ workout.id }}">https://rowsandall.com/rowers/workout/{{ workout.id }}</a>
<td>
</table>
</div>
@@ -117,6 +119,21 @@ Enter or change the interval and summary data for your workout
</p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 suffix_4 alpha">
<p>
{% if user.rower.rowerplan == 'pro' %}
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/histo">Power Histogram</a>
{% else %}
<a class="button blue small" href="/rowers/promembership">Dist Metrics Plot</a>
{% endif %}
</p>
<p>
Plot the Power Histogram of this workout
</p>
</div>
</div>
</div>
<div id="advancedplots" class="grid_6 omega">

View File

@@ -42,7 +42,7 @@
</tr>
<th>Public link to this workout</th>
<td>
<a href="/rowers/workout/{{ workout.id }}">http://rowsandall.com/rowers/workout/{{ workout.id }}</a>
<a href="/rowers/workout/{{ workout.id }}">https://rowsandall.com/rowers/workout/{{ workout.id }}</a>
<td>
</table>
</div>

View File

@@ -6,8 +6,7 @@
{% block content %}
<div id="workouts" class="grid_4 alpha">
<div class="grid_4 alpha">
<h1>Workout {{ id }}</h1>
<table width=100%>
<tr>
@@ -31,6 +30,35 @@
</tr>
</table>
</div>
<div class="grid_4 alpha">
<p>
<form id="searchform" action=""
method="get" accept-charset="utf-8">
<button class="button blue small" type="submit">
Search
</button>
<input class="searchfield" id="searchbox" name="q" type="text" placeholder="Search">
</form>
</p>
</div>
Select start and end date for a date range:
<div class="grid_4 alpha">
<p>
<form enctype="multipart/form-data" action="/rowers/workout/compare/{{ id }}/" method="post">
<table>
{{ dateform.as_table }}
</table>
{% csrf_token %}
</div>
<div class="grid_2 suffix_2 omega">
<input name='daterange' class="button green" type="submit" value="Submit"> </form>
</p>
</div>
</div>
<div id="comparison" class="grid_8 omega">
<h1>Compare this workout to:</h1>
@@ -73,5 +101,21 @@
{% else %}
<p> No workouts found </p>
{% endif %}
<div class="grid_2 prefix_5 suffix_1 omega">
<span class="button gray small">
{% if workouts.has_previous %}
<a class="wh" href="/rowers/workout/compare/{{ id }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}?page={{ workouts.previous_page_number }}">&lt;</a>
{% endif %}
<span>
Page {{ workouts.number }} of {{ workouts.paginator.num_pages }}.
</span>
{% if workouts.has_next %}
<a class="wh" href="/rowers/workout/compare/{{ id }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}?page={{ workouts.next_page_number }}">&gt;</a>
{% endif %}
</span>
</div>
</div>
{% endblock %}

View File

@@ -39,18 +39,16 @@
<div class="grid_2 alpha dropdown">
<button class="grid_2 alpha button blue small dropbtn">X-axis</button>
<div class="dropdown-content">
<a class="button blue small alpha" href="/rowers/flexall/time/{{ yparam1 }}/{{ yparam2 }}">Time</a>
<a class="button blue small alpha" href="/rowers/flexall/distance/{{ yparam1 }}/{{ yparam2 }}">Distance</a>
{% if promember %}
<a class="button blue small alpha" href="/rowers/flexall/power/{{ yparam1 }}/{{ yparam2 }}">Power</a>
<a class="button blue small alpha" href="/rowers/flexall/hr/{{ yparam1 }}/{{ yparam2 }}">HR</a>
<a class="button blue small alpha" href="/rowers/flexall/spm/{{ yparam1 }}/{{ yparam2 }}">SPM</a>
<a class="button blue small alpha" href="/rowers/flexall/peakforce/{{ yparam1 }}/{{ yparam2 }}">Peak Force</a>
<a class="button blue small alpha" href="/rowers/flexall/averageforce/{{ yparam1 }}/{{ yparam2 }}">Average Force</a>
<a class="button blue small alpha" href="/rowers/flexall/forceratio/{{ yparam1 }}/{{ yparam2 }}">Average/Peak Force Ratio</a>
<a class="button blue small alpha" href="/rowers/flexall/drivelength/{{ yparam1 }}/{{ yparam2 }}">Drive Length</a>
<a class="button blue small alpha" href="/rowers/flexall/driveenergy/{{ yparam1 }}/{{ yparam2 }}">Work per Stroke</a>
<a class="button blue small alpha" href="/rowers/flexall/drivespeed/{{ yparam1 }}/{{ yparam2 }}">Drive Speed</a>
<a class="button blue small alpha" href="/rowers/flexall/power/{{ yparam1 }}/{{ yparam2 }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Power</a>
<a class="button blue small alpha" href="/rowers/flexall/hr/{{ yparam1 }}/{{ yparam2 }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">HR</a>
<a class="button blue small alpha" href="/rowers/flexall/spm/{{ yparam1 }}/{{ yparam2 }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">SPM</a>
<a class="button blue small alpha" href="/rowers/flexall/peakforce/{{ yparam1 }}/{{ yparam2 }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Peak Force</a>
<a class="button blue small alpha" href="/rowers/flexall/averageforce/{{ yparam1 }}/{{ yparam2 }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Average Force</a>
<a class="button blue small alpha" href="/rowers/flexall/forceratio/{{ yparam1 }}/{{ yparam2 }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Average/Peak Force Ratio</a>
<a class="button blue small alpha" href="/rowers/flexall/drivelength/{{ yparam1 }}/{{ yparam2 }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Drive Length</a>
<a class="button blue small alpha" href="/rowers/flexall/driveenergy/{{ yparam1 }}/{{ yparam2 }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Work per Stroke</a>
<a class="button blue small alpha" href="/rowers/flexall/drivespeed/{{ yparam1 }}/{{ yparam2 }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Drive Speed</a>
{% else %}
<a class="button rosy small" href="/rowers/promembership">Power (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">HR (Pro)</a>
@@ -69,17 +67,16 @@
<div class="grid_2 dropdown">
<button class="grid_2 alpha button blue small dropbtn">Left</button>
<div class="dropdown-content">
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/pace/{{ yparam2 }}">Pace</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/hr/{{ yparam2 }}">HR</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/spm/{{ yparam2 }}">SPM</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/power/{{ yparam2 }}">Power</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/hr/{{ yparam2 }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">HR</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/spm/{{ yparam2 }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">SPM</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/power/{{ yparam2 }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Power</a>
{% if promember %}
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/peakforce/{{ yparam2 }}">Peak Force</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/averageforce/{{ yparam2 }}">Average Force</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/forceratio/{{ yparam2 }}">Average/Peak Force Ratio</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/drivelength/{{ yparam2 }}">Drive Length</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/driveenergy/{{ yparam2 }}">Work per Stroke</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/drivespeed/{{ yparam2 }}">Drive Speed</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/peakforce/{{ yparam2 }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Peak Force</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/averageforce/{{ yparam2 }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Average Force</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/forceratio/{{ yparam2 }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Average/Peak Force Ratio</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/drivelength/{{ yparam2 }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Drive Length</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/driveenergy/{{ yparam2 }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Work per Stroke</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/drivespeed/{{ yparam2 }}/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Drive Speed</a>
{% else %}
<a class="button rosy small" href="/rowers/promembership">Peak Force (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
@@ -95,16 +92,16 @@
<div class="grid_2 dropdown omega">
<button class="grid_2 alpha button blue small dropbtn">Right</button>
<div class="dropdown-content">
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/hr">HR</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/spm">SPM</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/power">Power</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/hr/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">HR</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/spm/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">SPM</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/power/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Power</a>
{% if promember %}
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/peakforce">Peak Force</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/averageforce">Average Force</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/forceratio">Average/Peak Force Ratio</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/drivelength">Drive Length</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/driveenergy">Work per Stroke</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/drivespeed">Drive Speed</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/peakforce/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Peak Force</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/averageforce/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Average Force</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/forceratio/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Average/Peak Force Ratio</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/drivelength/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Drive Length</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/driveenergy/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Work per Stroke</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/drivespeed/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d"}}">Drive Speed</a>
{% else %}
<a class="button rosy small" href="/rowers/promembership">Peak Force (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
@@ -114,7 +111,7 @@
<a class="button rosy small" href="/rowers/promembership">Drive Speed (Pro)</a>
{% endif %}
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/None">None</a>
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/None/{{startdate|date:"Y-m-d"}}/{{ enddate|date:"Y-m-d"}}">None</a>
</div>
</div>

View File

@@ -46,7 +46,7 @@
between {{ startdate|date }} and {{ enddate|date }}</p>
<p>Direct link for other Pro users:
<a href="/rowers/{{ id }}/histo/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}">http://rowsandall.com/rowers/{{ id }}/histo/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}</a>
<a href="/rowers/{{ id }}/histo/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}">https://rowsandall.com/rowers/{{ id }}/histo/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}</a>
</p>
</div>
<div id="form" class="grid_6 omega">

View File

@@ -0,0 +1,66 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}View Workout {% 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="navigation" class="grid_12 alpha">
{% if user.is_authenticated and mayedit %}
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ id }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2 suffix_8 omega">
<p>
<a class="button gray small" href="/rowers/workout/{{ id }}/advanced">Advanced Edit</a>
</p>
</div>
{% endif %}
</div>
<div id="title" class="grid_12 alpha">
<h1>Indoor Rower Power Histogram</h1>
</div>
<div id="graph" class="grid_12 alpha">
{{ the_div|safe }}
</div>
{% endblock %}

View File

@@ -42,7 +42,7 @@
<p>Summary of the past 12 months for {{ theuser.first_name }} {{ theuser.last_name }}</p>
<p>Direct link for other Pro users:
<a href="/rowers/{{ id }}/histo-all">http://rowsandall.com/rowers/{{ id }}/histo-all</a>
<a href="/rowers/{{ id }}/histo-all">https://rowsandall.com/rowers/{{ id }}/histo-all</a>
</p>
<div class="grid_12 alpha">

View File

@@ -6,6 +6,14 @@
{% block content %}
<h1>Recent Graphs</h1>
<form id="searchform" action="."
method="get" accept-charset="utf-8">
<button class="button blue small" type="submit">
Search
</button>
<input class="searchfield" id="searchbox" name="q" type="text" placeholder="Search">
</form>
{% if graphs1 %}
<div class="grid_1 alpha">
<p>&nbsp;</p>

View File

@@ -5,7 +5,26 @@
{% block title %}Workouts{% endblock %}
{% block content %}
<div class="grid_12">
Select start and end date for a date range:
<div class="grid_4 alpha">
<form enctype="multipart/form-data" action="/rowers/list-workouts/" method="post">
<table>
{{ dateform.as_table }}
</table>
{% csrf_token %}
</div>
<div class="grid_2 suffix_6 omega">
<input name='daterange' class="button green" type="submit" value="Submit"> </form>
</div>
</div>
<h1>My Workouts</h1>
{% if workouts %}
<table width="70%" class="listtable">
<thead>
@@ -49,4 +68,35 @@
{% else %}
<p> No workouts found </p>
{% endif %}
</div>
<div class="grid_6 alpha">
<form id="searchform" action="/rowers/list-workouts/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}"
method="get" accept-charset="utf-8">
<div class="grid_3 prefix_1 alpha">
<input class="searchfield" id="searchbox" name="q" type="text" placeholder="Search">
</div>
<div class="grid_1 suffix_1 omega">
<button class="button blue small" type="submit">
Search
</button>
</div>
</form>
</div>
<div class="grid_2 prefix_3 omega">
<span class="button gray small">
{% if workouts.has_previous %}
<a class="wh" href="?page={{ workouts.previous_page_number }}">&lt;</a>
{% endif %}
<span>
Page {{ workouts.number }} of {{ workouts.paginator.num_pages }}.
</span>
{% if workouts.has_next %}
<a class="wh" href="?page={{ workouts.next_page_number }}">&gt;</a>
{% endif %}
</span>
{% endblock %}

View File

@@ -48,7 +48,7 @@
</tr>
<th>Public link to this workout</th>
<td>
<a href="/rowers/workout/{{ workout.id }}">http://rowsandall.com/rowers/workout/{{ workout.id }}</a>
<a href="/rowers/workout/{{ workout.id }}">https://rowsandall.com/rowers/workout/{{ workout.id }}</a>
<td>
</table>
</div>

View File

@@ -43,7 +43,7 @@
between {{ startdate|date }} and {{ enddate|date }}</p>
<p>Direct link for other users:
<a href="/rowers/{{ id }}/ote-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}">http://rowsandall.com/rowers/{{ id }}/ote-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}</a>
<a href="/rowers/{{ id }}/ote-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}">https://rowsandall.com/rowers/{{ id }}/ote-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}</a>
</p>
<p>The table gives the best efforts achieved on the official Concept2 ranking pieces in the selected date range.</p>

View File

@@ -48,12 +48,12 @@
</tr><tr>
<th>Public link to this workout</th>
<td>
<a href="/rowers/workout/{{ workout.id }}">http://rowsandall.com/rowers/workout/{{ workout.id }}</a>
<a href="/rowers/workout/{{ workout.id }}">https://rowsandall.com/rowers/workout/{{ workout.id }}</a>
<td>
</tr><tr>
<th>Public link to interactive chart</th>
<td>
<a href="/rowers/workout/{{ workout.id }}/interactiveplot">http://rowsandall.com/rowers/workout/{{ workout.id }}/interactiveplot</a>
<a href="/rowers/workout/{{ workout.id }}/interactiveplot">https://rowsandall.com/rowers/workout/{{ workout.id }}/interactiveplot</a>
<td>
</tr>
</table>

View File

@@ -48,12 +48,12 @@
</tr><tr>
<th>Public link to this workout</th>
<td>
<a href="/rowers/workout/{{ workout.id }}">http://rowsandall.com/rowers/workout/{{ workout.id }}</a>
<a href="/rowers/workout/{{ workout.id }}">https://rowsandall.com/rowers/workout/{{ workout.id }}</a>
<td>
</tr><tr>
<th>Public link to interactive chart</th>
<td>
<a href="/rowers/workout/{{ workout.id }}/interactiveplot">http://rowsandall.com/rowers/workout/{{ workout.id }}/interactiveplot</a>
<a href="/rowers/workout/{{ workout.id }}/interactiveplot">https://rowsandall.com/rowers/workout/{{ workout.id }}/interactiveplot</a>
<td>
</tr>
</table>
@@ -189,6 +189,9 @@
{{ workout.summary }}
</pre>
</p>
<div class="grid_2 alpha">
<a class="button green small" href="recalcsummary">Update Summary</a>
</div>
</div>

View File

@@ -425,6 +425,9 @@ class ViewTest(TestCase):
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)
w = Workout.objects.get(id=1)

View File

@@ -49,6 +49,7 @@ urlpatterns = [
url(r'^list-workouts/c/(?P<message>\w+.*)/$',views.workouts_view),
url(r'^list-workouts/s/(?P<successmessage>\w+.*)/$',views.workouts_view),
url(r'^list-workouts/c/(?P<message>\w+.*)/s/(?P<successmessage>\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-graphs/$',views.graphs_view),
url(r'^dashboard/c/(?P<message>\w+.*)/$',views.dashboard_view),
@@ -62,6 +63,7 @@ urlpatterns = [
url(r'^ote-bests/$',views.rankings_view),
url(r'^(?P<theuser>\d+)/ote-bests/$',views.rankings_view),
url(r'^flexall/(?P<xparam>\w+.*)/(?P<yparam1>\w+.*)/(?P<yparam2>\w+.*)/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.cum_flex),
url(r'^flexall/(?P<xparam>\w+.*)/(?P<yparam1>\w+.*)/(?P<yparam2>\w+.*)$',views.cum_flex),
url(r'^flexall/$',views.cum_flex),
@@ -77,6 +79,7 @@ urlpatterns = [
url(r'^graph/(\d+)/delete$',views.graph_delete_view),
url(r'^workout/upload/$',views.workout_upload_view),
url(r'^workout/upload/(.+.*)$',views.workout_upload_view),
url(r'^workout/(?P<id>\d+)/histo$',views.workout_histo_view),
url(r'^workout/(?P<id>\d+)/export/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.workout_export_view),
url(r'^workout/(?P<id>\d+)/export/c/(?P<message>\w+.*)$',views.workout_export_view),
url(r'^workout/(?P<id>\d+)/export/s/(?P<successmessage>\w+.*)$',views.workout_export_view),
@@ -85,6 +88,7 @@ urlpatterns = [
url(r'^workout/(\d+)/emailcsv$',views.workout_csvemail_view),
url(r'^workout/compare/(\d+)/$',views.workout_comparison_list),
url(r'^workout/compare2/(?P<id1>\d+)/(?P<id2>\d+)/(?P<xparam>\w+.*)/(?P<yparam>\w+.*)/$',views.workout_comparison_view),
url(r'^workout/compare/(?P<id>\d+)/(?P<startdatestring>\d+-\d+-\d+)/(?P<enddatestring>\w+.*)$',views.workout_comparison_list),
url(r'^workout/(?P<id>\d+)/export/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.workout_edit_view),
url(r'^workout/(?P<id>\d+)/edit/c/(?P<message>.+.*)$',views.workout_edit_view),
url(r'^workout/(?P<id>\d+)/edit/s/(?P<successmessage>.+.*)$',views.workout_edit_view),
@@ -131,6 +135,7 @@ urlpatterns = [
url(r'^workout/(\d+)/c2upload/$',views.list_c2_upload_view),
url(r'^workout/(\d+)/c2uploadw/$',views.workout_c2_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+)/sporttracksuploadw/$',views.workout_sporttracks_upload_view),
url(r'^me/edit/$',views.rower_edit_view),
url(r'^me/edit/(.+.*)/$',views.rower_edit_view),

View File

@@ -1,11 +1,14 @@
import time
import operator
from django.views.generic.base import TemplateView
from django.db.models import Q
from django.shortcuts import render
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth import authenticate, login, logout
from rowers.forms import LoginForm,DocumentsForm,UploadOptionsForm
from django.core.urlresolvers import reverse
from django.template import RequestContext
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.conf import settings
from django.utils.datastructures import MultiValueDictKeyError
from django.utils import timezone,translation
@@ -1322,8 +1325,9 @@ def cum_flex(request,theuser=0,
u = ''
if allergworkouts:
res = interactive_cum_flex_chart(allergworkouts,xparam=xparam,
yparam1=yparam1,yparam2=yparam2,
res = interactive_cum_flex_chart2(allergworkouts,xparam=xparam,
yparam1=yparam1,
yparam2=yparam2,
promember=promember)
script = res[0]
div = res[1]
@@ -1352,6 +1356,36 @@ def cum_flex(request,theuser=0,
'promember':promember,
})
@login_required()
def workout_histo_view(request,id=0):
row = Workout.objects.get(id=id)
promember=0
mayedit=0
if not request.user.is_anonymous():
r = Rower.objects.get(user=request.user)
result = request.user.is_authenticated() and r.rowerplan=='pro'
if result:
promember=1
if request.user == row.user.user:
mayedit=1
if not promember:
return HttpResponseRedirect("/rowers/about/")
res = interactive_histoall([row])
script = res[0]
div = res[1]
return render(request,
'histo_single.html',
{'interactiveplot':script,
'the_div':div,
'id':id,
'mayedit':mayedit,
})
@login_required()
def histo(request,theuser=0,
@@ -1754,29 +1788,156 @@ def rankings_view(request,theuser=0,
})
@login_required()
def workouts_view(request,message='',successmessage=''):
def workout_recalcsummary_view(request,id=0):
row = Workout.objects.get(id=id)
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
url = reverse(workouts_view,args=[str(message)])
return HttpResponseRedirect(url)
filename = row.csvfilename
rowdata = rdata(filename)
row.summary = rowdata.allstats()
row.save()
successmessage = "Summary Updated"
url = reverse(workout_edit_view,
kwargs = {
'id':str(id),
'successmessage':str(successmessage),
})
return HttpResponseRedirect(url)
@login_required()
def workouts_view(request,message='',successmessage='',
startdatestring="",enddatestring="",
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now()):
try:
r = Rower.objects.get(user=request.user)
# res = mailprocessing.safeprocessattachments()
#if len(res)>0 and np.cumsum(np.array(res)).max()>0:
# successmessage = 'New Workouts have been created from email'
workouts = Workout.objects.filter(user=r).order_by("-date", "-starttime")
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))
if startdatestring:
startdate = iso8601.parse_date(startdatestring)
if enddatestring:
enddate = iso8601.parse_date(enddatestring)
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")
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,20) # 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)
return render(request, 'list_workouts.html',
{'workouts': workouts,
'message': message,
'successmessage':successmessage,
'dateform':dateform,
'startdate':startdate,
'enddate':enddate,
})
except Rower.DoesNotExist:
return HttpResponse("User has no rower instance")
@user_passes_test(promember,login_url="/login")
def workout_comparison_list(request,id=0,message='',successmessage=''):
def workout_comparison_list(request,id=0,message='',successmessage='',
startdatestring="",enddatestring="",
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now()):
try:
r = Rower.objects.get(user=request.user)
u = User.objects.get(id=r.user.id)
workouts = Workout.objects.filter(user=r).order_by("-date", "-starttime").exclude(id=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))
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 = Workout.objects.get(id=id)
return render(request, 'comparison_list.html',
@@ -1787,6 +1948,9 @@ def workout_comparison_list(request,id=0,message='',successmessage=''):
'first_name':u.first_name,
'message': message,
'successmessage':successmessage,
'dateform':dateform,
'startdate':startdate,
'enddate':enddate,
})
except Rower.DoesNotExist:
return HttpResponse("User has no rower instance")
@@ -2712,9 +2876,12 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
rowdata = rdata(f1)
hascoordinates = 1
if rowdata != 0:
try:
latitude = rowdata.df[' latitude']
except KeyError:
except KeyError,AttributeError:
hascoordinates = 0
else:
hascoordinates = 0
if hascoordinates:
@@ -3932,6 +4099,16 @@ def graphs_view(request):
try:
r = Rower.objects.get(user=request.user)
workouts = Workout.objects.filter(user=r).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))
)
g = GraphImage.objects.filter(workout__in=workouts).order_by("-creationdatetime")
if (len(g)<=5):
return render(request, 'list_graphs.html',

View File

@@ -17,14 +17,19 @@ a {
text-decoration: none;
}
a:visited { color:#000; }
a:link { color: #000; }
a:hover {
text-decoration: underline;
}
h1 {
/* font-family: Georgia, serif; */
font-weight: normal;
@@ -472,3 +477,15 @@ a.button {
position: relative;
z-index: 10;
}
a.wh:link {
color: #e9e9e9;
}
a.wh:visited {
color: #e9e9e9;
}
a.wh:hover {
color: #e9e9e9;
}