Merge branch 'release/v18.6.0'
This commit is contained in:
@@ -23,12 +23,12 @@ def time_in_path(df, p, maxmin='max', getall=False, name='unknown', logfile=None
|
|||||||
def f(x):
|
def f(x):
|
||||||
return coordinate_in_path(x['latitude'], x['longitude'], p)
|
return coordinate_in_path(x['latitude'], x['longitude'], p)
|
||||||
|
|
||||||
df['inpolygon'] = df.apply(f, axis=1)
|
inpolygon = df.apply(lambda row:f(row), axis=1).copy()
|
||||||
|
|
||||||
if maxmin == 'max':
|
if maxmin == 'max':
|
||||||
b = (~df['inpolygon']).shift(-1)+df['inpolygon']
|
b = (~inpolygon).shift(-1)+inpolygon
|
||||||
else: # pragma: no cover
|
else: # pragma: no cover
|
||||||
b = (~df['inpolygon']).shift(1)+df['inpolygon']
|
b = (~inpolygon).shift(1)+inpolygon
|
||||||
|
|
||||||
if len(df[b == 2]):
|
if len(df[b == 2]):
|
||||||
if logfile is not None: # pragma: no cover
|
if logfile is not None: # pragma: no cover
|
||||||
@@ -90,7 +90,7 @@ def coursetime_first(data, paths, polygons=[], logfile=None):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
entrytime, entrydistance = time_in_path(
|
entrytime, entrydistance = time_in_path(
|
||||||
data, paths[0], maxmin='max', name=polygons[0][1], logfile=logfile)
|
data, paths[0], maxmin='max', name=str(polygons[0]), logfile=logfile)
|
||||||
coursecompleted = True
|
coursecompleted = True
|
||||||
except InvalidTrajectoryError: # pragma: no cover
|
except InvalidTrajectoryError: # pragma: no cover
|
||||||
entrytime = data['time'].max()
|
entrytime = data['time'].max()
|
||||||
@@ -118,7 +118,7 @@ def coursetime_paths(data, paths, finalmaxmin='min', polygons=[], logfile=None):
|
|||||||
(
|
(
|
||||||
entrytime,
|
entrytime,
|
||||||
entrydistance
|
entrydistance
|
||||||
) = time_in_path(data, paths[0], maxmin=finalmaxmin, name=polygons[0][1], logfile=logfile)
|
) = time_in_path(data, paths[0], maxmin=finalmaxmin, name=str(polygons[0]), logfile=logfile)
|
||||||
coursecompleted = True
|
coursecompleted = True
|
||||||
except InvalidTrajectoryError: # pragma: no cover
|
except InvalidTrajectoryError: # pragma: no cover
|
||||||
entrytime = data['time'].max()
|
entrytime = data['time'].max()
|
||||||
@@ -129,7 +129,7 @@ def coursetime_paths(data, paths, finalmaxmin='min', polygons=[], logfile=None):
|
|||||||
if len(paths) > 1:
|
if len(paths) > 1:
|
||||||
try:
|
try:
|
||||||
time, dist = time_in_path(
|
time, dist = time_in_path(
|
||||||
data, paths[0], name=polygons[0][1], logfile=logfile)
|
data, paths[0], name=str(polygons[0]), logfile=logfile)
|
||||||
data2 = data[data['time'] > time].copy()
|
data2 = data[data['time'] > time].copy()
|
||||||
data2['time'] = data2['time'].apply(lambda x: x-time)
|
data2['time'] = data2['time'].apply(lambda x: x-time)
|
||||||
data2['cum_dist'] = data2['cum_dist'].apply(lambda x: x-dist)
|
data2['cum_dist'] = data2['cum_dist'].apply(lambda x: x-dist)
|
||||||
|
|||||||
@@ -16,3 +16,7 @@ database_url = 'mysql://{user}:{password}@{host}:{port}/{database_name}'.format(
|
|||||||
|
|
||||||
if settings.DEBUG or user == '':
|
if settings.DEBUG or user == '':
|
||||||
database_url = 'sqlite:///db.sqlite3'
|
database_url = 'sqlite:///db.sqlite3'
|
||||||
|
|
||||||
|
#database_name_dev = DEV_DATABASES['default']['NAME']
|
||||||
|
|
||||||
|
database_url_debug = database_url
|
||||||
|
|||||||
+417
-2075
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -148,6 +148,26 @@ class InstantPlanSelectForm(forms.Form):
|
|||||||
self.fields['enddate'].initial = timezone.now(
|
self.fields['enddate'].initial = timezone.now(
|
||||||
)+datetime.timedelta(days=instantplan.duration)
|
)+datetime.timedelta(days=instantplan.duration)
|
||||||
|
|
||||||
|
# Instroke Metrics interactive chart form
|
||||||
|
class InstrokeForm(forms.Form):
|
||||||
|
metric = forms.ChoiceField(label='metric',choices=(('a','a'),('b','b')))
|
||||||
|
individual_curves = forms.BooleanField(label='individual curves',initial=False,
|
||||||
|
required=False)
|
||||||
|
spm_min = forms.IntegerField(initial=15,label='SPM Min',widget=HiddenInput)
|
||||||
|
spm_max = forms.IntegerField(initial=45,label='SPM Max',widget=HiddenInput)
|
||||||
|
activeminutesmin = forms.IntegerField(
|
||||||
|
required=False, initial=0, widget=forms.HiddenInput())
|
||||||
|
activeminutesmax = forms.IntegerField(
|
||||||
|
required=False, initial=0, widget=forms.HiddenInput())
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs): # pragma: no cover
|
||||||
|
choices = kwargs.pop('choices', [])
|
||||||
|
super(InstrokeForm, self).__init__(*args, **kwargs)
|
||||||
|
if len(choices) > 0:
|
||||||
|
choices = [(choice, choice) for choice in choices]
|
||||||
|
self.fields['metric'].choices = choices
|
||||||
|
self.fields['metric'].initial = choices[0]
|
||||||
|
|
||||||
|
|
||||||
# Video Analysis creation form
|
# Video Analysis creation form
|
||||||
class VideoAnalysisCreateForm(forms.Form):
|
class VideoAnalysisCreateForm(forms.Form):
|
||||||
|
|||||||
@@ -4070,6 +4070,133 @@ def interactive_streamchart(id=0, promember=0):
|
|||||||
|
|
||||||
return [script, div]
|
return [script, div]
|
||||||
|
|
||||||
|
def instroke_interactive_chart(df,metric, workout, spm_min, spm_max,
|
||||||
|
activeminutesmin, activeminutesmax,
|
||||||
|
individual_curves):
|
||||||
|
|
||||||
|
|
||||||
|
df_pos = (df+abs(df))/2.
|
||||||
|
df_min = -(-df+abs(-df))/2.
|
||||||
|
|
||||||
|
mean_vals = df.median()
|
||||||
|
q75 = df_pos.quantile(q=0.75).replace(0,np.nan)
|
||||||
|
q25 = df_pos.quantile(q=0.25).replace(0,np.nan)
|
||||||
|
q75min = df_min.quantile(q=0.75).replace(0,np.nan)
|
||||||
|
q25min = df_min.quantile(q=0.25).replace(0,np.nan)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
xvals = np.arange(len(mean_vals))
|
||||||
|
|
||||||
|
df_plot = pd.DataFrame({
|
||||||
|
'x':xvals,
|
||||||
|
'median':mean_vals,
|
||||||
|
'high':q75,
|
||||||
|
'low':q75min,
|
||||||
|
'high 2':q25min,
|
||||||
|
'low 2': q25,
|
||||||
|
})
|
||||||
|
|
||||||
|
df_plot['high'].update(df_plot.pop('high 2'))
|
||||||
|
df_plot['low'].update(df_plot.pop('low 2'))
|
||||||
|
df_plot.interpolate(axis=1,inplace=True)
|
||||||
|
|
||||||
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,crosshair'
|
||||||
|
plot = Figure(plot_width=920,tools=TOOLS,
|
||||||
|
toolbar_location='above',
|
||||||
|
toolbar_sticky=False)
|
||||||
|
|
||||||
|
plot.sizing_mode = 'stretch_both'
|
||||||
|
|
||||||
|
plot.title.text = str(workout) + ' - ' + metric
|
||||||
|
|
||||||
|
# add watermark
|
||||||
|
watermarkurl = "/static/img/logo7.png"
|
||||||
|
|
||||||
|
watermarkrange = Range1d(start=0, end=1)
|
||||||
|
watermarkalpha = 0.6
|
||||||
|
watermarkx = 0.99
|
||||||
|
watermarky = 0.01
|
||||||
|
watermarkw = 184
|
||||||
|
watermarkh = 35
|
||||||
|
watermarkanchor = 'bottom_right'
|
||||||
|
plot.extra_y_ranges = {"watermark": watermarkrange}
|
||||||
|
plot.extra_x_ranges = {"watermark": watermarkrange}
|
||||||
|
|
||||||
|
plot.image_url([watermarkurl], watermarkx, watermarky,
|
||||||
|
watermarkw, watermarkh,
|
||||||
|
global_alpha=watermarkalpha,
|
||||||
|
w_units='screen',
|
||||||
|
h_units='screen',
|
||||||
|
anchor=watermarkanchor,
|
||||||
|
dilate=True,
|
||||||
|
x_range_name="watermark",
|
||||||
|
y_range_name="watermark",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
source = ColumnDataSource(
|
||||||
|
df_plot
|
||||||
|
)
|
||||||
|
|
||||||
|
TIPS = OrderedDict([
|
||||||
|
('x','@x'),
|
||||||
|
('median','@median'),
|
||||||
|
('high','@high'),
|
||||||
|
('low','@low')
|
||||||
|
])
|
||||||
|
|
||||||
|
hover = plot.select(type=HoverTool)
|
||||||
|
hover.tooltips = TIPS
|
||||||
|
|
||||||
|
s = 'SPM: {spm_min} - {spm_max}'.format(
|
||||||
|
spm_min = spm_min,
|
||||||
|
spm_max = spm_max,
|
||||||
|
)
|
||||||
|
|
||||||
|
label = Label(x=50, y=450, x_units='screen',y_units='screen',
|
||||||
|
text=s,
|
||||||
|
background_fill_alpha=0.7,
|
||||||
|
background_fill_color='white',
|
||||||
|
text_color='black',
|
||||||
|
)
|
||||||
|
|
||||||
|
s2 = 'Time: {activeminutesmin} - {activeminutesmax} min'.format(
|
||||||
|
activeminutesmin=activeminutesmin,
|
||||||
|
activeminutesmax=activeminutesmax
|
||||||
|
)
|
||||||
|
|
||||||
|
label2 = Label(x=50,y=400, x_units='screen', y_units='screen',
|
||||||
|
text=s2,
|
||||||
|
background_fill_alpha=0.7,
|
||||||
|
background_fill_color='white',
|
||||||
|
text_color='black',
|
||||||
|
)
|
||||||
|
|
||||||
|
plot.add_layout(label)
|
||||||
|
plot.add_layout(label2)
|
||||||
|
|
||||||
|
if individual_curves:
|
||||||
|
for index,row in df.iterrows():
|
||||||
|
plot.line(xvals,row,color='lightgray',line_width=1)
|
||||||
|
else:
|
||||||
|
plot.varea('x', y1='high', y2='low',source=source,fill_color="lightgray",alpha=0.5)
|
||||||
|
|
||||||
|
plot.line('x','median',source=source,legend_label='median',color="black",
|
||||||
|
line_width=3)
|
||||||
|
|
||||||
|
medrange = mean_vals.max()-mean_vals.min()
|
||||||
|
yrange = Range1d(start=mean_vals.min()-0.2*medrange,
|
||||||
|
end=mean_vals.max()+0.2*medrange,)
|
||||||
|
plot.y_range = yrange
|
||||||
|
|
||||||
|
plot.add_tools(HoverTool(tooltips=TIPS))
|
||||||
|
|
||||||
|
script, div = components(plot)
|
||||||
|
|
||||||
|
return (script, div)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def interactive_chart(id=0, promember=0, intervaldata={}):
|
def interactive_chart(id=0, promember=0, intervaldata={}):
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from django.shortcuts import redirect
|
|||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from rowers.mytypes import otwtypes
|
from rowers.mytypes import otwtypes
|
||||||
from rowers.tasks import handle_sendemail_expired
|
#from rowers.tasks import handle_sendemail_expired
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from rowers.models import Workout, PowerTimeFitnessMetric, Rower, PaidPlan
|
from rowers.models import Workout, PowerTimeFitnessMetric, Rower, PaidPlan
|
||||||
import datetime
|
import datetime
|
||||||
@@ -99,6 +99,7 @@ class RowerPlanMiddleWare(object):
|
|||||||
# remove from Free Coach groups
|
# remove from Free Coach groups
|
||||||
|
|
||||||
# send email
|
# send email
|
||||||
|
from rowers.tasks import handle_sendemail_expired
|
||||||
_ = myqueue(queue,
|
_ = myqueue(queue,
|
||||||
handle_sendemail_expired,
|
handle_sendemail_expired,
|
||||||
r.user.email,
|
r.user.email,
|
||||||
|
|||||||
+2
-1
@@ -553,8 +553,9 @@ def polygon_coord_center(polygon):
|
|||||||
return latitudes.mean(), longitudes.mean()
|
return latitudes.mean(), longitudes.mean()
|
||||||
|
|
||||||
|
|
||||||
def polygon_to_path(polygon):
|
def polygon_to_path(polygon, debug=False):
|
||||||
points = GeoPoint.objects.filter(polygon=polygon).order_by("order_in_poly")
|
points = GeoPoint.objects.filter(polygon=polygon).order_by("order_in_poly")
|
||||||
|
|
||||||
s = []
|
s = []
|
||||||
for point in points:
|
for point in points:
|
||||||
s.append([point.latitude, point.longitude])
|
s.append([point.latitude, point.longitude])
|
||||||
|
|||||||
+169
-217
@@ -1,3 +1,23 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
|
||||||
|
from YamJam import yamjam
|
||||||
|
CFG = yamjam()['rowsandallapp']
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE",CFG['settings_name'])
|
||||||
|
except KeyError: # pragma: no cover
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE","rowsandall_app.settings")
|
||||||
|
|
||||||
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
|
application = get_wsgi_application()
|
||||||
|
from rowers.models import (
|
||||||
|
Workout, GeoPolygon, GeoPoint, GeoCourse,
|
||||||
|
VirtualRaceResult, CourseTestResult, Rower,
|
||||||
|
GraphImage,
|
||||||
|
)
|
||||||
|
|
||||||
import math
|
import math
|
||||||
from rowers.courseutils import (
|
from rowers.courseutils import (
|
||||||
coursetime_paths, coursetime_first, time_in_path,
|
coursetime_paths, coursetime_first, time_in_path,
|
||||||
@@ -16,8 +36,7 @@ import rowers.longtask as longtask
|
|||||||
import requests
|
import requests
|
||||||
import rowers.datautils as datautils
|
import rowers.datautils as datautils
|
||||||
|
|
||||||
""" Background tasks done by Celery (develop) or QR (production) """
|
""" Background tasks done by QR (production) """
|
||||||
import os
|
|
||||||
import time
|
import time
|
||||||
import gc
|
import gc
|
||||||
import gzip
|
import gzip
|
||||||
@@ -27,6 +46,7 @@ import re
|
|||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import traceback
|
import traceback
|
||||||
|
from time import strftime
|
||||||
|
|
||||||
from scipy import optimize
|
from scipy import optimize
|
||||||
from scipy.signal import savgol_filter
|
from scipy.signal import savgol_filter
|
||||||
@@ -36,8 +56,8 @@ import rowingdata
|
|||||||
from rowingdata import make_cumvalues
|
from rowingdata import make_cumvalues
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from rowingdata import rowingdata as rdata
|
from rowingdata import rowingdata as rdata
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from sqlalchemy import create_engine
|
|
||||||
|
|
||||||
from rowers.celery import app
|
from rowers.celery import app
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
@@ -61,13 +81,15 @@ import rowers.otw_power_calculator_pb2_grpc as calculator_pb2_grpc
|
|||||||
import rowers.rowing_workout_metrics_pb2 as metrics_pb2
|
import rowers.rowing_workout_metrics_pb2 as metrics_pb2
|
||||||
import rowers.rowing_workout_metrics_pb2_grpc as metrics_pb2_grpc
|
import rowers.rowing_workout_metrics_pb2_grpc as metrics_pb2_grpc
|
||||||
|
|
||||||
from rowsandall_app.settings import SITE_URL
|
from django.conf import settings
|
||||||
from rowsandall_app.settings_dev import SITE_URL as SITE_URL_DEV
|
SITE_URL = settings.SITE_URL
|
||||||
from rowsandall_app.settings import PROGRESS_CACHE_SECRET
|
SITE_URL_DEV = settings.SITE_URL
|
||||||
from rowsandall_app.settings import SETTINGS_NAME
|
PROGRESS_CACHE_SECRET = settings.PROGRESS_CACHE_SECRET
|
||||||
from rowsandall_app.settings import workoutemailbox
|
SETTINGS_NAME = settings.SETTINGS_NAME
|
||||||
from rowsandall_app.settings import UPLOAD_SERVICE_SECRET, UPLOAD_SERVICE_URL
|
|
||||||
from rowsandall_app.settings import NK_API_LOCATION
|
UPLOAD_SERVICE_URL = settings.UPLOAD_SERVICE_URL
|
||||||
|
UPLOAD_SERVICE_SECRET = settings.UPLOAD_SERVICE_SECRET
|
||||||
|
NK_API_LOCATION = settings.NK_API_LOCATION
|
||||||
|
|
||||||
from requests_oauthlib import OAuth1, OAuth1Session
|
from requests_oauthlib import OAuth1, OAuth1Session
|
||||||
|
|
||||||
@@ -83,16 +105,19 @@ from rowers.emails import htmlstrip
|
|||||||
from rowers import mytypes
|
from rowers import mytypes
|
||||||
|
|
||||||
|
|
||||||
from rowers.dataprepnodjango import (
|
from rowers.dataroutines import (
|
||||||
getsmallrowdata_db, updatecpdata_sql, update_c2id_sql,
|
getsmallrowdata_db, updatecpdata_sql, update_c2id_sql,
|
||||||
update_workout_field_sql,
|
#update_workout_field_sql,
|
||||||
update_agegroup_db, update_strokedata,
|
update_agegroup_db, update_strokedata,
|
||||||
add_c2_stroke_data_db, totaltime_sec_to_string,
|
add_c2_stroke_data_db, totaltime_sec_to_string,
|
||||||
create_c2_stroke_data_db, update_empower,
|
create_c2_stroke_data_db, update_empower,
|
||||||
database_url_debug, database_url, dataprep,
|
# database_url_debug,
|
||||||
|
database_url, dataprep,
|
||||||
# create_strava_stroke_data_db
|
# create_strava_stroke_data_db
|
||||||
)
|
)
|
||||||
|
|
||||||
|
database_url_debug = database_url
|
||||||
|
|
||||||
|
|
||||||
from rowers.opaque import encoder
|
from rowers.opaque import encoder
|
||||||
|
|
||||||
@@ -274,10 +299,48 @@ def summaryfromsplitdata(splitdata, data, filename, sep='|', workouttype='rower'
|
|||||||
|
|
||||||
return sums, sa, results
|
return sums, sa, results
|
||||||
|
|
||||||
|
@app.task
|
||||||
|
def instroke_static(w, metric, debug=False, **kwargs):
|
||||||
|
f1 = w.csvfilename[6:-4]
|
||||||
|
rowdata = rdata(csvfile=w.csvfilename)
|
||||||
|
|
||||||
|
|
||||||
|
timestr = strftime("%Y%m%d-%H%M%S")
|
||||||
|
imagename = f1+timestr+'.png'
|
||||||
|
fullpathimagename = 'static/plots/'+imagename
|
||||||
|
r = w.user
|
||||||
|
fig1 = rowdata.get_plot_instroke(metric)
|
||||||
|
canvas = FigureCanvas(fig1)
|
||||||
|
canvas.print_figure('static/plots/'+imagename)
|
||||||
|
plt.close(fig1)
|
||||||
|
fig1.clf()
|
||||||
|
|
||||||
|
try:
|
||||||
|
width, height = Image.open(fullpathimagename).size
|
||||||
|
except:
|
||||||
|
width = 1200
|
||||||
|
height = 600
|
||||||
|
|
||||||
|
imgs = GraphImage.objects.filter(workout=w)
|
||||||
|
if imgs.count() < 7:
|
||||||
|
i = GraphImage(workout=w,
|
||||||
|
creationdatetime=timezone.now(),
|
||||||
|
filename=fullpathimagename,
|
||||||
|
width=width, height=height)
|
||||||
|
|
||||||
|
i.save()
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
def handle_request_post(url, data, debug=False, **kwargs): # pragma: no cover
|
def handle_request_post(url, data, debug=False, **kwargs): # pragma: no cover
|
||||||
response = requests.post(url, data)
|
if 'localhost' in url:
|
||||||
|
url = 'http'+url[5:]
|
||||||
|
response = requests.post(url, data, verify=False)
|
||||||
dologging('upload_api.log', data)
|
dologging('upload_api.log', data)
|
||||||
dologging('upload_api.log', response.status_code)
|
dologging('upload_api.log', response.status_code)
|
||||||
return response.status_code
|
return response.status_code
|
||||||
@@ -297,10 +360,11 @@ def handle_c2_sync(workoutid, url, headers, data, debug=False, **kwargs):
|
|||||||
s = response.json()
|
s = response.json()
|
||||||
c2id = s['data']['id']
|
c2id = s['data']['id']
|
||||||
|
|
||||||
res = update_workout_field_sql(
|
workout = Workout.objects.get(id=workoutid)
|
||||||
workoutid, 'uploadedtoc2', c2id, debug=debug)
|
workout.uploadedtoc2 = c2id
|
||||||
|
workout.save()
|
||||||
|
|
||||||
return res
|
return 1
|
||||||
|
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
@@ -316,8 +380,10 @@ def handle_sporttracks_sync(workoutid, url, headers, data, debug=False, **kwargs
|
|||||||
|
|
||||||
id = int(m)
|
id = int(m)
|
||||||
|
|
||||||
_ = update_workout_field_sql(
|
workout = Workout.objects.get(id=workoutid)
|
||||||
workoutid, 'uploadedtosporttracks', id, debug=debug)
|
workout.uploadedtosporttracks = id
|
||||||
|
workout.save()
|
||||||
|
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
@@ -338,7 +404,7 @@ def handle_strava_sync(stravatoken, workoutid, filename, name, activity_type, de
|
|||||||
tb = traceback.format_exc()
|
tb = traceback.format_exc()
|
||||||
dologging('strava_fail.log', tb)
|
dologging('strava_fail.log', tb)
|
||||||
failed = True
|
failed = True
|
||||||
except stravalib.exc.TimeoutExceeded:
|
except stravalib.exc.TimeoutExceeded: # pragma: no cover
|
||||||
dologging('strava_fail.log', 'Strava upload failed for Workout {id} TimeOutExceeded'.format(
|
dologging('strava_fail.log', 'Strava upload failed for Workout {id} TimeOutExceeded'.format(
|
||||||
id=workoutid))
|
id=workoutid))
|
||||||
tb = traceback.format_exc()
|
tb = traceback.format_exc()
|
||||||
@@ -378,8 +444,9 @@ def handle_strava_sync(stravatoken, workoutid, filename, name, activity_type, de
|
|||||||
failed = True
|
failed = True
|
||||||
|
|
||||||
if not failed:
|
if not failed:
|
||||||
_ = update_workout_field_sql(
|
workout = Workout.objects.get(id=workoutid)
|
||||||
workoutid, 'uploadedtostrava', res.id, debug=debug)
|
workout.uploadedtostrava = res.id
|
||||||
|
workout.save()
|
||||||
try:
|
try:
|
||||||
act = client.update_activity(res.id, activity_type=activity_type,
|
act = client.update_activity(res.id, activity_type=activity_type,
|
||||||
description=description, device_name='Rowsandall.com')
|
description=description, device_name='Rowsandall.com')
|
||||||
@@ -507,37 +574,14 @@ def getagegrouprecord(age, sex='male', weightcategory='hwt',
|
|||||||
return power
|
return power
|
||||||
|
|
||||||
|
|
||||||
def polygon_to_path(polygon, debug=True):
|
from rowers.models import polygon_to_path
|
||||||
pid = polygon[0]
|
|
||||||
query = "SELECT id, latitude, longitude FROM rowers_geopoint WHERE polygon_id = {pid}"\
|
|
||||||
" ORDER BY order_in_poly ASC".format(
|
|
||||||
pid=pid)
|
|
||||||
if debug:
|
|
||||||
engine = create_engine(database_url_debug, echo=False)
|
|
||||||
else: # pragma: no cover
|
|
||||||
engine = create_engine(database_url, echo=False)
|
|
||||||
|
|
||||||
with engine.connect() as conn, conn.begin():
|
|
||||||
result = conn.execute(query)
|
|
||||||
points = result.fetchall()
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
engine.dispose()
|
|
||||||
s = []
|
|
||||||
|
|
||||||
for point in points:
|
|
||||||
s.append([point[1], point[2]])
|
|
||||||
|
|
||||||
p = path.Path(s[:-1])
|
|
||||||
|
|
||||||
return p
|
|
||||||
|
|
||||||
|
|
||||||
@app.task(bind=True)
|
@app.task(bind=True)
|
||||||
def handle_check_race_course(self,
|
def handle_check_race_course(self,
|
||||||
f1, workoutid, courseid,
|
f1, workoutid, courseid,
|
||||||
recordid, useremail, userfirstname,
|
recordid, useremail, userfirstname,
|
||||||
**kwargs): # pragma: no cover
|
**kwargs):
|
||||||
|
|
||||||
logfile = 'courselog_{workoutid}_{courseid}.log'.format(
|
logfile = 'courselog_{workoutid}_{courseid}.log'.format(
|
||||||
workoutid=workoutid, courseid=courseid)
|
workoutid=workoutid, courseid=courseid)
|
||||||
@@ -567,11 +611,11 @@ def handle_check_race_course(self,
|
|||||||
mode = kwargs['mode']
|
mode = kwargs['mode']
|
||||||
|
|
||||||
summary = False
|
summary = False
|
||||||
if 'summary' in kwargs:
|
if 'summary' in kwargs: # pragma: no cover
|
||||||
summary = kwargs['summary']
|
summary = kwargs['summary']
|
||||||
|
|
||||||
successemail = False
|
successemail = False
|
||||||
if 'successemail' in kwargs:
|
if 'successemail' in kwargs: # pragma: no cover
|
||||||
successemail = kwargs['successemail']
|
successemail = kwargs['successemail']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -606,35 +650,21 @@ def handle_check_race_course(self,
|
|||||||
|
|
||||||
rowdata.fillna(method='backfill', inplace=True)
|
rowdata.fillna(method='backfill', inplace=True)
|
||||||
|
|
||||||
rowdata['time'] = rowdata['time']-rowdata.loc[0, 'time']
|
rowdata.loc[:, 'time'] = rowdata.loc[:, 'time'].copy()-rowdata.loc[0, 'time']
|
||||||
rowdata = rowdata[rowdata['time'] > splitsecond]
|
rowdata = rowdata.copy()[rowdata['time'] > splitsecond]
|
||||||
# we may want to expand the time (interpolate)
|
# we may want to expand the time (interpolate)
|
||||||
rowdata['dt'] = rowdata['time'].apply(
|
|
||||||
|
rowdata.loc[:,'dt'] = rowdata['time'].apply(
|
||||||
lambda x: safetimedelta(x)
|
lambda x: safetimedelta(x)
|
||||||
)
|
).values
|
||||||
|
|
||||||
|
|
||||||
rowdata = rowdata.resample('100ms', on='dt').mean()
|
rowdata = rowdata.resample('100ms', on='dt').mean()
|
||||||
rowdata = rowdata.interpolate()
|
rowdata = rowdata.interpolate()
|
||||||
|
|
||||||
# initiate database engine
|
course = GeoCourse.objects.get(id=courseid)
|
||||||
|
polygons = course.polygons.all()
|
||||||
|
|
||||||
if debug: # pragma: no cover
|
|
||||||
engine = create_engine(database_url_debug, echo=False)
|
|
||||||
else:
|
|
||||||
engine = create_engine(database_url, echo=False)
|
|
||||||
|
|
||||||
# get polygons
|
|
||||||
query = "SELECT id,name FROM rowers_geopolygon WHERE course_id = {courseid} ORDER BY order_in_course ASC".format(
|
|
||||||
courseid=courseid
|
|
||||||
)
|
|
||||||
|
|
||||||
with engine.connect() as conn, conn.begin():
|
|
||||||
result = conn.execute(query)
|
|
||||||
polygons = result.fetchall()
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
engine.dispose()
|
|
||||||
|
|
||||||
paths = []
|
paths = []
|
||||||
for polygon in polygons:
|
for polygon in polygons:
|
||||||
@@ -649,7 +679,7 @@ def handle_check_race_course(self,
|
|||||||
try:
|
try:
|
||||||
entrytimes, entrydistances = time_in_path(rowdata, paths[0], maxmin='max', getall=True,
|
entrytimes, entrydistances = time_in_path(rowdata, paths[0], maxmin='max', getall=True,
|
||||||
name=polygons[0].name, logfile=logfile)
|
name=polygons[0].name, logfile=logfile)
|
||||||
except AttributeError: # for testing
|
except AttributeError: # pragma: no cover
|
||||||
entrytimes, entrydistances = time_in_path(rowdata, paths[0], maxmin='max', getall=True,
|
entrytimes, entrydistances = time_in_path(rowdata, paths[0], maxmin='max', getall=True,
|
||||||
name='Start', logfile=logfile)
|
name='Start', logfile=logfile)
|
||||||
with open(logfile, 'ab') as f:
|
with open(logfile, 'ab') as f:
|
||||||
@@ -718,7 +748,8 @@ def handle_check_race_course(self,
|
|||||||
'endsecond': endseconds,
|
'endsecond': endseconds,
|
||||||
})
|
})
|
||||||
|
|
||||||
records = records[records['coursecompleted'] is True]
|
#records = records[records['coursecompleted'] is True]
|
||||||
|
records = records.loc[records['coursecompleted'], : ]
|
||||||
|
|
||||||
if len(records):
|
if len(records):
|
||||||
coursecompleted = True
|
coursecompleted = True
|
||||||
@@ -736,35 +767,29 @@ def handle_check_race_course(self,
|
|||||||
coursedistance = coursemeters
|
coursedistance = coursemeters
|
||||||
velo = coursedistance/coursetimeseconds
|
velo = coursedistance/coursetimeseconds
|
||||||
points = 100*(2.-referencespeed/velo)
|
points = 100*(2.-referencespeed/velo)
|
||||||
query = 'UPDATE rowers_virtualraceresult SET coursecompleted = 1,'\
|
|
||||||
' duration = "{duration}", distance = {distance},'\
|
|
||||||
' workoutid = {workoutid}, startsecond = {startsecond},'\
|
|
||||||
' endsecond = {endsecond}, points={points} WHERE id={recordid}'.format(
|
|
||||||
recordid=recordid,
|
|
||||||
duration=totaltime_sec_to_string(coursetimeseconds),
|
|
||||||
distance=int(coursemeters),
|
|
||||||
points=points,
|
|
||||||
workoutid=workoutid,
|
|
||||||
startsecond=startsecond,
|
|
||||||
endsecond=endsecond,)
|
|
||||||
|
|
||||||
if mode == 'coursetest':
|
if mode != 'coursetest':
|
||||||
query = 'UPDATE rowers_coursetestresult SET coursecompleted = 1,'\
|
record = VirtualRaceResult.objects.get(id=recordid)
|
||||||
' duration = "{duration}", distance = {distance},'\
|
record.duration = totaltime_sec_to_string(coursetimeseconds)
|
||||||
' workoutid = {workoutid}, startsecond = {startsecond},'\
|
record.distance=int(coursemeters)
|
||||||
' endsecond = {endsecond}, points={points} WHERE id={recordid}'.format(
|
record.points = points
|
||||||
recordid=recordid,
|
record.startsecond = startsecond
|
||||||
duration=totaltime_sec_to_string(coursetimeseconds),
|
record.endsecond = endsecond
|
||||||
distance=int(coursemeters),
|
record.workoutid = workoutid
|
||||||
points=points,
|
record.coursecompleted = 1
|
||||||
workoutid=workoutid,
|
record.save()
|
||||||
startsecond=startsecond,
|
|
||||||
endsecond=endsecond,)
|
|
||||||
|
|
||||||
with engine.connect() as conn, conn.begin():
|
else: # pragma: no cover
|
||||||
result = conn.execute(query)
|
record = CourseTestResult.objects.get(id=recordid)
|
||||||
|
record.duration = totaltime_sec_to_string(coursetimeseconds)
|
||||||
|
record.distance = int(coursemeters)
|
||||||
|
record.workoutid = workoutid
|
||||||
|
record.startsecond = startsecond
|
||||||
|
record.endsecond = endsecond
|
||||||
|
record.points = points
|
||||||
|
record.save()
|
||||||
|
|
||||||
if summary:
|
if summary: # pragma: no cover
|
||||||
try:
|
try:
|
||||||
row = rdata(csvfile=f1)
|
row = rdata(csvfile=f1)
|
||||||
except IOError: # pragma: no cover
|
except IOError: # pragma: no cover
|
||||||
@@ -784,17 +809,12 @@ def handle_check_race_course(self,
|
|||||||
|
|
||||||
summary = row.allstats()
|
summary = row.allstats()
|
||||||
row.write_csv(f1, gzip=True)
|
row.write_csv(f1, gzip=True)
|
||||||
|
workout = Workout.objects.get(id=workoutid)
|
||||||
|
workout.summary = summary
|
||||||
|
workout.save()
|
||||||
|
|
||||||
query = "UPDATE `rowers_workout` SET `summary` = '%s' WHERE `id` = %s" % (
|
|
||||||
summary, workoutid)
|
|
||||||
|
|
||||||
with engine.connect() as conn, conn.begin():
|
if successemail: # pragma: no cover
|
||||||
result = conn.execute(query)
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
engine.dispose()
|
|
||||||
|
|
||||||
if successemail:
|
|
||||||
handle_sendemail_coursesucceed(
|
handle_sendemail_coursesucceed(
|
||||||
useremail, userfirstname, logfile, workoutid
|
useremail, userfirstname, logfile, workoutid
|
||||||
)
|
)
|
||||||
@@ -804,36 +824,26 @@ def handle_check_race_course(self,
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
else: # pragma: no cover
|
else: # pragma: no cover
|
||||||
query = 'UPDATE rowers_virtualraceresult SET coursecompleted = 0,'\
|
record = VirtualRaceResult.object.get(id=recordid)
|
||||||
' duration = "{duration}", distance = {distance},'\
|
record.duration = totaltime_sec_to_string(0)
|
||||||
' workoutid = {workoutid}, startsecond = {startsecond},'\
|
record.distance = 0
|
||||||
' endsecond = {endsecond}, points={points} WHERE id={recordid}'.format(
|
record.workoutid = workoutid
|
||||||
recordid=recordid,
|
record.startsecond = startsecond
|
||||||
duration=totaltime_sec_to_string(0),
|
record.endsecond = endsecond
|
||||||
distance=0,
|
record.points = 0
|
||||||
points=0.0,
|
record.save()
|
||||||
workoutid=workoutid,
|
|
||||||
startsecond=startsecond,
|
|
||||||
endsecond=endsecond,)
|
|
||||||
|
|
||||||
if mode == 'coursetest':
|
if mode == 'coursetest':
|
||||||
query = 'UPDATE rowers_coursetestresult SET coursecompleted = 0,'\
|
record = CourseTestResult.objects.get(id=recordid)
|
||||||
' duration = "{duration}", distance = {distance}, workoutid = {workoutid}'\
|
record.duration = totaltime_sec_to_string(0)
|
||||||
', startsecond = {startsecond}, endsecond = {endsecond}'\
|
record.distance = 0
|
||||||
', points={points} WHERE id={recordid}'.format(
|
record.workoutid = workoutid
|
||||||
recordid=recordid,
|
record.startsecond = startsecond
|
||||||
duration=totaltime_sec_to_string(0),
|
record.endsecond = endsecond
|
||||||
distance=0,
|
record.points = 0
|
||||||
points=0,
|
record.save()
|
||||||
workoutid=workoutid,
|
|
||||||
startsecond=startsecond,
|
|
||||||
endsecond=endsecond,)
|
|
||||||
|
|
||||||
with engine.connect() as conn, conn.begin():
|
|
||||||
result = conn.execute(query)
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
engine.dispose()
|
|
||||||
|
|
||||||
# add times for all gates to log file
|
# add times for all gates to log file
|
||||||
with open(logfile, 'ab') as f:
|
with open(logfile, 'ab') as f:
|
||||||
@@ -1086,10 +1096,7 @@ def handle_calctrimp(id,
|
|||||||
hrmax,
|
hrmax,
|
||||||
hrmin,
|
hrmin,
|
||||||
debug=False, **kwargs):
|
debug=False, **kwargs):
|
||||||
if debug: # pragma: no cover
|
|
||||||
engine = create_engine(database_url_debug, echo=False)
|
|
||||||
else:
|
|
||||||
engine = create_engine(database_url, echo=False)
|
|
||||||
|
|
||||||
tss = 0
|
tss = 0
|
||||||
normp = 0
|
normp = 0
|
||||||
@@ -1178,21 +1185,14 @@ def handle_calctrimp(id,
|
|||||||
if hrtss > 1000: # pragma: no cover
|
if hrtss > 1000: # pragma: no cover
|
||||||
hrtss = 0
|
hrtss = 0
|
||||||
|
|
||||||
query = 'UPDATE rowers_workout SET rscore = {tss},'\
|
workout = Workout.objects.get(id=id)
|
||||||
' normp = {normp}, trimp={trimp}, hrtss={hrtss},'\
|
workout.tss = int(tss)
|
||||||
' normv={normv}, normw={normw} WHERE id={id}'.format(
|
workout.normp = int(normp)
|
||||||
tss=int(tss),
|
workout.trimp = int(trimp)
|
||||||
normp=int(normp),
|
workout.hrtss = int(hrtss)
|
||||||
trimp=int(trimp),
|
workout.normv = normv
|
||||||
hrtss=int(hrtss),
|
workout.normw = normw
|
||||||
normv=normv,
|
workout.save()
|
||||||
normw=normw,
|
|
||||||
id=id,)
|
|
||||||
|
|
||||||
with engine.connect() as conn, conn.begin():
|
|
||||||
_ = conn.execute(query)
|
|
||||||
conn.close()
|
|
||||||
engine.dispose()
|
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
@@ -1974,7 +1974,7 @@ def handle_sendemail_ical(first_name, last_name, email, url, icsfile, **kwargs):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
os.remove(icsfile)
|
os.remove(icsfile)
|
||||||
except:
|
except: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
@@ -2834,23 +2834,13 @@ def handle_update_wps(rid, types, ids, mode, debug=False, **kwargs):
|
|||||||
except ValueError: # pragma: no cover
|
except ValueError: # pragma: no cover
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
rower = Rower.objects.get(id=rid)
|
||||||
if mode == 'water':
|
if mode == 'water':
|
||||||
query = "UPDATE `rowers_rower` SET `median_wps` = '%s' WHERE `id` = '%s'" % (
|
rower.median_wps = wps_median
|
||||||
wps_median, rid)
|
|
||||||
else:
|
else:
|
||||||
query = "UPDATE `rowers_rower` SET `median_wps_erg` = '%s' WHERE `id` = '%s'" % (
|
rower.median_wps_erg = wps_median
|
||||||
wps_median, rid)
|
|
||||||
|
|
||||||
if debug: # pragma: no cover
|
rower.save()
|
||||||
engine = create_engine(database_url_debug, echo=False)
|
|
||||||
else:
|
|
||||||
engine = create_engine(database_url, echo=False)
|
|
||||||
|
|
||||||
with engine.connect() as conn, conn.begin():
|
|
||||||
_ = conn.execute(query)
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
engine.dispose()
|
|
||||||
|
|
||||||
return wps_median
|
return wps_median
|
||||||
|
|
||||||
@@ -2999,23 +2989,8 @@ def handle_nk_async_workout(alldata, userid, nktoken, nkid, delaysec, defaulttim
|
|||||||
workoutid, error = add_workout_from_data(userid, nkid, data, df)
|
workoutid, error = add_workout_from_data(userid, nkid, data, df)
|
||||||
|
|
||||||
# dologging('nklog.log','NK Workout ID {id}'.format(id=workoutid))
|
# dologging('nklog.log','NK Workout ID {id}'.format(id=workoutid))
|
||||||
|
workout = Workout.objects.get(id=workoutid)
|
||||||
if debug: # pragma: no cover
|
newnkid = workout.uploadedtonk
|
||||||
engine = create_engine(database_url_debug, echo=False)
|
|
||||||
else:
|
|
||||||
engine = create_engine(database_url, echo=False)
|
|
||||||
|
|
||||||
query = 'SELECT uploadedtonk from rowers_workout WHERE id ={workoutid}'.format(
|
|
||||||
workoutid=workoutid)
|
|
||||||
|
|
||||||
newnkid = 0
|
|
||||||
with engine.connect() as conn, conn.begin():
|
|
||||||
result = conn.execute(query)
|
|
||||||
tdata = result.fetchall()
|
|
||||||
if tdata:
|
|
||||||
newnkid = tdata[0][0]
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
parkedids = []
|
parkedids = []
|
||||||
try:
|
try:
|
||||||
@@ -3303,22 +3278,8 @@ def handle_c2_async_workout(alldata, userid, c2token, c2id, delaysec, defaulttim
|
|||||||
|
|
||||||
workoutid = response.json()['id']
|
workoutid = response.json()['id']
|
||||||
|
|
||||||
if debug: # pragma: no cover
|
workout = Workout.objects.get(id=workoutid)
|
||||||
engine = create_engine(database_url_debug, echo=False)
|
newc2id = workout.uploadedtoc2
|
||||||
else:
|
|
||||||
engine = create_engine(database_url, echo=False)
|
|
||||||
|
|
||||||
query = 'SELECT uploadedtoc2 from rowers_workout WHERE id ={workoutid}'.format(
|
|
||||||
workoutid=workoutid)
|
|
||||||
|
|
||||||
newc2id = 0
|
|
||||||
with engine.connect() as conn, conn.begin():
|
|
||||||
result = conn.execute(query)
|
|
||||||
tdata = result.fetchall()
|
|
||||||
if tdata: # pragma: no cover
|
|
||||||
newc2id = tdata[0][0]
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
parkedids = []
|
parkedids = []
|
||||||
with open('c2blocked.json', 'r') as c2blocked:
|
with open('c2blocked.json', 'r') as c2blocked:
|
||||||
@@ -3335,14 +3296,10 @@ def handle_c2_async_workout(alldata, userid, c2token, c2id, delaysec, defaulttim
|
|||||||
json.dump(tdata, c2blocked)
|
json.dump(tdata, c2blocked)
|
||||||
|
|
||||||
# set distance, time
|
# set distance, time
|
||||||
query = "UPDATE `rowers_workout` SET `distance` = '%s', `duration` = '%s' WHERE `id` = '%s'" % (
|
workout = Workout.objects.get(id=workoutid)
|
||||||
distance, duration, workoutid)
|
workout.distance = distance
|
||||||
|
workout.duration = duration
|
||||||
with engine.connect() as conn, conn.begin():
|
workout.save()
|
||||||
result = conn.execute(query)
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
engine.dispose()
|
|
||||||
|
|
||||||
# summary
|
# summary
|
||||||
if 'workout' in data:
|
if 'workout' in data:
|
||||||
@@ -3359,14 +3316,9 @@ def handle_c2_async_workout(alldata, userid, c2token, c2id, delaysec, defaulttim
|
|||||||
summary, sa, results = summaryfromsplitdata(
|
summary, sa, results = summaryfromsplitdata(
|
||||||
splitdata, data, csvfilename, workouttype=workouttype)
|
splitdata, data, csvfilename, workouttype=workouttype)
|
||||||
|
|
||||||
query = "UPDATE `rowers_workout` SET `summary` = '%s' WHERE `id` = %s" % (
|
workout = Workout.objects.get(id=workoutid)
|
||||||
summary, workoutid)
|
workout.summary = summary
|
||||||
|
workout.save()
|
||||||
with engine.connect() as conn, conn.begin():
|
|
||||||
result = conn.execute(query)
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
engine.dispose()
|
|
||||||
|
|
||||||
from rowingdata.trainingparser import getlist
|
from rowingdata.trainingparser import getlist
|
||||||
if sa:
|
if sa:
|
||||||
|
|||||||
@@ -42,14 +42,18 @@
|
|||||||
{% if instrokemetrics %}
|
{% if instrokemetrics %}
|
||||||
{% for metric in instrokemetrics %}
|
{% for metric in instrokemetrics %}
|
||||||
<p>
|
<p>
|
||||||
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/instroke/{{ metric }}/">{{ metric }}</a>
|
<a href="/rowers/workout/{{ workout.id|encode }}/instroke/{{ metric }}/">{{ metric }}</a>
|
||||||
</p>
|
</p>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
<p>
|
||||||
|
<a href="/rowers/workout/{{ workout.id|encode }}/instroke/interactive/">NEW: Dynamic</a>
|
||||||
|
</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>Unfortunately, this workout doesn't have any in stroke metrics</p>
|
<p>Unfortunately, this workout doesn't have any in stroke metrics</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
{% extends "newbase.html" %}
|
||||||
|
{% load static %}
|
||||||
|
{% load rowerfilters %}
|
||||||
|
|
||||||
|
{% block title %}Advanced Features {% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
<script type='text/javascript'
|
||||||
|
src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'>
|
||||||
|
</script>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>jQuery UI Slider - Range slider</title>
|
||||||
|
<link rel="stylesheet" href="//code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css">
|
||||||
|
<link rel="stylesheet" href="/resources/demos/style.css">
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
|
||||||
|
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function submit_form() {
|
||||||
|
console.log("form changed");
|
||||||
|
var frm = $("#instrokeform");
|
||||||
|
var data = new FormData(frm[0]);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url:"/rowers/workout/{{ workout.id|encode }}/instroke/interactive/",
|
||||||
|
type: "POST",
|
||||||
|
contentType: false,
|
||||||
|
processData: false,
|
||||||
|
data: data,
|
||||||
|
dataType: 'json',
|
||||||
|
|
||||||
|
success: function(data) {
|
||||||
|
console.log(data);
|
||||||
|
$("#id_script").replaceWith('<div id="id_script">'+data.script+'</d'+'iv>');
|
||||||
|
$("#id_chart").replaceWith('<div id="id_chart">'+data.div+'</d'+'iv>');
|
||||||
|
$("#ds").replaceWith('<div id="ds">'+data.ds+'</d'+'iv>');
|
||||||
|
$("#dd").replaceWith('<div id="dd">'+data.dd+'</d'+'iv>');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
|
||||||
|
function csrfSafeMethod(method) {
|
||||||
|
return(/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
|
||||||
|
}
|
||||||
|
$.ajaxSetup({
|
||||||
|
beforeSend: function(xhr, settings) {
|
||||||
|
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
|
||||||
|
xhr.setRequestHeader("X-CSRFToken", csrftoken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$("#instrokeform").on('change', function(evt) {
|
||||||
|
submit_form();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
$( function() {
|
||||||
|
$( "#slider-range" ).slider({
|
||||||
|
range: true,
|
||||||
|
min: 0,
|
||||||
|
max: 60,
|
||||||
|
values: [ {{ spm_min }}, {{ spm_max }} ],
|
||||||
|
slide: function( event, ui ) {
|
||||||
|
$( "#amountspm" ).val(ui.values[ 0 ] + " - " + ui.values[ 1 ] );
|
||||||
|
$( "#id_spm_min" ).val( ui.values[ 0 ]);
|
||||||
|
$( "#id_spm_max" ).val(ui.values[ 1 ] );
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
$( "#amountspm" ).val($( "#slider-range" ).slider( "values", 0 ) +
|
||||||
|
" - " + $( "#slider-range" ).slider( "values", 1 ));
|
||||||
|
|
||||||
|
} );
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
$("#instrokeform").mouseup(function() {
|
||||||
|
submit_form();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
$( function() {
|
||||||
|
console.log({{ activeminutesmin }}, {{ activeminutesmax}}, 'active range');
|
||||||
|
$( "#slider-timerange" ).slider({
|
||||||
|
range: true,
|
||||||
|
min: 0,
|
||||||
|
max: {{ maxminutes }},
|
||||||
|
values: [ {{ activeminutesmin }}, {{ activeminutesmax }} ],
|
||||||
|
slide: function( event, ui ) {
|
||||||
|
$( "#amount" ).val(ui.values[ 0 ] + " min - " + ui.values[ 1 ] + " min " );
|
||||||
|
$("#id_activeminutesmin").val(ui.values[0]);
|
||||||
|
$("#id_activeminutesmax").val(ui.values[1]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$( "#amount" ).val($( "#slider-timerange" ).slider( "values", 0 ) +
|
||||||
|
" min - " + $( "#slider-timerange" ).slider( "values", 1 ) + " min ");
|
||||||
|
} );
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
|
||||||
|
<script src="https://cdn.pydata.org/bokeh/release/bokeh-2.2.3.min.js"></script>
|
||||||
|
|
||||||
|
<script async="true" type="text/javascript">
|
||||||
|
Bokeh.set_log_level("info");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div id="id_script">
|
||||||
|
{{ the_script |safe }}
|
||||||
|
</div>
|
||||||
|
<div id="ds">
|
||||||
|
{{ ds |safe }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1>In Stroke Metrics</h1>
|
||||||
|
<ul class="main-content">
|
||||||
|
<li class="grid_4">
|
||||||
|
<p>This is an experimental prototype. It's behavior is not guaranteed. Response may be slow.
|
||||||
|
</li>
|
||||||
|
<li class="grid_4" >
|
||||||
|
<form id="instrokeform" enctype="multipart/form-data" action="" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<table>
|
||||||
|
{{ form.as_table }}
|
||||||
|
</table>
|
||||||
|
<div id="slider-range"></div>
|
||||||
|
<p>
|
||||||
|
<label for="amountspm">Active Range:</label>
|
||||||
|
<input type="text" id="amountspm" readonly style="border:0; color:#1c75bc; font-weight:bold;">
|
||||||
|
</p>
|
||||||
|
<div id="slider-timerange"></div>
|
||||||
|
<p>
|
||||||
|
<label for="amount">Active Range:</label>
|
||||||
|
<input type="text" id="amount" readonly style="border:0; color:#1c75bc; font-weight:bold;">
|
||||||
|
</p>
|
||||||
|
<input name='form' class="button" type="submit" value="Submit">
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
<li class="grid_4">
|
||||||
|
<div id="id_chart" class="flexplot">
|
||||||
|
{{ the_div|safe }}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="grid_4">
|
||||||
|
<div id="dd" class="flexplot">
|
||||||
|
{{ dd|safe }}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block sidebar %}
|
||||||
|
{% include 'menu_workout.html' %}
|
||||||
|
{% endblock %}
|
||||||
@@ -95,19 +95,35 @@
|
|||||||
<div>
|
<div>
|
||||||
{% if workout.name != '' %}
|
{% if workout.name != '' %}
|
||||||
<h3>
|
<h3>
|
||||||
|
{% if team %}
|
||||||
|
<a
|
||||||
|
href={% url rower.defaultlandingpage3 id=workout.id|encode %}
|
||||||
|
title="{{ rower.defaultlandingpage3|verbose }}">
|
||||||
|
{{ workout.name }} ({{ workout.user.user.first_name }} {{ workout.user.user.last_name}})
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
<a
|
<a
|
||||||
href={% url rower.defaultlandingpage3 id=workout.id|encode %}
|
href={% url rower.defaultlandingpage3 id=workout.id|encode %}
|
||||||
title="{{ rower.defaultlandingpage3|verbose }}">
|
title="{{ rower.defaultlandingpage3|verbose }}">
|
||||||
{{ workout.name }}
|
{{ workout.name }}
|
||||||
</a>
|
</a>
|
||||||
|
{% endif %}
|
||||||
</h3>
|
</h3>
|
||||||
{% else %}
|
{% else %}
|
||||||
<h3>
|
<h3>
|
||||||
|
{% if team %}
|
||||||
|
<a
|
||||||
|
href={% url rower.defaultlandingpage id=workout.id|encode %}
|
||||||
|
title="{{ rower.defaultlandingpage|verbose }}">
|
||||||
|
No Name ({{ workout.user.user.first_name }} {{ workout.user.user.last_name}})
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
<a
|
<a
|
||||||
href={% url rower.defaultlandingpage id=workout.id|encode %}
|
href={% url rower.defaultlandingpage id=workout.id|encode %}
|
||||||
title="{{ rower.defaultlandingpage|verbose }}">
|
title="{{ rower.defaultlandingpage|verbose }}">
|
||||||
No Name
|
No Name
|
||||||
</a>
|
</a>
|
||||||
|
{% endif %}
|
||||||
</h3>
|
</h3>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -81,10 +81,13 @@ class AsyncTaskTests(TestCase):
|
|||||||
|
|
||||||
def test_polygons(self):
|
def test_polygons(self):
|
||||||
polygons = GeoPolygon.objects.all()
|
polygons = GeoPolygon.objects.all()
|
||||||
|
|
||||||
polygon = polygons[0]
|
polygon = polygons[0]
|
||||||
obj = (polygon.id,polygon.name)
|
|
||||||
path = tasks.polygon_to_path(obj)
|
#obj = (polygon.id,polygon.name)
|
||||||
self.assertEqual(len(path),4)
|
path = tasks.polygon_to_path(polygon)
|
||||||
|
|
||||||
|
self.assertEqual(len(path),6)
|
||||||
|
|
||||||
def test_summaryfromsplitdata(self):
|
def test_summaryfromsplitdata(self):
|
||||||
splitdata = [
|
splitdata = [
|
||||||
@@ -495,21 +498,21 @@ class AsyncTaskTests(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(res,1)
|
self.assertEqual(res,1)
|
||||||
|
|
||||||
@patch('rowers.dataprepnodjango.create_engine')
|
@patch('rowers.dataprep.create_engine')
|
||||||
def test_handle_updateergcp(self,mocked_sqlalchemy):
|
def test_handle_updateergcp(self,mocked_sqlalchemy):
|
||||||
f1 = get_random_file()['filename']
|
f1 = get_random_file()['filename']
|
||||||
res = tasks.handle_updateergcp(1,[f1])
|
res = tasks.handle_updateergcp(1,[f1])
|
||||||
self.assertEqual(res,1)
|
self.assertEqual(res,1)
|
||||||
|
|
||||||
|
|
||||||
@patch('rowers.dataprepnodjango.getsmallrowdata_db')
|
@patch('rowers.dataprep.getsmallrowdata_db')
|
||||||
def test_handle_updatecp(self,mocked_getsmallrowdata_db_updatecp):
|
def test_handle_updatecp(self,mocked_getsmallrowdata_db_updatecp):
|
||||||
rower_id = 1
|
rower_id = 1
|
||||||
workoutids = [1]
|
workoutids = [1]
|
||||||
res = tasks.handle_updatecp(rower_id,workoutids)
|
res = tasks.handle_updatecp(rower_id,workoutids)
|
||||||
self.assertEqual(res,1)
|
self.assertEqual(res,1)
|
||||||
|
|
||||||
@patch('rowers.dataprepnodjango.getsmallrowdata_db')
|
@patch('rowers.dataprep.getsmallrowdata_db')
|
||||||
def test_handle_setcp(self,mocked_getsmallrowdata_db_setcp):
|
def test_handle_setcp(self,mocked_getsmallrowdata_db_setcp):
|
||||||
strokesdf = pd.read_csv('rowers/tests/testdata/uhfull.csv')
|
strokesdf = pd.read_csv('rowers/tests/testdata/uhfull.csv')
|
||||||
filename = 'rowers/tests/testdata/temp/pq.gz'
|
filename = 'rowers/tests/testdata/temp/pq.gz'
|
||||||
@@ -521,7 +524,7 @@ class AsyncTaskTests(TestCase):
|
|||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@patch('rowers.dataprepnodjango.getsmallrowdata_db')
|
@patch('rowers.dataprep.getsmallrowdata_db')
|
||||||
def test_handle_update_wps(self,mocked_getsmallrowdata_db_wps):
|
def test_handle_update_wps(self,mocked_getsmallrowdata_db_wps):
|
||||||
ids = [1,2,3]
|
ids = [1,2,3]
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ class CPChartTest(TestCase):
|
|||||||
|
|
||||||
# add some tests of complex form data (no hr, no spm, zero spm, etc)
|
# add some tests of complex form data (no hr, no spm, zero spm, etc)
|
||||||
|
|
||||||
@patch('rowers.dataprepnodjango.create_engine')
|
@patch('rowers.dataprep.create_engine')
|
||||||
def test_agerecords(self, mock_sqlalchemy):
|
def test_agerecords(self, mock_sqlalchemy):
|
||||||
# update_records(url='rowers/tests/c2worldrecords.html',verbose=False)
|
# update_records(url='rowers/tests/c2worldrecords.html',verbose=False)
|
||||||
|
|
||||||
|
|||||||
@@ -1554,6 +1554,36 @@ description: ""
|
|||||||
|
|
||||||
self.instantplan.save()
|
self.instantplan.save()
|
||||||
|
|
||||||
|
self.startdate = (datetime.datetime.now()-datetime.timedelta(days=1)).date()
|
||||||
|
self.enddate = (datetime.datetime.now()+datetime.timedelta(days=1)).date()
|
||||||
|
self.preferreddate = datetime.datetime.now().date()
|
||||||
|
|
||||||
|
|
||||||
|
self.ps = SessionFactory(startdate=self.startdate,enddate=self.enddate,
|
||||||
|
sessiontype='session',
|
||||||
|
sessionmode = 'time',
|
||||||
|
criterium = 'none',
|
||||||
|
sessionvalue = 60,
|
||||||
|
sessionunit='min',
|
||||||
|
preferreddate=self.preferreddate,
|
||||||
|
manager=self.u,
|
||||||
|
interval_string = '5x(800m/5min)'
|
||||||
|
)
|
||||||
|
|
||||||
|
self.ps.save()
|
||||||
|
|
||||||
|
|
||||||
|
result = plannedsessions.add_rower_session(self.r,self.ps)
|
||||||
|
|
||||||
|
self.step = PlannedSessionStep(
|
||||||
|
manager = self.u,
|
||||||
|
name = 'cd',
|
||||||
|
durationvalue = '50000',
|
||||||
|
durationtype = 'Distance',
|
||||||
|
)
|
||||||
|
|
||||||
|
self.step.save()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
@@ -1562,6 +1592,72 @@ description: ""
|
|||||||
except (IOError, FileNotFoundError,OSError):
|
except (IOError, FileNotFoundError,OSError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def test_stepadder(self):
|
||||||
|
login = self.c.login(username=self.u.username, password=self.password)
|
||||||
|
self.assertTrue(login)
|
||||||
|
|
||||||
|
url = '/rowers/plans/stepeditor/{id}/'.format(id=self.ps.id)
|
||||||
|
|
||||||
|
response = self.c.get(url,follow=True)
|
||||||
|
self.assertEqual(response.status_code,200)
|
||||||
|
|
||||||
|
url = '/rowers/plans/stepadder/{id}/'.format(id=self.ps.id)
|
||||||
|
|
||||||
|
bdy = json.dumps([self.step.id])
|
||||||
|
|
||||||
|
response = self.c.post(url, bdy, content_type='application/json',
|
||||||
|
**{'HTTP_X_REQUESTED_WITH':'XMLHttpRequest'})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_stepdelete(self):
|
||||||
|
login = self.c.login(username=self.u.username, password=self.password)
|
||||||
|
self.assertTrue(login)
|
||||||
|
|
||||||
|
url = '/rowers/plans/step/{id}/delete'.format(id=self.step.id)
|
||||||
|
|
||||||
|
response = self.c.get(url,follow=True)
|
||||||
|
self.assertEqual(response.status_code,200)
|
||||||
|
|
||||||
|
def test_stepedit(self):
|
||||||
|
login = self.c.login(username=self.u.username, password=self.password)
|
||||||
|
self.assertTrue(login)
|
||||||
|
|
||||||
|
url = '/rowers/plans/step/{id}/edit/{psid}/'.format(
|
||||||
|
id=self.step.id, psid=self.ps.id
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.c.get(url)
|
||||||
|
self.assertEqual(response.status_code,200)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'durationtype': 'RepeatUntilStepsCmplt',
|
||||||
|
'durationvalue': '0.0',
|
||||||
|
#'targettype': None,
|
||||||
|
'targetvalue': '8',
|
||||||
|
'targetvaluelow': '0',
|
||||||
|
'targetvaluehigh': '0',
|
||||||
|
'intensity': 'Active',
|
||||||
|
'description': 'aap'
|
||||||
|
}
|
||||||
|
|
||||||
|
form = StepEditorForm(data)
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
|
||||||
|
reponse = self.c.post(url, data)
|
||||||
|
self.assertTrue(response.status_code,200)
|
||||||
|
|
||||||
|
def test_save_plan_yaml_view(self):
|
||||||
|
login = self.c.login(username=self.u.username, password=self.password)
|
||||||
|
self.assertTrue(login)
|
||||||
|
|
||||||
|
url = '/rowers/sessions/saveasplan/?when={s}/{e}'.format(
|
||||||
|
s = self.startdate.strftime("%Y-%m-%d"),
|
||||||
|
e = self.enddate.strftime("%Y-%m-%d")
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.c.get(url,follow=True)
|
||||||
|
self.assertEqual(response.status_code,200)
|
||||||
|
|
||||||
def test_clone_view(self):
|
def test_clone_view(self):
|
||||||
login = self.c.login(username=self.u.username, password=self.password)
|
login = self.c.login(username=self.u.username, password=self.password)
|
||||||
self.assertTrue(login)
|
self.assertTrue(login)
|
||||||
|
|||||||
@@ -888,8 +888,7 @@ class ChallengesTest(TestCase):
|
|||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
@patch('rowers.tasks.create_engine', side_effect=mocked_sqlalchemy_courses)
|
def test_virtualevent_check_view(self):
|
||||||
def notest_virtualevent_check_view(self,mocked_sqlalchemy_courses):
|
|
||||||
|
|
||||||
res = tasks.handle_check_race_course(
|
res = tasks.handle_check_race_course(
|
||||||
self.wthyro.csvfilename,
|
self.wthyro.csvfilename,
|
||||||
@@ -898,7 +897,6 @@ class ChallengesTest(TestCase):
|
|||||||
self.result.id,
|
self.result.id,
|
||||||
self.wthyro.user.user.email,
|
self.wthyro.user.user.email,
|
||||||
self.wthyro.user.user.first_name,
|
self.wthyro.user.user.first_name,
|
||||||
mode='coursetest',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(res,1)
|
self.assertEqual(res,1)
|
||||||
|
|||||||
@@ -249,6 +249,8 @@ urlpatterns = [
|
|||||||
path('404/', TemplateView.as_view(template_name='404.html'), name='404'),
|
path('404/', TemplateView.as_view(template_name='404.html'), name='404'),
|
||||||
path('400/', TemplateView.as_view(template_name='400.html'), name='400'),
|
path('400/', TemplateView.as_view(template_name='400.html'), name='400'),
|
||||||
path('403/', TemplateView.as_view(template_name='403.html'), name='403'),
|
path('403/', TemplateView.as_view(template_name='403.html'), name='403'),
|
||||||
|
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/instroke/interactive/$',
|
||||||
|
views.instroke_chart_interactive, name='instroke_chart_interactive'),
|
||||||
re_path(r'^exportallworkouts/?/$', views.workouts_summaries_email_view,
|
re_path(r'^exportallworkouts/?/$', views.workouts_summaries_email_view,
|
||||||
name='workouts_summaries_email_view'),
|
name='workouts_summaries_email_view'),
|
||||||
path('failedjobs/', views.failed_queue_view, name='failed_queue_view'),
|
path('failedjobs/', views.failed_queue_view, name='failed_queue_view'),
|
||||||
|
|||||||
@@ -515,7 +515,7 @@ def rower_process_garmincallback(request): # pragma: no cover
|
|||||||
|
|
||||||
# Process Rojabo callback
|
# Process Rojabo callback
|
||||||
@login_required()
|
@login_required()
|
||||||
def rower_process_rojabocallback(request): # prgrma: no cover
|
def rower_process_rojabocallback(request): # pragma: no cover
|
||||||
# do stuff
|
# do stuff
|
||||||
try:
|
try:
|
||||||
code = request.GET.get('code', None)
|
code = request.GET.get('code', None)
|
||||||
|
|||||||
+24
-18
@@ -1409,7 +1409,7 @@ def save_plan_yaml(request, userid=0):
|
|||||||
steps = ps.steps
|
steps = ps.steps
|
||||||
steps['filename'] = ""
|
steps['filename'] = ""
|
||||||
workouts.append(steps)
|
workouts.append(steps)
|
||||||
else:
|
else: # pragma: no cover
|
||||||
if ps.sessionmode == 'distance':
|
if ps.sessionmode == 'distance':
|
||||||
ps.interval_string = '{d}m'.format(d=ps.sessionvalue)
|
ps.interval_string = '{d}m'.format(d=ps.sessionvalue)
|
||||||
elif ps.sessionmode == 'time':
|
elif ps.sessionmode == 'time':
|
||||||
@@ -2994,7 +2994,8 @@ def rower_create_trainingplan(request, id=0):
|
|||||||
redirect_field_name=None)
|
redirect_field_name=None)
|
||||||
def stepadder(request, id=0):
|
def stepadder(request, id=0):
|
||||||
is_ajax = request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
|
is_ajax = request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
|
||||||
if not is_ajax:
|
|
||||||
|
if not is_ajax: # pragma: no cover
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status=403, data={
|
status=403, data={
|
||||||
'status': 'false',
|
'status': 'false',
|
||||||
@@ -3005,7 +3006,7 @@ def stepadder(request, id=0):
|
|||||||
|
|
||||||
is_save = request.GET.get('save',0)
|
is_save = request.GET.get('save',0)
|
||||||
|
|
||||||
if request.method != 'POST':
|
if request.method != 'POST': # pragma: no cover
|
||||||
message = {'status': 'false',
|
message = {'status': 'false',
|
||||||
'message': 'this view cannot be accessed through GET'}
|
'message': 'this view cannot be accessed through GET'}
|
||||||
return JSONResponse(status=403, data=message)
|
return JSONResponse(status=403, data=message)
|
||||||
@@ -3013,14 +3014,16 @@ def stepadder(request, id=0):
|
|||||||
try:
|
try:
|
||||||
json_data = json.loads(request.body)
|
json_data = json.loads(request.body)
|
||||||
post_data = json_data
|
post_data = json_data
|
||||||
except (KeyError, JSONDecodeError):
|
except (KeyError, JSONDecodeError): # pragma: no cover
|
||||||
q = request.POST
|
q = request.POST
|
||||||
post_data = {k: q.getlist(k) if len(
|
post_data = {k: q.getlist(k) if len(
|
||||||
q.getlist(k)) > 1 else v for k, v in q.items()}
|
q.getlist(k)) > 1 else v for k, v in q.items()}
|
||||||
|
|
||||||
# only allow local host
|
# only allow local host
|
||||||
hostt = request.get_host().split(':')
|
hostt = request.get_host().split(':')
|
||||||
if hostt[0] not in ['localhost', '127.0.0.1', 'dev.rowsandall.com', 'rowsandall.com']:
|
|
||||||
|
if hostt[0] not in ['localhost', '127.0.0.1', 'dev.rowsandall.com', 'rowsandall.com',
|
||||||
|
'testserver']: # pragma: no cover
|
||||||
message = {'status': 'false',
|
message = {'status': 'false',
|
||||||
'message': 'permission denied for host '+hostt[0]}
|
'message': 'permission denied for host '+hostt[0]}
|
||||||
return JSONResponse(status=403, data=message)
|
return JSONResponse(status=403, data=message)
|
||||||
@@ -3028,7 +3031,7 @@ def stepadder(request, id=0):
|
|||||||
if ps.steps:
|
if ps.steps:
|
||||||
filename = ps.steps.get('filename','')
|
filename = ps.steps.get('filename','')
|
||||||
sport = ps.steps.get('sport','rowing')
|
sport = ps.steps.get('sport','rowing')
|
||||||
else:
|
else: # pragma: no cover
|
||||||
filename = ''
|
filename = ''
|
||||||
sport = 'rowing'
|
sport = 'rowing'
|
||||||
|
|
||||||
@@ -3045,10 +3048,10 @@ def stepadder(request, id=0):
|
|||||||
d = step.asdict()
|
d = step.asdict()
|
||||||
d['stepId'] = nr
|
d['stepId'] = nr
|
||||||
steps['steps'].append(d)
|
steps['steps'].append(d)
|
||||||
except PlannedSessionStep.DoesNotExist:
|
except PlannedSessionStep.DoesNotExist: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if is_save:
|
if is_save: # pragma: no cover
|
||||||
# save the darn thing
|
# save the darn thing
|
||||||
ps.steps = steps
|
ps.steps = steps
|
||||||
|
|
||||||
@@ -3066,9 +3069,12 @@ def stepdelete(request, id=0):
|
|||||||
|
|
||||||
step.delete()
|
step.delete()
|
||||||
|
|
||||||
backid = request.GET.get('id')
|
backid = request.GET.get('id',0)
|
||||||
|
|
||||||
|
if backid: # pragma: no cover
|
||||||
url = reverse(stepeditor,kwargs={'id':backid})
|
url = reverse(stepeditor,kwargs={'id':backid})
|
||||||
|
else:
|
||||||
|
url = reverse('plannedsessions_view')
|
||||||
|
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@@ -3079,7 +3085,7 @@ def stepedit(request, id=0, psid=0):
|
|||||||
step = get_object_or_404(PlannedSessionStep, pk=id)
|
step = get_object_or_404(PlannedSessionStep, pk=id)
|
||||||
try:
|
try:
|
||||||
ps = PlannedSession.objects.get(id=psid)
|
ps = PlannedSession.objects.get(id=psid)
|
||||||
except PlannedSession.DoesNotExist:
|
except PlannedSession.DoesNotExist: # pragma: no cover
|
||||||
ps = None
|
ps = None
|
||||||
|
|
||||||
form = StepEditorForm(instance=step)
|
form = StepEditorForm(instance=step)
|
||||||
@@ -3094,7 +3100,7 @@ def stepedit(request, id=0, psid=0):
|
|||||||
ee = ss.copy()
|
ee = ss.copy()
|
||||||
ee.pop('stepId')
|
ee.pop('stepId')
|
||||||
|
|
||||||
if (dd == ee):
|
if (dd == ee): # pragma: no cover
|
||||||
ss['durationType'] = form.cleaned_data['durationtype']
|
ss['durationType'] = form.cleaned_data['durationtype']
|
||||||
ss['durationValue'] = form.cleaned_data['durationvalue']
|
ss['durationValue'] = form.cleaned_data['durationvalue']
|
||||||
ss['targetType'] = form.cleaned_data['targettype']
|
ss['targetType'] = form.cleaned_data['targettype']
|
||||||
@@ -3126,15 +3132,15 @@ def stepedit(request, id=0, psid=0):
|
|||||||
step.name = form.cleaned_data['name']
|
step.name = form.cleaned_data['name']
|
||||||
step.description = form.cleaned_data['description']
|
step.description = form.cleaned_data['description']
|
||||||
|
|
||||||
if step.durationtype == 'Time':
|
if step.durationtype == 'Time': # pragma: no cover
|
||||||
step.durationvalue *= 60000
|
step.durationvalue *= 60000
|
||||||
elif step.durationtype == 'Distance':
|
elif step.durationtype == 'Distance': # pragma: no cover
|
||||||
step.durationvalue *= 100
|
step.durationvalue *= 100
|
||||||
|
|
||||||
step.save()
|
step.save()
|
||||||
|
|
||||||
|
|
||||||
if step.durationtype == 'Time':
|
if step.durationtype == 'Time': # pragma: no cover
|
||||||
form.fields['durationvalue'].initial = step.durationvalue / 60000
|
form.fields['durationvalue'].initial = step.durationvalue / 60000
|
||||||
elif step.durationtype == 'Distance':
|
elif step.durationtype == 'Distance':
|
||||||
form.fields['durationvalue'].initial = step.durationvalue / 100
|
form.fields['durationvalue'].initial = step.durationvalue / 100
|
||||||
@@ -3143,7 +3149,7 @@ def stepedit(request, id=0, psid=0):
|
|||||||
stepdescription = step_to_string(step.asdict(), short=False)[0]
|
stepdescription = step_to_string(step.asdict(), short=False)[0]
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
if 'stepsave_and_return' in request.POST:
|
if 'stepsave_and_return' in request.POST: # pragma: no cover
|
||||||
url = reverse('stepeditor',kwargs = {'id': ps.id})
|
url = reverse('stepeditor',kwargs = {'id': ps.id})
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@@ -3204,7 +3210,7 @@ def stepeditor(request, id=0):
|
|||||||
targetvaluehigh = targetvaluehigh,
|
targetvaluehigh = targetvaluehigh,
|
||||||
intensity = intensity,
|
intensity = intensity,
|
||||||
)
|
)
|
||||||
if not archived_steps.count() and durationvalue != 0:
|
if not archived_steps.count():
|
||||||
s = PlannedSessionStep(
|
s = PlannedSessionStep(
|
||||||
manager = request.user,
|
manager = request.user,
|
||||||
durationtype = durationtype,
|
durationtype = durationtype,
|
||||||
@@ -3217,14 +3223,14 @@ def stepeditor(request, id=0):
|
|||||||
name = step.get('wkt_step_name','Step')
|
name = step.get('wkt_step_name','Step')
|
||||||
)
|
)
|
||||||
s.save()
|
s.save()
|
||||||
else:
|
else: # pragma: no cover
|
||||||
s = archived_steps[0]
|
s = archived_steps[0]
|
||||||
currentsteps.append(s)
|
currentsteps.append(s)
|
||||||
|
|
||||||
|
|
||||||
form = StepEditorForm()
|
form = StepEditorForm()
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST': # pragma: no cover
|
||||||
form = StepEditorForm(request.POST)
|
form = StepEditorForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
step = form.save(commit=False)
|
step = form.save(commit=False)
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ from rowers.forms import (
|
|||||||
VideoAnalysisMetricsForm, SurveyForm, HistorySelectForm,
|
VideoAnalysisMetricsForm, SurveyForm, HistorySelectForm,
|
||||||
StravaChartForm, FitnessFitForm, PerformanceManagerForm,
|
StravaChartForm, FitnessFitForm, PerformanceManagerForm,
|
||||||
TrainingPlanBillingForm, InstantPlanSelectForm,
|
TrainingPlanBillingForm, InstantPlanSelectForm,
|
||||||
TrainingZonesForm,
|
TrainingZonesForm, InstrokeForm
|
||||||
)
|
)
|
||||||
|
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
@@ -239,6 +239,7 @@ from rowers.rows import handle_uploaded_file, handle_uploaded_image
|
|||||||
from rowers.plannedsessions import *
|
from rowers.plannedsessions import *
|
||||||
from rowers.tasks import handle_makeplot, handle_otwsetpower, handle_sendemailtcx, handle_sendemailcsv
|
from rowers.tasks import handle_makeplot, handle_otwsetpower, handle_sendemailtcx, handle_sendemailcsv
|
||||||
from rowers.tasks import (
|
from rowers.tasks import (
|
||||||
|
instroke_static,
|
||||||
fetch_rojabo_session,
|
fetch_rojabo_session,
|
||||||
handle_sendemail_unrecognized, handle_sendemailnewcomment,
|
handle_sendemail_unrecognized, handle_sendemailnewcomment,
|
||||||
handle_request_post,
|
handle_request_post,
|
||||||
|
|||||||
+127
-32
@@ -2904,36 +2904,8 @@ def instroke_chart(request, id=0, metric=''): # pragma: no cover
|
|||||||
instrokemetrics = rowdata.get_instroke_columns()
|
instrokemetrics = rowdata.get_instroke_columns()
|
||||||
|
|
||||||
if metric in instrokemetrics:
|
if metric in instrokemetrics:
|
||||||
f1 = w.csvfilename[6:-4]
|
job = myqueue(queuelow,
|
||||||
timestr = strftime("%Y%m%d-%H%M%S")
|
instroke_static,w, metric)
|
||||||
imagename = f1+timestr+'.png'
|
|
||||||
fullpathimagename = 'static/plots/'+imagename
|
|
||||||
u = w.user.user
|
|
||||||
r = getrower(u)
|
|
||||||
fig1 = rowdata.get_plot_instroke(metric)
|
|
||||||
canvas = FigureCanvas(fig1)
|
|
||||||
canvas.print_figure('static/plots/'+imagename)
|
|
||||||
plt.close(fig1)
|
|
||||||
fig1.clf()
|
|
||||||
gc.collect()
|
|
||||||
|
|
||||||
try:
|
|
||||||
width, height = Image.open(fullpathimagename).size
|
|
||||||
except:
|
|
||||||
width = 1200
|
|
||||||
height = 600
|
|
||||||
|
|
||||||
imgs = GraphImage.objects.filter(workout=w)
|
|
||||||
if imgs.count() < 7:
|
|
||||||
i = GraphImage(workout=w,
|
|
||||||
creationdatetime=timezone.now(),
|
|
||||||
filename=fullpathimagename,
|
|
||||||
width=width, height=height)
|
|
||||||
|
|
||||||
i.save()
|
|
||||||
else:
|
|
||||||
messages.error(
|
|
||||||
request, 'You have reached the maximum number of static images for this workout. Delete an image first')
|
|
||||||
|
|
||||||
r = getrower(request.user)
|
r = getrower(request.user)
|
||||||
url = reverse(r.defaultlandingpage,
|
url = reverse(r.defaultlandingpage,
|
||||||
@@ -2943,9 +2915,132 @@ def instroke_chart(request, id=0, metric=''): # pragma: no cover
|
|||||||
|
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True)
|
||||||
|
def instroke_chart_interactive(request, id=0):
|
||||||
|
|
||||||
|
is_ajax = request_is_ajax(request)
|
||||||
|
|
||||||
|
w = get_workoutuser(id, request)
|
||||||
|
|
||||||
|
rowdata = rrdata(csvfile=w.csvfilename)
|
||||||
|
instrokemetrics = rowdata.get_instroke_columns()
|
||||||
|
|
||||||
|
form = InstrokeForm(choices=instrokemetrics)
|
||||||
|
|
||||||
|
script = ''
|
||||||
|
div = get_call()
|
||||||
|
|
||||||
|
metric = instrokemetrics[0]
|
||||||
|
spm_min = 15
|
||||||
|
spm_max = 45
|
||||||
|
|
||||||
|
activeminutesmax = int(rowdata.duration/60.)
|
||||||
|
activeminutesmin = 0
|
||||||
|
maxminutes = activeminutesmax
|
||||||
|
individual_curves = False
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = InstrokeForm(request.POST,choices=instrokemetrics)
|
||||||
|
if form.is_valid():
|
||||||
|
metric = form.cleaned_data['metric']
|
||||||
|
spm_min = form.cleaned_data['spm_min']
|
||||||
|
spm_max = form.cleaned_data['spm_max']
|
||||||
|
activeminutesmin = form.cleaned_data['activeminutesmin']
|
||||||
|
activeminutesmax = form.cleaned_data['activeminutesmax']
|
||||||
|
individual_curves = form.cleaned_data['individual_curves']
|
||||||
|
|
||||||
|
|
||||||
|
activesecondsmin = 60.*activeminutesmin
|
||||||
|
activesecondsmax = 60.*activeminutesmax
|
||||||
|
|
||||||
|
data = rowdata.get_instroke_data(
|
||||||
|
metric,
|
||||||
|
spm_min=spm_min,
|
||||||
|
spm_max=spm_max,
|
||||||
|
activeminutesmin=activeminutesmin,
|
||||||
|
activeminutesmax=activeminutesmax,
|
||||||
|
)
|
||||||
|
|
||||||
|
script, div = instroke_interactive_chart(
|
||||||
|
data, metric, w,
|
||||||
|
spm_min,
|
||||||
|
spm_max,
|
||||||
|
activeminutesmin,
|
||||||
|
activeminutesmax,
|
||||||
|
individual_curves,
|
||||||
|
)
|
||||||
|
|
||||||
|
# change to range spm_min to spm_max
|
||||||
|
vals, units, typ = rowdata.updateinterval_range(
|
||||||
|
' Cadence (stokes/min)', spm_min, spm_max,
|
||||||
|
debug=False, smoothwindow=2.,
|
||||||
|
activewindow=[activesecondsmin, activesecondsmax])
|
||||||
|
|
||||||
|
intervalstats = rowdata.allstats()
|
||||||
|
itime, idist, itype = rowdata.intervalstats_values()
|
||||||
|
|
||||||
|
intervaldata = {
|
||||||
|
'itime': itime,
|
||||||
|
'idist': idist,
|
||||||
|
'itype': itype,
|
||||||
|
'selector': '',
|
||||||
|
'normp': 0,
|
||||||
|
'normv': 0,
|
||||||
|
}
|
||||||
|
ds, dd = interactive_chart(encoder.decode_hex(
|
||||||
|
id), promember=1, intervaldata=intervaldata)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
breadcrumbs = [
|
||||||
|
{
|
||||||
|
'url': '/rowers/list-workouts/',
|
||||||
|
'name': 'Workouts'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'url': get_workout_default_page(request, id),
|
||||||
|
'name': w.name
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'url': reverse('instroke_view', kwargs={'id': id}),
|
||||||
|
'name': 'In-Stroke Metrics'
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
if is_ajax:
|
||||||
|
response = json.dumps({
|
||||||
|
'script': script,
|
||||||
|
'div': div,
|
||||||
|
'ds': ds,
|
||||||
|
'dd': dd,
|
||||||
|
})
|
||||||
|
|
||||||
|
return HttpResponse(response, content_type='application/json')
|
||||||
|
|
||||||
|
return render(request,
|
||||||
|
'instroke_interactive.html',
|
||||||
|
{
|
||||||
|
'workout': w,
|
||||||
|
'rower': w.user,
|
||||||
|
'active': 'nav-workouts',
|
||||||
|
'breadcrumbs': breadcrumbs,
|
||||||
|
'teams': get_my_teams(request.user),
|
||||||
|
'instrokemetrics': instrokemetrics,
|
||||||
|
'form':form,
|
||||||
|
'the_script': script,
|
||||||
|
'the_div': div,
|
||||||
|
'spm_min': spm_min,
|
||||||
|
'spm_max': spm_max,
|
||||||
|
'ds': ds,
|
||||||
|
'dd': dd,
|
||||||
|
'activeminutesmin': activeminutesmin,
|
||||||
|
'activeminutesmax': activeminutesmax,
|
||||||
|
'maxminutes': maxminutes,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
# erase column
|
# erase column
|
||||||
|
|
||||||
|
|
||||||
@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True)
|
@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True)
|
||||||
def workout_erase_column_view(request, id=0, column=''):
|
def workout_erase_column_view(request, id=0, column=''):
|
||||||
r = getrower(request.user)
|
r = getrower(request.user)
|
||||||
|
|||||||
Reference in New Issue
Block a user