Private
Public Access
1
0

Merge branch 'release/v4.64'

This commit is contained in:
Sander Roosendaal
2017-10-29 21:14:59 +01:00
8 changed files with 189 additions and 25 deletions
+3
View File
@@ -3,6 +3,7 @@ from __future__ import absolute_import
import os import os
from celery import Celery from celery import Celery
from celery import result
# Only used for testing with Celery on localhost. RQ is not available # Only used for testing with Celery on localhost. RQ is not available
# on Windows, so I use Celery on my notebook. # on Windows, so I use Celery on my notebook.
@@ -17,6 +18,7 @@ app = Celery('tasks',
broker='redis://localhost', broker='redis://localhost',
backend='redis://localhost',) backend='redis://localhost',)
class Config: class Config:
CELERY_TIMEZONE = 'Europe/Prague' CELERY_TIMEZONE = 'Europe/Prague'
@@ -31,3 +33,4 @@ database_url = 'sqlite:///db.sqlite3'
@app.task(bind=True) @app.task(bind=True)
def debug_task(self): def debug_task(self):
print('Request: {0!r}'.format(self.request)) print('Request: {0!r}'.format(self.request))
+9 -6
View File
@@ -473,9 +473,11 @@ def updatecpdata_sql(rower_id,delta,cp,table='cpdata',distance=[]):
engine.dispose() engine.dispose()
def runcpupdate(rower,type='water'): def runcpupdate(
startdate = timezone.now()-datetime.timedelta(days=365) rower,type='water',
enddate = timezone.now()+datetime.timedelta(days=5) startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now()+datetime.timedelta(days=5)
):
if type == 'water': if type == 'water':
theworkouts = Workout.objects.filter( theworkouts = Workout.objects.filter(
user=rower,rankingpiece=True, user=rower,rankingpiece=True,
@@ -499,11 +501,12 @@ def runcpupdate(rower,type='water'):
theids = [w.id for w in theworkouts] theids = [w.id for w in theworkouts]
if settings.DEBUG: if settings.DEBUG:
res = handle_updatecp.delay(rower.id,theids,debug=True,table=table) job = handle_updatecp.delay(rower.id,theids,debug=True,table=table)
else: else:
res = queue.enqueue(handle_updatecp,rower.id,theids,table=table) job = queue.enqueue(handle_updatecp,rower.id,theids,table=table)
return job
def fetchcperg(rower,theworkouts): def fetchcperg(rower,theworkouts):
theids = [int(w.id) for w in theworkouts] theids = [int(w.id) for w in theworkouts]
+9
View File
@@ -264,6 +264,15 @@ class Rower(models.Model):
p3 = models.FloatField(default=1.0,verbose_name="CP p4") p3 = models.FloatField(default=1.0,verbose_name="CP p4")
cpratio = models.FloatField(default=1.0,verbose_name="CP fit ratio") cpratio = models.FloatField(default=1.0,verbose_name="CP fit ratio")
ep0 = models.FloatField(default=1.0,verbose_name="erg CP p1")
ep1 = models.FloatField(default=1.0,verbose_name="erg CP p2")
ep2 = models.FloatField(default=1.0,verbose_name="erg CP p3")
ep3 = models.FloatField(default=1.0,verbose_name="erg CP p4")
ecpratio = models.FloatField(default=1.0,verbose_name="erg CP fit ratio")
otwslack = models.IntegerField(default=0,verbose_name="OTW Power slack") otwslack = models.IntegerField(default=0,verbose_name="OTW Power slack")
pw_ut2 = models.IntegerField(default=124,verbose_name="UT2 Power") pw_ut2 = models.IntegerField(default=124,verbose_name="UT2 Power")
+14 -2
View File
@@ -50,7 +50,11 @@
</button> </button>
<div class="dropdown-content"> <div class="dropdown-content">
{% for member in user|team_members %} {% for member in user|team_members %}
{% if workouttype == 'water' %}
<a class="button green small" href="/rowers/{{ member.id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}">{{ member.first_name }} {{ member.last_name }}</a> <a class="button green small" href="/rowers/{{ member.id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}">{{ member.first_name }} {{ member.last_name }}</a>
{% else %}
<a class="button green small" href="/rowers/{{ member.id }}/ote-ranking/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}">{{ member.first_name }} {{ member.last_name }}</a>
{% endif %}
{% endfor %} {% endfor %}
</div> </div>
{% else %} {% else %}
@@ -64,14 +68,22 @@
between {{ startdate|date }} and {{ enddate|date }}</p> between {{ startdate|date }} and {{ enddate|date }}</p>
<p>Direct link for other users: <p>Direct link for other users:
<a href="/rowers/{{ id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}">https://rowsandall.com/rowers/{{ id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}</a> {% if workouttype == 'water' %}
<a href="/rowers/{{ id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}">https://rowsandall.com/rowers/{{ id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}</a>
{% else %}
<a href="/rowers/{{ id }}/ote-ranking/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}">https://rowsandall.com/rowers/{{ id }}/ote-ranking/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}</a>
{% endif %}
</p> </p>
<p>The table gives the OTW efforts you marked as Ranking Piece. <p>The table gives the efforts you marked as Ranking Piece.
The graph shows the best segments from those pieces, plotted as The graph shows the best segments from those pieces, plotted as
average power (over the segment) vs the duration of the segment/ average power (over the segment) vs the duration of the segment/
In other words: How long you can hold that power. In other words: How long you can hold that power.
</p> </p>
<p>When you change the date range, the algorithm calculates new
parameters in a background process. You may have to reload the
page to get an updated prediction.</p>
<p>At the bottom of the page, you will find predictions derived from the model.</p> <p>At the bottom of the page, you will find predictions derived from the model.</p>
</div> </div>
<div id="form" class="grid_6 omega"> <div id="form" class="grid_6 omega">
+10 -1
View File
@@ -43,7 +43,16 @@
</p> </p>
{% endif %} {% endif %}
</div> </div>
<div class="grid_2 suffix_8 omega"> <div class="grid_2">
{% if user.is_authenticated and user == rower.user %}
<p>
<a class="button gray small" href="/rowers/workout/{{ graph.workout.id }}/workflow">Workflow View</a>
</p>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_2 suffix_6 omega">
{% if user.is_authenticated and user == rower.user %} {% if user.is_authenticated and user == rower.user %}
<p> <p>
<a class="button red small" href="/rowers/graph/{{ graph.id }}/deleteconfirm">Delete Chart</a> <a class="button red small" href="/rowers/graph/{{ graph.id }}/deleteconfirm">Delete Chart</a>
+14 -2
View File
@@ -21,7 +21,7 @@
<p>On this page, you can configure the content of your "Workflow" page <p>On this page, you can configure the content of your "Workflow" page
for each workout. If you want to remove an element, change it to "None". for each workout. If you want to remove an element, change it to "None".
You can add one new element at a time.</p> You can add one new element at a time.</p>
<div class="grid_6 alpha"> <div class="grid_4 alpha">
<h2>Left Panel</h2> <h2>Left Panel</h2>
<form enctype="multipart/form-data" action="" method="post"> <form enctype="multipart/form-data" action="" method="post">
{{ leftpanel_formset.management_form }} {{ leftpanel_formset.management_form }}
@@ -33,7 +33,7 @@
</form> </form>
</div> </div>
<div class="grid_6 omega "> <div class="grid_4">
<h2>Middle Panel</h2> <h2>Middle Panel</h2>
<form enctype="multipart/form-data" action="" method="post"> <form enctype="multipart/form-data" action="" method="post">
{{ middlepanel_formset.management_form }} {{ middlepanel_formset.management_form }}
@@ -45,6 +45,18 @@
</form> </form>
</div> </div>
<div class="grid_4 omega">
<div class="grid_2 prefix_2 alpha">
{% if rower.defaultlandingpage == 'workout_edit_view' %}
<p>Default landing page is Edit View. Set default landing page to</p>
<a class="button green small" href="/rowers/me/workflowdefault">Workflow View</a>
{% else %}
<p>Default landing page is Workflow View. Set default landing page to</p>
<a class="button green small" href="/rowers/me/workflowdefault">Edit View</a>
{% endif %}
</div>
</div>
</div> </div>
</div> </div>
+1
View File
@@ -320,6 +320,7 @@ urlpatterns = [
url(r'^me/favoritecharts/$',views.rower_favoritecharts_view), url(r'^me/favoritecharts/$',views.rower_favoritecharts_view),
url(r'^me/workflowconfig$',views.workout_workflow_config_view), url(r'^me/workflowconfig$',views.workout_workflow_config_view),
url(r'^me/workflowconfig2$',views.workout_workflow_config2_view), url(r'^me/workflowconfig2$',views.workout_workflow_config2_view),
url(r'^me/workflowdefault$',views.workflow_default_view),
url(r'^email/send/$', views.sendmail), url(r'^email/send/$', views.sendmail),
url(r'^email/thankyou/$', TemplateView.as_view(template_name='thankyou.html'), name='thankyou'), url(r'^email/thankyou/$', TemplateView.as_view(template_name='thankyou.html'), name='thankyou'),
url(r'^email/$', TemplateView.as_view(template_name='email.html'), name='email'), url(r'^email/$', TemplateView.as_view(template_name='email.html'), name='email'),
+127 -12
View File
@@ -131,6 +131,16 @@ queue = django_rq.get_queue('default')
queuelow = django_rq.get_queue('low') queuelow = django_rq.get_queue('low')
queuehigh = django_rq.get_queue('low') queuehigh = django_rq.get_queue('low')
from redis import StrictRedis
from rq.exceptions import NoSuchJobError
from rq.registry import StartedJobRegistry
from rq import Queue
redis_connection = StrictRedis()
rq_registry = StartedJobRegistry(queue.name,connection=redis_connection)
from rq.job import Job
from rest_framework_swagger.views import get_swagger_view from rest_framework_swagger.views import get_swagger_view
from rest_framework.renderers import JSONRenderer from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser from rest_framework.parsers import JSONParser
@@ -155,10 +165,30 @@ from scipy.interpolate import griddata
USER_LANGUAGE = 'en-US' USER_LANGUAGE = 'en-US'
from interactiveplots import * from interactiveplots import *
from rowers.celery import result as celery_result
# Define the API documentation # Define the API documentation
schema_view = get_swagger_view(title='Rowsandall API') schema_view = get_swagger_view(title='Rowsandall API')
def get_job_result(jobid):
if settings.DEBUG:
result = celery_result.AsyncResult(jobid).result
else:
running_job_ids = rq_registry.get_job_ids()
if len(running_job_ids) and jobid in running_job_ids:
# job is running
return None
else:
# job is ready
try:
job = Job.fetch(jobid,connection=redis_connection)
result = job.result
except NoSuchJobError:
return None
return result
# Test if row data include candidates # Test if row data include candidates
def rowhascoordinates(row): def rowhascoordinates(row):
# create interactive plot # create interactive plot
@@ -3077,6 +3107,32 @@ def otwrankings_view(request,theuser=0,
delta,cpvalue,avgpower = dataprep.fetchcp(r,theworkouts) delta,cpvalue,avgpower = dataprep.fetchcp(r,theworkouts)
runningjob = 0
try:
jobid = request.session['job_id']
if jobid:
result = get_job_result(jobid)
if result:
messages.info(request,'Your calculation is ready')
runningjob = 1
request.session['job_id'] = 0
else:
runningjob = 1
messages.info(request,'Your job is still running')
except KeyError:
pass
if not runningjob:
job = dataprep.runcpupdate(
r,type='water',
startdate=startdate,
enddate=enddate
)
request.session['job_id'] = job.id
messages.info(request,'New calculation queued. Refresh page or resubmit the date form to get the result')
powerdf = pd.DataFrame({ powerdf = pd.DataFrame({
'Delta':delta, 'Delta':delta,
'CP':cpvalue, 'CP':cpvalue,
@@ -3181,6 +3237,7 @@ def otwrankings_view(request,theuser=0,
'startdate':startdate, 'startdate':startdate,
'enddate':enddate, 'enddate':enddate,
'teams':get_my_teams(request.user), 'teams':get_my_teams(request.user),
'workouttype':'water',
}) })
# Show ranking distances including predicted paces # Show ranking distances including predicted paces
@user_passes_test(ispromember,login_url="/",redirect_field_name=None) @user_passes_test(ispromember,login_url="/",redirect_field_name=None)
@@ -3299,6 +3356,32 @@ def oterankings_view(request,theuser=0,
r,theworkouts,table='cpergdata' r,theworkouts,table='cpergdata'
) )
runningjob = 0
try:
jobid = request.session['job_id']
if jobid:
result = get_job_result(jobid)
if result:
messages.info(request,'Your calculation is ready')
runningjob = 1
request.session['job_id'] = 0
else:
runningjob = 1
messages.info(request,'Your job is still running')
except KeyError:
pass
if not runningjob:
job = dataprep.runcpupdate(
r,type='rower',
startdate=startdate,
enddate=enddate
)
request.session['job_id'] = job.id
messages.info(request,'New calculation queued. Refresh page or resubmit the date form to get the result')
powerdf = pd.DataFrame({ powerdf = pd.DataFrame({
'Delta':delta, 'Delta':delta,
'CP':cpvalue, 'CP':cpvalue,
@@ -3320,11 +3403,11 @@ def oterankings_view(request,theuser=0,
div = res[1] div = res[1]
p1 = res[2] p1 = res[2]
ratio = res[3] ratio = res[3]
r.p0 = p1[0] r.ep0 = p1[0]
r.p1 = p1[1] r.ep1 = p1[1]
r.p2 = p1[2] r.ep2 = p1[2]
r.p3 = p1[3] r.ep3 = p1[3]
r.cpratio = ratio r.ecpratio = ratio
r.save() r.save()
paulslope = 1 paulslope = 1
paulintercept = 1 paulintercept = 1
@@ -3403,6 +3486,7 @@ def oterankings_view(request,theuser=0,
'startdate':startdate, 'startdate':startdate,
'enddate':enddate, 'enddate':enddate,
'teams':get_my_teams(request.user), 'teams':get_my_teams(request.user),
'workouttype':'rower',
}) })
# Reload the workout and calculate the summary from the stroke data (lapIDx) # Reload the workout and calculate the summary from the stroke data (lapIDx)
@@ -5427,7 +5511,7 @@ def workout_otwsetpower_view(request,id=0,message="",successmessage=""):
ratio=r.cpratio) ratio=r.cpratio)
else: else:
res = queuelow.enqueue(handle_otwsetpower,f1,boattype, res = queuelow.enqueue(handle_otwsetpower,f1,boattype,
weightvalue, weightvalue,
first_name,last_name,emailaddress,id, first_name,last_name,emailaddress,id,
ps=[r.p0,r.p1,r.p2,r.p3], ps=[r.p0,r.p1,r.p2,r.p3],
ratio=r.cpratio) ratio=r.cpratio)
@@ -6064,6 +6148,22 @@ def workout_comparison_view2(request,id1=0,id2=0,xparam='distance',
'promember':promember, 'promember':promember,
}) })
# Change default landing page
@login_required()
def workflow_default_view(request):
r = getrower(request.user)
if r.defaultlandingpage == 'workout_edit_view':
r.defaultlandingpage = 'workout_workflow_view'
else:
r.defaultlandingpage = 'workout_edit_view'
r.save()
url = reverse(workout_workflow_config2_view)
return HttpResponseRedirect(url)
# Workflow Configuration # Workflow Configuration
@login_required() @login_required()
def workout_workflow_config_view(request): def workout_workflow_config_view(request):
@@ -6176,7 +6276,7 @@ def workout_workflow_config2_view(request):
return render(request,tmplt, return render(request,tmplt,
{ {
'rower':r, 'rower':getrower(request.user),
'leftpanel_formset':leftpanel_formset, 'leftpanel_formset':leftpanel_formset,
'middlepanel_formset':middlepanel_formset, 'middlepanel_formset':middlepanel_formset,
'workoutid': workoutid, 'workoutid': workoutid,
@@ -6203,6 +6303,10 @@ def workout_workflow_view(request,id):
if request.user == row.user.user: if request.user == row.user.user:
mayedit=1 mayedit=1
comments = WorkoutComment.objects.filter(workout=row)
aantalcomments = len(comments)
workouttype = 'ote' workouttype = 'ote'
if row.workouttype in ('water','coastal'): if row.workouttype in ('water','coastal'):
workouttype = 'otw' workouttype = 'otw'
@@ -6264,6 +6368,7 @@ def workout_workflow_view(request,id):
'mapdiv':mapdiv, 'mapdiv':mapdiv,
'statcharts':statcharts, 'statcharts':statcharts,
'rower':r, 'rower':r,
'aantalcomments':aantalcomments,
}) })
# The famous flex chart # The famous flex chart
@@ -7560,7 +7665,9 @@ def workout_getstravaworkout_view(request,stravaid):
messages.error(request,message) messages.error(request,message)
url = reverse(workout_edit_view, r = getrower(request.user)
url = reverse(r.defaultlandingpage,
kwargs = { kwargs = {
'id':int(id), 'id':int(id),
}) })
@@ -7581,7 +7688,9 @@ def workout_getrunkeeperworkout_view(request,runkeeperid):
if message: if message:
messages.error(request,message) messages.error(request,message)
url = reverse(workout_edit_view, r = getrower(request.user)
url = reverse(r.defaultlandingpage,
kwargs = { kwargs = {
'id':int(id), 'id':int(id),
}) })
@@ -7600,7 +7709,9 @@ def workout_getunderarmourworkout_view(request,underarmourid):
if message: if message:
messages.error(request,message) messages.error(request,message)
url = reverse(workout_edit_view, r = getrower(request.user)
url = reverse(r.defaultlandingpage,
kwargs = { kwargs = {
'id':int(id), 'id':int(id),
}) })
@@ -7625,7 +7736,9 @@ def workout_getsporttracksworkout_view(request,sporttracksid):
if message: if message:
messages.error(request,message) messages.error(request,message)
url = reverse(workout_edit_view, r = getrower(request.user)
url = reverse(r.defaultlandingpage,
kwargs = { kwargs = {
'id':int(id), 'id':int(id),
}) })
@@ -7769,7 +7882,9 @@ def workout_getc2workout_view(request,c2id):
if message: if message:
messages.error(request,message) messages.error(request,message)
url = reverse(workout_edit_view, r = getrower(request.user)
url = reverse(r.defaultlandingpage,
kwargs = { kwargs = {
'id':int(id), 'id':int(id),
}) })