Private
Public Access
1
0

Merge branch 'develop' into feature/restapi

This commit is contained in:
Sander Roosendaal
2016-12-20 16:31:14 +01:00
7 changed files with 266 additions and 160 deletions

BIN
logos/logofornk.psd Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -178,6 +178,56 @@ def interactive_forcecurve(theworkouts):
plot.add_layout(avf) plot.add_layout(avf)
peakflabel = Label(x=455,y=530,x_units='screen',y_units='screen',
text="Fpeak: {peakforceav:6.2f}".format(peakforceav=peakforceav),
background_fill_alpha=.7,
text_color='blue',
)
avflabel = Label(x=465,y=500,x_units='screen',y_units='screen',
text="Favg: {averageforceav:6.2f}".format(averageforceav=averageforceav),
background_fill_alpha=.7,
text_color='blue',
)
catchlabel = Label(x=460,y=470,x_units='screen',y_units='screen',
text="Catch: {catchav:6.2f}".format(catchav=catchav),
background_fill_alpha=0.7,
text_color='red',
)
peakforceanglelabel = Label(x=420,y=440,x_units='screen',y_units='screen',
text="Peak angle: {peakforceangleav:6.2f}".format(peakforceangleav=peakforceangleav),
background_fill_alpha=0.7,
text_color='red',
)
finishlabel = Label(x=455,y=410,x_units='screen',y_units='screen',
text="Finish: {finishav:6.2f}".format(finishav=finishav),
background_fill_alpha=0.7,
text_color='red',
)
sliplabel = Label(x=470,y=380,x_units='screen',y_units='screen',
text="Slip: {slipav:6.2f}".format(slipav=slipav),
background_fill_alpha=0.7,
text_color='red',
)
washlabel = Label(x=460,y=350,x_units='screen',y_units='screen',
text="Wash: {washav:6.2f}".format(washav=washav),
background_fill_alpha=0.7,
text_color='red',
)
plot.add_layout(peakflabel)
plot.add_layout(peakforceanglelabel)
plot.add_layout(avflabel)
plot.add_layout(catchlabel)
plot.add_layout(sliplabel)
plot.add_layout(washlabel)
plot.add_layout(finishlabel)
plot.xaxis.axis_label = "Angle" plot.xaxis.axis_label = "Angle"
plot.yaxis.axis_label = "Force (lbs)" plot.yaxis.axis_label = "Force (lbs)"
plot.title.text = theworkouts[0].name plot.title.text = theworkouts[0].name
@@ -193,6 +243,13 @@ def interactive_forcecurve(theworkouts):
source=source, source=source,
source2=source2, source2=source2,
avf=avf, avf=avf,
avflabel=avflabel,
catchlabel=catchlabel,
finishlabel=finishlabel,
sliplabel=sliplabel,
washlabel=washlabel,
peakflabel=peakflabel,
peakforceanglelabel=peakforceanglelabel,
), code=""" ), code="""
var data = source.data var data = source.data
var data2 = source2.data var data2 = source2.data
@@ -254,6 +311,13 @@ def interactive_forcecurve(theworkouts):
data['y'] = [0,thresholdforce,peakforceav,thresholdforce,0] data['y'] = [0,thresholdforce,peakforceav,thresholdforce,0]
avf.location = averageforceav avf.location = averageforceav
avflabel.text = 'Favg: '+averageforceav.toFixed(2)
catchlabel.text = 'Catch: '+catchav.toFixed(2)
finishlabel.text = 'Finish: '+finishav.toFixed(2)
sliplabel.text = 'Slip: '+slipav.toFixed(2)
washlabel.text = 'Wash: '+washav.toFixed(2)
peakflabel.text = 'Fpeak: '+peakforceav.toFixed(2)
peakforceanglelabel.text = 'Peak angle: '+peakforceangleav.toFixed(2)
source.trigger('change'); source.trigger('change');
""") """)

View File

@@ -344,13 +344,18 @@ class WorkoutForm(ModelForm):
duration = forms.TimeInput(format='%H:%M:%S.%f') duration = forms.TimeInput(format='%H:%M:%S.%f')
class Meta: class Meta:
model = Workout model = Workout
fields = ['name','date','starttime','duration','distance','workouttype','notes'] fields = ['name','date','starttime','duration','distance','workouttype','boattype','notes']
widgets = { widgets = {
'date': DateInput(), 'date': DateInput(),
'notes': forms.Textarea, 'notes': forms.Textarea,
'duration': forms.TimeInput(format='%H:%M:%S.%f'), 'duration': forms.TimeInput(format='%H:%M:%S.%f'),
} }
def __init__(self, *args, **kwargs):
super(WorkoutForm, self).__init__(*args, **kwargs)
if self.instance.workouttype != 'water':
del self.fields['boattype']
class AdvancedWorkoutForm(ModelForm): class AdvancedWorkoutForm(ModelForm):
class Meta: class Meta:
model = Workout model = Workout

View File

@@ -15,7 +15,8 @@
<h1>Advanced OTW features</h1> <h1>Advanced OTW features</h1>
{% if user.rower.rowerplan == 'basic' %} {% 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 %} {% endif %}
<div class="grid_2 alpha"> <div class="grid_2 alpha">
<p> <p>
@@ -60,96 +61,14 @@
Compare this workout to other workouts. Plot HR, SPM, or pace vs time or distance for the two workouts. Compare this workout to other workouts. Plot HR, SPM, or pace vs time or distance for the two workouts.
</p> </p>
</div> </div>
<div class="grid_2"> <div class="grid_2">
<p>
{% if user.rower.rowerplan == 'pro' %}
<a class="button blue small"href="/rowers/workout/{{ workout.id }}/smoothenpace">Smooth out Pace Data</a>
{% else %}
<a class="button blue small" href="/rowers/promembership">Smooth out Pace Data</a>
{% endif %}
</p>
<p>
This will reduce noise on your pace data (EWMA average). The smoothing is irreversible
but you can use the reset smoothing button.
</p>
</div>
<div class="grid_2 omega">
<p>
{% if user.rower.rowerplan == 'pro' %}
<a class="button blue small"href="/rowers/workout/{{ workout.id }}/undosmoothenpace">Raw Data</a>
{% else %}
<a class="button blue small" href="/rowers/promembership">Reset Smoothing</a>
{% endif %}
</p>
<p>
Reset pace data to values before smoothing (as originally imported/uploaded)
</p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
{% if user.rower.rowerplan == 'pro' %}
<a class="button blue small"href="/rowers/workout/{{ workout.id }}/crewnerdsummary">CrewNerd Summary</a>
{% else %}
<a class="button blue small" href="/rowers/promembership">CrewNerd Summary</a>
{% endif %}
</p>
<p>
Upload a CrewNerd Summary (CSV file) to this workout.
</p>
</div>
<div class="grid_2">
<p>
{% if user.rower.rowerplan == 'pro' %}
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/geeky">Geeky Stuff</a>
{% else %}
<a class="button blue small" href="/rowers/promembership">Geeky Stuff</a>
{% endif %}
</p>
<p>
Add weather and current data and OTW power calculations.
</p>
</div>
<div class="grid_2 omega">
<p>
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/interactiveplot">Big Interactive Plot</a>
</p>
<p>
See (and save) the big interactive plot
</p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p> <p>
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/flexchart">Flexible Interactive Plot</a> <a class="button blue small" href="/rowers/workout/{{ workout.id }}/flexchart">Flexible Interactive Plot</a>
</p> </p>
<p> <p>
Flexible Interactive plot. Pick your own X and Y axis parameters. Flexible Interactive plot. Pick your own X and Y axis parameters.
</p> </p>
</div>
<div class="grid_2 tooltip">
<p>
{% if user.rower.rowerplan == 'pro' %}
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/addotwpowerplot">OTW Power Plot</a>
{% else %}
<a class="button blue small" href="/rowers/promembership">OTW Power Plot</a>
{% endif %}
</p>
<span class="tooltiptext">Note: You must run the OTW calculations under Geeky Stuff first. Otherwise the plot will be empty</span>
<p>
Pace, wind corrected pace, power, equivalent erg power in a static plot
</p>
</div> </div>
<div class="grid_2 omega tooltip"> <div class="grid_2 omega tooltip">
@@ -166,6 +85,111 @@ Pace, wind corrected pace, power, equivalent erg power in a static plot
Enter or change the interval and summary data for your workout Enter or change the interval and summary data for your workout
</p> </p>
</div> </div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
{% if user.rower.rowerplan == 'pro' %}
<a class="button blue small"href="/rowers/workout/{{ workout.id }}/crewnerdsummary">CrewNerd Summary</a>
{% else %}
<a class="button blue small" href="/rowers/promembership">CrewNerd Summary</a>
{% endif %}
</p>
<p>
Upload a CrewNerd Summary (CSV file) to this workout.
</p>
</div>
<div class="grid_2 tooltip">
<p>
{% if user.rower.rowerplan == 'pro' %}
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/forcecurve">Stroke Profile (Empower)</a>
{% else %}
<a class="button blue small" href="/rowers/promembership">Stroke Profile (Empower)</a>
{% endif %}
</p>
<span class="tooltiptext">Analyze your stroke force profile (need Empower Oarlock data)</span> <p>
Interactive plot of Stroke Profile (with Empower Oarlock)
</p>
</div>
<div class="grid_2 omega tooltip">
<p>
{% if user.rower.rowerplan == 'pro' %}
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/addotwpowerplot">OTW Power Plot</a>
{% else %}
<a class="button blue small" href="/rowers/promembership">OTW Power Plot</a>
{% endif %}
</p>
<span class="tooltiptext">Note: You must run the OTW calculations under Geeky Stuff first. Otherwise the plot will be empty</span>
<p>
Pace, wind corrected pace, power, equivalent erg power in a static plot
</p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
{% if user.rower.rowerplan == 'pro' %}
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/geeky">Geeky Stuff</a>
{% else %}
<a class="button blue small" href="/rowers/promembership">Geeky Stuff</a>
{% endif %}
</p>
<p>
Add weather and current data and OTW power calculations.
</p>
</div>
<div class="grid_2 tooltip">
<p>
{% if user.rower.rowerplan == 'pro' %}
<a class="button blue small"href="/rowers/workout/{{ workout.id }}/smoothenpace">Smooth out Pace Data</a>
{% else %}
<a class="button blue small" href="/rowers/promembership">Smooth out Pace Data</a>
{% endif %}
</p>
<span class="tooltiptext">This will reduce noise on your pace data. The smoothing is irreversible but you can use the reset smoothing button.</span>
<p>
Pace data too noisy? Press this button!
</p>
</div>
<div class="grid_2 omega">
<p>
{% if user.rower.rowerplan == 'pro' %}
<a class="button blue small"href="/rowers/workout/{{ workout.id }}/undosmoothenpace">Raw Data</a>
{% else %}
<a class="button blue small" href="/rowers/promembership">Reset Smoothing</a>
{% endif %}
</p>
<p>
Reset pace data to values before smoothing
</p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/interactiveplot">Big Interactive Plot</a>
</p>
<p>
See (and save) the big interactive plot
</p>
</div>
</div> </div>
</div> </div>
@@ -204,5 +228,5 @@ Enter or change the interval and summary data for your workout
{{ the_div |safe }} {{ the_div |safe }}
</div> </div>
</div> </div>
</div>
{% endblock %} {% endblock %}

View File

@@ -45,7 +45,7 @@
<tbody> <tbody>
{% for workout in workouts %} {% for workout in workouts %}
<tr> <tr>
<td> {{ workout.date }} </td> <td> {{ workout.date |truncatechars:15}} </td>
<td> {{ workout.starttime }} </td> <td> {{ workout.starttime }} </td>
<td> <td>
{% if user.rower.rowerplan == 'pro' %} {% if user.rower.rowerplan == 'pro' %}
@@ -81,7 +81,7 @@
<div class="site-announcement-box"> <div class="site-announcement-box">
<div class="site-announcement"> <div class="site-announcement">
<i>{{ a.created }}:</i> <i>{{ a.created }}:</i>
{{ a.announcement }} {{ a.announcement|urlize }}
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
@@ -96,7 +96,8 @@
Read his <a href="http://blog.rowsandall.com/">blog</a> Read his <a href="http://blog.rowsandall.com/">blog</a>
</p> </p>
<div style="text-align: right; padding: 2em"> <div style="text-align: right; padding: 2em">
<img src="/static/img/sander.jpg" width="80"> <a href="http://blog.rowsandall.com/">
<img src="/static/img/sander.jpg" width="80"></a>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -3041,6 +3041,10 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
duration = form.cleaned_data['duration'] duration = form.cleaned_data['duration']
distance = form.cleaned_data['distance'] distance = form.cleaned_data['distance']
notes = form.cleaned_data['notes'] notes = form.cleaned_data['notes']
try:
boattype = request.POST['boattype']
except KeyError:
boattype = Workout.objects.get(id=id).boattype
startdatetime = (str(date) + ' ' + str(starttime)) startdatetime = (str(date) + ' ' + str(starttime))
startdatetime = datetime.datetime.strptime(startdatetime, startdatetime = datetime.datetime.strptime(startdatetime,
"%Y-%m-%d %H:%M:%S") "%Y-%m-%d %H:%M:%S")
@@ -3058,6 +3062,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
row.notes = notes row.notes = notes
row.duration = duration row.duration = duration
row.distance = distance row.distance = distance
row.boattype = boattype
row.save() row.save()
# change data in csv file # change data in csv file
# startdatetime = dateutil.parser.parse("{}, {}".format(date,starttime)) # startdatetime = dateutil.parser.parse("{}, {}".format(date,starttime))
@@ -3738,6 +3743,13 @@ def workout_upload_view(request,message=""):
fileformat = fileformat[2] fileformat = fileformat[2]
os.remove(f_to_be_deleted) os.remove(f_to_be_deleted)
if fileformat == 'c2log':
message = "This C2 logbook summary does not contain stroke data. Please download the Export Stroke Data file from the workout details on the C2 logbook."
url = reverse(workout_upload_view,
args=[str(message)])
response = HttpResponseRedirect(url)
return response
if fileformat == 'unknown': if fileformat == 'unknown':
message = "We couldn't recognize the file type" message = "We couldn't recognize the file type"
url = reverse(workout_upload_view, url = reverse(workout_upload_view,