Merge branch 'release/v2.9'
This commit is contained in:
@@ -95,6 +95,15 @@ from scipy.signal import savgol_filter
|
||||
|
||||
import datetime
|
||||
|
||||
def get_workouts(ids,userid):
|
||||
goodids = []
|
||||
for id in ids:
|
||||
w = Workout.objects.get(id=id)
|
||||
if int(w.user.user.id) == int(userid):
|
||||
goodids.append(id)
|
||||
|
||||
return [Workout.objects.get(id=id) for id in goodids]
|
||||
|
||||
def filter_df(datadf,fieldname,value,largerthan=True):
|
||||
|
||||
try:
|
||||
|
||||
@@ -18,6 +18,7 @@ from rowsandall_app.settings import DATABASES
|
||||
|
||||
from utils import lbstoN
|
||||
|
||||
|
||||
user = DATABASES['default']['USER']
|
||||
password = DATABASES['default']['PASSWORD']
|
||||
database_name = DATABASES['default']['NAME']
|
||||
@@ -335,6 +336,7 @@ def handle_nonpainsled(f2,fileformat,summary=''):
|
||||
|
||||
return (f2,summary,oarlength,inboard)
|
||||
|
||||
|
||||
# Create new workout from file and store it in the database
|
||||
# This routine should be used everywhere in views.py and mailprocessing.py
|
||||
# Currently there is code duplication
|
||||
|
||||
@@ -22,6 +22,9 @@ def getexpired():
|
||||
if r.planexpires < datetime.datetime.now().date():
|
||||
if r.rowerplan == 'pro' or r.rowerplan == 'coach':
|
||||
lijst.append(r)
|
||||
if r.teamplanexpires < datetime.datetime.now().date():
|
||||
if r.rowerplan == 'pro' or r.rowerplan == 'coach':
|
||||
lijst.append(r)
|
||||
return lijst
|
||||
|
||||
|
||||
|
||||
+2
-1
@@ -173,6 +173,7 @@ class Rower(models.Model):
|
||||
|
||||
# Power Zone Data
|
||||
ftp = models.IntegerField(default=226,verbose_name="Functional Threshold Power")
|
||||
otwslack = models.IntegerField(default=0,verbose_name="OTW Power slack")
|
||||
|
||||
pw_ut2 = models.IntegerField(default=124,verbose_name="UT2 Power")
|
||||
pw_ut1 = models.IntegerField(default=171,verbose_name="UT1 Power")
|
||||
@@ -576,7 +577,7 @@ class AdvancedWorkoutForm(ModelForm):
|
||||
class RowerPowerForm(ModelForm):
|
||||
class Meta:
|
||||
model = Rower
|
||||
fields = ['ftp']
|
||||
fields = ['ftp','otwslack']
|
||||
|
||||
# Form to set rower's Power zones, including test routines
|
||||
# to enable consistency
|
||||
|
||||
@@ -123,12 +123,10 @@ of a rowers technique.</p>
|
||||
|
||||
|
||||
<p>Click on the PayPal button to pay for your Premium membership.
|
||||
It will be valid for one year with automatic renewal which you can stop at any time.
|
||||
Before you pay, please <a href="/rowers/register">register</a> for the free Basic membership and log in.
|
||||
In this way, your user name will be added to the payment details.
|
||||
Your payment will be valid for one year with automatic renewal which you can stop at any time.
|
||||
You will be taken to the secure PayPal payment site.
|
||||
<ul>
|
||||
<li>Please mention the username you are registered under in
|
||||
"instructions to seller".</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>After you do the payment, we will manually change your membership to
|
||||
"Pro". Depending on our availability, this may take some time
|
||||
@@ -138,6 +136,10 @@ You will be taken to the secure PayPal payment site.
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
|
||||
<input type="hidden" name="cmd" value="_s-xclick">
|
||||
<input type="hidden" name="hosted_button_id" value="964GLEXX3THAW">
|
||||
{% if user.is_authenticated %}
|
||||
<input type="hidden" name="os0" value="{{ user }}">
|
||||
<input type="hidden" name="on0" value="username">
|
||||
{% endif %}
|
||||
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_subscribeCC_LG_global.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online.">
|
||||
<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
|
||||
</form>
|
||||
|
||||
@@ -6,7 +6,18 @@
|
||||
|
||||
{% block content %}
|
||||
<h1>Available on C2 Logbook</h1>
|
||||
|
||||
{% if workouts %}
|
||||
<div class="grid_2 alpha ">
|
||||
<a href="/rowers/workout/c2import/all/" class="button gray">Import all NEW</a>
|
||||
</div>
|
||||
<div class="grid_10 omega">
|
||||
<p>This imports all workouts that have not been imported to rowsandall.com.
|
||||
The action may take a longer time to process, so please be patient. Click on Import in the list below to import an individual workout.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid_12 alpha">
|
||||
<table width="70%" class="listtable">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -17,6 +28,7 @@
|
||||
<th> Type</th>
|
||||
<th> Source</th>
|
||||
<th> Comment</th>
|
||||
<th> New</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -30,11 +42,13 @@
|
||||
<td>{{ workout|lookup:'rowtype' }}</td>
|
||||
<td>{{ workout|lookup:'source' }}</td>
|
||||
<td>{{ workout|lookup:'comment' }}</td>
|
||||
<td>{{ workout|lookup:'new' }}</td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p> No workouts found </p>
|
||||
{% endif %}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
|
||||
{% extends "base.html" %}
|
||||
{% block title %}About us{% endblock title %}
|
||||
{% block content %}
|
||||
<h2>PayPal Test Button</h2>
|
||||
|
||||
<div class="grid_6 alpha">
|
||||
|
||||
<p>This is a test page for testing the paypal functionality. Do not use it.</p>
|
||||
|
||||
<p>Donations are welcome to keep this web site going. To help cover the hosting
|
||||
costs, I have created a <q>Pro</q> membership option (for only 15 EURO per year). Once I process your
|
||||
donation, I will give you access to some <q>special</q> features on this
|
||||
website. </p>
|
||||
|
||||
<p>Currently, the Pro membership will give you the following extra functionality (and more will follow):
|
||||
<ul>
|
||||
<li>More stroke metrics plots</li>
|
||||
<li>Power curves for OTW rowing</li>
|
||||
<li>Add weather information to OTW rowing sessions</li>
|
||||
<li>Power histogram</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>Click on the PayPal button to pay for your Pro membership. Before you pay, please <a href="/rowers/register">register</a> for the free Basic membership and log in.
|
||||
In this way, your user name will be added to the payment details.
|
||||
Your payment will be valid for one year with automatic renewal which you can stop at any time.
|
||||
You will be taken to the secure PayPal payment site.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid_6 omega">
|
||||
<form action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post" target="_top">
|
||||
<input type="hidden" name="cmd" value="_s-xclick">
|
||||
<input type="hidden" name="hosted_button_id" value="JN7HNMTDBDARJ">
|
||||
{% if user.is_authenticated %}
|
||||
<input type="hidden" name="os0" value="{{ user }}">
|
||||
<input type="hidden" name="on0" value="username">
|
||||
{% endif %}
|
||||
<input type="image" src="https://www.sandbox.paypal.com/en_US/i/btn/btn_subscribeCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
|
||||
<img alt="" border="0" src="https://www.sandbox.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1">
|
||||
</form>
|
||||
|
||||
</form>
|
||||
|
||||
<p>After you do the payment, we will manually change your membership to
|
||||
"Pro". Depending on our availability, this may take some time
|
||||
(typically one working day). Don't hesitate to contact us
|
||||
if you have any questions at this stage.</p>
|
||||
|
||||
<p>If, for any reason, you are not happy with your Pro membership, please let me know through the contact form. I will contact you as soon as possible to discuss how we can make things better.</p>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
@@ -19,11 +19,10 @@ website. </p>
|
||||
<li>Power histogram</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>Click on the PayPal button to pay for your Pro membership. It will be valid for one year with automatic renewal which you can stop at any time.
|
||||
<p>Click on the PayPal button to pay for your Pro membership. Before you pay, please <a href="/rowers/register">register</a> for the free Basic membership and log in.
|
||||
In this way, your user name will be added to the payment details.
|
||||
Your payment will be valid for one year with automatic renewal which you can stop at any time.
|
||||
You will be taken to the secure PayPal payment site.
|
||||
<ul>
|
||||
<li>Please mention the username you are registered under in "instructions to seller".</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -31,6 +30,10 @@ You will be taken to the secure PayPal payment site.
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
|
||||
<input type="hidden" name="cmd" value="_s-xclick">
|
||||
<input type="hidden" name="hosted_button_id" value="964GLEXX3THAW">
|
||||
{% if user.is_authenticated %}
|
||||
<input type="hidden" name="os0" value="{{ user }}">
|
||||
<input type="hidden" name="on0" value="username">
|
||||
{% endif %}
|
||||
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_subscribeCC_LG_global.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online.">
|
||||
<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
|
||||
</form>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<div class="grid_6 alpha">
|
||||
<p>
|
||||
<h2>Heart Rate Zones</h2>
|
||||
<p>Set your heart rate zones with this form.</p>
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
@@ -27,6 +28,8 @@
|
||||
<div class="grid_6 omega">
|
||||
<p>
|
||||
<h2>Power Zones</h2>
|
||||
<p>The power zones are defined relative to power as measured by the
|
||||
indoor rower.</p>
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
{% if powerzonesform.errors %}
|
||||
<p style="color: red;">
|
||||
@@ -127,10 +130,15 @@
|
||||
</div>
|
||||
<div class="grid_6 omega">
|
||||
<p>
|
||||
<h2>Functional Threshold Power</h2>
|
||||
<h2>Functional Threshold Power and OTW Slack</h2>
|
||||
<p>Use this form to quickly change your zones based on the power of a
|
||||
recent
|
||||
full out 60 minutes effort. It will update all zones defined above.</p>
|
||||
full out 60 minutes effort on the ergometer.
|
||||
It will update all zones defined above.</p>
|
||||
<p>The OTW Power Slack is the percentage drop of your On-the-water
|
||||
rowing power
|
||||
vs the erg power. Typical values are around 15%. This will lower
|
||||
the power zones for your OTW workouts.</p>
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
<table>
|
||||
{{ powerform.as_table }}
|
||||
|
||||
@@ -7,6 +7,16 @@
|
||||
{% block content %}
|
||||
<h1>Available on SportTracks</h1>
|
||||
{% if workouts %}
|
||||
<div class="grid_2 alpha ">
|
||||
<a href="/rowers/workout/sporttracksimport/all/" class="button gray">Import all NEW</a>
|
||||
</div>
|
||||
<div class="grid_10 omega">
|
||||
<p>This imports all workouts that have not been imported to rowsandall.com.
|
||||
The action may take a longer time to process, so please be patient. Click on Import in the list below to import an individual workout.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid_12 alpha">
|
||||
<table width="70%" class="listtable">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -16,6 +26,7 @@
|
||||
<th> Duration </th>
|
||||
<th> Total Distance</th>
|
||||
<th> Type</th>
|
||||
<th> New</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -28,11 +39,13 @@
|
||||
<td>{{ workout|lookup:'duration' }} </td>
|
||||
<td>{{ workout|lookup:'distance' }} m</td>
|
||||
<td>{{ workout|lookup:'type' }}</td>
|
||||
<td>{{ workout|lookup:'new' }}</td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p> No workouts found </p>
|
||||
{% endif %}
|
||||
|
||||
@@ -6,46 +6,46 @@
|
||||
|
||||
{% block content %}
|
||||
<h1>Available on Strava</h1>
|
||||
{% if data %}
|
||||
{% if workouts %}
|
||||
<div class="grid_2 alpha ">
|
||||
<a href="/rowers/workout/stravaimport/all/" class="button gray">Import all NEW</a>
|
||||
</div>
|
||||
<div class="grid_10 omega">
|
||||
<p>This imports all workouts that have not been imported to rowsandall.com.
|
||||
The action may take a longer time to process, so please be patient. Click on Import in the list below to import an individual workout.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid_12 alpha">
|
||||
<table width="70%" class="listtable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th> Import </th>
|
||||
<th> Duration </th>
|
||||
<th> Type</th>
|
||||
<th> Date</th>
|
||||
<th> Distance </th>
|
||||
<th> Name</th>
|
||||
<th> Date</th>
|
||||
<th> Duration </th>
|
||||
<th> Distance </th>
|
||||
<th> Type</th>
|
||||
<th> New</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for workout in data %}
|
||||
{% for workout in workouts %}
|
||||
<tr>
|
||||
{% for key,value in workout.items %}
|
||||
{% if key == "start_date" %}
|
||||
<td>{{ value }}</td>
|
||||
{% endif %}
|
||||
{% if key == "name" %}
|
||||
<td>{{ value }}</td>
|
||||
{% endif %}
|
||||
{% if key == "type" %}
|
||||
<td>{{ value }}</td>
|
||||
{% endif %}
|
||||
{% if key == "distance" %}
|
||||
<td>{{ value }}m</td>
|
||||
{% endif %}
|
||||
{% if key == "elapsed_time" %}
|
||||
<td>{{ value }}</td>
|
||||
{% endif %}
|
||||
{% if key == "id" %}
|
||||
<td><a href="/rowers/workout/stravaimport/{{ value }}/">Import</a></td>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<td>
|
||||
<a href="/rowers/workout/stravaimport/{{ workout|lookup:'id' }}/">Import</a></td>
|
||||
<td>{{ workout|lookup:'name' }}</td>
|
||||
<td>{{ workout|lookup:'starttime' }}</td>
|
||||
<td>{{ workout|lookup:'duration' }} </td>
|
||||
<td>{{ workout|lookup:'distance' }} m</td>
|
||||
<td>{{ workout|lookup:'type' }}</td>
|
||||
<td>{{ workout|lookup:'new' }}</td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p> No workouts found </p>
|
||||
{% endif %}
|
||||
|
||||
@@ -170,6 +170,7 @@ urlpatterns = [
|
||||
url(r'^workout/(?P<id>\d+)/comment$',views.workout_comment_view),
|
||||
url(r'^workout/(?P<id>\d+)/emailtcx$',views.workout_tcxemail_view),
|
||||
url(r'^workout/(?P<id>\d+)/emailcsv$',views.workout_csvemail_view),
|
||||
url(r'^workout/(?P<id>\d+)/csvtoadmin$',views.workout_csvtoadmin_view),
|
||||
url(r'^workout/compare/(?P<id>\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),
|
||||
@@ -211,9 +212,12 @@ urlpatterns = [
|
||||
url(r'^workout/c2import/$',views.workout_c2import_view),
|
||||
url(r'^workout/stravaimport/$',views.workout_stravaimport_view),
|
||||
url(r'^workout/c2import/(?P<c2id>\d+)/$',views.workout_getc2workout_view),
|
||||
url(r'^workout/c2import/all/$',views.workout_getc2workout_all),
|
||||
url(r'^workout/stravaimport/(?P<stravaid>\d+)/$',views.workout_getstravaworkout_view),
|
||||
url(r'^workout/stravaimport/all/$',views.workout_getstravaworkout_all),
|
||||
url(r'^workout/sporttracksimport/$',views.workout_sporttracksimport_view),
|
||||
url(r'^workout/sporttracksimport/(?P<sporttracksid>\d+)/$',views.workout_getsporttracksworkout_view),
|
||||
url(r'^workout/sporttracksimport/all/$',views.workout_getsporttracksworkout_all),
|
||||
url(r'^workout/runkeeperimport/$',views.workout_runkeeperimport_view),
|
||||
url(r'^workout/runkeeperimport/(?P<runkeeperid>\d+)/$',views.workout_getrunkeeperworkout_view),
|
||||
url(r'^workout/underarmourimport/$',views.workout_underarmourimport_view),
|
||||
@@ -274,6 +278,7 @@ urlpatterns = [
|
||||
url(r'^videos', TemplateView.as_view(template_name='videos.html'),name='videos'),
|
||||
url(r'^analysis', TemplateView.as_view(template_name='analysis.html'),name='analysis'),
|
||||
url(r'^promembership', TemplateView.as_view(template_name='promembership.html'),name='promembership'),
|
||||
url(r'^paypaltest', TemplateView.as_view(template_name='paypaltest.html'),name='paypaltest'),
|
||||
url(r'^legal', TemplateView.as_view(template_name='legal.html'),name='legal'),
|
||||
url(r'^register$',views.rower_register_view),
|
||||
url(r'^register/thankyou/$', TemplateView.as_view(template_name='registerthankyou.html'), name='registerthankyou'),
|
||||
|
||||
@@ -4,6 +4,21 @@ import numpy as np
|
||||
|
||||
lbstoN = 4.44822
|
||||
|
||||
def uniqify(seq, idfun=None):
|
||||
# order preserving
|
||||
if idfun is None:
|
||||
def idfun(x): return x
|
||||
seen = {}
|
||||
result = []
|
||||
for item in seq:
|
||||
marker = idfun(item)
|
||||
# in old Python versions:
|
||||
# if seen.has_key(marker)
|
||||
# but in new ones:
|
||||
if marker in seen: continue
|
||||
seen[marker] = 1
|
||||
result.append(item)
|
||||
return result
|
||||
|
||||
def serialize_list(value,token=','):
|
||||
assert(isinstance(value, list) or isinstance(value, tuple) or isinstance(value,np.ndarray))
|
||||
|
||||
+286
-27
@@ -267,7 +267,7 @@ def splitstdata(lijst):
|
||||
|
||||
return [np.array(t),np.array(latlong)]
|
||||
|
||||
from utils import geo_distance,serialize_list,deserialize_list
|
||||
from utils import geo_distance,serialize_list,deserialize_list,uniqify
|
||||
from rowers.models import checkworkoutuser
|
||||
|
||||
# Check if a user is a Coach member
|
||||
@@ -1150,6 +1150,41 @@ def workout_csvemail_view(request,id=0):
|
||||
|
||||
return response
|
||||
|
||||
# Get Workout CSV file and send it to user's email address
|
||||
@login_required()
|
||||
def workout_csvtoadmin_view(request,id=0):
|
||||
message = ""
|
||||
r = Rower.objects.get(user=request.user)
|
||||
try:
|
||||
w = Workout.objects.get(id=id)
|
||||
except Workout.DoesNotExist:
|
||||
raise Http404("Workout doesn't exist")
|
||||
|
||||
csvfile = w.csvfilename
|
||||
if settings.DEBUG:
|
||||
res = handle_sendemailcsv.delay(
|
||||
'Sander',
|
||||
'Roosendaal',
|
||||
'roosendaalsander@gmail.com',csvfile)
|
||||
|
||||
else:
|
||||
res = queuehigh.enqueue(
|
||||
handle_sendemailcsv,
|
||||
'Sander',
|
||||
'Roosendaal',
|
||||
'roosendaalsander@gmail.com',
|
||||
csvfile)
|
||||
|
||||
successmessage = "The CSV file was sent to the site admin per email"
|
||||
messages.info(request,successmessage)
|
||||
url = reverse(workout_export_view,
|
||||
kwargs = {
|
||||
'id':str(w.id),
|
||||
})
|
||||
response = HttpResponseRedirect(url)
|
||||
|
||||
return response
|
||||
|
||||
# Send workout to TP
|
||||
@login_required()
|
||||
def workout_tp_upload_view(request,id=0):
|
||||
@@ -1310,8 +1345,10 @@ def workout_c2_upload_view(request,id=0):
|
||||
except C2NoTokenError:
|
||||
return HttpResponseRedirect("/rowers/me/c2authorize/")
|
||||
|
||||
if message:
|
||||
if message and c2id <=0:
|
||||
messages.error(request,message)
|
||||
elif message:
|
||||
messages.info(request,message)
|
||||
|
||||
|
||||
url = reverse(workout_export_view,
|
||||
@@ -1499,6 +1536,8 @@ def workout_sporttracks_upload_view(request,id=0):
|
||||
sporttracksid = sporttracksstuff.getidfromresponse(response)
|
||||
w.uploadedtosporttracks = sporttracksid
|
||||
w.save()
|
||||
message = "Upload to SportTracks was successful"
|
||||
messages.info(message)
|
||||
url = "/rowers/workout/"+str(w.id)+"/export"
|
||||
return HttpResponseRedirect(url)
|
||||
else:
|
||||
@@ -3111,7 +3150,7 @@ def boxplot_view(request,userid=0,
|
||||
})
|
||||
else:
|
||||
return HttpResponse("Form is not valid")
|
||||
if request.method == 'POST' and 'ids' in request.session:
|
||||
elif request.method == 'POST' and 'ids' in request.session:
|
||||
chartform = BoxPlotChoiceForm(request.POST)
|
||||
if chartform.is_valid():
|
||||
plotfield = chartform.cleaned_data['yparam']
|
||||
@@ -3124,7 +3163,18 @@ def boxplot_view(request,userid=0,
|
||||
workstrokesonly = not includereststrokes
|
||||
ids = request.session['ids']
|
||||
request.session['ids'] = ids
|
||||
workouts = [Workout.objects.get(id=id) for id in ids]
|
||||
workouts = dataprep.get_workouts(ids,userid)
|
||||
if not workouts:
|
||||
message = 'Error: Workouts in session storage do not belong to this user.'
|
||||
messages.error(request,message)
|
||||
url = reverse(user_boxplot_select,
|
||||
kwargs={
|
||||
'userid':userid,
|
||||
}
|
||||
)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
# workouts = [Workout.objects.get(id=id) for id in ids]
|
||||
|
||||
labeldict = {
|
||||
int(w.id): w.__unicode__() for w in workouts
|
||||
@@ -4344,6 +4394,10 @@ def cumstats(request,theuser=0,
|
||||
def workout_stats_view(request,id=0,message="",successmessage=""):
|
||||
|
||||
r = Rower.objects.get(user=request.user)
|
||||
try:
|
||||
w = Workout.objects.get(id=id)
|
||||
except Workout.DoesNotExist:
|
||||
raise Http404("Workout doesn't exist")
|
||||
|
||||
workstrokesonly = True
|
||||
if request.method == 'POST' and 'workstrokesonly' in request.POST:
|
||||
@@ -4412,9 +4466,13 @@ def workout_stats_view(request,id=0,message="",successmessage=""):
|
||||
pwr4 = datadf['power']**(4)
|
||||
normp = (pwr4.mean())**(0.25)
|
||||
if not np.isnan(normp):
|
||||
intensityfactor = datadf['power'].mean()/float(r.ftp)
|
||||
intensityfactor = normp/float(r.ftp)
|
||||
tss = 100.*((duration*normp*intensityfactor)/(3600.*r.ftp))
|
||||
ftp = float(r.ftp)
|
||||
if w.workouttype == 'water':
|
||||
ftp = ftp*(100.-r.otwslack)/100.
|
||||
|
||||
intensityfactor = datadf['power'].mean()/float(ftp)
|
||||
intensityfactor = normp/float(ftp)
|
||||
tss = 100.*((duration*normp*intensityfactor)/(3600.*ftp))
|
||||
|
||||
if not np.isnan(tss):
|
||||
otherstats['tss'] = {
|
||||
@@ -5249,6 +5307,10 @@ def workout_add_otw_powerplot_view(request,id):
|
||||
r.pw_tr,r.pw_an])/r.ftp
|
||||
|
||||
|
||||
ftp = r.ftp
|
||||
if w.workouttype == 'water':
|
||||
ftp = ftp*(100.-r.otwslack)/100.
|
||||
|
||||
hrpwrdata = {
|
||||
'hrmax':r.max,
|
||||
'hrut2':r.ut2,
|
||||
@@ -5256,7 +5318,7 @@ def workout_add_otw_powerplot_view(request,id):
|
||||
'hrat':r.at,
|
||||
'hrtr':r.tr,
|
||||
'hran':r.an,
|
||||
'ftp':r.ftp,
|
||||
'ftp':ftp,
|
||||
'powerperc':serialize_list(powerperc),
|
||||
'powerzones':serialize_list(r.powerzones),
|
||||
}
|
||||
@@ -5306,6 +5368,9 @@ def workout_add_piechart_view(request,id):
|
||||
r.pw_at,
|
||||
r.pw_tr,r.pw_an])/r.ftp
|
||||
|
||||
ftp = float(r.ftp)
|
||||
if w.workouttype == 'water':
|
||||
ftp = ftp*(100.-r.otwslack)/100.
|
||||
|
||||
hrpwrdata = {
|
||||
'hrmax':r.max,
|
||||
@@ -5314,7 +5379,7 @@ def workout_add_piechart_view(request,id):
|
||||
'hrat':r.at,
|
||||
'hrtr':r.tr,
|
||||
'hran':r.an,
|
||||
'ftp':r.ftp,
|
||||
'ftp':ftp,
|
||||
'powerperc':serialize_list(powerperc),
|
||||
'powerzones':serialize_list(r.powerzones),
|
||||
}
|
||||
@@ -5364,6 +5429,9 @@ def workout_add_power_piechart_view(request,id):
|
||||
r.pw_at,
|
||||
r.pw_tr,r.pw_an])/r.ftp
|
||||
|
||||
ftp = float(r.ftp)
|
||||
if w.workouttype == 'water':
|
||||
ftp = ftp*(100.-r.otwslack)/100.
|
||||
|
||||
hrpwrdata = {
|
||||
'hrmax':r.max,
|
||||
@@ -5372,7 +5440,7 @@ def workout_add_power_piechart_view(request,id):
|
||||
'hrat':r.at,
|
||||
'hrtr':r.tr,
|
||||
'hran':r.an,
|
||||
'ftp':r.ftp,
|
||||
'ftp':ftp,
|
||||
'powerperc':serialize_list(powerperc),
|
||||
'powerzones':serialize_list(r.powerzones),
|
||||
}
|
||||
@@ -5419,6 +5487,9 @@ def workout_add_timeplot_view(request,id):
|
||||
r.pw_at,
|
||||
r.pw_tr,r.pw_an])/r.ftp
|
||||
|
||||
ftp = float(r.ftp)
|
||||
if w.workouttype == 'water':
|
||||
ftp = ftp*(100.-r.otwslack)/100.
|
||||
|
||||
hrpwrdata = {
|
||||
'hrmax':r.max,
|
||||
@@ -5427,7 +5498,7 @@ def workout_add_timeplot_view(request,id):
|
||||
'hrat':r.at,
|
||||
'hrtr':r.tr,
|
||||
'hran':r.an,
|
||||
'ftp':r.ftp,
|
||||
'ftp':ftp,
|
||||
'powerperc':serialize_list(powerperc),
|
||||
'powerzones':serialize_list(r.powerzones),
|
||||
}
|
||||
@@ -5476,6 +5547,9 @@ def workout_add_distanceplot_view(request,id):
|
||||
r.pw_at,
|
||||
r.pw_tr,r.pw_an])/r.ftp
|
||||
|
||||
ftp = float(r.ftp)
|
||||
if w.workouttype == 'water':
|
||||
ftp = ftp*(100.-r.otwslack)/100.
|
||||
|
||||
hrpwrdata = {
|
||||
'hrmax':r.max,
|
||||
@@ -5484,7 +5558,7 @@ def workout_add_distanceplot_view(request,id):
|
||||
'hrat':r.at,
|
||||
'hrtr':r.tr,
|
||||
'hran':r.an,
|
||||
'ftp':r.ftp,
|
||||
'ftp':ftp,
|
||||
'powerperc':serialize_list(powerperc),
|
||||
'powerzones':serialize_list(r.powerzones),
|
||||
}
|
||||
@@ -5531,6 +5605,9 @@ def workout_add_distanceplot2_view(request,id):
|
||||
r.pw_at,
|
||||
r.pw_tr,r.pw_an])/r.ftp
|
||||
|
||||
ftp = float(r.ftp)
|
||||
if w.workouttype == 'water':
|
||||
ftp = ftp*(100.-r.otwslack)/100.
|
||||
|
||||
hrpwrdata = {
|
||||
'hrmax':r.max,
|
||||
@@ -5539,7 +5616,7 @@ def workout_add_distanceplot2_view(request,id):
|
||||
'hrat':r.at,
|
||||
'hrtr':r.tr,
|
||||
'hran':r.an,
|
||||
'ftp':r.ftp,
|
||||
'ftp':ftp,
|
||||
'powerperc':serialize_list(powerperc),
|
||||
'powerzones':serialize_list(r.powerzones),
|
||||
}
|
||||
@@ -5588,6 +5665,9 @@ def workout_add_timeplot2_view(request,id):
|
||||
r.pw_at,
|
||||
r.pw_tr,r.pw_an])/r.ftp
|
||||
|
||||
ftp = float(r.ftp)
|
||||
if w.workouttype == 'water':
|
||||
ftp = ftp*(100.-r.otwslack)/100.
|
||||
|
||||
hrpwrdata = {
|
||||
'hrmax':r.max,
|
||||
@@ -5596,7 +5676,7 @@ def workout_add_timeplot2_view(request,id):
|
||||
'hrat':r.at,
|
||||
'hrtr':r.tr,
|
||||
'hran':r.an,
|
||||
'ftp':r.ftp,
|
||||
'ftp':ftp,
|
||||
'powerperc':serialize_list(powerperc),
|
||||
'powerzones':serialize_list(r.powerzones),
|
||||
}
|
||||
@@ -5640,10 +5720,33 @@ def workout_stravaimport_view(request,message=""):
|
||||
url = reverse(workouts_view)
|
||||
return HttpResponseRedirect(url)
|
||||
else:
|
||||
data = res.json()
|
||||
workouts = []
|
||||
r = Rower.objects.get(user=request.user)
|
||||
stravaids = [int(item['id']) for item in res.json()]
|
||||
knownstravaids = uniqify([
|
||||
w.uploadedtostrava for w in Workout.objects.filter(user=r)
|
||||
])
|
||||
newids = [stravaid for stravaid in stravaids if not stravaid in knownstravaids]
|
||||
|
||||
for item in res.json():
|
||||
d = int(float(item['distance']))
|
||||
i = item['id']
|
||||
if i in knownstravaids:
|
||||
nnn = ''
|
||||
else:
|
||||
nnn = 'NEW'
|
||||
n = item['name']
|
||||
ttot = str(datetime.timedelta(seconds=int(float(item['elapsed_time']))))
|
||||
s = item['start_date']
|
||||
r = item['type']
|
||||
keys = ['id','distance','duration','starttime','type','name','new']
|
||||
values = [i,d,ttot,s,r,n,nnn]
|
||||
res = dict(zip(keys,values))
|
||||
workouts.append(res)
|
||||
|
||||
|
||||
return render(request,'strava_list_import.html',
|
||||
{'data':data,
|
||||
{'workouts':workouts,
|
||||
'teams':get_my_teams(request.user),
|
||||
})
|
||||
|
||||
@@ -5753,15 +5856,25 @@ def workout_sporttracksimport_view(request,message=""):
|
||||
return HttpResponseRedirect(url)
|
||||
else:
|
||||
workouts = []
|
||||
r = Rower.objects.get(user=request.user)
|
||||
stids = [int(getidfromsturi(item['uri'])) for item in res.json()['items']]
|
||||
knownstids = uniqify([
|
||||
w.uploadedtosporttracks for w in Workout.objects.filter(user=r)
|
||||
])
|
||||
newids = [stid for stid in stids if not stid in knownstids]
|
||||
for item in res.json()['items']:
|
||||
d = int(float(item['total_distance']))
|
||||
i = getidfromsturi(item['uri'])
|
||||
i = int(getidfromsturi(item['uri']))
|
||||
if i in knownstids:
|
||||
nnn = ''
|
||||
else:
|
||||
nnn = 'NEW'
|
||||
n = item['name']
|
||||
ttot = str(datetime.timedelta(seconds=int(float(item['duration']))))
|
||||
s = item['start_time']
|
||||
r = item['type']
|
||||
keys = ['id','distance','duration','starttime','type','name']
|
||||
values = [i,d,ttot,s,r,n]
|
||||
keys = ['id','distance','duration','starttime','type','name','new']
|
||||
values = [i,d,ttot,s,r,n,nnn]
|
||||
res = dict(zip(keys,values))
|
||||
workouts.append(res)
|
||||
return render(request,'sporttracks_list_import.html',
|
||||
@@ -5813,6 +5926,57 @@ def c2listdebug_view(request,message=""):
|
||||
'teams':get_my_teams(request.user),
|
||||
})
|
||||
|
||||
# Import all unknown workouts available on Concept2 logbook
|
||||
@login_required()
|
||||
def workout_getc2workout_all(request,message=""):
|
||||
try:
|
||||
thetoken = c2_open(request.user)
|
||||
except C2NoTokenError:
|
||||
return HttpResponseRedirect("/rowers/me/c2authorize/")
|
||||
|
||||
res = c2stuff.get_c2_workout_list(request.user)
|
||||
|
||||
if (res.status_code != 200):
|
||||
message = "Something went wrong in workout_c2import_view (C2 token refresh)"
|
||||
messages.error(request,message)
|
||||
else:
|
||||
r = Rower.objects.get(user=request.user)
|
||||
c2ids = [item['id'] for item in res.json()['data']]
|
||||
knownc2ids = uniqify([
|
||||
w.uploadedtoc2 for w in Workout.objects.filter(user=r)
|
||||
])
|
||||
newids = [c2id for c2id in c2ids if not c2id in knownc2ids]
|
||||
for c2id in newids:
|
||||
res = c2stuff.get_c2_workout(request.user,c2id)
|
||||
if (res.status_code == 200):
|
||||
data = res.json()['data']
|
||||
splitdata = None
|
||||
if 'workout' in data:
|
||||
if 'splits' in data['workout']:
|
||||
splitdata = data['workout']['splits']
|
||||
if 'intervals' in data['workout']:
|
||||
splitdata = data['workout']['intervals']
|
||||
|
||||
# Check if workout has stroke data, and get the stroke data
|
||||
if data['stroke_data']:
|
||||
res2 = c2stuff.get_c2_workout_strokes(request.user,c2id)
|
||||
# We have stroke data
|
||||
if res2.status_code == 200:
|
||||
strokedata = pd.DataFrame.from_dict(res2.json()['data'])
|
||||
# create the workout
|
||||
id,message = add_workout_from_strokedata(
|
||||
request.user,c2id,data,strokedata,
|
||||
source='c2')
|
||||
w = Workout.objects.get(id=id)
|
||||
w.uploadedtoc2=c2id
|
||||
w.save()
|
||||
if message:
|
||||
messages.error(request,message)
|
||||
|
||||
url = reverse(workouts_view)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
|
||||
# List of workouts available on Concept2 logbook - for import
|
||||
@login_required()
|
||||
def workout_c2import_view(request,message=""):
|
||||
@@ -5833,6 +5997,12 @@ def workout_c2import_view(request,message=""):
|
||||
return HttpResponseRedirect(url)
|
||||
else:
|
||||
workouts = []
|
||||
r = Rower.objects.get(user=request.user)
|
||||
c2ids = [item['id'] for item in res.json()['data']]
|
||||
knownc2ids = uniqify([
|
||||
w.uploadedtoc2 for w in Workout.objects.filter(user=r)
|
||||
])
|
||||
newids = [c2id for c2id in c2ids if not c2id in knownc2ids]
|
||||
for item in res.json()['data']:
|
||||
d = item['distance']
|
||||
i = item['id']
|
||||
@@ -5841,8 +6011,12 @@ def workout_c2import_view(request,message=""):
|
||||
r = item['type']
|
||||
s2 = item['source']
|
||||
c = item['comments']
|
||||
keys = ['id','distance','duration','starttime','rowtype','source','comment']
|
||||
values = [i,d,ttot,s,r,s2,c]
|
||||
if i in knownc2ids:
|
||||
nnn = ''
|
||||
else:
|
||||
nnn = 'NEW'
|
||||
keys = ['id','distance','duration','starttime','rowtype','source','comment','new']
|
||||
values = [i,d,ttot,s,r,s2,c,nnn]
|
||||
res = dict(zip(keys,values))
|
||||
workouts.append(res)
|
||||
|
||||
@@ -5948,6 +6122,69 @@ def workout_getsporttracksworkout_view(request,sporttracksid):
|
||||
})
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
# Imports all new workouts from SportTracks
|
||||
@login_required()
|
||||
def workout_getsporttracksworkout_all(request):
|
||||
res = sporttracksstuff.get_sporttracks_workout_list(request.user)
|
||||
if (res.status_code == 200):
|
||||
r = Rower.objects.get(user=request.user)
|
||||
stids = [int(getidfromsturi(item['uri'])) for item in res.json()['items']]
|
||||
knownstids = uniqify([
|
||||
w.uploadedtosporttracks for w in Workout.objects.filter(user=r)
|
||||
])
|
||||
newids = [stid for stid in stids if not stid in knownstids]
|
||||
for sporttracksid in newids:
|
||||
res = sporttracksstuff.get_sporttracks_workout(
|
||||
request.user,sporttracksid)
|
||||
data = res.json()
|
||||
|
||||
id,message = add_workout_from_stdata(request.user,sporttracksid,data)
|
||||
if id==0:
|
||||
messages.error(request,message)
|
||||
|
||||
else:
|
||||
w = Workout.objects.get(id=id)
|
||||
w.uploadedtosporttracks=sporttracksid
|
||||
w.save()
|
||||
|
||||
url = reverse(workouts_view)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
|
||||
# Imports all new workouts from SportTracks
|
||||
@login_required()
|
||||
def workout_getstravaworkout_all(request):
|
||||
res = stravastuff.get_strava_workout_list(request.user)
|
||||
if (res.status_code == 200):
|
||||
r = Rower.objects.get(user=request.user)
|
||||
stravaids = [int(item['id']) for item in res.json()]
|
||||
knownstravaids = uniqify([
|
||||
w.uploadedtostrava for w in Workout.objects.filter(user=r)
|
||||
])
|
||||
newids = [stravaid for stravaid in stravaids if not stravaid in knownstravaids]
|
||||
|
||||
for stravaid in newids:
|
||||
res = stravastuff.get_strava_workout(request.user,stravaid)
|
||||
strokedata = res[1]
|
||||
data = res[0]
|
||||
|
||||
id,message = add_workout_from_strokedata(request.user,stravaid,data,strokedata,
|
||||
source='strava',
|
||||
workoutsource='strava')
|
||||
|
||||
if id==0:
|
||||
messages.error(request,message)
|
||||
|
||||
else:
|
||||
w = Workout.objects.get(id=id)
|
||||
w.uploadedtostrava=stravaid
|
||||
w.save()
|
||||
|
||||
url = reverse(workouts_view)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
|
||||
|
||||
# Imports a workout from Concept2
|
||||
@login_required()
|
||||
def workout_getc2workout_view(request,c2id):
|
||||
@@ -6180,6 +6417,10 @@ def workout_upload_view(request,
|
||||
r.pw_at,
|
||||
r.pw_tr,r.pw_an])/r.ftp
|
||||
|
||||
ftp = float(r.ftp)
|
||||
if w.workouttype == 'water':
|
||||
ftp = ftp*(100.-r.otwslack)/100.
|
||||
|
||||
hrpwrdata = {
|
||||
'hrmax':r.max,
|
||||
'hrut2':r.ut2,
|
||||
@@ -6187,7 +6428,7 @@ def workout_upload_view(request,
|
||||
'hrat':r.at,
|
||||
'hrtr':r.tr,
|
||||
'hran':r.an,
|
||||
'ftp':r.ftp,
|
||||
'ftp':ftp,
|
||||
'powerperc':serialize_list(powerperc),
|
||||
'powerzones':serialize_list(r.powerzones),
|
||||
}
|
||||
@@ -6424,6 +6665,10 @@ def team_workout_upload_view(request,message="",
|
||||
r.pw_at,
|
||||
r.pw_tr,r.pw_an])/r.ftp
|
||||
|
||||
ftp = float(r.ftp)
|
||||
if w.workouttype == 'water':
|
||||
ftp = ftp*(100.-r.otwslack)/100.
|
||||
|
||||
hrpwrdata = {
|
||||
'hrmax':r.max,
|
||||
'hrut2':r.ut2,
|
||||
@@ -6431,7 +6676,7 @@ def team_workout_upload_view(request,message="",
|
||||
'hrat':r.at,
|
||||
'hrtr':r.tr,
|
||||
'hran':r.an,
|
||||
'ftp':r.ftp,
|
||||
'ftp':ftp,
|
||||
'powerperc':serialize_list(powerperc),
|
||||
'powerzones':serialize_list(r.powerzones),
|
||||
}
|
||||
@@ -6633,9 +6878,13 @@ def workout_summary_restore_view(request,id,message="",successmessage=""):
|
||||
r.pw_at,
|
||||
r.pw_tr,r.pw_an])/r.ftp
|
||||
|
||||
ftp = float(r.ftp)
|
||||
if row.workouttype == 'water':
|
||||
ftp = ftp*(100.-r.otwslack)/100.
|
||||
|
||||
rr = rrower(hrmax=r.max,hrut2=r.ut2,
|
||||
hrut1=r.ut1,hrat=r.at,
|
||||
hrtr=r.tr,hran=r.an,ftp=r.ftp,
|
||||
hrtr=r.tr,hran=r.an,ftp=ftp,
|
||||
powerperc=powerperc,powerzones=r.powerzones)
|
||||
rowdata = rdata(f1,rower=rr)
|
||||
if rowdata == 0:
|
||||
@@ -6744,9 +6993,13 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
|
||||
r.pw_at,
|
||||
r.pw_tr,r.pw_an])/r.ftp
|
||||
|
||||
ftp = float(r.ftp)
|
||||
if row.workouttype == 'water':
|
||||
ftp = ftp*(100.-r.otwslack)/100.
|
||||
|
||||
rr = rrower(hrmax=r.max,hrut2=r.ut2,
|
||||
hrut1=r.ut1,hrat=r.at,
|
||||
hrtr=r.tr,hran=r.an,ftp=r.ftp,
|
||||
hrtr=r.tr,hran=r.an,ftp=ftp,
|
||||
powerperc=powerperc,powerzones=r.powerzones)
|
||||
rowdata = rdata(f1,rower=rr)
|
||||
if rowdata == 0:
|
||||
@@ -7065,6 +7318,7 @@ def rower_edit_view(request,message=""):
|
||||
if powerform.is_valid():
|
||||
cd = powerform.cleaned_data
|
||||
ftp = cd['ftp']
|
||||
otwslack = cd['otwslack']
|
||||
try:
|
||||
r = Rower.objects.get(user=request.user)
|
||||
powerfrac = 100*np.array([r.pw_ut2,
|
||||
@@ -7072,6 +7326,7 @@ def rower_edit_view(request,message=""):
|
||||
r.pw_at,
|
||||
r.pw_tr,r.pw_an])/r.ftp
|
||||
r.ftp = max(min(ftp,650),50)
|
||||
r.otwslack = max(min(otwslack,50),0)
|
||||
ut2,ut1,at,tr,an = (r.ftp*powerfrac/100.).astype(int)
|
||||
r.pw_ut2 = ut2
|
||||
r.pw_ut1 = ut1
|
||||
@@ -7079,7 +7334,7 @@ def rower_edit_view(request,message=""):
|
||||
r.pw_tr = tr
|
||||
r.pw_an = an
|
||||
r.save()
|
||||
message = "Functional Threshold Value Changed"
|
||||
message = "FTP and/or OTW slack values changed."
|
||||
messages.info(request,message)
|
||||
url = reverse(rower_edit_view)
|
||||
response = HttpResponseRedirect(url)
|
||||
@@ -7428,9 +7683,13 @@ def strokedatajson(request,id):
|
||||
r.pw_at,
|
||||
r.pw_tr,r.pw_an])/r.ftp
|
||||
|
||||
ftp = float(r.ftp)
|
||||
if row.workouttype == 'water':
|
||||
ftp = ftp*(100.-r.otwslack)/100.
|
||||
|
||||
rr = rrower(hrmax=r.max,hrut2=r.ut2,
|
||||
hrut1=r.ut1,hrat=r.at,
|
||||
hrtr=r.tr,hran=r.an,ftp=r.ftp,
|
||||
hrtr=r.tr,hran=r.an,ftp=ftp,
|
||||
powerperc=powerperc,powerzones=r.powerzones)
|
||||
rowdata = rdata(row.csvfilename,rower=rr).df
|
||||
|
||||
|
||||
Reference in New Issue
Block a user